iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
DevOps

AWS ECS + Gitlab + Laravel + Terraform 從入門到摔坑系列 第 20

Day 20 讓 Terraform configuration 與 cloud resource 保持一致

  • 分享至 

  • xImage
  •  

在首爾成功 apply resource 後,我們回到東京 region 對 configuration 做最後的修正。

先把之前移到別的資料夾的 state 檔案 terraform.tfstateterraform.tfstate.backup 搬回來,import 之前漏掉的 resource,然後用 terraform plan 檢查 cloud 上的 resource 跟 terraform configuration 有什麼差異、修正讓兩者一致,這樣才完成了轉移到 terraform。

Import 漏掉的 resource

前兩天我們在首爾的驗證過程中有多建立一些 resource:給 Codepipeline 放 artifact 的 s3 bucket bucket 跟 EventBridge rule,要把它們 import 回來。因為已經寫了 resource block 們,這邊我們一樣用 terraform import 這個老方法~~,順便多感受一下 import block 的好~~

CodePipeline trigger 的 IAM Role & Policy

$ terraform import aws_iam_role.codepipeline_trigger cwe-role-ap-northeast-1-my-app

import 完用 terraform plan 可以檢查 terraform configuration 跟 cloud 實際 resource 的差別:

https://ithelp.ithome.com.tw/upload/images/20230930/20160671e9o1l63pwN.png

把 configuration 改成跟 AWS 上 resource 符合,讓它不會有 changes。

接著發現東京 region 在 codepipeine trigger 的 IAM role 跟 policy resource 跟筆者自己寫的不一樣,筆者是直接用一個 aws_iam_role_policy resource 在 role 裡寫 inline policy,但 web console 是建立一個 IAM policy 再 attach 給 role。這邊我們還是以 cloud 上實際有的 resource 為主,不同的 resource 是不能 import 的,所以把 aws_iam_role_policy.codepipeline_trigger 改成 aws_iam_policy.codepipeline_trigger (要去掉 role attribute)再 import:

$ terraform import aws_iam_policy.codepipeline_trigger arn:aws:iam::xxxxxxxx:policy/service-role/start-pipeline-execution-ap-northeast-1-my-app

import 後一樣 plan 看差異、修正 configuration。

https://ithelp.ithome.com.tw/upload/images/20230930/20160671xRlrqjP2k1.png

改用 aws_iam_policy 就需要多 import aws_iam_role_policy_attachment resource,也就是 IAM role 與 policy 的關聯。我們可以直接寫以下 resource block 然後 import:

resource "aws_iam_role_policy_attachment" "codepipeline_trigger" {
  role       = aws_iam_role.codepipeline_trigger.name
  policy_arn = aws_iam_policy.codepipeline_trigger.arn
}
$ terraform import aws_iam_role_policy_attachment.codepipeline_trigger cwe-role-ap-northeast-1-my-app/arn:aws:iam::xxxxxxxxx:policy/service-role/start-pipeline-execution-ap-northeast-1-my-app

EventBridge 的 rule 跟 target

$ terraform import aws_cloudwatch_event_rule.codepipeline_trigger codepipeline-myapp-latest-447280-rule

https://ithelp.ithome.com.tw/upload/images/20230930/201606718kAZxKbPUp.png

一樣是修改 terraform configuration,讓它跟真實 resource 保持一致,所以這邊我們會把 role_arn attribute 去掉。

event target 需要用到 event_bus_name/rule-name/target-id 來 import,其中 target id 在 web console 上看不到,要用 awscli events list-targets-by-rule 指令查:

$ aws events list-targets-by-rule --rule codepipeline-myapp-latest-447280-rule
{
    "Targets": [
        {
            "Id": "codepipeline-my-app",
            "Arn": "arn:aws:codepipeline:ap-northeast-1:384137370331:my-app",
            "RoleArn": "arn:aws:iam::xxxxxxxxx:role/service-role/cwe-role-ap-northeast-1-my-app"
        }
    ]
}

查到 target id 後 import:

$ terraform import aws_cloudwatch_event_target.codepipeline_trigger default/codepipeline-myapp-latest-447280-rule/codepipeline-my-app

S3 bucket

最後是用來放 codepipeline artifact 的 s3 bucket 也要 import:

$ terraform import aws_s3_bucket.codepipeline_artifact codepipeline-ap-northeast-1-239650800043

修正剩餘 changes

import 完之前漏的 resource 後,我們最後要用 terraform plan 看還有什麼 changes,把 configuration 修正成跟 cloud 上實際 resource 一致。

Day 18 我們把預設的 security group 改成用 resource aws_default_security_group ,所以這邊要先 remove state 再重新 import:

$ terraform state rm aws_security_group.default
$ terraform import aws_default_security_group.default sg-0920ca3925dc173a2

terraform state rm [RESOURCE] 是把這個 resource 從 terraform state 中移除,也就是 terraform 不再管理這個 resource,之後的 plan、apply 跟 destroy 都不會影響這個 resource。所以如果有不想再用 terraform 管理的 resource,把 resource 從 terraform state 中移除即可。這裡我們是要把原本以 resource aws_security_group 管理的 default security group 改成用 aws_default_security_group 來管理,所以先移除舊的 state,再用 import 把同一個 security group 跟 aws_default_security_group resource 對應起來。

Terraform state 操作

既然 terraform importterraform state rm 都說到了,順便介紹另外幾個跟 state 有關的操作指令:

  • terraform state list :列出所有 terraform 管理的 resource

  • terraform state show [RESOURCE] :顯示某個 resource 的詳細資料

  • terraform state mv [SOURCE] [DESTINATION] :rename resource

terraform state listterraform state show 很好理解,是列出清單跟顯示詳細資訊。

terraform state mv 會用在我們想幫 resource 改名稱的時候,例如想把 aws_security_group.allow_http_from_internet 的名稱改成 allow_web_from_internet ,我們要做兩件事:

  • 修改 configuration 中 resource block 的名稱

  • terraform state mv aws_security_group.allow_http_from_internet aws_security_group.allow_web_from_internet 修改 terraform state 中記錄的 resource 名稱

如果 state 沒有跟著修改,plan / apply 會看到 destroy 原本的 resource 然後新增新的 resource,這可能不是我們希望的動作,security group 刪掉重建可能還好,但總不能把 RDS 的資料庫砍掉重建…… 😨

apply 時看到的 ecs service changes 是什麼?

不知道各位在前面 terraform apply 的過程中有沒有注意到有時後會有像下面這樣的 changes:

https://ithelp.ithome.com.tw/upload/images/20230930/20160671LBCl4WiV1B.png

它會在 apply 或 plan 的時候出現,但其實不是我們真的要改動 task definition,雖然直接 apply 無傷大雅但常常出現也是挺惱人的(會需要多一點點判斷才能確認改 terraform 沒有改到實際 resource),我們來說一下這是怎麼回事以及如何讓它不會在不需要出現 changes 的時候一直冒出來。

從 changes 可以看到 ecs service 的 task_definition 從 my-app:5 改成 my-app ,在 resource 裡我們是這樣寫的:

task_definition                    = aws_ecs_task_definition.td.family

所以它想改成 my-app 是正確的,問題在為什麼 resource 上會變成 my-app:5

我們進到 ecs service 的 configuration 可以看到 task 是:

https://ithelp.ithome.com.tw/upload/images/20230930/201606710qGaXDQdKM.png

我們明明沒有改 task definition,怎麼會出現新的 revision 呢?觀察看看不同 revision 的差別,會發現它們 container 的 image 最後面的 sha256 是不同的,像是我的 revision 4 的 sha256 是 sha256:d99e2a0c654ffa0f3ac22e8892ecea879423dd043c879b7f3643ea5c1a3df3ac 、revision 5 則是 sha256:a90959084e3b58a1f815b363d4436677c8dad4bea75cdb8f3d95f5a13fb56005 。再回到 ECR repository,會發現目前 latest tag 的 image 的 digest 正是 sha256:a90959084e3b58a1f815b363d4436677c8dad4bea75cdb8f3d95f5a13fb56005 ,而前一個 tag 的 image digest 是 sha256:d99e2a0c654ffa0f3ac22e8892ecea879423dd043c879b7f3643ea5c1a3df3ac 。我們可以推測在 Gitlab 推新的 image 上 ECR 後,會有新的 task definition revision 產生來讓 ECS service 使用到最新版的 image。要驗證這個推測最簡單的方式當然是讓 Gitlab pipeline 再跑一次囉~

確認 task definition 會在 Gitlab pipeline deploy 時產生新的 revision,這是一個我們想要的行為沒錯,但又不希望 Gitlab 的 deployment 干擾到 terraform 的使用,這種狀況可以利用 terraform 的 data source 來避免一直出現不需要出現的 changes。

官方文件中,data source 讓 terraform 可以使用其他人定義或修改的 resource,Gitlab pipeline deploy 造成的 task definition revision 更新便是屬於「其他人修改 resource」。在我們的例子裡,要加個 data source block:

data "aws_ecs_task_definition" "td" {
  task_definition = aws_ecs_task_definition.td.family
}

並且把 aws_ecs_service.servicetask_definition 改成:

task_definition = "${aws_ecs_task_definition.td.family}:${max("${aws_ecs_task_definition.td.revision}", "${data.aws_ecs_task_definition.td.revision}")}"

data source 會在 refresh 階段(會在 plan 前執行)讀取相關 resource 的值並更新到 state ,在 plan 跟 apply 時便能利用讀取到的值避免出現不必要的 changes,我們也用 max() 來確保 ECS service 是使用最新的 active revision。

從 Gitlab deploy 到 AWS 的流程

https://ithelp.ithome.com.tw/upload/images/20230930/20160671zPDAOZlKup.png

最後我們來看一下整個從 Gitlab deploy 到 AWS ECS 的流程。

Gitlab Pipeline 會將 docker image push 進 ECR repository、更新 latest tag 的 image,這時候 EventBridge 的 rule 被觸發接著 trigger CodePipeline 啟動。CodePipeline 從 ECR repository 拿到 docker image,最後 deploy 到 ECS 的 service。


上一篇
Day 19 完整驗證 & EventBridge
下一篇
Day 21 以 Gitlab 作為 Terraform backend
系列文
AWS ECS + Gitlab + Laravel + Terraform 從入門到摔坑30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言