13
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ローカル開発環境自動化のススメ

Last updated at Posted at 2022-04-14

ごきげんよう。皆さんはWebアプリ開発の際に環境構築ってめんどくさいなぁと思ったこと、ありませんか?私はあります。もし「そんなこと経験したことないなぁ」という方であれば、レガシーソフトウェア改善ガイドの第7章を読みましょう。

かいつまんで説明すると、散らばったビルドスクリプトや環境構築手順書が腐っている上に、ひとつひとつコマンドを叩いたり、DBアカウントの申請が必要だったりと、レガシーソフトウェアはその開発環境の構築すらも困難になっていることがあります。それを vagrant + ansible で自動化し、「仕事を始める日」(テイク2)では、vagrant upを起動した後にコーヒーマシンの使い方を調べる余裕までできちゃうぜ、っていうストーリーです。

これ自体はすごく素敵なことなのですが、最近の macOS だと vagrant が動かないことがあったりして、そもそもの vagrant 環境構築が辛くなってきました。ので、vagrantの代わりに docker を使って幸せになりましょう、というのがこの記事の趣旨です。

ローカル開発環境自動化、とは?

vagrantもansibleも聞いたことない、っていう、ちょっと昔の私からすると「ローカル開発環境自動化ナニソレ?おいしいの?」って感じだったので、改めてそのメリットを整理します。ローカル開発環境自動化を行うことで、以下のような開発体験が得られます。

  • dockerが入っているPCであれば、あなたのアプリを動作させることができます。
  • コマンド1つで、DBの立ち上げ、複数のアプリの立ち上げだって可能です。
  • ソースコードはdockerコンテナと共有できるので、コンテナ側でホットリロード状態で起動していれば、コードの変更を検知して自動で再コンパイルさせることもできます。

ただし、現状dockerコンテナは基本的にはlinuxなので、Xcode Toolchainを要求するようなmacOS/iOS開発だと、恩恵が得にくいかもしれません...

基本戦略

ここでは、 PlayFramework を例に、ローカル開発環境を自動化してみましょう。手始めにこんな docker-compose.yml を用意します。

docker-compose.yml
version: '3'

services:
  app:
    build:
      context: .
      dockerfile: ./docker/local/app/Dockerfile
    command: sbt run
    environment:
      - SBT_OPTS=-Dsbt.boot.directory=.sbt -Dsbt.coursier.home=.coursier
    ports:
      - "9000:9000"
    volumes:
      - .:/app
docker/local/app/Dockerfile
FROM hseeberger/scala-sbt:11.0.14.1_1.6.2_2.13.8

# locale
RUN apt-get update && apt-get install -y locales
RUN sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen && locale-gen
ENV LC_ALL=ja_JP.UTF-8 TZ=Asia/Tokyo

# volumes
WORKDIR /app

これを docker compose up すると、

  • 日本語ロケールで
  • sbt/coursierはホスト側にキャッシュして(2回目以降はキャッシュからjarを解決できる)
  • ソースコードはコンテナと共有し
  • ホットリロード状態で起動

したPlayアプリが http://localhost:9000 で立ち上がります。あとはソースコードを変更したら勝手にリコンパイルされるので、ブラウザで変更を確認できます。便利でしょ?

ここからは、これを前提として、細かい tips を紹介します。

特定のディレクトリはホストと共有させない

例えばnode_modulesのように、OSに依存したバイナリを含むディレクトリをmacOSホスト+Linuxコンテナで共有すると、ホストかコンテナのどちらかでしか起動できなくなります。そういうめんどくさい環境は作りたくないですよね?

全てのソースコードを共有しましたが、一部のフォルダだけ共有したくない場合はdocker-compose.ymlvolumesにそのフォルダ名を書くと、共有から外れます。

docker-compose.yml
    volumes:
      - .:/app
      - /app/node_modules

ただ、こうしてしまうとコンテナ側には node_modules がないので、npm ciが失敗してしまいます。なので、このアプリの場合はDockerfileの中でnode_modulesを構築してしまいます。

docker/local/app/Dockerfile
# (上の続き)

# install node packages
COPY package*.json /app/
RUN npm ci

Dockerfileビルド時はappディレクトリには何もマウントされてないので、npm ciに必要なpackage*.jsonをコンテナにコピーしておきます。

https でアクセスしたい

アプリがなんらかの外部リソースにアクセスする際にhttpsを要求すること、あると思います。これは https-portal を使いましょう。

docker-compose.yml
services:
  ...

  https-portal:
    image: steveltn/https-portal:1
    ports:
      - "80:80"
      - "443:443"
    links:
      - app
    restart: always
    environment:
      STAGE: local
      DOMAINS: "localhost.hoge.jp -> http://app:9000"
    volumes:
      - ./.ssl_certs:/var/lib/https-portal
/etc/hosts
127.0.0.1       localhost.hoge.jp # 追加

STAGE: localという設定は自己証明書を自動で作成する設定になります。作成された証明書は.ssl_certsに配置されます(キャッシュ用)。これで https://localhost.hoge.jp でアプリにアクセスすることができます。

一緒にDBも立ち上げたい

例えば postgres だとこんな感じ。

docker-compose.yml
services:
  db:
    image: postgres:10.3
    environment:
      - POSTGRES_DB=${POSTGRES_DB_NAME:-app}
      - POSTGRES_USER=${POSTGRES_DB_USER:-app}
      - POSTGRES_PASSWORD=${POSTGRES_DB_PASSWORD:-app}
    ports:
      - "5432:5432"

  app:
    ...
    
    environment:
      - POSTGRES_DB_URL=jdbc:postgresql://db:5432/${POSTGRES_DB_NAME:-app}
      - POSTGRES_DB_USER=${POSTGRES_DB_USER:-app}
      - POSTGRES_DB_PASSWORD=${POSTGRES_DB_PASSWORD:-app}
    links:
      - db

postgresでは「デフォルトのDB名(POSTGRES_DB)」「ユーザー名(POSTGRES_USER)」「パスワード(POSTGRES_PASSWORD)」くらいは設定に含めておくと良さげ。使う側はJDBC用のURLとユーザー名、パスワードを環境変数として設定しておく感じ。linksdbサービスを指定することで、appdbの立ち上げ後に立ち上がり、かつdbをホスト名として利用することができます(上でJDBC用のURLにdb:5432として利用している)。

通常は(Playみたいなリッチなフレームワーク使ってると)フレームワークのマイグレーションツール使うのが楽ですが、仮に使ってなかったとしても、そういうDockerfile用意してlinksdbを指定してあげるといい感じです。

docker/local/db_setup/Dockerfile
FROM postgres:10.3

# install flyway
# https://flywaydb.org/documentation/usage/commandline/#-linux
RUN apt-get update && apt-get install -y wget
RUN VERSION=8.5.7 && wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/${VERSION}/flyway-commandline-${VERSION}-linux-x64.tar.gz | tar xvz && ln -s `pwd`/flyway-${VERSION}/flyway /usr/local/bin

# volumes
WORKDIR /app

# setup
ENTRYPOINT ["/app/docker/local/db_setup/entrypoint.sh"]
docker/local/db_setup/entrypoint.sh
#!/bin/sh
set -euC

# なんかいい感じのマイグレーション処理
flyway \
  -user="$POSTGRES_DB_USER" \
  -password="$POSTGRES_DB_PASSWORD" \
  -url="$POSTGRES_DB_URL" \
  -locations=filesystem:db/migration/ \ # db/migration フォルダにいい感じに作っとく
  migrate
docker-compose.yml
services:
  ...

  db_setup:
    build:
      context: .
      dockerfile: docker/local/db_setup/Dockerfile
    environment:
      - POSTGRES_DB_URL=jdbc:postgresql://db:5432/${POSTGRES_DB_NAME:-app}
      - POSTGRES_DB_USER=${POSTGRES_DB_USER:-app}
      - POSTGRES_DB_PASSWORD=${POSTGRES_DB_PASSWORD:-app}
    links:
      - db
    volumes:
      - .:/app

独自ホストを解決したい

例えば、別でホストされているアプリと接続する必要があり、開発環境では、開発環境からしか繋げないような場所にあるオンプレのサーバーに接続したいとか、まあ、あると思います。そんな時は extra_hosts を使います。

docker-compose.yml
services:
  app:
    ...
    
    extra_hosts:
      - qa1:192.168.0.101
      - qa2:192.168.0.102

これでappサービス内ではqa1ホストやqa2ホストにアクセスできます。

まとめ

私はこれまでDockerの主な用途はクラウド上にいい感じにホストしてうまうま、という世界しか知らなかったので、このようにローカル開発環境自動化としても利用できるのはちょっと目から鱗でした。今後も便利に使っていきたいと思います。

13
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?