CodeDeploy オンプレミスをためしてみました

CodeDeploy オンプレミスをためしてみました。



結論
====

配布元に github を指定したときは、S3 へのアクセスは不要です。

codedeploy-agent が対応している配布元が、S3 か Github.com なので、そのままだと配布元データの場所はインターネット上になりますが、少しエージェントを手直しすれば、オンプレミスのリポジトリからデプロイできました。

サポートするOS
==============

The AWS CodeDeploy agent has been **tested**

-   Amazon Linux 2014.09.1, 2015.03, 2016.03.0, 2016.03.1
-   Ubuntu Server 14.04 LTS
-   Windows Server 2008 R2 and Windows Server 2012 R2
-   Red Hat Enterprise Linux (RHEL) 7.x

今回は、Ubuntu 16.04.1 LTS で実験したため、リストにはありません。

>   **The AWS CodeDeploy agent is available as open source for you to adapt to
>   your needs**. It can be used with other on-premises instance operating
>   systems. For more information, go to the [AWS CodeDeploy Agent](https://github.com/aws/aws-codedeploy-agent) repository in GitHub.

自分で直してね、ということで。

Ubuntu 16.04.1 LTS
==================

Ruby2.0 が動作すればなんとかなりそうです。

rubyパッケージは2.3なので、PPAから ruby2.0
をインストールして直接エージェントをインストールします。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ sudo apt-add-repository ppa:brightbox/ruby-ng
$ sudo apt-get update
$ sudo apt-get install ruby2.0 libruby2.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

エージェントを手動でインストールします。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install
$ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/VERSION
$ cat VERSION
{"rpm":"releases/codedeploy-agent-1.0-1.1037.noarch.rpm","deb":"releases/codedeploy-agent_1.0-1.1037_all.deb","msi":"releases/codedeploy-agent-1.0.1.998.msi"}
$ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/releases/codedeploy-agent_1.0-1.1037_all.deb
$ sudo dpkg -i codedeploy-agent_1.0-1.1037_all.deb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dpkg -L codedeploy-agent するとインストールされたファイル一覧が表示されます。

一覧の中に設定ファイルがありました。

`/etc/codedeploy-agent/conf/codedeployagent.yml`

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
:log_aws_wire: false
:log_dir: '/var/log/aws/codedeploy-agent/'
:pid_dir: '/opt/codedeploy-agent/state/.pid/'
:program_name: codedeploy-agent
:root_dir: '/opt/codedeploy-agent/deployment-root'
:verbose: false
:wait_between_runs: 1
:proxy_uri:
:max_revisions: 5
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

セットアップ
============

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ export AWS_REGION=us-east-1
$ NAME=Ubuntu-16
$ IAM_USER_ARN=arn:aws:iam::XXXXXXXXXXXX:user/XXXXXXXXXX
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


ここで使用する IAM ユーザには、つぎの内容のポリシーをアタッチして作成してあります。


ポリシー > CodeDeply-On-Premises

~~~~~~~
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codedeploy:*",
                "iam:CreateAccessKey",
                "iam:CreateUser",
                "iam:DeleteAccessKey",
                "iam:DeleteUser",
                "iam:DeleteUserPolicy",
                "iam:ListAccessKeys",
                "iam:ListUserPolicies",
                "iam:PutUserPolicy",
                "iam:GetUser",
                "tag:GetTags",
                "tag:GetResources"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::aws-codedeploy-us-east-1/*",
                "arn:aws:s3:::aws-codedeploy-us-east-2/*",
                "arn:aws:s3:::aws-codedeploy-us-west-1/*",
                "arn:aws:s3:::aws-codedeploy-us-west-2/*",
                "arn:aws:s3:::aws-codedeploy-ap-northeast-1/*",
                "arn:aws:s3:::aws-codedeploy-ap-northeast-2/*",
                "arn:aws:s3:::aws-codedeploy-ap-south-1/*",
                "arn:aws:s3:::aws-codedeploy-ap-southeast-1/*",
                "arn:aws:s3:::aws-codedeploy-ap-southeast-2/*",
                "arn:aws:s3:::aws-codedeploy-eu-central-1/*",
                "arn:aws:s3:::aws-codedeploy-eu-west-1/*",
                "arn:aws:s3:::aws-codedeploy-sa-east-1/*"
            ]
        }
    ]
}
~~~~~~~

オンプレミスインスタンスの登録

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ aws deploy register-on-premises-instance --instance-name $NAME --iam-user-arn $IAM_USER_ARN --region $AWS_REGION
$ aws deploy add-tags-to-on-premises-instances
--instance-name $NAME --tags Key=Name,Value=CodeDeploy-OnPremises
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

あるいは、一行でまとめて実行できます。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ aws deploy register --instance-name $NAME --iam-user-arn $IAM_USER_ARN --region $AWS_REGION --tags Key=Name,Value=CodeDeploy-OnPremises
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

エージェントのインストール

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ sudo aws deploy install --override-config --config-file codedeploy.onpremises.yml
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`codedeploy.onpremises.yml`:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
iam_user_arn: arn:aws:iam::XXXXXXXXXXXX:user/XXXXXXXXXX
region: us-east-1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

デプロイのセットアップ
======================

アプリケーションを作成します。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
aws deploy create-application --application-name MyApp --region $AWS_REGION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ロールを作成します。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
aws iam create-role --role-name CodeDeploy-OnPremises --assume-role-policy-document file://CodeDeploy-Trust.json
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`CodeDeploy-Trust.json`:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": [ "codedeploy.us-east-1.amazonaws.com" ] }, "Action":
"sts:AssumeRole" } ] }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ SERVICE_ROLE_ARN=arn:aws:iam::XXXXXXXXXXXX:role/CodeDeploy-OnPremises
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

オンプレミスインスタンスが使用するIAMユーザにロールをアタッチします。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
aws iam attach-role-policy --role-name CodeDeployServiceRole --policy-arn $SERVICE_ROLE_ARN
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

デプロイメントグループを作成します。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
aws deploy create-deployment-group \
--region $AWS_REGION --application-name MyApp \
--deployment-group-name MyDepGroup \
--deployment-config-name CodeDeployDefault.OneAtATime \
--on-premises-instance-tag-filters Key=Name,Value=Ubuntu-16,Type=KEY_AND_VALUE \
--service-role-arn $SERVICE_ROLE_ARN
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

デプロイ
========

デプロイしてみます。

`appspec.yml`:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
version: 0.0
os: linux
files:
  - source: subtree
    destination: /opt/codedeploy-on-premises
  - source: README.md
    destination: /opt/codedeploy-on-premises
  - source: LICENSE
    destination: /opt/codedeploy-on-premises
hooks:
  ApplicationStop:
    - location: scripts/application-stop.sh
      timeout: 300
      runas: root
  BeforeInstall:
    - location: scripts/before-install.sh
      timeout: 300
      runas: root
  AfterInstall:
    - location: scripts/after-install.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: scripts/application-start.sh
      timeout: 300
      runas: root
  ValidateService:
    - location: scripts/validate-service.sh
      timeout: 300
      runas: root
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ aws deploy create-deployment --region $AWS_REGION --application-name MyApp --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name MyDepGroup --github-location repository=xxxxx/yyyyyy,commitId=zzzzzzzz
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

クレデンシャルについて
======================

-   EC2インスタンスの場合
    インスタンスプロファイルで設定するので不要です。

-   オンプレミス
    `codedeploy.onpremises.yml`で指定します。
    登録ごとに1対1でIAMユーザが必要です。

-   Github.com へのアクセス
    マネージメントコンソールで 「Githubに接続」を実行しておけば、デプロイ時にOAuthのアクセストークンがエージェントに渡ってくるようです。
    ログではマスクされて、 "GitHubAccessToken"=\>"REDACTED" になります。

エージェントの動作
==================

デプロイするファイルはここに展開されます。

`/opt/codedeploy-agent/deployment-root/${DEPLOYMENT_GROUP_ID}/${DEPLOYMENT_ID}/deployment-archive`


`max_revisions` で指定した世代分保存されています。

ポーリング対象のURL。ここからコマンドを受け取っているようです。

`vendor/gems/codedeploy-commands-1.0.0/lib/aws/plugins/deploy_control_endpoint.rb`:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
url = "https://codedeploy-commands.#{cfg.region}.amazonaws.com"
poll_host_command(host_identifier: IAM_USER_ARN)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`codedeployagent.yml` に `proxy_uri`
の項目があるので、プロキシサーバ経由でも使えるようです。(未確認)

ログファイルの場所

`/var/log/aws/codedeploy-agent/codedeploy-agent.log`
`/opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log`

フック
======

* スクリプトの実行で、終了ステータス 0 のとき成功、それ以外の時は失敗です。

* ApplicationStop だけ実行タイミングが異なります。

* ApplicationStopで指定されたスクリプトは、今回のデプロイではなく、後のデプロイの時に、このデプロイしたアプリケーションを停止するためにに使用されます。

Install
=======

`appspec.yml` に書かれたインストール対象のファイルは、Install のステップで存在していれば良いようです。

`files:` に記載しておいて、BeforeInstall スクリプトで準備することもできそうです。

たとえばこのように。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DEPLOYMENT_ROOT=/opt/codedeploy-agent/deployment-root
DEPLOYMENT_DIR=$DEPLOYMENT_ROOT/${DEPLOYMENT_GROUP_ID}/${DEPLOYMENT_ID}
[ -d $DEPLOYMENT_DIR/subtree ] || mkdir $DEPLOYMENT_DIR/subtree
cd /path/to/workset
git pull
rsync -a --exclude .git . $DEPLOYMENT_DIR/subtree/.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

スクリプト中で利用できる環境変数
================================

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
APPLICATION_NAME=デプロイアプリケーション名
DEPLOYMENT_GROUP_NAME=デプロイグループ名
DEPLOYMENT_GROUP_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
DEPLOYMENT_ID=d-XXXXXXXXX
LIFECYCLE_EVENT=ステージ名
AWS_HOST_IDENTIFIER=codedeploy.onpremises.ymlのiam_user_arn
AWS_ACCESS_KEY=codedeploy.onpremises.ymlのaws_access_key_id
AWS_SECRET_KEY=codedeploy.onpremises.ymlのaws_secret_access_key
AWS_REGION=リージョン名
PWD=/opt/codedeploy-agent
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ステージ名: (ApplicationStop|BeforeInstall|AfterInstall|ApplicationStart|ValidateService)


手元のリポジトリからデプロイしたいとき
======================================

gitbucketをセットアップして実験しました。

エージェントの `lib/instance_agent/plugins/codedeploy/command_executor.rb`
の中に`download_from_s3()`や`download_from_github()`があります。

ここのURLを書き換えて、ダウンロードしてくるアーカイブのタイプも変更しました。


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        private
        def download_from_github(deployment_spec, account, repo, commit,
anonymous, token)
          retries = 0
          errors = []
          if InstanceAgent::Platform.util.supported_oses.include? 'windows'
            deployment_spec.bundle_type = 'zip'
            format = 'zipball'
          else
            #deployment_spec.bundle_type = 'tar'
            #format = 'tarball'
            deployment_spec.bundle_type = 'zip'
            format = 'zip'
          end
          #uri = URI.parse("https://api.github.com/repos/#{account}/#{repo}/#{format}/#{commit}")
          #options = {:ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER, :redirect => true, :ssl_ca_cert => ENV['AWS_SSL_CA_DIRECTORY']}
          uri = URI.parse("https://gitbucket.repository.local/#{account}/#{repo}/archive/#{commit}.#{format}")
          options = {:ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE, :redirect => true, :ssl_ca_cert => ENV['AWS_SSL_CA_DIRECTORY']}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

* ダウンロードはHTTP/HTTPSのみ対応可能です。

* 渡せるパラメータが限られるため、自由度はありません。

* 認証が必要な場合は、別途 BeforeInstall スクリプトで取得する方がよさそうです。



気になる点
==========

Ruby 2.0.0系の公式サポートは2016年2月24日に終了しています。

> Ruby 2.0.0系に関しては今後バグ修正や脆弱性修正のパッチがリリースされることはありません。

Ruby 2.3 に対応するのはいつになるのでしょう。