Docker ComposeでConfluenceを構築する

この記事は [Docker2 Advent Calendar 2016](http://qiita.com/advent-calendar/2016/docker2) の7日目です。(空いていたので滑り込みました。)

全国のConfluence好きの皆さんこんにちは。
転職先の会社にConfluenceがなかった...そんなことありませんか？

私も転職先にConfluenceがなかったので、早速稟議を出しつつ折角なのでDocker環境でConfluenceを構築しました。

## 要件

以下の環境を想定。

* Docker Compose
* Confluence 5.10.8 (6系はまだ6.0.1なので見送り)
* nginx
* PostgreSQL (RDS利用予定なので、docker-compose.ymlには含めない)

## 全体の構成

これから構築するファイル構成。

```
.
├── docker-compose.yml
├── data
│   └── confluence-home
├── confluence
│   ├── Dockerfile
│   ├── server.xml
│   └── setenv.sh
├── nginx
│   ├── Dockerfile
│   ├── conf.d
│   │   └── default.conf
│   └── nginx.conf
└── postgres
    └── Dockerfile
```


# ConfluenceのDockerfileを作成する

まずConfluenceのDockerfileを作成する。
今回は5.10系をインストールするので公式の [Dockerfile — Bitbucket](https://bitbucket.org/atlassian/docker-atlassian-confluence-server/src/ba62d39c9559f3f0dbc21dc0fdeda0fcefba8da4/Dockerfile?at=5.10&fileviewer=file-view-default) の5.10ブランチのDockerfileをベースにする。

しかし、このままConfluenceのコンテナ内にMySQLドライバをダウンロードしてしまうので書き換える。

## MySQLドライバダウンロード箇所を取り除く

MySQLドライバをダウンロードしている箇所を取り除く。
PostgreSQLを利用する場合には、特に追加で何もする必要はないので削除のみ。

```diff:confluence/Dockerfile
@@ -10,9 +10,6 @@ LABEL Description="This image is used to start Atlassian Confluence" Vendor="Atl

 ENV CONFLUENCE_DOWNLOAD_URL http://www.atlassian.com/software/confluence/downloads/binary/atlassian-confluence-${CONF_VERSION}.tar.gz

-ENV MYSQL_VERSION 5.1.38
-ENV MYSQL_DRIVER_DOWNLOAD_URL http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-${MYSQL_VERSION}.tar.gz
-
 # Use the default unprivileged account. This could be considered bad practice
 # on systems where multiple processes end up being executed by 'daemon' but
 # here we only ever run one process anyway.
@@ -31,7 +28,6 @@ RUN set -x \
     && chown ${RUN_USER}:${RUN_GROUP}     "${CONFLUENCE_HOME}" \
     && mkdir -p                           "${CONFLUENCE_INSTALL}/conf" \
     && curl -Ls                           "${CONFLUENCE_DOWNLOAD_URL}" | tar -xz --directory "${CONFLUENCE_INSTALL}" --strip-components=1 --no-same-owner \
-    && curl -Ls                           "${MYSQL_DRIVER_DOWNLOAD_URL}" | tar -xz --directory "${CONFLUENCE_INSTALL}/confluence/WEB-INF/lib" --strip-components=1 --no-same-own
     && chmod -R 700                       "${CONFLUENCE_INSTALL}/conf" \
     && chmod -R 700                       "${CONFLUENCE_INSTALL}/temp" \
     && chmod -R 700                       "${CONFLUENCE_INSTALL}/logs" \
```

# メモリを可変にする

さて、Confluenceを環境構築して改めて実感したが、メモリが大量に必要となる。2GBでは全然足りない。

[How to fix out of memory errors by increasing available memory - Atlassian Documentation](https://confluence.atlassian.com/confkb/how-to-fix-out-of-memory-errors-by-increasing-available-memory-154071.html) を参考に、setenv.shを書き換えてTomcatのメモリ使用量を増やす必要があるが、今後のことを考えて環境変数にする。

setenv.shを下記のように書き換え、

```diff:confluence/setenv.sh
CATALINA_OPTS="-XX:-PrintGCDetails -XX:+PrintGCDateStamps -XX:-PrintTenuringDistribution ${CATALINA_OPTS}"
CATALINA_OPTS="-Xloggc:$LOGBASEABS/logs/gc-`date +%F_%H-%M-%S`.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=2M ${CATALINA_OPTS}"
CATALINA_OPTS="-XX:G1ReservePercent=20 ${CATALINA_OPTS}"
CATALINA_OPTS="-Djava.awt.headless=true ${CATALINA_OPTS}"
CATALINA_OPTS="-Datlassian.plugins.enable.wait=300 ${CATALINA_OPTS}"
+CATALINA_OPTS="-Xms${CONFLUENCE_CATALINA_MEM}m -Xmx${CONFLUENCE_CATALINA_MEM}m -XX:+UseG1GC ${CATALINA_OPTS}"


export CATALINA_OPTS
```

`ENV`に 環境変数としてデフォルトのメモリ使用量を記述する。

```diff:confluence/Dockerfile
@@ -4,6 +4,7 @@ MAINTAINER Atlassian Confluence
 # Setup useful environment variables
 ENV CONFLUENCE_HOME     /var/atlassian/application-data/confluence
 ENV CONFLUENCE_INSTALL  /opt/atlassian/confluence
+ENV CONFLUENCE_CATALINA_MEM  2048
 ENV CONF_VERSION  5.10.8

 LABEL Description="This image is used to start Atlassian Confluence" Vendor="Atlassian" Version="${CONF_VERSION}"
@@ -54,6 +55,7 @@ RUN set -x \
 # here we only ever run one process anyway.
 USER ${RUN_USER}:${RUN_GROUP}

+COPY ./setenv.sh  ${CONFLUENCE_INSTALL}/bin/
 COPY ./server.xml ${CONFLUENCE_INSTALL}/conf/
```


## プロキシ実行用にserver.xmlを書き換える

次にプロキシ実行用の設定を行う。
[How to use NGINX to proxy requests for Confluence - Atlassian Documentation](https://confluence.atlassian.com/confkb/how-to-use-nginx-to-proxy-requests-for-confluence-313459790.html) に従い、プロキシ実行用に書き換えたserver.xmlを用意する。

```diff:confluence/server.xml
- <Connector port="8090" connectionTimeout="20000" redirectPort="8443" maxThreads="48" minSpareThreads="10" enableLookups="false" acceptCount="10" debug="0" URIEncoding="UTF-8" protocol="org.apache.coyote.http11.Http11NioProtocol"/>
+ <Connector port="8090" connectionTimeout="20000" redirectPort="8443" maxThreads="48" minSpareThreads="10" enableLookups="false" acceptCount="10" debug="0" URIEncoding="UTF-8" protocol="org.apache.coyote.http11.Http11NioProtocol" proxyName="localhost" proxyPort="80"/>
     <Engine name="Standalone" defaultHost="localhost">
       <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="false">
-        <Context path="" docBase="../confluence" reloadable="false" useHttpOnly="true">
+        <Context path="/confluence" docBase="../confluence" reloadable="false" useHttpOnly="true">
```

併せて、Dockerfile側にもCOPYを追記する。

```diff:confluence/Dockerfile
--- a/confluence/Dockerfile
+++ b/confluence/Dockerfile
@@ -54,6 +54,8 @@ RUN set -x \
 # here we only ever run one process anyway.
 USER ${RUN_USER}:${RUN_GROUP}

+COPY ./server.xml ${CONFLUENCE_INSTALL}/conf/
+
 # Expose default HTTP connector port.
 EXPOSE 8090
```

ただし、このままではproxyNameはlocalhost固定となってしまう。ここは環境変数ではどうしようもないので、Dockerfile側で`ARG`と`sed`を使いbuild時に置換を行う。

```diff:confluence/Dockerfile
@@ -8,7 +8,7 @@
 ENV CONF_VERSION  5.10.8
 ENV JAVA_HOME           /usr/lib/jvm/java-8-openjdk-amd64/jre

+ARG CONFLUENCE_HOSTNAME="localhost"


 LABEL Description="This image is used to start Atlassian Confluence" Vendor="Atlassian" Version="${CONF_VERSION}"
@@ -64,7 +64,7 @@
 
 COPY ./server.xml ${CONFLUENCE_INSTALL}/conf/
+RUN sed -i -e "4s/localhost/${CONFLUENCE_HOSTNAME}/" ${CONFLUENCE_INSTALL}/conf/server.xml
```

# リバースプロキシとしてnginxを利用する

前職のConfluence管理者から、nginxを通してキャッシュしないとConfluenceは重過ぎるとの話をもらっていたのでnginxと併用する。実際かなり重い。

```:nginx/Dockerfile
FROM nginx:1.11.5-alpine

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

nginx.confは [docker版nginxのnginx.conf](https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine/nginx.conf) を参考に書き換える。

```nginx:nginx/nginx.conf
user  nginx;
worker_processes  1;

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


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    client_max_body_size 30m;

    gzip  on;
    gzip_types text/css text/javascript
               application/x-javascript application/javascript
               application/json;
    gzip_min_length 1k;
    gzip_disable "msie6";

    server {
      listen       80;

      location = / {
        return 302 /confluence;
      }

      location / {
        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://confluence:8090;
      }
    }
}
```

gzip周りはnginx実践入門を参考にした。

# docker-compose.yml を作成する

nginxイメージを作りはじめたのでdocker-compose.ymlも作り始める。

```yaml:docker-compose.yml
version: '2'
services:
  nginx:
    build: ./nginx
    links:
      - confluence
    ports:
      - '80:80'
  confluence:
    build:
      context: ./confluence
      args:
        CONFLUENCE_HOSTNAME: 'localhost'
    environment:
      CONFLUENCE_CATALINA_MEM: 2048
    ports:
      - '8090:8090'
    volumes:
      - ./data/confluence-home:/var/atlassian/application-data/confluence
```

`$ docker-compose` 利用時には、Dockerfileに書いた`ARG`や`ENV`は、docker-compose.yml に書いた`args`や`environment`でオーバーライドされる。
つまり可変部分は docker-compose.yml だけ書き換ればOK。便利。

# 設定ファイルをホストOSにマウントする

Confluenceは初回ブラウザアクセス時に行う設定の際に `/var/atlassian/application-data/confluence` 以下にファイル群が生成される。

このファイル群を削除した状態でConfluenceを再起動しアクセスすると、また初期設定画面になる。

その為、Dockerコンテナを破棄しても情報が引き継がれるように、このディレクトリをホストOSにマウントする。先程のdocker-compose.ymlの末尾がその設定に該当する。

```docker-compose.yml
    volumes:
      - ./data/confluence-home:/var/atlassian/application-data/confluence
```


# 起動してみる

`$ docker-compose build && docker-compose up` して localhostにアクセスしてみる。

<img width="500" alt="Screen Shot 2016-12-06 at 00.26.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/94332/752751f6-4ce9-e222-892c-79d0e66b6efa.png">

後はライセンスキーを入れたりDB設定をしたりすればConfluenceが使えるようになる！やったね！！

# オマケ

## PostgreSQL も Docker で動かす

ConfluenceとPostgreSQLをリンクさせつつ、環境変数を渡すだけで簡単にDockerで動かすことができる。

```diff:docker-compose.yml
19a20,21
+     links:
+       - postgres
23a26,34
+   postgres:
+     build: ./postgres
+     environment:
+       POSTGRES_USER: postgres
+       POSTGRES_PASSWORD: postgres
+     ports:
+       - '5432:5432'
+     volumes:
+       - ./data/postgres:/var/lib/postgresql/data
```

## Timezoneも可変にする

Confluenceの編集履歴の相対時刻表示はOSのタイムゾーンに依存するのでTimezoneをAsia/Tokyoに変更する。

```diff:confluence/Dockerfile
@@ -8,6 +8,9 @@ ENV CONFLUENCE_CATALINA_MEM  1024
 ENV CONF_VERSION  5.10.8
 ENV JAVA_HOME           /usr/lib/jvm/java-8-openjdk-amd64/jre

 ARG CONFLUENCE_HOSTNAME="localhost"
+ARG TIMEZONE="Asia/Tokyo"
 
 LABEL Description="This image is used to start Atlassian Confluence" Vendor="Atlassian" Version="${CONF_VERSION}"

 ENV CONFLUENCE_DOWNLOAD_URL http://www.atlassian.com/software/confluence/downloads/binary/atlassian-confluence-${CONF_VERSION}.tar.gz
@@ -51,6 +54,9 @@ RUN set -x \
                                           "${CONFLUENCE_INSTALL}/conf/server.xml" \
     && touch -d "@0"                      "${CONFLUENCE_INSTALL}/conf/server.xml"

+# set timezone
+RUN sed -i -e "s|Etc/UTC|${TIMEZONE}|" /etc/timezone
```

## IPAゴシックをマクロタイトルに適用する

順調に動作していたかに思われたConfluenceだが、編集画面のマクロのタイトル部分が文字化けしていた。

![image](https://qiita-image-store.s3.amazonaws.com/0/94332/938c509a-a32e-b383-29b8-23c0fd67db7a.png)

ここはIPAゴシックをインストールしてjreからも参照できるようにする。

```diff:confluence/Dockerfile
@@ -22,7 +23,7 @@ ENV RUN_GROUP           daemon
 # directory structure.
 RUN set -x \
     && apt-get update --quiet \
-    && apt-get install --quiet --yes --no-install-recommends libtcnative-1 xmlstarlet \
+    && apt-get install --quiet --yes --no-install-recommends libtcnative-1 xmlstarlet fonts-ipafont-gothic\
     && apt-get clean \
     && mkdir -p                           "${CONFLUENCE_HOME}" \
     && chmod -R 700                       "${CONFLUENCE_HOME}" \


+# add font for macro title
+RUN mkdir -p "${JAVA_HOME}/lib/fonts/fallback"
+RUN ln -s /usr/share/fonts/opentype/ipafont-gothic/ipag.ttf "${JAVA_HOME}/lib/fonts/fallback/ipag.ttf"
```

```diff:confluence/setenv.sh
@@ -44,6 +44,9 @@ CATALINA_OPTS="-Djava.awt.headless=true ${CATALINA_OPTS}"
 CATALINA_OPTS="-Datlassian.plugins.enable.wait=300 ${CATALINA_OPTS}"
 CATALINA_OPTS="-Xms${CONFLUENCE_CATALINA_MEM}m -Xmx${CONFLUENCE_CATALINA_MEM}m -XX:+UseG1GC ${CATALINA_OPTS}"

+# For macro titles
+CATALINA_OPTS="-Dconfluence.document.conversion.fontpath=${JAVA_HOME}/lib/fonts/fallback ${CATALINA_OPTS}"
+CATALINA_OPTS="-Dconfluence.document.conversion.slides.defaultfontname.regular=IPAGothic -Dconfluence.document.conversion.slides.defaultfontname.asian=IPAGothic -Dconfluence.do

 export CATALINA_OPTS
```

## PDFエクスポートが遅い問題を解決する

Confluence 5.10.8ではPDFエクスポートが遅くなる現象があるが、恐らくローカルの一時ファイルとして出力したPDFファイルをリクエスト元に返す際に上手くいってない。 /etc/hosts に自身のホスト名を書くと解消するのでこちらもdocker-compose.ymlから対応する。

```diff:docker-compose.yml
@@ -12,7 +12,10 @@ services:
   confluence:
     build: ./confluence
     environment:
       CONFLUENCE_HOSTNAME: "www.example.com"
       CONFLUENCE_CATALINA_MEM: 2048
+    extra_hosts:
+      - "www.example.com:127.0.0.1"
     ports:
       - '8090:8090'
     volumes:
```

このようにしておいて、各自の環境で `:%s/www\.example\.com/<任意のドメイン>/`に置換すればconfluenceコンテナの /etc/hosts/ にextra_hostsに書いた内容が追記される。

[Compose file reference - Docker](https://docs.docker.com/compose/compose-file/#/extrahosts)

# 注意事項

[atlassian/confluence-server - Docker Hub](https://hub.docker.com/r/atlassian/confluence-server/)

にも記載されているが、OpenJDKはConfluenceのサポート対象外となっている。

サポートされるJava環境はOracle JDKだが、ライセンス上再配布不可となっているので、Oracle JDK環境で動作させるには、各自でOracle JDKのDockerイメージを構築し、そのイメージを参照してConfluenceイメージを構築する必要がある。
[Update the Confluence Docker image to use Oracle JDK - Atlassian Documentation](https://confluence.atlassian.com/confkb/update-the-confluence-docker-image-to-use-oracle-jdk-829062521.html) 

# 〆

諸々躓く所があったが、無事にDocker ComposeでConfluenceを構築することができました。

上記のDocker環境はGitHubにも公開しております(OpenJDKです)。
[shinespark/docker-confluence: GitHub](https://github.com/shinespark/docker-confluence)

好みの環境にあわせてdocker-compose.ymlのホスト名だけ書き換えて `$ docker-compose up` すればほぼ問題なく利用可能です。PR歓迎しています。