はじめに
この連載ではAWSCDKを用いたaws環境構築の基本を学びます。
前回はawscdkを使用してECS環境を構築しました。
今回はcodepiplineをcdkで構築し、lambda関数の自動デプロイ環境を構築しましょう。
連載記事一覧
初期処理
開発用のディレクトリを作成し、ディレクトリ内でcdkの雛形を作成しましょう。
$ mkdir cdk-pipline $ cd cdk-pipline $ cdk init --language=typescript
次に、必要な依存ライブラリをインストールしましょう。
$ npm install @aws-cdk/aws-codebuild @aws-cdk/aws-codecommit @aws-cdk/aws-codepipeline @aws-cdk/aws-codepipeline-actions @aws-cdk/aws-s3 @aws-cdk/aws-lambda @aws-cdk/aws-codedeploy
色々めんどくさくなるので、今回はテストは考えず、「test/cdk-pipline.test.ts」は削除しておきましょう。
Lambda Stack
Lambda Stackを作成し、lambda関数と実行環境の定義を行いましょう。
実行されるlambda関数の定義
まず、「cdk-pipline」ディレクトリ直下に「lambda」ディレクトリを作成し、そこにindex.jsを以下の内容で作成します。
// file: lambda/index.js exports.handler = async (event) => { // TODO implement const response = { statusCode: 200, body: JSON.stringify('Hello from Lambda!!'), }; return response; };
また.gitignoreに以下を追記して、index.jsがgitの管理対象に含まれる様にしましょう。
!*index.js
Lambda Stackの作成
libディレクトリ配下にlambda-stack.tsを作成しLambdaStackの定義をしましょう。
// file: lib/lambda-stack.ts import codedeploy = require('@aws-cdk/aws-codedeploy'); import lambda = require('@aws-cdk/aws-lambda'); import { App, Stack, StackProps } from '@aws-cdk/core'; export class LambdaStack extends Stack { public readonly lambdaCode: lambda.CfnParametersCode; constructor(app: App, id: string, props?: StackProps) { super(app, id, props); this.lambdaCode = lambda.Code.cfnParameters(); const func = new lambda.Function(this, 'Lambda', { code: this.lambdaCode, handler: 'index.handler', runtime: lambda.Runtime.NODEJS_8_10, }); const version = func.addVersion(new Date().toISOString()); const alias = new lambda.Alias(this, 'LambdaAlias', { aliasName: 'Prod', version, }); new codedeploy.LambdaDeploymentGroup(this, 'DeploymentGroup', { alias, deploymentConfig:codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE, }); } }
Pipeline Stack
Pipeline Stackを作成し、ソースコードをgitにpushした際に自動でビルド・デプロイが走る仕組みを作りましょう。
Pipeline Stackの作成
libディレクトリ配下のcdk-pipline.tsファイルを以下の様に編集しましょう。
// lib/cdk-pipeline-stack.ts import codebuild = require('@aws-cdk/aws-codebuild'); import codecommit = require('@aws-cdk/aws-codecommit'); import codepipeline = require('@aws-cdk/aws-codepipeline'); import codepipeline_actions = require('@aws-cdk/aws-codepipeline-actions'); import lambda = require('@aws-cdk/aws-lambda'); import s3 = require('@aws-cdk/aws-s3'); import { App, Stack, StackProps } from '@aws-cdk/core'; export interface PipelineStackProps extends StackProps { readonly lambdaCode: lambda.CfnParametersCode; } export class PipelineStack extends Stack { constructor(app: App, id: string, props: PipelineStackProps) { super(app, id, props); const repo = codecommit.Repository.fromRepositoryName(this, 'ImportedRepo', 'awscdk-pipline'); const cdkBuild = new codebuild.PipelineProject(this, 'CdkBuild', { buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', phases: { install: { commands: 'npm install', }, build: { commands: [ 'npm run build', 'npm run cdk synth -- -o dist' ], }, }, artifacts: { 'base-directory': 'dist', files: [ 'LambdaStack.template.json', ], }, }), environment: { buildImage: codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_8_11_0, }, }); const lambdaBuild = new codebuild.PipelineProject(this, 'LambdaBuild', { buildSpec: codebuild.BuildSpec.fromObject({ version: '0.2', phases: { install: { commands: [ 'cd lambda', 'npm install', ], }, build: { commands: 'npm run build', }, }, artifacts: { 'base-directory': 'lambda', files: [ 'index.js', 'node_modules/**/*', ], }, }), environment: { buildImage: codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_8_11_0, }, }); const sourceOutput = new codepipeline.Artifact(); const cdkBuildOutput = new codepipeline.Artifact('CdkBuildOutput'); const lambdaBuildOutput = new codepipeline.Artifact('LambdaBuildOutput'); new codepipeline.Pipeline(this, 'Pipeline', { stages: [ { stageName: 'Source', actions: [ new codepipeline_actions.CodeCommitSourceAction({ actionName: 'CodeCommit_Source', repository: repo, output: sourceOutput, }), ], }, { stageName: 'Build', actions: [ new codepipeline_actions.CodeBuildAction({ actionName: 'Lambda_Build', project: lambdaBuild, input: sourceOutput, outputs: [lambdaBuildOutput], }), new codepipeline_actions.CodeBuildAction({ actionName: 'CDK_Build', project: cdkBuild, input: sourceOutput, outputs: [cdkBuildOutput], }), ], }, { stageName: 'Deploy', actions: [ new codepipeline_actions.CloudFormationCreateUpdateStackAction({ actionName: 'Lambda_CFN_Deploy', templatePath: cdkBuildOutput.atPath('LambdaStack.template.json'), stackName: 'LambdaDeploymentStack', adminPermissions: true, parameterOverrides: { ...props.lambdaCode.assign(lambdaBuildOutput.s3Location), }, extraInputs: [lambdaBuildOutput], }), ], }, ], }); } }
一点気をつけて頂きたいのは
const repo = codecommit.Repository.fromRepositoryName(this, 'ImportedRepo','cdk-pipline');
の「cdk-pipline」の部分がソースを取得するリポジトリ名となっていることにご注意ください。
後述しますが、今回はaws code commitに「cdk-pipline」という名前でリポジトリを作成し、こちらからソースを取得・変更の検知を行います。
メインプログラム
最後に、AWSCDKのエントリーポイントとなるMainプログラムを作成しましょう。
binディレクトリ配下のcdk-pipline.tsを以下の様に編集します。
// bin/cdk-pipline.ts #!/usr/bin/env node import { App } from '@aws-cdk/core'; import { LambdaStack } from '../lib/lambda-stack'; import { PipelineStack } from '../lib/cdk-pipline-stack'; const app = new App(); const lambdaStack = new LambdaStack(app, 'LambdaStack'); new PipelineStack(app, 'PipelineDeployingLambdaStack', { lambdaCode: lambdaStack.lambdaCode, }); app.synth();
ここまでで一通りの実装は完了しました。
最後に以下のコマンドでビルドを行いましょう。
$ npm run build
ビルドが完了しましたら、ローカルリポジトリにコミットしておきましょう。
$ git add . $ git commit -m "first commit"
CodeCommitへのソースコードアップ
AWS CodeCommitに「cdk-pipline」という名称でリポジトリを作成し、 これまでに作成したソースコードをPushしてください。
$ git remote add origin https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/cdk-pipline $ git push --set-upstream origin master
AWS環境へ反映
では今までに作成したlambdaとcodepiplineを実際にAWSに反映させてみましょう。
$ cdk deploy PipelineDeployingLambdaStack
作成に成功しましたら、lambda関数が作成されていることや、適当にREADME.mdあたりを変更してcommit→pushした際に自動でデプロイが走っていることをcodepiplineのawsコンソールから確認してみましょう。
削除は以下の感じで。
$ cdk destroy PipelineDeployingLambdaStack
まとめ
今回はcodepiplineをcdkで構築し、lambda関数の自動デプロイ環境を構築しました。
次回は未定。