LoginSignup
1
0

More than 1 year has passed since last update.

AWS日記15 (Serverless Application Model)

Last updated at Posted at 2020-08-29

はじめに

今回は AWS Serverless Application Model (AWS SAM)を試します。
アクセスすると自己削除するWebページを作成します。
[Lambda関数・SAMテンプレート]
(https://github.com/tanaka-takurou/serverless-application-ephemerality-page-go/tree/minimum)

準備

AWS SAM CLI をインストールします
S3の準備をします

[AWS SAMの資料]
AWS サーバーレスアプリケーションモデル
AWS Serverless Application Model

AWS SAM テンプレート作成

AWS SAM テンプレートで API-Gateway , Lambdaの設定をします。

[参考資料]
AWS SAM テンプレートを作成する

template.yml
template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Serverless Application Ephemerality

Parameters:
  ApplicationName:
    Type: String
    Default: 'ServerlessApplicationEphemeralityMinimum'
  EphemeralityFunctionName:
    Type: String
    Default: 'EphemeralityFunctionMinimum'

Metadata:
  AWS::ServerlessRepo::Application:
    Name: Serverless-Application-Minimum
    Description: 'This application is deleted when accessed.'
    Author: tanaka-takurou
    SpdxLicenseId: MIT
    LicenseUrl: LICENSE.txt
    ReadmeUrl: README.md
    Labels: ['ServerlessRepo']
    HomePageUrl: https://github.com/tanaka-takurou/serverless-application-ephemerality-page-go/tree/minimum
    SemanticVersion: 0.0.2
    SourceCodeUrl: https://github.com/tanaka-takurou/serverless-application-ephemerality-page-go/tree/minimum

Resources:
  EphemeralityApi:
    Type: AWS::Serverless::HttpApi
  EphemeralityFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Ref EphemeralityFunctionName
      CodeUri: bin/
      Handler: main
      MemorySize: 256
      Runtime: go1.x
      Description: 'Front Function'
      Role: !GetAtt EphemeralityFunctionRole.Arn
      Events:
        EphemeralityApi:
          Type: HttpApi
          Properties:
            Path: '/'
            Method: get
            ApiId: !Ref EphemeralityApi
      Environment:
        Variables:
          COUNT: "0"
          LIMIT: "0"
          REGION: !Ref AWS::Region
          STACK_NAME: !Ref AWS::StackName
          FUNCTION_NAME: !Ref EphemeralityFunctionName
  EphemeralityApiPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref EphemeralityFunction
      Principal: apigateway.amazonaws.com
  EphemeralityFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      Policies:
        - PolicyName: KillFunctionPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: 'Allow'
                Action:
                  - 'logs:CreateLogGroup'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: '*'
              - Effect: 'Allow'
                Action:
                  - 'cloudformation:DescribeStackResources'
                  - 'cloudformation:DeleteStack'
                Resource: '*'
              - Effect: 'Allow'
                Action:
                  - 'lambda:*'
                  - 'events:RemoveTargets'
                  - 'events:DeleteRule'
                  - 'iam:DeleteRolePolicy'
                  - 'iam:DeleteRole'
                Resource: '*'
              - Effect: 'Allow'
                Action:
                  - 'apigateway:*'
                Resource: '*'

Outputs:
  APIURI:
    Value: !Join [ '', [ 'https://', !Ref EphemeralityApi, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/'] ]

API-Gatewayの設定は以下の部分

  EphemeralityApi:
    Type: AWS::Serverless::HttpApi

Lambdaの設定は以下の部分

  EphemeralityFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Ref EphemeralityFunctionName
      CodeUri: bin/
      Handler: main
      MemorySize: 256
      Runtime: go1.x
      Description: 'Front Function'
      Role: !GetAtt EphemeralityFunctionRole.Arn
      Events:
        EphemeralityApi:
          Type: HttpApi
          Properties:
            Path: '/'
            Method: get
            ApiId: !Ref EphemeralityApi
      Environment:
        Variables:
          COUNT: "0"
          LIMIT: "0"
          REGION: !Ref AWS::Region
          STACK_NAME: !Ref AWS::StackName
          FUNCTION_NAME: !Ref EphemeralityFunctionName

Lambda関数作成

※ Lambda関数は aws-lambda-go を利用し、cloudformationの周りの処理は aws-sdk-go-v2 を利用しました。

main.go
main.go
package main

import (
	"os"
	"log"
	"context"
	"strconv"
	"net/http"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/aws/external"
	slambda "github.com/aws/aws-sdk-go-v2/service/lambda"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
)

var cfg aws.Config

func HandleRequest(ctx context.Context, request events.APIGatewayV2HTTPRequest) (events.APIGatewayProxyResponse, error) {
	count, _ := strconv.Atoi(os.Getenv("COUNT"))
	limit, _ := strconv.Atoi(os.Getenv("LIMIT"))
	if count < limit {
		client := slambda.New(cfg)
		req := client.GetFunctionConfigurationRequest(&slambda.GetFunctionConfigurationInput{
			FunctionName: aws.String(os.Getenv("FUNCTION_NAME")),
		})
		res, err := req.Send(ctx)
		if err != nil {
			log.Println(err)
		} else {
			env := res.GetFunctionConfigurationOutput.Environment.Variables
			env["COUNT"] = strconv.Itoa(count + 1)
			req_ := client.UpdateFunctionConfigurationRequest(&slambda.UpdateFunctionConfigurationInput{
				FunctionName: aws.String(os.Getenv("FUNCTION_NAME")),
				Environment: &slambda.Environment{
					Variables: env,
				},
			})
			_, err := req_.Send(ctx)
			if err != nil {
				log.Println(err)
			}
		}
	} else {
		client := cloudformation.New(cfg)
		req := client.DeleteStackRequest(&cloudformation.DeleteStackInput{
			StackName: aws.String(os.Getenv("STACK_NAME")),
		})
		_, err := req.Send(ctx)
		if err != nil {
			log.Println(err)
		}
	}
	return events.APIGatewayProxyResponse{
		StatusCode:      http.StatusOK,
		IsBase64Encoded: false,
		Body:            "<html><head><title>Serverless Application Ephemerality</title></head><body><span>Serverless Application Ephemerality</span></body></html>",
		Headers: map[string]string{
			"Content-Type": "text/html",
		},
	}, nil
}

func init() {
	var err error
	cfg, err = external.LoadDefaultAWSConfig()
	cfg.Region = os.Getenv("REGION")
	if err != nil {
		log.Print(err)
	}
}

func main() {
	lambda.Start(HandleRequest)
}

スタックを削除するには DeleteStackRequest を使う


client := cloudformation.New(cfg)
req := client.DeleteStackRequest(&cloudformation.DeleteStackInput{
	StackName: aws.String(os.Getenv("STACK_NAME")),
})
_, err := req.Send(ctx)

※ この処理により 作成したWebページが削除されます。

デプロイ

sam package --output-template-file packaged.yml --s3-bucket "${bucket}"
sam deploy --stack-name "${stack}" --capabilities CAPABILITY_IAM --template-file packaged.yml

※ ${bucket} には 「準備」 で作成した S3バケット名を入力
※ ${stack} には スタック名を入力 (既にあるスタックを指定すると、スタックを更新します)

・デプロイが完了すると、作成したWebページにアクセスできるようになります。

・デプロイ完了後 CloudFormationのスタック一覧 に表示されます。

パブリッシュ

sam package --output-template-file packaged.yml --s3-bucket "${bucket}"
sam publish --template packaged.yml" --region "${AWS_DEFAULT_REGION}"

※ ${bucket} には 「準備」 で作成した S3バケット名を入力
※ ${AWS_DEFAULT_REGION} には リージョン (ap-northeast-1 など) を入力

・パブリッシュ完了後 Serverless Application Repositoryのマイアプリケーション一覧 に表示されます。

削除

aws cloudformation delete-stack --stack-name "${stack}"

※ ${stack} には スタック名を入力

終わりに

Policy周りが原因でデプロイする際にエラーが発生することが多くありました。
使い慣れることで、AWS管理画面よりも簡単に、サーバレスアプリケーションの作成・削除・複製できそうです。

参考資料
[serverless-application-model](https://github.com/aws/serverless-application-model) [究極のCloudFormationをたずねて三千里](https://qiita.com/Kta-M/items/53bfd638eae21b76250f) [AWS SAMを使ってみる](https://qiita.com/spring_i/items/e087905a82c40cf900a0) [AWS SAMのコマンドをまとめてみた](https://qiita.com/gnk263/items/7f8796c26b9b61d33d96) [AWS SAM アプリケーションをデプロイする](https://qiita.com/yh1224/items/f3e22b886639e2605f2f) [今日から始めるサーバーレス SAM【API Gateway + Lambda + DynamoDB】](https://qiita.com/y4u0t2a1r0/items/2d27eed7afd4e4ffbab5) [SAMで作成されるApiGatewayをエッジ最適化以外で作成したいとき](https://qiita.com/tinsep19/items/cb84d1bba795ffeb3ceb) [aws-sam-cliでLambda,DynamoDBのサーバーレスアプリケーション開発に入門してみる](https://qiita.com/umeneri/items/6fb3f7560f4a878f6dfd) [AWS SAM で Hello World する](https://qiita.com/mokuo/items/3348f19d12cb9b17295d) [ゼロから始める AWS SAM 入門](https://qiita.com/OMOIKANESAN/items/b62fb62b8cd04544e1fc) [SAM で API Gateway の CloudWatch Logs を有効にしたい](https://qiita.com/couzie/items/17b6fb94e92dd43d2743) [CloudFormationでクソが!って叫んだこと](https://qiita.com/ryo0301/items/c43451dc1ac1eb938f77) [SAMでtemplate.yamlの記述方法(Events、Policyはここを参照して書く)](https://qiita.com/icck/items/4d1debd8b1710b25a2b2) [AWS SAM CLI 再入門 2020.07](https://qiita.com/hayao_k/items/7827c3778a23c514e196) [AWS SAMでローカル環境でS3とDynamoDBを扱うLambdaを実行する](https://qiita.com/Yuki10/items/7a445a108b63b9298071) [cloudformation/samでsns通知をslackに流すgoのlambdaを作る](https://qiita.com/ymgyt/items/648554a6b5c2cf07a981) [CloudFormation で Cognito](https://qiita.com/y13i/items/1923b47079bdf7c44eec) [AWS SAMのテンプレートではリソースごとに!Refと!GetAttの戻り値が違う](https://qiita.com/takamizawach/items/6a84359bd1432242ca41) [CloudFormationスタック作成エラー: Requires capabilities : [CAPABILITY_NAMED_IAM]](https://qiita.com/akatsukaha/items/796d73e2c736667a6909) [AWS SAMを利用してGolangなLambdaをデプロイする](https://qiita.com/ikeisuke/items/3c0c422888ae8ae09831) [多分わかりやすいCloudFormation入門とチュートリアル](https://qiita.com/ryurock/items/766154e0afb8fdb629e2) [CloudFormationでAWS Lambdaを作成・更新する際のベストプラクティス](https://qiita.com/kihoair/items/fe204fc8582946646d07) [CloudFormationでAPI Gateway+LambdaなAPIを作成する](https://qiita.com/hir00/items/c5c0ca61658d4c8949c2) [AWS SAMを使う前にCloudFormationテンプレートを書こう](https://qiita.com/izanari/items/78258251cced2f713b33)
1
0
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
1
0