Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

今日から始める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の起動後にされることを確認できました

関連記事

参考

yu-croco
Zennに移行中: https://zenn.dev/nameless_gyoza
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away