Docker
Memo
バインドマウント先を削除した場合の挙動
バインドマウントしているディレクトリをまるごと削除すると、後からそこにディレクトリをおいても中身がバインドされないことに気づいた。
- ワーキングディレクトリ - aaa(ディレクトリ) - test1(ファイル) - test2(ファイル) - bbb(ディレクトリ) - test1(ファイル) - test2(ファイル)
docker run -d -it --name devtest --mount type=bind,source="$(pwd)"/aaa,target=/aaa nginx:latest
docker inspect devtest
$ docker exec -it devtest /bin/sh > cd /app > ls test1 test2
$ rm -rf /tmp/aaa
$ docker exec -it devtest /bin/sh > cd /app > ls (何も出ない)
$ cp -r /tmp/bbb /tmp/aaa
$ docker exec -it devtest /bin/sh > cd /ap > ls (何も出ない)
docker inspect devtest
- File mount does not update with changes from host · Issue #15793 · moby/moby
バインドマウントはinodeで一致しているかで同期を管理しているという。新しく作る場合はinodeが変わるので同期されなくなる。
- mvだとinodeが変わらない。のでバインドマウントをし続けられる。そのシェルセッションで何もしていなくても、別で名前を変えても追従し、ワーキングディレクトリは変わることがある
- cpだとinodeが変わる。のでバインドマウントの同期が切れ、同じ名前のディレクトリを配置しても同期はされない
docker-compose restart
するとまたバインドマウントされるようになった。
サービス名をつけると管理が便利
サービス名をつけて起動すると、名前を指定して止めることもできるようになる。
docker run -d -p 80:80 --name webserver nginx
docker stop webserver
Docker本体のビルド方法
- moby/moby: Moby Project
- 手元にcloneしている前提
sudo make build # environment sudo make binary # binary sudo chown -R $USER:$USER . # なくてもいい
すると、bundles化に実行ファイルが生成される。あとはビルドしたものでデーモンを起動する。
sudo service docker stop # 現状デーモン停止 sudo ./bundles/binary-daemon/dockerd
docker buildのデバッグ
buildでどこまで成功しているかを確かめるために、コマンドを仕込みたいことがある。デフォルトでは出力されないので、オプションが必要。
docker build . --progress plain --no-cache -t test
ログを追従させる
docker-compose logs -f
-dオプションはログが出ないので使ってこなかった。これによって卒業できる。
ボリュームとマウントの違い
- ボリューム
- データを永続化できる場所のこと
- マウント
- コンテナにホストのディレクトリをマウントすること
ボリュームはマウントしないと、使えるようにはならない。docker-composeのvolumesではボリュームといいつつ書き方によってマウントしてくれる。
コンテナからホストのポートにアクセスできるようにする
docker-composeでコンテナ側からホストのポートへアクセスできるようにする方法。
container: extra_hosts: - "host.docker.internal:host-gateway"
あとはコンテナ内のコード側で、 host.docker.internal:{ホストのポート番号}
とすることでホストのポートへアクセスできるようになる。
Got permission deniedエラー
dockerがsudo権限以外で実行できなくなるときがある。
$ docker ps Got permission denied while trying to connect to the Docker daemon socket
これはログイン中のユーザがdocker権限を持っていないから。
sudo gpasswd -a $(whoami) docker id $(whoami) # dockerが追加されたのを確認する
コマンドを実行したあと、ログアウトする。sudoなしでdockerコマンドを打てるようになっている。
GitHub Actionsでビルドする
CIによるコンテナビルドには、–cache-from を使って、レジストリに送信したイメージから各ステージのキャッシュを取得していく方法と、CIのキャッシュ機能を使う方法の2つがある。
レジストリからキャッシュを取る方法には弱点がある。
- キャッシュが登録イメージの1つしかない。たとえば異なるブランチでキャッシュが更新されると、キャッシュが失われる
- –mount=type=…のcacheが、pushイメージには含まれない
- ステージごとにキャッシュ通信(取得+送信)をするが、オーバーヘッドが大きい
- イメージに含めることができるキャッシュ(inline cache)には、minモードしか適用できない。つまりキャッシュに制限があるmoby/buildkit: concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit
のため、CIのキャッシュ機能を使うのが現実的か。複数のキャッシュ…ブランチごと、Gemfileのハッシュ値ごとでハッシュを保持できるためキャッシュがヒットしやすい。キャッシュはひとまとめで保存され、レジストリへの送信イメージは利用イメージだけになる。
RUN –mount=type=…オプション
ビルド時にだけアクセスできる、cacheマウントを利用できる。マウントと言うが、ホストマシンとは関係ない。マウントディレクトリはビルド後削除されるため、イメージサイズにも優しい。 https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md
RUN --mount=type=cache,target=/root/.cache/go-build go build
たとえばpackage.jsonに変更があったときも途中から再開できる。ビルドキャッシュがヒットする/しないのゼロイチでなくなる。
rake assets:precompile高速化
- public/assets
- tmp/cache/assets
をキャッシュしておくことで高速化できる。
ビルドキャッシュをレジストリに保存し、CI環境でキャッシュを使ってビルドする
レジストリのキャッシュを利用してビルドできる。これによって、キャッシュがローカル環境に保持されないCI環境などでもキャッシュを利用して高速にビルドできる。
ポイントは–build-argと–cache-from。 –build-argでメタ情報を含めてビルドする。このイメージをpushしておくことで、次回からキャッシュを利用できる。 –cache-fromによってレジストリにある指定イメージからキャッシュを取得してビルドする。
docker build --target build -t ghcr.io/kijimad/roam-build:master --cache-from ghcr.io/kijimad/roam-build:master --build-arg BUILDKIT_INLINE_CACHE=1 .
docker push ghcr.io/kijimad/roam-build:master
本番用yarn buildの例
本番用にコンパクトにビルドする場合の例。 node_modulesはいらなくて、ビルド成果物だけあればよい。 ステージを分けることで、意味が明確になり、サイズも小さくできる(高速化)。
COPY package.json $HOME/ COPY front/ $HOME/front/ # front にはビルド対象のjs, tsファイルが配置されている想定。サブモジュールを導入している場合、package.jsonは階層上に複数あるため、COPYしておく必要がある RUN npm install COPY babel.config.js $HOME/ COPY tsconfig.json $HOME/ COPY webpack.config.js $HOME/ RUN yarn run build
COPY --from=rails-yarn-build $HOME/public/webpack/ $HOME/public/webpack/
Rails開発のMy docker-compose
Rails開発をすべてdockerでやる想定。
一発ですべてが準備され、クリーンな環境を構築する。bundle install やyarn install など、立ち上げ続ける前提でないコマンドも含まれる。そのコマンドだけ再度実行したいときは docker-compose restart bundle
などとする。
元ネタ: foremのdocker-compose.yml。
↓あとはdockerizeを設定すれば完璧か。
# 共通のimage名: app # imageのワーキングディレクトリ: /app version: '3.7' services: mysql: image: mysql:latest ports: - '${MYSQL_PORT:-3306}:3306' environment: # DBクライアントでの接続時に必要なので明示する MYSQL_DATABASE: develop MYSQL_ROOT_PASSWORD: root MYSQL_USER: user MYSQL_PASSWORD: password MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' volumes: - 'mysql-data:/var/lib/mysql' redis: image: redis:latest ports: - '${REDIS_PORT:-6379}:6379' memcached: image: memcached:latest ports: - '${MEMCACHED_PORT:-11212}:11211' rails: image: app environment: RAILS_ENV: development REDIS_URL: 'redis://redis:6379' MEMCACHED: 'memcached:11211' DATABASE_URL: 'mysql2://root@mysql:3306' depends_on: - mysql - redis - memcached - bundle - yarn - seed command: bash -c 'bundle exec rails s -b 0.0.0.0' volumes: - .:/app:delegated # delegatedで高速化 - gem_data:/usr/local/bundle:delegated # package系は永続化して最初からinstallにならないようにする - node_modules:/app/node_modules:delegated ports: - '3000:3000' webpack: image: app environment: NODE_ENV: development WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 command: bash -c 'yarn watch' volumes: - .:/app:delegated - node_modules:/app/node_modules:delegated ports: - 8080:8080 sidekiq: image: app command: bash -c 'bundle exec sidekiq -C config/sidekiq.yml' environment: REDIS_URL: 'redis://redis:6379' DATABASE_URL: 'mysql2://root@mysql:3306' volumes: - .:/app:delegated - gem_data:/usr/local/bundle:delegated links: - mysql - redis bundle: image: app environment: RAILS_ENV: development volumes: - .:/app:delegated - gem_data:/usr/local/bundle:delegated command: bash -c "bundle install --jobs 8" # マシンがいくつ並列処理できるかは`$ getconf _NPROCESSORS_ONLN` で調べられる yarn: image: app environment: NODE_ENV: development volumes: - .:/app:delegated - node_modules:/app/node_modules:delegated command: bash -c "yarn install" seed: image: app environment: DATABASE_URL: 'mysql2://root@mysql:3306' volumes: - .:/app:delegated - gem_data:/usr/local/bundle:delegated command: bash -c "rake db:seed_fu" volumes: gem_data: node_modules: mysql_data:
#! /bin/bash set -e if [ -f tmp/pids/server.pid ]; then rm -f tmp/pids/server.pid fi cat << EOF ░░▄████████████▄▐█▄▄▄▄█▌░ ░░████████████████▌▀▀██▀▀░░ ░░████▄████████████▄▄█▌░░░░ ░░▄▄▄▄▄██████████████▀ ░░░░ EOF exec "$@"
docker service再起動
おかしくなったときの再起動。
sudo service docker restart
コンテナ掃除関係
コマンドでDockerコンテナを停止・削除、イメージの削除をする - Qiita
docker stop $(docker ps -q) # 全コンテナ停止 docker rm $(docker ps -q -a) # 全コンテナ削除 docker rmi $(docker images -q) # 全イメージ削除:
ディスク使用率がとんでもないことになっていたとき
ディスク使用率がほぼ100%になっていた。占めているほとんどはDocker関係のようだった。 イメージは削除するようにしてたが、ほかにも色々あるよう。
専用のページがある。 https://docs.docker.com/config/pruning/
非常に多くのゴミがありそうだったので、多少再pullに時間がかかることを許容してすべて削除することにした。
docker system prune
ゴリゴリbuildして試しているときは、気をつけたほうがよさそう。
キャッシュ削除だけ行う。この場合が多そう。
docker builder prune
entrypoint.sh
公式Docker Imageでよく用いられる、コンテナ起動時に実行するスクリプト。 公式のイメージのままで、初回起動時に実行したいフックとして記述できる。
例(Dockerfile): rails-on-kubernetes/Dockerfile at master · tzumby/rails-on-kubernetes
ADD . /myapp
COPY docker-entrypoint.sh /usr/local/bin
ENTRYPOINT ["docker-entrypoint.sh"]
例(entrypoint.sh): rails-on-kubernetes/docker-entrypoint.sh at master · tzumby/rails-on-kubernetes
#!/bin/sh set -e if [ -f tmp/pids/server.pid ]; then rm tmp/pids/server.pid fi echo "Waiting for Postgres to start..." while ! nc -z postgres 5432; do sleep 0.1; done echo "Postgres is up" echo "Waiting for Redis to start..." while ! nc -z redis 6379; do sleep 0.1; done echo "Redis is up - execuring command" exec bundle exec "$@"
docker-composeとdocker
docker-composeは自動でタグ名をつけてくれたり、マウントしてくれたり、dockerコマンドよりややこしくなりにくい。 単に開発環境として使っているだけでは、ほとんどdocker-composeで事足りる。 が、docker-composeへ依存しているということで、docker-compose関係ない別の文脈で使おうとすると途端に動かなくなる。本質的にdocker-composeはコンテナ間の関係性を記述しているだけで、コンテナ自体を表現しているわけではない。
本当にdockerコンテナとしての正しい使い方をしているかテストするには、コンテナを複数のデプロイやCIで利用してみるのがよい。同じ流れで簡単にできたのなら正しい。簡単にできないなら何かが間違っている。
よく使うdockerオプション
docker run --rm -v "$PWD/":/roam -w /roam ghcr.io/kijimad/roam:master sh deploy.sh
--rm
: コマンド実行後にコンテナを削除する
-v
: ホストマシンにマウントする。左がホストマシン、右がコンテナ内。
docker run --rm -it ghcr.io/kijimad/roam:master
-it はttyオプション。インタラクティブなシェルを作成する。つけないと、一瞬で消える。
buildkitをオンにする
環境変数をオンにすることで、新しい機能が使えるようになる。
export COMPOSE_DOCKER_CLI_BUILD=1 export DOCKER_BUILDKIT=1 docker build .
docker-composeでマウントしたときにnode_modulesが消える問題
- npm install するコンテナを作成
- コンテナをマウント
- ホストマシンにないnode_modulesは消える
- エラー
なので、node_modulesもマウントする。
docker run --rm -v "$PWD":/roam -v /roam/node_modules ghcr.io/kijimad/roam_lint:master make textlint
dockleでセキュリティチェック
dockleというツールでイメージをチェックできる。 goodwithtech/dockle: Container Image Linter for Security, Helping build the Best-Practice Docker Image, Easy to start
自前のイメージにかけるとたくさん見つかった。
$ dockle ghcr.io/kijimad/roam:4f3296b FATAL - DKL-DI-0001: Avoid sudo command * Avoid sudo in container : /bin/sh -c yum -y update && yum -y install yum-utils gcc gcc-c++ make openssl-devel openssh-server readline nuplot WARN - CIS-DI-0001: Create a user for the container * Last user should not be root INFO - CIS-DI-0005: Enable Content trust for Docker * export DOCKER_CONTENT_TRUST=1 before docker pull/build INFO - CIS-DI-0006: Add HEALTHCHECK instruction to the container image * not found HEALTHCHECK statement INFO - CIS-DI-0008: Confirm safety of setuid/setgid files * setgid file: g--x--x--x usr/libexec/openssh/ssh-keysign * setuid file: urwxr-xr-x usr/sbin/pam_timestamp_check * setuid file: urwxr-xr-x usr/bin/mount * setgid file: grwx--x--x usr/libexec/utempter/utempter * setuid file: urwxr-xr-x usr/bin/chage * setuid file: urwxr-xr-x usr/bin/su * setuid file: urwxr-x--- usr/libexec/dbus-1/dbus-daemon-launch-helper * setuid file: urwxr-xr-x usr/sbin/unix_chkpwd * setuid file: u--x--x--x usr/bin/sudo * setgid file: g--x--x--x usr/bin/ssh-agent * setuid file: urwxr-xr-x usr/bin/umount * setuid file: urwxr-xr-x usr/bin/gpasswd * setuid file: urwxr-xr-x usr/bin/newgrp * setgid file: grwxr-xr-x usr/bin/write INFO - DKL-LI-0003: Only put necessary files * Suspicious directory : roam/.git * Suspicious directory : usr/local/plugins/ruby-build/.git * Suspicious directory : usr/local/plugins/ruby-build/test/tmp * Suspicious directory : tmp * unnecessary file : roam/docker-compose.yml * unnecessary file : roam/Dockerfile
pushスクリプト
Amazon.co.jp: Deploying Rails with Docker, Kubernetes and ECS (English Edition) eBook : Acuña, Pablo: Foreign Language Booksに載ってたスクリプト。書いてリポジトリに入れておくとスムーズにビルドやプッシュができる。 レジストリ・ユーザ名・リポジトリを適宜変える。
#!/bin/sh LC=$(git rev-parse --short HEAD) docker build -t ghcr.io/kijimad/webapp:${LC} . docker push ghcr.io/kijimad/webapp:${LC}
実行後にコンテナ削除
docker run するとコンテナ内に入れるが、作ったコンテナはそのままになる。
実行後に削除して欲しい場合は、 docker --rm webapp /bin/sh
などrmオプションを使う。
コンテナ間の接続はサービス名を用いる
コンテナ間の接続をしようとして、このようなエラーが出た。
Error connecting to Redis on 127.0.0.1:6379 (Errno::ECONNREFUSED)
127.0…とあることから、コンテナ内のアドレスを見に行ってる。 コンテナ間での通信には、サービス名のアドレスを追加する必要がある。
rootユーザでファイル作成しないようにする
Dockerコンテナ内でファイルを作成すると、ownerがrootになり編集や削除ができず面倒。 Dockerの内部ではユーザid(uid)やグループid(gid)がホストと異なる。idがホストマシンと合わないためrootとして実行されたことになる、よう。
安易な解決策としては、権限をホストユーザに変更すれば問題ない。 とはいえ、コンテナ内のサービスが新しくファイルを作るたび(たとえばマイグレーションファイル生成)に実行するのは面倒。 If you are running Docker on Linux, the files rails new created are owned by root.
sudo chown -R $USER:$USER .
解決策としてはいくつか種類があるようなのだが、とりあえずできた。 サービスのvolumesにユーザ情報をマウントする。:roは読み取り専用(read onlyか)。 これでidの照合元がホストと同じになる。
volumes: - /etc/passwd:/etc/passwd:ro - /etc/group:/etc/group:ro
あとはidを環境変数経由で渡せば、コンテナ内でもホストのユーザが実行したことになる。
sudo docker run -u "$(id -u $USER):$(id -g $USER)" rails /bin/sh sudo docker-compose run -u "$(id -u $USER):$(id -g $USER)" rails /bin/sh
overrideがある場合、このようになる(長すぎ)。
sudo docker-compose -f docker-compose.yml -f docker-compose-app.override.yml run -u "$(id -u $USER):$(id -g $USER)" rails /bin/sh
Docker Hub
Dockerイメージをインターネット上にアップロードできるスペース。 個別にビルドしなくてよくなるためDocker関連の全工程が高速化する。テスト、ローカル、デプロイ…。
Dockerfileは何か
Dockrfileはイメージを作る。(image build) docker-compose upは↑で作られたイメージを元にコンテナを作り起動までする。そのなかアプリケーションを走らせて開発する。
image構築 → コンテナ構築 → コンテナ起動 という流れ。
コンテナの作り方には2種類ある。
- 自作する必要があるものは↑Dockerfileで作る
- 既存コンテナ(MySQLとか)はイメージをダウンロードする
コンテナ内でコマンド実行する
コンテナ内部で実行したいコマンドがあるときにやりたいこと、たとえばRailsだと、gemfileが新しくなったときにbundle installしたい。
runは新しくコンテナを作成し、内部でコマンドを実行する。サービス名はdocker-compose.ymlから取っている。つまり立ち上がっているコンテナ名は関係ないのに注意。何も指定してない場合、docker-compose.ymlからサービス名を決定する。ほかのファイルの場合には-fオプションが必要。外部で永続化される…volumeが指定されてるような処理(bundle install)とか、データベース関係はいいのだが、その他は永続化されないので注意。
docker-compose run {サービス名} {shellコマンド}
execはコンテナを再利用してコマンドを実行する。高速。
docker-compose exec {サービス名} {shellコマンド}
キャッシュを使わずにbuildする
docker-compose build --no-cache
立ち上げと停止
docker-compose up --build -d # コンテナ作成する docker-compose down
docker外に公開する
Rails Dockerfileで。
CMD bundle exec rails server -b 0.0.0.0
などと書いておくと、外部(Docker外)からアクセスできるようになる。-b 0.0.0.0 がないと別のネットワークからアクセスが不可。コンテナを超えると別のネットワーク扱いになるのでこの記述が必要。
ポート指定する
どっちだったか忘れる。 左が公開、右がコンテナ内。だからブラウザでポート8000アクセスできるようになる。
docker run -p 8000:3000 -it bdd92ace66ec
ログを確認する
docker ps -a # id確認 docker logs 1111... # idを入れる
イメージを削除する
使ってないイメージを削除する。
docker images prone
一気に全部削除する。
docker stop $(docker ps -q) docker rm $(docker ps -aq) docker rmi $(docker images -q)
コンテナの大まかな仕組み
仮想化をどうやっているか、なぜ独立した環境にできるのか知らない。
解説は↓にある。非常にわかりやすい。Goのミニマル実装もある。
dockerの構成。
- Docker Host
- Docker Daemon
- Container
- Images
- Network
- Docker client
- build, pull, runとか
network, container, image, volumesはCli経由でDocker daemonの機能を呼び出す。 コンテナを一言で言うと「 システムから分離されたプロセス 」。Linux上でunshareコマンドを打つことにより、最速でコンテナを作成できる。
$ sudo unshare -u /bin/bash # ユーザがrootになった $ hostname newhost && hostname -> newhost # ホスト名を変更した $ which emacs -> /usr/bin/emacs # unshareしてない状態だとEmacsはguixディレクトリ化に入っているので、確かに環境が別になっている
コンテナに必要なLinuxの機能3つ。
- Namespace
- プロセスはそれぞれでNamespaceを持っている。unshareはプロセスを分離させNamespaceを作成した
- Control Group
- アプリケーションを特定のリソースセットに制限する。メモリの最大利用数や、プロセス最大実行数を制限できる
cat /sys/fs/cgroup/cpuset/cpuset.cpus
- File System
- 親からマウントされたFile Systemに関するデータのコピーを取得し、親と同じデータ構造へのポインタを取得して変更できるようにする
- cat
/proc/mounts
コンテナからホストにコピーする
docker-compose cp が使える。dockerコマンドと違って、コンテナIDを指定する必要がない。
コンテナ → ホストでも、ホスト → コンテナでも、入れ替えて使える。当然、コンテナは前もって起動しておく必要がある。
docker compose cp doc:/usr/share/nginx/html ./
Tasks
TODO About · Container Security Book
コンテナセキュリティの本。
TODO Docker networking is CRAZY!! (you NEED to learn it) - YouTube
動かして学ぶ、Docker networkの解説動画。
docker network ls
NETWORK ID NAME DRIVER SCOPE ce33ed323134 bridge bridge local bf0cec25fb71 docs_default bridge local 13c040755115 host host local 5482a9ce687b none null local
docker run -itd --rm --name test1 busybox docker run -itd --rm --name test2 busybox
c1df10ea18c045e19832b6e5016f7c0b1b08742f38a6af1ad3222cf255165332 89e1be2705a7e08e569cbd7a5fb9b51e2ffd9f26e3b3c4be3effbd09d39c7fc6
↑コマンドではネットワークまわりの設定はないが、自動で追加されている。確認する。
↓ホストマシンから確認する。 veth*
が増えている。
ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: wlp0s20f3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether a0:29:42:f6:35:97 brd ff:ff:ff:ff:ff:ff inet 192.168.0.135/24 brd 192.168.0.255 scope global dynamic noprefixroute wlp0s20f3 valid_lft 3956sec preferred_lft 3956sec inet6 240b:10:91c1:d500:a535:6b03:37ab:c2a/64 scope global temporary dynamic valid_lft 597967sec preferred_lft 79426sec inet6 240b:10:91c1:d500:7c43:5116:5189:7920/64 scope global dynamic mngtmpaddr noprefixroute valid_lft 2591770sec preferred_lft 604570sec inet6 fe80::630:a649:296b:62a6/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:92:8f:f5:2c brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:92ff:fe8f:f52c/64 scope link valid_lft forever preferred_lft forever 4: br-bf0cec25fb71: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:68:39:3c:cb brd ff:ff:ff:ff:ff:ff inet 172.19.0.1/16 brd 172.19.255.255 scope global br-bf0cec25fb71 valid_lft forever preferred_lft forever 1164: vethbd4a2ba@if1163: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master # 👈 docker0 state UP group default link/ether 3e:4a:f3:57:3c:f6 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::3c4a:f3ff:fe57:3cf6/64 scope link valid_lft forever preferred_lft forever 1166: vethd4e3cec@if1165: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master # 👈 docker0 state UP group default link/ether d2:1c:e5:39:4b:7a brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::d01c:e5ff:fe39:4b7a/64 scope link valid_lft forever preferred_lft forever
bridge link
1164: vethbd4a2ba@if1163: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2 # 👈 docker0 1166: vethd4e3cec@if1165: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2 # 👈 docker0
ブリッジをさらに詳しく見る。
docker inspect bridge
[ { “Name”: “bridge”, “Id”: “ce33ed32313474bcb0aa3f914152fd0c1df57c1e1fadc740a6fd16d6f91d4637”, “Created”: “2024-02-28T00:10:39.317309307+09:00”, “Scope”: “local”, “Driver”: “bridge”, “EnableIPv6”: false, “IPAM”: { “Driver”: “default”, “Options”: null, “Config”: [ { “Subnet”: “172.17.0.0/16”, “Gateway”: “172.17.0.1” } ] }, “Internal”: false, “Attachable”: false, “Ingress”: false, “ConfigFrom”: { “Network”: “” }, “ConfigOnly”: false, “Containers”: { “89e1be2705a7e08e569cbd7a5fb9b51e2ffd9f26e3b3c4be3effbd09d39c7fc6”: { “Name”: “test2”, “EndpointID”: “7464bc9f1771de61da18e637390233e9146e2d8a0fdb7fd2822c4358534b8a96”, “MacAddress”: “02:42:ac:11:00:04”, “IPv4Address”: “172.17.0.4/16”, # 👈 “IPv6Address”: “” }, “c1df10ea18c045e19832b6e5016f7c0b1b08742f38a6af1ad3222cf255165332”: { “Name”: “test1”, “EndpointID”: “87edd7c730152b05cc16931305b5178a1a26d6649c462d405b700a4071afbe38”, “MacAddress”: “02:42:ac:11:00:03”, “IPv4Address”: “172.17.0.3/16”, # 👈 “IPv6Address”: “” } }, “Options”: { “com.docker.network.bridge.default_bridge”: “true”, “com.docker.network.bridge.enable_icc”: “true”, “com.docker.network.bridge.enable_ip_masquerade”: “true”, “com.docker.network.bridge.host_binding_ipv4”: “0.0.0.0”, “com.docker.network.bridge.name”: “docker0”, “com.docker.network.driver.mtu”: “1500” }, “Labels”: {} } ]
↑ docker0
ネットワーク内に追加されている。
ホストネットワークで起動する。
docker run -itd --rm --network host --name test3 nginx
0574b61623240c3ce524c44bba37e85386a2052c64811a06bd96dd80ea9cc98a
ポートを公開することなく、ホストマシンからアクセスできる。ネットワークは隔離されない。
curl -I http://localhost curl -I http://172.17.0.1
HTTP/1.1 200 OK Server: nginx/1.25.4 Date: Sat, 02 Mar 2024 04:55:15 GMT Content-Type: text/html Content-Length: 615 Last-Modified: Wed, 14 Feb 2024 16:03:00 GMT Connection: keep-alive ETag: “65cce434-267” Accept-Ranges: bytes
HTTP/1.1 200 OK Server: nginx/1.25.4 Date: Sat, 02 Mar 2024 04:55:15 GMT Content-Type: text/html Content-Length: 615 Last-Modified: Wed, 14 Feb 2024 16:03:00 GMT Connection: keep-alive ETag: “65cce434-267” Accept-Ranges: bytes
dockerドキュメントのタイポ修正
- Docker ドキュメント日本語化プロジェクト — Docker-docs-ja 24.0 ドキュメント
- 特定のユーザに割り当てられたほ場的なグループと共に実行されます
- 議事 tty(pseudo-tty)の割り当て
TODO introduce require to load sub-compose projects as dependencies by ndeloof · Pull Request #416 · compose-spec/compose-go
気になる機能追加。-fオプションの上書きは、わかりづらい。
TODO Docker Compose入門 (3) ~ネットワークの理解を深める~ | さくらのナレッジ
docker networkの解説。
TODO Docker と LXC - Qiita
コンテナのLXCとはなにかを解説。
TODO Dockerコンテナの/var/lib/docker/overlay配下の容量が大きくなって起動できない事象に遭遇したので周辺知識を調べた。 - 蚊帳の中の日記
overlayのわかりやすい説明。事象を理解するためには、仕組みを理解していなければいけない。
TODO docker ignoreの仕組み DontKnow
どうやってignoreしているのだろうか。
https://github.com/kd-collective/buildkit/blob/37d54ebc592a54db8764911eb320d02d2260c5e6/frontend/dockerfile/dockerignore/dockerignore.go#L13
// ReadAll reads a .dockerignore file and returns the list of file patterns
- ファイルを読み込み、パスのスライスを出しているだけ
TODO Golang UK Conf. 2016 - Liz Rice - What is a container, really? Let’s write one in Go from scratch - YouTube
コンテナランタイムを使わずにGoでコンテナを作ることで、コンテナとは何かを学ぶ。
Archives
DONE Docker Swarmで学ぶサービスメッシュ - Qiita
swarmを学ぶ。
DONE タスクを簡単に実行する方法を調べる
Emacs拡張あるいは、Makefile的なのにまとめる。
ありがちなbundle-installなどはdocker-composeにワンショットのコマンドを書くことで、定形コマンドを実行することが少なくなった。自動で動かしたいやつはこれでOK。コマンドはdockerだから特殊ということはなく、ローカルと同じようにやれば良い。
DONE 誤字を修正する
用語集 — Docker-docs-ja 20.10 ドキュメント PRを送る。
- なお、オリジナルのドキュメントは群は
- ビルド(build)とは、 を使って Docker イメージを構築する工程です。
- イメージ構築に必要なディレクトリに置いてあるファイル群です
- ために、 コピーオンライト 技術と を使います
- ベストな解決作です。
- ENTRYPOINT` に /bin/sh ま
- ユニオン・ファイル・システムで結語するために 技術を使い
DONE ゴミファイルができないようにする
とりあえず、👇でよい。
sudo chown -R $USER:$USER .
キャッシュや履歴関係がroot権限でできるので、削除が面倒+コンテナを作るのが邪魔される。
- できないようにする
- 自動削除するようにする
DONE Rails開発 Docker環境化[9/9]
仕事をLinuxで行えるようにする。基本的なところはカバーしたが、一部できないものがある状態。
CLOSE rails c内で日本語が含まれると失敗する
何かおかしくなる。
CLOSE CapybaraでJavascriptをオンにしたときsystem specが失敗する
js: trueのときだけ。
DONE migration時にschemaに変な差分が出る
DB設定がおかしいようだ。
DONE 非同期処理の動作確認
redis, sidekiqが本当に動いてるかわからない。 letter openerを見る限り、できてない。
追加した。
DONE dockerがrootユーザでファイルを生成する問題
生成したファイルがroot権限になってしまう。 だからbundle installを実行すると、その後は通常ユーザでは編集できなくなる。 面倒だし、migrationとか明らかにダメな気がする。
簡単な解決策と環境変数によって解決する方法を調べた。
DONE 基本コマンド
Rails部分をDocker化する。表示はまったく問題なさそう。 リロードするとちゃんとローカルの変更が反映される。
最初にルートファイルのdockerfileでベースイメージをビルドして、名前を付ける。
docker build . -t app
各コンテナでは↑で作成したベースイメージappを用いる。
イメージを使う代わりに build .
でも可能だが、各コンテナがイメージをビルドする(中身は同じ)ので遅くごちゃつく。
rails: image: app environment: RAILS_ENV: development REDIS_URL: redis://redis:6379 MEMCACHED_URL: memcached://memcached:11211 SKIP_RECAPTCHA: "true" MEMCACHED_HOST: memcached MEMCACHED: memcached:11211 WEBPACKER_DEV_SERVER_HOST: webpack CHROME_HOST_NAME: http://selenium_chrome:4444/wd/hub ports: - 3000:3000 stdin_open: true tty: true command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'" volumes: - .:/rails - /etc/passwd:/etc/passwd:ro # Linux用 - /etc/group:/etc/group:ro # Linux用 depends_on: - mysql sidekiq: image: app command: bundle exec sidekiq links: - mysql - redis webpack: image: app environment: NODE_ENV: development RAILS_ENV: development WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 command: yarn watch volumes: - .:/rails - /etc/passwd:/etc/passwd:ro # Linux用 - /etc/group:/etc/group:ro # Linux用 ports: - 8080:8080
sudo docker-compose up --build
docker-compose {service} restart
docker-compose run rails bundle exec rails c
docker-compose run rails bundle install
docker-compose run rails bundle exec bin/rspec spec/requests/top/top_spec.rb
docker-compose run rails /bin/bash
DONE docker-compose.ymlのオーバーライド
個人で微妙に設定が異なることもある。 Dockerでやるのはミドルウェアだけとか、Railsもすべてやる、といったような。 そのときはgitignoreを指定したymlを指定して起動する。
docker-compose -f docker-compose.yml -f docker-compose-app.override.yml up
もちろん一般性があるならgit管理にするのがベストだが、人によって構成が異なるので仕方ない。とくにMacだと速度に問題あるため、RailsはDockerで立ち上げないのが多数派。
Railsサービスをoverride.ymlに、それ以外のミドルウェアサービスをdocker-compose.ymlに書いてる場合は、明示する必要がある。
docker-compose -f docker-compose.yml -f docker-compose-app.override.yml run rails bundle install
docker-compose runする場合も-fオプションが必要。 runはコンテナを新しく作る…つまりymlを見てるので、指定が必要なのである。
docker-compose -f docker-compose.yml -f docker-compose-app.override.yml exec rails bundle exec rspec --options ./.rspec ./spec/models/user_spec.rb
↑いちいちクソ長いコマンドを打つのは苦痛なので、shellに入って作業すると楽。
sudo docker-compose -f docker-compose.yml -f docker-compose-app.override.yml run rails /bin/sh
DONE DBのGUIツールとの接続
Linux用のsqlectronがよさそう。が、上手くMySQLと接続できない
docker-compose.ymlで MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
を追加すると入れるように。
パスワードを指定してるとログインできない。
だがこのsqlectron、表示テーブルでの編集ができないので値を書き換えるのに非常に不便。 別のを使ったほうがいいだろう。
DONE yarnができてない
- ポートを合わせる
- webpack.config.jsにhostを加える
が必要。
webpack: build: . environment: NODE_ENV: development RAILS_ENV: development WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 command: yarn watch volumes: - .:/rails ports: - 8080:8080 depends_on: - rails
ホットリロードできるのを確認。 hostを加える必要があった。
devServer: { contentBase: path.join(__dirname, 'app/assets/javascripts'), allowedHosts: ['.lvh.me'], host: '0.0.0.0', },
DONE Linux Container Book【委託】 - 達人出版会
コンテナの解説。後半は理解できてない。また必要なときに読む。
$ sudo mount --bind dir1 dir2
みたいに、バインドマウントするコマンドが存在する- Namespace(名前空間)はプロセスをグループ化して、コンテナの隔離された空間を作り出す。独立させたいリソースによっていくつかの機能がある
- Dockerの初期はコンテナ内でコマンドを実行できなかった
- カーネルでsetnsがすべてのNamespaceに対して動作するようになってから、docker execコマンドが実行できるようになった
- Mount NamespaceはLinuxカーネルに最初に実装されたNamespace(2002年)
- あるNamespaceごとに異なるマウントポイントの一覧を持てる
- コンテナ内でマウント操作を行った場合でも、そのマウントはホストOSや他のコンテナから見えないようにできる
$ cat /proc/self/mounts
でマウント状況を確認できる- マウントプロパゲーション
- マウントがほかのディレクトリで反映されるか、反映されないか
References
opencontainers/runtime-spec: OCI Runtime Specification
コンテナランタイムの標準仕様。
opencontainers/image-spec: OCI Image Format
コンテナイメージの標準仕様。
コンテナの仕組み(Linux学習) - YouTube
コンテナの解説。
カーネルの変更がコンテナにどう影響しそうだな、という視点すごいな。
複数のdocker-compose間で通信する
docker networkを作り、コンテナを同じnetworkに所属させると、サービス名解決ができる。
docker-library/buildpack-deps
Dockerの公式イメージ集。
Add health start interval by cpuguy83 · Pull Request #40894 · moby/moby
ヘルスチェックのスタートを指定するオプションを追加するプルリク。
Improved debugging support · Issue #1472 · moby/buildkit
デバッガーモードの提案。気になる。
allow removing something in docker-compose.override.yml · Issue #3729 · docker/compose
-fオプションで起動するとき、要素によっては上書きされない問題がある。例えばポートをoverride.ymlに書くと上書きはされず、2つともポート公開されてしまう。その仕様の変更が7年かかって仕様に組み込まれた。
introduce remove to configure runtime autoremove for service containers by ndeloof · Pull Request #364 · compose-spec/compose-spec
removeキーワードの導入。
Docker Engine API v1.42 Reference
Docker EngineのAPIリファレンス。
個人的docker composeおすすめtips6選 - Qiita
tips。
- ヘルスチェック
- サービスをグループ化
Extension fieldsを使ってdocker-composeのコンテナ設定を共通化する
共通化設定。
x-
をサービス名につけると無視される
Haxxnet/Compose-Examples: Various Docker Compose examples of selfhosted FOSS and proprietary projects.
docker-compose集。
GitHub Actionを使って自前Docker内で自動テスト - Qiita
github actionsでdocker-composeを使う例。
Build Containers the Hard Way (WIP) - Build Containers the Hard Way
コンテナ技術の低レイヤーの仕組み。
docker-slim/docker-slim: DockerSlim (docker-slim): Don’t change anything in your Docker container image and minify it by up to 30x (and for compiled languages even more) making it secure too! (free and open source)
dockerイメージを分析してスリムにするツール。
wagoodman/dive: A tool for exploring each layer in a docker image
dockerのレイヤーごとにイメージを調査できるツール。
Docker とは - 解説、メリット、できること | Red Hat
わかりやすい概要。
Docker - Wikipedia
ソフトウェアのわかりやすい説明。
phusion/passenger-docker: Docker base images for Ruby, Python, Node.js and Meteor web apps
Web開発用の扱いやすいDockerイメージ。
The Twelve-Factor App
SaaS開発の方法論。 日本語訳もあった。The Twelve-Factor App (日本語訳)
Docker ドキュメント日本語版 PDF ダウンロード — Docker-docs-ja 19.03 ドキュメント
Dockerのドキュメント。
Dockerfileを改善するためのBest Practice 2019年版
ベストプラクティス。
Backlinks
- Emacs
- Rails
- GNU Guix
- project
- Docker
- History
- Go
- dotfiles
- Kubernetes
- GitHub
- 100knocks
- deploy
- ECS
- KDOC 16: 2022年のまとめ
- Mermaid
- KDOC 25: docker progress を読む
- KDOC 74: The input device is not a TTYを理解する
- KDOC 76: コンテナでLocaleを設定する
- KDOC 104: やりたいことが多すぎる
- KDOC 139: 『Googleのソフトウェアエンジニアリング』
- KDOC 152: Dependabotが作ったPRでワークフローが失敗する理由
- KDOC 183: イメージビルド時にツール側のキャッシュを使う
- Insomnia