GitLab + CodePipeline + CodeDeploy で SpringBootApp を自動デプロイ


GitLab + CodePipeline + CodeDeploy で SpringBootApp を自動デプロイ

GitLab は CodePipeline のトリガーに指定することができないので S3 を噛ませて Codeシリーズを起動するサンプル。

  1. 修正内容を GitLab に push
  2. GitLab CI/CD で S3 にプロジェクトをアップロード
  3. CodePipeline で S3 のアップロードを検知
  4. CodeBuild で S3 にアーティファクトを保存
  5. CodeDeploy で EC2 インスタンスにデプロイ

の流れを試してみる。

手順

  1. SpringBoot のプロジェクトを作成
  2. IAMユーザとS3バケットの作成
  3. GitLab CI/CD の設定
  4. デプロイ用EC2インスタンスを作成/設定
  5. CodeBuild の設定
  6. CodeDeploy の設定
  7. CodePipeline の設定

SpringProject に加えて追加するファイルは以下のように配置にする。

.
├─ .gitlab-ci.yml
├─ buildspec.yml
├─ appspec.yml
└─ hooks
  └─ restart.sh

各役割をまとめると・・・

ファイル名 役割
.gitlab-ci.yml GitLabのCI設定ファイル。今回はS3にアップロードする手順を記載する
buildspec.yml CodeBuildでSpringBootをビルドしてJarを生成する手順を記載する
appspec.yml CodeDeployで生成したJarをEC2に配置する手順を記載する
restart.sh CodeDeployでデプロイが完了した後にサービスの再起動などを行うスクリプト

では早速始めよう。

SpringBoot のプロジェクトを作成

Spring Initializr でプロジェクトを作成する。
今回は以下の条件で作成してみた。

key value
Project Gradle Project
Language Java
Spring Boot 2.2.5
Project Metadata - Group com.example
Project Metadata - Artifact cisample
Options - Name cisample
Options - Description cisample project for Spring Boot
Options - Package name com.example.cisample
Options - Packaging Jar
Options - Java 11
Dependencies Spring Web

設定が終わったら Generate で作成/DLして展開しておく。
簡単なテキストを表示するために CisampleApplication.java を変更しておく。

CisampleApplication
 package com.example.cisample; 

 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;

+@RestController
 @SpringBootApplication
 public class CisampleApplication { 

    public static void main(String[] args) {
        SpringApplication.run(CisampleApplication.class, args);
    } 

+   @RequestMapping("/")
+   String home() {
+       return "Hello World ver 1.0";
+   } 

 }

展開できたらコンテナを起動して動作確認をする。
※ 面倒ならスキップしてもOK

# コンテナの起動/接続
docker run --rm -it -p 8080:8080 -v $(pwd):/app openjdk:11 bash

# jarのビルド
cd /app
./gradlew build

# jarの実行
java -jar ./build/libs/cisample-0.0.1-SNAPSHOT.jar --server.port=8080

ブラウザから http://localhost:8080 にアクセスして画面が表示されれば成功。
コンテナは停止してOK
次にS3バケットとIAMユーザを作成する。

S3バケットとIAMユーザの作成

S3にプロジェクトをzip形式で保存するためのS3バケットを作成する。S3はバージョニングを有効にしておくこと。
また、アップロード権限を持ったIAMユーザも作成する。

  1. AWSコンソールにログイン
  2. S3バケットの作成
    1. サービスから S3 を検索
    2. バケットを作成
    3. バケット名を入力 ex) cisample-backet
    4. リージョンを入力 ex) ap-northeast-1
    5. パブリックアクセスをすべてブロックをチェック
    6. バケット作成ボタンでバケットを作成
    7. 作成したバケットを選択
    8. プロパティタブ
    9. バージョニング
    10. バージョニングの有効化を選択
    11. 保存
  3. IAMユーザの作成
    1. サービスから IAM を検索
    2. 左メニューのユーザー
    3. ユーザーを追加ボタン
    4. ユーザー名を入力 ex) cisample-user
    5. プログラムによるアクセスをチェック
    6. 次のステップ
    7. 既存のポリシーを直接アタッチ
    8. AmazonS3FullAccess を検索してチェック
    9. 次のステップ
    10. タグは任意で設定して次のステップ
    11. ユーザーの作成ボタン
    12. アクセスキーIDとシークレットアクセスキーを控えておく

S3バケットとIAMユーザができた。
次に公開用のデプロイ先インスタンスを作成する。

デプロイ用EC2インスタンスを作成/設定

httpアクセスできるネットワークにインスタンスを作成してPublicIPを付与する。
jarが起動できればなんでもいいので、細かい手順はよしなに作成する。
デプロイ時に使うので条件として2つ

  1. デプロイグループの設定で使うのでタグを設定する ex) key:DeployGroup val:cisample
  2. ロールに S3FullAccess をつけておく ※必要に応じてアクセス制限をつけてね

作成したらsshで接続して設定する。

# CodeDeploy用エージェントをインストール & 起動
sudo -i
yum update -y && yum install -y ruby wget
wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install
chmod +x ./install
./install auto
systemctl enable codedeploy-agent
systemctl start codedeploy-agent


# jar(配置予定地)を起動するサービスを作成して権限を付与しておく
mkdir /usr/local/app
cat <<EOS > /usr/local/app/start.sh
#!/bin/sh
java -jar /usr/local/app/cisample-0.0.1-SNAPSHOT.jar --server.port=80
EOS
chmod +x /usr/local/app/start.sh

cat <<EOS > /etc/systemd/system/cisample.service
[Unit]
Description = ci sample daemon

[Service]
ExecStart = /usr/local/app/start.sh
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
EOS

systemctl enable cisample


# 適当な場所に java をインストールする
cd /opt
wget https://download.java.net/java/GA/jdk11/9/GPL/openjdk-11.0.2_linux-x64_bin.tar.gz
tar -zxf openjdk-11.0.2_linux-x64_bin.tar.gz
ln -s /opt/jdk-11.0.2/bin/java /usr/bin/java

# インストールの確認
java -version

とりあえず一旦Javaが動けばOK
次にプロジェクトをアップロードする GitLab CI の設定を追加する。

GitLab CI/CD の設定

pushを検知したら AWS の S3 にプロジェクトの内容をアップロードする処理を追加する。
AWSCLIの認証情報はファイルに書いてしまうと悪い人に見つかってしまうので、GitLab側に変数として値を保持しておいて実行時に参照するように設定しておく。

  1. GitLabにログイン
  2. テスト用のリポジトリを作成
  3. さっきのプロジェクトをpushしておく
  4. プロジェクトのページに移動
  5. 左メニューの 設定 > CI/CD
  6. Variables メニューを展開
  7. 以下の値を設定して変数を保存する
type key val
Variable aws_access_key_id IAMユーザのアクセスキー
Variable aws_secret_access_key IAMユーザのシークレットアクセスキー
Variable backet_name 作成したバケット名

GitLab CIの設定ファイルを追記する。

.gitlab-ci.yml
image: "alpine:3"

stages:
  - build
  - push

build:
  stage: build
  script:
    - apk --no-cache add zip
    - zip -qr src.zip *
    - zip -u src.zip .gitlab-ci.yml
  artifacts:
    paths: 
      - src.zip

deploy:
  stage: push
  script:
    - AWS_CLI_VERSION="1.18.31"
    - |-
      apk --no-cache add \
        python \
        py-pip \
        mailcap
    - pip install --upgrade awscli==$AWS_CLI_VERSION python-magic
    - mkdir ~/.aws
    - |-
      cat << EOS > ~/.aws/config
      [default]
      region = ap-northeast-1
      output = json
      EOS
    - |-
      cat << EOS > ~/.aws/credentials
      [default]
      aws_access_key_id = ${aws_access_key_id}
      aws_secret_access_key = ${aws_secret_access_key}
      EOS
    - aws s3 cp ./src.zip s3://${backet_name}/src.zip

apline で awscli の2系がうまく動かなかったので1系で動かす。
バージョンはこちらで確認する。
独自のRunnerを使いたい場合は別途設定をすること。
作成したらGitLabにpushしてパイプラインを眺めましょう。
ステータス: 成功 が確認できたらS3に src.zip があることを確認する。

続いてこの src.zip をビルドするためにCodeBuildの設定に進もう。

CodeBuild の設定

ビルドするコンピューティングリソースなど、ビルドの設定を作成する。

  1. サービスから CodeBuild を検索
  2. ビルドプロジェクトを作成する
  3. プロジェクト名を入力 ex) cisample-build
  4. 送信元をソースプロバイダを Amazon S3 に設定
  5. バケット名を入力 ex) cisample-backet
  6. S3 オブジェクトキーまたは S3 フォルダ を入力 ex) src.zip
  7. 環境イメージにマネージド型イメージを選択
  8. OSに Amazon Linux 2 を選択
  9. ランタイムの Standard を選択
  10. イメージとバージョンは最新を選択
  11. 環境タイプは Linux を選択
  12. サービスロールは新しいサービスロールを選択
  13. ロール名を入力 ex) CiSampleBuildRole
  14. ビルド仕様は buildspec ファイルを使用するを選択
  15. アーティファクトタイプは Amazon S3 を選択
  16. バケット名を入力 ex) cisample-backet
  17. 名前を入力 ex) artifact.zip
  18. アーティファクトのパッケージ化はZipを選択
  19. CloudWatch Logs をチェック
  20. ビルドプロジェクトを作成する

作成したロールに権限を追加する

  1. サービスから IAM を検索
  2. 左メニューのロールを選択
  3. 作成したロールを検索する ex) CiSampleBuildRole
  4. ポリシーをアタッチします
  5. AmazonS3FullAccess をチェックしてポリシーのアタッチ

次にビルド内容の定義ファイルを作成する。

buildspec.yml
version: 0.2

phases:
  build:
    commands:
      - echo start build at `date`
      - ./gradlew build
artifacts:
  type: zip
  files:
    - ./appspec.yml
    - ./hooks/restart.sh
    - ./build/libs/cisample-0.0.1-SNAPSHOT.jar
  name: artifact
  discard-paths: yes

appspec.ymlhooks/restart.sh は後ほど追加するので気にしない。
これでビルド設定ができた。
ビルドの開始でビルドを実行して、ステータス: 成功 なるまで待つ。
ビルドに成功したら S3に artifact.zip があることを確認して、デプロイの設定にはいる。

CodeDeploy の設定

push されたプロジェクトをビルドするまでできたので、ビルド結果をデプロイする部分を作成する。

手順は4段階
1. デプロイ用ロールを作成
2. デプロイ用アプリケーションを作成
3. デプロイ設定の作成
4. デプロイ設定ファイルの追加

まずはデプロイ用ロールを作成する。

  1. サービスから IAM を詮索
  2. ロールを選択
  3. ロールの作成
  4. AWS サービス > CodeDeploy > CodeDeploy を選択して次のステップ
  5. AWSCodeDeployRole が表示されている状態で次のステップ
  6. タグを任意で設定する
  7. ロール名を入力する ex) CisampleDeployRole
  8. ロールの作成

次にデプロイ用アプリケーションを作成する。

  1. サービスから CodeDeploy を検索
  2. アプリケーションの作成
  3. アプリケーションの名前を入力 ex) cisample-app
  4. コンピューティングプラットフォームを選択 ex) EC2/オンプレミス
  5. アプリケーションの作成

続いてデプロイの設定を作成する。

  1. デプロイグループの作成
    1. デプロイグループの作成
    2. デプロイグループ名を入力 ex) cisample-dg
    3. サービスロールのarnを入力 ex) arn:aws:lam::xxxxxxxxxxxx:role/CisampleDeployRole
    4. デプロイタイプを選択 ex) インプレース
    5. 環境設定は Amazon EC2 インスタンス を選択
    6. タグは key: DeployGroup val: cisample を追加
    7. 一意に一致したインスタンス が 1 になっていることを確認する
    8. デプロイ設定は CodeDeployDefault.AllAtOnce を選択
    9. ロードバランシングを有効にする のチェックを外す
    10. デプロイグループの作成
  2. デプロイの作成
    1. デプロイの作成
    2. デプロイグループを入力 ex) cisample-dg
    3. リビジョンタイプは Amazon S3 を選択
    4. リビジョンの場所を入力 ex) s3://cisample-backet/artifact.zip
    5. リビジョンファイルの種類は .zip を選択
    6. デプロイの作成

このままでは設定ファイルがなくてデプロイが失敗してしまうので、設定ファイルを追加する。

appspec.yml
version: 0.0
os: linux
files:
  - source: /
    destination: /usr/local/app
hooks:
  AfterInstall:
    - location: restart.sh
      timeout: 180

新jarのデプロイ後に再起動するスクリプトも追加しておく

hooks/restart.sh
#!/bin/sh

sudo systemctl restart cisample

これでデプロイが通ってEC2インスタンスにビルド結果の artifact.zip の内容が展開される。
サービスも起動しているはずなのでブラウザからEC2インスタンスにアクセスしてHello World ver 1.0が表示されることを確認する。

最後に S3の更新を検知して CodeBuild と CodeDeploy を起動するための CodePipelineを作成する。

CodePipeline の設定

  1. サービスから CodePipeline を検索
  2. パイプラインを作成する
  3. パイプライン名を入力 ex) cisample-pipeline
  4. 新しいサービスロールを選択
  5. ロール名を入力 ex) CiSamplePipelineRole
  6. 高度な設定を展開する
  7. アーティファクトストアは カスタムロケーション を選択
  8. S3バケット名を入力 ex) cisample-backet
  9. 次に
  10. ソースプロバイダーに Amazon S3 を選択
  11. バケット名を入力 ex) cisample-backet
  12. S3 オブジェクトキーを入力 ex) src.zip
  13. 検出オプションを変更する は Amazon CloudWatch Events を選択
  14. 次に
  15. プロバイダーを構築するは AWS CodeBuild を選択
  16. リージョンを選択 ex) アジアパシフィック(東京)
  17. プロジェクト名を入力 ex) cisample-build
  18. 環境変数は今回は未設定で次に
  19. デプロイプロバイダーは AWS CodeDeploy を選択
  20. リージョンを選択 ex) アジアパシフィック(東京)
  21. アプリケーション名を入力 ex) cisample-app
  22. デプロイグループを入力 ex) cisample-dg
  23. 次に
  24. パイプラインを作成する

これでS3の変更を検知して一連の流れが通るはず。
Pipelineが成功したらインスタンスにアクセスして
Hello World ver 1.0 が出ることを確認する。

できた!!

おわりに

今回の手順で以下のリソースができているはず。

  • S3バケット
  • IAMユーザ
  • IAM Role
  • IAM ポリシー
  • EC2 Instance
  • CodeBuild
  • CodeDeploy
  • CodePipeline

不要な場合は削除しちゃいましょ。
画像がないのでそのうちCLI操作のコマンドでも確認してみましょうかね。