Dockerのimageとは
Dockerのimageとはある実行環境のスナップショットみたいなもので、これを共有することで環境構築を簡単にすることができる。
dockerのimageはdockerhubというサイトから様々なものをインストールすることができる。
Dockerfileとは
自分でimageを作成するときに使うファイル。vimとかでも書くことができる。
1 FROM ubuntu
2 COPY hello.txt /tmp/hello.txt
3 CMD ["cat", "/tmp/hello.txt"]
4
こんな感じのファイル。
catコマンドとは
一番よく使われるのはファイルの閲覧。
$ cat fileA
このようにして使う。
buildとは
docker buildコマンドではDockerfileからdockerimageを作成する。
次のコマンドをみてみよう
$ docker build -t hello .
-t hello オプションは「Docker Imageを hello という名前にする」という意味です。
. はdocker build 実行時のコンテキストの指定です。 . は COPY コマンドを実行する際にどのディレクトリを起点とするかを指定します。(入門dockerより)
docker images
インストールしたり作成したimageはdocker imagesというコマンドで確認することができる。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello latest 98d66d64d276 13 seconds ago 64.2MB
ubuntu latest ccc6e87d482b 28 hours ago 64.2MB
nginx latest c7460dfcab50 7 days ago 126MB
ruby latest fb53c5f433da 10 days ago 842MB
python 2.7 426ba9523d99 2 weeks ago 896MB
docker buildで作成したdocker imageを実行してみよう
$ docker run hello
hello docker !
実行中のコンテナを確認しよう
docker container ls -aで確認することができる。
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
429c028bcf74 ubuntu "echo hoge" 16 seconds ago Exited (0) 15 seconds ago stoic_greider
実行中のコンテナを削除しよう
docker image prune -aで削除することができる。
$ docker image prune -a
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N] y
Deleted Images:
Dockerhubから作成したimageの取得をしよう
$docker pull yokotadaiki/hello
これでdocker run yokotadaiki/helloを実行できるようになった。
Dockerfileの基本的なコマンド
基本的なコマンド¶
個人的に、Dockerfileを本番のワークロードで使用する場合以下の7つのコマンドを覚えるだけで十分だと考えています。
FROM , COPY , RUN , CMD , WORKDIR , ENV , USER
(入門dockerより)
FROM
ベースとなるDocker Image を指定します。
DockerはベースとなるDocker Image の上に COPY や RUN のようなコマンドを重ねてDocker Image を作成します。
ベースとなるDocker Image は公式で提供されているImageを使用するのが一般的です。
また、ここで設定したイメージを「ベースイメージ」と呼びます。
ENV
Docker内で使用する環境変数を定義します。
NODE_ENV のようなDockerの起動時にデフォルトで定義されていてほしい環境変数を定義すると良いでしょう。
ENV NODE_ENV=production
WORKDIR
Dockerfileでコマンドを実行する際に基準となるディレクトリを設定します。
このとき存在しないディレクトリを指定すると自動的にディレクトリが作成されます。
デフォルトだと / が設定されているため、最悪の場合既存のディレクトリを上書きしてしまいコンテナが起動しなくなります。
WORKDIR /scripts
COPY
Docker内へホストのファイル/ディレクトリをコピーします。
COPY は基本的に2つの引数を設定します。1つ目はホスト側のディレクトリ、2つ目はDocker側のディレクトリです。
ホスト側のディレクトリは docker build . で指定したディレクトリです。この場合 . を指定しており、カレントディレクトリが参照されます。
Docker側は WORKDIR で定義されたディレクトリを参照します。
RUN
Docker内でコマンドを実行します。
ここでコンテナへ依存するライブラリやパッケージのインストールやユーザーの設定などの処理を実行します。
RUN npm install \
&& groupadd app \
&& useradd -g app app \
&& chown -R app:app /scripts
USER
作成したDocker Image を起動時にログインするユーザーを指定します。
デフォルトは root が設定されているため、セキュリティリスクを回避するために別のユーザーを指定するのが良いでしょう。
USER app
CMD
Docker起動時にデフォルトで実行されるコマンドを定義します。
Dockerはここで設定したコマンドがフォアグラウンドで実行されている間が生存期間になります。
そのため、プロセスの処理が走っている間はフォアグラウンドで実行するように記述します(バックグラウンドで起動するとDockerが終了してしまう)。
CMD ["npm", "run", "start"]
EXPOSE
コンテナ起動時に公開することを想定されているポートを記述します。
EXPOSE を記載することで他の人から「このDockerはポートをどの使用するのか」がわかりやすくなるため、記述すると丁寧でしょう。
VOLUME
Data Volumeを作成するためのコマンドです。Volumeについては後の章で説明します。
永続的なデータや共有するためのデータ、更新頻度の激しいファイルを扱うために使用されます。
基本的に永続的なデータはDockerで管理することは推奨されないため、基本的にログのような更新頻度の激しいファイルで使用すると良いでしょう。
VOLUME ["/app/log"]
Docker Containerとは
Docker Image がスナップショットだとしたらDocker Container はその 「スナップショットから起動したプロセス」 です。
より具体的にいうと docker run を実行するとDocker Image をもとにしてDocker Containerが作成され、隔離された環境が作成されます。
Docker Container は Docker Imageを元にして作成され、リソースの許す限り立ち上げることができます。
意識する点として、Docker Container は1つのコマンドをフォアグラウンドで動かすように設計されていることです。
Docker Containerは1つのコマンドを隔離された環境で実行し、そのコマンドの実行がフォアグラウンドで終了するまで生存します。
Dockerのライフサイクル
Dockerコンテナは大きく5つの状態に分けられる。
image
指定したdocker imageからコンテナを起動する。
RUNNING
Docker Containerが起動した状態です。
Dockerfileの CMD もしくは ENTRYPOINT で指定したコマンドがフォアグラウンドで動いている間がRUNNINGの状態です。
例えば docker run -P nginx のようにnginxを起動した場合、nginxが起動してアクセスを待ち受けてる間はRUNNINGの状態となります
STOPPED
起動したContainerが終了した状態です。
正常終了・異常終了、どのような形であっても終了したContainerはSTOPPEDへ遷移します。
PAUSED
Containerが停止した状態です。
ユーザーが docker pause を実行すると、現在の状態を保持して一時停止します。
docker unpause で一時停止したコンテナIDを指定することで再開することが可能です。
ユーザーが明示的に指定しない限りこの状態へは遷移しません。
DELETED
Docker Container は明示的に削除を行わない限り停止した状態で残り続けます。
docker rm で明示的に削除するとDELETEDの状態へ遷移し、削除されます。
ここまでの重要なポイント
・コンテナは隔離された環境で実行される
・Dockerコンテナは1つのコマンドを実行してプロセスを実行する
・基本的に「1コンテナ1プロセス」
・ライフサイクルは実行されたコマンドがフォアグラウンド上で実行されている期間
・バックグラウンドでプロセスが立ち上げられていてもフォアグラウンドでコマンドが終了したらコンテナは停止する
Networkを利用する
dockerコンテナは、1コンテナ1プロセスが基本だった。
そのため、railsとmysqlなど複数のコンテナを協調して動作させるにはnetworkを利用する必要がある。
$ docker network ls
このコマンドで現在dockerが管理しているnetwork一覧を出力することができる。
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
3d295ce0594e bridge bridge local
0539a1b864dd host host local
30c3217049fb none null local
次は新しいBRIDGEネットワークを作成しよう。
$ docker network create myapp
6dbd1262d023114e349e2f12e843044327cc2d870bd41cc4d74103ad68d24725
作成したnetworkにnginxを参加させる
通信を受けるためのサーバーとしてnginxを構築します。
$ docker run --name nginx --network=myapp -d nginx
AmazonLinux2を起動し、Nginxコンテナへ接続する
Bridgeネットワークの場合、同一ネットワークのコンテナにはコンテナ名で名前解決が可能です。
Nginxと疎通できるか myapp ネットワーク内にAmazonLinux2 イメージでコンテナを起動し、 curl を実行してみましょう。
$ docker run --network=myapp -it amazonlinux:2 curl nginx:80
ここまでの重要ポイント
・1プロセス1コンテナ、復数プロセスはネットワークを通して通信を行う。
・Bridgeを基本的に使用する。
Volumeとは
ボリュームはデータを永続化するための機能です。
Docker Containerは基本的にエフェメラルなもので、コンテナ上で作成されたファイルはコンテナのライフサイクルの終了と共に消えてしまいます。
Volumeはデータ保持・永続化のために設計されており、コンテナのライフサイクルとは独立してファイルの管理を行います。
ここは難しい。。。
デバッグ(dockerの動作確認方法)
そもそも、dockerは実行されるたびに新しいコンテナが立ち上がる。そのため、dockerimageが1つでも複数のコンテナが立ち上がっているという状況がある。
コンテナの状態を確認するコマンドとして
$ docker container ls -a
としてきた。
ちなみに実行中のコンテナだけ確認したいときは
$ docker container ls
とすれば良い。
コンテナの停止~imageの削除
### コンテナの停止
$ docker stop $(docker container ls -q)
### コンテナの削除
$ docker rm $(docker container ls -aq)
### imageの削除
$ docker rmi $(docker images -q)
Dockerfileのベストプラクティス
例えばPHPの環境を構築するのにCentOSのベースイメージで、phpenvを入れて、MySQLを入れて、、といったことは非推奨です。
DockerはいままでのVMとは思想が異なります。1コンテナ1プロセスになるように設計を行いましょう。
復数のプロセスを使用したい場合はそれぞれコンテナに分け、オーケストレーションツールを使用してコンテナを協調させて動かしましょう。
.dockerignoreを使う
Dockerのビルド時に無視するファイル/ディレクトリを指定することができます。
".git" のようなコンテナ内に不要な情報、 "node_modules" のような上書きされると困るものを記述します。
.dockerignore は基本的に .gitignore と同じ書き方が可能です。
ここも難しい。
オーケストレーションツール
複数のDockerを動かすためのツール。
docker-composeもその中の1つだと思う。
docker-composeについて
docker-composeはローカルでDockerのオーケストレーションを行うためのツールです。
DockerのビルドからNetworkやVolumeの管理をコードベースで定義して行ってくれます。
docker-composeはDockerの構成をyamlを定義し、そのyamlを元に起動します。
例えばnginxを起動し、ホストの8080ポートへコンテナの80ポートをフォワードする設定は以下のyamlになります。
例えば以下のようなファイル
version: '3.7'
services:
nginx:
image: nginx
ports:
- 8080:80
docker run -p 8080:80 nginx とほぼ同じ動きをします(異なる点としては、docker-composeでは専用のNetworkを作成・使用する点です)。
単純なnginxの起動であれば素のdockerコマンドで問題ありませんが、ここにPHP, MySQL...と増えていくとその威力を発揮します。
つまり、数が増えるほどymlファイルにまとめて記載してdocker-composeでまとめてコンテナを立ち上げた方が簡単じゃない?って話だと思う。
Lalavelのサンプルで雰囲気を掴む
dockerでLalavelをのサーバーを起動する。
$ git clone https://github.com/y-ohgi/introduction-docker.git
$ cd introduction-docker/handson/laravel
$ docker-compose up
この状態はrailsでいうrails sを実行したのと同じ。
docker-compose.ymlを読む。
version: '3.7'
services:
nginx:
build:
context: .
dockerfile: docker/nginx/Dockerfile
volumes:
- ./public:/var/www/html/public:ro
ports:
- 8080:80
environment:
PHP_HOST: app
app:
build:
context: .
dockerfile: Dockerfile
env_file:
- .env.example
# volumes:
# - .:/var/www/html:cached
mysql:
image: mysql:5.7
volumes:
- ./mysql:/var/lib/mysql:delegated
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
ports:
- 13306:3306
version
docker-composeのバージョンを指定する。
特にこだわりがなければ最新のものを。
services
起動するコンテナの定義を行います。
このdocker-compose.yamlでは nginx , app , mysql の3つが定義されています。
build
docker buildの実行情報を記述します。
ここで定義された情報を元にDockerをビルドし、そのビルドしたイメージ使用してコンテナを起動します。
image もしくは build どちらかを記述する必要があります。
コマンドの場合、 docker build -f docker/nginx/Dockerfile . と同一です。
volumes
ボリュームのマウントを行います。 コマンドの場合、 -v $(pwd)/public:/var/www/html/public:ro オプションと同一です。
ports
ポートの開放を行います。
左にホストのポートを、右にコンテナのポートを指定します。
コマンドの場合、 -p 8080:80 オプションと同一です。
command
Dockerfileで定義されている CMD の上書きを行います。
Docker-composeのコマンド集
1.カレントディレクトリに存在する docker-compose.yaml を参照してdocker-composeの起動
$ docker-compose up
2.カレントディレクトリの docker-compose.yaml に紐付いているContainerとNetworkを削除
$ docker-compose down
3.Imageも削除
$ docker-compose down --rmi all
4.volumeを削除
$ docker-compose rm
プロダクションへの導入
新規開発なら最初からDockerで開発していきましょう。
既存のVMからの移行であればステップを踏んで行くのが良いでしょう。
1.ローカル環境のDocker化
まずはローカル環境での導入から行います
チームの開発環境をDockerで統一し、Dockerに慣れていくところから始めていきましょう
2.テスト/CIへの導入
次にテスト/CI環境への導入を行います
また、CIを導入してないのであればDockerとの相性が良いのでこの機会に導入するのもありでしょう。
3.ステージングへの導入
本番へいきなり導入する前にステージング環境でDockerの動作を確認しましょう
4.本番への導入
最後に本番環境への導入を行いましょう
tools
全てのcontainerを停止、削除します。関連するvolumeとnetworksも削除します。
$ docker-clean stop
Dockerのリソース全てを削除する。起動中のcontainerも削除される点に注意。
$ docker-clean all
開発スタート
あとは、コンテナが起動している時は、以下のようにexec webに続いてrailsのコマンドを実行していけば、scaffoldやmigrationの実行が可能です。
$ docker-compose exec web bin/rails g scaffold User first_name:string last_name:string
マイグレーションの場合は、以下のような感じです。
$ docker-compose exec web bin/rails db:migrate
コンテナを終了させたい場合は、以下のコマンドになります。
$ docker-compose down
Gemを追加したときは、イメージを再構築する必要があるので、以下のコマンドを実行してください。
$ docker-compose build web