昨天我們在 region ap-northeast-2
用 terraform 建立起一套 infrastructure,現在要從 Gitlab deploy Laravel web application 上去確認 web 跟 deployment 都能正常運作。(本日程式碼)
換了 region,ECR repository 的 URI 會不同,我們要修改 Gitlab CI/CD variable 的 ECR_BASE
為 xxxxxxxx.dkr.ecr.ap-northeast-2.amazonaws.com
。
還有 .gitlab-ci.yml
的這行:
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $ECR_BASE
把寫死的 ap-northeast-1
改成 $AWS_DEFAULT_REGION
,然後在 CI/CD variable 設定變數值為 ap-northeast-2
。
記得在 push docker image 上 ECR 時有設定一組 AWS credential 來 push image 到 ECR repository 嗎?我們要找到那個 IAM user(可以用 AWS key id 直接在 IAM user 介面搜尋),幫他的 policy 加上可以 push image 到 ap-northeast-2
的 ECR repo,這部份的 policy 會變成像這樣:
...(ignore)
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"
],
"Resource": [
"arn:aws:ecr:ap-northeast-1:[YOUR_ACCOUNT_ID]:repository/my-app",
"arn:aws:ecr:ap-northeast-2:[YOUR_ACCOUNT_ID]:repository/my-app"
]
}
...(ignore)
這些修改完成後就能夠從 Gitlab trigger pipeline 啦~
Gitlab 成功推 image 上 ap-northeast-2
的 ECR repository 後,我們觀察 CodePipeline 的運作:
嗯,毫無反應,就是個不會動的 CodePipeline。
為什麼勒~?
CodePipeline 是什麼時候要開始做事?當然是 ECR repository 有新的 image 的時候~那 CodePipeline 又是怎麼知道要開始做事呢?顯然 ECR repository 跟 CodePipeline 之間有某種魔法機制讓 CodePipeline 知道 ECR repository 有新的 image、可以開始進行 deploy。
這個機制就是 EventBridge(以前叫 CloudWatch Event),它可以建立 event rule,在收到 AWS service 的 event 時去做某些事情。讓我們回到東京 region 偷看(?) EventBridge 的 rules:
web console 自動建立了一個 rule,從描述看起來是在 ECR image tag 改變的時候會去 trigger CodePipeline 開始做事。進去可以看到 event pattern:
{
"source": ["aws.ecr"],
"detail": {
"action-type": ["PUSH"],
"image-tag": ["latest"],
"repository-name": ["my-app"],
"result": ["SUCCESS"]
},
"detail-type": ["ECR Image Action"]
}
這段 event pattern 的意思是 ECR repository my-app
的 image tag latest
被 push 成功時,會觸發這個 event rule 去 trigger 另一個動作。event rule 會 trigger 誰、做什麼動作由 target 設定:
這邊的 target 當然是 CodePipeline 了~
知道機制後,要在 terraform configuration 補上 EventBridge 的 rule。(對辣~就是 import resource 的時候又漏了辣~)
首先是 target 需要的 IAM role 及 policy:
resource "aws_iam_role" "codepipeline_trigger" {
name = "codepipeline-my-app"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "events.amazonaws.com"
}
},
]
})
}
resource "aws_iam_role_policy" "codepipeline_trigger" {
name = "codepipeline-my-app"
role = aws_iam_role.codepipeline_trigger.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"codepipeline:StartPipelineExecution",
]
Resource = aws_codepipeline.pipeline.arn
},
]
})
}
接著是 event rule 跟 target:
resource "aws_cloudwatch_event_rule" "codepipeline_trigger" {
name = var.trigger_name
description = var.trigger_desc
role_arn = aws_iam_role.codepipeline_trigger.arn
event_pattern = jsonencode(
{
detail = {
action-type = ["PUSH"]
image-tag = ["latest"]
repository-name = [var.source_ecr_repo_name]
result = ["SUCCESS"]
}
detail-type = [
"ECR Image Action",
]
source = [
"aws.ecr",
]
}
)
}
resource "aws_cloudwatch_event_target" "codepipeline_trigger" {
rule = aws_cloudwatch_event_rule.codepipeline_trigger.name
arn = var.target_codepipeline_arn
role_arn = aws_iam_role.codepipeline_trigger.arn
}
apply 這些 resource 上去,Gitlab pipeline 再執行一次,發現 CodePipeline 開始動了!🥳
然後它就壞掉了 😐
點進去看它又怎麼了,在 phase details 看到:
看起來是 codebuild 對存放 artifact 的 s3 bucket 缺乏權限,這邊 artifact 的觀念跟 Gitlab pipeline 的一樣,是 pipeline 過程中產生的各種產出物,codebuild project 會把 artifact 放在 s3 某個 bucket。我們在 resource aws_iam_policycode_build_project
幫它補上:
...
{
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::codepipeline-${var.region}-*",
"${aws_s3_bucket.codepipeline_artifact.arn}",
"${aws_s3_bucket.codepipeline_artifact.arn}/*"
]
},
...
接著從 CodePipeline 按右上角的 Release change 讓它重跑,因為這邊跟 Gitlab Pipeline 無關了,我們重跑 CodePipeline 就可以了:
終於成功!
終於來到最後確認——確定 ECS service 正常運作、可以看到 Laravel 歡迎頁。
把 aws_autoscaling_group.asg
的 desired_capacity
、max_size
跟 min_size
都設成 1,還有 aws_ecs_service.service
的 desired_count
也設成 1 然後 apply。
一樣到 ECS service 的 deployment 觀察 service 的 deploy 狀況,service 穩定之後就可以用 ALB 的 DNS name 看看是不是能看到 Laravel 歡迎頁囉!
如果可以看到 Laravel 歡迎頁,表示我們的 terraform 是正確的,可喜可賀可口可樂!
測試完當然要把首爾 region 的 resource 刪掉啦~用 terraform 就非常簡單,只要下 terraform destroy
指令並且 yes
即可!
會出現 error 的常常是 s3 bucket 跟 ECR repository,它們會說 bucket 或 repository 不是空的所以刪不掉,這種時候筆者通常手動把東西砍了再跑一次 destroy。
最後祝大家中秋節快樂~