EY-Office ブログ

Dockerとは何なのか?の続き、開発環境をDockerに移行中です

Dockerとは何なのか?の続きです。この記事では偉そうにDockerに付いて解説しましたが、実はEY-Officeの開発環境はDocker化されていませんでした。私はDockerについてはこのブログ記事のように2014年には試していました、また仕事でDockerを使った環境での開発も行っていました。

しかし、EY-Office主体のECサイトの開発環境等ではDockerを使っていませんでした。理由はこのブログ記事のように初期のDockerに触れて必ずしも良くなイメージを持っていました。またMac上での開発環境に問題もなかったので、ずっとDockerを使わずに来てしまいました。

その間に、Docker Desktop for Mac / Docker Desktop for Windows のような便利なDocker環境が現れ、 CircleCIGitHub ActionsのようにDockerを基本としたCI/CDサービスなども現れ、世の中はどんどんDockerへと向かっていました。

私もこれはヤバイなと思いながらも、なかなかDockerへと舵が切れずにいました。しかし、GitHub ActionsでCI環境を作るという目標ができたので、ECサイトの開発環境をDockerに移行し始めました。

Docker https://www.flickr.com/photos/134416355@N07/31518965950

Dockerとは何なのか?の続き

Dockerとは何なのか?ではDockerの原理、Dockerイメージなどを説明しましたが、今回はさらにいくつかのトピックを説明したいと思います。

Dockerfile

Dockerイメージの作成手順を記述したものがDockerfileです。Dockerfileには

  • どのDockerイメージを元にするかの FROM
  • Docker内でプログラムのインストール、ディレクトリー作成等のコマンドを実行する RUN
  • Dockerを起動しているMac / PCからファイル等のコピーを行う ADDCOPY
  • 環境変数の設定を行う ENV

などが書かれています。下の「開発・テスト環境の構築」にDockerfileの例を説明しています。Dockerの作成・管理を行うコマンドはdockerです。

Docker Compose

DockerはLinuxですから多数のサーバープログラムを動かすことも可能ですが、管理のし易さ、サーバーを並べてスケーラビリティーを上げたり、可用性を高める事を考え1つのDocker上では1つのサーバーを動かす事が多いです。
たとえばECサイトのようなサービスでは、RDB、アプリケーション(例、Ruby on Rails)、Webサーバー(例、Nginx)、高速化のためのキャッシュ(例、Redis)に分ます。

複数のDockerが連携して動かすために管理や、各Dockerの設定情報を管理するものがDocker Composeです。設定ファイルのデフォルトは docker-compose.yml で、コマンドはdocker-composeです。

Kubernetes (k8s)

多数のDockerを使った、大規模なシステムを運用、デプロイなどを行う標準規格です。 詳しくはKubernetesとは何か? を読んでください。

開発・テスト環境の構築

Docker構成

現在、Dockerへ移行中のECサイトの開発環境のに付いて簡単に説明します。このサイトはRuby on Rails + PostgreSQL (+ Nginx)で構成されています。開発環境ではNginxは不要なので、DockerはPostgreSQL(名前 pg)とRuby on Rails(名前 rails)の2つになります。

PostgreSQL用Dockerは、PostgreSQLと全文検索のPGroongaのみなので、PGroonga開発チームの公開するDockerイメージ を利用する事にしました。このDockerはデータベースのみなのでLinuxディストリビューションは軽量なAlpineにしました。

Ruby on Rails用DockerはRuby、Ruby on Railsに加え多数のGem、いつかのコマンドが必要なので、Dockerfileを書いて独自に構築しました。またDocker利用時の起動時間短縮のため、DockerイメージはDockerHubに保存しました、Rails等をバージョンアップした際にはイメージは構築し直しになりますが、もうメンテ状態なので頻繁にはなく問題はないと思います。

  • docker/rails/Dockerfile
    • FROM: で判るようにRuby 2.6、Linuxディストリビューションは実行環境のUbuntuに近いDebianを元に作っています
    • いくつかのコマンドやテストで使うphantomjsをインストールしています
    • 日本語の全文検索などを行うので、日本語環境にしています、またタイムゾーンも日本語にしています
    • COPYで開発環境のGemfile, Gemfile.lockをコピーしGemをインストールしています
    • ruby:2.6-stretchに入っているbundlerは1.Xなので2.1.4をインストールしています
FROM ruby:2.6-stretch

RUN apt-get update && \
    apt-get install -y locales-all postgresql-client-9.6 tidy --no-install-recommends && \
    rm -rf /var/lib/apt/lists/* && \
    curl -L  https://github.com/Medium/phantomjs/releases/download/v2.1.1//phantomjs-2.1.1-linux-x86_64.tar.bz2  | tar xj -C /tmp && \
    cp /tmp/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin && \
    rm -rf /tmp/phantomjs-2.1.1-linux-x86_64 && \
    mkdir -p /usr/src/app
WORKDIR /usr/src/app

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ Asia/Tokyo

COPY ./Gemfile /usr/src/app
COPY ./Gemfile.lock /usr/src/app
RUN gem install bundler -v 2.1.4
RUN bundle install

EXPOSE 3000
  • Dockerの作成手順は以下のようになります
    • Dockerイメージ名は今のところ伏せておきます
    • docker build でイメージを構築します
    • docker tagでRailsのバージョンをTAGに設定します
    • docker push でDockeHubに保存します
$ docker build -t xxxx/ruby_rails_xxxx -f docker/rails/Dockerfile .
$ docker tag <IMAGE-ID> xxxx/ruby_rails_xxxx:5.2.4.3
$ docker push xxxx/ruby_rails_xxxx:5.2.4.3

docker-compose

以下がpg, rails用Dockerを使い、開発環境を起動するdocker-composeの設定です

  • docker-compose.yml
    • rails用DockerはMac上の開発環境.をDockerの/usr/src/appにマウントし、プログラムのコードはMac上のファイルを使います、これによりMac上のエディター(VSCode)でコードの編集が行えます
    • おなじみのポート3000をアクセスすればRailsが起動します
    • command: でDocker上でコマンドを実行します
      1. Railsのpidファイルが残る事もあるので削除
      2. wait-for-it.shでPostgreSQLが起動するまで待ちます、depends_onでpgを指定していますがPostgreSQLが起動前にDB操作が動くのを防いでいます
      3. set_seed_db.shで開発環境用データを取り込んでいます、テーブル作成等もここで行われます
      4. 最後にrailsを起動
    • pg用Dockerの設定は
      • Dockerイメージはgroonga/pgroonga:latest-alpine-12-slim
      • あまり使わないですが、Macからポート5433でこのPostgreSQLにアクセスできるよう設定
      • rails用Dockerenvironmentで日本語環境に設定
      • POSTGRES_USER、POSTGRES_PASSWORDでPostgreSQL管理アカウントの設定
      • volumesでPostgreSQL起動時に実行されるSQLファイルを指定。この中で開発、テスト用データベース、アカウントの作成、PGroonga用extention作成を行っています
version: "3"

services:
  rails:
    container_name: rails
    image: xxxxx/ruby_rails_xxxx:5.2.4.3
    environment:
      - PGHOST=pg
      - PGPASSWORD=xxxxxx
    ports:
      - "3000:3000"
    volumes:
      - ".:/usr/src/app"
    depends_on:
      - pg
    command: bash -c "
      rm -f /usr/src/app/tmp/pids/server.pid &&
      ./docker/bin/wait-for-it.sh pg:5432 --
      ./tools/set_seed_db.sh &&
      ./bin/rails server -b 0.0.0.0"

  pg:
    container_name: pg
    image: groonga/pgroonga:latest-alpine-12-slim
    ports:
      - "5433:5432"
    environment:
      - LANG=ja_JP.UTF-8
      - LANGUAGE=ja_JP:ja
      - LC_ALL=ja_JP.UTF-8
      - TZ=Asia/Tokyo

      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=xxxxxxxx
    volumes:
      - ./docker/pg/init:/docker-entrypoint-initdb.d
  • 開発用Dockerの起動は以下のようになります
    • 必要があればdocker-compose logsでログを表示
$ docker-compose up -d
$ docker-compose logs rails
  • テスト用Dokerのdocker-compose.test.yml
    • docker-compose.ymlとの差分が書かれています。起動コマンドが開発環境とは違い
      1. PostgreSQL待ち
      2. テスト用DBのマイグレーション実行
      3. Model, Controller等のRSpec実行
      4. 管理画面のfeature specを実行
      5. 本当はこの後にE2Eテストを実行するのですが、現在対応中
version: "3"

services:
  rails:
    command: bash -c "
      ./docker/bin/wait-for-it.sh pg:5432 --
      rake tmp:create &&
      rake db:migrate RAILS_ENV=test &&
      rspec spec &&
      rspec spec/features/*.rb"

テスト用Dokerのを起動するには作成手順は以下のようになります。 docker-compose.test.ymlには差分しか書いてないので、docker-compose.ymlも指定します。

$ docker-compose -f docker-compose.yml -f docker-compose.test.yml up

GitHub Actions

GitHub Actionsでテストを実行するワークフローファイルは以下のようになります。docker-compose.ymlに似ていますが微妙に違い苦労しました。

  • .github/workflows/main.yml
    • on: はテスト(CI)を起動する設定
      • scheduleは時間指定、ここでは1時間に1回
      • workflow_dispatch: inputs: はGitHubページにあるボタンからの起動、ワークフロー開発時に便利です
    • jobs: は docker-compose.ymlに似ているので、解説なしでほぼわかるかと思います
      • ${{ github.workspace }}はテストを行う作業用ディレクトリーがわかる変数です。GitHub Actionsのvolumesは絶対パスで指定しないといけないようです
      • Dockerイメージはpublicなものしか指定できません、少し困っています
      • pg:にはvolumes:を指定していませんが、pg:が起動される時点ではまだコードがチェックアウトされてないからです
    • steps: に実行コマンドを書きます
      • uses: actions/checkout@v2でGitHubからコードのチェックアウトが行われます、ブランチはwith: ref: で指定します
      • run: で実行するコマンドを指定します
      • PostgreSQLのテスト用データベース等の作成はこの時点で行っています
      • 後のマイグレーション、RSpecの実行はdocker-compose.ymlと同様です
name: CI

on:
  schedule:
    - cron: '0 * * * *'
  workflow_dispatch:
    inputs:

jobs:
   build:
    runs-on: ubuntu-latest
    container:
      image: xxxx/ruby_rails_xxxx:5.2.4.3
      env:
        PGHOST: pg
        PGPASSWORD: xxxxx
        RAILS_ENV: test
      volumes:
        - ${{ github.workspace }}:/usr/src/app

    services:
      pg:
        image: groonga/pgroonga:latest-alpine-12-slim
        env:
          LANG: ja_JP.UTF-8
          LANGUAGE: ja_JP:ja
          LC_ALL: ja_JP.UTF-8
          TZ: Asia/Tokyo
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: xxxxxx
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

    steps:
      - name: fetch from GitHub
        uses: actions/checkout@v2
        with:
          ref: docker
      - name: create DB
        run: PGPASSWORD=xxxxxx psql -U postgres -f docker/pg/init/1_init.sql
      - name: create tmp
        run: rake tmp:create
      - name: DB migration
        run: rake db:migrate
      - name: Run rspec models, controller, helper
        run: rspec spec
      - name: Run rspec admin feature
        run: rspec spec/features/*.rb

まとめ

いろいろと苦労しましたが、Dockerベースの開発環境とGitHub ActionsのCI環境がだいたいできました。 Dockerベースの開発環境もCI環境は安定して動いています。

開発環境が簡単に短時間で作れるのには感動的です、まだDockerに不慣れで悩む事も多いですが、Dockerベースに移行して良かったと思います。Dockerアレルギーもすっかり無くなりました ^^)/

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ