AWS SAM+Lambda+Go+WSLではじめるサーバーレスアプリケーション開発

# 目的

AWS SAM を使って Go 言語でサーバーレスアプリケーションを開発するための、環境構築の手順をまとめます。個人的な備忘録を兼ねているため、必須ではない細かな設定なども記載しています。Lambda でサーバーレスアプリケーションの開発をはじめようと思っている方の参考になれば幸いです。

# 当記事に含まれる内容

当記事に含まれる内容は下記の通りです。ソフトウェアのインストールなどは既にわかりやすい記事があるので、そのリンクを貼っています。

- Hello World アプリケーションについて
- WSL2 の設定
- Docker の設定
- AWS CLI の設定
- Session Manager プラグインの設定
- SAM と Go のインストール
- Ubuntu の環境変数設定の自動化
- VSCode の設定
- SAM プロジェクトの新規作成
- SAM プロジェクトのデプロイ
- AWS コンソールから各サービスを確認

# Hello World アプリケーションについて

Hello World アプリケーションは、SAM プロジェクトを新規作成するときに便利なサンプルプロジェクトです。クライアントが API Gateway のエンドポイントにリクエストすると、「Hello, xxx.xxx.xxx.xxx（Lambda の IP アドレス）」とレスポンスを返す、シンプルなアプリです。はじめは、このアプリに変更を加えながらオリジナルのサーバーレスアプリケーションを開発していくと、楽だと思います。

### Hello World アプリケーションの AWS 構成

![AWS Architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/5c7a5ace-3c3c-1a83-44e0-29e4eeafb2d7.png)

構成図上のサービスの概要をひと言でまとめました。詳細が知りたい方はサービス名のリンクを参照してください。

- **[Amazon API Gateway](https://aws.amazon.com/jp/api-gateway/)**：API の開発や公開などが簡単に行えるサービスです。
- **[AWS Lambda](https://aws.amazon.com/jp/lambda/)**：サーバーレスで小さなプログラムを実行できるサービスです。
- **[Amazon CloudWatch Logs](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html)**：AWS リソースのログをモニタリングできるサービスです。

# WSL2 の設定

WSL2 (Windows Subsystem for Linux 2) は、Windows 10 上で Linux を動作させるための仕組みです。サーバーレスアプリケーションをローカル環境でテストする際に、SAM は Lambda ファンクションを実行するための Docker コンテナを立ち上げます。Windows 上で Docker を利用するとエラーにハマることが多いので、WSL2 上に環境構築しています。WSL1 だと裏で動いているのは Windows なので、Docker をはじめとする一部のソフトウェアでは不具合が生じます。そのため WSL2 を利用しています。

### WSL2 のインストール

インストールがまだの方は、下記のサイトなどを参考にインストールをお願いします。

- [WSL のインストール](https://docs.microsoft.com/ja-jp/windows/wsl/install)

インストールが終わったら PowerShell を開き、 `wsl --list --verbose` コマンドで WSL のバージョンを確認してください。下記のように VERSION が 2 と表示されていれば大丈夫です。

```
[win] wsl --list --verbose
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2
```

### WSL に Windows の PATH を引き継がないように設定する

WSL はそのまま利用すると Windows の環境変数の PATH を引き継ぎます。WSL 上の `/etc/wsl.conf` ファイルを編集すると、Windows の PATH を引き継がないように設定できます。

```shell
# WSL 上の /etc/wsl.conf ファイルを編集
$ sudo vim /etc/wsl.conf

# wsl.conf がない場合は新規作成
$ sudo touch /etc/wsl.conf
```

wsl.conf に下記の文言を追記してください。

```
[interop]
appendWindowsPath = false
```

以上で WSL の設定は完了です。

**参考サイト**：[WSLでWindowsのPATHを引き継がないようにする方法](https://qiita.com/raccy/items/456a7158f588670c0850)

# Docker の設定

### 1. WSL に Docker と docker-compose をインストール

Docker の公式サイトなどを参考に、WSL に Docker や docker-compose をインストールします。

- [Docker のインストール](https://docs.docker.com/engine/install/ubuntu/)
- [docker-compose のインストール](http://docs.docker.jp/compose/install.html#linux-compose)

### Windows に Docker Desktop をインストール

Windows に Docker Desktop をインストールして、Docker Desktop を WSL バックエンドで使えるように設定します。下記のサイトなどを参考に、Docker Desktop のインストールをお願いします。

- [Windows に Docker Desktop をインストール](https://docs.docker.jp/docker-for-windows/install.html)

インストールが完了したら、Dokcer Desktop を開いて以下の設定を行います。

1. **Settigs > General** を開き、**Expose daemon on tcp://localhost:2375 without TLS** にチェックをつける
2. **Apply & Restart** を実行する
3. PC を再起動する ※一度再起動しないと VSCode から WSL に接続した際に Dokcer を起動できないようです。

![docker_desktop.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/843640ae-7e48-1abd-3401-51e7c5bea35f.png)

以上で Docker の設定は完了です。

# AWS CLI の設定

AWS CLI は AWS をコマンドラインで操作・管理するためのツールです。インストールがまだの方は、下記のサイトなどを参考にインストールをお願います。

- [AWS CLI をインストール (LINUX)](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-linux.html)

インストールが完了したら `aws configure` コマンドを実行して、AWS Access Key ID、AWS Secret Access Key、Default region name、Default output format を入力します。

```
$ aws configure
AWS Access Key ID [None]: XXXXXXXXXX
AWS Secret Access Key [None]: XXXXXXXXXX
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

設定が完了したら、`aws sts get-caller-identity` コマンドで IAM ユーザーアクセス権があることを確認してみます。

```
$ aws sts get-caller-identity
{
    "UserId": "XXXXXXXXXX",
    "Account": "XXXXXXXXXX",
    "Arn": "arn:aws:iam::XXXXXXXXXX:user/XXXXXXXXXX"
}
```

以上で AWS CLI の設定は完了です。

# Session Manager プラグインの設定

Session Manager プラグインをインストールしておくと、プライベートサブネットの EC2 に SSH で接続したり、SCP で EC2 - ローカル間でファイル転送ができるようになり便利です。興味のある方は、下記のサイトなどを参考にインストールしてみてください。

- [Session Manager plugin をインストール (LINUX)](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html#install-plugin-debian)

インストールにあたり WSL の CPU 情報が不明な場合は、`sudo lshw -class processor` コマンドで確認できます。

```
$ sudo lshw -class processor
[sudo] password for funaki: 
  *-cpu                     
       product: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
       vendor: Intel Corp.
       physical id: 1
       bus info: cpu@0
       width: 64 bits
       capabilities: ...
```

インストールが完了したら、`～.ssh\config` にある SSH 設定ファイルに下記の文言を追記して、Session Manager を通した SSH 接続を有効にします。

```
# SSH over Session Manager
host i-* mi-*
    ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
```

以上で Session Manager プラグイン の設定は完了です。

プライベートサブネットにある EC2 に SSH や SCP でアクセスする方法について、下記の記事にまとめています。興味のある方はぜひご覧ください。

- [AWS CLIを使用してEC2にSSHやSCPでアクセスする](https://qiita.com/sakeafterbeer/items/e67c23a73fde53abe928)

# SAM と Go のインストール

続いて、SAM と Go のインストールです。SAM をインストールする前に Python のパッケージマネージャをインストールする必要があります。以下の順番でコマンドを実行してください。
※Delve という Go のデバッグツールもインストールしていますが、インストールしなくても問題ありません。

```shell
1. sudo apt update                                      # Ubuntu のローカルキャッシュ済みパッケージ一覧を更新
2. sudo apt install python3-pip                         # SAM をインストールするため、Python のパッケージマネージャをインストール
3. sudo pip3 install aws-sam-cli                        # SAM をインストール
4. sudo apt install golang-go                           # Go をインストール
5. go install github.com/go-delve/delve/cmd/dlv@latest  # Go のリモートデバッグ用に Delve をインストール
```

※2021年7月の SAM CLI の重要なアップグレードにより、バージョン 1.25 以上へのアップデートが必要になりました。もし古いバージョンをお使いでしたら、下記のコマンドで SAM CLI をアップデートしてください。

```shell
sudo pip3 install -U aws-sam-cli  # SAM CLI をアップデート
```

下記のコマンドで Python、SAM、Go がインストールされていることを確認できます。

```shell
$ pip3 --version
pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8)

$ sam --version
SAM CLI, version 1.21.1

$ go version
go version go1.13.8 linux/amd64

$ dlv version
Delve Debugger
Version: 1.6.0
Build: $Id: 8cc9751909843dd55a46e8ea2a561544f70db34d
```

以上で SAM と Go のインストールは完了です。

# Ubuntu の環境変数設定の自動化

`export` コマンドで GOPATH や PATH などの環境変数を設定しても、Ubuntu ターミナルを閉じると初期化されてしまいます。Ubuntu ターミナルを起動する度に環境変数を設定するのは面倒なので、起動時に環境変数が自動で設定されるよう、下記のコマンドで `~.profile` に `export` コマンドを追記しておきます。

```shell
$ cd                                                   # HOME ディレクトリに移動
$ sudo echo export GOPATH=$HOME/go >> .profile         # GOPATH の設定
$ sudo echo export PATH=$PATH:$GOPATH/bin >> .profile  # PATH の設定
```

`~.profile` ファイルに下記の 2 行が追記されていれば設定完了です。

```.profile
export GOPATH="Path to Go"
export PATH="Path to Go"/bin
```

**参考サイト**：[【Linux】環境変数の確認・設定・削除・永続化について](https://ac-as.net/linux-environment-variable/)

# VSCode の設定

Go で開発するときの IDE は VSCode を利用しています。VSCode の設定内容について、備忘録として残しておきます。

### 1. 拡張機能と依存パッケージをインストール

下記の記事の以下 2 項目を実行します。

- [VSCodeでGo言語の開発環境を構築する](https://qiita.com/melty_go/items/c977ba594efcffc8b567)

1. Go言語の拡張機能をインストール

2. 拡張機能の依存パッケージをインストール

以上です。

### 2. VSCode のフォーマットツールを gofmt に設定

VSCode のフォーマットツールを (1) でインストールした **gofmt** に設定します。

メニューから **File > Preferences > Settings > Extensions > Go > Format Tool** の順に開き、**gofmt** を選択します。

![スクリーンショット 2021-09-28 032757.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/c5ec3584-235a-e347-f0d2-67e20cd82b80.png)

以上です。

**gofmt** がインストールされているかどうかは、下記のコマンドで確認できます。

```shell
$ which gofmt 
/usr/bin/gofmt
```

### 3. VSCode のリントツールを golangci-lint に設定

VSCode のリントツールを (1) でインストールした **golangci-lint** に設定します。

メニューから **file > Preferences > Settings > Extensions > Go > Lint Tool** の順に開き、 **golangci-lint** を選択します。

![スクリーンショット 2021-09-28 033440.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/26f2adac-945f-5117-1ed9-139bf15fcb62.png)

以上です。

**golangci-lint** がインストールされているかどうかは、下記のコマンドで確認できます。

```shell
$ golangci-lint --version
golangci-lint has version 1.33.0 built from b90551c on 2020-11-23T05:15:36Z
```

### 4. VSCode から WSL に接続

VSCode から WSL に接続する方法は、下記の記事を参考にしました。

* [VSCode から WSL に接続する](https://qiita.com/EBIHARA_kenji/items/12c7a452429d79006450)

上記サイトの以下 2 項目を実行します。

1. Visual Studio Code を WSL へ接続する

2. Ubuntu から Visual Studio Code を起動する

以上です。

# SAM プロジェクトの新規作成

ここからは SAM プロジェクトの新規作成に入ります。冒頭で説明した通り、ここでは Hello World アプリケーションを作成していきます。

まず、WSL2 を起動して、サンプルプロジェクトをクローンするディレクトリに移動します。

```shell
$ cd /path/to/dir
```

サンプルプロジェクトを作成するには`sam init [OPTION]` コマンドを使用します。オプションをつけて `sam init --runtime go1.x --name プロジェクト名` として、ランタイムとプロジェクト名を指定します。すると、コマンド入力後にテンプレートについて聞かれるので、(1) の「クイックスタートテンプレート」を選択します。

```shell
$ sam init --runtime go1.x --name SamTest
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1 # 1 を選択
```

使用しているコマンドのオプションの解説です。

|オプション|説明|
|---|---|
|--runtime|アプリケーションのランタイムを指定します。言語設定のようなものです。|
|--name|プロジェクトディレクトリに任意の名前をつけることができます。|

その他のオプションが知りたい方は、[AWS デベロッパーガイド](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-init.html) をご参照ください。

次に、パッケージタイプを聞かれるので、(1) の「Zip」を選択します。

```shell
What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1 # 1 を選択

Cloning from https://github.com/aws/aws-sam-cli-app-templates
```

最後に、クイックスタートテンプレートの種類を決めます。ここでは、(1) の「Hello World Example」を選択します。

```shell
AWS quick start application templates:
        1 - Hello World Example
        2 - Step Functions Sample App (Stock Trader)
Template selection: 1 # 1 を選択

    -----------------------
    Generating application:
    -----------------------
    Name: SamTest
    Runtime: go1.x
    Dependency Manager: mod
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./SamTest/README.md
```

以上でサンプルプロジェクトが作成されます。

プロジェクトの内容は下記の通りです。

```
.
├── Makefile                    <-- 自動ビルドや自動デプロイなどの設定を記載
├── README.md                   <-- プロジェクトの説明ファイル
├── hello-world                 <-- Lambda ファンクションのコードを格納するディレクトリ
│   ├── main.go                 <-- Lambda ファンクションのコード
│   └── main_test.go            <-- ユニットテスト
└── template.yaml               <-- SAM テンプレート
```

`template.yaml` ファイルは SAM テンプレートと呼ばれるもので、CloudFormation を拡張したものになっています。SAM テンプレートのリソースには Lambda ファンクション（AWS::Serverless::Function）しか記載されていませんが、暗黙的に以下のリソースが作成されます。各サービスの当アプリでの役割も簡単に記載しておきます。

- **Amazon API Gateway**：API Gateway が提供するエンドポイントへのリクエストをトリガーに Lambda が起動します。
- **[IAM ロール](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles.html)**：Lambda にアタッチされる IAM ロールです。Lambda に各 AWS サービスへのアクセス権を与えます。
- **Amazon CloudWatch ロググループ**：Lambda のログをモニタリングするためのロググループです。

# SAM プロジェクトのデプロイ

では、サンプルプロジェクトの Hello World アプリケーションを、このまま AWS にデプロイしていきたいと思います。

### 1. プロファイルの設定

デプロイする前に、デプロイ先の AWS アカウント を確認しておきましょう。僕の場合は、[名前付きプロファイル](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html) を設定して、プロファイルを切り替えることでデプロイ先を指定しています。

プロファイルを設定済みの方は、`aws configure list` コマンドで現在使用中のプロファイルを表示できます。

```bash
$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                     Name           manual    --profile
access_key     ****************XXXX shared-credentials-file
secret_key     ****************XXXX shared-credentials-file
    region           ap-northeast-1      config-file    ~/.aws/config
```

まだ IAM ユーザーのアクセスキーとシークレットアクセスキーを作成していない方は、下記の記事を参考に作成してください。

- [AWS ユーザーガイド アクセスキー ID とシークレットアクセスキー](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-creds)

プロファイルの設定がまだの方は、`aws configure --profile プロファイル名` で設定できます。

```bash
$ aws configure                 # デフォルトのプロファイルを設定
$ aws configure --profile Name  # 名前付きプロファイルを設定
```

### 2. ビルド

デプロイ先の AWS アカウントのプロファイルの設定が完了したら、`sam build` コマンドでビルドします。

```bash
$ sam build
Building codeuri: /path/to/project/dir/SamTest/hello-world runtime: go1.x metadata: {} functions: ['HelloWorldFunction']
Running GoModulesBuilder:Build

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
```

以上でビルド完了です。

### 3. デプロイ

はじめてデプロイする場合は、`sam deploy --guided` コマンドを利用し、スタック名やリージョンなどを指定します。

```bash
$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]: SamTest
        AWS Region [ap-northeast-1]: 
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: y # Yes を選択
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]: Y # Yes を選択
        HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
        Save arguments to configuration file [Y/n]: # Yes を選択
        SAM configuration file [samconfig.toml]: # デフォルトの samconfig.toml で作成
        SAM configuration environment [default]: # デフォルトの default で設定

        ～省略～

Successfully created/updated stack - SamTest in ap-northeast-1
```

下記に、質問される内容を記載しておきます。

- **スタック名**：CloudFormation にデプロイするスタックの名前。これはアカウントと地域に固有である必要があります。プロジェクト名から始めるとわかりやすいです。
- **AWSリージョン**：アプリをデプロイする AWS リージョンを指定します。
- **展開前に変更を確認する**：Yes に設定すると、デプロイするときにリソースの変更内容を確認できます。No に設定すると、リソースの変更内容が表示されないまま自動的にデプロイされます。
- **SAM CLI IAMロールの作成を許可する**：AWS SAM テンプレートは、作成された Lambda ファンクションが他の AWS サービスにアクセスするために必要な IAM ロールを作成します。デフォルトでは、最低限必要な権限を持つIAMロールが作成されます。Lambda にアタッチする IAM ロールを自分で作成する場合には、オプションで `--capabilities CAPABILITY_NAMED_IAM` を指定する必要があります。
- **引数をsamconfig.tomlに保存**：Yes に設定すると、選択した内容がプロジェクト内の samconfig.toml ファイルに保存されます。samconfig.toml を作成しておくと、次回以降のデプロイは `sam deploy` コマンドだけで実行することが可能です。

質問にひと通り回答したら、ビルドしたプログラムが S3 にアップロードされ、template.taml で定義した AWS リソースが作成されていきます。

"Successfully created/updated stack" というメッセージが表示されたら、デプロイ完了です。

ターミナルに表示されている **Outputs** に、デプロイされた Lambda ファンクションのエンドポイント（Value）が記載されています。

```bash
Key                 HelloWorldAPI                                                                                                                                                                             
Description         API Gateway endpoint URL for Prod environment for First Function                                                                                                                          
Value               https://xxxxxxxx
```

ブラウザからエンドポイントにアクセスしてみます。

![hello_world_app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/ef42101e-6a9b-c0b0-c722-3e2ef0e41d79.png)

以上のように表示されれば、デプロイ成功です。

ここから少しずつコードに変更を加えたり、SAM テンプレートに変更を加えたりしていくと、オリジナルのサーバーレスアプリケーションを開発することができます。

# AWS コンソールから各サービスを確認

最後に、AWS のコンソールから作成したアプリの各サービスを見てみます。なお、各サービスには自動で名前がつけられていますが、SAM テンプレートを使って任意の名前をつけることができます。

### CloudFormation

**CloudFormation > スタック** の順に開くと、`sam deploy --guided` で指定したスタック名でスタックが作成されていることがわかります。スタック名をクリックすると詳細を確認できます。SAM CLI を使ってデプロイした場合、SAM テンプレートの内容をもとに CloudFormation で各リソースが作成されることがわかると思います。2 回目以降のデプロイでは、このスタックを更新して各リソースに変更を加えていくことができます。

![CloudFormation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/6fb69e8d-21b3-eb44-5aee-7f3dcba97d14.png)

### AWS Lambda

**Lambda > 関数** の順に開くと、新たに Lambda ファンクションが作成されていることがわかります。関数名から詳細を確認できます。

![Lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/241a6ae0-bfaf-3cf8-96e0-e9b89c4d38a0.png)

### API Gateway

**API Gateway** を開くと、新たに API が作成されていることがわかります。API 名から詳細を確認できます。

![APIGateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/33797e05-4a20-7efa-de7e-ed51086821a5.png)

### CloudWatch ロググループ

**CloudWatch > ロググループ** の順に開くと、新たにロググループが作成されていることがわかります。ロググループ名から Lambda のログファイルを見ることができます。

![CloudWatch_Logs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450243/14fba8dc-66ce-a926-12ef-dc2cddf87caf.png)

以上で当記事の内容は終了です。お疲れさまでした！

# おわりに

説明が簡単になってしまったところや、今回書ききれなかった内容などは、時間を見つけて修正したり続編の記事などを書いていけたらと思っています。
最後までお読みいただき、ありがとうございました！