コンテンツにスキップ

環境構築後の手動作業手順 (forDemo)

terraform/(coordination環境・coordination-client環境)をterraform applyで新規構築した後、手動で必要な作業をまとめる。対象は運航調整機能(coordination)のforDemo環境(issue #108, docs/coordination/DipsConnector_forDemo.md関連)。

terraform apply直後の状態:

  • coordination環境: docker compose up -dまで完了し、backend/postgres/nginx/mock-utmが起動済み(模擬DIPSとは未接続、DIPS_MQTT_ENABLED=false
  • coordination-client環境: SSM/sshdが有効な状態でEC2が起動済み(追加のセットアップなし)

1. 模擬DIPS(MQTT)接続の有効化(模擬DIPSと接続する試験を行う場合のみ)

Section titled “1. 模擬DIPS(MQTT)接続の有効化(模擬DIPSと接続する試験を行う場合のみ)”

既定では模擬DIPSへのMQTT接続(DIPS Connector相当機能)は無効。模擬DIPSと接続した試験(重複通知・重複解除通知をDIPS経由で受信する試験)を行う場合のみ、下記を実施する。

compose.ymlbackendサービスには、DIPS_MQTT_*環境変数(既定値付き)と証明書配置用ボリューム(./certs/dips:/certs/dips:ro)が組み込み済み。/opt/app/.envDB_PASSWORD等の機密情報を含むファイル)自体は書き換えず、模擬DIPS用の値だけ追加のenv-fileとして重ねて起動する方式のため、通常の起動手順(docker compose up -d)や.envには影響しない。

模擬DIPSから提供されたPEM形式の証明書(ルートCA証明書・クライアント証明書・クライアント秘密鍵)を、DipsMqttPropertiessrc/main/java/com/intent_exchange/utm/config/DipsMqttProperties.java)のjavadoc記載のコマンドでPKCS12形式へ変換し、coordination環境の/opt/app/certs/dips/配下へ配置する。

# (例。javadoc記載のコマンドを参照。実行はローカル等の作業端末で行い、変換済みファイルをcoordination環境へ配置する)
$ keytool -import -alias dips-root-ca -file AmazonRootCA1.pem \
-keystore dips-mqtt-truststore.p12 -storetype PKCS12
$ openssl pkcs12 -export -in <client-certificate>.crt -inkey <client-private>.key \
-name dips-mqtt-client -out dips-mqtt-keystore.p12
# coordination環境側(SSM接続後)
$ sudo mkdir -p /opt/app/certs/dips
# (変換済みの dips-mqtt-truststore.p12 / dips-mqtt-keystore.p12 を /opt/app/certs/dips/ へ配置)

1.2. env/dips-enabled.envの作成(初回のみ)

Section titled “1.2. env/dips-enabled.envの作成(初回のみ)”

terraform/modules/coordination/app/env/dips-enabled.env.exampleをテンプレートとして、coordination環境上に実際の値を記載したファイルを作成する。パスワード等の機密情報を含むため、このファイル自体はリポジトリにコミットせず、S3同期対象にも含めない(SSM接続後、EC2上で直接作成する)。

$ sudo mkdir -p /opt/app/env
$ sudo tee /opt/app/env/dips-enabled.env > /dev/null <<'EOF'
DIPS_MQTT_ENABLED=true
DIPS_MQTT_TRUSTSTORE_PATH=/certs/dips/dips-mqtt-truststore.p12
DIPS_MQTT_TRUSTSTORE_PASSWORD=(実際のパスワード)
DIPS_MQTT_KEYSTORE_PATH=/certs/dips/dips-mqtt-keystore.p12
DIPS_MQTT_KEYSTORE_PASSWORD=(実際のパスワード)
EOF
$ sudo chmod 600 /opt/app/env/dips-enabled.env

飛行計画REST API(dips.api.*)を有効にする場合は、DIPS_API_ENABLED=trueに加えて DIPS_API_HOSTの設定が必須である。既定値を持たないため、未設定のまま有効にすると起動時に 失敗する(設定漏れに気づかないまま外部ホストへ接続しないようにするための仕様。 docs/issues/issue-143-dips-base/open-questions.md E-18)。ホスト名は証明書と併せて模擬DIPS 提供元から入手する。

1.3. 起動(模擬DIPSと接続する場合)

Section titled “1.3. 起動(模擬DIPSと接続する場合)”

通常のsudo docker compose up -dの代わりに、下記を実行する(.envは変更せず、追加のenv-fileを重ねて指定するだけでよい)。

$ cd /opt/app
$ sudo docker compose --env-file .env --env-file env/dips-enabled.env up -d
$ sudo docker compose restart nginx

模擬DIPSとの接続を終えて通常の状態に戻す場合は、追加のenv-fileを外して再起動するだけでよい(.env側の既定値DIPS_MQTT_ENABLED:-falseに戻る)。

$ sudo docker compose up -d

2. client環境: DIPS飛行計画通報API用クライアント証明書の配置(初回のみ)

Section titled “2. client環境: DIPS飛行計画通報API用クライアント証明書の配置(初回のみ)”

模擬DIPSの飛行計画通報API(POST https://${DIPS_API_HOST}/api/v1/{USSID}/flightplan)をclient環境からCurl実行するために、クライアント証明書を配置する。実ホスト名は本書に書かない(他社が運用する検証環境へ意図せず到達するのを防ぐため。docs/issues/issue-143-dips-base/open-questions.md E-18)。証明書と併せて模擬DIPS提供元から入手し、DIPS_API_HOSTに設定する。

  • 配置先: /home/ec2-user/certs/dips/client.pem/home/ec2-user/certs/dips/client.key
  • 証明書自体は模擬DIPS提供元(NTT-DATA様)から入手し、ローカルからSCP等で配置する

3. uss_default_info初期データの投入(既知の未自動化事項。模擬DIPSと接続する試験を行う場合のみ)

Section titled “3. uss_default_info初期データの投入(既知の未自動化事項。模擬DIPSと接続する試験を行う場合のみ)”

DIPS Connector相当機能(ReceiveDipsAdjustmentMessageUseCase)が、DIPSの重複通知に含まれない情報(自分側UTMエンドポイントURL・運航者識別子等)を補うために参照するcoordination.uss_default_infoテーブルは、docker-entrypoint-initdb.ddb/schema/coordination.sql)の実行時にテーブル自体は作成されるが、初期データ(IX_001NTTD_001の2行)は投入されない。

これは、初期データがdb/migrations/002_seed_uss_default_info.sqlsql-migrate用の例外的マイグレーション。db/README.md参照)として管理されており、docker-entrypoint-initdb.ddb/schema/配下のみを対象とするため。新規環境構築直後は、下記のいずれかの方法で手動投入が必要(模擬DIPSと接続しない試験ではuss_default_infoを参照しないため、この節は不要)。

方法A: SQLを直接実行する(推奨。追加ツール不要)

Section titled “方法A: SQLを直接実行する(推奨。追加ツール不要)”
$ cd /opt/app
$ sudo docker compose exec postgres psql -U utm -d utm -c "
INSERT INTO coordination.uss_default_info (uss_id, utm_endpoint_url, operator_id, mail_address, utm_usage_type)
VALUES
('IX_001', 'https://demo-001.uss-intent-exchange.com', 'ix_ope_001', 'ix-dummy@example.com', 'IX_UTM'::coordination.utm_usage_type),
('NTTD_001', 'https://diana.airpaletteutm.com', 'nttd_ope_001', 'nttd-dummy@example.com', 'COORDINATION_CAPABLE_UTM'::coordination.utm_usage_type)
ON CONFLICT (uss_id) DO NOTHING;
"

方法B: sql-migrateを正規の手順で実行する

Section titled “方法B: sql-migrateを正規の手順で実行する”

開発者の手元でpsqldef/sql-migratemise install済み)を使い、下記「4. DB構成変更時のスキーマ追従」と同じ要領でcoordination環境のpostgresへポートフォワード接続したうえで実行する。sql-migrate$(mise which sql-migrate)で呼ぶ理由は「4.」の注意書きを参照。

$ DB_HOST=localhost DB_PORT=15432 DB_NAME=utm DB_USERNAME=utm DB_PASSWORD=<DB_PASSWORD> \
$(mise which sql-migrate) up -env development -config db/dbconfig.yml

この投入は冪等(ON CONFLICT (uss_id) DO NOTHING)なため、複数回実行しても問題ない。

自動化の余地: 「6. 今後の自動化検討」参照。

3-2. Assetサンプルデータ(機体・操縦者)の投入(既知の未自動化事項。飛行計画の登録を行う場合のみ)

Section titled “3-2. Assetサンプルデータ(機体・操縦者)の投入(既知の未自動化事項。飛行計画の登録を行う場合のみ)”

飛行計画の登録(FLIGHT_PLAN_PILOT_ASSIGNMENT.pilot_id / .aircraft_id)が参照するasset.asset / asset.asset_uas_attrs / asset.asset_uas_dips_attrs / asset.pilotの4テーブルは、「4.」のpsqldef適用(db/schema/asset.sql)でテーブル自体は作成されるが、機体7件・操縦者7件のデータは投入されない(forDemo環境にはasset.sqlを配布していないため、docker-entrypoint-initdb.dでは作成されない。「4.」冒頭の注記参照)。

理由は、データがdb/seed/local/asset_sample_data.sqldb/README.mdが定めるローカル検証用シード。psqldefsql-migrateいずれも関与せずpsqlで直接投入する)として管理されており、docker-entrypoint-initdb.ddb/schema/配下のみを対象とするため。ここで投入されるASSET.id / PILOT.idはフロントエンドと合意した固定IDであり、飛行計画の登録画面はこのIDを直接保持している。新規環境構築直後は、下記の方法で手動投入が必要。

db/seed/local/asset_sample_data.sqlsql-migrate用のマイグレーションではなく単純なpsqlスクリプトのため、ファイル全体をそのまま流せばよい(uss_default_info(3節)の-- +migrate Up/-- +migrate Downのような抽出は不要)。

[!IMPORTANT] このファイルはEC2へ配布されていない。 S3経由で/opt/appへ配布しているのはdb/schema/coordination.sqlの1本のみ(terraform/modules/coordination/user_data.sh.tpl。「4.」冒頭の注記参照)。そのためEC2上でsudo docker compose exec -T postgres psql ... < db/seed/local/asset_sample_data.sqlとは書けず、開発者の手元からポートフォワード経由で流す。「4.」の手順2・3でポートフォワードとDBパスワードを用意した状態のまま、同じセッションで実行できる。

作業端末にpsqlは入っていない(miseの管理対象はpsqldefsql-migrateのみ)ため、ローカルにあるpostgisイメージのpsqlを使い捨てコンテナで借りる。--network hostでホストのポートフォワード先(127.0.0.1:15432)へ到達させる。

$ cat db/seed/local/asset_sample_data.sql \
| docker run --rm -i --network host -e PGPASSWORD=<DB_PASSWORD> postgis/postgis:18-3.6 \
psql -h 127.0.0.1 -p 15432 -U utm -d utm -v ON_ERROR_STOP=1

冪等(4つのINSERTすべてがON CONFLICT ... DO NOTHING)なため、複数回実行しても問題ない。INSERT 0 5が4行出れば新規投入、INSERT 0 0が4行なら既に投入済み。

$ docker run --rm --network host -e PGPASSWORD=<DB_PASSWORD> postgis/postgis:18-3.6 \
psql -h 127.0.0.1 -p 15432 -U utm -d utm -c "
SELECT (SELECT count(*) FROM asset.asset) AS assets,
(SELECT count(*) FROM asset.pilot) AS pilots,
(SELECT count(*) FROM asset.asset_uas_dips_attrs WHERE dips_aircraft_type IS NULL) AS dips_type_null;
"

assets=5 / pilots=5 / dips_type_null=0であること(dips_aircraft_typeはDIPS通報のflightPlanInfo.aircraftInfo[].typeに対応する必須項目)。

自動化の余地: 「6. 今後の自動化検討」参照。

4. DB構成変更時のスキーマ追従(継続運用)

Section titled “4. DB構成変更時のスキーマ追従(継続運用)”

docker compose up -dでアプリのイメージを更新しても、postgresコンテナのボリューム(postgres_data)は初回作成時のまま維持され、スキーマは自動更新されない(db/schema/docker-entrypoint-initdb.d経由のためボリュームが空の場合にしか実行されない)。新規構築直後は最新のスキーマが適用された状態だが、以後デプロイのたびにdb/schema/*.sqldb/migrations/*.sqlに変更が入っていないか確認し、必要に応じて追従させること。

事前条件(初回のみ): compose.ymlpostgresサービスに127.0.0.1:5432のポート公開を追加済み(backend/mock-utmと同様、ループバック限定)。

[!WARNING] sql-migrateはmiseシム経由で実行しないこと。 sql-migrateは接続先を環境変数でしか指定できないが、mise.toml[env] _.file = ".env"により、miseシム(~/.local/share/mise/shims/sql-migrate)にはローカル開発用の.envの接続情報が注入され、コマンド行にインライン指定したDB_HOST/DB_PORT等より優先されるdb/README.md「検証時の注意」参照)。ポートフォワードの15432を指定したつもりでも、ローカルのlocalhost:5432へ接続してしまう。

本節では$(mise which sql-migrate)と書いて実体バイナリを直接呼ぶこと。これならインライン指定の環境変数が効く。

あわせて、手順4・6の実行前にはローカルのdocker composeのpostgresを停止しておくこと。誤ってシム経由で実行した場合、ローカル開発DBが起動していると、そちらへマイグレーションが適用されてしまう(起動していなければconnection refusedで止まる)。

psqldef--host/--port/--userをコマンドライン引数で渡すため、この問題の影響を受けない。

[!IMPORTANT] 現時点のforDemo環境で適用しないのは通知基盤スキーマ(db/schema/notification_*.sqldb/schema/00_extensions.sqldb/migrations/003_notification_outbox_notify.sql)のみ(issue #156)。それ以外(coordination / asset / flight_planningの各スキーマと00_shared_extensions.sqlpostgis拡張)はすべて追従対象とする。

  • 配布(S3→EC2)と適用(psqldef)は別物である。 EC2の/opt/app/db/schema/へ配布しているのはcoordination.sqlの1本のみだが(terraform/modules/coordination/user_data.sh.tpl)、psqldefは開発者の手元のリポジトリからスキーマを流すため、配布されていないスキーマも適用できる。配布物が効くのはdocker-entrypoint-initdb.d(=postgres_dataボリュームが空のときの初期化)だけである
  • 上記の裏返しとして、現状はpostgres_dataボリュームを作り直すとassetflight_planningpostgis拡張が欠けた状態で初期化される。作り直した場合は必ず本節のpsqldefを再実行して追従させること
  • 配布物をdb/schema/のディレクトリ同期へ変更する恒久対応は、通知基盤側の是正(CREATE EXTENSION btree_gistdb/schema/へ移す・psqldefのスキーマ解決の是正)がmainへマージされた後に、assetflight_planning00_shared_extensions.sqlも含めて行う
  • 通知基盤の.sqlは配布・適用ともしていないため、通知基盤のテーブルはこの環境に存在しない
  • backendとmock-utmの通知Worker(Outbox監視)は停止したままにする。compose.ymlenvironmentNOTIFICATION_OUTBOX_LISTENER_ENABLED: ${NOTIFICATION_OUTBOX_LISTENER_ENABLED:-false}を既定falseとして宣言済みのため、追加の指定は不要。trueにすると通知基盤テーブルが無い状態で監視が動き、60秒ごとにエラーログが出る
  • --env-fileはcompose.yml内の${...}置換に使う変数を与えるものであり、サービスのenvironmentに宣言のない変数はコンテナへ渡らない(DIPS_MQTT_*が追加env-fileで効くのは、compose.yml側に宣言があるため)
  • 10月デモでは通知基盤のOutboxテーブルへ手動登録して通知を出す可能性があるため、最終的には通知基盤のDDLも流す方針。その段階で本注記は見直す

運航調整機能・飛行計画機能のコードから通知基盤テーブルへの参照はないため、この状態で疎通試験に支障はない。

  1. 変更の有無を確認する(開発者の手元、対象コミットのutm-backendリポジトリで)
$ git diff <前回デプロイ時のコミット> HEAD -- db/schema db/migrations

差分がなければ、以降の手順は不要。

<前回デプロイ時のコミット>が不明な場合は、ECRへの前回push日時から特定する(イメージにコミットハッシュのタグは付けていないため)。

$ aws ecr describe-images --region ap-northeast-1 --repository-name demo-001-coordination-app \
--query 'sort_by(imageDetails,&imagePushedAt)[].[imagePushedAt,join(`,`,imageTags||[`-`])]' --output text
# 前回push日時を確認したうえで、その時点のブランチ先端を reflog から特定する
$ git reflog show sprint2/coordination --date=iso
  1. coordination環境のpostgresへポートフォワード接続する
$ aws ssm start-session --target i-03c3cba3107ebdfed \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["5432"],"localPortNumber":["15432"]}'
  1. DB接続パスワードを取得する(別ターミナルで)
$ aws ssm get-parameter --region ap-northeast-1 \
--name "/demo-001-coordination/db_password" --with-decryption \
--query 'Parameter.Value' --output text
  1. マイグレーションの適用順を確認する(db/migrations/に新規ファイルがある場合)

psqldef(宣言的スキーマ同期)とsql-migrate(例外的なマイグレーション)は、変更内容によって適用順が変わる。まず未適用のマイグレーションを確認する。

$ DB_HOST=localhost DB_PORT=15432 DB_NAME=utm DB_USERNAME=utm DB_PASSWORD=<DB_PASSWORD> \
$(mise which sql-migrate) status -env development -config db/dbconfig.yml

未適用のファイルの中身を見て、psqldefの前後どちらで適用すべきかを判断する。

  • psqldefより前に適用するもの: 拡張(CREATE EXTENSION)・関数など、db/schema/*.sqlのテーブル定義が依存するもの。例: 002_notification_btree_gist.sqlnotification_zz_template_exclude.sqlのEXCLUDE制約がbtree_gist拡張を前提とする)
  • psqldefより後に適用するもの: トリガ・データ移行など、対象テーブルが既に存在することを前提とするもの。例: 003_notification_outbox_notify.sqlnotification_outboxテーブルの存在が前提)

前者・後者が同時に未適用の場合、sql-migrate upを一度に流すと後者が失敗する。-limit=<件数>で前者だけを先に適用すること(未適用分はファイル名順に適用される)。

$ DB_HOST=localhost DB_PORT=15432 DB_NAME=utm DB_USERNAME=utm DB_PASSWORD=<DB_PASSWORD> \
$(mise which sql-migrate) up -limit=<件数> -env development -config db/dbconfig.yml

なお、本環境ではsql-migrateをこれまで一度も実行していないため、適用履歴テーブル(migrations)自体が存在せず、statusでは既存分を含む全ファイルが未適用(no)として表示される(初期構築時のスキーマはdocker-entrypoint-initdb.dが配布済みのdb/schema/coordination.sqlを流したもので、db/migrations/は経由していない)。既存分の再適用は問題ない。

  • 001_initial.sql: 中身はコメントのみのベースラインで、適用しても何も起きない
  • 002_seed_uss_default_info.sql: ON CONFLICT (uss_id) DO NOTHINGで冪等。「3.」の方法Aで投入済みでも二重投入にならない

2026-09-02時点の未適用4件の場合は、-limit=3001_initial / 002_notification_btree_gist / 002_seed_uss_default_info)を手順5の前に適用し、003_notification_outbox_notify.sql適用しない(上記の通知基盤スキーマ対象外の方針による)。

また「5.」のpostgres_dataボリューム作り直しを実施した場合、migrationsテーブルもuss_default_infoの投入内容も失われるため、statusは再び全件未適用に戻る。作り直し後はstatusの結果を鵜呑みにせず、「3.」の初期データ投入状況(SELECT uss_id FROM coordination.uss_default_info;)もあわせて確認すること。

  1. スキーマ差分を確認・適用する(psqldef)
$ cat db/schema/00_shared_extensions.sql db/schema/asset.sql db/schema/coordination.sql db/schema/flight_planning.sql \
| PGPASSWORD=<DB_PASSWORD> psqldef --host localhost --port 15432 --user utm --dry-run utm
# 内容を確認のうえ問題なければ --dry-run を外して再実行
$ cat db/schema/00_shared_extensions.sql db/schema/asset.sql db/schema/coordination.sql db/schema/flight_planning.sql \
| PGPASSWORD=<DB_PASSWORD> psqldef --host localhost --port 15432 --user utm utm
  • 渡すのは通知基盤以外の全ファイルで、辞書順にcatして1回で渡す(本節冒頭の注記参照)。ファイルごとにpsqldefを複数回実行してはならない。 psqldefは渡されたDDLを「DB全体のあるべき状態」として扱うため、その1回に含めなかったテーブルはすべてDROP候補として扱われる。db/README.mdのデプロイ手順(cat db/schema/*.sql)から通知基盤のnotification_*.sql00_extensions.sqlbtree_gist。通知基盤専用)を除いたものにあたる。
  • 辞書順は依存関係と一致している。flight_planning.flight_plan_pilot_assignmentasset.pilotasset.assetをFK参照するためasset.sqlflight_planning.sqlの順が必須で、00_shared_extensions.sqlpostgis拡張はflight_planningのgeometry列より先に必要(mise run db:schemadocker-entrypoint-initdb.dも同じ辞書順で流す)。
  • 00_shared_extensions.sqlを必ず含めること。 forDemo環境にはpostgis拡張が存在しない(「5.」の補足参照)。飛行経路の妥当性検証(PostgisGeometryValidator)がPostGIS関数を使うため、拡張が無いと飛行計画APIが失敗する。psqldefは入力のCREATE EXTENSION IF NOT EXISTS postgis;をそのまま差分として出力・実行する(psqldef 3.11.4で実測)。
  • psqldefは渡されたDDLを「DB全体のあるべき状態」として扱うため、渡さなかったテーブルに対するDROPが差分として現れるが、--enable-dropを付けない限り実行されず-- Skipped: DROP TABLE ...として出力されるだけ(psqldef 3.11.4で実測)。--enable-dropは付けないこと。 migrations(sql-migrateの管理テーブル)もdb/schema/には無いため、常にSkippedとして出力される。なおpostgis拡張が持つpublic.spatial_ref_sys等は拡張の所有物としてDROP候補にならない(psqldef 3.11.4で実測)。
  • --dry-runの出力で、Skippedではない実行対象のDDLが想定どおりかを必ず確認する。
  • asset.asset_uas_attrsのCHECK制約2件(ck_asset_uas_attrs_weight_positiveck_asset_uas_attrs_mtow_positive)は、適用済みでも毎回DROP CONSTRAINTADD CONSTRAINTの差分として出力される(psqldefが組み立てる比較形がPostgreSQL側の格納形と一致しないため。db/schema/asset.sqlの該当箇所のコメント参照)。この2件だけが出ている状態は「差分なし」と判断してよい。
  • 対象テーブルにレコードが残っている状態でNOT NULLカラムを追加しようとすると失敗する(DEFAULT付きの場合は失敗しない)。その場合は先にDBのデータクリア(TRUNCATE TABLE coordination.confliction, coordination.coordination CASCADE;)を実施してから再実行すること。
  1. 残りの例外的なマイグレーションを適用する(sql-migrate、手順4で未適用分が残っている場合のみ。通知基盤関連は除く)
$ DB_HOST=localhost DB_PORT=15432 DB_NAME=utm DB_USERNAME=utm DB_PASSWORD=<DB_PASSWORD> \
$(mise which sql-migrate) up -env development -config db/dbconfig.yml
  1. ポートフォワードのセッションを終了する(手順2のターミナルで Ctrl+C)

4-2. アプリイメージ更新(継続運用)

Section titled “4-2. アプリイメージ更新(継続運用)”

compose.ymlのbackend・mock-utmはECRのdemo-001-coordination-app:latestを参照している。イメージを更新した場合の反映手順。

「4.」のスキーマ追従(および必要なら「3.」「3-2.」の初期データ投入)を先に済ませ、そのあとにイメージを更新する。 逆順にすると、新イメージが参照するテーブルが未作成の状態で起動することになる(テーブル追加は旧イメージの稼働中に行っても影響しない)。

  1. ECRへ再ログインする(EC2上、SSM接続後)

user_data.sh.tplが実行するdocker loginEC2の起動時1回だけで、ECRの認証トークンは12時間で失効する。起動から時間が経った環境では、再ログインなしにdocker compose pullするとno basic auth credentialsで失敗する。

$ aws ecr get-login-password --region ap-northeast-1 | sudo docker login --username AWS \
--password-stdin 284391259464.dkr.ecr.ap-northeast-1.amazonaws.com
  1. イメージをpullする
$ cd /opt/app
$ sudo docker compose pull backend mock-utm

サービス名を明示すること。 引数なしのdocker compose pullnginx:latestも対象になり、続くup -dで意図しないnginxの更新・再作成を招く(postgresはタグ固定(postgis/postgis:18-3.6)のため実害はないが、あわせて対象外にしておく)。

  1. 起動する
$ sudo docker compose --env-file .env --env-file env/dips-enabled.env up -d
$ sudo docker compose restart nginx
  • 模擬DIPSと接続しない場合は追加のenv-fileを外す(「1.3.」参照)
  • backendとmock-utmは同じ:latestを参照しているため、両サービスが再作成される
  1. 起動を確認する
$ sudo docker compose ps
$ sudo docker compose logs --tail=50 backend

イメージにコミットハッシュのタグを付けず:latestを上書きする運用のため、どのコミットからビルドしたかはイメージからは辿れない。pushのたびにビルド元のコミットハッシュを作業記録(issue・PR等)へ残すこと。「4.」の手順1で<前回デプロイ時のコミット>を特定する際に必要になる(記録が無い場合はECRのpush日時とgit reflogから推定するほかない)。

5. PostgreSQL 18(PostGIS)への移行(2026-09の一時作業)

Section titled “5. PostgreSQL 18(PostGIS)への移行(2026-09の一時作業)”

forDemo環境のpostgresは当初postgres:17-alpineだったが、ローカル開発・統合テストはPR #20でpostgis/postgis:18-3.6(PostgreSQL 18)へ移行済みであり、通知基盤のDDLもPostgreSQL 18組み込みのuuidv7()を要求する(issue #156 事象1)。UTM側との結合も見据え、forDemo環境も同じイメージへ揃える。

compose.yml側の変更(imageとボリュームのマウント先)は対応済みで、環境への反映手順は下記のとおり。PostgreSQL 17と18はデータディレクトリに互換性がないため、postgres_dataボリュームの作り直しが必須で、DBのデータは全て失われる(疎通試験環境のためデータ保全は行わない前提)。

  1. 更新したcompose.ymldb/schema/coordination.sqlをS3(s3://<バケット名>/env/app/配下)へアップロードする(開発者の手元)

  2. coordination環境へSSMで接続し、コンテナを停止する

$ cd /opt/app
$ sudo docker compose down
  1. S3から最新のファイルを配置し直す(user_data.sh.tplが新規構築時に行うのと同じaws s3 cp
$ sudo aws s3 cp "s3://<バケット名>/env/app/compose.yml" /opt/app/compose.yml
$ sudo aws s3 cp "s3://<バケット名>/env/app/db/schema/coordination.sql" /opt/app/db/schema/coordination.sql
  1. ボリュームを削除する(ボリューム名はcomposeのプロジェクト名接頭辞付き。事前にdocker volume lsで確認する)
$ sudo docker volume ls | grep postgres_data
$ sudo docker volume rm app_postgres_data
  1. 起動する(空のボリュームに対してdocker-entrypoint-initdb.d/opt/app/db/schema/coordination.sqlを実行する)
$ sudo docker compose --env-file .env --env-file env/<追加のenv-file> up -d
$ sudo docker compose restart nginx
  • 模擬DIPSと接続する場合は追加のenv-file(env/dips-enabled.env)の指定を忘れないこと(「1.3.」参照)。通知Workerの停止はcompose.yml側の既定値で効くため、指定は不要(「4.」の注記を参照)
  1. 「3.」のuss_default_info初期データを再投入する(模擬DIPSと接続する試験を行う場合のみ)
  • backendイメージのECRへのpush・EC2でのpullは不要。変更対象がS3経由で配布するファイルのみのため
  • この作り直しで、DB名・DBユーザがcoordinationからutmへ変わる。リポジトリの既定(compose.yaml.devcontainer/compose.yaml.env.examplemise.toml)はutmで、forDemo環境だけがcoordinationだったため、ボリューム作り直しのタイミングで揃える(issue #156 別件②)。/opt/app/.envDB_PASSWORDのみを持ち、DB名・ユーザはcompose.ymlの既定値がそのまま実効値になるため、compose.ymlの差し替えだけで反映される。coordinationスキーマの名前は変わらないcoordination.sqlCREATE SCHEMA coordinationを作り完全修飾しているため、DBユーザ名とは独立)
  • マウント先をpostgres_data:/var/lib/postgresqlへ変更している理由: PostgreSQL 18の公式イメージ(postgis/postgis:18-3.6もこれをベースとする)はPGDATA=/var/lib/postgresql/18/dockerで、VOLUME宣言も/var/lib/postgresql。従来の/var/lib/postgresql/dataのままだと名前付きボリュームがPGDATAの外を指し、実データは自動生成される匿名ボリュームに書かれるため、永続化されているつもりで失われる
  • この作り直しではpostgis拡張は作成されない。postgisイメージが持つ/docker-entrypoint-initdb.d/10_postgis.shは、./db/schema/を同じパスへディレクトリごとマウントすることで隠れるため(postgisイメージを./db/schema/のマウントなしで起動するとpostgispostgis_topology等が自動作成されることを実測で確認済み。マウントがこれを隠す)。イメージ側の自動作成には頼らず、DDL側のdb/schema/00_shared_extensions.sqlで明示的にCREATE EXTENSION IF NOT EXISTS postgisする方式をとる(ローカル開発環境も同じ方式)。
    • flight_planningスキーマのgeometry列とPostgisGeometryValidatorがPostGISを要求するため、この環境でも拡張の作成が必須である。00_shared_extensions.sqlは配布物に含まれていないので、「4.」のpsqldef適用(渡すファイルに00_shared_extensions.sqlを含める)で作成すること
    • 本節の手順でボリュームを作り直した場合、配布物にはcoordination.sqlしか無く拡張も作られないため、作り直しの直後に必ず「4.」を実行する

現時点で手動のまま残している事項のうち、user_data.sh.tpl側で削減できる可能性があるものを記載する(本ドキュメント作成時点では未実施の提案)。

  • uss_default_info初期データ・Assetサンプルデータの自動投入: uss_default_info側はdb/migrations/002_seed_uss_default_info.sql-- +migrate Upセクションのみを抽出し、docker-entrypoint-initdb.dが実行するファイル名としてdb/schema/coordination.sqlより後にソートされる名前(例: zz_002_seed_uss_default_info.sql)でS3へ同期・配置すれば、新規構築時に自動投入できる(-- +migrate Downセクションを含めると初期化直後に削除文が実行されてしまうため、抽出処理が必要)。Asset側はdb/seed/local/asset_sample_data.sqlが既にsql-migrateのセクション分けを持たない単純なpsqlスクリプトのため抽出不要で、db/schema/asset.sqlより後にソートされる名前(例: zz_asset_sample_data.sql)でそのままS3へ同期・配置すればよい。sql-migrate自体は継続運用の例外マイグレーション追従用に引き続き必要なため、この対応はあくまで新規構築時の初期状態を投入済みの状態にショートカットする位置づけとなる。
  • 模擬DIPS証明書のSSM経由自動配置: coordination環境のdb_passwordと同様、PKCS12形式のトラストストア・キーストア(およびそのパスワード)をSSM Parameter Store(SecureString)へ登録しておき、user_data.sh.tpl側で/opt/app/certs/dips/へ自動配置すれば、環境再構築のたびの手動配置(1.1節)が不要になる。既存のdb_password取得と同じパターンを踏襲できる。DIPS接続自体の有効/無効切り替え(1.2〜1.3節、env/dips-enabled.envの作成・追加env-file指定での起動)は状況に応じて切り替える運用のため、既定で有効化する自動化は行わず、証明書の事前配置のみを自動化する想定。
  • client環境のDIPSクライアント証明書のSSM経由自動配置: client環境は現状user_dataを持たないが、coordination環境と同一のIAMインスタンスプロファイル(SSM Parameter Store読み取り権限を含む)を再利用しているため、同様の仕組みで/home/ec2-user/certs/dips/への自動配置が技術的には可能。

上記はいずれも新規のSSM Parameter登録(terraform管理)とuser_data.sh.tplの追記が必要なため、対応する場合は別途実施する。