承前
ほぼ同じタイトルで、以前 やってみた記事 を書きました。同じようなDocker Compose for Amazon ECS を使っていた別のアプリも移し替えたのでそちらのメモを残しておきます。前回はAPIでしたが今回はアプリケーションです。前回とはスタックが少し違います。
- アプリケーション:Flask
- Webサーバ:Nignx
- データストア:PostgreSQL
この記事はその1で振れていなかった
- Aurora
- セキュアな変数
- Pipline
- 環境による切り替え
などについて触れていきたいと思います。前回の記事ではいろいろ準備のことも書いてありますが、今回はそのへんはすっとばしています。
copilot のバージョンだけ記載しておきます。
$ copilot -v
copilot version: v1.33.2
app 作成
app を作成します。
copilot app init hawk-api --domain 'ai-hawk.com'
env 作成
env を作成します。今回は dev と prod を両方いっぺんに作っておきました。VPCも作ってもらいたいので質問にはデフォルトとして進めます。
copilot env init --app hawk-basic --name dev --profile zenk_dev
copilot env deploy --name dev
copilot env init --app hawk-basic --name prod --profile zenk_prd
copilot env deploy --name prod
svc 作成
webapp というディレクトリ以下に flask のアプリケーションがあり Dockerfile がここにあるのでこうします。
copilot svc init --app hawk-basic --name webapp --dockerfile ./webapp/Dockerfile --svc-type "Load Balanced Web Service"
ここで作成されたmanifestは後で書き換えるので後述。
storage 作成
前回の記事では DyanmoDB でしたが、今回はもともと RDS(PostgreSQL) だったので Aurora に乗り換えます(乗り換えなのでデータ移行が必要になるのですがこれは余談なので後述)
-w でワークロード(svnでつけた名前のことです)を指定、 -l は lifecycle で何を削除したときに一緒に削除されてほしいかを記述します。ここではアプリケーションの削除と連動してほしいので workload と指定します。
copilot storage init -n hawk-basic-dbcluster -t Aurora -w webapp -l workload --engine PostgreSQL --initial-db hawk_basic_db
作るとこんな結果が来ます。
const {username, host, dbname, password, port} = JSON.parse(process.env.HAWKBASICDBCLUSTER_SECRET)
このDBへのアクセス情報は HAWKBASICDBCLUSTER_SECRET という名前の環境変数から JSON で取得できるから、それを展開しましょうということが書いてあります。この名前は使いにくいので変更します。
copilot/webapp/addons
というフォルダの下に hawk-basic-dbcluster.yml というファイルが出来ています。
これはCloudFormationのファイルなのですが、ここの
Outputs:
- hawkbasicdbclusterSecret: # injected as HAWKBASICDBCLUSTER_SECRET environment variable by Copilot.
+ databaseSecret: # injected as DATABASE_SECRET environment variable by Copilot.
Description: "The JSON secret that holds the database username and password. Fields are 'host', 'port', 'dbname', 'username', 'password', 'dbClusterIdentifier' and 'engine'"
Value: !Ref hawkbasicdbclusterAuroraSecret
hawkbasicdbclusterSecurityGroup:
Description: "The security group to attach to the workload."
Value: !Ref hawkbasicdbclusterSecurityGroup
という箇所の hawkbasicdbclusterSecret を databaseSecret に変更します(混乱するのでコメント部分も書き換えておきます)
これで、DATABASE_SECRET という環境変数が自動的に作成されるはずです。
さて、さすがにコードの修正が必要になります。
もともと
SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{user}:{password}@{host}/{name}'.format(**{
'user': os.environ['DB_USER'],
'password': os.environ['DB_PASSWORD'],
'host': os.environ['DB_HOST'],
'name': os.environ['DB_NAME']
})
こうなっていたので
db_secret = env.json('DATABASE_SECRET', default=None)
if db_secret is not None:
SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{user}:{password}@{host}:{port}/{name}'.format(**{
'user': db_secret['username'],
'password': db_secret['password'],
'host': db_secret['host'],
'name': db_secret['dbname'],
'port': db_secret['port']
})
else:
SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://{user}:{password}@{host}/{name}'.format(**{
'user': os.environ['DB_USER'],
'password': os.environ['DB_PASSWORD'],
'host': os.environ['DB_HOST'],
'name': os.environ['DB_NAME']
})
こう書き換えました。ローカルで動かすときや別環境に持っていくときは今までの環境変数が使えるように元の記述も残した形での書き換えです。
さて、この CloudFromation Stack の設定ファイルにはもう一工夫必要です。今回は、 もともとRDSで動いていたアプリケーションをCopilotでデプロイできるようにする必要があります 。Copilot では RDS はなく Aurora になりますが、普通に作ってしまうと当然ですがデータが空っぽの DB ができてしまいます。かといってスナップショットから主導で作ってしまうと Copilot のエコシステムから外れてしまいます。
ということで、strage init で作成された addons 内の CloudFormation Stack のマニフェストファイルを書き換えて、初期 Aurora データベースを予め用意したスナップショットから作る必要 があります。ここでは自分のアカウント内のスナップショットがすでに作成されているものとします。
今回RDSが13.13で作成されていたので、Auroraの方を下げました。バージョンが一致していれば特に変える必要は無いかと思いますし、元々のデータベース側で一致させてから移行しても良いと思います。
Mappings:
(中略)
+ hawkbasicdbclusterSnapshotsMap:
+ dev:
+ ARN: 'arn:aws:rds:ap-northeast-1:*******:snapshot:hawk-basic-dev-old-snapshot'
+ prod:
+ ARN: 'arn:aws:rds:ap-northeast-1:*******:snapshot:hawk-basic-prod-old-snapshot'
Resources:
(中略)
hawkbasicdbclusterDBClusterParameterGroup:
Metadata:
'aws:copilot:description': 'A DB parameter group for engine configuration values'
Type: 'AWS::RDS::DBClusterParameterGroup'
Properties:
Description: !Ref 'AWS::StackName'
- Family: 'aurora-postgresql14'
+ Family: 'aurora-postgresql13'
Parameters:
client_encoding: 'UTF8'
hawkbasicdbclusterDBCluster:
Metadata:
'aws:copilot:description': 'The hawkbasicdbcluster Aurora Serverless v2 database cluster'
Type: 'AWS::RDS::DBCluster'
Properties:
MasterUsername:
!Join [ "", [ '{{resolve:secretsmanager:', !Ref hawkbasicdbclusterAuroraSecret, ":SecretString:username}}" ]]
MasterUserPassword:
!Join [ "", [ '{{resolve:secretsmanager:', !Ref hawkbasicdbclusterAuroraSecret, ":SecretString:password}}" ]]
DatabaseName: !Ref hawkbasicdbclusterDBName
Engine: 'aurora-postgresql'
- EngineVersion: '14.4'
+ EngineVersion: '13.13'
DBClusterParameterGroupName: !Ref hawkbasicdbclusterDBClusterParameterGroup
DBSubnetGroupName: !Ref hawkbasicdbclusterDBSubnetGroup
Port: 5432
VpcSecurityGroupIds:
- !Ref hawkbasicdbclusterDBClusterSecurityGroup
ServerlessV2ScalingConfiguration:
# Replace "All" below with "!Ref Env" to set different autoscaling limits per environment.
MinCapacity: !FindInMap [hawkbasicdbclusterEnvScalingConfigurationMap, All, DBMinCapacity]
MaxCapacity: !FindInMap [hawkbasicdbclusterEnvScalingConfigurationMap, All, DBMaxCapacity]
+ SnapshotIdentifier: !FindInMap [hawkbasicdbclusterSnapshotsMap, !Ref Env, ARN]
修正した箇所は
secret 作成
copilot secret init --name hawk_api_token
各 Environment ごとに聞かれるので入力します。
Dev secret value:
Prod secret value:
...Put secret hawk_api_token to environment dev and prod
✔ Successfully put secret hawk_api_token in environment dev as /copilot/hawk-basic/dev/secrets/hawk_api_token.
✔ Successfully put secret hawk_api_token in environment prod as /copilot/hawk-basic/prod/secrets/hawk_api_token.
You can refer to these secrets from your manifest file by editing the `secrets` section.
ほかにもあれば、都度作ります。
svn デプロイ
いくつかエラーがあり回避するのがなかなか大変でした。
Copilotはエラーを見るのが大変です。なおAuroraの作成にかなり時間がかかっているようです。タスクはこのAuroraの作成の後にしか行われないので、タスクのエラーを見たい場合はこのAuroraの作成を待つ必要があります。10分以上かかりました。
ProstgreSQL バージョン問題
もともとのRDSで使っていたPostgreSQLは13.13というバージョンでしたが、Copilotがデフォルトで作成するバージョンが14.3でした。これが違っているせいでデプロイに失敗してました。
dbname 見付からない問題
なんとdbnameがありません……!
{
"dbClusterIdentifier": "hawk-basic-dev-webapp-add-hawkbasicdbclusterdbclus-******",
"password": "******",
"engine": "postgres",
"port": 5432,
"host": "hawk-basic-dev-webapp-add-hawkbasicdbclusterdbclus-******.cluster-cigkhswdpwb7.ap-northeast-1.rds.amazonaws.com",
"username": "postgres"
}
解決しなかったので、dbnameはプログラム上で解決することにしました。
自分でゼロから作らないと取得できないんですね。