ENGINEER BLOG

ENGINEER BLOG

コンテナによる開発環境の構築

こんにちは、C&I統括本部の吉田です。
はじめてエンジニアブログを担当します。

開発をするときに、環境構築に苦労されたことはないでしょうか?
開発に時間を割きたいのに、環境構築が上手くいかず、とても焦った経験が私にはあります……。手順書があっても、新人の頃は手順書通りに作業することも難しくて、何でこんな簡単なこともできないんだろう、と少し落ち込んだこともあります(笑)

でも、もう大丈夫! 今回ご紹介するコンテナを利用すれば、面倒な開発環境構築作業から解放されるのです!

では、早速見ていきましょう。

0. はじめに

今回環境を構築するシステムは下記の特徴があります。

  • Amazon ECRにコンテナイメージを保存している
  • アプリケーションが複数に分割された、マイクロサービスである

今回はAmazon ECRにPushされているコンテナイメージをそのまま利用して、ローカル環境に本番環境を再現し、アプリケーションを簡単に動かしてみたいと思います。

Amazon ECRやコンテナの詳細については公式ページをご確認ください。

1. Amazon ECRのコンテナイメージをそのまま利用

まず、Docker Desktop をインストールします。

Docker Desktop とは、Docker社が開発している、コンテナ型の仮想環境を作成・配布・実行するためのプラットフォームです。

1-1. インストーラーをダウンロード

下記URLにアクセスし、インストーラーをダウンロードします。

1-2. インストール

ダウンロードしたインストーラーを実行してインストールします。
下記のアラートが表示された場合、赤枠のリンクから WSL2 Linux カーネル更新プログラムパッケージをインストールします。
画像1

赤枠のリンクからインストーラーをダウンロードします。

画像2

インストーラーを起動し、Next をクリックして、インストールを実行します。

1-3. Dockerの起動

Docker Desktop を起動し、コマンドプロンプトで下記のコマンドを実行します。

docker run --rm hello-world

インストールが成功していれば、下記のように表示されます。

画像3

上記の場合、docker run コマンドを実行することで、Docker社が運営しているコンテナ共有サービス DockerHub より、hello-worldのコンテナイメージを取得し、コンテナイメージからコンテナを起動します。
--rm オプションを付けることで、コンテナの実行終了時にコンテナを削除してくれますが、元となるコンテナイメージは残ったままになります。

コンテナイメージは不要なので、下記のコマンドを実行して削除します。

docker rmi hello-world

これで、Docker Desktop の稼働確認が完了しました。

続いては、いよいよAmazon ECRのコンテナイメージを利用します。

1-4. Amazon ECRにログイン

弊社では、ログインにAWS IAM アイデンティティセンターを利用しています。

以降の手順は、AWS IAM アイデンティティセンターを利用してAmazon ECRにログインするものになります。IAMユーザを利用する場合は、下記の公式ページを参照ください。

それでは、インストーラーをダウンロードし、AWS CLIをインストールします。

ログインページで「Command line or programmatic access」をクリックします。

画像5

Windowsタブで赤枠内のセットコマンドをコピーし、コマンドプロンプトに貼り付けて環境変数をセットします。

画像4

続いて、下記のコマンドを実行し、Amazon ECRにログインします。「Login Succeeded」と表示されればOKです。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [レジストリID.dkr.ecr.ap-northeast-1.amazonaws.com]

1-5. コンテナイメージをAmazon ECRから取得し、コンテナを作成

コンテナイメージをAmazon ECRから取得し、コンテナを作成してみます。

コマンドプロンプトで下記コマンドを実行し、コンテナイメージを取得します。

docker pull --rm -it --name [任意のコンテナ名] [レジストリID].dkr.ecr.ap-northeast-1.amazonaws.com/[URI]

リポジトリのURIはAmazon ECRで確認してください。
コンテナイメージを取得する際はデフォルトで :latest タグが使われるため、最新のコンテナイメージが取得されます。
特定のバージョンを取得したい場合は docker pull [URI]:タグ を実行してください。

タグはAmazon ECRの管理画面上のリポジトリをクリックして、「イメージタグ」で確認できます。
下記コマンドを実行すると、取得したコンテナイメージの一覧を確認できます。

docker images

ここまででローカル環境でのDockerの基本操作と、Amazon ECRのコンテナイメージのPullができることが確認できました。

2. アプリケーション起動に必要な環境変数の設定​

Amazon ECRのコンテナイメージは、AWS上で実行するための設定であったり、実行時に環境変数で指定される設定があるため、ローカル環境で実行するためには、ローカル環境に沿った設定や環境変数を指定する必要があります。コマンドで指定することもできますが、指定する環境変数が膨大だとtypoによるミスが予想されるため、Docker Compose というツールを利用します。

Docker Compose とは、複数コンテナのアプリケーションの定義・共有に役立つように開発されたツールです。docker-compose.ymlにコンテナと環境変数を定義すれば、コマンドを1つ実行するだけで、定義に沿って複数のコンテナの起動のコントロールや停止が行えます。
複数のコンテナからなる今回のシステムのようなマイクロサービスを起動したい場合に、大助かりなツールです。

詳細は公式ページをご確認ください。

Docker ComposeDocker Desktop に含まれるようになったため、インストールは不要になります。
では、早速、docker-compose.ymlを作成していきましょう。

2-1. docker-compose.ymlを作成

docker-compose.ymlは下記のようになります。

version: '3'    # docker-composeで使用するバージョンで2023年8月時点で最新
services:
  demo-web: # 任意のサービス名
    image: XXXXXXXX # Amazon ECRリポジトリのURI
    container_name: demo-web    # 任意のコンテナ名
    ports:
      - 20100:20100 # 端末のポート番号とコンテナのポート番号のマッピング
    entrypoint: # Dockerfileのentrypointを上書きする
      - /bin/bash
      - -c
      - java -jar demo-web.jar
  demo-api:
    image: XXXXXXXX
    container_name: demo-api
    ports:
      - 20101:20101
    entrypoint:
      - /bin/bash
      - -c
      - java -jar demo-api.jar

Amazon ECRにコンテナイメージを保存しているシステムなので、Dockerfileは既に作成済みとなっていますが、Java起動時オプションを変更したい場合、entrypointを上書きします。

それぞれ項目の詳細については公式ページをご確認ください。

2-2. 環境変数を指定する

entrypointと同様、環境変数を使って設定情報をローカル環境向けに上書きします。
なお、本アプリケーションはSpringbootで開発されています。

version: '3'
services:
  demo-web:
    image: XXXXXXXX
    container_name: demo-web
    ports:
      - 20100:20100
    entrypoint:
      - /bin/bash
      - -c
      - java -jar demo-web.jar
    environment:    # 環境変数を指定する
      SERVER_PORT: 20100
      LOGGING_FILE_NAME: /home/demo/demo-web.log
      DEMO_ENDPOINT_API: http://demo-api:20101/demo-api
  demo-api:
    image: XXXXXXXX
    container_name: demo-api
    ports:
      - 20101:20101
    entrypoint:
      - /bin/bash
      - -c
      - java -jar demo-api.jar
    environment:    # 環境変数を指定する
      SERVER_PORT: 20101
      LOGGING_FILE_NAME: /home/demo/demo-api.log
      SPRING_DATASOURCE_DRIVER_CLASS_NAME: software.aws.rds.jdbc.mysql.Driver
      SPRING_DATASOURCE_URL: jdbc:mysql://host.docker.internal:3316/demo_db # MySQLもコンテナで準備する場合、指定が必要
      SPRING_DATASOURCE_USERNAME: *****
      SPRING_DATASOURCE_PASSWORD: *****

3. ミドルウェアもコンテナで準備​

続いて、ミドルウェアもコンテナで準備してみます。

これが実現できると、ミドルウェアのバージョンアップの検証が簡単にできるようになります。また、同じミドルウェアで異なるバージョンを別PJで使用したい、というときに、アンインストールをせずにコンテナのミドルウェアを使用すれば良いので大変便利です。

今回は、MySQLを準備します。

3-1. docker-compose.ymlにミドルウェアを定義

2で作成したdocker-compose.ymlに追記する形で、MySQLを定義します。MySQLはAmazon ECRではなく、DockerHubから持ってくる形になります。

version: '3'
services:
  db:
    image: mysql:8.0.23 # DockerHubで確認したMySQLのバージョン
    container_name: db
    ports:
      - 3316:3306 # ローカルのMySQLとポートを変える
    environment:
      MYSQL_ROOT_PASSWORD: *******
      TZ: Asia/Tokyo
    command: mysqld --character-set-server=utf8mb4 --skip-character-set-client-handshake --sql-mode="NO_ENGINE_SUBSTITUTION"

3-2. マスタデータ投入

DockerのMySQLイメージの仕組みを利用して、MySQLコンテナ生成・起動時にマスタデータを登録してみます。

アプリケーションを動かすために必要なマスタデータの投入を自動化できれば、「アプリケーションがうまく動かない! と思って調べてみたらマスタデータ投入漏れだった・・・」なんてことも防ぐことができます。

詳細は公式ページをご確認ください。

コンテナの「/docker-entrypoint-initdb.d」ディレクトリに.sqlや.shのファイルを配置すると、そのファイルが自動的に実行され、初期データとしてMySQLに投入されるようになります。そのために、ホストマシン側とコンテナ側のディレクトリをマウントし、ホストマシン側にあらかじめ初期データ用のファイルを準備しておく必要があります。

そこで、volumes オプションを使用します。volumes とは、ホストマシン内にあるDockerが管理する領域をコンテナ上にマウントする方法です。

下記では、ホストマシン側に「db」ディレクトリを作成し、そこに.sqlや.shのファイルを配置して、コンテナ側の「/docker-entrypoint-initdb.d」ディレクトリにマウントさせています。

version: '3'
services:
  db:
    image: mysql:8.0.23
    container_name: db
    ports:
      - 3316:3306 # ローカルのMySQLとポートを変える
    environment:
      MYSQL_ROOT_PASSWORD: *******
      TZ: Asia/Tokyo
      # 複数のデータベースを作成したい場合は.shを利用する
      # MYSQL_DATABASE: demo_db
    command: mysqld --character-set-server=utf8mb4 --skip-character-set-client-handshake --sql-mode="NO_ENGINE_SUBSTITUTION"
    volumes:
      - ./db:/docker-entrypoint-initdb.d # マスタデータ投入

ディレクトリ構成は下記の通りです。

複数のスクリプトが存在する場合は名前順で実行されます。今回は、データベースの作成、テーブルの作成、初期データの投入、indexの追加を順番に実行したいため、.sh のファイル名の接頭辞として数字連番を付与しています。
また、対象システムの仕様上、複数データベースを作成する必要があったため、データベースの作成はdocker-compose.ymlのenvironmentの MYSQL_DATABASE ではなく、.shで実施しています。

docker-app
│  docker-compose.yml
│
└─db
    │  000_make_databases.sql # データベース作成
    │  001_make_tables.sh # テーブル作成
    │  002_insert_initdata.sh # 初期データ投入
    │  003_alter_index.sh # index追加
    │
    ├─data # データ永続化で使用
    │
    ├─ddl # 001.shで使用するddlを配置
    │
    └─dml # 002,003.shで使用するdmlを配置

これで、コンテナ生成時にDBにマスタデータが登録されていれば成功です!
データが登録されているかどうかは、コンテナのDBでもホスト・ポートを指定することで MySQL WorkBenchで簡単に確認することができます。

3-3. データ永続化

コンテナ内で生成されたファイルやデータは、コンテナが削除されると消えてしまいます。ですが、3-2で登録したマスタデータはアプリケーションの起動に必要なため、保持しておく必要があります。そこで、データの永続化を実現させたいと思います。

ここでも volumes オプションを使用します。MySQLのデータ領域のデフォルトは「/var/lib/mysql」になります。
下記では、「./db/data」を「/var/lib/mysql」にマウントすることで、データ永続化を実現しています。

version: '3'
services:
  db:
    image: mysql:8.0.23
    container_name: db
    ports:
      - 3316:3306 # ローカルのMySQLとポートを変える
    environment:
      MYSQL_ROOT_PASSWORD: *******
      TZ: Asia/Tokyo
    command: mysqld --character-set-server=utf8mb4 --skip-character-set-client-handshake --sql-mode="NO_ENGINE_SUBSTITUTION"
    volumes:
      - ./db:/docker-entrypoint-initdb.d # マスタデータ投入
      - ./db/data:/var/lib/mysql # データ永続化

これでミドルウェアもコンテナで準備​し、マスタデータ投入の自動化と、データの永続化をすることができました!

3-4. Docker Compose の起動

Docker Compose を起動してみましょう。
コマンドは下記になります。

docker compose up -d

Docker Desktop で対象のコンテナをクリックすると、それぞれのコンテナのログが確認できます。
問題なく全コンテナが起動したことを確認できたら、アプリケーションが正しく動作しているか確認しましょう。

問題なければ、コンテナを利用した環境構築の完了です!

実際に開発する際は、開発対象のアプリケーションのコンテナを停止し、EclipseやVSCodeといったIDE側のアプリケーションと置き換えればOKです。

画像6

Docker Compose を停止する際は下記コマンドになります。

docker compose down

4. おわりに

いかがだったでしょうか? コンテナを利用すると下記のメリットがあります。

  • ミドルウェアのバージョンアップの検証が簡単にできるようになる
  • 同ミドルウェアの異なるバージョンを別PJで使用したい、というときでも、同時に複数バージョンのミドルウェアを利用できる
  • コンテナイメージを取得する際にタグ指定をすれば、過去のバージョンのアプリケーションでの調査や検証が、簡単にローカル環境で実施できる
  • コンテナイメージを取得する際に本番環境と同じタグ指定をすれば、ローカル環境で再現ができる

コンテナを活用して、快適な開発ライフを送っていきたいものですね。