ようへいの日々精進XP

よかろうもん

CircleCI から Serverless Framework で実装したアプリケーションを \\デプローイ// する #jawsug #jawsugfuk #circlecijp

tl;dr

社内に Serverless Framework を使って実装したサーバーレスアプリケーションが増えつつあるので, 福岡で最も CircleCI を愛しているであろう企業で働く者として, CircleCI から Lambda に \デプローイ// してみることにしました. あくまでも, この記事は三ヶ月後の自分に向けての内容が多く含まれており, もしかすると, 全く以て汎用的ではないかもしれません.

この記事は JAWS-UG 福岡 もくもく会 #33 〜愛サンサンと〜 で書きました.

jaws-ug-kyushu.doorkeeper.jp

\\デプローイ//

\\デプローイ// したいアプリケーション

要件

  • deployment/xx というブランチがついている時だけ, Lambda にデプロイする
  • 上記以外はテストのみ実行

.circleci/config.yml

.circleci/config.yml は以下のように書きました.

---
version: 2.1
jobs:
  build:
    docker:
      - image: circleci/python:3.6.8-jessie

    working_directory: ~/work

    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "requirements.txt" }}
            - v1-dependencies-
      - run:
          name: install dependencies
          command: |
            python3 -m venv venv
            . venv/bin/activate
            make init
      - save_cache:
          paths:
            - ./venv
          key: v1-dependencies-{{ checksum "requirements.txt" }}
      - run:
          name: run tests
          command: |
            . venv/bin/activate
            make test

      - store_artifacts:
          path: test-reports
          destination: test-reports

  deploy:
    docker:
      - image: circleci/python:3.6.8-jessie

    working_directory: ~/work

    steps:
      - checkout
      - run:
          name: Install Dependency Package (nodejs, npm)
          command: |
            curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -
            sudo apt-get install -y nodejs
      - restore_cache:
          keys:
          - v1-dependencies-{{ checksum "package.json" }}
          - v1-dependencies-
      - run:
          name: Install Serverless Framework
          command: |
            sudo npm install -g serverless
      - run: 
          name: Install Serverless Framework's some plugins
          command: |
            npm install
      - save_cache:
          paths:
            - node_modules
          key: v1-dependencies-{{ checksum "package.json" }}
      - run:
          name: Deploy
          command: |
            case ${CIRCLE_BRANCH} in
              "deployment/dev" )
                DEPLOY_STAGE="dev" ;;
              "deployment/production" )
                DEPLOY_STAGE="prd" ;;
            esac
            sls deploy --stage=${DEPLOY_STAGE}

workflows:
  pr-build:
    jobs:
      - build:
          filters:
            branches:
              ignore:
                - /deployment\/.*/
  dev-deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only:
                - deployment/dev
  prd-deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only:
                - deployment/prd

長めの YAML ですが, とてもシンプルな内容になりました. Docker イメージは色々あって circleci/python:3.6.8-jessie を使っています. make コマンドで実行している部分を適宜読み替えて頂くことで, Python を使った他のプロジェクトにも流用は可能です. 今回の Makefile は以下のような内容です.

help: ## ヘルプを表示する
    @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

init: ## pip install を実行
    @npm install && python3 -m venv venv && . venv/bin/activate && pip install -r requirements.txt

deploy-dev: ## sls deploy を実行 (dev 環境)
    @sls deploy --stage=dev -v

deploy-prd: ## sls deploy を実行 (prd 環境)
    @sls deploy --stage=prd -v

test: ## pytest を実行
    @python3 -m venv venv && . venv/bin/activate && pytest -p no:warnings

yamllint: ## yamllint を実行
    @python3 -m venv venv && . venv/bin/activate && yamllint serverless.yml

invoke-dev: ## sls invoke を実行 (dev 環境)
    @sls invoke --stage=dev --function=rotation --path=invoke.json

invoke-prd: ## sls invoke を実行 (dev 環境)
    @sls invoke --stage=prd --function=rotation --path=invoke.json

invoke.json は関数に動的に渡したい JSON を設定しています.

AWS の認証情報

Lambda へのデプロイ, AWS の各種リソースを操作する為に IAM ユーザーをこさえました. ポリシー主に以下のようなポリシーを適用しています.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": [
        "apigateway:GET",
        "apigateway:POST",
        "apigateway:PUT",
        "apigateway:DELETE",
        "cloudformation:CreateStack",
        "cloudformation:Describe*",
        "cloudformation:ValidateTemplate",
        "cloudformation:UpdateStack",
        "cloudformation:List*",
        "ec2:CreateNetworkInterface",
        "ec2:Describe*",
        "ec2:DeleteNetworkInterface",
        "iam:GetRole",
        "iam:PassRole",
        "iam:CreateRole",
        "iam:DeleteRole",
        "iam:CreateServiceLinkedRole",
        "iam:DetachRolePolicy",
        "iam:PutRolePolicy",
        "iam:AttachRolePolicy",
        "iam:DeleteRolePolicy",
        "lambda:*",
        "s3:CreateBucket",
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:GetBucketLocation",
        "s3:ListBucket",
        "s3:PutObject",
        "s3:DeleteBucket",
        "s3:GetEncryptionConfiguration",
        "s3:PutEncryptionConfiguration",
        "logs:Describe*",
        "logs:CreateLogGroup",
        "logs:DeleteLogGroup",
        "events:PutRule",
        "events:DescribeRule",
        "events:PutTargets"
      ],
      "Resource": "*"
    }
  ]
}

もしかしたら, これでは冗長かもしれません (apigateway:* あたりは不要かも) し, 不足しているかもしれません. Serverless Framework によって作成される AWS リソースによって調整する必要があると思います. 特筆すべきなのは, 今回は VPC 内で Lambda を動かす為, ec2:* 周りを設定している点です.

Lambda Layers

CircleCI を離れて, Serverless Framework の話です.

サードパーティー製のパッケージを毎回 zip で固めてアップロードする生活を送っていましたが, Lambda Layers に切り替えました. また, Python の場合, serverless-python-requirements という requirements.txt に書かれたパッケージをバンドルしてくれる便利プラグインが個人的なデファクトですが, このプラグインが Lambda Layers に対応していて, 以下のように設定するだけでした.

...
custom:
  pythonRequirements:
    dockerizePip: 'non-linux'
    layer: true
...

macOS 上で開発やデプロイを進める場合には, dockerizePiptrue にしておいて Docker を使ってパッケージをビルドさせる必要があります.

Docker イメージの妥協

circleci/python:3.6.8-jessie ではなく, circleci/python:3.7.1-stretch を使いたかったんですが, elasticsearch-curator という Python モジュールをビルドしようとして失敗してしまいました. なぜだろう.

以上

あとは

適当なブランチ名でコードを修正して Github に push すればテストが動いて, deployment/xx にマージすれば Lambda にデプロイされるはずです.