0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WindowsのEclipseからCodePipelineを起動してEC2にデプロイ&ECS on Fargateにアプリをデプロイする

Last updated at Posted at 2020-03-15

前提条件

まずは「AWS上でサーバレスなコンテナまで動かせた!」なところまでできていれば。
拙筆ながらこの記事あたりをやっていることを前提としている。
あと、↑の記事中のシンプルなSpring Boot アプリをAWS Code シリーズを利用して自動デプロイするハンズオンも、最後まで走らせて、CodePipelineでEC2にデプロイできるところまでやった上でこの記事を書いている。

まずはWindowsでの統合開発環境構築から

イマドキならIntelliJ+SpringBootかもしれないけど、裾野の広さからEclipseでやってみる。
あと、AWS公式のドキュメントがあるのがEclipseだからという理由も。

[Java]EclipseでSpring Bootを使ってみる。

この辺を見ながらWindowsでEclipse+SpringBootな環境を作っていく。
Javaな人たちからすると当たり前すぎるからか、あんまり書かれていないのだけど、Javaのパス情報(PATHとJAVA_PATHの環境変数)が正しくないとGradleを使ったビルドが通らなかったりするので、設定しておく。GRADLE_PATHあたりも入れておかないと怪しい(ハマりポイント)。
参考情報は以下。

Let's プログラミング PATHの設定及び環境変数JAVA_HOMEの設定

さらに、今回はCodeCommitと連携することを目的に、以下を見ながらAWS Toolkit for Eclipseの設定を入れる。

【AWS公式】Eclipse と AWS CodeCommit の統合

インストールしようとすると、以下のようなエラーになったりする(Eclipseを日本語化していない場合、"No repository found containing" な感じで出力されていると思う)。

インストールする項目の収集中にエラーが発生しました
セッション・コンテキスト:(プロファイル=C__Users_NERU_eclipse_jee-2019-12_eclipse、フェーズ=org.eclipse.equinox.internal.p2.engine.phases.Collect、オペランド=、アクション=)。
含まれているリポジトリーが見つかりません: osgi.bundle,org.eclipse.jgit,5.6.1.202002131546-r
含まれているリポジトリーが見つかりません: osgi.bundle,org.eclipse.jgit.archive,5.6.1.202002131546-r
含まれているリポジトリーが見つかりません: osgi.bundle,org.eclipse.jgit.http.apache,5.6.1.202002131546-r
含まれているリポジトリーが見つかりません: osgi.bundle,org.eclipse.jgit.ssh.apache,5.6.1.202002131546-r
含まれているリポジトリーが見つかりません: org.eclipse.update.feature,org.eclipse.jgit,5.6.1.202002131546-r
含まれているリポジトリーが見つかりません: org.eclipse.update.feature,org.eclipse.jgit.http.apache,5.6.1.202002131546-r
含まれているリポジトリーが見つかりません: org.eclipse.update.feature,org.eclipse.jgit.ssh.apache,5.6.1.202002131546-r

エラーになったプラグインをググって、ダウンロードサイトを「ヘルプ」⇒「新規ソフトウェアのインストール」で開いたウィンドウでサイトを入力する。たとえば、↑のjgitとかは http://download.eclipse.org/egit/updates を入れれば見つけることができる。全てのプラグインがこの方法でできるかは不明。見つけられないものもあった。謎。

あと、AWS Toolkit for Eclipseのリージョンはデフォルトで東京リージョンになっていない。↑のサイトを読んでも、

リポジトリが表示されない場合は、フラグアイコンを選択して AWS リージョンメニューを開き、リポジトリが作成された AWS リージョンを選択します。

としか書かれていなくて意味が分からない。「フラグアイコンって何だよ!」って思ったら、

キャプチャ3.png

これのことだった。東京リージョンを選択すると

キャプチャ4.PNG

無事、東京リージョンでお試しで作ってみたCodeCommitのプロジェクトにアクセスすることができた。

あとは、手順に従ってローカルにチェックアウトしてくればOK。

ローカル環境からgit commit&pushしてみる

キャプチャ5.PNG

こんな感じ(画像の蛍光ペンの場所)でindex.htmlを編集してみて、右クリックからの「チーム」⇒「コミット」でGitステージングのタブを開き、

キャプチャ6.PNG

作成者やコミッター、コミット・メッセージを入れて、「コミットおよびプッシュ」をクリック!
この時はCommit時にキャプチャを取り忘れたけど、コミット・メッセージには Commit From Eclipseと入れている。

で、正常終了してからAWSのマネジメントコンソールを見てみると、

キャプチャ7.PNG

ちゃんとCommitされている。ということは、

キャプチャ8.PNG

無事、パイプラインの起動によるビルド&デプロイも成功!

EC2向けのパイプラインをECS+Fargateに組み替える

以下の書籍を参考にしながら。

クラウドネイティブファーストストーリー

CodeBuildでECRにpushするようにBuildSpecファイルを組み替える

まずは、ビルド結果をECRにPUSHするので、CodeBuildで使うIAMロールにECRにアクセスするためのポリシを設定しておく。CloudWatch Logsにログを出すならそのポリシ(CloudWatchLogsFullAccess)も必要。

ビルド環境は何でも良いかと思いきや、Amazon Linux2の全バージョンと、Ubuntu Standard 4.0についてはOpenJDKが入っていないので注意(Corettoになっている)。

特権モードとか、何も考えずにEC2向けのCodeBuildの設定と同じと考えていると、書籍内で「ハマるから気を付けて」と書いてある箇所は100%ハマるので、事前にしっかり読み込んでおくか、ハマったら読み返してみるといい。

Buildspecファイルはこんな感じで。元のハンズオンのbuildspec.ymlを汚したくないのであれば、別名で作って、ビルドプロジェクトの設定でファイル指定すると良い。アーティファクトが元のハンズオンのままだけど、今回はECRのリポジトリに登録すれば終わりなので不要な気がする。この時点では書いてあってあっても困らない(S3にファイル置かれるくらい)ので、このままでも良い。後でハマりポイントになるので注意。

キャプチャ.PNG

buildspec_container.yml
version: 0.2
 
env:
  variables:
    AWS_REGION_NAME: ap-northeast-1
    ECR_REPOSITORY_NAME: my-greeting-web
 
phases:
  install:
    runtime-versions:
      java: openjdk11
  pre_build:
    commands:
      - $(aws ecr get-login --region ${AWS_REGION_NAME} --no-include-email)
      - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
      - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION_NAME}.amazonaws.com/${ECR_REPOSITORY_NAME}
      - IMAGE_TAG=$(git rev-parse --short HEAD)
  build:
    commands:
      - echo Build started on `date`
      - mvn test package
      - docker build -t ${REPOSITORY_URI}:latest .
      - docker tag ${REPOSITORY_URI}:latest ${REPOSITORY_URI}:${IMAGE_TAG}
  post_build:
    commands:
      - docker push ${REPOSITORY_URI}:${IMAGE_TAG}
artifacts:
  files:
    - target/my-greeting-web-0.1.0.jar
    - appspec.yml
    - deploy/*
  discard-paths: yes
cache:
  paths:
    - '/root/.m2/**/*'

あと、Buildspecファイルと同じディレクトリにDockerfileも必要なので注意。

さて、これでビルドが通るようになったはずなので、ビルドの実行をして確認してみる。
上手くいくと、ECRのリポジトリが更新されているはず。

CodeDeployを組み替える

例によってCodeDeployが使うIAMロールがECSにアクセスできるようにポリシ(AWSCodeDeployRoleForECS)を設定する。

さらに、ECSにCodeDeployする場合はBlue/Greenデプロイメントになるので、ALBにもターゲットグループを追加して、2つのターゲットグループを打つようにしておく。

また、CodeDeploy用にECSのサービスを起動しておく必要があるので、これまでローリングアップデートで起動する設定の場合は、以下のように変更する。というか、デプロイメントの設定は作成時にしかできないため、新しくサービスを作る。
キャプチャ2.PNG
このときも、ECSのIAMロールにも、CodeDeployのアプリケーションを作成するためのポリシ(AWSCodeDeployRoleForECS)を設定しておく。うーん、↑のポリシと同じなのが本当に正しいのかはよく分からない……。

新しいサービスが上手く作れると、CodeDeployにこんな感じでアプリケーションが作成される。
キャプチャ3.PNG

このアプリケーションに対して、デプロイを作成してみる。
ここまでの流れだと、Appspecファイルをまだ登場させていないので、JSONかYAMLを直接以下のように定義。
ググったり↑の書籍では<TASK_DEFINITION>というプレースホルダが使えたりすると書いているが、これはCodePipelineで置換されるプレースホルダであるため、CodeDeploy単独でお試ししている現時点では使えない。

JSON
{
  "version": 1,
  "Resources": [
    {
      "TargetService": {
        "Type": "AWS::ECS::Service",
        "Properties": {
          "TaskDefinition": "arn:aws:ecs:ap-northeast-1:[アカウントID]:task-definition/my-greeting-web:[タスクのバージョン番号]",
          "LoadBalancerInfo": {
            "ContainerName": "my-greeting-web",
            "ContainerPort": 8080
          }
        }
      }
    }
  ]
}
YAML
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "arn:aws:ecs:ap-northeast-1:[アカウントID]:task-definition/my-greeting-web:2"
        LoadBalancerInfo:
            ContainerName: "my-greeting-web"
            ContainerPort: 8080

デプロイに成功すると、↓こんな感じで成功の画面が出る。この前はプログレスバーが青かったので、BlueがGreenに切り替わるのが視覚的に分かって良い感じ。

キャプチャ4.PNG

右上の「元のタスクセットの終了」を押すと、ちゃんとタスクを停止してくれる。

パイプラインを繋ぐ

ここからが難しい。パイプラインの中では、新規にECRに登録したタグのタスク定義をする必要があるため、そのCloudFormationの定義ファイルを作っておく必要がある。

taskdef.json
{
  "executionRoleArn": "arn:aws:iam::[アカウントID]:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::[アカウントID]:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "my-greeting-web",
      "image": "<IMAGE1_NAME>",
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 8080,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "cpu": 512,
      "memoryReservation": 1024,
    }
  ],
  "requiresCompatibilities": ["FARGATE"],
  "networkMode": "awsvpc",
  "cpu": "512",
  "memory": "1024",
  "family": "my-greeting-web"
}

Appspecファイルも、以下のような感じでプレースホルダを設定しておく。
元のハンズオンではEC2で起動するための設定になっているので、以下の様に修正。Buildspecファイル同様、元のファイルを残しておきたいならファイル名を変更して、後でファイル名を指定して読み込ませる。

appspec_container.yml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
            ContainerName: "my-greeting-web"
            ContainerPort: 8080

CodePipelineから起動するECSのBlue/GreenデプロイメントのDeployフェーズは、↑で勝手に作られたCodeDeployのアプリケーションとアプリケーショングループを選択すればOK。

ただし、パイプラインを繋ぐ際に、単純にCodeBuildで設定したBuildSpecを使うのではダメ。
以下の様に、IMAGE_TAGの設定をCodeBuildから呼ばれた際のバージョンに置き換えたり、アーティファクトの出力を変更したりする必要がある。特に、このアーティファクトの出力が曲者で、アーティファクトが大きすぎるとエラーになる。実際には、ECRにちゃんとコンテナが登録されていれば、imageDetail.jsonさえ出力されていれば良い(後で小細工するからだけど)。
imageDetailは、コンテナイメージの詳細情報。これが、taskdef.json<IMAGE1_NAME>のプレースホルダに使われることになる。

buildspec_container.yml
version: 0.2
 
env:
  variables:
    AWS_REGION_NAME: ap-northeast-1
    ECR_REPOSITORY_NAME: my-greeting-web
 
phases:
  install:
    runtime-versions:
      java: openjdk11
  pre_build:
    commands:
      - $(aws ecr get-login --region ${AWS_REGION_NAME} --no-include-email)
      - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
      - REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION_NAME}.amazonaws.com/${ECR_REPOSITORY_NAME}
      - IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | cut -c 1-7)
  build:
    commands:
      - echo Build started on `date`
      - mvn test package
      - docker build -t ${REPOSITORY_URI}:latest .
      - docker tag ${REPOSITORY_URI}:latest ${REPOSITORY_URI}:${IMAGE_TAG}
  post_build:
    commands:
      - docker push ${REPOSITORY_URI}:${IMAGE_TAG}
      - printf '{"name":"%s","ImageURI":"%s"}' $ECR_REPOSITORY_NAME $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
  files:
    - imageDetail.json
  discard-paths: yes
cache:
  paths:
    - '/root/.m2/**/*'

さらに、CodePipelineのデプロイでは、なぜか作成時点ではSource Artifactが指定できない部分があるので、パイプラインを作成した後に、以下のような感じで変更を行う。

キャプチャ5.png

キャプチャ6.png

キャプチャ7.png

これで、EclipseでCommitしたらCodePipelineが起動してBlue/Greenデプロイメントをしてくれるはず。

その他Tips的なもの

CodeBuildの実態

ちょっと調べてみたときに、CodeBuildはGradleのバージョンが古いからとかなんとかいう記事を見つけて、そもそもCodeBuildってどうやって動いているのよ、というのが気になった。

SlideShareで過去のBlackBeltオンラインセミナーの資料が公開されていて、それによるとビルド環境のDockerコンテナを起動してその上でビルドを走らせているようだ。

探してみると、GitHubにCodeBulildビルド環境のDockerfileがあった。Gradleは、この記事を書いた2020年3月時点で

    INSTALLED_GRADLE_VERSIONS="4.10.3 5.4.1" \
    GRADLE_VERSION=5.4.1 \

な感じのバージョンが入っている。環境変数でバージョンを変えられるということか。

これで、いちいちググらなくても自力でバージョンを調べられる。

CodePipelineの無効化

CodePipelineのパイプラインそのものを無効化することはできない。
消さないと勝手に全部走っちゃうのかよ!と思いきや、CodeCommitからCodeBuildを繋ぐ部分を無効化すれば、基本的に動かないようになっている。はず。

tempsnip.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?