GoのLambda関数のランタイムをGo1.xからprovided.al2に変更しました（Serverless Framework）

# はじめに

AWSサービスから以下のようなメッセージが来たので、Lambda のランタイムを`GO 1.x`から `provided.al2`に変更しました。
Web検索で調べたところ、SAMの記事に対して`ServerlessFramerwork（SLS)`の記事が少ないように感じたので、
その時の苦戦したところやわかりにくかったポイントをまとめておこうと思います。

```
お客様の AWS アカウントに現在 Go 1.x ランタイムを使用する 1 つ以上の Lambda 関数があることを確認しましたので、ご連絡いたします。

Amazon Linux AMI のメンテナンスサポートが 2023 年 12 月 31 日に終了するのに合わせて、AWS Lambda での Go 1.x ランタイムのサポートを終了します [1]。Lambda は、provided.al2 ランタイムを使用して Go プログラミング言語を引き続きサポートします。provided.al2 ランタイムを使用すると、AWS Graviton2 プロセッサのサポートや、より小さなデプロイパッケージとより高速な関数呼び出しパスによる効率的な実装など、go1.x ランタイムに比べていくつかの利点があります。詳細については、ブログ記事 [2] を参照してください。

Lambda ランタイムサポートポリシー [3] で説明されているように、Lambda での言語ランタイムのサポートの終了は 2 段階で行われます。2023 年 12 月 31 日以降、Lambda は Lambda 関数で使用される Go 1.x ランタイムにセキュリティパッチやその他アップグレードを適用しなくなり、Go 1.x を使用する関数は、テクニカルサポートの対象外となります。さらに、Go 1.x ランタイムを使用する新しい Lambda 関数を作成できなくなります。2024 年 1 月 30 日以降、Go 1.x ランタイムを使用して既存の関数を更新することができなくなります。

2023 年 12 月 31 日までに、既存の Go 1.x 関数を provided.al2 ランタイムにアップグレードすることをお勧めします。

サポートの終了は機能の実行には影響しません。関数は引き続き実行されます。しかしながら、AWS Lambda チームによるメンテナンスやパッチ適用がされない、サポートされていないランタイムで実行されることとなります。
```

アップグレードしなくても動くが、更新はできなくなるよ〜とのこと

# 現状のデプロイ

現在リリースしているLambda関数（ランタイム　Go1.x）は以下のコードを使用してデプロイを行なっている。

### ビルドコマンド
```bash
GOOS=linux GOARCH=amd64 go build -o bin/healthcheck/main  cmd/healthcheck/main.go
```

### serverless.yml
```yaml
service: engimeerLambda

provider:
  name: aws
  runtime: go1.x
  region: ap-northeast-1

functions:
  Healthcheck:
    name: ${self:service}-Healthcheck
    handler: bin/healthcheck/main
    description: Healthcheck
    package:
      individually: true
      patterns:
        - ./bin/healthcheck/main
    events:
      - http:
          path: /healthcheck
          method: get
```

# 変更後のデプロイ
上記のビルドコマンドとserverless.ymlを以下のコードに書き換え、provided.al2のランタイムを使用してLambda関数のデプロイを実行しました。

※AmazonLinux2の提供cpuはamd64とarm64のどちらもありますが、`serverless.yml`のアーキテクチャを指定しない場合は、x86_64となるので、build時のコマンドを`GOARCH=amd64`とした場合は記載不要です。

また、ランタイムの変更に合わせてビルドコマンドに`-tags lambda.norpc`のオプションを付与しました。

> • オプションの `lambda.norpc` タグを使用して、[Lambda](https://github.com/aws/aws-lambda-go/tree/master/lambda) ライブラリの Remote Procedure Call (RPC) コンポーネントを除外することができます。RPC コンポーネントは Go 1.x ランタイムを使用する場合にのみ必要です。RPC を除外すると、デプロイパッケージのサイズが小さくなります。（引用：https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/golang-package.html）
> 

### ビルドコマンド
- コンパイル後のバイナリファイル名を`bootstrap`に変更
- バイナリファイルをフォルダ構造を持たないzipにアーカイブ化
```bash
GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o bin/healthcheck/bootstrap cmd/healthcheck/main.go
zip -j bin/healthcheck.zip bin/healthcheck/bootstrap
```

### serverless.yml
- `provider:runtime`を`go1.x`から`provided.al2`に変更
- `functions:Healthcheck:handler`を`bin/healthcheck/main`から`bootstrap`に変更
- `functions:Healthcheck:package:patterns`を`functions:Healthcheck:package:artfact`に変更
- artifactには先ほど作成したアーカイブファイルを指定（`bin/`はローカルのデプロイ時の保存ディレクトリなので任意でOK）
```yaml
service: engimeerLambda

provider:
  name: aws
  runtime: provided.al2
  region: ap-northeast-1

functions:
  Healthcheck:
    name: ${self:service}-Healthcheck
    handler: bootstrap
    description: Healthcheck
    package:
      individually: true
      artifact: bin/healthcheck.zip
    events:
      - http:
          path: /healthcheck
          method: get
```

上記にファイルを変更してsls deployを実行することでランタイムの変更ができ、問題なく動作するようになりました。

# 時間を要したポイント

以下の点がうまくいかないポイントでした。

- `provided.al2`ではバイナリファイルをzipにアーカイブ化する必要があるが、階層構造に注意する必要があった点
  - `zip bin/healthcheck.zip bin/bootstrap`ではうまくいかず、`-j`オプション（ディレクトリ名無しで格納）が必要です。
  - オプションなしでデプロイすると`entrypoint`が見つけられないため、以下のエラーが出力されます。
  ```         
    {
      “errorType”: “Runtime.InvalidEntrypoint”,
      “errorMessage”: “RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Error: Couldn’t find valid bootstrap(s): [/var/task/bootstrap /opt/bootstrap]”
    }
  ```
<br>

- yamlファイルで`package: artifact:`を指定する必要がある
  - デプロイファイルとして`.zip`ファイルを指定する
    - 指定しないとこちらも`entrypoint`が見つけられないため「`bootstrap`いないよ」と以下のエラーが出ます。
  ```         
    {
      “errorType”: “Runtime.InvalidEntrypoint”,
      “errorMessage”: “RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Error: Couldn’t find valid bootstrap(s): [/var/task/bootstrap /opt/bootstrap]”
    }
  ```
<br>
         
- バイナリファイル以外のファイルを一緒にLambda環境にdeployしたい場合、go1.xの時はserverless.ymlの`package:patterns`に指定すればよかったが、`package:artifact`と同時に使用するとうまくデプロイできませんでした。
  - zip化する際にzipに一緒にdeployしたいファイルを格納することで改善できました。