Toyokumo Tech Blog

トヨクモ株式会社の開発本部のブログです。

Elastic Beanstalk Multicontainer DockerでChromeDriverを使うJavaアプリケーションをデプロイする

こんにちは。 @makinoshi です。

トヨクモでは製品の外形監視などに、Clojureで作成したWebアプリケーションを使っています。

E2E監視に使うためにサーバー上でChrome Driverなどを動かす必要がありましたが、その環境を構成管理ツールでも、ましてや手動で構築する手間をかけたくはありません。 またその環境が正常に動作するかをローカルで確認して、そのままデプロイできれば簡単です。

そこでElastic Beanstalk Multicontainer Docker(以下、EB)Elastic Container Registry(以下、ECR)を使って構築することにしました。

Clojureのアプリケーションは1つのjarファイルにして java -jar で実行するので、ノウハウとしてはJavaアプリケーションのデプロイと変わりません。

手順としては、ECRにコンテナをデプロイし、EBの環境を構築して設定ファイルを準備し、EBにデプロイすれば完了です。

ディレクトリ構成

まずディレクトリ構成からです。今回の記事の主旨以外のソースコード等は省略しています。

.
├── Makefile
├── docker
│   ├── java
│   │   ├── Dockerfile
│   │   └── jar
│   │       └── app.jar
│   └── nginx
│       ├── Dockerfile
│       └── nginx.conf
├── eb
│   ├── Dockerrun.aws.json

ECRにコンテナのリポジトリを作る

まずAWS ECRの管理画面で今回デプロイするコンテナのリポジトリを作ります。ECR > リポジトリの作成から作ることができます。

この例ではNginxとJavaアプリケーションの2つのコンテナが必要なので2つのリポジトリを作りました。

JavaアプリケーションのDockerfile

前述のように今回はJavaアプリケーションが動作するコンテナ上にChromeDriverが動作する環境が必要だったので、次のようなDockerfileを書きました。

FROM amazoncorretto:8

WORKDIR /app

COPY jar/app.jar /app/app.jar

RUN yum update -y && yum install -y \
    wget \
    unzip \
    ipa-gothic-fonts ipa-mincho-fonts ipa-pgothic-fonts ipa-pmincho-fonts \
    https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm \
    && yum clean all \
    && rm -rf /var/cache/yum

RUN wget https://chromedriver.storage.googleapis.com/75.0.3770.90/chromedriver_linux64.zip \
    && unzip chromedriver_linux64.zip \
    && mv chromedriver /usr/local/bin/ \
    && rm chromedriver_linux64.zip

EXPOSE 5000

ENTRYPOINT ["java", "-jar", "-server", "-XX:+TieredCompilation", "-Xms512m", "-Xmx512m", "/app/app.jar"]

JDKとしてAmazon Correttoの公式Dockerコンテナを使っています。

またChromeとChromeDriverのインストールではyumのキャッシュやダウンロードしたzipファイルを削除することで、極力ビルドしたコンテナサイズを小さくするようにしています。

最後の ENTRYPOINT でJVMのオプションを渡していますが、特にメモリ設定についてはお使いのインスタンスサイズ等に合わせたものにしていただければと思います。

NginxのDockerfile

リバースプロキシとしてNginxを使います。

まず nginx.conf は次のようになります。

user  nginx;
worker_processes auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;

    sendfile  on;
    tcp_nopush on;

    upstream app_backend {
        server java:5000;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://app_backend;
        }
    }
}

ポイントは upstream app_backend の設定です。後述しますが、EBに渡す設定ファイルで別のDockerコンテナをどう参照できるか設定でき、ここでは java という名前で参照させ、またJavaのDockerfileで5000をEXPOSEしたので、 server java:5000 と設定しました。

Dockerfileはシンプルです。

FROM nginx:mainline

COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

ECRへのプッシュ方法

作ったリポジトリの詳細画面に入り、「プッシュコマンドの表示」をクリックすると、そのリポジトリにDockerイメージをプッシュする方法が表示されます。

後ほどMakefileの中でECRへのプッシュを含む、全体のデプロイ方法を紹介します。

EBの環境を構築

今回はCLIではなくマネジメントコンソールから作成しました。

環境の作成を選択し、「事前設定済みプラットフォーム」に「Multi-container Docker」を選択します。アプリケーションコードはサンプルアプリケーションのままにします。 本記事ではVPN等のネットワーク環境の構築については解説しませんが、VPNの中に入れ、さらにNAT経由で外にアクセスするようにしています。

作成が完了したら、ステータスやログを確認して、正常に環境が構築できたことを確認します。

確認できたらebcliを使ってEBの環境を参照するための設定ファイルを作ります。

$ cd eb
$ eb init
# 作成した環境を選択

Dockerrun.aws.json

EBに対して具体的なコンテナの設定をするのが Dockerrun.aws.json ファイルです。

{
  "AWSEBDockerrunVersion": 2,
  "containerDefinitions": [
    {
      "name": "java",
      "image": "<ECR上のURL>",
      "essential": true,
      "memory": 1280,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "<Resion>",
          "awslogs-group": "<ロググループ名>"
        }
      }
    },
    {
      "name": "nginx",
      "image": "<ECR上のURL>",
      "essential": true,
      "memory": 128,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 80
        }
      ],
      "links": [
        "java"
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-region": "<Resion>",
          "awslogs-group": "<ロググループ名>"
        }
      }
    }
  ]
}

logConfiguration ではawslogsドライバーを使用してコンテナのログをCloudWatchLogsに集めるための設定をしています。

linksname を指定して参照させています。リバースプロキシするために、nginxにjavaで参照できるように設定を渡しています。

Makefileでデプロイ

最後に一連のデプロイをMakefileで単一のタスクに組み上げます。

# ClojureアプリケーションをLeiningenでビルドし、
# Dockerfileから参照できるディレクトリにJARファイルを配置する
jar-build:
    @lein do clean, uberjar
    @mkdir -p docker/java/jar
    @cp -p target/uberjar/harvest.jar docker/java/jar/

docker-build:
    @cd docker/java && docker build -t <ECRのリポジトリ名> .
    @cd docker/nginx && docker build -t <ECRのリポジトリ名> .

build: jar-build docker-build

login-ecr:
    $(shell aws ecr get-login --no-include-email --region <Resion>)

push-java-docker:
    docker tag <ECRのリポジトリ名>:latest <ECRのイメージURL>:latest
    docker push <ECRのイメージURL>:latest

push-nginx-docker:
    docker tag <ECRのリポジトリ名>:latest <ECRのイメージURL>:latest
    docker push <ECRのイメージURL>:latest

push-docker: login-ecr push-java-docker push-nginx-docker

deploy: build push-docker
    @cd eb && eb deploy

JARファイルのビルド以外は、ECRのリポジトリで解説されている手順通りです。

あとは、make deploy を実行すれば完了です。

おわりに

EBを使うことで非常に簡単にDockerをデプロイすることができました。

弊社では現在運用中のPHP製品をEB+ECRの構成に変更するプロジェクトが進行中です。

Javaアプリケーションだと本記事のようにJARファイル1つを配置して起動するだけですし、起動オプション等もENTRYPOINTで java -jar コマンドの引数に渡すだけとかなり簡単にできますが、PHPのアプリケーションではそうはいきません。

次回以降の記事で、PHPの本番環境をDocker化するノウハウについて共有していこうと思います。


弊社では技術が好きなエンジニアを継続的に募集しています。

興味を持っていただいた方は、ページ下部の採用情報をご覧ください。