AWS

AWS CodeDeployを使ってEC2インスタンスのデプロイを行う

はじめに

AWSのサービスのひとつにCodeDeployというサービスがあります。
今回はこのCodeDeployを使う機会があったのでそれについて調べたことをまとめていこうと思います。
CodeDeployではデプロイする元のソースはS3GitHubができますが今回はGitHubで行います。

CodeDeployとは

CodeDeployとは、EC2インスタンスへのデプロイを自動化出来るサービスです。
デプロイをする際にはダウンタイムを最小限におさえるために多くの工程があるかとおもいますが、それを自動化し作業の効率化につながります。加えて、複数のEC2インスタンスで冗長化等している場合にも、一元管理をしてくれて簡単にデプロイをすることができます。
今回はEC2インスタンスへの設定となりますがオンプレミス環境でもCodeDeployを使用することが出来るみたいです。

CodeDeployでできること

細かく上げるとたくさんありますが、僕的に使用する上で大きなメリットなると思ったものをあげると

1, shellスクリプトを実行することが出来る。
単純にgit pull origin master的なことをして終わるだけではなくて、shellスクリプトを実行することが出来るのでキャッシュクリアであったり、再起動も自由に記述することが出来るわけです。なので実際なんでもできてしまいます。

2, ELBで複数台管理されてたとしてもうまくやってくれる
複数台のサーバーで構築されている場合に、デプロイ中は一旦ロードバランサーから切り離して、デプロイ完了したら接続し直して、次のデプロイを始めるというようなことも自動で行ってくれるのですごく便利です。

3, デプロイのトリガーを設定できる
今回は扱いませんが、例えばmasterブランチをプッシュしたときに自動でデプロイが走るようにする設定ができたりもします。

CodeDeployの仕組み

導入の手順を説明する前に、CodeDeployの仕組みを簡単に説明します。

EC2インスタンスにCodeDeployAgentをインストールして起動します。
CodeDeployAgentが何をするかというと、定期的にAWS側にデプロイの命令があるかどうかを問い合わせます。
もしここでデプロイの命令があればデプロイが開始します。

このような流れになっていて、この仕組を理解しておくと導入がスムーズになるので理解しておきたいところです。

導入手順

CodeDeployAgentの導入

まず先程説明したようにEC2インスタンスにCodeDeployAgentをインストールします。
CodeDeployAgentをインストールするためにはEC2内でaws cliを使えるようにする必要があります。


sudo yum update
sudo yum install ruby
sudo yum install aws-cli
cd /home/ec2-user
aws s3 cp s3://aws-codedeploy-ap-northeast-1/latest/install . --region ap-northeast-1
chmod +x ./install
sudo ./install auto

導入がちゃんと出来てるかどうかは以下のコマンドで確認できます。


sudo service codedeploy-agent status

これでエラーが出る場合は導入の何処かでミスが有ると思われます。

コードデプロイエージェント関連で使う主要コマンドは、


sudo service codedeploy-agent status //ステータスの確認
sudo service codedeploy-agent start //起動
sudo service codedeploy-agent restart //再起動
vi /var/log/aws/codedeploy-agent/codedeploy-agent.log //ログの確認

ですね。

appspec.ymlを作成

githubで作成したリポジトリの一番上の階層にappspec.ymlを作成します。
これはデプロイの設定を書くファイルです。


version: 0.0
os: linux
files:
  - source: ./
    destination: /usr/share/nginx/html
hooks:
  BeforeInstall:
    - location: scripts/before.sh
      timeout: 300
      runas: root
  AfterInstall:
    - location: scripts/after.sh
      timeout: 300
      runas: root

このようなかんじで設定します。
いじるべき箇所はdestinationhooksの部分です。
destinationはデプロイを行う階層を設定します。この場合html以下に、普段git cloneしたときに.gitファイルが置かれる階層のファイルが並びます。(CodeDeployした際には.gitファイルは作られません。これについては後述)

hooksはデプロイする際に実行したいシェルスクリプトのファイルを指定することができます。セオリーとしてappspec.ymlと同じ階層にscriptsというフォルダを作成してその中にまとめておきます。
BeforeInstallAfterInstallというのはCodeDeployのライフサイクルのことです。

AWSのページにのっていた図なのですが、ライフサイクル的にはこのようになっています。

左が通常のライフサイクルで、右はロードバランサーをかませてるときのライフサイクルです。ロードバランサーをかませている場合は、ロードバランサーから切り離したりと接続が増えるのでその分多くなっています。

先程の図の黒く塗られてない部分はフックとしてshellを実行することができます。

各ライフサイクルの簡単な説明を書いておきます。

ApplicationStop アプリケーションを正常にストップするための処理を書きます。
DownloadBundle CodeDeployAgentがアプリケーションリビジョンファイルを一時的な場所にコピーします。
BeforeInstall インストールの直前に呼ばれ、現在のバージョンのバックアップの作成などの事前インストールタスクに使用します。
Install CodeDeployAgentがリビジョンファイルをdestinationに指定した場所にコピーします。
AfterInstall インストールの直後に呼ばれ、アプリケーションの設定や、ファイルのアクセス権限の変更などのタスクに使用できます。
ApplicationStart 通常、ApplicationStop中に停止したサービスを再起動する場合に、このデプロイライフサイクルイベントを使用します。
ValidateService デプロイが正常に完了したことを確認するために使用されます。
BeforeBlockTraffic ロードバランサーから登録解除される直前に呼ばれます。
BlockTraffic ロードバランサーから登録解除されます。
AfterBlockTraffic ロードバランサーから登録解除された直後に呼ばれます。
BeforeAllowTraffic ロードバランサーに登録する直前に呼ばれます。
AllowTraffic ロードバランサーに登録します。
AfterAllowTraffic ロードバランサーに登録する直後に呼ばれます。

ここでApplicationStopだけ注意なのですが、新しいリビジョンファイルのダウンロードの前に呼ばれるものなので、このイベントに関しては既存のappspec.ymlの設定に書かれているものが実行されるので注意です。それ以降のイベントに関してはデプロイするバージョンのappspec.ymlの設定が採用されます。

IAMを作成する

EC2に設定する用と、CodeDeployの設定で作成するデプロイグループに設定する用のIAMを作成しないといけません。

・EC2に設定するIAM
AmazonEC2RoleforAWSCodeDeployをアタッチ
信頼関係には以下を設定


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

・デプロイグループに設定するIAM
AWSCodeDeployRoleをアタッチ
信頼関係には以下を設定


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

以上の2つのIAMを作成して、対象のEC2に割り当てましょう。デプロイグループはこの後作成するのでその際に付けます。

IAMで注意

今回のようにCodeDeployAgentの起動のあとにIAMを作成して割り当てた場合にCodeDeployAgentの権限のエラーが起こる場合があります。


vi /var/log/aws/codedeploy-agent/codedeploy-agent.log

でログが確認できますが、その際に


2017-12-13 11:56:56 INFO [codedeploy-agent(2604)]: Version file found in /opt/codedeploy-agent/.version with agent version OFFICIAL_1.0-1.1352_rpm.
2017-12-13 11:56:56 ERROR [codedeploy-agent(2604)]: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Missing credentials - please check if this instance was started with an IAM instance profile

このようなかんじのエラーがはかれていると思います。これはCodeDeployAgentが定期的にデプロイがあるかどうかをAWS側に確認しに行く際にその権限がなくてエラーになっているというログです。

その場合には、


sudo service codedeploy-agent restart

を実行するようにしましょう。
その後ログを確認して先程のエラーが出てなければ大丈夫です。

CodeDeployの設定

ここからついにAWSのCodeDeployのコンソール画面で設定していきます。
CodeDeployの画面に行き「アプリケーションの作成」をクリックします。
アプリケーションごとに設定することができます

このような画面になるので、アプリケーション名を入力します。
コンピューティングプラットフォームは今回EC2インスタンスを使用するのでEC2/オンプレミスを選択します。
デプロイグループ名も設定してください。1つのアプリケーションに対して、デプロイグループを設定することができて、デプロイグループごとにどのインスタンスに対してデプロイするのかなど分けることができます。

デプロイタイプは今回は「インプレースデプロイ」にします。
これは同じインスタンスにデプロイするのか、その都度新しいインスタンスを作成して古いのを削除するのかのやりかたの違いです。

次にデプロイを行うEC2インスタンスを選択します。

対象のEC2がAutoScalingグループとして既に設定されている場合はAutoScalingグループを選択します。

そうでない場合はインスタンスを個別に設定することが出来るので設定しましょう。

ロードバランサーをかませている場合は「ロードバランシングを有効にする」にチェックを入れましょう。

加えてそのロードバランサーを選択します。

 

次にデプロイ設定です。

これは複数台合った場合にどの順番でデプロイするかを設定します。

CodeDeployDefault.AllAtOnce 一回で全部のインスタンスにデプロイしようとします。
CodeDeployDefault.HalfAtATime 半分ずつデプロイします。
CodeDeployDefault.OneAtATime 1つずつデプロイします。

このようになっていて、ロードバランサーを経由している場合はCodeDeployDefault.OneAtATimeで設定しておくといいと思います。

そしてサービスロールARNは先程作成したCodeDeploy用のIAMを選択しましょう。

これで設定は完了になります。

実際にデプロイ

設定が完了したので実際にデプロイをしてみましょう。

CodeDeployの画面に行きデプロイしたいアプリケーションを選択します。
するとこのような画面になるので、デプロイしたいデプロイグループを選択して、新しいリビジョンのデプロイを選択します。

次の画面でデプロイの設定をしていきます。

デプロイするアプリケーションとデプロイグループが正しいことを確認し、今回はリポジトリタイプにGitHubを選択します。

GitHubにする場合には最初にアカウント認証が入ると思うので設定しておきます。
設定し終わったら対象のリポジトリとデプロイするコミットIDを入力します。

コミットIDで指定できるのでどのバージョンでもデプロイ出来ることがわかります。
なぜコミットID指定になっているかというと、CodeDeployでは内部的にgit archiveをしています。git archiveはコミットIDを指定してZip形式で出力するものです。
なのでコミットID指定であるのとデプロイした後に.gitファイルが作成されないのはこのためです。

あとはロールバックの設定をします。とりあえずこんなかんじにしました。

これで完了です。

うまく設定できていればこれで完了になって無事完了になります。

しかし今回は失敗してしまいました。失敗するとこのような画面になります。何か設定がミスってたみたいです。

まずエラーログをみたいので、その場合には「全てのインスタンスの表示」ボタンをクリックします。

このような画面になるので「イベントの表示」をクリックします。
この画面はエラーじゃなくても進行中の場合にどこまで進行しているか確認のために使うことができます。
先程のライフサイクルごとにどこまで進行したかがわかるようになっています。

ログを表示するをクリックします。

このような画面になりログが表示されます。今回はパスの指定が悪かったみたいです。
この画面で表示されるログは、


vi /var/log/aws/codedeploy-agent/codedeploy-agent.log

で表示されるログの直近のものを表示してくれる画面になっています。

修正してみたらうまくいきました。

今後の展望

今回CodeDeployを扱ってみましたが今回の内容は最低限必要な部分をまとめたというかんじでした。他にも細かい機能があるのでそのへんも今後使いこなせるようにしていけたらなと思います。