AWS CloudFormation 実践 第4回 CI/CD編(GitHub Actions)

今回は、前回までに作成したCloud Formationテンプレートを、GitHub Actionsを使ってデプロイする方法についてアウトプットしていきます。

事前知識

GitHub Actions


GitHub Actionsは、GitHubが提供するワークフロー自動化サービスで、GitHub Actionsを使うことで、CI/CD、デプロイ、テスト、パッケージングなど、作業を効率的に実行できます。ワークフローの定義ファイルを作成し、GitHub上で設定することで、自動化された処理を実行できます。

CI/CD

CI/CD(継続的インテグレーションと継続的デリバリー)は、ソフトウェア開発プロセスの一部であり、開発プロセスを効率化し、品質を向上させるための重要な手法です。

  • 継続的インテグレーション (CI):
    • 開発者がコードを共有リポジトリにプッシュするたびに、自動的にビルド、テスト、およびコードの品質チェックが行われます。 バグや問題を早期に検出し、コードの品質を向上させます。 CIツール(例:Jenkins、Travis CI、CircleCI)を使用して実現されます。
  • 継続的デリバリー (CD):
    • CIの成果物を自動的にステージング環境にデプロイします。ステージング環境でさらなるテストや検証を行い、問題がないことを確認します。成果物が本番環境にデプロイできる状態になったら、自動的に本番環境にデプロイされます。CDツール(例:GitHub Actions、AWS CodePipeline、GitLab CI/CD)を使用して実現されます。

全体構成

今回の作業を実装した後の構成は下図のとおりとなります。AWS VPC内部の構造は、Cloud Formationテンプレートで記述したものであり、前回と同様です。

処理の流れ

Actionsが動作する大まかな流れは、次の通りとなります。

  1. (開発者)GitHubにテンプレートファイルとワークフローyamlをプッシュする。
  2. GitHub Actions)プッシュを受けて、AWSにCloudFormationをDeployする
  3. AWSが)Cloud Formationに基づいてスタックを構成する

【ポイント】手順2で、ActionsからCloudFormationを操作する際、AWSへの接続・認証には、AWS IAMの「IDプロバイダ」で指定するOIDC(OpenID Connect)を使用します。詳細は後述します。

対応手順

それでは、GitHub Actionsの構成作業に入ります。作業手順はおおまかに以下の流れで実施していきます。

  1. CloudFormationテンプレートの作成
  2. AWS IDプロバイダの作成
  3. AWS IAMロールの作成
  4. GitHubレポジトリ設定(シークレットの登録)
  5. GitHub Actionsワークフローの作成
  6. GitHubへのPush
  7. 動作確認

CloudFormationテンプレートの作成

こちらは前回までの内容をそのまま使用します。テンプレートは、以下のGitHubにある「cf.yaml」を使用します。

github.com

AWS IDプロバイダの作成

こちらの手順を始める前に、GitHub Actionsが行うAWS認証について、少し説明します。

OIDCについて

OpenID Connect(OIDC)は、OAuth2.0メカニズムを使用した認証プロトコルのひとつです。GitHubから、AWS CloudFormationにDeployを指示する際、AWSへの接続に使用します。

この方法として、以前は、IAMユーザのアクセスキーとシークレットキーを使用してデプロイする手段もあったのですが(現在も使用可能ですが)、昨今では、よりセキュアなアプローチとして、今回のIDプロバイダを使用する方法が主流となっているようです。

シークレットアクセスキーのような固有のキーを発行、管理するのではなく、AWSにプロバイダ(今回の場合、GitHub)を登録し、これを信頼するロールを定義することでAWSに、特定のGitHubからのAPIリクエストを受け入れることができるようになります。またロールの信頼ポリシーに、対象リポジトリを指定することで、受け入れる対象を限定できます。下図にイメージを示します。

なお、本手順についてはGitHubのドキュメントに詳細がありますので、詳しくは以下をご確認ください。

docs.github.com

IDプロバイダの作成

ここでは、GitHub ActionsをIDプロバイダとして登録します。

  • AWSのマネジメントコンソールで、IAMサービスを開きます。
  • サイドメニューから「アクセス管理」-「IDプロバイダ」を選択し、「プロバイダを追加」ボタンをクリックします。
  • 画面には、次の内容を登録します。

    • プロバイダのタイプ:OpenID Connect
    • プロバイダ名:「https://token.actions.githubusercontent.com
      入力後「サムプリントを取得」をクリックします。
    • 対象者:「sts.amazonaws.com」
      ※ 上記「プロバイダ名」のURLや対象者の値は、GitHub固有の値であり、固有値です(前述のドキュメントに記載があります)

  • 入力が終わりましたら、右下の「プロバイダを追加」ボタンをクリックします。

  • 作成後、IDプロバイダ画面にプロバイダtoken.actions.githubusercontent.comが表示されます。こちらをクリックして表示される、ARNの値 arn:aws:iam::xxxxxxxxxxxx:oidc-provider/token.actions.githubusercontent.com を控えておいてください。

AWS IAMロールの作成

ここでは、GitHub Actionsを受け入れるためのロールを作成します。

  • AWSのマネジメントコンソールで、IAMサービスを開きます。
  • サイドメニューから「アクセス管理」-「ロール」を選択し、「ロールを作成」ボタンをクリックします。
  • 「信頼されたエンティティを選択」画面では、GitHub Actionsのトリガとなるレポジトリの情報を指定します。例として、レポジトリ https://github.com/camelrush/aws_cloudformation_tutorial の master ブランチへのpushをトリガにする場合、以下のように登録します。入力したら「次へ」をクリックします。
  • 「許可を追加」画面では、Cloud Formationの動作に必要なリソースへのアクセス権限を設定します。本来はAWSのWell Architected に従い、必要最低限の権限を指定すべきですが、今回は暫定的に「AdministratorAccess」を設定します。設定したら、「次へ」をクリックします。
  • 「名前、確認、および作成」画面で任意のロール名(cf-tutorial-dev-githubactionsiam-roleとしました)、説明を登録し、「ロールを作成」ボタンをクリックします。
  • 作成されたロールの「信頼関係」を確認すると、下図のようにポリシーが定義されています。

以上で、AWS側の設定は終わりです。

GitHubレポジトリ設定(シークレットの登録)

次に、上記で作成したIAMロールを、GitHubレポジトリに登録します。

  • WebブラウザGitHubレポジトリを開き、サインインします。
  • 上部メニューの「Settings」をクリックし、再度メニューから「Secrets and variables」-「Actions」をクリックします。

  • 「New repository secret」ボタンをクリックし、表示される画面に、次の情報を登録して「Add secret」ボタンをクリックします。

    • Name:「AWS_IAM_ROLE_ARN」
    • Secret:(前述で作成したロール名(例では、cf-tutorial-dev-githubactionsiam-role ))

以上で、GitHubに対して、AWSアクセスのためのロール名を設定できました。

GitHub Actionsワークフローの作成

ここでは、GitHub Actionsのワークフローファイルを作成していきます。フォーマットについても、簡単な解説を挟みます。

ディレクトリ構成

対象となるGitリポジトリのルート配下に、ディレクトリ「.github」「workflows」を作成し、その下にymlファイルを作成してください。ymlファイルの名前は任意です。

 .github(dir)
  └ workflows(dir)
    └ deploy-on-push.yml

ワークフローファイルymlファイルの記述

最初に、作成するパラメタファイルの全体を示します。

name: "CloudFormation Deploy on Push Event"

on:
  push:
    branches: master

env:
  AWS_REGION: ap-northeast-1
  CF_ENVIRONMENT: Dev
  CF_SOURCEIP: 0.0.0.0

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    name: deploy for CloudFormation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source.
        uses: actions/checkout@v4
      - name: Configure AWS Credential
        uses: aws-actions/configure-aws-credentials@v4
        with: 
          aws-region: ${{ env.AWS_REGION }}
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
      - name: deployCloudformation
        uses: aws-actions/aws-cloudformation-github-deploy@master
        with:
          name: cf-tutorial-stack
          template: cf.yaml
          no-fail-on-empty-changeset: "1"
          parameter-overrides: >-
            cftMyIpAddress=${{ env.CF_SOURCEIP }},
            cftEnv=${{ env.CF_ENVIRONMENT }}

以下、ブロックごとに詳細を説明します。

on(トリガ)
on:
  push:
    branches: master

ワークフローを実行するトリガーを記載します。上記の場合、masterブランチへのpushがトリガーとなります。これ以外に、pull_requestという記述でプルリクをトリガーとしたり、path指定で特定の階層への操作をトリガ―に指定することもできます。

env(環境変数
env:
  AWS_REGION: ap-northeast-1
  CF_ENVIRONMENT: Dev
  CF_SOURCEIP: 0.0.0.0

ワークフロー内で環境変数を使用する場合、ここで任意の変数を定義します。定義した変数は、後述のjobs等で使用します。今回定義した内容は次の通りです。

  • AWS_REGION: ap-northeast-1:後述のOIDC接続で使用するパラメタ(リージョン)
  • CF_ENVIRONMENT: Dev:CloudFormationテンプレートに引き渡すパラメタ1(環境)
  • CF_SOURCEIP: 0.0.0.0:CloudFormationテンプレートに引き渡すパラメタ2(SSH接続元IP)
permission(権限)
permissions:
  id-token: write
  contents: read

GitHub ActionsがOIDCを使用してワークフローを実行するために必要な権限を指定します。上記2つが必要となりますので、必ず設定してください(この指定は、前述で紹介したGitHub Actionsのドキュメントに記載されています

jobs(ワークフロー内容)① runs-on迄
jobs:
  deploy:
    name: deploy for CloudFormation
    runs-on: ubuntu-latest

jobsには、ワークフローの処理内容を記述します。上記3行の設定内容を以下に示します。

  • deploy:jobの識別名です。値は任意ですので、なんでも構いません。
  • name:こちらも任意ですのでなんでもかいませんが、後ほどGitHub画面で進捗を確認する際、画面に表示されます。
  • runs-on: ワークフローを実行する環境(ジョブランナー)を指定します。大きく分けて、Githubがホストしている固有の環境と、自身が提供するセルフホストが指定できます。今回は、Githubホストのubuntuの最新バージョンを指定しました。ほかに、Windowsや、MacOSも指定が可能です。指定するや、詳細を知りたい方はこちらを参照ください。
jobs(ワークフロー内容)② steps

jobsの内訳として、stepsで複数の実行内容を定義していきます。今回は以下の3ステップを実行しています。

  1. Checkout Source:リポジトリから、ジョブランナーにソースをチェックアウト(取得)する。
  2. Configure AWS Credential:AWS接続のためのクレデンシャルをセットする
  3. deployCloudformation:Cloud Formationテンプレートをデプロイする。
jobs(ワークフロー内容)② step 1. ソースをチェックアウト(取得)する
    steps:
      - name: Checkout Source.
        uses: actions/checkout@v4

ここで、上記のuses: actions/checkout@v4 について、マーケットプレイスActionsについて説明します。

Actions実行内容 uses とマーケットストアについて

stepsで実行する内容は、さまざまあるのですが、代表的なものは runまたはusesの2つです。

runは、ジョブランナー環境で実行するスクリプト命令を直接記述します。ファイルにコピーや移動など、ShellやBatで実行するような命令があれば、このタイプを宣言します。

    steps:
      - name: File Create
        # ↓ 複数以上のコマンドは run | と記載します
        run: |                         
           touch sample.txt
           cat sample.txt
           pwd

usesは、他で定義されたActionを参照して使用します。定義済のActionには、GitHub Marketplaceで公開されているアクションを活用することができます。下の例では、AWSのOIDC接続で必要となる認証Actionを使用しています。

    steps:
      - name: Configure AWS Credential
        # ↓ Marketplace Actionの「aws-actions/configure-aws-credentials@v4」 を実行します
        uses: aws-actions/configure-aws-credentials@v4   
        # ↓ 上記Actionに対するパラメタを指定しています
        with: 
          aws-region: ${{ env.AWS_REGION }}
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}   

GitHub Marketplaceは、GitHubのサイドバーから「Marketplace」を選択することで参照でき、誰でも自由に使用できます。多様なActionが定義されており、中には「プルリクされたソースを、ChatGPTがコードレビューしてくれる」といったものもあります。

使用するActionのリンクを参照することで、具体的な使用方法や、指定できるパラメタの説明が書かれています。

今回のJobでは、usesを使用した3つの定義済ActionをStep実行します。

ソースをチェックアウト(取得)する

話を戻しまして、Step1 のソースチェックアウトです。

    steps:
      - name: Checkout Source.
        uses: actions/checkout@v4

ここでは、MarketplaceのActions actions/checkout@v4 を使用して、ジョブランナー環境にレポジトリのソースを取得しています。

jobs(ワークフロー内容)② step 2. AWS接続のためのクレデンシャルをセットする
      - name: Configure AWS Credential
        uses: aws-actions/configure-aws-credentials@v4
        with: 
          aws-region: ${{ env.AWS_REGION }}
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}

ここでは、Actions aws-actions/configure-aws-credentials@v4 を使用して、AWS接続のためのクレデンシャルを設定しています。with以下にあるのがActionのためのパラメタであり、リージョンと、ロールを指定しています。

変数の使用について

withの内容にある ${{ env.AWS_REGION }}${{ secrets.AWS_IAM_ROLE_ARN }}について解説します。

このようにsecretsを使用することで、外部に公開したくない情報を、定義ファイルの外で管理することができます。

jobs(ワークフロー内容)② step 3. Cloud Formationテンプレートをデプロイする
      - name: deployCloudformation
        uses: aws-actions/aws-cloudformation-github-deploy@master
        with:
          name: cf-tutorial-stack
          template: cf.yaml
          no-fail-on-empty-changeset: "1"
          parameter-overrides: >-
            cftMyIpAddress=${{ env.CF_SOURCEIP }},
            cftEnv=${{ env.CF_ENVIRONMENT }}

Cloud Formationをコールして、テンプレートファイルをAWS環境にデプロイします。Actionには「aws-actions/aws-cloudformation-github-deploy@master」を使用します。パラメタには、次の内容を指定しています。

  • name:Cloud Formationのスタック名
  • template:テンプレートファイル名
  • no-fail-on-empty-changeset:"1"を指定することでChange Set(変更点)が存在しない場合であっても、正常終了となります。
  • parameter-overrides:Cloud Formationのparameterに渡す値を指定。

parameter-overridesはテンプレートファイル内に定義している内容ですので、具体的な内容は、前回までのコラムを参照してください。なお設定しているCF_SOURCEIPCF_ENVIRONMENTは、envとして本ファイル上部に設定済みです。

以上で、ymlファイルの解説は終わりです。

GitHubへのPush

  • 最初に、上記で作成したワークフローyamlファイルを、GitHubにPushします。
  • 次に、Cloud Formationのテンプレートファイル「cf.yaml」を更新して、GitHubにPushします。
  • Push後、ブラウザでGitHubのActionタブを参照すると、定義したワークフローに従ってStepが実行されている進捗状況が表示されます。

  • ちょうど、上の図ではCloud Formationを呼び出しているところなので、別のブラウザでを使ってAWSマネージメントコンソールを開き、Cloud Formationを確認します。下図のとおり、スタックが実行中であることがわかります。

  • しばらくすると、ステータスが緑色(Complete)となり、デプロイが完了します。

動作確認

前述のワークフロー動作によって、下図のVPC内構成が作成されました。

SSHで接続し、動作確認を行います。

無事接続できました。

まとめ

CI/CD構成を作ることが目的でしたが、GitHub Actionsでできることが幅広くあり、Marketplace等を含めて知ることができたことはよかったです。今度、プルリクをChatGPTがコードレビューしてくれるActionなどは、試してみたいですね。

IaCについては、Terraformの使用や、ステート管理(実際の構成との差分)など、実運用に載せるにはまだまだ奥が深そうですが、精進して学んでいきたいと思います。