AWS CodeDeploy x CodePipeline x EC2 x ALB x SVNで自動デプロイ環境を構築する

# 背景
* 月1度～2度リリースが発生するシステムの構築中
* 手動デプロイは工数がかかる上、危険
* せっかくなので自動デプロイにしたい
* Gitの場合はそのままAWS Codeシリーズと接続できるが、SVNで構築する資料があまりないので誰かの参考になれば……

# 環境
* VCS
    * SVN
        * 弊社はSVNがまだまだマジョリティ……
* ビルダー
    * Gradle
* インフラ
    * EC2
        * マルチAZの2台構成
        * OSはAmazon Linux 2
    * ALB
* Jenkins
* 以下はチャット通知用なので任意用意でOK
    * Microsoft Teams
    * Microsoft PowerAutomate
    * Gmail

# やりたいこと

![20210128_004520_capture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182088/951fe4ad-6d43-d937-bf32-2fbb76e63b03.png)

1. 開発者がSVNリポジトリにコミット
2. JenkinsがSVNをポーリングしてコミットを検知
3. JenkinsがビルドしてS3へ資材をアップロード（検証環境・本番環境）
4. AWS CodePipelineがS3のアップロードをトリガとしてパイプライン実行
5. AWS CodeDeployが検証環境へローリングデプロイ
6. AWS CodePipelineがGmailへ検証環境デプロイ完了・本番環境リリース承認メールを送信
7. Microsoft PowerAutomate(Flow)がメールを検知してMicrosoft Teamsのチャンネルに投稿
8. 開発者がチャンネルへの投稿を見て検証環境でテスト
9. テストして問題なさそうであれば、開発者がAWS CodePipelineの画面より「本番環境リリース」をクリック
10. AWS CodeDeployが本番環境へローリングデプロイ

# この記事を読んでわかること
* 以下のCI環境構築・設定
    * S3
    * CodeDeploy
    * CodePipeline
    * Jenkins(設定のみ)

# この記事で解説しないこと
* AWSアカウントの作成方法
* Gradleでのビルド方法
* Jenkinsサーバの構築方法
* 自動テスト周りの設定

# 前提
* ビルドはあらかじめGradleで作成したスクリプトで行う
* Jenkinsは構築済・ユーザ作成済
    * 未構築の場合はEC2のAWS MarketplaceにBitnamiが出しているJenkinsのAMIがあるのでそれを使うとスムーズに構築できる

# 手順
## S3
### S3バケットの作成
リリース資材を格納するために必要。
AWSマネジメントコンソールのサービス一覧より「S3」で検索
「バケットを作成する」をクリック

### バケット設定
以下を設定してバケットを作成しておく

* バケット名
    * 任意のバケット名（今回はデプロイ先のURLと似た名称にしました）
    * バージョニング
        * 「オン」
            * オンにすると同じファイル名でリリース資材を格納しても自動的に連携してくれるため

## CodeDeploy
### エージェントのインストール
対象インスタンスにSSHし、以下のコマンドを実行してエージェントをインストールしておく
参考：CodeDeploy エージェントのインストールまたは再インストール - AWS CodeDeploy https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install.html

```
$ sudo yum install -y ruby
$ mkdir [作業用ディレクトリ] 
$ cd [作業用ディレクトリ] 
$ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install
$ chmod +x ./install
$ sudo ./install auto
```

以下のコマンドで起動しているか確認する

```
$ sudo service codedeploy-agent status
```

### appspec.ymlの作成
プロジェクトのルートに「appspec.yml」を作成する
内容は以下のようにした

```
version: 0.0
os: linux
files:
    # 展開するファイル
  - source: sample.war
    # 展開する場所
    destination: /opt/apache-tomcat/webapps
```

### サービスロールの作成
#### ロールの作成
以下を参照してロールを作成しておく
ステップ 3: CodeDeploy のサービスロールを作成する - AWS CodeDeploy https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/getting-started-create-service-role.html

#### ロールのアタッチ
AWSマネジメントコンソールより、対象のインスタンスを右クリック→「インスタンスの設定」→「IAM ロールの割り当て/置換」
作成したIAMロールをアタッチし、インスタンスを再起動する

### IAMインスタンスプロファイルの作成
以下を参照してロールを作成しておく
ステップ 4: Amazon EC2 インスタンス用の IAM インスタンスプロファイルを作成する - AWS CodeDeploy https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html

### アプリケーションの作成
AWSマネジメントコンソールにログインし「CodeDeploy」で検索

### アプリケーションの作成
* アプリケーション名：任意のアプリケーション名
* コンピューティングプラットフォーム：EC2/オンプレミス
上記を記載して「作成」をクリック

### デプロイグループの作成
#### デプロイグループとは
デプロイグループ = 対象環境にような概念。
今回は検証環境と本番環境で2グループ作成する。
#### 作成
「アプリケーション」画面より「デプロイグループの作成」をクリックし、以下を入力して「デプロイグループの作成」をクリック

* サービスロール
    * 作成したサービスロールを選択
* デプロイタイプ
    * 任意で選択（今回はインプレースデプロイにしました）
* 環境設定
    * 「Amazon EC2 インスタンス」にチェックし、Nameタグで対象インスタンスを指定
    * Auto Scalingは設定していないため割愛
* デプロイ設定
    * 「デプロイ設定の作成」をクリック
        * デプロイ設定名
            * 任意
        * 正常なホストの最小数
            * 2台構成のため、1台生きていればよいので「数値」として「1」
* ロードバランサー
    * 「ロードバランシングを有効にする」にチェック
    * 「ALB・NLB」か「CLB」を選択
        * 今回はALB
        * ターゲットグループを選択
* 詳細 - オプション
    * 一旦デフォルトのままにした
        * （ロールバック機能が気になる）

これを検証環境・本番環境の両方で作成しておく。

## CodePipeline
サービスを「CodePipeline」で検索し、「パイプラインを作成する」をクリック
### パイプラインの設定
* パイプライン名
    * 任意のパイプライン名
* サービスロール
    * 「新しいサービスロール」を選択し、ロール名を記載する
    * 「AWS CodePipeline がサービスロールを作成できるようになるため、この新しいパイプラインでの使用が可能になります。」にチェック
* 高度の設定
    * 一旦デフォルトのままにした

### ソースの設定
* ソースプロバイダ
    * 今回はS3を選択
* バケット
    * 先ほど作成したバケットを選択
* S3オブジェクトキー
    * リリース資材を含んだZIPファイルの名称を入力
    * 今回は「test.zip」とした
* 検出オプションを選択する
    * Amazon CloudWatch Events を選択

### ビルドステージの選択
今回は手動でビルドするためスキップ

### デプロイステージの選択
「ソースの設定」とほぼ同様

* アクションプロバイダー
    * AWS CodeDeploy
* アプリケーション名
    * CodeDeployの設定で作成したアプリケーション名を入力
* S3 オブジェクトキー
    * CodeDeployの設定で作成したデプロイグループを入力（検証）

### レビュー
内容を確認し「パイプラインを作成する」をクリック

### 修正
現状、S3から検証環境への自動デプロイができるように設定されている。
これに「手動テストによる検証環境での確認・承認」と「本番環境への自動デプロイ」を追加する。
「ステージを追加する」より以下を追加する
1. Manual approval（メールでの通知が必要な場合は設定する。今回はGmailへの通知メール受信をトリガとしてMicrosoft TeamsのWebHookを叩くフローをMicrosoft PowerAutomateで別途作成したが省略）
2. 本番環境へのデプロイ（設定内容は上記Pipelineとほぼ同様。本番環境のS3とCodeDeployを呼ぶようにする）

## Jenkins
EC2 AMIにJenkinsがあったためそれをベースに新規構築した。
今回はJenkinsのユーザの作成まで終わっている前提。
### 新規ジョブの作成
以下を入力

* ソースコード管理：Subversion
* Repository URL：チェックアウトするディレクトリのURL
* Credentials：ユーザ認証情報
* チェックアウト方式：svn revertしてからsvn updateを実行
    * revertは必須ではないが気休め程度に

### ビルド・トリガ
SVMをポーリングを選ぶ。
5分に1度ポーリングするように設定する。
※念のためタイムゾーンをJSTにしておく

```
TZ=Asia/Tokyo
H/5 * * * *
```

### ビルド
#### シェルの実行（検証環境ビルド）
ビルドスクリプトを実行し、検証環境をビルドする。
※Gradleの場合は実行権限の付与を行う
※「#WORKSPACE」はJenkinsのワークスペースを指す環境変数

```
chmod +x $WORKSPACE/gradlew
sudo $WORKSPACE/gradlew clean bootWar generateDeployZip -Penv=stg
```

Gradleの引数などはプロジェクトごとに異なるので、上記コマンドは参考程度にお願いします。

#### シェルの実行（本番環境ビルド）
検証環境のビルドとほぼ同様。
変数envが異なるくらい。

```
chmod +x $WORKSPACE/gradlew
sudo $WORKSPACE/gradlew clean bootWar generateDeployZip -Penv=prd
```

#### シェルの実行（S3へアップロード）
検証環境・本番環境の資材をS3コマンドを利用して該当のバケットへアップロードする。

```
sudo s3cmd put $WORKSPACE/deploy/test_stg.zip s3://staging.deploy.test.co.jp
sudo s3cmd put $WORKSPACE/deploy/test_prd.zip s3://production.deploy.test.co.jp
```

### 完成
SVNリポジトリにテストコミットして動くか試してみる。
Jenkins→S3→CodePipeline→CodeDeployと走ればOK。