linebot-serverless-blueprint-javaの作り方(その1)

linebot-serverless-blueprint-javaを作った!で紹介したLINE BOTの雛形の作り方を何回かに分けて紹介します。

環境

  • Windows 10
  • Node: v6.9.1
  • npm: 3.10.8
  • Git for Windows: 2.9.3.windows.2
  • Java 8
  • AWSアカウント(AdministratorAccess権限)
    ※rootアカウントはやめましょう

前準備~serverlessの導入

プロジェクト用フォルダーの作成

mkdir linebot-serverless-blueprint-java
cd linebot-serverless-blueprint-java

package.jsonの作成

npm init -y
package.json
{
  "name": "linebot-serverless-blueprint-java",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

serverlessのインストール

npm install serverless --save-dev

“devDependencies”が追加されます。

package.json
{
  "name": "linebot-serverless-blueprint-java",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "serverless": "^1.8.0"
  }
}

serverlessプロジェクトの作成

node_modules.binserverless create -t aws-java-gradle

なぜかgradlewrappergradle-wrapper.jarが作成されませんので、個別にダウンロードします。
https://github.com/serverless/serverless/blob/master/lib/plugins/create/templates/aws-java-gradle/gradle/wrapper/gradle-wrapper.jar
こんな感じになります。

C:.
│  .npmignore
│  build.gradle
│  gradlew
│  gradlew.bat
│  package.json
│  serverless.yml
│  
├─gradle
│  └─wrapper
│          gradle-wrapper.jar
│          gradle-wrapper.properties
│          
├─node_modules
~~~省略~~~
│              
└─src
    └─main
        ├─java
        │  └─com
        │      └─serverless
        │              ApiGatewayResponse.java
        │              Handler.java
        │              Response.java
        │              
        └─resources
                log4j.properties

Javaプロジェクトのフォルダー構成の変更

今回はJavaプロジェクトを2つ用意しますので、フォルダー構成を変更します。

srcフォルダーとbuild.gradleをサブフォルダー内に移動

mkdir webhook
move src webhook
        1 個のディレクトリを移動しました。
move build.gradle webhook
        1 個のファイルを移動しました。

もう一つのJavaプロジェクト用ファイルもコピーして作成

xcopy /E /I webhook reply
webhookbuild.gradle
webhooksrcmainjavacomserverlessApiGatewayResponse.java
webhooksrcmainjavacomserverlessHandler.java
webhooksrcmainjavacomserverlessResponse.java
webhooksrcmainresourceslog4j.properties
5 個のファイルをコピーしました

settings.gradleファイルの作成

type nul > settings.gradle
settings.gradle
include 'webhook','reply'

Gradle動作確認

gradlew projects
------------------------------------------------------------
Root project
------------------------------------------------------------

Root project 'linebot-serverless-blueprint-java'
+--- Project ':reply'
--- Project ':webhook'

To see a list of the tasks of a project, run gradlew <project-path>:tasks
For example, try running gradlew :reply:tasks

BUILD SUCCESSFUL

Total time: 7.01 secs

build.gradleファイルの修正

webhook/build.gradleを少し修正します。reply/build.gradleも同様(baseNameはreply)に修正します。

webhook/build.gradle
apply plugin: 'java'

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

+tasks.withType(AbstractCompile)*.options*.encoding = tasks.withType(GroovyCompile)*.groovyOptions*.encoding = 'UTF-8'

dependencies {
    compile (
        'com.amazonaws:aws-lambda-java-core:1.1.0',
        'com.amazonaws:aws-lambda-java-log4j:1.0.0',
+        'com.amazonaws:aws-java-sdk-dynamodb:1.11.98',
+        'com.linecorp.bot:line-bot-api-client:1.6.0',
    )
    testCompile 'junit:junit:4.12'
}

// Task for building the zip file for upload
task buildZip(type: Zip) {
    // Using the Zip API from gradle to build a zip file of all the dependencies
    //
    // The path to this zip file can be set in the serverless.yml file for the
    // package/artifact setting for deployment to the S3 bucket
    //
    // Link: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html

    // set the base name of the zip file
-    baseName = "hello"
+    baseName = "webhook"
    from compileJava
    from processResources
    into('lib') {
        from configurations.runtime
    }
}

build.dependsOn buildZip

以上で下準備は完了です。
次回はServerlessの設定やJavaプログラムについての解説を行いたいと思います。

続きを読む

Laravel+Rocketeer+CircleCIで、githubからAWS(EC2)サーバにデプロイを自動化できたので、記録しておく。

まずはこのサイトを参照。

http://webmake.info/laravelrocketeercircleci%E3%81%AE%E6%A7%8B%E6%88%90%E3%81%A7aws%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B/

基本的に↑のサイトに書いてある通りにやればできる。

ありがたや、ありがたや。

若干ハマったところ。3つ。
・その1 
  .rocketeer/config.phpの設定。
 
’connections’ => [
‘production’ => [
‘host’ => ‘AWSのEC2 サーバ情報のEIP(接続先IP)’,
‘username’ => ‘SSH ユーザのユーザー名’,
‘password’ => ‘SSH ユーザのパスワード’,
‘key’ => ‘SSH ユーザのSSH-key’,
‘keyphrase’ => ”,
‘agent’ => ”,
‘db_role’ => true,
],
],
 
 ’production’というラベル?が何かよく分からないが、
  config.phpの
 ’default’ => [‘production’],
  と合わせる必要があるのだろう。
他の名前でも多分いいのだろう。

・その2 
   4.デプロイのテスト
   で、実行すると、
   「The following extensions could not be found: mcrypt」
   というエラーが出た。
   エラーを解消せず無視してもデプロイはできた。

・その3
   5. デプロイのシェルファイルを作成する

      AWS_ACCESS_KEY_ID
      AWS_SECRET_ACCESS_KEY
      MYSECURITYGROUP

これらは自分で作成する必要がある。
https://aws.amazon.com/jp/
ここから管理画面にサインアップし、
ナビゲーションバーの「サービス>IAM」を選択
左メニューの「ユーザー」を選択
新規ユーザーを作成すると、「アクセスキー」と「シークレットキー」が取得できる。
また、作成したユーザーに権限を付与する必要がある。
ポリシーのアタッチで、新規作成したユーザーに対してAdministratorAccessを付与すると、
管理者権限と同様となり、すべての操作権限を付与できる。
 
「セキュリティグループ」については、ナビゲーションバーの「サービス>EC2」を選択
  左メニューの「セキュリティーグループ」から作成できる。

あとは、circle.ymlの記述だが、これも基本的に上記のサイト通りに書けばうまくいく。
———————
deployment:
production:  ←ここは、config.phpと合わせる
branch: master ←ここは、デプロイしたいブランチを書く。masterブランチ以外も指定できる。
commands:
– ./deploy.sh
———————

have a nice day!!

続きを読む

API Gateway+Lambda+Serverless+TravisでAPI開発のCI/CDパイプラインを構築する

概要

スクリーンショット 2017-02-21 10.15.16.png

API GatewayとLambdaを使ったAPI開発時のCI/CDについての記事です。
僕がnode.jsを普段から使用しているため、解説はnodeがベースになっています。

https://github.com/horike37/serverless-api-integration-test-sample
ソースはすべてGitHubに上がってますのでそちらもご確認ください。

CIで実施する内容

以下の内容をCIとして行うことを考えます。

  • ESLintによる構文チェック
  • Mocha, Chaiを使用したユニットテスト
  • APIをAWSへデプロイしてテストを行うインテグレーションテスト

CDで実施する内容

以下のようなルールでデプロイのサイクルを回します。

  • Gitのdevelopmentブランチへのpushをテスト環境へのデプロイと想定。

    • 構文チェックとユニットテストを実施。
    • ビルドが通れば、ServerlessのdevelopmentステージにAPIをデプロイ。
  • Gitのmasterを最新ソースの集約場所として想定して、pushを実施。
    • 構文チェックとユニットテストとインテグレーションテストを実施。
    • ビルドが通ってもデプロイは実施しない。
  • Gitのtagへのpushを本番環境へのデプロイと想定。
    • 構文チェックとユニットテストとインテグレーションテストを実施。
    • ビルドが通れば、ServerlessのproductionステージにAPIをデプロイ。

Continuous Integration

ESLintによる構文チェック

http://eslint.org/
JavaScript界隈ではもっともメジャーな構文チェックツールだと思います。
構文ルールを定義して、構文エラーや記法を統一させることでソースの可読性や品質を向上させます。

https://github.com/horike37/serverless-api-integration-test-sample/blob/master/.eslintrc.js#L2
今回はルールとしてArbnbを採用しました。どの構文ルールを採用するかはチームの好みで決めれば良いと思います。

Mocha, Chaiを使用したユニットテスト

単体テストを実施します。メソッドや関数毎に入力値と出力値をチェックして関数単位での品質チェックを行います。

https://github.com/horike37/serverless-api-integration-test-sample/tree/master/lib
このようにLambdaがメインで実行する部分から、ビジネスロジックをclassに切り出してあげるとテストが書きやすくなるのでそうすることが多いです。

APIをAWSへデプロイしたテストを行うインテグレーションテスト

インテグレーションテストはサーバレスアーキテクチャの特徴とも言えるテストです。

サーバレスアーキテクチャは複数のサービスで構成されているケースが多く、ユニットテストだけではアーキテクチャ全体のテストを網羅できません。実際のクラウド環境へリソースをデプロイして結合テストを行い、すべて成功すれば、それを破棄します。

このデプロイ管理にはServerlessを使用しています。

https://github.com/horike37/serverless-api-integration-test-sample/blob/master/integration-test/test.js#L18
https://github.com/horike37/serverless-api-integration-test-sample/blob/master/integration-test/test.js#L41
テストの最初と最後にアーキテクチャのデプロイと削除を行っています。このメソッドの実体は、sls deploysls removeです。

it('should return correct values from all apis', () => {
     const testEndpoint = `${endpoint}/hello`;

     return fetch(testEndpoint, { method: 'GET' })
       .then(response => response.json())
       .then((json) => expect(json.message).to.equal('Go Serverless v1.0! Your function executed successfully!'));
  });

https://github.com/horike37/serverless-api-integration-test-sample/blob/master/integration-test/test.js#L35-L37
そしてここがテストのメインの部分です。デプロイされたAPIへリクエストを送り、その返り値をchaiでチェックを行います。
こうすることで何本APIを作ったとしても自動テストが可能になるというメリットがあります。

Continuous Delivery

デプロイスクリプト

以下の様なスクリプトでデプロイを実施します。Gitのtagにpushされた際にproduction環境へ。Gitのdevelopmentブランチにpushされた際にdeployment環境へ。それぞれビルドが正しく通ればデプロイされるようになっています。

bin/deploy.sh
#!/bin/bash
set -e
BRANCH=${TRAVIS_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}
if [[ $TRAVIS_TAG ]]; then
  STAGE="production"
elif [[ $BRANCH == 'development' ]]; then
  STAGE="development"
fi

if [ -z ${STAGE+x} ]; then
  echo "Not deploying changes";
  exit 0;
fi

echo "Deploying from branch $BRANCH to stage $STAGE"
npm prune --production  #remove devDependencies
sls deploy --stage $STAGE --region $AWS_REGION

ServerlessのIAM権限

TravisにてServerlessが動作するように専用のユーザを発行します。今回は最低限で以下の様なIAMポリシーを与えています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt543534534",
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:PutRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:PassRole",
                "logs:*",
                "s3:*",
                "lambda:*",
                "cloudformation:*",
                "apigateway:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

そして、TravisからAWSリソースへアクセスするためにcredentialをTravisへ設定します。
もちろん生のcredentialを.travis.ymlへ追記することはご法度です。

Travisのコマンドにより暗号化し、credentialがTravisの環境上でしか使えないようにします。

$ travis encrypt AWS_ACCESS_KEY_ID=xxxxxxxxxxx --add
$ travis encrypt AWS_SECRET_ACCESS_KEY=xxxxxxxxxxx --add

このコマンドにより。.travis.ymlに暗号化した状態でcredentialが設定されます。
これはTravis上でビルドを実行した際に環境変数として設定されます。

Travisの設定ファイル

最終的にはこんな感じに.travis.ymlが仕上がりました。

travis.yml
language: node_js
node_js:
  - '4.3'
env:
  global:
    - AWS_REGION=us-east-1
    - SLS_DEBUG=true
    - secure: lSxO7tZ0c/FA8VL72042dqQZ+tRjsS93iVYxMr1ghP/0tBxdmrhhYdAD9UrSv/Kk+Y1jRlkpQ2uaARHoy+6ZqmhchX32HpIYBQVJ/ntMSgv37gFbrNTOfSFoATMTRy6RT2UKaIWAa4xnmDxaQNFPx4X5l9Y25RdivoR+WXrEPd4eCCTVL/23bABSIySSTs+VGqQIppE4Jw5ibbcSoTLsuj00nK+VrmYHNlTSiEuKIxgFC1Ix0hqayJ/kely0DqYW/CY/vCCf0V4yazJo9fG1EFfrHsSIAKKeGRY7WMnLPJ7hJGwRiVV1/atMx/5kRPKOADcRTfoh3noXS3/sd1hbGjTwnJVRVrYiUocHwuNbo1TpW1On85jXEdvnKY9JYelFEnXnWn6A2bRMhgL/zul/WuSPCGq7HpsGMRhXrEiEYJ9YhnVNiUTaV2amoOClMOpHFnStMfTJVg7NJ8mBF4XOzODvhAzyPFDWdJ94Ejl1LAnGOAp/wBQVbFswKPdwdosFU6LyirQkA4k0q7C4zXYywyQtrY7H9w7FtKo+U4596GQAQtvzQz6GS42c1WBX0fIrMu1VXc+KmwCUBEVmvBxLS7c0DJUI61atDFGq7788K7IMWw83lIFjJULdwv1qU4uBi3MvPm2OHCdRAzBGEYIC87zfcYI/gi41rh3bj/C0wiI=
    - secure: LIh0lkl/t72EbMd47WgEXqnoG3REp+oPhIfDR5Cs8SMO4sacvo2j4pRkRKIwwpdKozxdgLEMl1rwDoHyYPH77FzvDnwiufpaYgQs278wmi+6ZvoC9nhgdn2sT7cFnYuYAO8dC7G/NHzXogAVmiObf3I+hzNLjDqWwWVjqPm41p4P4c2EJUVo0nVlcaUOf8elS1j6zp+ZL1EQo4Fm4IumDgNpZUP4bSq8CcVPvF0ynMlslI8XNMnBOiYmG+644qILScyPK1Q2SPdMLqL5YXHuYfE0aCpFcWOcNZIalBmaxPqFNW+QHQvaYiwoENx/i91KS3U2mqfcNYY4o9viih47PFsaddvtBeB83Wfls7GIZ/XmvBKREuS5Gwhz930DbAvUNQT1ylS9Y6TTIIWIbe3Qmv6ngd9TrDHlnbVhQYgar9ur+TgvLyhs5YLAeLn85c3Z3GYN5JUuCq5bclzh4I+myagaWJPoujS1nT+vKLUW8hu5MkxApn+uFUo/OIW1WG4qho/ddh7RoJbI9oTebWjpXhLwd1pSET1yBVSEetORluh0pW7r5A425Rm80B58Mg/x0NNmM1x6DBrCZ5R8d+Bam6C7P4WxxrRex7vFcqRWeKlJNINO+rtPW3Uuo/s9yC9980uQ00eY0kqhouR8ol6xs/4Xye1AHovPR2unzEe37fM=
before_install:
  - chmod +x ./bin/deploy.sh
  - npm i -g serverless@1.6.1
install:
  - travis_retry npm install
script:
  - npm run lint
  - npm run test
  - if [[ $TRAVIS_BRANCH == "master" ]] || [[ $TRAVIS_TAG ]]; then npm run integration-test; fi
after_success:
  - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
  - ./bin/deploy.sh

実行結果

developmentブランチへのpush

https://travis-ci.org/horike37/serverless-api-integration-test-sample/builds/203491545
ESLintとユニットテストのビルドが成功し、developmentステージへデプロイされています。

masterブランチへのpush

https://travis-ci.org/horike37/serverless-api-integration-test-sample/builds/203489525
ESLintとユニットテストとインテグレーションテストが成功しましたが、デプロイはされていません。

tagへのpush

https://travis-ci.org/horike37/serverless-api-integration-test-sample/builds/203484838

0.1というタグにpushをしています。
ESLintとユニットテストとインテグレーションテストが成功し、puroductionステージにデプロイされています。

如何でしたでしょうか。こんな感じでCI/CDパイプラインを構築することですべてが自動化され品質も担保されるようになりました。よろしければ是非参考にしてみてください!

では、良いパイプラインライフを!

参考文献

CodePipelineでServerless Frameworkのデプロイを管理する

続きを読む

CircleCIからServerless Frameworkのデプロイを実行する

経緯

「APIはServerless FWでデプロイするでー」
「フロントエンドはS3なー」
「devDependenciesのパッケージ増えたから、デプロイ前に消しといてなー」
「間違えて本番にデプロイしないようにステージの確認しっかりね」

_人人人人人人_
> めんどい <
 ̄Y^Y^Y^Y^Y ̄

計画

  • 手でコマンドうつから事故る
  • 開発環境へのデプロイくらい自動化したい
  • CircleCIからsls deployすればええやん

想定

clientディレクトリにS3へアップするファイルが、srcディレクトリ内にServerless FW用のスクリプトが配置されている。

├── serverless.yml
├── ci
│   └── deploy_dev.sh
├── circle.yml
├── client
│   ├── css
│   ├── img
│   ├── index.html
│   └── js
├── src
│   └── api1.js
└── tests

結果

circleci.yml
---
machine:
  node:
    version: 4.3

dependencies:
  override:
    - npm install

test:
  override:
    - npm test
    - npm run lint

deployment:
  dev:
    branch: dev
    commands:
      - ./ci/deploy_dev.sh
ci/deploy_dev.sh
#!/bin/bash
npm install -g serverless
cd ~/${CIRCLE_PROJECT_REPONAME}
npm prune --production
serverless deploy --stage dev --region us-east-1
cd client
aws s3 sync ./ s3://YOUR_BACKET_NAME_HERE --delete --exclude '*.DS_Store'

ハマったところ

Serverless Frameworkはグローバルインストール

devDependenciesでServerless Frameworkを入れてるので、npm prune --productionしてしまうと./node_modules/serverless/bin/serverlessは動かない。
かといってLambdaに同梱する必要もないものなので、npm install -g serverlessで対応しています。

必要に応じてバージョン指定とかもすればいいかなと。

serverless.ymlのあるディレクトリに移動する

cd ~/${CIRCLE_PROJECT_REPONAME}でリポジトリ内のディレクトリに移動しないと以下のようなエラーでCIがコケます。

 Serverless Error ---------------------------------------

     This command can only be run inside a service directory

slsコマンドはリージョンを指定する

CircleCIだとAWS Permissionsでクレデンシャル情報を設定していると思いますが、これだけだとリージョンが指定されてないのでやっぱりデプロイがコケます。

--region us-east-1をつけてやってください。

CircleCIに設定しているIAMロールに要注意

CircleCIに設定しているIAMロールに権限が足りないと、これもまたデプロイ失敗します。
できるだけきっちり設定しておいたほうがいいと思いますが、とりあえずIAMユーザー部分だけ最低限にしたバージョンは以下。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt9999999998000",
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:PutRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:PassRole"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "Stmt9999999999000",
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "lambda:*",
                "cloudformation:*",
                "apigateway:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

終わりに

master or releaseブランチあたりに対しても同様のことを行うことで、本番デプロイの自動化も可能とは思います。
「目視確認後にリリースしたい」とか「リリースのタイミングはこちらで決めたい」という場合は、AWS CodePipelineの手動承認機能とCode Buildで設定してみてください。

参考:https://aws.amazon.com/jp/about-aws/whats-new/2016/07/aws-codepipeline-adds-manual-approval-actions/

続きを読む