kevin wang's blog

AWS 上的 Provision 實作

May 08, 2021

上一篇提到了 Provision 的概念,
這一篇會再提到 AWS 上實作 Provision 的一些細節與思路。

定義初始化腳本

在 Provision 的時候,
機器需要做一系列的指令來完成準備。
通常會透過開機腳本來達成。

開機腳本

在 Launch Templete 中有 User Data 的設定,
如果機器是指定依照 Launch Templete 開機,
就可以使用 User Data 內的指令達成執行開機腳本的目的。
我們可以將開機腳本放在 S3 上,
在機器執行 User Data 內的指令時,
從 S3 取得腳本執行指令。
可以參考如下的實作

#!/bin/bash -xe
aws s3 cp s3://{{s3 bucket name}}/main.sh main.sh --region {{region}}
bash main.sh

共用 main.sh

不同的服務可能會有不同的部屬方式。
但大致上都會有一些相同的流程,
比方像是安裝服務,
發送服務完成部屬的訊息等。
大概會像是這樣

安裝共用服務(e.g: filebeat) ...

安裝線上服務 ...

測試服務是否安裝完成 ...

發送服務準備結果通知  ...

以上面的例子,
服務準備相關的動作可能會不同,
但安裝共用服務就會是相同的。
我們可以將 script 拆成 main 以及 install 兩個 Script,
install 實作各自服務的安裝,
並在 main 內執行各自的 install,
完成不同的部屬動作。

從共用 main 中取得不同的 install script

要在共用的 main 中取得不同服務的 install script 的方向是

  • 在 EC2 Tag 標示該服務的相關資訊

    • 可以在 Launch templete 或是 AutoScaling Group 設定是否要 Tag new instance
  • 在 main 中取得 Tag
  • 取得服務對應的 install script

AWS 的文件有提到,
在 AWS 上的機器內可以透過

curl http://169.254.169.254/latest/dynamic/instance-identity/document

取得類似以下的回傳

{
  "accountId" : "zzz",
  "architecture" : "x86_64",
  "availabilityZone" : "ap-northeast-1d",
  "billingProducts" : null,
  "devpayProductCodes" : null,
  "marketplaceProductCodes" : null,
  "imageId" : "ami-xxx",
  "instanceId" : "i-xxx",
  "instanceType" : "t3a.medium",
  "kernelId" : null,
  "pendingTime" : "2021-01-06T12:31:42Z",
  "privateIp" : "172.31.xx.xx",
  "ramdiskId" : null,
  "region" : "ap-northeast-1",
  "version" : "2017-09-30"
}

取得 Instance-Id 後,
再透過 AWS Cli 取得機器上的 Tag 資訊

aws ec2 describe-tags --filter "Name=resource-id,Values=${INSTANCE_ID}" --region ${REGION}
{
    "Tags": [
        {
            "ResourceType": "instance",
            "ResourceId": "i-xxx",
            "Value": "xxx",
            "Key": "ServiceName"
        },        
    ]
}

最後取得需要的 Tag 資訊,
main 裡面的實作可以參考以下

...

REGION=`curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq .region -r`
INSTANCE_ID=`curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq .instanceId -r`
TAGS="$(aws ec2 describe-tags --filter "Name=resource-id,Values=${INSTANCE_ID}" --region ${REGION})"
ServiceName=`echo $TAGS | jq -r '.Tags[] | select(.Key == "ServiceName").Value'`

aws s3 cp s3://{{s3 bucket name}}/$ServiceName/install.sh install.sh --region $REGION
bash install.sh

....

自動擴展

這樣的 Provision 方式,
讓服務部屬的準備可以完全自動化。
也可以讓自動擴展的設計上,
直接依賴 Provision 機制自動橫向擴展。
根據現有團隊的測試,
一台 T3.Medium,
在 Windows Server 的服務可以在 15 分鐘左右完成 Provision,
在 Linux Server 的服務可以在 7 分鐘左右完成 Provision。