之前寫過 CodeDeploy 在 EC2 的應用,然後終於可以來寫 CodeDeploy 在 On-Premises 也就是在 非EC2 的主機佈署,
On-Premises 與 EC2 不同的地方在於 AWS 不認得你的 On-Premises Server,AWS 不可能知道你家的 Server 在哪裡吧 !!,基於 AWS 雲的概念是 IP 不是一個可以作為依據的資訊,所以也不會用 IP 來鎖 (用企業的方式思考,一家公司只有一個 Public IP 怎麼佈署萬台 Server ?)
由於之前沒有涉獵 AWS 太深,這次重新深入了解 AWS 後踩了非常多的坑 .. 但也了解 AWS 的強大
對於非 AWS 的第三方驗證可以採用 Security Token Service (STS) 來取得暫時授權,通常 STS 會和 IAM Role 並用,而 CodeDeploy On-Premises 這篇我採用的就是 AssumeRole 這個 API
要用 CodeDeploy 的 On-Premises 官方有提供幾種選擇
- Use an IAM user ARN to authenticate requests.
- Use the register command for the most automated registration process. Best for registering a single on-premises instance. For information, see Use the register Command (IAM User ARN) to Register an On-Premises Instance.
- Use the register-on-premises-instance command to manually configure most registration options. Suitable for registering a small number of on-premises instances. For information, see Use the register-on-premises-instance Command (IAM User ARN) to Register an On-Premises Instance.
- Use an IAM role ARN to authenticate requests.
- Use the register-on-premises-instance command and periodically refreshed temporary credentials generated with the AWS Security Token Service (AWS STS) to manually configure most registration options. Best for registering a large number of on-premises instances. For information, see Use the register-on-premises-instance Command (IAM Session ARN) to Register an On-Premises Instance.
- register 是使用 IAM User 來認證每一個 On-Premises,我認為這並不適合大量佈署,會產生非常多的 IAM User 在你的 Account
- register-on-premises-instance(IAM User) 你的 CodeDeploy Agent 是使用 IAM User 權限去跑,好處是你可以將每一個 On-Premises Server 都使用不同的 Key,避免一台掉 Key 就全部都掉。。
- register-on-premises-instance(IAM Role) 用 IAM Role 適合大量佈署,透過 IAM User(集中控管) 去產生 sts token,你的 CodeDeploy agent 只會拿到 AssumeRole 的權限,而且一個 token 只有 1 hour,你必須一直更換,產生 token 的動作可以集中用 Puppet 管理或是在本機跑 IAM User 用 cronjob 去重新產生 token
在這篇我選擇用 register-on-premises-instance(IAM Role) 來建立 CodeDeploy On-Premises。
關係架構圖
如果你的 On-Premises 環境沒有辦法直接對外訪問 or 必須使用 Proxy,那你必須先確認以下環境資訊。
環境
- Region(東京) = ap-northeast-1
- 產品名稱 = project
在進行 CodeDeploy On-Premises 之前必須先確認你的網路環境允許連上以下 AWS 服務
- S3 (每一個 S3 bucket 都是全球唯一的位址)
- ${bucketname}.s3.${region}.amazonaws.com
- CodeDeploy Server
- codedeploy.${region}.amazonaws.com
- AWSCLI 安裝
- s3.amazonaws.com
- CodeDeploy Agent 安裝
- aws-codedeploy-us-east-1.s3.amazonaws.com (非 EC2 的 fail-safe mode 安裝位置)
- aws-codedeploy-${region}.s3.amazonaws.com
- codedeploy-commands.${region}.amazonaws.com
- STS
- sts.amazonaws.com
- sts.${region}.amazonaws.com
必要條件
用來建立以下服務的 IAM User 必須擁有以下權限
Attach
AWSCodeDeployFullAccess
lnline
iam:CreateAccessKey,
iam:CreateUser,
iam:DeleteAccessKey,
iam:DeleteUser,
iam:DeleteUserPolicy,
iam:ListAccessKeys,
iam:ListUserPolicies,
iam:PutUserPolicy,
iam:GetUser
此安裝將會建立以下 AWS 服務
- 一個 S3 bucket
- 一個 IAM User 給 Travis CI 使用 CodeDeploy, S3
- 一個 IAM User 給 On-Premises 產生 sts 使用,必須擁有 sts:AssumeRole
- 一個 IAM Role 給 On-Premises 使用 CodeDeploy, S3 服務使用,必須 Principal 給 AWS IAM root
- 一個 IAM Role 給 CodeDeploy 服務使用
- 一個 CodeDeploy Application 與 deployment_group
- 在 CodeDeploy 註冊 On-Premises (register-on-premises-instance)
全程將使用 AWS command line 建立,將不會有 Web cosole 的執行過程
開始建立 CodeDeploy On-Premises (IAM Role/STS)
或者你可以用 shazi7804/codedeploy-permissions-onpremises 進行自動佈署。
Step 1: 建立 S3 bucket
$ aws s3api create-bucket --bucket codedeploy-project --region ap-northeast-1
確認可以訪問
$ aws s3 ls project
Step 2: 建立 IAM User for Travis CI
建立 IAM User
$ aws iam create-user --user-name travisci-project
Attach Policy – AWSCodeDeployDeployerAccess
$ aws attach-user-policy --user-name travisci-project --policy-arn arn:aws:iam::aws:policy/AWSCodeDeployDeployerAccess
lnline Policy – 授與 bucket name 為 codedeploy-project 有權限
$ tee travisci-project-lnline.json <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1487528506000", "Effect": "Allow", "Action": [ "s3:*" ], "Resource": [ "arn:aws:s3:::codedeploy-project/*" ] } ] } EOF $ aws iam put-user-policy --user-name travisci-project --policy-name travisci-project-lnline.json $ rm travisci-project-lnline.json
Step 3: 建立 IAM User for On-Premises
給 On-Premises 的 IAM User 可以共用,所以一個 AWS account 僅需要一個。
$ aws iam create-user --user-name codedeploy-onpremises
lnline sts-AssumeRole
$ tee codedeploy-onpremises-lnline.json <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "sts:AssumeRole" ], "Resource": "arn:aws:iam::012345678:role/Role-onpremises" } ] } EOF $ aws iam put-user-policy --user-name codedeploy-onpremises --policy-name role-codedeploy-onpremises --policy-document file://codedeploy-onpremises-lnline.json $ rm codedeploy-onpremises-lnline.json
Step 4: 建立 IAM Role for On-Premises
給 On-Premises 的 IAM Role 可以共用,所以一個 AWS account 僅需要一個。
trust policy 給 AWS IAM root
$ tee role-onpremises-trust.json <<EOF { "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::0123456789:root"}, "Action": "sts:AssumeRole" } } EOF $ aws iam create-role --role-name Role-onpremises --assume-role-policy-document file://role-onpremises-trust.json $ rm role-onpremises-trust.json
給予 S3 權限,並且 bucket name 一樣鎖在 codedeploy-*
$ tee role-onpremises-policy.json <<EOF { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:GetObjectVersion", "s3:ListBucket" ], "Effect": "Allow", "Resource": "arn:aws:s3:::codedeploy-*" } ] } EOF $ aws iam put-role-policy --role-name Role-onpremises --policy-name role-onpremises-policy --policy-document file://role-onpremises-policy.json
Create Role Profile
$ aws iam create-instance-profile --instance-profile-name Role-onpremises-profile
把 Role 和 Profile 關聯起來
$ aws iam add-role-to-instance-profile --role-name Role-onpremises --instance-profile-name Role-onpremises-profile
Step 5: 建立 IAM Role for CodeDeploy
給 CodeDeploy Server 的 IAM Role 一個 AWS account 僅需要一個。
$ tee role-codedeploy-trust.json <<EOF { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "codedeploy.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOF $ aws iam create-role --role-name Role-CodeDeploy --assume-role-policy-document file://role-codedeploy-trust.json $ rm role-codedeploy-trust.json
Attach policy
$ aws iam attach-role-policy --role-name Role-CodeDeploy --policy-arn arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole
Create Profile
$ aws iam create-instance-profile --instance-profile-name Role_CodeDeploy-Profile
把 Role 和 Profile 關聯起來
$ aws iam add-role-to-instance-profile --role-name Role-CodeDeploy --instance-profile-name Role_CodeDeploy-Profile
Step 6: 建立 CodeDeploy
Create application
$ aws deploy create-application --application-name project --region ap-northeast-1
Create deployment_group (prod)
$ aws deploy create-deployment-group \ --application-name project \ --deployment-config-name CodeDeployDefault.AllAtOnce \ --deployment-group-name prod \ --service-role-arn arn:aws:iam::0123456789:role/Role-CodeDeploy \ --region ap-northeast-1
Step 7: 註冊 CodeDeploy On-Premises
利用 Step4 建立的 IAM Role 去產生 sts session
$ aws sts assume-role --role-arn arn:aws:iam::0123456789:role/Role-onpremises --role-session-name project-session-server01
將產生的 sts session 去註冊 On-Premises instance
$ aws deploy register-on-premises-instance --instance-name project-server01 --iam-session-arn arn:aws:sts::0123456789:assumed-role/Role-onpremises/project-session-server01
替註冊的 On-Premises instance 加入 tags:Key=project,Values=project-server01
$ aws deploy add-tags-to-on-premises-instances --instance-names project-server01 --tags Key=project,Values=project-server01
在 On-Premises Server 安裝 CodeDeploy
以下步驟皆在 On-Premises Server 下進行操作 或者你可以用 shazi7804/codedeploy-session-helper 進行自動佈署。
Step 1: 設定 Proxy
如果你的 On-Premises 不允許直接訪問外部網路,需要另外設定 Proxy,因為安裝 CodeDeploy 必須對外訪問。
建議先把他寫到 /etc/environment,否則在接下來的 sudo 會無法使用 export 環境變數
Step 2: 安裝 aws cli
$ sudo apt-get install -y wget unzip $ cd /tmp $ wget https://s3.amazonaws.com/aws-cli/awscli-bundle.zip $ unzip awscli-bundle.zip $ cd awscli-bundle $ sudo chmod +x install $ sudo ./install -i /usr/local/aws -b /usr/local/bin/aws
確認版本為最新的 aws cli
$ aws --version
在 On-Premises 必須取得 #Step 3: “建立 IAM User for On-Premises” 的 Access Key/ Secret Access Key,並且登記在 On-Premises Server 的 aws cli
$ aws configure
Step 3: 安裝 CodeDeploy agent
$ sudo apt-get install -y ruby $ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install $ sudo chmod +x install $ sudo ./install auto
install 有支援 proxy 參數,可以用 –proxy 解決
驗證服務正在使用
$ ps ax | grep codedeploy codedeploy-agent: master 19248 codedeploy-agent: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller of master 19248
Step 4: 設定檔 codedeploy.onpremises.yml & codedeployagent.yml
建立 CodeDeploy On-Premises 需要使用的設定檔
$ sudo tee /etc/codedeploy-agent/conf/codedeploy.onpremises.yml <<EOF --- iam_session_arn: arn:aws:sts::0123456789:assumed-role/Role-onpremises/project-session-server01 aws_credentials_file: /etc/codedeploy-agent/conf/credentials region: ap-northeast-1 EOF
- iam_session_arn 是在 #Step 6: “建立 CodeDeploy” 中建立的 sts session arn
- aws_credentials_file 是要用來放 sts session 的 token and access key
- region 為你 codedeploy 的所在地區
在 codedeployagent.yml 把 proxy 參數加入,讓 codedeploy agent 可以用 proxy 去更新
:proxy_uri: http://proxy.com:3128
重新啟動 CodeDeploy agent 把 proxy 的設定參數吃進去
$ sudo service codedeploy-agent restart
Step 5: 建立 sts 臨時認證檔 credentials
將 #Step 6: “建立 CodeDeploy” 中建立的 sts 認證資訊寫入 /etc/codedeploy-agent/conf/credentials
$ tee /etc/codedeploy-agent/conf/credentials <<EOF [default] aws_access_key_id = $sts_access_key_id aws_secret_access_key = $sts_secret_access_key aws_session_token = $sts_session_token EOF
在 CodeDeploy 中決定 deploy 的 On-Premises instance tags
CodeDeploy 中的 deployment_group tags 必須和 On-Premises instance tags 符合才會進行 CodeDeploy。
你可以透過 Web cosole 在 CodeDeploy/Application/deployment_group/Search by tags 進行設定 tags,tags type 必須是 On-premises-instance
或是使用 awscli “更新” tags,此設定會直接覆蓋現有 tags,若有多筆 tags 可以用 json 或是多個 –on-premises-instance-tag-filters 來指定 tag
$ aws deploy update-deployment-group \ --application-name project \ --current-deployment-group-name master \ --on-premises-instance-tag-filters Key=Name,Value=project-server01,Type=KEY_AND_VALUE
或是用 json 更新 tags
$ tee multi_tags.json <<EOF [ { "Key": "project", "Value": "server01", "Type": "KEY_AND_VALUE" }, { "Key": "project", "Value": "server02", "Type": "KEY_AND_VALUE" } ] EOF $ aws deploy update-deployment-group \ --application-name project \ --current-deployment-group-name prod \ --on-premises-instance-tag-filters file://multi_tags.json
驗證
正常運作時 access codedeploy 的 log。
$ less /var/log/aws/codedeploy-agent/codedeploy-agent.log 2017-03-06 00:03:13 INFO [codedeploy-agent(19252)]: Version file found in /opt/codedeploy-agent/.version. 2017-03-06 00:04:14 INFO [codedeploy-agent(19252)]: [Aws::CodeDeployCommand::Client 200 61.019261 0 retries] poll_host_command(host_identifier:"arn:aws:sts::0123456789:assumed-role/Role-onpremises/project-session-server01")
收到 codedeploy 的 access log 狀態為 200
[codedeploy-agent(...)]: [Aws::CodeDeployCommand::Client 200 ....
Note
- 已 register 的 On-Premises,如果要 deregister 必須等待 24H 後才會消除,一樣的 sts session name / instance name 無法重複申請
- 必須注意 sts session 的臨時憑證預設時效僅有 1 小時,超過時間即過期,所以可以透過 codedeploy-session-helper 中的 sts-renew 來產生 sts session。
- 在 On-Premises 中產生的 credentials 是可以動態修改,不需要重啟 codedeploy-agent
- CodeDeploy 的 On-Premises 方式不支援 Auto Scaling。
- CodeDeploy 的 deploy tags 方式不支援 * 等字符,必須完整輸入 tag name。
Reference