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アプリケーションを起動させる必要がありました。
面倒くさい…コマンドを一回叩くだけで実施したい…
てなわけで次の章へ↓↓
頑張った成果物
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
Special Thanks
最後の素敵なイラストは たま子さん が描かれたイラストで、掲載許可をいただきました。ありがとうございます。
坂本たま子 (@sakamototamaco) | Twitter