社内向けの講演のために作ったスライドですが、わざわざGoogle プレゼンテーションにするまでもなかったし、特段秘密の情報もなかったので、Qiitaスライドの方に作りました
多分ちょいちょい追記します
対象
- まだコンテナ開発していない人
- コンテナ開発がうまくいかない人
- 既存プロジェクトをコンテナ化したい人
お品書き
- コンテナの概念のおさらい
- コンテナでの開発
- docker のネットワーク
- docker-compose を使った開発の効率化
コンテナの概念のおさらい
#VM と コンテナ
https://qiita.com/niisan-tokyo/items/88a53a1b4aa7ad60723e
コンテナの目的
ある前提となる状態のもとで、特定のコマンド(のみ)を実行したときの動作をシミュレートすること
サーバの中のプロセス
$ ps -A
PID TTY TIME CMD
1 ? 00:00:00 init
2 ? 00:00:00 kthreadd
3 ? 00:00:04 ksoftirqd/0
5 ? 00:00:00 kworker/0:0H
6 ? 00:00:51 kworker/u30:0
7 ? 00:00:13 rcu_sched
8 ? 00:00:00 rcu_bh
9 ? 00:00:00 migration/0
10 ? 00:00:00 khelper
11 ? 00:00:00 kdevtmpfs
12 ? 00:00:00 netns
...
コンテナの中のプロセス (PHP-FPM)
# ps -A
PID USER TIME COMMAND
1 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
6 www-data 0:00 php-fpm: pool www
7 www-data 0:00 php-fpm: pool www
8 root 0:00 sh
13 root 0:00 ps -A
※最後の2つはdocker exec で侵入した自分自身のプロセスなので、それを除くとfpmのプロセスしか存在していない
コンテナでの開発
コンテナ開発で考えること
- そもそも対象がコンテナで開発すべきものか
- コンテナを動かすコマンドを知る(Dockerを使う)
- 役割に応じたコンテナの分離
- 適切なイメージの使用
- 適切なイメージの作成
- コアとなるコンテナの把握
そもそも対象がコンテナで開発すべきものか
- コンテナは動きを再現するもの
- サーバを作るプロビジョニングツールの起点としてはコンテナで動かしてもいいが、プロビジョニングの対象はVMであるべき
- アプリケーションはほとんどがコンテナ開発して差し支えない
コンテナを操作するコマンドを知る(Docker)
まずはこれだけ知っておけばいいってやつ
- docker run
- docker build
- docker ps
docker run
コンテナを実行する。
docker run [<OPTIONS>] <IMAGE_NAME>[:<TAG_NAME>] [<COMMAND>]
イメージがローカルにない場合はdockerhubなど、外部レジストリのイメージを pull しつつコンテナを実行する
例1) コンテナをそのまま実行する
docker run hello-world
※結果は標準出力に出る
例2) PHPのalpineイメージを、コマンドラインで操作できるようなコンテナを実行する
docker run -it --rm php:alpine sh
共有ボリューム(ディレクトリ)
-v
オプションにより、コンテナ内のディレクトリとホストマシンのディレクトリを共有ボリュームにすることができる
例3) 現在のディレクトリをコンテナの/var/www
との共有ボリュームにする
docker run -it --rm -v `pwd`:/var/www php:alpine sh
※厳密にはコンテナ内部の出力を永続化するためのものだけど、開発なので気にしない
docker ps
実行中のコンテナを一覧表示する
docker ps
-a
オプションを付けると停止中のコンテナも一緒に表示する
docker ps -a
docker build
コンテナイメージを作成する
docker build -t <IMAGE_NAME>[:<TAG_NAME>] <DIR_NAME>
自分は、基本的にはDockerfileのあるディレクトリ上で実行している
例) 自分がphpの作業用コンテナをビルドするときのコマンド
dockder build -t niisan/php .
役割に応じたコンテナの分離
Dockerが使えるようになったら、次はコンテナの分離を考える
基本的に、一つのコンテナに付き一つの動作を担当する
よくあるNginx + php-fpm + mysql + redis とかであれば、システムを4つのコンテナに分離できる
作業用コンテナがあるとなおよし
作業用コンテナとは、直接動作には関係ないが、パッケージの依存性解決など、いくつかの作業を実施するのに利用されるコンテナ
- nodeのyarn でnode_module のインストール
- composer install
- etc...
適切なイメージの使用
役割に応じてコンテナを分離したら、各コンテナにあったイメージを探す
- Dockerhubから公式のイメージを落とすのが手っ取り早い
- mysql, redisなど、適当な環境変数を設定するだけで動くものは、イメージそのまま使える
- 予めバージョン指定 (ex.] mysql:5.7) しておくと、バージョンブレイクで困らない
- nginx + php-fpm だと、nginx側に追加設定が必要なので、公式イメージをベースにカスタマイズしたイメージを自作する必要が出てくる
mysql イメージを直接使った例
docker run -d -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:5.7
- 環境変数
MYSQL_ROOT_PASSWORD
を設定することで、id: root, pass: my-secret-pw でアクセス可能なmysql5.7のコンテナが立ち上がる - これはポートフォワード
-p 3306:3306
しているので、外部から接続可能
適切なイメージの作成
composerを使うためのイメージなど、公式にないイメージなどは自作する
イメージの自作は
- Dockerfileを作る
-
docker build
でイメージを作る
で行う
例) composer入のphpイメージの作成
FROM php:alpine
RUN apk update && apk add unzip bzip2-dev
RUN docker-php-ext-install mysqli pdo_mysql
RUN curl -sS https://getcomposer.org/installer | php; mv composer.phar /usr/local/bin/composer ; mkdir /var/www
RUN docker-php-ext-install bz2
WORKDIR /var/www
CMD ["sh"]
コアとなるコンテナの把握
運用フェーズで特に必要になるが、コンテナ群で置き換え不可能なコンテナ (コアとなるコンテナ) が何かを把握しておく
エントリーポイント
アプリケーションのコアコンテナの中で、エントリーポイントになっているコンテナはどれかも把握する
例えば、 nginx -> php-fpm をコアとするアプリでは、ユーザーのエントリーポイントはnginxになる。
外部からnginxにブラウザなどでつなげるために、こちら側のポートから、コンテナの内部のポートにポートフォワードを設定する
docker run -d -p 8080:80 nginx
この設定は、Docker for Macであれば、 localhost:8080
によって、コンテナ内部の80番ポートにアクセスできる
docker のネットワーク
コンテナ間の通信
これはどうやって実現できるか?
dockerのネットワークを使った解決
dockerはdockerサーバ自体が提供しているネットワークが存在している。
このネットワーク上でaliasをつければ良い
例 ) php:alpineから、mysqlコンテナをdbという名前で他のコンテナからアクセスできるようにする
docker run -d -e MYSQL_ROOT_PASSWORD=my-secret-pw --name db mysql:5.7
docker run -it --rm --link db:db php:alpine sh
php:alpineの中からdbをpingした様子
/ # ping db
PING db (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.122 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.093 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.118 ms
64 bytes from 172.17.0.3: seq=3 ttl=64 time=0.080 ms
64 bytes from 172.17.0.3: seq=4 ttl=64 time=0.144 ms
64 bytes from 172.17.0.3: seq=5 ttl=64 time=0.086 ms
docker の ネットワーク追加
dockerはデフォルトの内部ネットワークだけでなく、ユーザー定義のネットワークもできちゃう
「sample」という名前のネットワークを構築するコマンド
docker network create -d bridge --subnet 172.26.0.0/24 sample
「sample」上にmysqlを設置し、dbという名前でネットワーク内からなら繋げられるよう設定
docker run -d --network sample --network-alias db -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql:5.7
php:alpineコンテナを同じネットワークに乗っける
docker run -it --rm --network sample php:alpine sh
php:alpine 内部から、dbにpingを打ってみる
# ping db
PING db (172.26.0.2): 56 data bytes
64 bytes from 172.26.0.2: seq=0 ttl=64 time=0.337 ms
64 bytes from 172.26.0.2: seq=1 ttl=64 time=0.234 ms
64 bytes from 172.26.0.2: seq=2 ttl=64 time=0.087 ms
64 bytes from 172.26.0.2: seq=3 ttl=64 time=0.087 ms
接続していることがわかる
複数のネットワークを使って、複雑なネットワーク構成のシステムを組むこともできる
開発のまとめ
- dockerコマンドの基本的なところを使えるようにしておく
- 対象のシステムを役割ごとに分離する
- 分離した役割ごとにコンテナのイメージを設定する
- 各コンテナを立ち上げる
- 必要があればネットワークの設定をする
docker-compose を使った開発の効率化
不便なコンテナ開発
- コンテナを一つ一つ起動しなければならない
- ネットワーク設定を考えなければならない
- コンテナ起動コマンドのスニペットがぶくぶく太っていく
=> 「操作より設定」の概念を導入して解決
docker-compose
- 設定ファイルを通して、コンテナを立ち上げるのに必要なコマンドを簡略化する
- ネットワークの設定も自動で設定してくれる
- 設定ファイルをリポジトリ管理することで、構成管理を透明化できる
- .env対応
- kubernetesのpodみたいなもん
- ECSのタスクみたいなもん
設定ファイル (docker-compose.yml)
一例としてlaravelのプロジェクトでniisan-tokyoがよく使っている設定です
version: '3'
services:
workspace:
build: workspace/
volumes:
- ${BASE_DIR}
php-fpm:
build: ./php-fpm
volumes:
- ${BASE_DIR}
expose:
- "9000"
nginx:
build:
context: ./nginx
args:
- PHP_UPSTREAM=php-fpm
ports:
- "8080:80"
- "444:443"
volumes:
- ./logs/nginx/:/var/log/nginx
- ./nginx/sites/:/etc/nginx/sites-available
- ./nginx/ssl/:/etc/nginx/ssl
- ${BASE_DIR}
db:
image: mysql:5.7
environment:
- MYSQL_DATABASE=homestead
- MYSQL_USER=homestead
- MYSQL_PASSWORD=secret
- MYSQL_ROOT_PASSWORD=root
volumes:
- mysql:/var/lib/mysql
volumes:
mysql:
driver: "local"
docker-compose.ymlの説明
- version
- services
- image
- build
- environment
- ports
- volumes
- depends_on
version
docker-compose.ymlの記法のバージョン
docker-compose自体のバージョンによって使えたり使えなかったりする
例
version '3'
services
それぞれが動作するコンテナを意味している。
システムを分離できたら、それらを全部書き込んでいく
services:
workspace:
...
...
php-fpm:
...
db:
...
image, environment, volumes
- imageはコンテナのイメージをそのまま使う場合に使用
- environmentは起動時に必要な環境変数 ( 配列形式で指定 )
- volumesは共有ボリュームの設定 ( 配列形式で指定 )
db:
image: mysql:5.7
environment:
- MYSQL_DATABASE=homestead
- MYSQL_USER=homestead
- MYSQL_PASSWORD=secret
- MYSQL_ROOT_PASSWORD=root
volumes:
- ../mysql:/var/lib/mysql
build, ports, depends_on
- buildは自前のイメージを使う場合に、Dockerfileの存在するディレクトリを指定
- portsはポートフォワードするときのポートを設定
- depends_onは自分が立ち上がる時に一緒に立ち上がっているべきコンテナ( サービスを指定 )
nginx:
build:./nginx
ports:
- "8080:80"
- "444:443"
volumes:
- ./logs/nginx/:/var/log/nginx
- ./nginx/sites/:/etc/nginx/sites-available
- ./nginx/ssl/:/etc/nginx/ssl
- ${BASE_DIR}
depends_on:
- php-fpm
- db
- cache
docker-composeコマンド
まずは以下を覚えておけば良い
- up
- down
docker-compose up
サービスを立ち上げる
ターゲット指定した場合、depends_onに設定されたサービスが有る場合はそれらも一緒に立ち上げる
例) 設定されているコンテナ全部立ち上げる
docker-compose up
例) nginx コンテナをバックグラウンドで動作させる
docker-compose up -d nginx
docker-compose down
立ち上がっているサービスを停止させ、コンテナを除去する
やることやって、作業を終了するときなどに使う
docker-compose down
.env
docker-compose は.envが使えるので、環境変数をここで設定しておくことができる
COMPOSE_PROJECT_NAME=sample
BASE_DIR=../:/var/www
※COMPOSE_PROJECT_NAME
の設定はプロジェクトごとにやっておくとサービス名などがかぶらないので便利
まとめ
- コンテナ開発を始めよう
- システムを分離して役割に応じてコンテナを立てよう
- ネットワーク機能を使ってコンテナ同士をつなげよう
- docker-compose を使ってコンテナ開発を楽に使用