ある時、内部利用限定のサーバーでCentOS 8を立ててGitLabを運用していた。
GitLabはPlantUMLサーバーのURL指定するだけで、
マークダウンに書いたPlantUMLをレンダリングできるようになるので
PlantUMLも同一サーバー内で起動することにした。
GitLabは公式がパッケージインストールを推奨していたのでdnf install
していたが、
PlantUMLはDockerで動かそうと思った。
しかし、RHEL 8がDockerサポートしていない関係から、
作業時点では、CentOS 8もDocker Community Editionのインストール手順でサポート対象になっていなかった。
一応GitHubにはissueが存在しており、CentOS 8サポートの動きがちらほら見えるが、まだ作業途中らしい。
Support centos 8 · Issue #873 · docker/for-linux · GitHub
じゃあRHEL 8はDockerがないのかと思ったが、PodmanというRedHat製のコンテナツールがあるようだったので、
当面はこれでしのごうと思った。
NOTE: DockerでいうところのコンテナはPodmanではポッドと呼ぶようだが、面倒なのでこの投稿内では区別せずに全部コンテナと呼ぶ。
構成
Podmanを導入するにあたり、サーバーへのすべてのHTTP系リクエストをNginxでリバースプロキシしてやろうと考えた。
一部の人間しか利用しないサーバーだが、URLを打つたびにいちいちポート番号を指定するのが面倒だと考えたからだ。
PodmanでNginxを80番ポートで起動し、GitLabやPlantUMLはサブURLのアクセスでリクエストを割り振ることにした。
GitLabは/gitlab/
サブディレクトリで起動し、GitLab用のNginxもリッスンポートを適当に変更するように/etc/gitlab/gitlab.rb
の設定を変更した。
PlantUMLはpodman run
で起動するだけなので簡単だった。
余談
PlantUMLのイメージは公式のこのイメージのJettyサーバーのイメージ使わせてもらった。
しかし、このイメージはPlantUMLをROOT.war
としてデプロイしている。
Java Servletの仕様を知らなければわからないことだが、Java Servletは.war
のファイル名で
WEBサーバーにデプロイされたWEBアプリケーションのルートパスを決定する仕組みになっている。
例えばbar.war
をデプロイすると、そのWEBサーバーの/bar/
にアクセスすると
WEBアプリケーションの/
にマッピングされたコントローラー(厳密にはJava Servletだが)が呼び出しされる。
この中で、ROOT.war
という名前は特別で、/
にWEBアプリケーションをマッピングする。
PlantUMLはリバースプロキシのNginxから/plantuml/
へのアクセスをリバースプロキシしているが、
プロキシ先のURLが/
になってしまった。
イメージ提供元はdocker build
時にBUILD_ARG
渡してROOT.war
以外に名前を変更できるので、
docker-compose
を使うように案内している。
しかし、Podmanにもdocker-compose
互換を目指すpodman-compose
が存在するが、
今のところうまく動作しない部分が多いようなので利用は見送った。
PlantUMLは/
で起動することになるが、リバースプロキシで到達するだけなら問題ない。
ただし、リバースプロキシ下でのリダイレクトが難しくなったが、
GitLabからの利用だけであればリダイレクトすることはないと考え、リダイレクトの件は放置することにした。
Podmanで起きたトラブル
サーバーを再起動したらコンテナが停止していた
サーバーを再起動したところ、Podmanで起動していたコンテナが起動していない。
podman run --restart=always
をでコンテナを起動していたが、
今のところPodmanはオプションを受け付けるがサーバー再起動後にコンテナを起動しないらしい。
RHELのドキュメントなどを見る限り、Systemd Unit Fileでpodomanでコンテナを起動する様にしろということらいい。
少し面倒だが、内部利用サーバーなので再起動もめったにしないので運用でカバーする事にした。
何もしていないのにコンテナ間で通信できなくなった
本題。
GitLabはPlantUMLのURLを設定していれば、
MarkdownやAsciiDoc内にPlantUMLのDSLを書くだけで、PlantUMLによるレンダリング結果を出力する様にしてくれる。
ある日、Nginx経由でGitLabに接続していると、Markdown内に表示されているはずの
PlantUMLが表示されておらず、ブラウザのデベロッパーツールで確認すると
PlantUMLへの接続がタイムアウトしている事に気が付いた。
NginxのコンテナログをみるとNginx側でもPlantUMLへのリバースプロキシ接続がタイムアウトしている。
しかし、NginxからGitLabへのリバースプロキシは動作している。
PlantUMLもコンテナログを見る限り正常に動作し続けている。
何より不思議なのがcurlで直接PlantUMLのポート番号を指定するとレスポンスが返ってくる。
しかし、Nginxコンテナ内からPlantUMLコンテナに対してcurlでリクエストを行うとタイムアウトが発生する。
というかTCPコネクションが確立していない。
原因は不明だがNginxコンテナからPlantUMLコンテナの通信だけが成立しなくなっているようだった。
通信状況は以下のような感じ。
コンテナ間だけで通信が成立しない事例について調べてみると、以下のissueがヒットした。
- Containers cannot access published ports of other containers on same host · Issue #2886 · containers/podman · GitHub
- 1703261 – podman containers unable to communicate via IP to one container when use port forward on host
内容を呼んでみるとカーネルモジュールの br_netfilter がロードされている必要があるが、
CentOS 8はデフォルトでロードされていないらしい。
このコメントで提示してくれているように、
echo br_netfilter > /etc/modules-load.d/podman.conf
で br_netfilter モジュールをロードするようにするのが恒久的な解決策になるようだ。
試しにmodprobe br_netfilter
すると、NginxからPlantUMLへのリバースプロキシが復活した。
しかし、br_netfilter がロードされていないのが原因だと書かれているが、
この事象はNginxとPlantUMLのコンテナを起動して数日が経過してから発生したので、
ちょっと事象が違うような気もする。
この事象に見舞われた時に使用していたPodmanのバージョンは1.xだった、
最新版は2.xでメジャーバージョンアップしているので、
最新バージョンであればこのような問題は起きないかもしれない。
正直Dockerの代替として使うには、ちょっと問題が多いように感じた。
2020年9月18日追記
CentOS 8向けのパッケージが追加された: https://github.com/docker/for-linux/issues/873#issuecomment-694654334