初心者の状態から、既存サービスをDocker化するまでに学んだ知識のメモです。
※ 随時更新していきます
検証環境 | Version |
---|---|
Docker | 18.09.1 |
Docker Compose | 1.23.2 |
Compose file format | 3.0 |
以下の内容は、他の記事で分かりやすく説明されているのでここでは省きます。
- Docker / Docker Compose のインストール方法
- 基本的な使い方(といっても、私が知らなかっただけで一般的な使い方を挙げているかもしれませんがそこはご了承ください
また、dockerコマンドオプションとdocker-compose.ymlのオプション指定は適宜読み替えてください。
では早速行きましょう!
Docker コマンド
1. build 時にキャッシュを使わない
Dockerfileを編集しても内容が反映されないときは試してみてください。
$ docker build -t hoge:v1 . --no-cache
2. <none>:<none> の image のみ抽出する
これまで grep
使ってましたが、ちゃんとドキュメントにありました。
$ docker images --filter "dangling=true"
↑を使って消す時は、
$ docker rmi $(docker images --filter "dangling=true" --format {{.ID}})
# もっと簡単に
$ docker rmi $(docker images --filter "dangling=true" -q)
3. コンテナ内のタイムゾーンを設定する
環境変数のTZ
にAsia/Tokyo
を設定すればOKです。
$ docker run -it -e TZ=Asia/Tokyo ruby:2.4 bash
Dockerfileに書いてももちろん問題ありません。
4. -it
オプションってなに?
次のようなコマンドをよく見ます。私も大抵はこう使います。
$ docker exec -it [コンテナID] bash
この -it
オプションは、-i
と-t
をまとめたもので、それぞれのざっくりな意味合いは
-
-i
:コンテナ内の標準出力とホスト側の出力をつなげる -
-t
:ホスト側の入力をコンテナの標準出力につなげる
だそうです。試してみましょう。
$ docker run -i ruby:2.4 bash
(なにも操作ができない
$ docker run -t ruby:2.4 bash
root@05cda9646908:/#
(コンテナには入れるが、入力しても表示されない
コンテナ内でゴニョゴニョしたい場合は、2つのオプションをセットで使う必要があります。
5 --no-trunc
をつけると、情報を省略せずに表示される
--no-trunc
なし
$ docker ps --format "{{.Command}}"
"irb"
"docker-entrypoint.s…"
"drakov -f api.md -p…"
"/usr/local/bin/run …"
"/usr/local/bin/run …"
"/usr/local/bin/run …"
"docker-entrypoint.s…"
"docker-entrypoint.s…"
--no-trunc
あり
$ docker ps --format "{{.Command}}" --no-trunc
"irb"
"docker-entrypoint.sh mysqld"
"drakov -f api.md -p 3031 --autoOptions --public"
"/usr/local/bin/run npm run start"
"/usr/local/bin/run bundle exec sidekiq -q default -q mailers -q pull -q push"
"/usr/local/bin/run bundle exec rails s -p 3000 -b 0.0.0.0"
"docker-entrypoint.sh postgres"
"docker-entrypoint.sh redis-server"
Dockerfile / docker-compose.yml
6. RUNを多用する時は
一行ごとにRUNを書くのではなく、次のようにつなげて書きましょう。
できるだけRUNを呼ぶ回数を減らすことで、可読性・メンテナンス性が向上します。また、buildのStep数削減で多少の時間短縮ができます。
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
curl \
git \
zlib1g-dev \
libssl-dev \
libreadline-dev \
libyaml-dev \
libxml2-dev \
libxslt-dev \
libmysqlclient-dev
7. 不要な命令は減らす
Dockerfileで一通り動作する環境が出来たら、次は最適化していきましょう。
Quickstart: Compose and Rails を見ると、こんな記述があります。
RUN mkdir /myapp
WORKDIR /myapp
myappディレクトリを作成して、作業用ディレクトリとするためcd myapp
をした感じです。
このWORKDIR
ですが、指定するディレクトリがもし存在しない場合は作成してくれますので、
WORKDIR /myapp
だけでよいことになります。
8. ADDとCOPYってどう使い分けるの?
基本的にはローカルのファイルをコンテナにコピーする役割で使用します。
ただし、
- ADD: コピーするソースが
gzip、bzip2、xz
の場合、指定ディレクトリに展開される - COPY:ただコピーするだけ
という違いがあります。
使い分けとして、以下のように何をする命令なのか明確にする
という点で使用するといいと思います。
ADD db_data.tar.gz /tmp/dump/
COPY Gemfile /app/Gemfile
9. EXPOSEって必要?
弊社で開発環境をDocker化しているリポジトリを覗いてみると、7割のDockerfileで、EXPOSEが記述されていました。
公式でEXPOSEを見てみると、
EXPOSE 命令は、特定のネットワーク・ポートをコンテナが実行時にリッスンすることを Docker に伝えます。 EXPOSE があっても、これだけではホストからコンテナにアクセスできるようにしません。
とあります。
つまり、この命令で何かが実行されるわけではありませんが、どのポートに接続するのかを明示しておく
、という点で書いておいたほうが親切かもしれません。
10. RUNとCMDの違い
先ほどもでてきたRUN
ですが、これはシェル形式でコマンドを実行するものであり、Dockerfile内に複数回出てきても問題のないものです。
それに比べてCMD
は、Dockerfileには原則1つのみ記述します。
もし複数の記述がある場合は、最後のCMD
が実行されます。
また、CMD
は、コンテナ実行時のデフォルトを提供するそうです。
例として、Rubyの公式イメージを使用してみます。
$ docker run -it ruby:2.4
irb(main):001:0>
irb
の待受状態になりました。
これは、Rubyの公式イメージを見るとわかりますが、Dockerfileの最後に
CMD [ "irb" ]
と書いてあるためです。
RUNとCMDの使い分けは、Railsのアプリケーションで例えると、
RUN bundle install \
&& rails db:create \
&& rails db:migrate
CMD ["rails", "s", "-b", "0.0.0.0"]
という感じでしょうか。
これでコンテナが実行されるとき、同時にRails Serverが起動します。
11. DBコンテナであらかじめユーザを作成しておきたい
MySQLやPostgreSQLを使用する時、事前にDBやユーザを作成しておきたい、なんてことがあると思います。
その時は、あらかじめ拡張子が.sh
または.sql
のファイルを用意し、/docker-entrypoint-initdb.d/
ディレクトリへ設置しておくだけで、コンテナ実行時に自動で行ってくれます。
具体的にはこんな感じです。
psql -v ON_ERROR_STOP=1 -U postgres <<-EOSQL
CREATE USER hoge_user WITH CREATEDB;
CREATE DATABASE hoge_development OWNER hoge_user;
EOSQL
FROM postgres:9.3.16
COPY init.sh /docker-entrypoint-initdb.d/
ファイルの実行順を指定したい場合は、1_init.sh
、2_restore.sql
とすればOKです。
12. cachedを使う
マウント遅い問題ですが、Docker 17.04.0-ce
からcached
が使えるようになり、多少は改善されたようです。
app:
build: .
volumes:
- .:/myapp:cached
ただし、読み込みは速くなったものの、ホストからの書き込みがコンテナ内で反映されるまで時間がかかる場合もあるそうです。
ちなみに、cachedの他にもconsistent
とdelegated
が追加されていました。
- consistent:ホストとコンテナの整合性を保つ(デフォルト)
- delegated:コンテナ内でのマウントは速いものの、コンテナで行われた書き込みがホストからの読み込みに遅延が生じることがある
私は基本的にcached
指定ですが、適宜変更していく形がいいかと思います。
13. 複数のDockerfileをディレクトリに含める
複数のDockerfileを扱おうとすると、リポジトリ直下が汚くなったり、Dockerfileの名前をDockerfile.db
として扱うのはちょっとかっこ悪いので、例えば.docker
というディレクトリにまとめて管理したいとします。
version: '3'
services:
web:
build:
context: .
dockerfile: .docker/containers/app/Dockerfile
...
buildのcontext
とdockerfile
を指定します。
contextを変えれば、docker-compose.yml
も.docker
ディレクトリに含めることも可能ですが、その場合はdocker-compose
コマンドでファイルオプションをつける必要があるため、ymlだけはディレクトリ直下に置いています。
14. EC2でDockerコンテナ内のcronが動かない
ローカルでは問題なく動いていたのに…
詳しくは http://tanksuzuki.com/post/docker-pam-error/ をご覧ください。
解決方法として、docker-compose.yml
を以下のように修正します
app:
build: .
cap_add:
- audit_control
...
15. RUN
命令のキャッシュ
RUN、COPY、ADD
コマンドではレイヤを作成し、RUN
は文字列が変わらない限りレイヤーを cache します。
COPY
ADD
したファイルはチェックサムを計算し、以前と異なる場合は、それ以降の RUN
の cache が破棄されるそうです。
よって、Lockfile などを先にコピーしておくことにより、効率的にレイヤーキャッシュを活用できるそうです。
...
# Gemfile, Gemfile.lock が更新されない限り、bundle install はキャッシュされる
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
16. 意図しない改行を防ぐ
コンテナ内のコンソールを操作中に意図しないところで改行してしまうことがあります。
そのときは、以下の環境変数を設定しましょう。
$ docker-compose exec -e COLUMNS=$COLUMNS -e LINES=$LINES -e TERM=$TERM app bash
その他
17. no space left on device
docker-compose build
をすると、さっきまで何もなかったのに、急に以下のメッセージで通らなくなってしまうことがあります。
... no space left on device
これは、VMの容量が足りないときに発生するようです。
そんなときは、使用していないイメージとコンテナを削除してみましょう。
$ docker rm CONTAINER [CONTAINER…]
$ docker rmi IMAGE [IMAGE...]
18. Docker関連をまっさらに
イメージ、コンテナ、ボリュームをキレイに消せる便利なコマンドがあります。
https://github.com/ZZROTDesign/docker-clean
ただ、Dockerのステータスを理解しておかないと望んでいないコンテナを削除してしまう恐れもある(経験あり)ので、使用には気をつけてください。
[追記] @AkihiroSuda さんよりコメントいただきました
$ docker system prune
というものがあるそうで、実際に試してみると
$ docker system prune --all
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all build cache
Are you sure you want to continue? [y/N]
.
.
.
Total reclaimed space: 14.8GB
と親切に警告も出してくれます。スッキリしました。
おわりに
まだまだ勉強中のため、「お、そこ違うかもよ?」という点があればいつでもコメントお待ちしております!
誰かのお役に立てば幸いです。