一條龍佈署 CI/CD 從 Travis 到 CodeDeploy 的 On-Premises (IAM Role/STS)

2017-03-09 AWS

之前寫過 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 官方有提供幾種選擇

  • 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

CodeDeploy: deployment-config

給 Mr. 沙先生一點建議

彙整

分類

展開全部 | 收合全部

License

訂閱 Mr. 沙先生 的文章

輸入你的 email 用於訂閱