いつの日かクラウドサービス使用不可案件が降ってくると信じて。1
概要
- 選んで使える静的Webサイト/テンプレート開発用ブラウザアプリスイート
- 数人~十数人程度での使用を想定
- KeycloakでSSO化
- nginx-proxyを使ってリバースプロキシの設定もdocker-compose.ymlに持たせる
- SSL対応(自己署名にも対応)
- アプリは原則として日本語対応のもので、できるだけ初期設定も日本語にする
- 既製イメージを利用し自分ではDockerfileを書かない
- イメージは今後も安定してリリースが継続されそうなものを選ぶ
- 極力dockerリソースには名前をつける
成果物
- スモークテストのみ実施
- インストール手順等はリポジトリ内の
docs/INSTALL_ja.md
に概ね書いてあるため、本稿では方針や試行錯誤した点を扱う
作業環境
- OS: RHEL8互換(今回はOL8)
- SELinuxは切っておく
- 作業はrootで行う
- 一部のシェルスクリプトはRHEL8互換でないディストロでは動かないかも
- CPU: 4コア
- メモリ: 16GB
- 8GB以上推奨(メモリ消費の多いものを使わないなら少なくても可)
- ストレージ: 500GB (SSD)
- 全て入れるとイメージだけで16GB強使用する
- dockerとdocker-compose
対応アプリケーション
名称 | 種別 |
---|---|
Keycloak | SSO |
GitLab | Gitプラットフォーム |
Gitea | Gitプラットフォーム |
CodiMD | Markdownエディタ |
Wiki.js | wiki |
GROWI | wiki |
Mattermost | チャットツール |
rocket.chat | チャットツール |
Wekan | カンバン |
TAIGA | プロジェクト管理 |
PENPOT | デザインプロトタイピング |
NextCloud | クラウドストレージ |
- 追加検討中
- SynapseまたはDendrite+Element: Matrixサーバ+クライアント
- Mailu: 全部入りメール環境
- dnsmasq: DNSサーバ(踏み台構築用)
- 選外
- Zulip: Postgresに専用パッケージを入れる必要がある
- OpenProject: SSO対応が有償版のみ
- Restyaboard: SSO対応が有償版のみ, 日本語が微妙
全導入イメージ一覧
名称 | 種別 | Port | Project Name | Alias | Depends on | Auth |
---|---|---|---|---|---|---|
Keycloak | SSO | Auto | keycloak | kc | pg | ID/Pass |
GitLab | Gitプラットフォーム | Auto | gitlab | pg redis | SAML | |
Gitea | Gitプラットフォーム | Auto | gitea | pg | OIDC | |
CodiMD | Markdownエディタ | Auto | codimd | pg | SAML | |
Wiki.js | wiki | Auto | wikijs | pg es | SAML | |
GROWI | wiki | Auto | growi | mongo redis es | SAML | |
Mattermost | チャットツール | Auto | mattermost | mm | pg | GitLab |
rocket.chat | チャットツール | Auto | rocketchat | rc | mongo | SAML |
Wekan | カンバン | Auto | wekan | mongo | OIDC | |
TAIGA | プロジェクト管理 | Auto | taiga | pg rmq | GitLab | |
PENPOT | デザインプロトタイピング | Auto | penpot | pg redis | GitLab | |
NextCloud | クラウドストレージ | Auto | nextcloud | nc | pg | SAML |
pgAdmin | PostgreSQL WebUI | Auto | pgadmin | pga | pg | |
phpMyAdmin | MariaDB WebUI | Auto | phpmyadmin | pma | maria | |
mongo-express | MongoDB WebUI | Auto | mongo_express | me | mongo | |
RedisInsight | Redis WebUI | Auto | redisinsight | ri | redis | |
ElasticHQ | Elasticsearch WebUI | Auto | elastichq | eshq | es | |
PostgreSQL | RDBMS | 5432 | postgres | pg | ||
MariaDB | RDBMS | 3306 | mariadb | maria | ||
MongoDB | NoSQL | 27017 | mongo | |||
Redis | KVS | 6379 | redis | |||
Elasticsearch | 全文検索 | 9200 | elasticsearch | es | ||
RabbitMQ | メッセージキューイング | 5672 | rabbitmq | rmq | ||
nginx-proxy | Reverse Proxy | 80,443 | proxy | |||
MailCatcher | ダミーSMTPサーバ | Auto | mailcatcher | smtp | ||
Scope | Docker WebUI | 4040 | scope | |||
Portainer | Docker WebUI | 9000 | portainer |
- self-sign-cert: 自己署名作成用
使用バージョン/リポジトリ
名称 | Tag | Version | Override | Repository | Tags |
---|---|---|---|---|---|
Keycloak | latest | 12.0.4 | config | quay.io | Link |
GitLab | latest | 13.9.3-ce.0 | dockerhub | Link | |
Gitea | 1 | 1.13.4 | dockerhub | Link | |
CodiMD | latest | 2.3.2 | dockerhub | Link | |
Wiki.js | 2 | 2.5.191 | dockerhub | Link | |
GROWI | latest-nocdn | 4.2.12-nocdn | dockerhub | Link | |
Mattermost | latest | 5.32.1 | dockerhub | Link | |
rocket.chat | latest | 3.12.1 | dockerhub | Link | |
Wekan | latest | v5.05 | quay.io | Link | |
TAIGA | latest | 6.x | source | dockerhub | |
PENPOT | latest | 1.3.0-alpha-1 | source | dockerhub | |
NextCloud | 21-apache | 21.0.0-apache (21.0.0.18) | config | dockerhub | Link |
pgAdmin | latest | 5.0 | dockerhub | Link | |
phpMyAdmin | latest | 5.1.0-apache | dockerhub | Link | |
mongo-express | latest | 0.54.0 | dockerhub | Link | |
RedisInsight | latest | 1.10.0 | dockerhub | Link | |
ElasticHQ | latest | v3.5.6 | dockerhub | Link | |
PostgreSQL | 13-alpine | 13.2-alpine | dockerhub | Link | |
MariaDB | 10 | 10.5.9 | dockerhub | Link | |
MongoDB | 4-bionic | 4.4.4-bionic | dockerhub | Link | |
Redis | 6-alpine | 6.2.1-alpine | dockerhub | Link | |
Elasticsearch | 6.8.14 | 6.8.14 | elasticsearch | Link | |
RabbitMQ | 3-management-alpine | 3.8.14-management-alpine | dockerhub | Link | |
nginx-proxy | alpine | 0.8.0 | source | dockerhub | Link |
MailCatcher | latest | dockerhub | Link | ||
Scope | latest_release | 1.13.1 | dockerhub | Link | |
Portainer | alpine | 1.24.1-alpine | dockerhub | Link |
docker-composeプロジェクト構築
共通方針
プロジェクト名・サービス名・コンテナ名
- プロジェクト名は必ず.envの
COMPOSE_PROJECT_NAME
で明示的に指定し、ディレクトリ名と一致させる- ハイフン・アンダースコア使用不可
- プロジェクト内のメインとなるコンテナのサービス名とコンテナ名も違和感がなければ合わせる
- container_nameは明示的に指定する
- hostnameに転用する関係でcontainer_nameにはアンダースコア使用不可
hostname
- 明示的に指定する
- aliasがあるものは
[alias]-server
、ないものは[container_name]-server
を原則とする - アンダースコア使用不可
ports, expose
-
expose
にコンテナ側のポートを指定する - コンテナ間ネットワークに繋がったnginx-proyxで配信するため
ports
は使用しない
volumes
- 一方的にコンテナに送り込むファイル・ディレクトリは
<project_root>/mount
に配置する- オーバーライド用の設定ファイルや鍵など
- git管理内とする
- ホストとコンテナ双方で編集するか、ホストから頻繁に参照するディレクトリは
<project_root>/volumes
に配置する- 設定など
- git管理外とする(雛形などがあるならgit管理内の別の場所に用意しておく)
- 通常閲覧も編集もしないものは
docker volume
で予め作成しておく- データベースのデータディレクトリなど
- 命名は
[project_name]-<purpose>
を原則とする
- 勝手にvolumeが作られる場合は
docker inspect
してmounts
の内容を確認する
networks
- 原則として全てのコンテナはbridgeネットワーク
docker.internal
に繋げておく - 個別に必要な場合は名前をつける
- ハイフン使用不可
logging
- 標準では無制限のため理由がなければ予防的措置として全てのサービスに下記を設定しておく
logging:
options:
max-size: "10m"
max-file: "3"
extra_hosts
- テレメトリっぽいものや不要な外部アクセスを
192.0.2.0
に向けて遮断 - KeycloakのアドレスをサーバのローカルIPに向ける
- コンテナ内部からKeycloakへのアクセスを外に出さず、かつSSLでも通るように
ホスト上でのdocker-composeの操作
専用のMakefileを使用する。詳しくはMakafileのREADME参照。
もちろん普通にdocker-composeコマンドを打っても構わない。
コンテナ作成後はScopeやPortainerでブラウザ上から操作。
カテゴリ毎の方針
データベース等
PostgreSQL, MariaDB, MongoDB, Redis, RabbitMQ, ElasticSearch
- コンテナ間ネットワークでのみ使用
- rootユーザーはユーザー名
root
パスワードroot
とする- PostgreSQL, MariaDB, MongoDB, RabbitMQで設定
- Mongo, Redis, ElasticSearchは認証なし
- 初期化は
inidb.d
を使用して初回起動時に自動で行う- RabbitMQはdocker-composeを使った初期化方法が分からないためWebUIから行う
- RedisとElasticSearchは初期化不要
- MongoDBはrocket.chatがレプリケーションを必要とするため独立した初期化処理が必要(シェルスクリプトで対応)
データベース用WebUI
pgAdmin, phpMyAdmin, mongo-express, RedisInsight, RabbitMQ (Built-in), ElasticHQ
- ローカルネットワークからのアクセスのみ許可する
- 具体的には
127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
からのアクセスのみ許可- environmentに
NETWORK_ACCESS=internal
と書いておくとnginx-proxyが自動でやってくれる
- environmentに
サーバ
nginx-proxy, MailCatcher
- 特に設定することはない
モニタリング
Scope, Portainer
- nginx-proxyの管理下には置かない
- ポート
4040
と9000
をファイアーウォールで適切に制限する - ブラウザ経由でコンテナにフルアクセスできるため絶対に外部に公開しないこと
アプリケーション
Keycloak, GitLab, Gitea, CodiMD, Wiki.js, GROWI, Mattermost, rocket.chat, Wekan, TAIGA, PENPOT, NextCloud
- 管理者アカウントはユーザー名
webmaster
, パスワードPa55w@rd
, メールアドレスwebmaster@<DOMAIN>
とする- ものによっては
admin
が使えない → 統一しておきたいのでwebmaster
としておく
- ものによっては
その他
SSO
各アプリの認証方式と、「Keycloak上のユーザープロパティ/属性」と「アプリのユーザー情報」のマッピングについては事前調査の記事を参照のこと。
アプリケーション毎の設定方法についてはリポジトリの各アプリのディレクトリにある INSTALL_ja.md
を参照のこと。
Gitea対策
上記の調査にある「Keycloakのユーザー属性に nickname
が存在するとGiteaが問答無用で使おうとする問題」については、Giteaのソースを大幅に修正する以外に解決方法がなさそうなため、ユーザー属性には nickname
を使用しないことにした。
表示名を格納するユーザー属性は displayName
としてマッピングで対処する。
TAIGAのソース修正
TAIGAの公式Dockerイメージに同梱されているGitLab OAuth2用の taiga-contrib-gitlab-auth
プラグインの実装に問題があり、Keycloakと連携するにはソースの修正が必要だった。
具体的にはrequests.postに渡すデータの型が param
(クエリパラメータ)になっており、これを data
に修正する。(該当箇所のソース)
PENPOTのソース修正
PENPOTの使用しているhttp実装はhttpステータスコード307によるPOSTリクエストのリダイレクトに対応しておらず、Keycloakコンテナ上のGitLabのエンドポイントに対するアクセスをKeycloakのエンドポイントにリダイレクトすることができないため、ソースの修正が必要だった。
具体的にはハードコーディングされているGitLab OAuth2エンドポイントのパスを、KeycloakのOpenID Connectエンドポイントのものに書き換えればよい。(該当箇所のソース)
SSL対応
nginx-proxyが各docker-compose.ymlのenvironmentを見てよしなにやってくれる。
各アプリケーションのenvironmentに記述するnginx-proxy用の設定項目のうち、SSLに関連する設定項目の有無を見て自動で構成してくれる模様。詳しい条件はconf出力用のテンプレートを読めば書いてある。
SHWでは SSL_POLICY
の有無で分岐させている。
自己署名認証局
ホスト上で認証局の証明書を然るべきディレクトリに配置し update-ca-trust
で登録すると、PEMだけでなくJavaやEDK2用のバイナリなど各種フォーマットのCAバンドルが自動で生成される。
生成したCAバンドルを必要に応じてコンテナにマウントすることで、既製イメージ内のOS、Python、Java等に任意の認証局を追加できる。( update-ca-certificates
とか keytool -importcert -trustcacerts -file ca.crt -cacerts -storepass changeit -alias self-sign -noprompt
とかやらなくて済む)
なお、証明書と鍵の作成には下記記事のものを使用した。
サーバの証明書と秘密鍵はnginx-proxyが使用するため、マウント用ディレクトリにコピーする。
一連の作業はシェルスクリプトで実行できるようにした。詳しくは setup/*selfsigncert.sh
を参照のこと。
インストール用シェルスクリプト
サーバのローカルIP・ドメイン・SSLの使用有無・言語・タイムゾーンといった設定項目は関係するファイル全般に反映する必要があるが、対象が――
- docker-compose.yml
- volumesでマウントする各アプリ用の設定ファイルや修正を加えたソース
- jsonとかphpとかpythonとかclojureとか色々
- nginx-proxy用設定ファイルのファイル名
- ドメイン名とファイル名を照合して取捨選択する仕組みのため
――と多岐に渡るため、予め設定部分を置換用文字列にしておき、シェルスクリプトで設定ファイルに従って置換する方法を採った。
処理の内訳は docs/INSTALL_ja.md
に詳しく書いてある。
コンテナのタイムゾーン統一
イメージによって tzdata
が入っていたり入っていなかったりするため、environmentで TZ=Asia/Tokyo
を指定しても日本時間にならないものがある。
全てのコンテナに TZ=Asia/Tokyo
を設定してdateコマンドを打ってみたところ、全34コンテナ中21個がJST、11個がUTC、2個がAsia(時刻はUTC)となった。
要はtzdataのAsia/Tokyo用のバイナリがコンテナ内に存在していればいい訳で、ホスト上の /usr/share/zoneinfo/Asia/Tokyo
をそのまま read-only
でコンテナにマウントすることにした。
︙
environment:
- TZ=Asia/Tokyo
volumes:
- /usr/share/zoneinfo/Asia/Tokyo:/usr/share/zoneinfo/Asia/Tokyo:ro
︙
Asia/Tokyo
は置換用文字列 <TZ>
にしておき、インストール用シェルスクリプトの一括置換対象とする。
ToDo
- アプリのUI上の時刻表示が日本時間になっているか確認する
- docsの整備
- production用に使用するバージョンを固定したものも欲しい
-
Dockerもdocker-composeもなんならLinuxも初学者なので多少の粗はご容赦ください ↩