OpenProjectが公開しているdocker-compose.ymlをAWS ECSにデプロイしてみました
(2020/4月ごろに投稿した記事を再度投稿します、内容的に古いところがあるかもです)
OpenProjectコンテナをECS(EC2)で動かしてみまして、それなりに苦労したので記録を残しておきます。
OpenProjectとは
オープンソースのプロジェクト管理ソフトウェアです。
https://www.openproject.org/
コミュニティ版dockerイメージが公開されています。
https://hub.docker.com/r/openproject/community
複数コンテナでの起動
One container per process (recommended)
にあるように、複数コンテナで動作させるのがおすすめだそうですのでこちらでやってみました。
ちなみにAll-in-one containerのケースだと、その名の通りapache2/PUMA(ruby)/メール処理(postfix)/memcached/postgresqlを一緒くたに動作させるという豪快さです。
(OpenProject本体は、One container per processとAll-in-onでdocker imageは同じ、2Gに迫る巨体というのがトホホ感すごいんですが、わざわざ分けるのも違うような・・・悩ましいところです)
docker-compose.ymlの入手
上記手順に従って
git clone --depth=1 --branch=stable/10 https://github.com/opf/openproject
とやるだけです。
作業時点でオリジナルファイルはこんな感じ。
version: "3.7"
networks:
frontend:
backend:
volumes:
pgdata:
opdata:
x-op-restart-policy: &restart_policy
restart: unless-stopped
x-op-image: &image
image: openproject/community:${TAG:-10}
x-op-app: &app
<<: *image
<<: *restart_policy
environment:
- "RAILS_CACHE_STORE=memcache"
- "OPENPROJECT_CACHE__MEMCACHE__SERVER=cache:11211"
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
- "DATABASE_URL=postgres://postgres:p4ssw0rd@db/openproject"
- "USE_PUMA=true"
# set to true to enable the email receiving feature. See ./docker/cron for more options
- "IMAP_ENABLED=false"
volumes:
- "opdata:/var/openproject/assets"
depends_on:
- db
- cache
services:
db:
image: postgres:10
<<: *restart_policy
stop_grace_period: "3s"
volumes:
- "pgdata:/var/lib/postgresql/data"
environment:
- POSTGRES_PASSWORD=p4ssw0rd
- POSTGRES_DB=openproject
networks:
- backend
cache:
image: memcached
<<: *restart_policy
networks:
- backend
proxy:
<<: *image
<<: *restart_policy
command: "./docker/proxy"
ports:
- "8080:80"
environment:
- APP_HOST=web
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
depends_on:
- web
networks:
- frontend
web:
<<: *app
command: "./docker/web"
networks:
- frontend
- backend
worker:
<<: *app
command: "./docker/worker"
networks:
- backend
cron:
<<: *app
command: "./docker/cron"
networks:
- backend
seeder:
<<: *app
command: "./docker/seeder"
restart: on-failure
networks:
- backend
編集
素のdocker-compose.ymlをecs-cli composeに食わせても盛大に怒られますので、以下の点を編集しました。
- version
今現在のecs-cliはversion3.7非対応ですので、3.xでの拡張部分を修正します。version記載部と変数部とかですね - network
ecs非対応なのでこれも削除。 - restart
これも非対応。 - depends_on
ECSで対応してないわけではないんですが、ecs-cliでは食ってくれず、依存関係はAWS console上で編集せねばならんようです。本来依存で解決しなければならないところがあるのですが、コマンドだけで完結させたかったのでカッコ悪いですが該当部はbash -c "sleep XX && command"を使いました・・・すいません - コンテナ間通信
EC2でも名前解決してくれないので必要なコンテナにlinks:記述を追加します。localhost記述でなんとかなるらしいですが、辛抱できずFargateではなくEC2で動かしました。 - logging
awslogsドライバを使ってCloudWatchに出力してます。 - proxyの外部ポート番号
これはECS関係ないですが80番で動かしたかったので。 - openproject/communityのタグ
${TAG:-10}となっていて、TAG指定しなかったら"10"ってやつを使うことになってるんですが、 https://hub.docker.com/r/openproject/community/tags を見ると latest/10.5.1/10.5/10 が同じハッシュになっててなんとも台無しなので、明示的に10.5.1とすべきですねこれは。 動かしたあとで気づいたので、もう試す気力もなく、以下引用のファイルではカッコ悪いですが変数部をベタ書きにしてます。
volumeについてはなにやってんだかよくわかりませんが、とりあえず残しました。
また、気持ち悪いのでmemcachedとpostgresqlはコンテナ動作させず、マネージドサービスを使うことにしました(昭和おじさんの性)。なのでdbコンテナとcacheコンテナの起動定義部は削除して、DB接続設定とmemcachedの設定部分も変えてます。
編集後のファイルはこんな感じです。
version: "3"
volumes:
opdata:
services:
proxy:
image: openproject/community:10.5.1
command: "./docker/proxy"
ports:
- "80:80"
- "443:443"
environment:
- APP_HOST=web
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
links:
- web:web
logging:
driver: awslogs
options:
awslogs-group: xxxx
awslogs-region: xx-xxxx-x
awslogs-stream-prefix: proxy
web:
image: openproject/community:10.5.1
environment:
- "RAILS_CACHE_STORE=memcache"
- "OPENPROJECT_CACHE__MEMCACHE__SERVER=xxx.xx.xxx.xxx.cache.amazonaws.com:11211"
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
- "DATABASE_URL=postgres://postgres:p4ssw0rd@openproject2.cm6di7rgxd2w.xx-xxxx-x.rds.amazonaws.com/openproject"
- "USE_PUMA=true"
- "IMAP_ENABLED=false"
volumes:
- "opdata:/var/openproject/assets"
command: "./docker/web"
logging:
driver: awslogs
options:
awslogs-group: xxxx
awslogs-region: xx-xxxx-x
awslogs-stream-prefix: web
worker:
image: openproject/community:10.5.1
environment:
- "RAILS_CACHE_STORE=memcache"
- "OPENPROJECT_CACHE__MEMCACHE__SERVER=xxx.xx.xxx.xxx.cache.amazonaws.com:11211"
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
- "DATABASE_URL=postgres://postgres:p4ssw0rd@openproject2.cm6di7rgxd2w.xx-xxxx-x.rds.amazonaws.com/openproject"
- "USE_PUMA=true"
- "IMAP_ENABLED=false"
volumes:
- "opdata:/var/openproject/assets"
command: bash -c "sleep 45 && ./docker/worker"
logging:
driver: awslogs
options:
awslogs-group: xxxx
awslogs-region: xx-xxxx-x
awslogs-stream-prefix: worker
cron:
image: openproject/community:10.5.1
environment:
- "RAILS_CACHE_STORE=memcache"
- "OPENPROJECT_CACHE__MEMCACHE__SERVER=xxx.xx.xxx.xxx.cache.amazonaws.com:11211"
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
- "DATABASE_URL=postgres://user:password@xxx.xxx.xxx.rds.amazonaws.com/openproject"
- "USE_PUMA=true"
- "IMAP_ENABLED=false"
volumes:
- "opdata:/var/openproject/assets"
command: "./docker/cron"
logging:
driver: awslogs
options:
awslogs-group: xxxx
awslogs-region: xx-xxxx-x
awslogs-stream-prefix: cron
seeder:
image: openproject/community:10.5.1
environment:
- "RAILS_CACHE_STORE=memcache"
- "OPENPROJECT_CACHE__MEMCACHE__SERVER=xxx.xx.xxx.xxx.cache.amazonaws.com:11211"
- "OPENPROJECT_RAILS__RELATIVE__URL__ROOT=${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
- "DATABASE_URL=postgres://user:password@xxx.xxx.xxx.rds.amazonaws.com/openproject"
- "USE_PUMA=true"
- "IMAP_ENABLED=false"
volumes:
- "opdata:/var/openproject/assets"
command: "./docker/seeder"
logging:
driver: awslogs
options:
awslogs-group: xxxx
awslogs-region: xx-xxxx-x
awslogs-stream-prefix: seeder
こうすると、変数使ってるオリジナルはスマートですねー
もともとはdb/cache/proxy/web/worker/cron/seederの7コンテナだったのが、上記の通りdbとcacheを外だししたので5コンテナ構成になってます。
それぞれ何やってるかですが、公式のドキュメントになにもなさそうなので一応書いておくと
- proxy
apache2。webコンテナにトラヒックを投げる。 - web
PUMAが動いている。処理本体。 - worker
よくわからんがrake jobs:workさせている。
(seederの処理が終わりきらないと、未処理のテーブル見にいって落ちる。restartしてくれれば救えるのにそれもしてくれないのでsleepさせてから起動) - cron
sleep使ってメール処理をループしている。cronって言われるとなんか違う気がしますが。。。 - seeder
初回起動時にdbの初期化を行う。
あとはecs-params.ymlですがこちらは割愛します。
ポイントはseederに"essential: false"を設定すること。上述のとおり、seederはdbの初期化をするコンテナなんですが、処理が終わるとexitしてしまうのでessential:true(default)だと全体が落ちてしまいます。dbの初期化してるだけなのにメモリをあまりケチるとOOMで落ちます。512MでOKでした。メモリ2Gあたりのインスタンス使うような場合は、このためだけにインスタンスタイプを上げてしまうことになりかねずなので、どうしてもちいさく動かしたい方はdocker-compose.ymlを2つに分けてseederだけまず動かして、その後seeder以外というふうにすればよいかと思います。
外部に持っていったrdsとelasticacheについても割愛。
起動
あとは
チュートリアル: Amazon ECS CLI を使用して EC2 タスクのクラスターを作成する
に従って作業すればよしです。
ちなみにopenprojectのimageですが、
The current Docker image does not support SSL by default.
です。前段にELBなりなんなり立ててそこで処理してあげるのが手っ取り早いです。