Edited at

今日から始めるDocker【docker-composeを使ってGo & Mysqlをよしなに起動しよう編】

More than 1 year has passed since last update.

前略、最近暑くなってきましたね。 :sunglasses:

ちょっと前に買ったサーキュレーターさんも今日から私の隣で大活躍してくれてます!

さて、前回はRubyを例にして単体プロセスをDockerfileで管理する方法を学びました。

今回は、複数プロセスをどうやって管理するかについて学んでいきます。


対象読者


  • まだDockerに触れたことがない人

  • Dockerをこれから使いたいなーと思っている人

  • Dockerfileは何となく分かるけど、docker-composeがよくわからない人


記載していること


  • docker-composeの解説

  • docker-composeを使って複数プロセスをよしなに起動する方法


サンプルコード


目次


  • docker-composeを理解しよう

  • docker-composeを書いてみよう

  • なんとなく動かしてみよう

  • プロセスを立ち上げる順番を考慮してみよう

  • なんとなく動かしてみようver2


docker-composeを理解しよう


docker-composeとは

公式によると


Compose is a tool for defining and running multi-container Docker applications.


要は、複数のDockerコンテナを使ってアプリケーションを立ち上げる際の管理をよしなにしてくれるツール、というカンジです。

実際のwebサービスを立ち上げる時には複数のプロセスを立ち上げて管理する必要があるので、その辺の管理情報をdocker-compose.ymlというファイルに記載すると、よしなに連携してくれます。


Dockerfileとdocker-compose.ymlの違い(雰囲気)


  • Dockerfileには個々のコンテナを構築するために必要な細かい手順(どんなツールをインストールするかなど)を記載します

  • docker-compose.ymlには個々のコンテナの起動定義(どんな感じで起動していくか)を記載します

  • 要は、Dockerfileは個々のコンテナに関する説明書で、docker-compose.ymlはコンテナ全体に関する説明書です。


docker-composeを書いてみよう

GolangとMysqlを使ったアプリケーションを想定してdocker-compose.ymlを書きます

あれが無い、これが無いという書きっぷりかもしれませんが、優しく見てください


docker-compose.yml


# docker-composeのバージョン
version: '3'
# services配下に各コンテナの情報を記載する
services:
# ここからはmysqlのコンテナに関する情報
# "mysql"はサービス名(任意につけてok)
mysql:
# コンテナに名前をつけることもできる
container_name: mysql
# Dockerfileのあるディレクトリのパスを指定する
build:
context: .
dockerfile: ./docker/mysql/Dockerfile
# mysqlのホスト名を決める
hostname: mysql
# 外部に公開するポートを指定する
# これがないと外部からアクセスできない
ports:
- "3306:3306"
# mysqlの設定
environment:
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: password
# docker-compose run実行時に実行される
# オプションは日本語文の字化けに対するおまじない
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --skip-character-set-client-handshake
# パスをボリュームとしてマウント
# :の左側がホストディレクトリ、:の右側がコンテナ上のディレクトリ
volumes:
- "db-data:/var/lib/mysql"
# 初期設定を読み込む
- "./docker/mysql/my.conf:/etc/mysql/my.conf"
# MySQL起動時にDBを作成する
- "./docker/mysql/init/:/docker-entrypoint-initdb.d"
# ここからはGolangのアプリケーションに対する情報
sample_docker_compose:
# mysqlのコンテナとリンク付けする
# mysqlコンテナを起動してからsample_docker_composeを起動してくれるようになる
links:
- mysql
build:
context: .
dockerfile: ./docker/golang/Dockerfile
container_name: sample_docker_compose
ports:
- "8080:8080"
volumes:
- .:/go/src/sample_docker_compose
# docker-compose run実行時に実行される
command: go run main.go
# トップレベルでvolumesを定義すると、各サービスからボリュームを参照できる
volumes:
db-data:
driver: local

これらに付随するDockerfileは下記のようなディレクトリ構成で書きます

sample-docker-compose/

 ├ docker-compose
 ├ main.go # <- sample_docker_composeのことで、port:8080でサーバーを起動するだけ
 ├ docker/
 │ └ golang/
 │   └ Dockerfile
 │ └ mysql/
 │   └ Dockerfile
 │   └ my.conf # <- MySQLの初期設定用
 │   └ init/ # <- MySQLの初期設定用
 │     └ create_db_table.sql # <- MySQL起動時にテーブル作成用

GolangのDockerfile


golang/Dockerfile


# 使用するGolangのイメージを指定する
FROM golang:1.8

# 必要なパッケージなどなどをインストールする
RUN apt-get update -qq && \
apt-get install -y mysql-client vim

# ワーキングディレクトリを指定する
WORKDIR /go/src/sample_docker_compose

# sample_docker_compose直下のディレクトリをコンテナ上に載せる
ADD . .

# プロジェクトをビルド
RUN go build


mysqlのDockerfile


mysql/Dockerfile

FROM mysql:5.7

RUN chown -R mysql /var/lib/mysql && \
chgrp -R mysql /var/lib/mysql


ついでに、main.goはこんな感じ

http://localhost:8080を叩くと返事をくれるだけのwebサーバー


main.go

package main

import(
"fmt"
"net/http"
)

func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, from Docker container!")
}



なんとなく動かしてみよう

下記を実行する(省略した他のファイルに関しては、サンプルコードを参照くださいm(__)m)

# 複数のプロセスに対してよしなにビルドしてくれる

$ docker-compose build
# 複数のプロセスをよしなに起動してくれる
$ docker-compose up

ターミナル上にはブワーッとログが出て、GoのアプリケーションとMySQLが起動します

試しにhttp://localhost:8080/にアクセスすると、Hello, from Docker container!と返されます


プロセスを立ち上げる順番を考慮してみよう

さて、以上でひとまず複数のコンテナを起動できたのですが、実際のアプリケーションを扱っていく上では問題となる事項が出てきました

docker-compose.yml上でlinksを記載することでコンテナ起動の順番はよしなにやってくれますが、MySQLが起動するまでは待ってくれません

コンテナの起動イコールプロセスの起動ではないので、このままではGoのアプリケーションがMySQLより先に起動完了する可能性があります

説明簡略化のためにGo側でMySQLに対してアクセスする処理は書いていませんが、一般的なwebアプリであればwebサーバーがDBへアクセスしにいくと思います

その場合、MySQLがまだ起動していないのにアクセスを試みることになります

下手しー、サーバーが落ちます

これに対する対処としては、アプリ側でなんとかするのが一般的のようです

どうするかというと、MySQLが起動するまで待ってからGoのアプリケーションを起動するスクリプトを書き、docker-compose.ymlのsample_docker_composeのcommandで実行します


docker-compose.yml

# command: go run main.go # <-このままじゃマズイ

command: sh ./start_app.sh # <- ひとまずシェルを呼ぶ

$ touch start_app.sh

$ chmod 755 start_app.sh


start_app.sh

# !/bin/bash

# MySQLサーバーが起動するまでmain.goを実行せずにループで待機する
until mysqladmin ping -h mysql --silent; do
echo 'waiting for mysqld to be connectable...'
sleep 2
done

echo "app is starting...!"
exec go run main.go



なんとなく動かしてみようver2

再度docker-composeを立ち上げて見ます

$ docker-compose up

しばらくログにwaiting for mysqld to be connectable...が出て、MySQLが立ち上がった後にapp is starting...!が表示されるかと思います

GoのアプリケーションがDBの起動後にされることを確認できました


関連記事


参考