Github ActionsでEKSにコンテナをデプロイする

プログラム
スポンサーリンク

前の記事ではEKSに独自コンテナをデプロイして公開するところまでを行いました。

今回はいよいよGithub Actionsを使ってコンテナの自動デプロイとロードバランサでの公開を行います。当初の目標である「GithubのCI/CD機能であるGithub ActionsでAWSにSpring Bootが動くコンテナをデプロイする」にかなり近づいてきました。

今回も公開するのは簡単なnginxのサーバです。

Github Actionsを使う前提を整えるために今回もCloud9を使います。前回の環境を削除していなければ、そのまま使いまわします。一度削除していたら、前回の記事で行った(2.Cloud9でEKSを使う環境を整える)のと同じように環境を構築してください。

この記事でわかること
  • Github Actionsの概要
  • Github Actionsで秘密情報を扱う方法
  • Github ActionsからAWSにアクセスする方法
  • Github Actionsで使う設定ファイルの概要
前提条件
見出し
  • アカウント作成後12か月以内でも無料枠外のことを行うので費用がかかる
  • 作成したEKS Clusterなどのリソースを削除し忘れるとずっと費用がかかり続けることになる

この記事のソースコード

この記事のソースコードはGithubに公開しています。

GitHub - gsg0222/cicd-test
Contribute to gsg0222/cicd-test development by creating an account on GitHub.

任意のフォルダでcloneしてください。

記事内で説明する前提条件を満たしていれば、cloneした内容をあなたのGithubリポジトリのmasterブランチにpushすると、Github Actionsが動き自動でEKSにコンテナがデプロイされます。

全体のシーケンスを確認する

登場人物が多いので、今回Github Actionsで構築するCI/CDの流れを図にしておきます。

この記事で作成するCI/CDの流れ。開発者がGithubにPushし、ActionsでECRにDockerイメージをPush、kubernetesにmanifestを適用してECRからEKSにデプロイ
  1. gitコマンドでmasterにpushすると
  2. Github Actionsが動いて作成したDockerイメージをECRにpush
  3. 同じくGithub Actionsからkubectlでmanifestを適用して
  4. manifestの指示に従ってECRからDockerイメージを取得してデプロイ

というのが今回の記事で行う一連の流れです。

Github Actionsとは

すでに何回か出てきている「Github Actions」という言葉ですが、ここで概要を説明します。

Github ActionsはGithubが提供するCI / CDツールです。Github Actionsの書式に沿ったファイルを特定のフォルダにおいておくと、条件を満たした場合そのファイルで指定した動きを実行するというものです。

具体的には、/.github/workflows/フォルダに書式に沿ったyamlファイルを置くことで利用できます。

例えばですが、

  • コードの静的解析とビルド
  • テストの自動実施
  • Dockerコンテナの作成
  • レジストリへのパッケージの公開
  • Slackやメールへの通知

こんなことができます。コマンドラインから実行できることはGithub Actionsでも大体実行できると思って良いでしょう。

パブリックリポジトリであれば無料で使うことができます。プライベートリポジトリでもCPU時間2000分は無料で利用可能です。

AWSでGithub Actionsからリソースを使えるようにする

Github ActionsからAWSのリソースを使うために、EKSのClusterを作成するロールに追加設定を行う必要があります。

Clusterを作成したロール以外でClusterを使えるようにすることも可能そうなのですが、設定がややこしそうなので今回はロールの設定で対応することにしました。

IDプロバイダを作成する

Github Actionsからの接続を許可するためのIDプロバイダを作成します。

AWSコンソールにログインして、[IAM]→[ID プロバイダ]→[プロバイダを追加]と選びます。すると次のような画面になるはずです。

IDプロバイダ作成画面

項目ごとに次のように値を入力してください。

  • プロバイダのタイプ:OpenID Connect
  • プロバイダのURL:https://token.actions.githubusercontent.com(入力後[サムプリントを取得]を押す)
  • 対象者:sts.amazonaws.com

これらを入力後、画面下部の[プロバイダを追加]を押せば作成完了です。

Github Actionsの特定リポジトリからロールを使用可能にする

EKSでClusterを作成したロール(=Cloud9のIAMロールに指定したもの)でIDプロバイダを使えるようにします。

[IAM]→[ロール]→[EKSでClusterを作成したロール]→[信頼関係]タブ→[信頼ポリシーを編集]を押してください。次のような画面になるはずです。

信頼ポリシー編集画面

この内容を、以下のように修正して[ポリシーを更新]ボタンを押します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"Service": "ec2.amazonaws.com"
			},
			"Action": "sts:AssumeRole"
		},
		{
			"Effect": "Allow",
			"Principal": {
				"Federated": "arn:aws:iam::{aws_account_id}:oidc-provider/token.actions.githubusercontent.com"
			},
			"Action": "sts:AssumeRoleWithWebIdentity",
			"Condition": {
				"StringEquals": {
					"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
				},
				"StringLike": {
					"token.actions.githubusercontent.com:sub": "repo:{github_repository}:*"
				}
			}
		}
	]
}

{aws_account_id}は前回記事と同じくAWSのアカウントID、{github_repository}はリポジトリのパスを指定します。例えばこの記事で使うソースコードを保存したリポジトリの場合、「”repo:{github_repository}:*”」の部分は「”repo:gsg0222/cicd-test:*”」です。

“Condition”の設定は忘れないようにしてください。このロールを使用可能なシステムを制限しています。もし”Condition”を設定していないと、すべてのシステムで利用可能な非常にセキュリティ的に弱いロールとなってしまいます。

Github Actionsの設定

Github Actionsの方の設定です。他人に見せたくない設定はSecretに変数として保存し、Github Actionsで行う処理は/.github/workflows/のYAMLファイルに記述します。

Secretを設定する

Github ActionsからAWSを利用するためには、認可が必要です。認証・認可に関係する値を直接ファイルに書くと、知られたくない人にも確認される危険性があります。それを回避するため、GithubではSecretsという仕組みを利用可能です。

Github Actionsを使いたいリポジトリを開いて、[Settings]→[Secrets]→[Actions]と選択してください。以下の様が画面が表示されるはずです。

GithubのSecrets編集画面

今回はこの画面で2つの値を設定します。

  • AWS_ASSUME_ROLE_ARN:EKSでClusterを作成したロールを指定します。対象のロールをAWSコンソールで開いて([IAM]→[ロール]→[対象のロール])、ARNの項目を確認してください。arn:aws:iam::{aws_account_id}:role/{role_name}という形式になっているはずです。
  • ECR_REGISTRY:ECRレジストリを指定します。ECRのRepository一覧で確認できるURIの前半部分です。{aws_account_id}.dkr.ecr.ap-northeast-1.amazonaws.comという形式になっているはず。

Secretsで設定した値は、削除や更新はできますが値の確認をすることはできません。秘密はかなりの確度で守られるはずです。

実はAWS_ASSUME_ROLE_ARNもECR_REGISTRYも見られたからと言って致命傷になるわけでもないのですが、あまり見られたくないのは確かなのでSecretsとしました。

Github Actions用のYAMLファイルを編集する

Github Actionsで行う処理は、/.github/workflows/のYAMLファイル(拡張子がyamlかymlのファイル)に記述します。

ファイル名は何でも大丈夫ですが、この記事ではactions.yamlとしています。早速YAMLファイルを見てみましょう。

actions.yaml:

name: Action Sample

# このActionsが動くのは、masterブランチにpushされたとき
on:
  push:
    branches: 
      - master
  # 手動でActionsを実行可能に
  workflow_dispatch:

# role-to-assumeを使ったAWSの認証をするのに必要らしい
permissions:
  id-token: write
  contents: read

# 変数定義
env:
  # ECRリポジトリ名
  ECR_REPOSITORY: sample-container

jobs:
  # Dockerイメージの作成とリポジトリへのアップロード
  build:
    runs-on: ubuntu-latest
    steps:
      # リポジトリの内容をこのジョブにclone
      - uses: actions/checkout@v2

      # dockerの設定を初期化
      - uses: docker/setup-buildx-action@v1

      # AWSへのログイン設定
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-region: ap-northeast-1
          # AWS上での操作として、ロールにIDプロバイダの設定が必須
          role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }}
          role-duration-seconds: 1200

      # dockerの操作対象をECRのリポジトリにする
      - uses: docker/login-action@v1
        with:
          registry: ${{ secrets.ECR_REGISTRY }}

      # Dockerイメージの作成とリポジトリへのPush
      - uses: docker/build-push-action@v2
        with:
          context: "{{defaultContext}}:nginx"
          push: true
          # タグにCommitのshaを設定する
          tags: ${{ secrets.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}

  # EKSにコンテナをデプロイしてロードバランサを設定
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.AWS_ASSUME_ROLE_ARN }}
          role-duration-seconds: 1200

      - name: Configure EKS
        # eksの認証情報を更新する。このコマンドを呼べばkubeconfigの内容をSecretに設定することは不要。
        run: |
          aws eks update-kubeconfig --name test

      - name: Deploy to EKS
        # YAMLファイルで指定するimageをsedで無理やりsha付きのイメージ名に変更
        # もっといい方法もありそうだが、調べきれず
        run: |
          mkdir ./tmp
          sed -e 's/<IMAGE>/${{ secrets.ECR_REGISTRY }}\/${{ env.ECR_REPOSITORY }}:${{ github.sha }}/g' ./yaml/sample-container.yaml > ./tmp/sample-container.yaml
          kubectl apply -f ./tmp/sample-container.yaml
          kubectl apply -f ./yaml/service.yaml

詳細な説明は公式のドキュメントを読んでください。この記事ではごく簡単に今回使うYAMLの説明をします。

name, on, permission, env構文

name: Action Sample

# このActionsが動くのは、masterブランチにpushされたとき
on:
  push:
    branches: 
      - master
  # 手動でActionsを実行可能に
  workflow_dispatch:

# role-to-assumeを使ったAWSの認証をするのに必要らしい
permissions:
  id-token: write
  contents: read

# 変数定義
env:
  # ECRリポジトリ名
  ECR_REPOSITORY: sample-container

この辺は事前準備の内容です。

on:でどのようなときにこのファイルが実行されるかを記述します。今回はmasterブランチにpushされたときという指定です。また、workflow_dispatchを書くことで手動での実行も可能としています。

permissions:はどうやらGITHUB_TOKENのパラメータを設定するためのもののようです。今回、AWSへのアクセス認可を得るためにOpenID Connectを使うのですが、そのへんの関連で必要な模様。正直正確な意味は把握できていません。

env:ではファイル内で使う変数を定義しています。

jobs:以降

jobs:以降は実際にどのような動作を行うのかを記述しています。だいたいコメントに書いてあるとおりです。

name: Deploy to EKSの部分だけややこしいので補足します。

      - name: Deploy to EKS
        # YAMLファイルで指定するimageをsedで無理やりsha付きのイメージ名に変更
        # もっといい方法もありそうだが、調べきれず
        run: |
          mkdir ./tmp
          sed -e 's/<IMAGE>/${{ secrets.ECR_REGISTRY }}\/${{ env.ECR_REPOSITORY }}:${{ github.sha }}/g' ./yaml/sample-container.yaml > ./tmp/sample-container.yaml
          kubectl apply -f ./tmp/sample-container.yaml
          kubectl apply -f ./yaml/service.yaml

この部分では、kubectlにファイルを渡してEKSへデプロイしているのですが、sample-container.yamlにはこのファイルを実行している途中でECRにPushしたタグ名を渡す必要がありました。

kubectlコマンドに渡すファイル内で、環境変数を使うことができればよかったのですが、残念ながら私はその方法を調べきれません。仕方ないので、元のファイルにプレースホルダーとして<IMAGE>という文字列を書いておき、sedコマンドで書き換えたものを./tmp/sample-container.yamlとして保存しています。

kubectlには<IMAGE>をデプロイするタグ名に置き換えた./tmp/sample-container.yamlを渡すことで、最新のコンテナをデプロイすることができています。

なお、kubectlに読み込ませている各種yamlファイルは前の記事の内容とほぼ同じです。

実際にデプロイしてみる

それでは実際にデプロイしてみます。まず、デプロイ対象のEKS ClusterとECRリポジトリを作成します。Cloud9で以下のコマンドを実行してください。(2.Cloud9でEKSを使う環境を整えるを参照)

作業終了後にリソースの削除を忘れないようにしましょう。

# ECRリポジトリ作成
aws ecr batch-delete-image --repository-name sample-container --image-ids imageTag=latest --region ap-northeast-1
# EKS Cluster作成
eksctl create cluster --name test --node-type=t2.micro --nodes=2 

続いて、Githubからコードをcloneして、先程Secretsを設定したリポジトリのmasterブランチにPushします。すると、自動的にGithub Actionsが動いてデプロイが行われるはずです。

Github Actionsの動作確認をするには、Githubの対象リポジトリにアクセス後、Actionsタブを選択してください。

Github Actionsの実行結果

もしエラーが発生していたら、Q&Aを確認してみてください。解決策があるかもしれません。

デプロイしたものが実際に動いていることは、以下のコマンドでロードバランサのURLを確認してブラウザでアクセスすると確認できます。EXTERNAL-IPがロードバランサのURLです。

kubectl get services

うまく行っていれば「Hello world form EKS.」と表示されるはず。

AWSリソースの削除

動作確認が終わったら、AWSからリソースを削除します。

# ECRリポジトリ削除
aws ecr batch-delete-image --repository-name sample-container --image-ids imageTag=latest --region ap-northeast-1
# EKS Cluster削除
eksctl delete cluster --name=test
注意

リソースの削除を忘れると毎時間ごとに経費がかかります。必ず削除しましょう。

うまくいかなかった場合のQ&A

error: You must be logged in to the server (Unauthorized)

Github ActionsのDeploy to EKSで上記エラーが発生した場合、可能性は次の通りです。

  • IDプロバイダの設定に入力ミスがあった
  • ロールの信頼ポリシーの指定が間違っている。リポジトリ名が正しくないなど
  • SecretsのAWS_ASSUME_ROLE_ARNが正しくない。変数名が違うか、正しいロール名を渡していないなど

どれも入力ミスだと思うので、1つづつ正しい値を設定したか確認してみてください。

まとめ:EKSを使って最低限のCI/CDができるようになりました

ここまでの内容が理解できれば、Github Actionsを使ったEKSでのCI/CDは最低限できるはずです。

例えば何コードをコンパイルしてその結果をDockerに保存したい場合などは、actions.yamlのbuild:を修正して、steps:にコンパイルする手順を追加することで対応できるはずです。

AWSでIDプロバイダとロールの信頼ポリシーを設定するあたりはややこしいと思います。ただ、作業1つ1つはそこまで大変ではないので、記事の通り順番に進めてみてください。

目標である「GithubのCI/CD機能であるGithub ActionsでAWSにSpring Bootが動くコンテナをデプロイする」まではあと一歩です。

コメント

タイトルとURLをコピーしました