1クール続けるブログ

とりあえず1クール続けるソフトウェアエンジニアの備忘録

ローカル開発環境(apache/tomcat)をコンテナ化+SSL化

ローカルのapache+tomcatで動いているアプリケーションをdockerに乗っけてみました。 SSL込みで設定していきました。Docker for Macを使用しています。

成果物をこちらのリポジトリにあげておきます。

github.com

Apache

ApacheでVirtualHostで3つのFQDNの通信を扱うようにしていきます。SSLの設定も行っていきますが、その際にはmkcertを使用してローカル認証局と鍵の作成を行います。

httpd.confの作成

SSL通信を有効にするため、conf/httpd.confをベースのdockerイメージからコピーしてきて変更を加えていきます。以下の項目のコメントアウトを外します。

#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
...
#LoadModule ssl_module modules/mod_ssl.so
...
#Include conf/extra/httpd-ssl.conf

今回はVirtualHosts+Proxy Moduleを使っていきたいと思うので下記のコメントアウトも外します。tomcatへの通信をhttpで行うために必要なモジュールもついでに外します。

#Include conf/extra/httpd-vhosts.conf
...
#LoadModule proxy_module modules/mod_proxy.so
...
#LoadModule proxy_http_module modules/mod_proxy_http.so

各VirtualHostsで共有の鍵を使いたいので、httpd-ssl.confに鍵の設定をして、httpd-vhosts.confの設定がhttpd-ssl.confより後にくるように記述の位置を変えます。下記のようになるはずです。

...
# Secure (SSL/TLS) connections
Include conf/extra/httpd-ssl.conf

# Virtual hosts
Include conf/extra/httpd-vhosts.conf
...

鍵の作成

mkcertを使って認証局+鍵の作成を行っていきます。Goで作られているらしいので興味ある方はぜひ。

github.com

brew install mkcert
brew install nss  # Firefoxでアクセスする際には必要のようです
mkcert -install # ローカル認証局作成 Macの方はキーチェーンアクセスを見ると出来ているのが分かる
mkcert localhost "*.example.com " "*.cool.com"
# generateされた下記のファイル2つをdockerコンテキストに配置

httpd-ssl.confの設定

httpd-ssl.confの下記をコメントアウトします。今回は先程生成した2つのpemファイルを利用するためです。

SSLCertificateFile "/usr/local/apache2/conf/server.crt"
...
SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"

そして下記のように記述を加えます。

SSLCertificateFile "/usr/local/apache2/conf/localhost+2.pem"  # 後ほどDockerfileでCOPYするように宣言します
SSLCertificateKeyFile "/usr/local/apache2/conf/localhost+2-key.pem"

httpd-vhosts.confの設定

tomcatのコンテナへajpで通信するように設定を加えます。 通常は静的ファイルはDocumentRoot配下を参照させて、それ以外はtomcatと通信という形になるかと思いますが、今回はとりあえず全部tomcatコンテナに流します。 下記のような形としました。VirtualHostのセクション内です。

   <Directory "/usr/local/apache2/docs/one.cool.com">
        Options Indexes 
        Require all granted
    </Directory>

   <Location /todolist>
        ProxyPass http://app:8080/todolist/
    </Location>

/private/etc/hostsに今回設定したFQDNを追加

本題とは関係ないですが自分のPCからアクセスできるように、/private/etc/hostsに記述しておきます。

127.0.0.1       www.example.com
127.0.0.1       one.cool.com
127.0.0.1       two.cool.com

Dockerfileの作成

設定ファイルと鍵、httpのドキュメントをコンテナにCOPYしポート80番と443番をListenします。イメージはなるべく軽くしたいのでDebian系OSがベースのものでなく、alpineベースのものを選んでいます。

FROM httpd:2.4-alpine

# copy configuration files
COPY --chown=root:www-data conf/httpd.conf /usr/local/apache2/conf
COPY --chown=root:www-data conf/extra /usr/local/apache2/conf/extra

# copy keys for ssl/tls
COPY --chown=root:www-data keys /usr/local/apache2/conf

# copy document
WORKDIR /usr/local/apache2/docs
RUN chown root:www-data /usr/local/apache2/docs
COPY --chown=root:www-data docs .

EXPOSE 80 443

httpdがうまく動いているかテスト

docker-compose.yamlを作成し、下記のようにhttpdの設定を宣言します。

version: '3'
services:
  web:
    container_name: httpd
    build: ./httpd
    ports:
    - "9080:80"
    - "9443:443"

以下のURLにSSLでアクセスできることが確認できました。

Tomcat

kotlin + SpringBootで作成した簡単なアプリケーションをコンテナに乗っけてみます。黒べこ本のサンプルに載っているTodoアプリです。

kotlinのアプリケーションの設定を一部変更

最初にkotlinのアプリケーションのbuild.gradleに変更を加えていきます。今回はwarを作成してそれをデプロイするため、下記のように記述を加えていきます。

apply plugin: 'war'
dependencies {
    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")  // SpringBoot組み込み以外のTomcatを利用するときに必要みたいです
}

次に、warファイルをTomcatに展開するにあたって必要らしいServletInitializerというclassをエントリポイントのあるclassと同パッケージに配置します。下記のような内容のclassです。

package com.example.todolist

import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer

class ServletInitializer : SpringBootServletInitializer() {

    override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
        return application.sources(TodolistApplication::class.java)
    }
}

Dockerfileの作成

Warに固める作業自体はローカルのIntelliJで行います。Dockerコンテキスト配下にソースを置いてそこに成果物を固めて良いのですが、今回は単純にcopyしてコンテキスト配下に盛ってきたいと思います。いちいちその作業をやるのが面倒くさいのでMakefileを作ります。
※ AutoDeployしてDockerで都度都度検証したい場合には、Volumeマウントして動かしたほうが良いとは思います。

build:
  cp ~/ideaProjects/todolist/build/libs/todolist.war tomcat/build
  docker-compose up --build

run: 
  docker-compose up 

makefileに記述したように、warはtomcat/build配下に格納するので、そのwarをdockerの方にcopyするだけです。なのでDockerfileは下記のようになります。tomcat側もイメージは軽くしたいのでalpineを使います。 またSpringBootは環境変数からコンテキストパスを設定できるのでしておきます。SpringBoot1.x系は別の変数名になるらしいので注意です。

FROM tomcat:9.0.16-jre8-alpine

ENV SERVER_SERVLET_CONTEXT_PATH /todolist
COPY build/todolist.war /usr/local/tomcat/webapps
EXPOSE 8080

動作を検証する

docker-comopseに下記を追記し、make buildしてみます。[https://one.cool.com:9443/todo]にアクセスしてみるとアプリケーションの画面が表示されました。

  app:
    container_name: tomcat
    build: ./tomcat
    ports:
    - "8080:8080"

f:id:jrywm121:20190218021406p:plain
表示された画面

まとめ

アプリケーションをBuildするところからDockerでできるように今度する。マルチステージビルドが使えそう。