CircleCI Orbs を使って AWS SAM アプリケーションを自動デプロイしてみる


こんにちは Masuyama です。

私は CircleCIブロガーコミュニティ に所属しているので、CircleCI Orbs を使って AWS SAM アプリケーションを自動デプロイしてみる記事を書いてみます。

今までは CircleCI Orbs はほとんど使ったことが無かったですが、一つ一つ設定を記述していくよりも簡潔に書けるという評判を聞くので Orbs を試してみます。
今回はサーバレスアプリケーションを Code でデプロイできる AWS SAM 専用の Orbs の存在にたまたま気づいたので、[email protected]公式ドキュメントを参照しながら使ってみたいと思います。

なお、純粋に AWS SAM CLI だけを用いてデプロイする方法は別記事で紹介しています。
そちらでは Docker を用いてローカルでテストも実行しているので、一度は試してみることをオススメします。
AWS SAM を用いて Hello World アプリケーションをデプロイしてみる

CircleCI Orbs とは?

公式から引用します。

CircleCI Orbs とは、ジョブ、コマンド、Executor などの、パラメーター化および再利用可能な構成要素をまとめた共有可能なオープン ソース パッケージです。 Orbs を使用すると、構成がシンプルになり、多くのプロジェクトにまたがってソフトウェアやサービス スタックとの連携を素早く、容易に行えるようになります。

すごく簡単にいうと 予め用意している便利な環境パッケージ だと思っています。
実際、今回使った Orbs では CircleCI 用の設定ファイルの記述では Orbs 無しでは 数百行 だったものが Orbs を活用することで 30行程度 で済みました。
強力すぎます。

事前準備

  • AWS アカウントの作成
    • Lambda, API Gateway, S3, IAM の FullAccess 権限を持つ IAM ユーザを作成
    • (実環境であればアクセス権は最低限のものに絞っておきましょう)
    • その AWS アクセスキー、AWS シークレットキーも控えておきます
  • ローカルに AWS SAM CLI のインストール
    • "sam --version" と叩いてバージョンが表示されれば OK
  • CircleCI アカウントの作成
    • GitHub でサインインしておきます
  • GitHub リポジトリの作成
    • 今回つくるアプリケーションを push できるリポジトリを作成しておきます

SAM アプリケーションの構築

サンプルアプリケーションのダウンロード

sam コマンド経由でサンプルとしてダウンロードできるアプリケーションがあるので、ダウンロードしていきます。
まずは下記のコマンドを入力します。

$ sam init

途中でテンプレートソース、パッケージタイム、ランタイムを選択していきます。
プロジェクト名は任意の名前で問題ありません。
ここでは「hello-sam」としています。

$ sam init
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1
What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

Which runtime would you like to use?
        1 - nodejs14.x
        2 - python3.8
        3 - ruby2.7
        4 - go1.x
        5 - java11
        6 - dotnetcore3.1
        7 - nodejs12.x
        8 - nodejs10.x
        9 - python3.7
        10 - python3.6
        11 - python2.7
        12 - ruby2.5
        13 - java8.al2
        14 - java8
        15 - dotnetcore2.1
Runtime: 9

Project name [sam-app]: hello-sam

ここまで入力したら、自動で GitHub からテンプレートがローカルにダウンロードされてきます。
最後にテンプレートを選択します。

Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
        1 - Hello World Example
        2 - EventBridge Hello World
        3 - EventBridge App from scratch (100+ Event Schemas)
        4 - Step Functions Sample App (Stock Trader)
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: hello-sam
    Runtime: python3.7
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .
    
    Next steps can be found in the README file at ./hello-sam/README.md

ここまで表示されれば最初の準備は OK です。

アプリケーションの構築

プロジェクトディレクトリに移動して中身を確認してみましょう。

$ cd hello-sam
$ tree
.
├── README.md
├── __init__.py
├── events
│   └── event.json
├── hello_world
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── template.yaml
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_handler.py

この中の **template.yaml** というファイルが重要です。
AWS SAM では template.yaml という設定ファイルを作成し、AWS SAM リソースを定義します。

template.yaml の中身はこんな感じです。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  hello-sam

  Sample SAM Template for hello-sam

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.7
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

AWS SAM は中で CloudFormation が動作して、コードとして書いた設定ファイルをもとに AWS リソースを作成してくれます。
template.yaml 内の Resources の欄で今回つくる Lambda、そして API Gateway を定義しています。

CircleCI 側の準備

GitHub リポジトリとの連携

CircleCI は GitHub リポジトリにコードが push された際に自動デプロイを走らせることができるので、該当のリポジトリと CircleCI を予め連携しておきます。
(GitHub アカウントで CircleCI にサインインしていれば下記のように最初から連携されているはずです。)

また、予め作成しておいた GitHub のリポジトリ情報が連携されているはずです。
今回は "circleci-sam-orb" というリポジトリを作成したので [Set Up Project] を選択します。

忘れないうちに [Start Building] も実行しておきましょう。

これでリポジトリに main ブランチでコードが push されたタイミングでビルドが自動的に走るようになります。

環境変数の設定

CircleCI 側から AWS へデプロイするにあたり、予め用意しておいた AWS アクセスキー、シークレットキーを使うことになりますが、これらは絶対に外に漏らしてはいけない情報です。
そんな時、CircleCI ではその値を 環境変数 として登録しておき、使いたい時に使うことができるので便利です。

CircleCI 上で Project ごとに環境変数を設定することもできますが、Project 横断で環境変数を設定できる Context という機能を使ってみたいと思います。
(参考:コンテキストの使用)

CircleCI のコンソール上で [Organization Settings]→[Contexts]→[Create Context] と選択していきます。
ここで環境変数のセットを作成し、Context Name を使って環境変数を呼び出すことができるようになります。
設定ファイルを部分的に使いまわしやすくなるのでオススメです。

Context Name は Orbs の公式ドキュメントに合わせて "aws-credentials-context" と設定しておきます。
入力できたら [Create Context] を選択します。

作成できたら aws-credentials-context 内で [Add Environmental Variable] を選択し、以下 3 つの環境変数を設定していきます。

  • AWS_ACCESS_KEY_ID : メモした値
  • AWS_SECRET_ACCESS_KEY : メモした値
  • AWS_DEFAULT_REGION : 使いたいリージョン (東京なら "ap-northeast-1")

完了したらこのような画面になっているはずです。

S3 バケットの作成

CircleCI による自動テスト、自動デプロイ時に関連ファイルをアップロードするために使われる S3 バケットを作成しておきます。
今回は以下の 2 つのバケットを作成しましたが、皆さんの AWS アカウントで作成したバケット名に適宜読み替えてください。

  • テスト用:circleci-sam-orb-test
  • デプロイ用:circleci-sam-orb-prod

CircleCI 設定ファイルの作成

CircleCI ではプロジェクトのルートディレクトリに .circleci/config.yml という設定ファイルを作成し、それに基づき CI/CD が動作します。
そのため、ここでは hello-sam ディレクトリの中に .circleci というディレクトリ、さらにその中に config.yml というからファイルを作成しておいてください。

さて、今回は Orbs 公式ドキュメント の例から build_test_deploy を参考に、config.yml の中身を次のように記述します。

version: '2.1'
orbs:
  sam: circleci/[email protected]
jobs:
  test_my_api:
    docker:
      - image: 'cimg/node:lts'
    steps:
      - run: echo "Run your API tests here"
workflows:
  test_and_deploy:
    jobs:
      - sam/deploy:
          context: aws-credentials-context
          name: deploy-staging
          s3-bucket: circleci-sam-orb-test
          stack-name: staging-stack
          template: ./template.yaml
      - test_my_api:
          requires:
            - deploy-staging
      - sam/deploy:
          context: aws-credentials-context
          name: deploy-production
          s3-bucket: circleci-sam-orb-prod
          requires:
            - test_my_api
          stack-name: production-stack
          template: ./template.yaml

ポイントとしては Context として先ほど作成した aws-credentials-context を指定していることと、
s3-bucket としてテスト、デプロイフェーズそれぞれ用に作成した S3 バケットを指定していることですね。

ここまで出来たら準備は完了です。

自動デプロイの実行

GitHub リポジトリへコードをすべて push

CircleCI では、デフォルトでは main ブランチでコードの更新があったことを検知して CI/CD が走ります。
ブランチは main を指定し、用意しておいたリポジトリに .circleci/config.yml ごと push しましょう。

$ git add .
$ git commit -m "first commit"
$ git branch -M main
$ git push -u origin main

正常に push できれば後は勝手に自動デプロイまで行ってくれます。

CircleCI ダッシュボードで確認

CircleCI のコンソールに戻り [Dashboard] からジョブのステータスを確認出来ます。

時間が経過するごとにジョブのステータスが変わっていき、すべて緑のチェックマークがついて "Success" と表示されれば、全ジョブが問題なく完了したことを表しています。

デプロイした API を叩く

それでは実際にデプロイした API を叩いてみましょう。
curl コマンドで URL へアクセスしてみます。

CircleCI 上で、先ほどのデプロイ結果のうち [deploy-production] を選択します。

各 Step の一覧が表示されるので [Deploy SAM application] を選択して展開します。

ここからは CircleCI ではなく AWS SAM の領域ですが、ここには "sam deploy" コマンドを実行した時と同じ結果が表示されています(実際、中でそういった動作が行われています)。
Outputs 内の HelloWorldAPI 欄に API Gateway のエンドポイント URL が表示されているはずなので、メモします。

なお「************」と表示されている項目は API Gateway がデプロイされたリージョン名が入っていますが、CircleCI で環境変数として設定している値であるため自動でマスクされています。
今回は "ap-northeast-1" を指定しているので、自分で補完してあげましょう。

この URL へ curl コマンドでアクセスしてみましょう。

$ curl https://uatlffehm2.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
{"message": "hello world"}

AWS SAM の HelloWorld テンプレートの指定通り JSON 形式でメッセージが返ってきたことを確認できました。
無事にデプロイ完了です!お疲れさまでした。

おわりに

CircleCI Orbs を使うことで config.yml の記述量が少なく済み、全体的に簡潔にデプロイできるようになると実感しました。
実際、今回使った Orbs のソースをみると config.yml は 500 行以上 となっているため、それが 30 行程度 で済むというのは非常に強力です。

正直、中身がブラックボックスになってエラー時に対応できないのではないかという心配も少しありましたが、各 Orbs のドキュメント内で中身を確認できるため、実行結果を読みながらトラブルシューティングすることも容易でした。
(実際、試している際に S3 バケットの設定まわりでエラーが出ていましたが、問題なくトラブルシューティングすることができました)

個人的な見解ですが、はじめて CircleCI を使う人は Orbs で記述量が少ないところからスタートし、やりたいことが増えるにつれて少しずつカスタマイズしていくのもよいかもしれません。
せっかく公式で用意されている便利なセットですので、活用してみましょう。