Dockerは仮想技術の一つ程度しか知らいない状態から、JUG CCC 2018 Spring で「JavaエンジニアのためのDocker入門 ~ 仮想開発・テスト環境構築 ~ 」を聞いて少し勉強してみたのでDockerで環境を作ってみました。
環境
- macOS
- Docker for Mac Version 18.03.1-ce-mac65 (Docker)
開発時の起こり得る環境差問題
- 自分の環境では動く
- 他のメンバーの環境では動かない
こういった問題の要因として
- 開発機はwindowsだけど、検証・本番環境はLinuxでOSの問題で動かなくなったり
- DBや言語のバージョンが違う
- Webコンテナのバージョン・設定が違う
- etc...
などなど人によって環境がバラバラなことがあるかと。例えば、自分はMySQL5.7で開発しているが、他の人はMySQL5.6で開発してしている。こういったバージョンの違いで生まれる不具合があったり、configを自分専用にカスタマイズしていたりなどなどあるかと思います。そんな状況をDockerで解決につながる。インフラ環境をgitとかで管理して、cloneしてdockerをコマンド一つ実行するだけで皆同じ環境になる。すばらしい
だけどいきなり、ほぼほどDockerについて知らない人しかいない状況だと導入は難しいので、参考になればと簡単な使い方と環境を作ってみました。
Docker基礎
Dockerとは
コンテナ技術を使ったプラットフォーム
virtualboxなどで仮想環境を構築した場合は、ホストOSの上にゲストOSを新たに立ち上げるので重いしリソースも食われる。
Dockerのようなコンテナの場合は、ゲストOSがホストOSを使うため、オーバーヘッドが小さく高速に動作し起動も早い。
- イメージ
仮想化するOSやアプリの雛形がDocker hubで公開されており、準備が楽 - イメージ作成設定
仮想化するOSやアプリの設定やコマンドをDockerfileに記述できる。
コンテナとイメージとDockerfile
冷凍チャーハンの例がわかりやすい。
- コンテナ
Dockerで仮想化したOSやアプリが実行されている環境 - イメージ
仮想化するOSやアプリの雛形
Docker hubで公開されており、準備が楽 - Dockerfile
イメージを作成するための設定やコマンドを記述するファイル
Docker hubにもDockerfileは公開されているので勉強の参考になる。
Dockerfileがあれば、イメージ・コンテナの作成が簡単にできるので、イメージ・コンテナは使い捨てにできる。
Dockerコマンド
イメージの取得
Docker hub からイメージを取得
$ docker pull NAME
例としてnginxを取得してみる。
$ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
Digest: sha256:0fb320e2a1b1620b4905facb3447e3d84ad36da0b2c8aa8fe3a5a81d1187b884
Status: Image is up to date for nginx:latest
イメージの一覧表示
Docker hub から取得したイメージやDockerfileで作成したイメージの一覧を表示
$ docker images
pullで取得したイメージを見てみる。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest ae513a47849c 4 weeks ago 109MB
イメージからコンテナ起動
$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
例としてnginxを起動してみる。
$ docker run --name my-nginx -d -p 8080:80 nginx:latest
コマンド実行後、http://127.0.0.1:8080/ かhttp://localhost:8080/ にアクセスするとWelcome to nginx!が表示される。
外部からコンテナにアクセスするにはポートフォワードを使うので、コマンドオプションの-p 8080:80
は、ポート8080でアクセスすると、nginxのポート80にフォワードするということになる。
起動中コンテナの一覧表示
$ docker ps
例としてnginxを起動してpsしてみる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b213816ff7e1 nginx:latest "nginx -g 'daemon of…" 4 minutes ago Up 4 minutes 0.0.0.0:8080->80/tcp my-nginx
コンテナ停止
起動中のコンテナを停止させる。
$ docker stop CONTAINERID
例としてnginxを起動して停止してみる。
$ docker stop b213816ff7e1
停止したコンテナは、docker ps -a
で一覧で見れる。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b213816ff7e1 nginx:latest "nginx -g 'daemon of…" 13 minutes ago Exited (0) 3 minutes ago my-nginx
コンテナの再起動
停止させたコンテナを起動させる。
$ docker start CONTAINERID
例として停止させたnginxを起動してみる。
$ docker start b213816ff7e1
Dockerfileを使ったビルド
Dockerfileを使ってnginxとMySQLを独自の設定を追加してビルドしてみる。
構成は以下の感じ
.
├── mysql
│ ├── Dockerfile
│ └── config
│ └── my.cnf
└── nginx
├── Dockerfile
├── config
│ └── default.conf
└── html
└── hello.html
nginxから
FROM nginx:1.14.0
COPY ./config/default.conf /etc/nginx/conf.d/default.conf
COPY ./html /usr/share/nginx/html
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
<!DOCTYPE html>
<html>
<body>
<h1>Hello!</h1>
</body>
</html>
FROM
は、ベースとなるイメージを指定します。今回は、nginxのタグが1.14.0をベースイメージという意味になります。
COPY
で、指定したファイル・ディレクトリをコンテナにコピーしてくれます。1個目のCOPYでは、./config/default.conf
をコンテナ内にある/etc/nginx/conf.d/default.conf
に上書きしてます。2個目は、./html
ディレクトリを/usr/share/nginx/html
にコピーしてます。
次にMySQL
FROM mysql:5.7
EXPOSE 3306
COPY ./config/my.cnf /etc/mysql/conf.d/my.cnf
CMD ["mysqld"]
[mysqld]
character-set-server=utf8
[mysql]
default-character-set=utf8
[client]
default-character-set=utf8
MySQLのDockerfileでは、EXPOSE
で外部に公開するポートを指定してます。CMD
では、指定したコマンドをdocker run
したときに実行してくれます。
これで雛形ができたので、イメージを作成するためにDockerfileを記述したあとはdocker build
コマンドを実行する必要がある。
nginxからビルドする
$ cd ./nginx
$ docker build -t hello-nginx .
docker build
を実行すると以下のメッセージと共にhello-nginxイメージが作成されます。
Sending build context to Docker daemon 5.12kB
Step 1/3 : FROM nginx:1.14.0
---> f759510436c8
Step 2/3 : COPY ./config/default.conf /etc/nginx/conf.d/default.conf
---> 5a2a0f48dc8d
Step 3/3 : COPY ./html /usr/share/nginx/html
---> 68797bf4b521
Successfully built 68797bf4b521
Successfully tagged hello-nginx:latest
作成したhello-nginxイメージを以下のコマンドで実行してみてブラウザでhttp://localhost:8080/hello.htmlにアクセスしてみてHelloが見れれば正常に動作してます。
$ docker run --name hello-nginx -d -p 8080:80 hello-nginx
MySQLも同様にビルドする
$ cd ../mysql/
$ docker build -t hello-mysql .
Sending build context to Docker daemon 3.584kB
Step 1/4 : FROM mysql:5.7
5.7: Pulling from library/mysql
f2aa67a397c4: Already exists
1accf44cb7e0: Already exists
2d830ea9fa68: Already exists
740584693b89: Already exists
4d620357ec48: Already exists
ac3b7158d73d: Already exists
a48d784ee503: Already exists
f122eadb2640: Pull complete
3df40c552a96: Pull complete
da7d77a8ed28: Pull complete
f03c5af3b206: Pull complete
54dd1949fa0f: Pull complete
Digest: sha256:d60c13a2bfdbbeb9cf1c84fd3cb0a1577b2bbaeec11e44bf345f4da90586e9e1
Status: Downloaded newer image for mysql:5.7
---> a8a59477268d
Step 2/4 : EXPOSE 3306
---> Running in 4138c3707cad
Removing intermediate container 4138c3707cad
---> 5bfedb5fc6c5
Step 3/4 : COPY ./config/my.cnf /etc/mysql/conf.d/my.cnf
---> 99d665b6cd01
Step 4/4 : CMD ["mysqld"]
---> Running in 1d70da245b37
Removing intermediate container 1d70da245b37
---> 8f1c4df89fc8
Successfully built 8f1c4df89fc8
Successfully tagged hello-mysql:latest
以下のコマンドを実行してMySQLも実行する。mysqlコマンドなどで接続できれば正常に起動している。
$ docker run --name hello-mysql -e MYSQL_ROOT_PASSWORD=mysql -d -p 3306:3306 hello-mysql
$ mysql -u root -h 127.0.0.1 -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.22 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
DockerのMySQLに接続するときのホストが127.0.0.1なのがミソ。
この状態で、docker ps
をしてみる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e25517d43ede hello-mysql "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 0.0.0.0:3306->3306/tcp hello-mysql
74b955bfe213 hello-nginx "nginx -g 'daemon of…" 17 minutes ago Up 17 minutes 0.0.0.0:8080->80/tcp hello-nginx
Dockerのホスト
まだまだ勉強不足であまりわかっていませんが・・・
Dockerコンテナが公開しているポートに対し、通常はループバック・アドレス(127.0.0.1)を用いて通信します。
なのでnginxやMySQLに接続する際に、ホストに127.0.0.1を使っています。
また、Linux(Mac)の場合、(多分)デフォルトで/etc/hosts
に以下のような設定があるので localhostでも見れます。
127.0.0.1 localhost
Docker compose
docker run
で一つ一つ起動してもちゃんと動いているいますが、起動するコンテナが増えるとdocker run
では辛くメリットがあまりないです。さらに複数のコンテナを使って互いに連携したり依存関係がある場合、起動順番も考慮が必要になってきます。そこで便利なのがdocker-compose
です。
Docker composeは、簡単に言うと、複数のコンテナをまとめて管理してくれるツールです。Docker Compose を使うと複数のコンテナ設定や依存関係をdocker-compose.ymlという設定ファイルに記述し、コマンド一つで複数コンテナを一気に起動できます。
Docker composeを使って複数コンテナを起動
先程のnginxとMySQLをDocker compose を使ってコマンド一つで起動してみます。
構成はこんな感じ
.
├── docker-compose.yml
├── mysql
│ ├── Dockerfile
│ └── config
│ └── my.cnf
└── nginx
├── Dockerfile
├── config
│ └── default.conf
└── html
└── hello.html
docker-compose.ymlが、増えただけです。
変更、追加したのは以下の2ファイルだけです。
version: "3.3"
services:
nginx:
build: ./nginx
volumes:
- ./nginx/log:/var/log/nginx
- ./nginx/html:/usr/share/nginx/html
ports:
- 8080:80
mysql:
build: ./mysql
environment:
- MYSQL_ROOT_PASSWORD=$MYSQL_DATABASE_PASSWORD
volumes:
- ./mysql/data:/var/lib/mysql
ports:
- 3306:3306
FROM nginx:1.14.0
COPY ./config/default.conf /etc/nginx/conf.d/default.conf
まずは、docker-compose.ymlから
- version
- バージョン指定
- services
- コンテナやイメージを定義
- build
- 指定ディレクトリ内にあるDockerfileを使ってコンテナを作る
- volumes
- xxx:yyy形式で記述
- nginxを例に、ホストの./nginx/logディレクトリにコンテナの/var/log/nginxディレクトリをマウントしてくれる
- これで、nginxのログファイルが、./nginx/logに吐き出されます
- volumesを指定しない場合、コンテナを削除したときに、データが消えてしまうので、永続化したいデータはマウントすることがおすすめ(dbのdataとか)
- environment
-
docker run
で指定したオプション-e
に当たる - mysqlを例に、MYSQL_ROOT_PASSWORD=$MYSQL_DATABASE_PASSWORDを環境変数として設定してくれる
- $MYSQL_DATABASE_PASSWORDはホストの環境変数
-
- ports
-
docker run
で指定したオプション-p
に当たる - ポートフォワーディングの設定
-
./mginx/Dockerfileはhtmlディレクトリをコピーではなくマウントするように変更しただけ
あとは、このdocker-compose.ymlのあるディレクトリでビルドと実行コマンドを叩けば、nginxとmysqlが立ち上がります。
まずは、ビルド
$ docker-compose build
Building nginx
Step 1/2 : FROM nginx:1.14.0
---> f759510436c8
Step 2/2 : COPY ./config/default.conf /etc/nginx/conf.d/default.conf
---> Using cache
---> 5fd32d08feb1
Successfully built 5fd32d08feb1
Successfully tagged hello-docker_nginx:latest
Building mysql
Step 1/4 : FROM mysql:5.7
---> 0d16d0a97dd1
Step 2/4 : EXPOSE 3306
---> Using cache
---> de0db8091aa0
Step 3/4 : COPY ./config/my.cnf /etc/mysql/conf.d/my.cnf
---> Using cache
---> 54afd7dceec3
Step 4/4 : CMD ["mysqld"]
---> Using cache
---> 6185c4750d81
Successfully built 6185c4750d81
Successfully tagged hello-docker_mysql:latest
Successfullyで成功したので、あとは実行
$ docker-compose up -d
Creating network "hello-docker_default" with the default driver
Creating hello-docker_mysql_1 ... done
Creating hello-docker_nginx_1 ... done
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
64a6bb25044e hello-docker_nginx "nginx -g 'daemon of…" Less than a second ago Up 18 seconds 0.0.0.0:8080->80/tcp hello-docker_nginx_1
e61fdd163f3b hello-docker_mysql "docker-entrypoint.s…" Less than a second ago Up 18 seconds 0.0.0.0:3306->3306/tcp hello-docker_mysql_1
docker ps
で見たところ動いているようなので、あとは動作確認。
nginxから動作を見てみます。http://127.0.0.1:8080/ かhttp://localhost:8080/ にアクセスすると403 Forbiddenになっていると思います。
これは、docker-compose.ymlで./nginx/html:/usr/share/nginx/html
を指定しているからです。今回はコピーしないで、直接ホストのディレクトリをマウントしているため、index.htmlが無く起きています。
<!DOCTYPE html>
<html>
<body>
<h1>index Hello!</h1>
</body>
</html>
を作成して更新すると見れるはずです。http://localhost:8080/hello.html も見れれば正常に動いていそうです。
次にMySQL
$ mysql -u root -p -h 127.0.0.1 -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.22 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
パスワードは、環境変数MYSQL_DATABASE_PASSWORDです。接続できたので、正常に動いていそうです。
最後に、コンテナを終了させます。方法は2つあります。
1つ目
$ docker-compose stop
Stopping hello-docker_nginx_1 ... done
Stopping hello-docker_mysql_1 ... done
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
64a6bb25044e hello-docker_nginx "nginx -g 'daemon of…" 10 minutes ago Exited (0) 17 seconds ago hello-docker_nginx_1
e61fdd163f3b hello-docker_mysql "docker-entrypoint.s…" 10 minutes ago Exited (0) 15 seconds ago hello-docker_mysql_1
正常に止まっているようです。
では、2つ目
$ docker-compose down
Stopping hello-docker_nginx_1 ... done
Stopping hello-docker_mysql_1 ... done
Removing hello-docker_nginx_1 ... done
Removing hello-docker_mysql_1 ... done
Removing network hello-docker_default
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
結果が、stop
とdown
では異なります。stop
の場合は、終了したコンテナが残ります。down
の場合は、コンテナを終了させたあとにコンテナを削除してくれます。なので、docker ps -a
で見ても当たり前ですが残りません。
コンテナを使い捨ての環境として使うのであれば、down
を使うべきと思います。基本的にはコンテナは使い捨てとして使うべきと思うので、必要なものはvolumesでマウントして、コンテナは、毎回削除するほうがいいと私は思います。
これで、コマンド一つで起動・停止もできるようになったので、あとは開発時に必要に応じてカスタマイズして、docker-compose.ymlやDockerfileを共有して実行すれば、同じ環境を作れるようになります。便利
作成したサンプルはgitにあります。参考までに。。。