コンテナ技術 ハンズオン Vol.3 Docker Compose編

# 本題の前に
---

## 位置付け・前提
本記事は、社内ワークショップ のために用意したものです。そのため、次の前提で進めることをご理解ください。

また、本稿はこちらの続きとなります。

[コンテナ技術 ハンズオン Vol.1 Docker操作編](https://qiita.com/syamane/items/65570b41b3d6d32691a5)
[コンテナ技術 ハンズオン Vol.2 Dockerイメージ編](https://qiita.com/syamane/items/1b552ce28932dbfd3c56)

目的や前提なども上記の内容に準拠します。

---

## docker ハンズオン環境

今回も [Play with Docker](https://labs.play-with-docker.com/) を活用してハンズオンを行います。

[![playwithdocker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107190/bc635cbe-c24f-6cec-c871-f4a8073cfffb.png)](https://labs.play-with-docker.com/)

※要：[Docker Hub](https://hub.docker.com/) アカウント

注意点として、この環境は4時間の制限があります。４時間経過すると環境が削除されてしまう点を注意して下さい。

もちろんローカル環境でも構いません。

---

# ハンズオンの前に

---

## 前回の問題（宿題）

前回のハンズオンで最後に出した問題はこちら。

Q1. CentOS を最新状態にアップデートして、nginx をインストール＆起動した状態のDockerイメージ「nginx1」を作成してください。
Q2. 起動しているコンテナはホストのどのディレクトリにあるでしょうか。
Q3. dockerイメージはどこにあるでしょうか。
Q4. dockerのプロセスはどれか。

うまく ~~ググれカス~~ 答えられましたでしょうか。

---
## 前回の問題の解答

---
### 解答1. CentOS を最新状態にアップデートして、nginx をインストール＆起動した状態のDockerイメージ「nginx1」を作成する方法

```dockerfile:dockerfile
FROM centos
RUN /bin/yum update -y
RUN yum install https://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.8.1-1.el7.ngx.x86_64.rpm -y
RUN sudo yum clean all
ENTRYPOINT /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
EXPOSE 80
```

yum のパッケージデータベースに無いので、こちらのURLから直接インストールするのがポイントです。

https://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.8.1-1.el7.ngx.x86_64.rpm

この dockerfile があるディレクトリで下記のコマンドを実行します。

```bash
docker build -t nginx1 .
```

これで nginx の Docker イメージが出来上がりました。下記のように確認できればOKです。

```bash
$ docker run -it -d -p 80:80 --name httpsvr nginx1
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
93832ab2a64d        nginx1:1.0          "/bin/sh -c '/usr/sb…"   24 seconds ago      Up 23 seconds       0.0.0.0:80->80/tcp   httpsvr
```

80番ポートに接続してみられます。


---
### 解答2. 起動しているインスタンスを一括停止するコマンド

以下のように対象インスタンスをたくさん並べる方法があります。

```bash
docker stop {コンテナID|NAMES} {コンテナID|NAMES} ...
```

以下のように対象インスタンスをすべて一発で停止する方法があります。

```bash
docker stop $(docker ps -q)
```

これ一発で完了です。誤操作すると困るので、数が多くなるとIDを指定した方が無難かも知れません

---
### 解答3. 起動しているインスタンスを一括削除するコマンド

おなじように対象インスタンスをたくさん並べる方法があります。

```bash
docker rm -f {コンテナID|NAMES} {コンテナID|NAMES} ...
```

-f で強制停止＆削除となります。

こちらも以下のように対象インスタンスをすべて一発で停止する方法があります。

```bash
docker rm $(docker ps -q -a)
```

これ一発で完了です。誤操作すると困るので、数が多くなるとIDを指定した方が無難かも知れません

---
### 解答4. 起動しているイメージを一括削除するコマンド

おなじように対象となるイメージをたくさん並べる方法があります。

```bash
docker rm -f {イメージID|REPOSITORY} {イメージID|REPOSITORY} ...
```

-f で利用しているインスタンスの強制停止よび削除も併せて行います。

こちらも以下のように全イメージをすべて一発で削除する方法があります。

```bash
 docker rmi $(docker images -q)
```

これ一発で完了です。ただ、このコマンドは自分の検証環境以外ではかなり危険です。
誤操作すると困るので、数が多くなるとIDを指定した方が無難かも知れません

---

# ハンズオン

---

## 今回のゴール

Docker Compose でコンテナを制御・ビルドできるようになる

---

## 今回やらないこと

* Docker のリソースを制御できるようになる
* Docekr のトラブルシューティングができるようになる
* kubernetes などのオーケストレーションツールは使いません。

---

## これから進める中で必ず理解しておいて欲しいこと

Docker-compose自体の知識は、こちらを読んで獲得してください。

[![Docker Compose入門](https://image.slidesharecdn.com/dockercomposeandswarmmodeorchestrationdistribution-170614223942/95/slide-1-1024.jpg)](https://www.slideshare.net/zembutsu/docker-compose-and-swarm-mode-orchestration)

[![Docker Compose徹底解説](https://image.slidesharecdn.com/dockercomposeguidebook-190306051717/95/slide-1-1024.jpg)](https://www.slideshare.net/zembutsu/docker-compose-guidebook)

本ハンズオンは、上記のスライドの内容を理解している前提で進めます。

非常に分かり易い資料ありがとうございます。＞ [Masahito Zembutsu](https://www.slideshare.net/zembutsu?utm_campaign=profiletracking&utm_medium=sssite&utm_source=ssslideview)様 ([Qiita](https://qiita.com/zembutsu))

---

## Docker Compose

---
### Docker Compose とは

端的に言えば、下記のツールとなります。

[![Docker Compose とは](https://image.slidesharecdn.com/dockercomposeandswarmmodeorchestrationdistribution-170614223942/95/slide-6-1024.jpg)](https://www.slideshare.net/zembutsu/docker-compose-and-swarm-mode-orchestration/6)

---
### Docker Compose の利用意義

[![Docker Compose とは](https://image.slidesharecdn.com/dockercomposeandswarmmodeorchestrationdistribution-170614223942/95/slide-11-1024.jpg)](https://www.slideshare.net/zembutsu/docker-compose-and-swarm-mode-orchestration/11)

---
### Docker Compose の実例

[![Docker Compose とは](https://image.slidesharecdn.com/dockercomposeandswarmmodeorchestrationdistribution-170614223942/95/slide-20-1024.jpg)](https://www.slideshare.net/zembutsu/docker-compose-and-swarm-mode-orchestration/20)

とても複雑な構成のコンテナたちを build して、run できます。

---
### Docker Compose のメリット

[![Docker Compose とは](https://image.slidesharecdn.com/dockercomposeandswarmmodeorchestrationdistribution-170614223942/95/slide-23-1024.jpg)](https://www.slideshare.net/zembutsu/docker-compose-and-swarm-mode-orchestration/23)

Docker Compose で複数のコンテナを連動させて運用していくハンズオンを行っていきます。

---
### はじめての Docker Compose

こちらで試した WordPress + MySQL のコンテナを Docker-compose で起動してみます。

[再び WordPressインスタンスの作成 - コンテナ技術 ハンズオン Vol.1 Docker操作編 - Qiita](https://qiita.com/syamane/items/65570b41b3d6d32691a5#%E5%86%8D%E3%81%B3-wordpress%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90)

Docker Compose では次のように[YAML形式](https://ja.wikipedia.org/wiki/YAML)のファイルを利用します。

```yaml:docker-compose.yml
version: '3.1'          # YAMLバージョンの指定（ほぼ固定）
services:
  db:                   # コンテナの名前
    image: mysql:5.7    # ベースとするイメージ
    restart: always     # ダウンしたら再起動する
    environment:        # 以下で環境変数を指定する
      MYSQL_ROOT_PASSWORD: wppassword
      MYSQL_DATABASE: wp
      MYSQL_USER: wpuser1
      MYSQL_PASSWORD: password
  wp:                   # コンテナの名前 
    image: wordpress    # ベースとするイメージ
    ports:              # 通信ポートの紐付け
      - 8081:80         # {ホストOSの通信ポート}:{コンテナの通信ポート}
    links:              # コンテナ同士の紐付け（IPアドレス無しで通信可）
      - db:mysql        # {コンテナ名}:{別名}
    depends_on:         # 依存関係（どのコンテナが動作することを前提とするか）
      - db
    restart: always     # ダウンしたら再起動する
    environment:        # 以下で環境変数を指定する
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_PASSWORD: password
```

ここでは、MySQL と WordPress を一気にイメージ化できる設定となります。

この docker-compose.yml があるディレクトリで、下記のコマンドを実行します。

```shell
docker-compose up -d
```

バックグラウンドでイメージ生成した後、コンテナ実行することができます。ちなみに、-d を外してフォアグラウンド実行すると、つらつらと実行ログが出力されたのち、アクセスログを確認できる状態になります。

```shell
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
f5f83c1f8db5        wordpress           "docker-entrypoint.s…"   6 minutes ago       Up 48 seconds       0.0.0.0:8081->80/tcp   dc1_wp_1
d1218cd9cc6b        mysql:5.7           "docker-entrypoint.s…"   7 minutes ago       Up 49 seconds       3306/tcp, 33060/tcp    dc1_db_1
```

docker-compose でもコンテナ（インスタンス）の起動状態を確認できます。

```shell
$ docker-compose ps
 Name                Command               State          Ports        
-----------------------------------------------------------------------
wp_db_1   docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp 
wp_wp_1   docker-entrypoint.sh apach ...   Up      0.0.0.0:8081->80/tcp
```

docker ps コマンドとは若干違う、簡潔な表示になっています。

次にインスタンスを停止するには、下記のコマンドで実行します。

```shell
docker-compose stop
```

この状態だとインスタンスが残ります。下記コマンドで確認しましょう。

```shell
docker-compose ps
```

停止するしているインスタンスを再開するには、下記のコマンドで実行します。

```shell
docker-compose start
```


次にインスタンスを停止・削除するには、下記のコマンドで実行します。

```shell
docker-compose down
```

これでインスタンスもイメージも消えました。下記コマンドで確認しましょう。

```shell
$ docker-compose ps
Name   Command   State   Ports
------------------------------

$ docker images ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
```


---

## Docker Compose でイメージ作成&起動する

こちらで紹介されている手順を試してみます。

[docker-compose.ymlでDockerfileを指定したい | cloudpack.media](https://cloudpack.media/44104)

下記３つの構成ファイルを作成してください。

```yaml:docker-compose.yml
version: '3'
 
services:
  container1:
    build:                     # イメージのbuild指定
      context: .
      dockerfile: Dockerfile   # Dockerfileのパス指定
  container2:
    build:                     # イメージのbuild指定 
      context: .
      dockerfile: Dockerfile2  # Dockerfile2のパス指定
  container3:
    build: .
```

```Dockerfile
FROM python:3.6
```

```Dockerfile2
FROM node:latest
```

この docker-compose.yml があるディレクトリで、下記のコマンドを実行します。

```shell
docker-compose up -d
```

このように一括ビルドすることも可能です。起動できていることが確認できたら削除しましょう。

```shell
$ docker-compose ps
     Name                  Command            State    Ports
------------------------------------------------------------
bd_container1_1   python3                     Exit 0        
bd_container2_1   docker-entrypoint.sh node   Exit 0        
bd_container3_1   python3                     Exit 0        

$ docker-compose down
Removing bd_container3_1 ... done
Removing bd_container2_1 ... done
Removing bd_container1_1 ... done
Removing network bd_default

$ docker-compose ps
Name   Command   State   Ports
------------------------------

$ docker images ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

```


ちなみに、build だけすることも可能です。

```shell
docker-compose build
---

違いを確認しておきましょう。

```shell
$ docker-compose ps
Name   Command   State   Ports
------------------------------

$ docker images ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

$ docker-compose up
Creating network "bd_default" with the default driver
Creating bd_container3_1 ... done
Creating bd_container1_1 ... done
Creating bd_container2_1 ... done
Attaching to bd_container3_1, bd_container1_1, bd_container2_1
bd_container3_1 exited with code 0
bd_container1_1 exited with code 0
bd_container2_1 exited with code 0
```

docker のローカルキャッシュしますので、images からは見えません。その代わり、up すると、Docker Hub/レジストリからは取得せずにインスタンスを起動します。

build に非常に時間がかかる場合には、分けて実行するのを推奨するケースもあるようです。

---
## Docker Compose で簡易メールサーバーを構築する

少し実用的なサーバーを作ってみましょう。こちらで紹介されている手順を試してみます。

[docker-compose + mailhogでローカルSMTPテストサーバを手軽に構築する](https://techracho.bpsinc.jp/kotetsu75/2019_08_30/80044)

下記のファイルを作成してください。

```yaml:docker-compose.yml
version: "3"
services:
  mail:
    image: mailhog/mailhog:latest
    ports:
      - "8025:8025"
      - "1025:1025"
```

このファイルがあるディレクトリで、コンテナを起動します。

```shell
docker-compose up -d
```

起動した後、8025 ポートにアクセスすると、受信メールの確認画面が使えます。1025 ポートは送信ポートなので、WEBアクセスしても画面表示はされません。

先の紹介ページのようにテストスクリプトやsendmailコマンドなどでメールの送信テストをしても良いかもしれません。

このように、インフラの検証などに利用しやすいシステムをすぐに用意するのに便利ですね。

---

## Docker Compose で Djangoアプリケーションを構築する

Python などのフレームワークで開発した WEBアプリケーション を収めたコンテナを起動する、といっった使い方には、非常に有効です。

こちらで紹介されている手順を試してみます。

[クイックスタート・ガイド：Docker Compose と Django — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/compose/django.html)

下記３つの構成ファイルを作成してください。

```yaml:docker-compose.yml
version: '2'
services:
  db:
    image: postgres
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000 # コンテナで実行する命令
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db          # dbインスタンス起動後に実行する
```

```Dockerfile
FROM python:2.7
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/
```

```requirements.txt
Django
psycopg2
```

この docker-compose.yml があるディレクトリで、Django プロジェクトを docker-compose コマンドを使って作成します。

```shell
docker-compose run web django-admin.py startproject composeexample .
```

ポイントは、イメージをビルドした後、Django プロジェクトを開始するコマンドをコンテナに送っているところです。

このように、フレームワークと組み合わせた複雑なコンテナを構築して利用することも簡単に実現できます。


---

## Docker Compose で SNSサービス Mastodonインスタンスを構築する

複雑なサービスで構成されている、こちらのSNSサービス「[Mastodon](https://github.com/tootsuite/mastodon)」を確認します。ここでは実際に構築しなくても構いません。

Mastodonは、build に**かなりの時間がかかる**＆**設定ファイルの作りこみが必要**なので、実際に起動できるようにするには少しハードルが高いです。別インスタンスを建てて実行する、あるいは時間のあるときにじっくり実行した方がよいかもしれません。

```shell_session
git clone https://github.com/tootsuite/mastodon
cd mastodon/
cp .env.production.sample .env.production
mkdir postgres
mkdir redis
docker-compose build
docker-compose run --rm web rake secret
```

git で clone したファイルに含まれる docker-compose.yml を使って、build および インスタンスの起動まで行います。

docker-compose.yml を見ると下記のサービスが含まれており、複雑に定義されていることが分かります。

* ruby
* redis
* PostgreSQL
* sidekiq

[![Mastodonコンテナ構成](https://camo.qiitausercontent.com/77407cc9302a3fe0c21004355908fa097212bcc1/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f31333030322f65613236313166382d303434652d643564332d316431612d3538363133623664303234372e706e67)](https://qiita.com/zembutsu/items/f1f1ede26102ba27fce2)
引用：[Docker応用チュートリアル：Mastodon](https://qiita.com/zembutsu/items/f1f1ede26102ba27fce2)

このように複雑な構成のシステムについて、コマンドほぼ一発で build & 起動するにも役立ちます。

---
### Docker Compose リファレンス

Docker-compose の豊富な機能を活用するには、下記を参照するのが良いでしょう。

* 公式リファレンス
    * [Overview of Docker Compose | Docker Documentation](https://docs.docker.com/compose/)
    * (日本語) [Docker Compose — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/compose/toc.html) ※少し古い
* スライド資料
    * [Docker Compose 徹底解説](https://www.slideshare.net/zembutsu/docker-compose-guidebook?qid=a19dd624-4e6c-4a92-b4e4-c0412355fc35&v=&b=&from_search=1)
        * [Tips](https://www.slideshare.net/zembutsu/docker-compose-guidebook/100) 
        * [Docker-composeの利点](https://www.slideshare.net/zembutsu/docker-compose-guidebook/88)
        * [Docker と Docker-compose の互換性](https://www.slideshare.net/zembutsu/docker-compose-guidebook/81)

---

## 問題（宿題）

下記のサイトの docker-compose.yml のいずれかを実行してみましょう。

* [【Docker】これは役に立つ!! docker-compose.ymlの例まとめ](https://qiita.com/okamu_/items/1c8e5fde742ca165fa12)
    * Alfresco
    * Apache Kafka
    * Apache Zookeeper
    * asciinema.org
    * Bind9 Domain Name Server
    * Cloud9
    * CloudFlare
    * Concrete5
    * Confluence
    * Consul Cluster
    * Consul-Registrator
    * DataDog Agent & DogStatsD
    * DNS Update (RFC2136)
    * DNSimple DNS
    * DokuWiki
    * Drone
    * Drone Rancher Node Manager
    * Rancher ECR Credentials Updater
    * Elasticsearch
    * Elasticsearch 2.x
    * AWS ELB Classic External LB
    * Etcd
    * F5 BIG-IP
    * FBCTF
    * Ghost
    * GitLab
    * Gocd agent
    * Gocd server
    * Gogs
    * Grafana
    * Hadoop + Yarn
    * Janitor
    * Jenkins
    * Jenkins Swarm Plugin Clients
    * Kibana 4
    * Let's Encrypt
    * Liferay Portal
    * Logmatic
    * Logspout
    * Logstash
    * MariaDB Galera Cluster
    * Minecraft
    * MongoDB
    * Mumble
    * Netdata
    * Nuxeo
    * Odoo
    * OpenVPN
    * OpenVPN-httpdigest
    * OpenVPN-LDAP
    * Owncloud
    * Percona XtraDB Cluster
    * PHP Adminer
    * Plone 5.0
    * PointHQ DNS
    * PowerDNS
    * Prometheus
    * Puppet 4 .x (standalone)
    * px-dev
    * QuasarDB
    * RabbitMQ
    * rancher-security-bench
    * Registry
    * Registry Convoy
    * REX-Ray
    * RocketChat
    * Route53 DNS
    * ScaleIO NAS/DAS
    * Secrets Bridge
    * Secrets Bridge Agents
    * Sematext Docker Agent
    * Sentry
    * SonarQube
    * Sysdig
    * Sysdig Cloud
    * Taiga
    * TeamCity
    * Traefik
    * Turtl
    * Weave Scope
    * Wekan
    * Wordpress
    * XPilots

---
# おわりに

今回は Docker Compose でコンテナを制御・ビルドする方法について、ハンズオンをしました。

あと少し Docker リソースを管理できるようになってくれば、ようやく現実的な運用に利用できる状態となってきます。

それはまた次のハンズオンで学習しましょう。

[コンテナ技術 ハンズオン Vol.4 Docker管理編](https://qiita.com/syamane/items/4b43dec0202deda25403)

---

# 参考

これまでに紹介した記事以外に、本ハンズオン資料を作成する上で下記の記事を参考にさせていただきました。

* [Overview of Docker Compose | Docker Documentation](https://docs.docker.com/compose/)
* [Docker Compose — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/compose/toc.html)
* [Docker Compose 徹底解説](https://www.slideshare.net/zembutsu/docker-compose-guidebook?qid=a19dd624-4e6c-4a92-b4e4-c0412355fc35&v=&b=&from_search=1)
* [Dockerfile を書くためのベストプラクティス解説編](https://www.slideshare.net/zembutsu/explaining-best-practices-for-writing-dockerfiles)
* [Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html)
* [再び WordPressインスタンスの作成 - コンテナ技術 ハンズオン Vol.1 Docker操作編 - Qiita](https://qiita.com/syamane/items/65570b41b3d6d32691a5#%E5%86%8D%E3%81%B3-wordpress%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90)
* [docker-compose.ymlでDockerfileを指定したい | cloudpack.media](https://cloudpack.media/44104)
* [docker-compose + mailhogでローカルSMTPテストサーバを手軽に構築する](https://techracho.bpsinc.jp/kotetsu75/2019_08_30/80044)
* [Docker+MacでMastodonインスタンスをローカルで構築する｜てくてくぷれいす](https://murabitoleg.com/mastodon/)
* [入門 Docker](https://y-ohgi.com/introduction-docker/)

大変ありがとうございました。