虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

本番環境に寄り添った開発用Docker環境の構築手法

皆さんこんにちは。虎の穴ラボの辻村です。

この記事は「虎の穴ラボ 夏のアドベントカレンダー」17日目の記事です。

目次

対象とする読者

  • これから既存環境を基にした開発用Docker環境を作るという人。
  • ある程度Dockerコマンドが分かる人。

前提

  • 既存の本番環境を基に、開発環境を構築します。
    なるべく本番環境と開発環境のライブラリのバージョンを合わせることで、手戻りを少なくすることを目的としています。
  • 今回の記事はあくまで一例です。
  • dnfについては触れません。yumで記述を統一させていただきます。
  • 複数の環境を使い分ける必要があるので、極力ホスト環境と同期を取らないようにしています。
  • あくまでも、「既存の本番環境にどう合わせて開発用のDocker環境を構築するか」を主眼にした記事となっております。

開発・実行環境

ホスト環境

  • OS:macOS BIg Sur
  • Docker と Docker Compose がインストール済みであること

Docker環境

  • DB: MySQL 5
  • OS: CentOS 7またはAmazon Linux 2
  • フレームワーク: Ruby on Rails 5〜6

既存環境調査

アプリケーションサーバー

OSバージョンに基づく対応イメージ特定

Amazon Linux 2の場合

イメージIDを確認。

$ cat /etc/image-id

以下のようにイメージファイル名が出力されるので

image_file="amzn2-ami-hvm-(バージョン)-x86_64.xfs.gpt"

※あくまでも例です。

以下のページより、上記で出力されたバージョンを以て、公式イメージを特定します。

hub.docker.com

CentOSの場合

$ cat /etc/system-release

以下のようにバージョンが出力されるので、

CentOS Linux release (バージョン)

以下のページより、上記で出力されたバージョンを以て、公式イメージを特定します。

hub.docker.com

ライブラリ確認

まずはコンテナ作成

「OSバージョンに基づく対応イメージ特定」で確認したOS、バージョンに合わせたコンテナを作ります。

ライブラリバージョン突合

既存ライブラリのバージョンを確認し、作成したコンテナのものと突合します。

※構築後にupdateされている場合など、デフォルトでインストールされているバージョンからずれている場合があるので、必ず実施する必要があります。

$ yum list installed

(特にgcc周り。全てを合わせる必要はないですが、Railsに関係あるものは可能な限り合わせるようにします。)

ビルドに必要なライブラリは、以下のサイトを参照します。

github.com

railsguides.jp

既存環境ありきの話なので、上記サイトの記述からブレることもありますが、試行錯誤してすり合わせていきます。 基本的には、gcc周りからビルドを実施していき、エラーに基づいて不足しているライブラリを追加していきます。
(今のところライブラリのバージョンがズレたことはCentOSでしかありませんでした。
対応策として、CentOS公式よりRPMをそのまま持ってくるようにしています。おそらくyum.confをいじるのが正しいやり方だと思いますが、そこはまだ検証できていません) vault.centos.org

私の場合は、yum-utilsを使って依存ライブラリを抽出→追加という手順で実施しております。
(yum deplistなどもあります)

依存ライブラリリストの見方

yum-utilsが入っている場合(結構強引にやっているので、改良の余地はあると思います)

$ sort <(sed -e 's/ [| \\\_]\+\|-[[:digit:]]\+..*\|[[:digit:]]\://g' <(repoquery --tree-requires <パッケージ名>)) | uniq | xargs yum info {}

※sedは以下を参考にしました。

stackoverflow.com

yum-utilsが入っていない場合

$ yum deplist <パッケージ名> | grep provider

Rubyのバージョン突合

当然Rubyのバージョンを合わせる必要があるので、確認しておきます。

$ ruby -v
Dockerfile記述例
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \
  && echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \
  && echo 'eval "$(rbenv init -)"' >> ~/.bashrc \
  && source ~/.bashrc \
  && mkdir -p "$(rbenv root)"/plugins \
  && git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build \
  && rbenv install (上記で確認したバージョン) \
  && rbenv global (上記で確認したバージョン) \

Bundlerのバージョン突合

Bundlerのバージョンも合わせる必要があるので、確認しておきます。 ※Gemfile.lockのbundlerバージョンとの比較も忘れずに!

$ bundler -v
Dockerfile記述例
RUN gem install bundler -v (上記で確認したバージョン)

※実際には、rbenvと一緒のRUNの中でinstallする方が良いです。

サーバーのタイムゾーン突合

サーバーによっては、UTCだったり、JSTだったりとバラつきがあるので、確認が必須です。

$ date +"%Z %z"
Dockerfile記述例
ENV TZ='Asia/Tokyo'

サーバーの言語設定突合

念のために言語設定も確認しておきます。

$ echo $LANG
Dockerfile記述例
ENV LANG=ja_JP.UTF-8

※実際には、他のENVとまとめて記述する方が良いです。

所属グループ突合

アプリ実行ユーザーの所属グループを確認し、作成済みコンテナの実行ユーザーが所属しているグループと突合します。
root以外のユーザーで実行されている場合は、ユーザーを新規作成します。
※とは言え、基本的にwheelグループに所属させるだけで良いと思いますが。

$ groups
Dockerfile記載例

DOCKER_USER: 実行ユーザー
DOCKER_USER_PASS: 設定するパスワード

RUN useradd -mU ${DOCKER_USER} \
  # sudo設定追加
  && echo '${DOCKER_USER} ALL=(ALL:ALL) ALL' | EDITOR='tee -a' visudo \
  # sudo権限を付与
  && gpasswd -a ${DOCKER_USER} ${DOCKER_USER} \
  # sudo可能なグループに所属させる。
  && usermod -aG wheel ${DOCKER_USER} \
  # パスワードを設定
  && echo "${DOCKER_USER}:${DOCKER_USER_PASS}" | chpasswd

その他使用ミドルウェア、ライブラリ突合

その他使っているミドルウェア、ライブラリなどがあれば随時突合します。 (私の場合は、Nginx導入まではやりませんでしたが、Redisは動作確認に必要な場合は入れています)

DBサーバー

バージョンを確認

SELECT version();

キャラクターセット、照合順序設定、タイムゾーン確認

以下をDBコンソール上で実行します。

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
SHOW VARIABLES LIKE '%time_zone%';

※上記で、「time_zone」が「SYSTEM」になっていた場合は、以下も試して実際に取得できる時刻を確認すること。

SELECT current_timestamp;

テーブル各自のエンコード、照合順序確認

SHOW TABLE STATUS FROM <DB名>

DBの権限状態確認

SELECT user,host FROM mysql.user ORDER BY user,host;

※初期権限設定は、私はまだ対応しきれていませんが、初期実行スクリプトを作るなどすれば対応できるはずです。

DBのSQLモードを確認

SELECT @@GLOBAL.sql_mode;

docker-compose.yml記述例

  db:
    image: mysql:(確認したMySQLのバージョン)
    volumes:
      - aocs-data:/var/lib/mysql:rw
      - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
   (中略)
    environment:
      (中略)
      TZ: "Asia/Tokyo"

my.cnf記述例

[mysqld]
sql_mode=(確認したSQLMODE)
character_set_server=(確認したキャラクターセット)
collation_server=(確認した照合順序)
skip_character_set_client_handshake
default_authentication_plugin=mysql_native_password

開発環境構築

構築

 上記の確認・調整を、以下のようにキャッシュをなるべく用いないようにして作成→破棄を繰り返して実施し、本番環境とDocker環境を可能な限り合わせていきます。

作成時

 $ docker-compose up -d --build

※キャッシュを使わずにビルドしたい場合はこちら。(詳細なログなど見たい場合は、--progress=plainをつける(デフォルトはauto))

$ docker-compose build --no-cache
$ docker-compose up -d

破棄時

$ docker-compose down
# ※必要に応じてvolume破棄なども行うこと。
$ docker volume rm

まとめ

まだまだ改善の余地があると考えております。
本手法は、継続的な改善を実施してブラッシュアップして行っている最中であり、拙いものではありますが、一旦まとめました。
これからも継続的な開発環境の改善を行ってまいりたいと思います。