Docker でロードバランサ・アプリケーションサーバ・DBサーバの環境構築


f:id:Naotsugu:20191016185503p:plain


はじめに

前回は Docker のインストールからイメージビルド・コンテナ起動・Compose までの流れをみてきました。

etc9.hatenablog.com


今回は以下のような、一般的な Web アプリケーションの開発環境を構築していきます。

f:id:Naotsugu:20191101200604p:plain

前回の記事とあわせて、Docker の活用方法を理解いただければと思います。


Nginx でロードバランサを構成する

最初に、単純な Web サーバを Nginx でロードバランシングする環境を作成して動作を見てみます。

このような構成となります。

f:id:Naotsugu:20191101201637p:plain


作業フォルダを作成しましょう。

$ mkdir web-app
$ cd web-app


最初に Nginx でバランシングする Webサーバを準備します。


Webサーバ1号機の作成

以下のように簡単なページを準備します。

$ mkdir -p nginx1
$ cat <<EOF > nginx1/index.html
<!DOCTYPE html>
<html><body><h1>Hello nginx1</h1></body></html>
EOF


docker-compose.yml では以下のような指定になるでしょう(後ほど作成するためここでは記載例のみ示します)。

  nginx2:
    container_name: "nginx1"
    image: nginx
    volumes:
      - ./nginx1:/usr/share/nginx/html


Webサーバ2号機の作成

同じように2号機を作成します。

$ mkdir -p nginx2
$ cat <<EOF > nginx2/index.html
<!DOCTYPE html>
<html><body><h1>Hello nginx1</h1></body></html>
EOF


docker-compose.yml では以下のような指定になるでしょう。

  nginx1:
    container_name: "nginx2"
    image: nginx
    volumes:
      - ./nginx2:/usr/share/nginx/html


ロードバランサの作成

HAProxy などでも良いですが、今回は Nginx でロードバランサを作成していきます。

proxy ディレクトリを作成し、Nginx の設定ファイルである default.conf を作成します。

$ mkdir proxy
$ touch default.conf


default.conf は以下のように編集します。

upstream backend {
    server nginx1;
    server nginx2;
}

server {
    listen 80;
    server_name localhost;
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
        proxy_pass http://backend;
    }
}

ロードバランサとして利用するためupstream ディレクティブに backend という名前を付けて内側のWebサーバを指定します。先ほど作成した Web サーバのサービス名を指定します。 compose で起動した場合は同じネットワークに属するため、サービス名での参照が可能となります。

server ディレクティブでは proxy_passbackend を指定して、/ でのアクセスをバックエンドサーバへ転送する設定とします。

また、proxy_set_headerX- 系のヘッダを付与するようにしています。


default.conf について少し補足しておきます。

nginxの設定ファイルは、/etc/nginx/nginx.conf にあり、以下のようになっています(大部分を省略しています)。

http {
  ...
  include /etc/nginx/conf.d/*.conf;
  server {
    ...
    include /etc/nginx/default.d/*.conf;
  }
}

http ディレクティブで /etc/nginx/conf.d/*.conf というパスで設定がインクルードされています。

上記で作成した default.conf はここからインクルードされるため、http ディレクティブの要素として定義されることになります。


ロードバランサとWebサーバの起動

web-app ディレクトリ配下に以下の内容で docker-compose.yml を作成します。

$ touch docker-compose.yml


以下のように編集します。

version: '3'
services:

  proxy:
    container_name: "proxy"
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./proxy/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - nginx1
      - nginx2

  nginx1:
    container_name: "nginx1"
    image: nginx
    volumes:
      - ./nginx1:/usr/share/nginx/html

  nginx2:
    container_name: "nginx2"
    image: nginx
    volumes:
      - ./nginx2:/usr/share/nginx/html

proxy と各 Web サーバを起動しているだけです。


ここまででのディレクトリ構成は以下のようになりました。

f:id:Naotsugu:20191101205502p:plain


では早速実行してみましょう。

$ docker-compose up


各サービスが起動したら http://localhost でアクセスします。


f:id:Naotsugu:20191101204926g:plain


リロードを行うことで、ラウンドロビンでバランシングされていることが分かります。


ここでは nginx イメージからロードバランサを作成していますが、jwilder/nginx-proxy というロードバランサ用のイメージを利用することで、Nginx の設定を行うことなくロードバランサを構築することもできます。


詳しくは触れませんが jwilder/nginx-proxy を利用した場合の docker-compose.yml のサンプルは以下のようになります。

version: '3'
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  whoami:
    image: jwilder/whoami
    environment:
      - VIRTUAL_HOST=whoami.local

VIRTUAL_HOST を設定しておけば、自動で(docker-genで設定ファイルが自動生成される)バランシングしてくれます。



Web アプリケーションの準備

Nginx で簡単なWebページの作成ができたので、次は Web アプリケーションを動かしてみましょう。

以下のような構成となります。

f:id:Naotsugu:20191101210359p:plain


アプリケーションは Spring Boot のサンプルアプリケーショである Spring Petclinic を利用しましょう。


app ディレクトリを作成し、git clone でソースを取得します。

$ mkdir app
$ cd app
$ git clone https://github.com/spring-projects/spring-petclinic.git


今回は、Docker でDBサーバを作るため、取得したソースの MySql の接続設定を編集します。

spring-petclinic/src/main/resources/application-mysql.properties を以下のように変更します。

# database init, supports mysql too
database=mysql
spring.datasource.url=jdbc:mysql://mysql/petclinic
spring.datasource.username=root
spring.datasource.password=petclinic
# Uncomment this the first time the app runs
spring.datasource.initialization-mode=always


データソースURLを、後ほど作成するサービス名のmysqlに変更しています。


Docker でアプリケーションをビルドする

プロジェクトのビルドも Docker でやりましょう。

maven イメージが公開されているので、Maven がインストールされていなくても、以下のコマンドでビルドできます。


ビルドは以下のコマンドで行えます。

$ docker run -it --rm --name my-maven-project -v "$(pwd)/spring-petclinic":/usr/src/mymaven -w /usr/src/mymaven maven:3.6.2-jdk-8 mvn package


とは言っても Maven の依存のダウンロードで時間がかかります(手元の環境で10分程)。

上記コマンドでビルドした場合、ビルド時にダウンロードした依存ファイルは再利用できません。ビルドを繰り返す場合は、データボリュームを使うと便利です。


以下で maven-repo という名前でデータボリュームを作成します。

$ docker volume create --name maven-repo


maven-repo/root/.m2 にマウントして実行します。

$ docker run -it --rm --name my-maven-project -v maven-repo:/root/.m2 -v "$(pwd)/spring-petclinic":/usr/src/mymaven -w /usr/src/mymaven maven:3.6.2-jdk-8 mvn package


初回は依存のダウンロードで少し時間がかかりますが、次回からは maven-repo にダウンロードした依存ファイルが使われるためすぐにビルドが完了します。

...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  58.010 s
[INFO] Finished at: 2019-00-00T00:00:00Z
[INFO] ------------------------------------------------------------------------


以上のようにビルドが完了しました。


ビルドした成果物は /app/spring-petclinic/target に jar ファイルが作成されるので、アプリケーションの実行は以下のように行えます。

  app:
    container_name: app
    image: openjdk:8
    volumes:
      - ./app/spring-petclinic/target:/usr/src/app 
    command: bash -c "cd /usr/src/app && java -Dspring.profiles.active=mysql -jar *.jar"

openjdk:8 というイメージで Java8 の実行環境が手に入るため、あとは java コマンドで起動するだけです。

java コマンドの引数で -Dspring.profiles.active=mysql としてプロファイルを指定します。


実際の docker-compose.yml は後述します。


DBサーバの準備

先ほど編集した application-mysql.properties で定義した接続先のDBサーバを準備します。


といっても、application-mysql.propertiesspring.datasource.initialization-mode=always と指定しているため、データベースの準備や初期データ登録は自動的に行われるため、コンテナを起動するだけです。


docker-compose.yml の指定は以下のようになるでしょう。

  mysql:
    container_name: mysql
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=petclinic
      - MYSQL_DATABASE=petclinic


ロードバランサとアプリケーションサーバの起動

web-app ディレクトリ直下の docker-compose.yml を以下のように編集します(先ほど作成した Web サーバの設定は削除します)。

version: '3'
services:
  proxy:
    container_name: "proxy"
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./proxy/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app1
      - app2

  app1:
    container_name: app1
    image: openjdk:8
    depends_on:
      - mysql
    volumes:
      - ./app/spring-petclinic/target:/usr/src/app 
    command: bash -c "cd /usr/src/app && java -Dspring.profiles.active=mysql -jar *.jar"

  app2:
    container_name: app2
    image: openjdk:8
    depends_on:
      - mysql
    volumes:
      - ./app/spring-petclinic/target:/usr/src/app 
    command: bash -c "cd /usr/src/app && java -Dspring.profiles.active=mysql -jar *.jar"

  mysql:
    container_name: mysql
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=petclinic
      - MYSQL_DATABASE=petclinic


内容は、今まで見てきたものを組み合わせただけなので、特に説明は不要でしょう。


ここまでのディレクトリ構成は以下のようになっています。

f:id:Naotsugu:20191101211725p:plain


では早速実行してみましょう。

$ docker-compose up


各サービスが起動したら http://localhost でアクセスします。

f:id:Naotsugu:20191101213315g:plain

アプリケーションが動いていますね。 動画だけだと分かりませんが、この間もラウンドロビンでアプリケーションサバーに対するバランシングが行われています。


まとめ

前回に続き、Docker の利用方法を見てきました。

主に開発環境構築を視野に、ロードバランサ・アプリケーションサーバ・DBサーバを使った環境構築を行いました。
流れを理解すれば、オプションなど調べつつ活用が広がるかと思います。


最後に、停止しているコンテナを一括で削除しておきましょう(ローカルの停止コンテナを全て消すので注意してください)。

$ docker container prune


コンテナが削除されました。なお、イメージの一括削除は docker image prune を使います。



Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

みんなのDocker/Kubernetes

みんなのDocker/Kubernetes