【AWS】Cloud9上でLambda Layer使用

1. 使用するサービス

(AWS)

  • Cloud9
  • Lambda
  • Lambda Layer


2.概要

Cloud9上でアップロード済のLambda Layerを使用する方法を説明します。
Cloud9でLambdaを開発していると、共通関数をまとめたくなることがあると思います。その際、Lambda Layerに共通関数をまとめ、LayerをLambdaにアタッチし、モジュールを呼び出すことでLayerを活用すると思います。Cloud9ではもちろんLayerをアタッチすることは出来ないので、今回はCloud9上でLambdaが載ったDocker上にLayerの中身をインストールすることでCloud9上での実行を実現します。

3. 実装

具体的にはtemplate.ymlにLayerのArnを記述するだけです。

LayerFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "${Project}-${Stage}-LayerFunction"
      Handler: index.handler
      Runtime: python3.8
      CodeUri: ./layerfunction
      Role: !GetAtt layerRole.Arn
      Layers:
        - !Sub "arn:aws:lambda:${AWSRegion}:${AccountId}:layer:${Project}-${Stage}-TestLayer:1"


最後のLayersの箇所のArnを変数なしに変えるだけです。
Cloud9上だと擬似パラメータがデフォルトのままなどの問題がある為です。gitでコミットする際は書き直すよう注意しましょう。

4. おわりに

Layerに関して検索しても、何故かCloud9上では実行出来ないなど出てきたが、案外やってみれば出来る物でした。
余裕があれば、templateの擬似パラメータのoverrideなどに挑戦し、template.ymlの中身を書き換える必要のない方法を模索してみようと思います。

【AWS】CloudformationでLambda Layer実装

1. 使用するサービス

(AWS)

  • Cloudformation
  • Lambda
  • Lambda Layer


2.概要

CloudformationでLambda Layerを実装する手順を説明します。
大まかな手順としては以下になります。

①Layerの中身を作成し、zipにする

②CodeBuildでzip化したLayerを指定したS3に入れる

③Cloudformationで指定したS3からzipファイルを選択してLayerを構築する


3. 実装

3-1. Layerの中身作成

フォルダ構成は以下のようにします。

Layer
    |
    |--LayerContent
                 |
                 |--python
                          |
                          |--layer.py
   

例としてファイル名はlayer.pyとし、zip化する対象はLayerContent直下になります。理由としては、Lambda上でのLayerの展開先はopt/pythonになるからです。
layer.pyの中身は任意の物で大丈夫です。importしたい形にしてください。

3-2. CodeBuildでzip化してS3に保存

CodePipelineで実装するイメージで、github等で作成したLayerをCodeBuidのステージでzip化し、S3に送ります。

version: 0.2
phases:
  install:
    runtime-versions:
        python: 3.8
    commands:
      - pip install --upgrade awscli boto3 mock pymysql==0.10.1
      - cd layer/LayerContent
      - zip -r layer.zip python
      - aws s3 cp ./layer.zip s3のURL
      - rm rdsconnection.zip
      - cp -r python/rdsConnection ../../
      - cd ../..
      
  build:
    commands:
      - python -m unittest discover tests/Unit
      - aws cloudformation package --template-file template.yml --s3-bucket $BUCKET --output-template-file outputtemplate.yml
      - aws s3 sync ./public/ $WEB_BUCKET$Stage --delete

主にinstallの部分がLayer関係です。zipにして指定したS3に送っているだけです。適宜S3のURLを編集してください。

3-3. CloudformationでLayer作成

以下、yamlでLayer及び、Lambdaへのアタッチを行います。

TestLayer:
    Type: "AWS::Lambda::LayerVersion"
    Properties:
      CompatibleRuntimes: 
        - python3.8
      Content: 
        S3Bucket: !Ref LayerBucketName
        S3Key: layer.zip    #Layer File
      Description: "test module layer"
      LayerName: !Sub "${Project}-${Stage}-TestLayer"

  LayerFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "${Project}-${Stage}-LayerFunction"
      Handler: index.handler
      Runtime: python3.8
      CodeUri: ./layerfunction
      Role: !GetAtt layerRole.Arn
      Layers:
        - !Sub "arn:aws:lambda:${AWSRegion}:${AccountId}:layer:${Project}-${Stage}-TestLayer:1"
  

LambdaからLayerを指定する場合は、バージョンまで必要なので注意が必要です。
各種パラメータも適宜編集お願いします。
また、Runtimeも合わせる必要があります。

4. おわりに

以上で、Layerの実装が出来ました。注意点としては、Layerをソースレベルで更新しても、Layerのデプロイまではされないので、yamlの更新やバージョンの再指定を忘れずにお願いします。

【AWS】ReactをCodepipeineでS3にデプロイ

1. 使用するサービス

(AWS)

  • CodePipeline
  • CodeBuild
  • S3


2.概要

GitHubで管理しているReactソースを指定したブランチをプッシュするとそのブランチの中のReactソースをビルドしてS3にデプロイしてくれる流れを説明します。
前提としてS3は作成済で、GitHubなどソース管理のツールは導入済として特に説明はしません。
CodePipelineの説明は過去記事参照。
buffalokusojima.hatenablog.com

3. 実装

GitHubやCodeCommitのブランチのプッシュに対するCodePipelineのキックは上記の過去記事参考。
CodeBuildから説明します。

3-1. CodeBuildの設定

基本的に過去記事通りで大丈夫です。buildspec.ymlの中は以下のようにします。

Reactソースはappフォルダにある事にして書いています。適宜編集お願いします。

version: 0.2
phases:
  install:
    commands:
      - n stable
      - npm update -g npm
      - node -v
      - npm -v
  pre_build:
    commands:
      - echo Entring app directory
      - cd app
      - npm install
      - npm run lint
  build:
    commands:
      - echo Build started on `date`
      - npm run build
      
  post_build:
    commands:
      #環境変数にCloudfrontのディストリビューションを設定しておく
      - aws cloudfront create-invalidation --distribution-id $DISTRIBUTION --paths "/*"
  
artifacts:
  files:
    - '**/*'
  #S3に入れる中身を指定
  #buildするとbuildフォルダが出来るのでそれを指定
  base-directory: app/build

3-2. Deploy

CodeBuildの作成が終わったら次にS3へのデプロイの設定です。
下記のように入力し、S3のバケットを指定すれば完了です。

deploy_to_s3
S3へのデプロイ

4.おわりに

Reactに関しては2年前に少し勉強した程度で全くわかりませんが、いずれ勉強はするつもりです。
今回、ReactのBuildについて触れ、ソースのbuildとデプロイをする機会があったので備忘録的な感じで載せました。
意外と簡単でした。ローカルだとReact死ぬ程分からなかったですが、S3へのデプロイとなると何故かすんなり行きました。

【AWS】Cloudfront使用時のedge Lambdaを使ったindex.html省略

1. 使用するサービス

(AWS)

  • Cloudformation
  • Cloudfront
  • edge Lambda

2. 概要

CloudfrontとS3を用いたWebサイトのURLがパスを区切った時にindex.htmlを指定しないと、見たいパスが表示されない現象を、Cloudfrontへのアクセスをedge Lambdaで受け取り、パスを変換してくれる構成を説明します。

3. 実装

3-1. Cloudfrontの設定

Cloudformationのテンプレート載せます。パラメータとかは適宜調整してください。
LambdaFunctionAssociationsの部分が今回の対象です。

Cloudfrontnet:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Aliases:
          - !Sub "${S3DomainName}.${HostedZoneName}"
        Enabled: true
        PriceClass: PriceClass_All
        DefaultCacheBehavior:
          TargetOriginId: !Sub "S3-${WebContentsBucketBaseName}-${Project}-${Stage}/*"
          ViewerProtocolPolicy: redirect-to-https
          MinTTL: 0
          AllowedMethods:
            - HEAD
            - GET
            - OPTIONS
          CachedMethods:
            - HEAD
            - GET
          ForwardedValues:
            Headers:
              - Authorization
              - Origin
              - Access-Control-Request-Method
              - Access-Control-Request-Headers
            Cookies:
              Forward: none
            QueryString: false
          LambdaFunctionAssociations:
            - EventType: origin-request
    #3-2で作成するLambdaのArn:version
              LambdaFunctionARN: !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:urlReplaceFunction:2"

3-2. Lambdaの実装

まず、リージョンをus-east-1にします。Lambdaを通常通りに作成し、triggerにCloudfrontを選択します。すると、下記のようにedge@Lambdaにするか聞かれるので、Deployを選択します。

edge_lambda_deploy
edge@Lambdaデプロイ

実装内容としてはnodejsで以下のようになります。

exports.handler = (event, context, callback) => 
{
   const request = event.Records[0].cf.request;
   const olduri = request.uri;

   const newuri = olduri.replace(/\$/, '\/index.html');

   request.uri = newuri;

   return callback(null, request);
}

4. 以上で、URLのパスだけでアクセス出来るようになります。index.htmlを打ち込む必要がなくなります。

【AWS】CloudformationでAPI GatewayとLambdaでHello World

1. 使用するサービス

(AWS)

  • Cloudformation
  • Lambda

2. 概要

API GatewayとLambdaを使った構成を簡単にCloudformationで実装出来るテンプレートを載せます。CORSにも対応しているのでS3の静的ウェブサイトからの通信にも対応してます。

3. 実装

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Outputs the API

Parameters:

  Project:
    Description: "Project Name"
    Default: "demo"
    Type: String

  Stage:
    Description: "Environment stage"
    Default: dev
    Type: String
    AllowedValues: [dev, staging, prod]
Resources:

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub "${Project}-${Stage}-HelloWorldFunction"
      Handler: index.handler
      Runtime: python3.8
      CodeUri: ./hello
      Role: !GetAtt HelloWorldRole.Arn

  HelloWorldRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${Project}-${Stage}-HelloWorldRole" 
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        
      MaxSessionDuration: 3600
      Path: "/"

  HelloWorldFunctionApiPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      FunctionName: !Ref HelloWorldFunction

  RestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Body:
        info:
          version: '1.0'
          title: !Sub "${Project}-${Stage}-API"
        paths:
          /hello:
            get:
              produces:
              - "application/json"
              responses:
                "200":
                  description: "200 response"
                  schema:
                    $ref: "#/definitions/Empty"
                  headers:
                    Access-Control-Allow-Origin:
                      type: "string"
                      
              x-amazon-apigateway-integration:
                uri: !Sub >-
                  arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations
                responses:
                  default:
                    statusCode: "200"
                    responseParameters:
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                passthroughBehavior: "when_no_match"
                httpMethod: "POST"
                contentHandling: "CONVERT_TO_TEXT"
                type: "aws"
            options:
              consumes:
              - "application/json"
              produces:
              - "application/json"
              responses:
                "200":
                  description: "200 response"
                  schema:
                    $ref: "#/definitions/Empty"
                  headers:
                    Access-Control-Allow-Origin:
                      type: "string"
                    Access-Control-Allow-Methods:
                      type: "string"
                    Access-Control-Allow-Headers:
                      type: "string"
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: "200"
                    responseTemplates:
                      application/json: |
                        {}
                    responseParameters:
                      method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
                      method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                requestTemplates:
                  application/json: "{\"statusCode\": 200}"
                passthroughBehavior: "when_no_match"
                type: "mock"
              
        definitions:
          Empty:
            type: object
            title: Empty Schema
        x-amazon-apigateway-gateway-responses:
          UNAUTHORIZED:
            statusCode: 401
            responseParameters:
              gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
            responseTemplates:
              application/json: "{\"message\":$context.error.messageString}" 
              
        swagger: '2.0'


  ApiGatewayDeploy:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref RestApi
      Description: "apigateway deployment you need to change Title when you modify Apigateway to deploy"
      StageName: !Ref Stage

4. おわりに

上記のテンプレートで簡単なAPI GatewayとLambdaの実装ができます。CORSの元URLの記載など、細かい部分の修正は必須ですが、とりあえず実装して試してみたいって場合には十分かと思います。

【AWS】CloudformationでSSH接続可能なEC2作成

1. 使用するサービス

(AWS)

  • Cloudformation
  • InternetGateway
  • SecurityGroup
  • EC2


2. 概要

今回は改めて基本に立ち帰り、SSHで外部接続出来るEC2の作成をCloudformationのテンプレートで説明しようかと思います。
構成は単純で、パブリックサブネットに対してVPCにアタッチされたインターネットゲートウェイにルートテーブルが設定され、かつEC2をパブリックサブネットに配置し、セキュリティグループに22番ポートを空けた物を設定して終わりです。
秘密鍵は作成済の物を使用する設定です。

3. 実装

以下、テンプレートコピペでいけるはずです。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Outputs EC2
Parameters:

  Project:
    Description: "Project Name"
    Type: String
    Default: test

  Stage:
    Description: "Environment stage"
    Type: String
    Default: dev

Resources:

  VPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: "Name"
        Value: !Sub "${Project}-${Stage}-Vpc"

  InternetSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/28
      AvailabilityZone: "ap-northeast-1c"
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${Project}-${Stage}-internetgateway-subnet"

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${Project}-${Stage}-internetgateway"
          
  InternetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${Project}-${Stage}-internetgateway-route"
          
  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
      
  InternetSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref InternetRouteTable
      SubnetId: !Ref InternetSubnet
      
  InternetRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      RouteTableId: !Ref InternetRouteTable
      GatewayId: !Ref InternetGateway
    DependsOn: VPCGatewayAttachment

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 SecurityGroup
      VpcId: !Ref VPC
      Tags: 
        - Key: "Name"
          Value: !Sub "${Project}-${Stage}-workbench"
      
  EC2SecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref EC2SecurityGroup
      IpProtocol: tcp
      FromPort: '22'
      ToPort: '22'
      CidrIp: 0.0.0.0/0

  EC2:
    Type: AWS::EC2::Instance
    Properties:
      DisableApiTermination: 'false'
      InstanceInitiatedShutdownBehavior: stop
      ImageId: ami-01748a72bed07727c
      InstanceType: t2.micro
      KeyName: testKey #作成済のキーペア名を入力
      Monitoring: 'false'
      Tags:
        - Key: Name
          Value: !Sub "${Project}-${Stage}-workbench" 
      NetworkInterfaces:
        - DeleteOnTermination: 'true'
          Description: Primary network interface
          DeviceIndex: 0
          SubnetId: !Ref InternetSubnet
          GroupSet:
            - !Ref EC2SecurityGroup
            
            
  EC2EIP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref EC2

4. おわりに

以上で、インターネットから接続出来るEC2の作成完了です。
何かインストールしたければ、ユーザデータにパッケージインストールの設定をすればいいと思います。
今回の用途的には外部からの踏み台として使用していて、EC2のセキュリティグループからのMYSQL接続を許可してRDSの接続等に使用していました。

【AWS】CodeBuildをVPCに入れる

1. 使用するサービス

(AWS)

  • CodeBuild
  • InternetGateway
  • NatGateway


2.概要

CodeBuild内でRDSに接続する必要がある場合、VPC内に設置する必要があります。今回はCodeBuildのVPC内設置の仕方について説明します。

大まかな図としては以下になります。

whole_architecture
全体図

①Internetgateway
インターネット接続のゲートウェイVPC自体にアタッチする。

②Nat Gateway
プライベートサブネット内の端末からインターネット接続出来るようにする為のゲートウェイ

③CodeBuild
AWSサービスのビルドツールです。

④RDS Proxy
今回はRDSProxy->RDSの形をとっていて、実際にCodeBuildが接続する先はRDSProxyになります。

⑤RDS
接続先のデータベースサービス

3. 実装

前提として、VPCとCodebuild、各種サブネット、RDSProxy RDSは作成済とします。

3-1. InternetGatewayの作成

ネットに落ちている通りに作成して、作成済のVPCにアタッチしておわりです。

3-2. Nat Gatewayの作成

Subnetはパブリックにしたいサブネットを選択します。Elastic IPAllocate Eastic IPで新規作成します。

NATGateway_create
NATGateway作成

3-3. サブネットのルートテーブル作成

サブネットそれぞれでルートテーブルを作成する。
VPCはどちらも作成済のVPCを洗濯します。

RouteTable_create
ルートテーブル作成

3-4. ルートテーブルの編集をする。
作成したルートテーブルをそれぞれ編集します。
下記のRoutesからedit routesを選択します。

route_table_edit
ルートテーブル編集


①パブリックサブネット側

0.0.0.0/0の転送先を作成したInternetGatewayにします。

public_subnet_route_table
パブリックサブネットルートテーブル編集


②プライベートサブネット側

0.0.0.0/0の転送先を作成したNAT Gatewayにします。

private_subnet_route_table_create
プライベートサブネットルートテーブル作成

3-5 ルートテーブルをサブネットに紐付ける

Subnet Associationsを選択し、Edit subnet associationsを開いてサブネット紐付け画面を開き、パブリック、プライベートそれぞれのサブネットに紐付けをします。

3-6. CodeBuildのVPC設定

CodeBuildの画面を開いて、editを選択してドロップダウンを表示し、environmentを選択肢、環境設定画面を開きます。

CodeBuild_setting
CodeBuild環境設定

VPCの蘭があるので、作成済のVPCを選択し、サブネットにはプライベートサブネットを選択、セキュリティグループにはRDSProxyに接続可能なものを選択します。

4. おわりに

以上で、CodeBuildのVPC内設置は完了です。
CodeBuildの環境設定画面にあるValidate VPC Configボタンを押して成功すれば疎通確認完了となります。