Lambda + Goでネストされたアプリケーションを構築

![1280px-Go_Logo_Aqua.svg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525769/2b8f3e0b-9e8b-1355-2e71-ffd3570f9337.png)

LambdaでGoを使ったAPIを作成したときの話
あるサービスでGoを採用し、Lambda上でapiを作成することになった、最初は小規模の認識で開発を行っていたが、仕様がどんどん膨らみ気づいた時にはエンドポイントが150を超え、そしてやってきたリソース制限

```
Template format error: Number of resources, 206, is greater than maximum allowed, 200
```

ネストさせることで回避できることを知り、ネストするも```sam build```できず、結局ネストされた各アプリケションを個別にビルドし、マージするシェルを書いて運用していた。
苦労した話を書こうと思い調べていると、いつの間にかネストされたアプリケーションのビルドができるようになっていた！！!

![スクリーンショット 2021-03-20 23.32.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525769/6de22541-54fd-0823-8f3c-4c3ef8a10652.png)
https://github.com/aws/aws-sam-cli/issues/1470

前段の話が長くなりましたが、Lambdaでネストされたアプリケーションを簡単に構築できる話です

## 構築
２つのアプリケーションにネストさせる例です

```yaml:template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-go
  Sample SAM Template for lambda-go

Parameters:
  Region:
    Type: String
    Default: ap-northeast-1
  Stage:
    Type: String
    Default: Dev
  ApiDomainName:
    Type: String
    Default: api.example.com

Resources:
  # Lambda Application
  App1Application:
    Type: AWS::Serverless::Application
    Properties:
      Location: app1.yaml
      Parameters:
        Region: !Ref Region
        Stage: !Ref Stage
        ApiDomainName: !Ref ApiDomainName
  App2Application:
    Type: AWS::Serverless::Application
    Properties:
      Location: app2.yaml
      Parameters:
        Region: !Ref Region
        Stage: !Ref Stage
        ApiDomainName: !Ref ApiDomainName
```

### アプリケーション１
```yaml:app1.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-go
  Sample SAM Template for lambda-go
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 10
    Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
      Variables:
        REGION: !Ref Region

Parameters:
  Stage:
    Type: String
  Region:
    Type: String
  ApiDomainName:
    Type: String

Resources:
  # ロール
  App1Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: app1-role
      Policies:
        - PolicyName: app1-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - ec2:CreateNetworkInterface
                  - ec2:DescribeNetworkInterfaces
                  - ec2:DetachNetworkInterface
                  - ec2:DeleteNetworkInterface
                Resource: '*'
      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
      Path: /

  # Api Gateway
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref Stage

  # Base path mapping
  ApiGatewayBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      DomainName: !Ref ApiDomainName
      RestApiId: !Ref ApiGateway
      BasePath: app1
      Stage: !Ref ApiGateway.Stage

  # Lambda Function
  Test1Function:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: ../../test1/
      Handler: test1
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Role: !GetAtt App1Role.Arn
      Events:
        GET:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /test1
            Method: GET
  Test2Function:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: ../../test2/
      Handler: test2
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Role: !GetAtt App1Role.Arn
      Events:
        GET:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /test2
            Method: GET
  Test3Function:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: ../../test3/
      Handler: test3
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Role: !GetAtt App1Role.Arn
      Events:
        GET:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /test3
            Method: GET
```

### アプリケーション２
```yaml:app2.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-go
  Sample SAM Template for lambda-go
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 10
    Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
      Variables:
        REGION: !Ref Region

Parameters:
  Stage:
    Type: String
  Region:
    Type: String
  ApiDomainName:
    Type: String

Resources:
  # ロール
  App2Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: app2-role
      Policies:
        - PolicyName: app2-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - ec2:CreateNetworkInterface
                  - ec2:DescribeNetworkInterfaces
                  - ec2:DetachNetworkInterface
                  - ec2:DeleteNetworkInterface
                Resource: '*'
      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
      Path: /

  # Api Gateway
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref Stage

  # Base path mapping
  ApiGatewayBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      DomainName: !Ref ApiDomainName
      RestApiId: !Ref ApiGateway
      BasePath: app2
      Stage: !Ref ApiGateway.Stage

  # Lambda Function
  Test10Function:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: ../../test10/
      Handler: test10
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Role: !GetAtt App2Role.Arn
      Events:
        GET:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /test10
            Method: GET
  Test11Function:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: ../../test11/
      Handler: test11
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Role: !GetAtt App2Role.Arn
      Events:
        GET:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /test11
            Method: GET
  Test12Function:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: ../../test12/
      Handler: test12
      Runtime: go1.x
      Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
      Role: !GetAtt App2Role.Arn
      Events:
        GET:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /test12
            Method: GET
```

ネスト以外のポイントとして、API GatewayのAPI マッピングを使ってネストされたアプリケーションをマッピングしています。

```yaml:app1.yaml,app2.yaml
  ApiGatewayBasePathMapping:
    Type: AWS::ApiGateway::BasePathMapping
    Properties:
      DomainName: !Ref ApiDomainName
      RestApiId: !Ref ApiGateway
      BasePath: app2
      Stage: !Ref ApiGateway.Stage
```
これをすると、`template.yaml`で指定している、`ApiDomainName`（api.example.com）で各ネストされたアプリケーションにアクセスできるようになる
例えば、https://api.example.com/app1/test1 とか https://api.example.com/app2/test10 など 
＊実際に試して頂く場合は`ApiDomainName`を変更して頂く必要があります


## デプロイ
デプロイ時には`capabilities`に`CAPABILITY_AUTO_EXPAND`を指定しデプロイをおこないます、成功するとCloudFormationでネストされたアプリケーションが表示されます

![スクリーンショット 2021-03-22 20.21.31のコピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525769/f88ae287-cc09-8660-43d4-74b4ff1f08c4.jpeg)


## ソース
参考までにソースをあげておきました。

https://github.com/akihiro-moriwaki/lambda-nest-go