(1) Dockerインストール
(2) CentOSやUbuntuのベースDockerイメージしか入ってないDockerコンテナの起動
(3) Apacheが入ったDockerコンテナでWebサーバを起動
(4) Apache+PHPが入ったDockerコンテナとMySQLが入ったDockerコンテナの2つで簡単なLAMPのWebサイトを起動
(5) DockerfileからのDockerイメージのビルドと起動
(7) Docker Composeによる複数Dockerコンテナの一括操作
- 南砂クラウド(OpenStack)上に、勉強会参加者1名につき1個、以下の条件で仮想マシンを新規作成しておいて下さい。
- フレーバ: m4.XS(vCPUx1、MEM4GB、Disk30GB)以上
- OS: CentOS 7 64bit
- パブリックイメージに「service_centos7_64bit」というのがありますので、そちらをお使いいただければよいのではと思います。
- ネットワーク、IPアドレス周りで特別な設定は不要
- SSHログイン可能としておく
- 極力事前にチェックしておいて下さい。
- sudoでroot権限を取得可能としておく
- 極力事前にチェックしておいて下さい。
- yumミラーリポジトリにアクセス可能としておく
- 以下のコマンドを実行し、BaseとUpdatesとExtrasがenableになっていること、アクセス先がミラーリポジトリサーバxadmin-10-pro-svn-001に向いていることを、極力事前にチェックしておいて下さい。
$ sudo yum repolist all
$ sudo yum repoinfo all
- 通山さんより以下のコメントあり。
NTT-Rでpuppetを使える人なら↓を見せて「VMを作って↓のようなpuppet manifestを適用しておいてください。
yumリポジトリへのアクセスが最低限必要です」とか言えば通じるはずです。
class <svccode>-<env>-<host>_rgc inherits mediainfra-01-common_rgc {
#必要なLinuxユーザを作成する定義は別途必要
include 'mediainfra-01-common_rc_hosts'
include 'mediainfra-01-common_rc_syslog'
include 'mediainfra-01-common_rc_yumrepos_centos'
include 'mediainfra-01-common_rc_subversion'
include 'mediainfra-01-common_rc_disable_services'
include 'mediainfra-01-common_rc_yum_clean_all'
include 'mediainfra-01-common_rc_yum_remove_i386'
}
$ sudo yum update
カーネルがアップデートされたら、リブートしておく
$ sudo reboot
yumリポジトリからCentOS7で配布されているDockerエンジン(v1.10)をインストール
$ sudo yum install docker
$ sudo systemctl list-unit-files | grep docker
Dockerサービスに南砂クラウドのproxy設定をエディタで追加
$ sudo vim /etc/sysconfig/docker
以下の行をファイルの末尾に追加
http_proxy=http://172.20.253.21:8080
https_proxy=http://172.20.253.21:8080
Dockerサービスのブート時自動起動を有効にする
$ sudo systemctl enable docker.service
Dockerサービスを起動する
$ sudo systemctl start docker
Dockerサービスの稼働状況を確認する
$ sudo docker info
DockerHubまで通信が通るか確認する
$ sudo docker search hello-world
ねらい: Dockerの仕組みの基本を理解する。
2枚めのターミナル(t2と呼称。1枚目はt1と呼称)を起動し、両方ともVMにSSHログインしておくこと。
(t1)DockerHubからCentOSのDockerイメージを取得
t1$ sudo docker pull centos
(t1)取得したDockerイメージからDockerコンテナを作成し、そのコンテナ内でbashを起動。
- キーボード入力を有効にするための
-i
オプションと、ターミナルを奪う-t
オプションを付ける
t1$ sudo docker run -it centos bash
(t1)コンテナ内でCentOSのバージョンを確認
container# cat /etc/redhat-release
(t2)取得したDockerイメージの一覧を確認
- CentOSのイメージがあるはず
t2$ sudo docker images
(t2)作成したDockerコンテナの一覧を確認
- コンテナが1個できているはず
t2$ sudo docker ps
(t1)コンテナ内でプロセスツリーを確認
- initやsystemdなどがPID=1になっておらず、bashがPID=1になってる
container# ps auxww
(t2)Dockerホスト側でもプロセスツリーを確認
- プロセスツリーの違いを確認。コンテナ内で起動したbashも見えているはず
t2$ ps auxww
(t1)コンテナ内でボリュームの状況を確認
container# df -m
(t2)Dockerホスト側でもボリュームの状況を確認
- ボリュームの構造、容量などの違いを確認。マウントポイントが違っていたり、容量が少なかったりするはず
t2$ df -m
(t1)コンテナ内から外向きの通信はちゃんと通ることを確認
container# http_proxy=http://172.20.253.21:8080 curl http://www.goo.ne.jp/
(t2)Dockerホスト側でネットワークインタフェースの状況を確認
- ホストローカルNWのLinuxブリッジと、コンテナ用のvethができている
t2$ ip a
(t1)コンテナ内でファイルを作ってみる
container# touch hogehoge
container# ls
(t1)コンテナ内のbashを終了する
container# exit
(t1)稼働中のコンテナの一覧を確認
t1$ sudo docker ps
(t1)停止したものも含めて全てのコンテナの一覧を確認
t1$ sudo docker ps -a
(t1)先ほどbashを動かしたコンテナが残留しているが、もう使えないので消す
- コンテナ名はランダムに生成される
t1$ sudo docker rm [コンテナ名]
(t1)Dockerイメージの一覧を確認
t1$ sudo docker images
(t1)再度コンテナを作成し、コンテナ内でbashを起動
- bash終了後に残留する使えないコンテナを自動で消すために
--rm
オプションを付ける
t1$ sudo docker run -it --rm centos bash
(t1)コンテナ内のディレクトリを確認する
- hogehogeファイルはない
container# ls
(t1)コンテナ内のbashを終了する
container# exit
(t1)停止したものも含めて全てのコンテナの一覧を確認
- 全て消えているはず
t1$ sudo docker ps -a
- プロセスツリーはあやしいが、ボリュームもちゃんとあってディレクトリツリーもあるし、通信もできるし、仮想マシンに似ている(ような気がする)
- コンテナを消すと、コンテナ内で作っていたファイルも揮発する
(t1)DockerHubからUbuntuのDockerイメージを取得し、取得したイメージからコンテナを作成し、コンテナ内でbashを起動
docker pull
コマンドを実行せず、いきなりdocker run
コマンドを実行することで、イメージ取得からコンテナ起動まで通しでできる
t1$ sudo docker run -it --rm ubuntu bash
(t1)コンテナ内でUbuntuのバージョンを確認
container# cat /etc/lsb-release
(t1)コンテナ内でLinuxカーネルのバージョンを確認
container# uname -a
(t2)Dockerホスト側でもLinuxカーネルのバージョンを確認
- コンテナ内で確認したものと同じはず
t2$ uname -a
(t1)Ubuntuコンテナを終了
container# exit
- Dockerイメージが異なれば、ディレクトリツリーも異なる
- LinuxカーネルはDockerホストのものを共有している
(t1)停止したものも含めて全てのコンテナの一覧を確認
- 全て消えているはず
t1# sudo docker ps -a
(t1)Dockerイメージの一覧を確認
- CentOSとUbuntuのDockerイメージが残っているはず
t1$ sudo docker images
(t1)Dockerイメージを削除
- docker.io/ とか latest は省略可能
t1$ sudo docker rmi centos
t1$ sudo docker rmi ubuntu
(t1)Dockerイメージの一覧を確認
- 全て消えているはず
t1$ sudo docker images
ねらい: Dockerコンテナによるサービス提供を体験する。
2枚めのターミナル(t2と呼称。1枚目はt1と呼称)を起動し、両方ともVMにSSHログインしておくこと。
(t1)DockerHubからApache httpd入りのDockerイメージを取得し、取得したイメージからコンテナを作成
- いきなりhttpdが起動する
- 起動後はターミナルを奪われたままフォアグラウンドで動作しつづける
t1$ sudo docker run --rm httpd
(t2)Webアクセスしてみる
- t2には It works! っていうHTMLが返ってくるはず
- t1にはアクセスログが出力されているはず
t2$ curl 172.17.0.2
(t2)localhostにWebアクセスしてみる
- t2にはConnection Refusedというエラーが返ってくるはず
- t1にはアクセスログが出力されてないはず
t2$ curl localhost
(t2)自ホストのIPアドレスをチェックし、そこにWebアクセスしてみる
- t2にはConnection Refusedというエラーが返ってくるはず
- t1にはアクセスログが出力されてないはず
t2$ ip a
t2$ curl [自ホストのIPアドレス]
(t1)httpdのコンテナを停止する
^C
(t1)停止したものも含めて全てのコンテナの一覧を確認
- 全て消えているはず
t1# sudo docker ps -a
(t1)Dockerイメージの一覧を確認
- Apache httpd入りDockerイメージが残っているはず
t1$ sudo docker images
- Dockerイメージを取得できれば、WebサーバなどいろんなプログラムをDockerコンテナ内で簡単に起動できる
- サービスをただ起動するだけだと、外部からもlocalhostからもアクセスできない
(t1)コンテナの待受ポートを開放するための-p
オプションを付けて、httpdのコンテナを起動する
t1$ sudo docker run --rm -p 80:80 httpd
(t2)localhostにWebアクセスしてみる
- t2には It works! っていうHTMLが返ってくるはず
- t1にはアクセスログが出力されているはず
t2$ curl localhost
(t2)自ホストのIPアドレスをチェックし、そこにWebアクセスしてみる
- t2には It works! っていうHTMLが返ってくるはず
- t1にはアクセスログが出力されているはず
t2$ ip a
t2$ curl [自ホストのIPアドレス]
(t2)稼働中のコンテナの一覧を確認
- PORTSの欄が
0.0.0.0:80->80/tcp
と表示されている。これはポートフォワーディングが行われているということ
t2$ sudo docker ps
(t1)httpdのコンテナを停止する
^C
(t1)停止したものも含めて全てのコンテナの一覧を確認
- 全て消えているはず
t1$ sudo docker ps -a
- Dockerコンテナで起動したサービスにlocalhostや外部からアクセス可能とするには、ポートフォワーディングを行うための「-p」オプションを使う
(t1)コンテナをバックグラウンドで動かすための-d
オプションを付けて、httpdのコンテナを起動する
--rm
オプションは使えないので注意- コマンドプロンプトが返ってくる
t1$ sudo docker run -p 80:80 -d httpd
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナがバックグラウンドで動いている
- コンテナに勝手に名前がつけられている
t1$ sudo docker ps
(t1)localhostにWebアクセスしてみる
- t1には It works! っていうHTMLが返ってくるはず
- アクセスログは・・・・どこ?
t1$ curl localhost
(t1)docker logsコマンドを実行
- 先ほどのアクセスのログも含め、これまでの標準出力・標準エラー出力を見れる
t1$ sudo docker logs [コンテナ名]
(t1)バックグラウンドで動いているコンテナのログをtail -fする
- コマンドプロンプトは返ってこず、
tail -f
的にログを見れる
t1$ sudo docker logs -f [コンテナ名]
(t2)localhostにWebアクセスしてみる
- t2には It works! っていうHTMLが返ってくるはず
- t1にはアクセスログが出力される
t2$ curl localhost
(t1)ログのtail -f
をやめる
^C
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナは、まだバックグラウンドで動いている
t1$ sudo docker ps
(t1)docker execコマンドにより、httpdのコンテナ内でbashを起動
- ちゃんとログインできる
t1$ sudo docker exec -it [コンテナ名] bash
(t2)localhostにWebアクセスしてみる
- t2には It works! っていうHTMLが返ってくるはず
t2$ curl localhost
(t1)コンテナ内でプロセスツリーを確認
- httpdがフォアグラウンドで動いている
container# ps auxww
(t1)index.htmlを確認してみる
- /usr/local/apache2/htdocs 配下にある
container# cd htdocs
container# cat index.html
(t1)Webサイトに新しいコンテンツを追加してみる
- viもemacsもないので、echoコマンドを使う
container# echo "hello" > test.txt
(t2)新しいコンテンツを確認してみる
- ちゃんと入っているはず
t2$ curl localhost/test.txt
(t1)httpdコンテナのbashから抜ける
container# exit
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナは、まだバックグラウンドで動いている
t1$ sudo docker ps
(t1)httpdのコンテナを停止する
t1$ sudo docker kill [コンテナ名]
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナはもう動いてない
t1$ sudo docker ps
(t1)停止したものも含めて全てのコンテナの一覧を確認
- httpdのコンテナは停止中で残っている
t1$ sudo docker ps -a
(t1)localhostにWebアクセスしてみる
- 当然応答はない
t1$ curl localhost
(t1)httpdのコンテナのログ取得を試みる
- ログは取得できるはず
t1$ sudo docker logs [コンテナ名]
(t1)停止したhttpdのコンテナを再度起動する
t1$ sudo docker restart [コンテナ名]
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナが再度動いている
t1$ sudo docker ps
(t1)localhostにWebアクセスしてみる
- t1には It works! っていうHTMLが返ってくるはず
t1$ curl localhost
(t1)追加したコンテンツtest.txtにもWebアクセスしてみる
- ちゃんと入っているはず
t2$ curl localhost/test.txt
(t1)httpdのコンテナを停止する
t1$ sudo docker kill [コンテナ名]
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナはもう動いてない
t1$ sudo docker ps
(t1)停止したものも含めて全てのコンテナの一覧を確認
- httpdのコンテナは停止中で残っている
t1$ sudo docker ps -a
(t1)httpdのコンテナを削除する
t1$ sudo docker rm [コンテナ名]
(t1)停止したものも含めて全てのコンテナの一覧を確認
- httpdのコンテナはもう残ってない
t1$ sudo docker ps -a
- Dockerコンテナをバックグラウンドで動かすには、docker runコマンドに「-d」オプションを付ける
- バックグラウンドで動いているDockerコンテナのログ(標準出力・標準エラー出力)は docker logsコマンドで取得できる
- バックグラウンドで動いているDockerコンテナ内で、bashなど任意のコマンドを追加実行できる
- バックグラウンドで動いているDockerコンテナは、docker killコマンドで停止でき、docker restartコマンドで再起動できる
- この際、前回の動作中に追加・変更されたファイルは残っている
- 停止したコンテナのログもdocker logsコマンドで取得できる
(t1)再度、httpdのコンテナをバックグラウンドで起動する
t1$ sudo docker run -p 80:80 -d httpd
(t1)稼働中のコンテナの一覧を確認
- httpdのコンテナがバックグラウンドで動いている
- コンテナに先ほどとは違う名前が勝手につけられている
t1$ sudo docker ps
(t1)localhostにWebアクセスしてみる
- t1には It works! っていうHTMLが返ってくるはず
t1$ curl localhost
(t1)test.txtにもWebアクセスしてみる
- Dockerイメージからコンテナを作り直したので、当然404が返ってくるはず
t2$ curl localhost/test.txt
(t1)docker execコマンドにより、httpdのコンテナ内でbashを起動
t1$ sudo docker exec -it [コンテナ名] bash
(t1)httpdのコンテナ内でディレクトリ構造を確認
- /usr/local/apache2/htdocs にDockerホストのディレクトリをマウントできれば、コンテナに入らなくてもDockerホストでコンテンツを追加・変更できそう
container# ls
(t1)コンテナから抜ける
container# exit
(t1)httpdのコンテナを停止し削除する
docker rm -f
コマンドで一気にできる
t1$ sudo docker rm -f [コンテナ名] bash
(t1)停止したものも含めて全てのコンテナの一覧を確認
- httpdのコンテナはもう残ってない
t1$ sudo docker ps -a
(t1)Dockerホストにマウント対象のディレクトリを作成し、index.htmlファイルを作成
- index.htmlファイルの中身は適当でOK
t1$ mkdir htdocs
t1$ vim ./htdocs/index.html
(t1)Dockerホストの特定のディレクトリをコンテナ内のディレクトリにマウントするための-v
オプションを付けて、httpdのコンテナを起動する
t1$ sudo docker run -p 80:80 -d -v $PWD/htdocs:/usr/local/apache2/htdocs httpd
(t1)localhostにWebアクセスしてみる
- t1には、先ほど作成したindex.htmlの中身が返ってくるはず
t1$ curl localhost
(t1)Dockerホストのマウントされたディレクトリ内のindex.htmlを編集する
- 適当に編集
t1$ vim ./htdocs/index.html
(t1)再度localhostにWebアクセスしてみる
- t1には、編集されたindex.htmlの中身が返ってくるはず
t1$ curl localhost
- Dockerホストの特定のディレクトリをコンテナ内のディレクトリにマウントするには、docker runコマンドに「-v」オプションを付ける
(t1)稼働中のコンテナの一覧を確認
t1$ sudo docker ps
(t1)httpdのコンテナを停止し削除する
docker rm -f
コマンドで一気にできる
t1$ sudo docker rm -f [コンテナ名]
(t1)停止したものも含めて全てのコンテナの一覧を確認
- httpdのコンテナはもう残ってない
t1$ sudo docker ps -a
ねらい: より実践的なDockerコンテナの活用を体験する。
2枚めのターミナル(t2と呼称。1枚目はt1と呼称)を起動し、両方ともVMにSSHログインしておくこと。
(t1)DockerHubからApache httpd+PHP入りのDockerイメージを取得し、取得したイメージからコンテナを作成
--name
オプションを付けることで、コンテナに任意の名前を付与できる。今回は「web」にする
t1$ sudo docker run -p 80:80 -d --name web php:apache
(t1)稼働中のコンテナの一覧を確認
- コンテナ名が「web」になっている
t1$ sudo docker ps
(t1)localhostにWebアクセスしてみる
- アクセスできるが、403が返ってくる
t1$ curl localhost
(t1)docker execコマンドにより、httpd+PHP入りコンテナ内でbashを起動
- 今回はホームディレクトリが /var/www/html になっている
t1$ sudo docker exec -it web bash
(t1)コンテナ内でindex.phpファイルを追加
- エディタがないのでechoコマンドを使用
container# echo "<?php phpinfo(); ?>" > index.php
(t2)localhostにWebアクセスしてみる
- phpinfo()の出力が得られるはず
t2$ curl localhost
(t1)コンテナから抜ける
container# exit
- Dockerイメージを取得できれば、PHP入りのWebサーバもDockerコンテナ内で簡単に起動できる
(t1)DockerHubからMySQL入りのDockerイメージを取得し、取得したイメージからコンテナを作成
--name
オプションを付けることで、コンテナに任意の名前を付与できる。今回は「mysql」にする-e
オプションを使用してMYSQL_ROOT_PASSWORDという環境変数を与え、管理者(root)のパスワードを設定
t1$ sudo docker run -p 3306:3306 -d --name mysql -e MYSQL_ROOT_PASSWORD=admin mysql:5.7
(t1)稼働中のコンテナの一覧を確認
- コンテナ名が「mysql」になっている
t1$ sudo docker ps
(t1)docker execコマンドにより、MySQL入りコンテナ内でbashを起動
t1$ sudo docker exec -it mysql bash
(t1)コンテナ内で環境変数を確認
- MYSQL_ROOT_PASSWORDという環境変数が設定されている
container# env
(t1)コンテナ内でMySQLクライアントCLIを起動
container# mysql -u root -padmin
(t1)論理DB一覧を取得
mysql> show databases;
(t1)論理DB、テーブルを作成
- 後のテスト用PHPスクリプトで使用する
mysql> create database test;
mysql> use test;
mysql> create table test(name VARCHAR(100));
mysql> show tables;
mysql> exit
(t1)コンテナから抜ける
container# exit
- Dockerイメージを取得できれば、MySQLもDockerコンテナ内で簡単に起動できる
(t1)docker exec
コマンドにより、webコンテナ内でbashを起動
t1$ sudo docker exec -it web bash
(t1)外部のaptリポジトリにアクセスできるよう、proxy設定を追加する
- コピペにミスらないようにご注意を。特にダブルクォート
container# echo "Acquire::http::proxy \"http://172.20.253.21:8080/\";" > /etc/apt/apt.conf.d/99proxy
(t1)外部のaptリポジトリにアクセスできることを確認
container# apt-get update
(t1)MySQLアクセス用のPHP拡張パッケージをインストール
container# docker-php-ext-install pdo_mysql mysqli
(t1)コンテナから抜ける
container# exit
(t1)docker restartコマンドにより、webコンテナを再起動
- PHP拡張パッケージを有効にするため
t1$ sudo docker restart web
- Dockerコンテナ内でproxy設定を適切に行えば、外部リポジトリへのアクセスも可能
(t1)Dockerホスト上でテスト用PHPスクリプト test.php を作成
t1$ vim ./test.php
- 以下をコピペし、[自ホストIPアドレス]の部分だけ変更して下さい。
<?php
$db = new PDO('mysql:host=[自ホストIPアドレス];dbname=test', 'root', 'admin');
$db->query("INSERT INTO test VALUES('test!')");
$st = $db->query("SELECT * FROM test");
var_dump($st->fetchAll());
?>
(t1)Dockerホストからwebコンテナ内に test.php をコピー
t1$ sudo docker cp ./test.php web:/var/www/html
(t1)webコンテナのtest.phpにWebアクセスしてみる
- 動くはず
t1$ curl localhost/test.php
(t1)webコンテナのtest.phpに繰り返しWebアクセスしてみる
- アクセスのたびにDB insertが行われ、レコードが増えてゆくはず
t1$ curl localhost/test.php
t1$ curl localhost/test.php
- DockerホストからDockerコンテナ内へのファイルコピーが docker cpコマンドで可能
- Dockerコンテナ内からDockerホストへのファイルコピーも、もちろん可能です
(t1)稼働中のコンテナの一覧を確認
t1$ sudo docker ps
(t1)webコンテナをDockerイメージとして保存する
docker commit
コマンドを使用する[チーム名]
は英数字であればなんでもよい。
t1$ sudo docker commit web [チーム名]/web:v1
(t1)Dockerイメージが保存されたか確認する
- ちゃんとあるはず
t1$ sudo docker images
(t1)稼働中のコンテナの一覧を確認
- まだ動いているはず
t1$ sudo docker ps
(t1)webコンテナを停止し削除する
t1$ sudo docker rm -f web
(t1)停止したものも含めて全てのコンテナの一覧を確認
- webコンテナはもう残ってない
t1$ sudo docker ps -a
(t1)保存したDockerイメージを使用してwebコンテナを復活させる
t1$ sudo docker run -p 80:80 -d --name=web [チーム名]/web:v1
(t1)稼働中のコンテナの一覧を確認
- 再度動いているはず
t1$ sudo docker ps
(t1)webコンテナのtest.phpにWebアクセスしてみる
- 動くはず
t1$ curl localhost/test.php
- DockerコンテナからDockerイメージの保存がdocker commitコマンドで可能
ねらい: 各チームの今後の課題解決に向けた知識を獲得する。
- docker commitコマンドを用いて、動作中のDockerコンテナに手を加えた結果をDockerイメージとして保存できることがわかった。
- しかし、そのやりかただと、Dockerイメージの作成方法についてのノウハウが属人化してしまう。
- Dockerイメージの作成方法をコードとして管理したいので、Dockerfileを使ったDockerイメージのビルドの手順も知っておきたい。
FROM php:apache
RUN echo "Acquire::http::proxy \"http://172.20.253.21:8080/\";" > /etc/apt/apt.conf.d/99proxy
RUN apt-get update
RUN docker-php-ext-install pdo_mysql mysqli
RUN echo "<?php phpinfo(); ?>" > /var/www/html/index.php
ADD ./test.php /var/www/html
t1$ vim Dockerfile
t1$ sudo docker build -t [チーム名]/web:v2 .
t1$ sudo docker images
t1$ sudo docker rm -f web
t1$ sudo docker run -p 80:80 -d --name=web [チーム名]/web:v2
動作確認する。
t1$ sudo docker ps
t1$ curl localhost
t1$ curl localhost/test.php
- DockerfileからDockerイメージのビルドがdocker buildコマンドで可能
- Dockerfileは、基本的にDockerコンテナ内で手で実行する手順を書き下したものと捉えることができる
- centos https://github.com/CentOS/sig-cloud-instance-images/blob/155416b523b3353f97015f1942bab8152992a689/docker/Dockerfile
- ubuntu https://github.com/tianon/docker-brew-ubuntu-core/blob/188bcceb999c0c465b3053efefd4e1a03d3fc47e/xenial/Dockerfile
- httpd https://github.com/docker-library/httpd/blob/b13054c7de5c74bbaa6d595dbe38969e6d4f860c/2.4/Dockerfile
- php:apache https://github.com/docker-library/php/blob/3cb02a21164bc2bdb8b25ec48886ffcb7e195510/7.0/apache/Dockerfile
- mysql https://github.com/docker-library/mysql/blob/10eb5eab8d3d793c49a701ed7a42e1e5d1c9bb59/5.7/Dockerfile
ねらい:CI(Continuous Integration、継続的インテグレーション)の導入・拡充に向けたコンテナ技術の応用を考える
- ここまでで、Dockerコンテナ(を含めたコンテナ仮想化)には以下の特長があることがわかった
- 仮想環境のライフサイクルの操作(作成、起動、停止、削除)が軽い
- なので、トライアンドエラーを繰り返しやすい
- ライフサイクルを繰り返してもホストのディスクを汚染しない
- コンテナの削除を忘れなければ
- 仮想環境のライフサイクルの操作(作成、起動、停止、削除)が軽い
- これらは、自動的に反復してテスト実行を繰り返す、CIの実行環境に非常に適した特長
- 今回はPHPUnitを使った単体テストをDockerコンテナ内で実行してみることを通じて、そのような特長の活かし方を考える
- htdocsディレクトリを利用。
- 単体テストの対象となる本体コードを準備
$ vim htdocs/sample.php
<?php
class Sample{
protected $value;
public function get_value(){
return $this->value;
throw new RuntimeException('Not yet implemented.');
}
public function set_value($value){
$this->value = $value * 2;
}
}
?>
- 単体テストを実装したテストコードを準備
$ vim htdocs/sample_test.php
<?php
require_once 'sample.php';
class SampleTest extends PHPUnit_Framework_TestCase{
public function setUp(){
$this->sample = new Sample;
}
public function testGetValue(){
$this->sample->set_value(20);
$this->assertEquals(40, $this->sample->get_value());
}
}
?>
- htdocsディレクトリをコンテナ内の/appディレクトリにマウントし、テストコードのPHPファイルを指定してPHPunitを起動する。テスト完了したらコンテナを消す
$ sudo docker run --rm -v $PWD/htdocs:/app phpunit/phpunit sample_test.php
- テストケース1個が通ったとの出力があるはず。ちなみに、終了コードは「0」のはず
$ echo $?
- テストを故意に失敗させるため、本体コードにバグを仕込む
$value * 2
を$value * 3
に変更
$ vim htdocs/sample.php
<?php
class Sample{
protected $value;
public function get_value(){
return $this->value;
throw new RuntimeException('Not yet implemented.');
}
public function set_value($value){
$this->value = $value * 3;
}
}
?>
- 再度PHPunitを起動
$ sudo docker run --rm -v $PWD/htdocs:/app phpunit/phpunit sample_test.php
- テストケース1個が失敗した旨の出力があるはず。ちなみに、終了コードは「1」のはず
$ echo $?
テストツールやテストスクリプトの終了コードとして、テスト成功だったら0、テスト失敗だったら0以外とするのは、今後CIツール(Jenkins、Concourseなど)を活用するうえでの基本となる。
https://github.com/JulienBreux/phpunit-docker/blob/master/5.5.0/Dockerfile
- PHP7.0、PHPUnit5.5.0がインストールされたDockerイメージであることがわかる
- ENTRYPOINTに
/usr/local/bin/phpunit
が指定してあるため、(2)でbashを起動したときのようにわざわざ/usr/local/bin/phpunit
と打たなくてもphpunitが起動する CMD ["--help"]
というのは、docker run時にイメージ名の後に何もつけなければphpunitコマンドに--help
オプションを付けるという意味。今回は sample_test.php を指定したので、CMDの定義がオーバライドされた- 最後のWORKDIRが /app なので、phpunit実行時のカレントディレクトリが /app になる。
docker run
時に-v
オプションをつけて /appに htdocs ディレクトリをマウントさせたので、sample_test.php を指定する際にhtdocsディレクトリを指定しなくてもよかった - その他詳細は https://hub.docker.com/r/phpunit/phpunit/ を参照
$ sudo docker ps -a
$ sudo docker images
- PHPunitのDockerイメージは残っているが、コンテナ自体は消えているので、テストが繰り返されてもその都度ディスクを消費するということはない
- 異なるバージョンのPHPやPHPUnitを使用したい場合は、Dockerfileを改造して
docker build
コマンドを使って別の新たなDockerイメージを作成することになる
ねらい:複数コンテナを組み合わせた開発検証環境の迅速な構築・更新・削除に向けた知識を獲得する
- 複数コンテナから構成されるサービスを一括で操作できると、実行コマンド数が減ってオペミスが減りそう
- 特に
docker run
コマンドは、コンテナ実行条件を指定するための引数が非常に多くなりがちで、仮にスクリプト化したとしても可読性は低いので、コンテナ実行条件を設定ファイルにしたい - 上記の問題はDocker Composeという、dockerコマンドをラップするツールを使うと解決できる
- 今回は、(4)で試した2コンテナでLAMPを動かすという構成を、Docker Composeを使って一括で構築・削除するのをやってみる
Docker DomposeはGo言語で書かれているコマンドラインツールであり、ライブラリが静的にリンクされたバイナリがGitHubで配布されているので、それをそのままダウンロードして使用する。
t1$ sudo curl -vLx http://172.20.253.21:8080 https://github.com/docker/compose/releases/download/1.8.1/docker-compose-`uname -s`-`uname -m` > ./docker-compose
t1$ chmod +x ./docker-compose
t1$ sudo mv ./docker-compose /usr/bin/
t1$ docker-compose -v
t1$ sudo docker run -p 3306:3306 -d --name mysql -e MYSQL_ROOT_PASSWORD=admin mysql:5.7
t1$ sudo docker exec -it mysql bash
container# mysql -u root -padmin
mysql> create database test;
mysql> create table test.test(name VARCHAR(100));
mysql> exit
- mysqlコンテナにbashで入ってmysqlコマンド起動するが、その際に環境変数で与えられるべきrootのパスワードを指定する必要がある。
→ https://hub.docker.com/_/mysql/ によると、
create database test
の代わりにdocker run
のコマンドオプションとして-e MYSQL_DATABASE=test
を付け加えることで、コンテナ起動時に自動的に create databaseしてくれる
t1$ sudo docker build -t ojiri/web:v2 .
t1$ sudo docker run -p 80:80 -d --name=web ojiri/web:v2
create table test.test
の代わりは、test.phpを改造して対応することにする。- ついでに、PHPからMySQLへの接続先IPアドレスも環境変数から読み込むようにしておく。
(t1)test.php を次のように改造する
t1$ vim test.php
<?php
$db = new PDO('mysql:host='.$_ENV['MYSQL_PORT_3306_TCP_ADDR'].';dbname=test', 'root', 'admin');
$db->query("CREATE TABLE IF NOT EXISTS test (name VARCHAR(100))");
$db->query("INSERT INTO test VALUES('test!')");
$st = $db->query("SELECT * FROM test");
var_dump($st->fetchAll());
?>
(t1)Docker Compose用の設定ファイル docker-compose.yml を用意する
t1$ vim docker-compose.yml
version: '2'
services:
mysql:
image: mysql:5.7
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: test
web:
build: .
ports:
- "80:80"
environment:
MYSQL_PORT_3306_TCP_ADDR: [自ホストのIPアドレス]
(t1)まずはコンテナが何もないことを確認
t1$ sudo docker ps -a
(t1)一括起動
- webコンテナのイメージのビルドもしたいので
--build
オプションをつける - ログがゾロゾロと流れ、フォアグラウンドで起動完了する
t1$ sudo docker-compose up --build
(t2)動作確認
- 問題なく動くはず。t1でアクセスをログも確認
t2$ curl localhost
t2$ curl localhost/test.php
(t2)コンテナの状態を確認
- 2コンテナちゃんと動いているはず
- docker-compose.ymlが入ってるディレクトリ名と、docker-compose.ymlで記述したサービス名を使って、勝手にコンテナ名が振られているはず
t2$ sudo docker ps
(t2)Docker Composeでコンテナの状態を確認
- 出力が簡素化されているが、確認できる
t2$ sudo docker-compose ps
(t1)2コンテナを強制停止、削除
- t1で
^C
- コンテナが残るので、Docker Composeで一括削除
t1$ sudo docker ps -a
t1$ sudo docker-compose rm
- Docker Composeにも
-d
オプションがある
t1$ sudo docker-compose up -d
t1$ sudo docker ps
(t1)動作確認する
t1$ curl localhost
(t1)ログを確認する
t1$ sudo docker-compose logs
(t1)ログをtail -f
的に流す
- 止めるのはt1で
^C
t1$ sudo docker-compose logs -f
t2$ curl localhost
(t2)ip
コマンドでネットワークインタフェースの状況を確認
- 新しいbridgeが増えていて、172.18.0.1のIPアドレスが振られている
t2$ ip a
(t2)docker network ls
コマンドでネットワークの状況を確認
- 一番下になんか増えてる
t2$ sudo docker network ls
(t2)docker network inspect
コマンドで増えたネットワークの詳細を確認する
- 詳しい情報がとれる。今回起動した2コンテナのIPアドレスも情報に含まれている
t2$ sudo docker network inspect [ネットワーク名]
(t1)一括停止・一括削除
- ネットワークも削除される
t1$ sudo docker-compose down
(t1)コンテナ、ネットワークの状態確認
- いずれも消えてるはず
t1$ sudo docker ps -a
t1$ sudo docker network ls
- MySQLへの接続先IPアドレスを明示的にdocker-compose.ymlに書かなければならないのをなんとかする
(t1)docker-compose.ymlを編集
- webコンテナのMYSQL_PORT_3306_TCP_ADDR環境変数設定を削除する代わりに、linksという設定を追加
- mysqlコンテナのports設定も削除
t1$ vim docker-compose.yml
version: '2'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: test
web:
build: .
ports:
- "80:80"
links:
- mysql
(t1)test.phpの編集
- test.phpの2行目でMYSQL_PORT_3306_TCP_ADDR環境変数からMySQL接続先IPアドレスを取っていたところを、単に「mysql」に変更
t1$ vim test.php
<?php
$db = new PDO('mysql:host=mysql;dbname=test', 'root', 'admin');
$db->query("CREATE TABLE IF NOT EXISTS test (name VARCHAR(100))");
$db->query("INSERT INTO test VALUES('test!')");
$st = $db->query("SELECT * FROM test");
var_dump($st->fetchAll());
?>
(t1)一括起動
- webコンテナのイメージの再ビルドもしたいので
--build
オプションをつける
t1$ sudo docker-compose up -d --build
(t1)動作確認
- ちゃんと動作するはず
t1$ curl localhost/test.php
(t1)webコンテナ内で「mysql」がちゃんと名前解決できているか確認
- Docker Composeだと、
-it
オプションつけなくてもコンテナ内でbashのコマンドプロンプトが取れる - pingがちゃんと返ってくるはず。これはDocker Composeが提供する機構により、ホスト名→IPアドレスの名前解決ができているから
- 解決されたIPアドレスは追加された内部ネットワークのものになっている。このため、1つのdocker-compose.ymlに閉じていれば、ポートを外部に開放する必要はなくなる
t1$ sudo docker-compose exec web bash
container# ping mysql
t1で^C
したあと、以下を実行
docker-compose up --build
してイメージの再ビルドが走ると、ゴミイメージが残りがちなので、要らないものは消す
t1$ sudo docker-compose down
t1$ sudo docker images
ねらい:Dockerをチーム内コラボに活用するための知識を獲得する
https://speakerdeck.com/ozzozz/dockerimezizhi-kichang-falsejian を説明
$ sudo docker run -d -p 5000:5000 --name=registry registry:2
[チーム名]
はなんでもよい
$ sudo docker tag [チーム名]/web:v2 localhost:5000/[チーム名]/web:v2
$ sudo docker push localhost:5000/[チーム名]/web:v2
$ sudo docker images
$ sudo docker rmi -f [[チーム名]/web:v2のイメージID]
$ sudo docker pull localhost:5000/[チーム名]/web:v2
$ $ sudo docker images
$ sudo vim /etc/sysconfig/docker
以下の行を追加
INSECURE_REGISTRY='--insecure-registry=[プライベートDockerレジストリのIPアドレス]'
Dockerエンジンを再起動
$ sudo systemctl restart docker