LoginSignup
37
24

More than 5 years have passed since last update.

serverless framework + CodePipeLine + CodeBuildを使ってサーバーレスでCI/CDする

Posted at

概要

以下のようなアーキテクチャを作っていきます。アプリケーション自体は、LambdaからSlackBotに通知させるだけの単純なものです。

GitHubのプッシュをトリガーに、CodePipeLine + CodeBuildでビルドとテストを行なって、OKだったら開発環境にデプロイします。手動承認後、本番環境にデプロイするような流れです。AWSの新アイコンを使ってみたかったので使っています。個人的には、新アイコンの方が、スタイリッシュでかっこいいけど、旧アイコンの方が見やすいかなと思います。
スライド1.JPG

serverless frameworkのプロジェクトを作成

①GitHubのリポジトリとserverless frameworkのプロジェクトの作成

まずは、適当なGitHubのリポジトリを作成します。その後、serverless frameworkを使って、プロジェクトを作成します。axiosを使ってPOSTリクエストを行うため、npmでインストールしておきます。

serverless create -t aws-nodejs -p sample-slack-app
npm init
npm install --save axios

②serverless.ymlを修正

以下のようにserverless.ymlに記述します。内容としては、API GatewayとLambda関数を作成しています。

serverless.yml
service: serverless-qiita-advent-sample


custom:
  defaultStage: dev

provider:
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage, self:custom.defaultStage}
  region: ap-northeast-1
  environment: 
    SLACK_WEBHOOK_URL: https://hooks.slack.com/services/.............

package:
  exclude:
    - .git/**

functions:
  postSlack:
    handler: handler.postSlack
    events:
      - http:
          path: slack
          method: post
          cors: true

③handler.jsを修正

SlackBotに通知させるプログラムを書きます。node v8.10を使っています。

handler.js
'use strict';

const axios = require('axios');

module.exports.postSlack = async (event, context) => {

  const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL;
  const message = "テストだよ";

  try {
    //リクエスト送信
    await axios.post(slackWebhookUrl, { text: message });
    return {
      statusCode: 200,
      body: JSON.stringify({
        message: 'Slackの通知に成功しました',
        input: event,
      }),
    };
  } catch (err) {
    console.log(err);
    return {
      statusCode: 400,
      body: JSON.stringify({
        message: 'Slackの通知に失敗しました。',
        input: event,
      }),
    };
  }
};

動作確認のため、一度AWSへデプロイして、Lambdaを実行してみます。

$ sls deploy
Serverless: WARNING: Missing "tenant" and "app" properties in serverless.yml. Without these properties, you can not publish the service to the Serverless Platform.
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (1.99 KB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............
Serverless: Stack update finished...
Service Information
service: serverless-qiita-advent-sample
stage: dev
region: ap-northeast-1
stack: serverless-qiita-advent-sample-dev
api keys:
  None
endpoints:
  POST - https://1xxxxxxxxxxxxxxxxxxxxxxx.ap-northeast-1.amazonaws.com/dev/slack
functions:
  postSlack: serverless-qiita-advent-sample-dev-postSlack

sls invoke -f postSlack
{
    "statusCode": 200,
    "body": "{\"message\":\"Slackの通知に成功しました\",\"input\":{}}"
}

SlackにPostしているのを確認できました。
slack.png

AWSでCI/CD環境を構築する

今回の主題である、CodePipeline + CodeBuildを用いて、AWS上にCI/CD環境を構築していきます。

CodepPipeLineの作成

パイプラインプロジェクトの作成

AWSコンソール上でCodePipeLineを選択します。パイプラインの作成ボタンがあるのでそれをクリックします。まずはパイプラインの設定をしていきます。以下のように設定します

パイプライン名: serverless-qiita-advent-sample-pipeline
サービスロール: 新しいロール
アーティファクトストア: デフォルトの場所

codepipeline2.png

ソースステージの設定

次に、ソースステージの設定をしていきます。ソースステージとはソースコードを取得するステージです。今回はGitHubのPushをトリガーにするので、GitHubを選択します。他にもAWSのCodeCommitやS3などを設定することもできます。GitHubに接続し対象となるリポジトリとブランチを選択します。

ソースプロバイダ: GitHub
リポジトリ: 自分のリポジトリ
ブランチ: master
変更検出オプション: GitHubウェブフック

codepipeline3.png

ビルドステージの設定

ビルドステージの設定をしていきます。ビルドステージとは実際にビルドやテストが行われる場所で、今回はAWS CodeBuildを選択します。他にもJenkinsやSolano CIなども選択することができます。
CreateProjectをクリックするとCodeBuildの設定画面が小画面で表示されます。今回はDev環境とProd環境でビルドを分けるためBuildSpec名を変更しています。

codepipeline4.png

プロジェクト名: serverless-qiita-advent-sample-dev-build
環境イメージ: マネージド型イメージ
オペレーティングシステム: Ubuntu
ランタイム: Node.js
ランタイムバージョン: aws/codebuild/nodejs:8.11.0
イメージのバージョン: このランタイムイメージには常に最新のイメージを使用してください
サービスロール: 新しいサービスロール
ロール名: codebuild-serverless-qiita-advent-sample-build-service-role
ビルド仕様: buildspecファイルを使用する
BuildSpec名: buildspec-dev.yml

codepipeline5.png
codepipeline6.png

CodeBuildプロジェクトを作成するとCodePipelineプロジェクトに戻り、先程作成したCodeBuildプロジェクトが自動で選択されています。

codepipeline7.png

CodeBuildのIAMロールのポリシーの設定

今回はCodeBuildからserverless frameworkを用いてデプロイを行うため、先ほど作ったIAMロールにポリシーを追加しておかないと各種サービスへのデプロイ時にアクセス拒否されてしまいます。先程作ったcodebuild-serverless-qiita-advent-sample-build-service-roleのIAMロールにポリシーをアタッチします。

AWSのコンソールからIAMを選択し、IAMロールタブに移動し、codebuild-serverless-qiita-advent-sample-build-service-roleがあると思うのでそれを選択します。

qiita11.png

serverless frameworkでデプロイをするのでかなりポリシーを許可しないといけません。あまりよろしくはないですが、AdministratorAccessのポリシーをアタッチします。本当ならserverless frameworkで使用するものだけを許可するのがいいです。

qiita12.png

デプロイステージの設定

次にデプロイステージですが、CodeBuild上でserverless frameworkを使用してデプロイを行うため、今回は使用しません。EC2などにソースコードをデプロイするような場合はここで設定することができます。今回はスキップを選択します。

codepipeline8.png

入力した内容を確認して、パイプラインの作成をします。

codepipeline9.png

パイプラインの作成をすると実際にパイプラインが動き始めて、実際にGithubから最新のソースの取得とビルドが始まります。ソースの取得はSucceedになると思いますが、ビルドはFaildになると思います。これはまだGitHubのリポジトリにBuildSpecファイルを含めていないためです。

codepipeline10.png

buildspecファイルと単体テストの作成

buildspecファイルの作成

先程作ったserverless frameworkのプロジェクトのルートにbuildspecファイルを作成します。今回はDev環境とProd環境用の2つを作ります。

buildspec-dev.yml
version: 0.2

phases:
  install:
    commands:
       - npm install -g serverless
       - npm install     
  build:
    commands:
       - npm test
       - sls deploy
buildspec-prod.yml
version: 0.2

phases:
  install:
    commands:
       - npm install -g serverless
       - npm install     
  build:
    commands:
       - npm test
       - sls deploy --stage prod

2つ作りますが、違いはsls deployの--stateオプションの違いぐらいです。

単体テストの作成

次にLambda関数の簡単な単体テストを書いていきます。この程度のアプリケーションだと単体テストはあまり意味がないですが、実際にテスト→ビルドを実行するためにサンプルとして書いていきます。Lambda関数のテストを書くためには、Eventオブジェクトのモックが必要になります。今回はAPI Gatewayを使用しているためAPIGateway用のEventオブジェクトのモックを作成します。また、axiosも使用しているためaxiosのモックも作成します。今回はaws-event-mocksaxios-mock-adapterというOSSのモックライブラリを使用します。
まずは、単体テストを行うために、必要なライブラリのインストールをします。

npm install axios-mock-adapter aws-event-mocks chai mocha --save-dev

次にtestフォルダを作成し、単体テストを書いていきます。

test/test-lambda-function.js
"use strict";

const chai = require('chai');
const createEvent = require('aws-event-mocks');
const handler = require('../handler');
const should = chai.should();

// axios mock
const axios = require('axios');
const MockAdapter = require('axios-mock-adapter');
var mock = new MockAdapter(axios);

// event mock
const event = createEvent({
  template: 'aws:apiGateway',
  merge: {}
});

describe('Lambda function test', () => {

  beforeEach(function() {

    process.env.SLACK_WEBHOOK_URL = "test"

  });

  it('postリクエストに成功したら、ステータスコード200', async () => {
    mock.onPost().reply(200);
    const res = await handler.postSlack(event, null);
    res.statusCode.should.equal(200); 
  });

  it('postリクエストに失敗したら、ステータスコード400', async () => {
    mock.onPost().reply(500);
    const res = await handler.postSlack(event, null);
    res.statusCode.should.equal(400); 
  });
});

npmで単体テストを実行するため、package.jsonに追記します。

package.json
"scripts": {
    "test": "mocha --timeout 10000 ./test/*"
  }, 

単体テストが通るかどうかテストを実行してみます。

$ npm test

> serverless-qiita-advent-sample@1.0.0 test C:\Users\sato14\github\serverless-qiita-advent-sample
> mocha --timeout 10000 ./test/*



  Lambda function test
    √ postリクエストに成功したら、ステータスコード200
    √ postリクエストに失敗したら、ステータスコード400


  2 passing (12ms)

通りました。

GitHubにプッシュしてCodePipeLineを動作させる

実際にGitHubのmasterにプッシュしてCodePipeLineを動作させてみます。

git add -A
git commit -m "buildspecファイルとLambda関数の単体テストの作成"
git push origin master

動作してソースステージ、ビルドステージともにSucceedになりました。
qiita13.png

CodeBuildのログでも単体テスト実行後にserverless frameworkでのデプロイが確認できました。
qiita14.png

承認プロセスの作成とProd環境用ビルドの作成

ここまでで、Dev環境用のビルドとデプロイができましたので、今度は承認プロセスとProdのビルド環境を作成します。

承認プロセスの作成

先程作ったCodePipeLineのプロジェクトを開きます。開いたら編集ボタンがあると思いますので、クリックします。

編集モードになりますので、一番下のステージの追加でステージ名:Prodとして追加します。追加後、ステージの編集をクリックしステージの編集モードにしアクショングループの追加をクリックします。

以下の内容にし保存をクリックします。他にもSNSトピックで承認プロセスを通知したり、承認するためのレビュー用のURLなどを設定することもできます。今回は空白で設定します。

qiita20.png

次にもう一度アクショングループの追加をクリックし、CodeBuildの新プロジェクトを作成します。以下の画像の内容で作成します。Dev環境と違うのは一箇所だけでBuildSpec名がbuildspec-prod.ymlになっています。本当ならブランチも分けたいところですが、今回はmaster一本でやっています。

qiita22.png
qiita23.png

その後以下を設定してパイプラインを保存します。
入力アーティファクト: SourceArtifact
出力アーティファクト: ProdBuildArtifact

最終的には以下のようなパイプラインになっています。
qiita28.png

手動承認してみる

パイプラインを動かすために適当にソースコードを修正してGitHubにプッシュします。Dev環境のビルドとデプロイが終わったタイミングで承認プロセスのところで承認待機状態になります。
qiita26.png
確認というボタンがでてるはずなので、押すとコメントを入力する小画面がでます。そこで適当なコメントを書いて承認を完了します。完了し、Prod環境のビルドとデプロイが走ってSucceedになればOKです。

まとめ

今回初めてアドベントカレンダーに参加しました。CodePipeLineとCodeBuildはAWSの中でもあまり触れていないサービスであったため、これを気に勉強をしようと思い、今回参加しました。触ってみた感想としてはあまりハマることもなくCI/CDを構築できたなーという印象です。出たばかりのときにちょっと触ったときはまだ環境が整っていなかったため、難しい印象があったんですが、UIが刷新され、Codeシリーズが一つの管理画面に収まったことでCodeシリーズの連携がスムーズにできるようになったと思います。

この記事のソースコードは以下のリポジトリに入っています。
https://github.com/briete/serverless-qiita-advent-sample

37
24
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
37
24