docker-composeでローカルでの動作確認用環境を準備したお話

この記事はフラー Advent Calendar 2020 の21日目の記事です。20日目は
@jfurudo さんでApp Clips を試してみる - Qiita でした。


前置き

前回の投稿でチラッとお話した通り、今年7月にフラー株式会社に転職しました。

初めましてのGolangに戸惑いはしたものの、研修内容がしっかりしており、OJTが終わる頃にはそこそこGolangと仲良くなれた気がしました。

OJT期間中のことはこちらの記事に詳しく載ってますので、よろしければ合わせて読んでください。

本記事ではGolang…の話からはちょっと外れた、それでいて私がOJT期間中に四苦八苦したdocer-composeでのローカル動作確認環境の構築の話をしたいと思います。

背景

OJT期間中に作成したのはいわゆる3層アーキテクチャのシステムです。

  • リバースプロキシ(nginx)
  • アプリケーション(goで構築)
  • データベース(mysql

メインで開発していたのはアプリケーションだったのですが、付随してnginxやmysqlを使用する必要がありました。


…ということは、goで作成したアプリケーションを起動させ、動作確認をするためには毎度nginxやmysqlを起動させる必要があります。

「いやいや、PC起動時に自動で起きるように設定していれば毎度ぽちぽちする必要はない!」という意見もあるかと思います。
その通りだと思います。

ただ、そうなると業務で同時に複数のシステムを構築しているときなど、ポートがかち合う不都合が出てきてしまいます。
ソレはちょっと避けたい

そんなこんなでローカルで仮想環境を準備したいのでDocker、特に複数のコンテナを準備するためにdocker-composeを使用することとしました。


また作成したgoアプリケーションは起動時にDBコネクションを作成する設計になっていました。
そのためmysqlサーバが起動完了しDBが準備されるのを待ってから、goアプリケーションを起動させる必要がありました。


面倒くさい…コマンドを一回叩くだけで実施したい…

てなわけで次の章へ↓↓

目指すもの

この記事で目指すのものは次の通りです。

  • nginx、 mysqlgolangで作成したアプリの実行環境を構築する
  • mysqlサーバでDBが準備できてからgoアプリケーションを実行させる
  • コマンドを一回叩くだけで上記が実行できる

頑張った成果物

Dockerfile

goで作成したアプリケーションをwebアプリケーションサーバに載せる仕組みを準備します。
ローカルでgoアプリケーションをojtという名称でコンパイルし、Alpine Linuxのイメージに転送します

FROM alpine:latest

# コンパイルしたアプリケーションを配置するディレクトリ
WORKDIR /app 
# 先のディレクトリニにコンパイルしたアプリケーションをコピー
COPY ojt /app 

# 下のシェルスクリプト で必要なmysql-clientをインストール
RUN apk add --no-cache mysql-client
# DB起動を待つためのシェルスクリプト をコピー
COPY scripts/waitdb.sh /scripts/waitdb.sh

最後の「DB起動を待つための下準備」の詳しい中身は後述します。

docker-compose

リバースプロキシ、webアプリケーション、データベースを同時に起動できるよう、docker-composeを準備します。

version: '3'
services:

    db:
        image: mysql:5.7
        restart: always
        container_name: ojt-db
        ports: 
            - "3306:3306"
        volumes:
            - ./mysql/init:/docker-entrypoint-initdb.d
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_USER: admin
            MYSQL_PASSWORD: hoge


    web:
        build: .
        container_name: ojt-web
        ports: 
            - "8080:8080"
        depends_on: 
            - db
        # 環境変数の設定
        environment: 
            - GOPATH=/go
            - OJT_DB_HOST=ojt-db
            - OJT_DB_USER=admin
            - OJT_DB_PASS=hoge
        command: sh /scripts/waitdb.sh /app/ojt
    
    proxy:
        build: nginx/.
        container_name: ojt-proxy
        ports: 
            - "8000:80"
        depends_on: 
            - web

起動順は データベース→webアプリケーション→リバースプロキシ の順にしました。
portsで指定しているポート番号をよしなにすれば、他のシステムを同時に扱っていたとしてもポート番号の被りを防ぐことができます。

またcommand: sh /scripts/waitdb.sh /app/ojt によって先DockerfileでCopyをしていたシェルスクリプトを実行します。

waitdb.sh

DBの準備ができてからWebアプリケーションを起動するシェルの中身です。

#! bin/sh
set -e

# 引数からDBホスト名とojtアプリの起動コマンドを受け取る
cmd=$1

echo "Waiting for mysql"

until mysql -h"$OJT_DB_HOST" -u"$OJT_DB_USER" -p"$OJT_DB_PASS" &> /dev/null
do
        >&2 echo -n "."
        sleep 1
done

>&2 echo "MySQL is up - executing command"
exec $cmd

1秒ごとにDB接続を試し、成功したらgoアプリケーションを実行する仕組みにしています。

Makefile

さて、ここまで準備したはいいですが、goアプリケーションをコンパイルしてそれからdocker-compose up -dをするのでは、当初の目的の「コマンドを一回叩くだけ」には微妙に外れてしまっています。
そのためMakefileを準備します。

BINARY_NAME=ojt

build-linux:
	GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o $(BINARY_NAME) main.go
docker-up:
	make build-linux
	docker-compose up -d

build-linuxコンパイル をし、docker-up で先のコマンドとdocker-compose up -dを実施しています

実行

ではコマンドを一つ実行するだけで実現してみせましょう。

% make docker-up     
(略)
Creating ojt-db ... done
Creating ojt-web ... done
Creating ojt-proxy ... done

yoshi

Special Thanks

最後の素敵なイラストは たま子さん が描かれたイラストで、掲載許可をいただきました。ありがとうございます。
坂本たま子 (@sakamototamaco) | Twitter