サーバーレスWebAPI開発に入門しようとしたけどうまくいかなかった話

AWS DAVの勉強をしていたら、Lambdaを使えばとても簡単にWebAPIが作れてしまうのでは?と感じたので、 ググって最初にヒットしたこちらの記事を参考に、サーバーレスWebAPIを作ろうとしたらうまくいかなかった話です。

なお、Terraformを使ったインフラ構築は特に詰まることも無かったので本記事では触れません。

future-architect.github.io

そもそもWindowsの開発環境で無理やりやっている事が原因だと考えていますが、 Dockerfileの書き方とか色々調べてて勉強になったので書き残しておきます。
記事の内容が間違っているぞということが主旨ではないです。

開発環境

読みにくいかもしれませんが、以下にやったことを時系列順に書いていきます。

Dockerコンテナ上にGolang開発環境を作る

最初はWindows上で作業していたのですが、Makefile中のコマンドを Windowsのコマンドに置き換える事がうまくできなかったため、 Dockerコンテナ上にGolang開発環境を作ることにしました。

こちらの記事を参考に、Dockerfiledocker-compose.ymlを作成。 cpp-learning.com

フォルダ構成

.
├build
│└─Dockerfile
├cmd
├gen
├testdata
└docker-compose.yml

Dockerfile

# goバージョン
FROM golang:1.17.5-alpine

# 前提パッケージのインストール
RUN apk add --update && apk --no-cache add git curl zip unzip sudo binutils make alpine-sdk build-base

ENV GLIBC_VER=2.34-r0

# install glibc
RUN curl -sL https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub
RUN curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-${GLIBC_VER}.apk
RUN curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk
RUN apk add --no-cache glibc-${GLIBC_VER}.apk glibc-bin-${GLIBC_VER}.apk

# aws cli v2 のインストール
# https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-linux.html
RUN curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip -q awscliv2.zip
RUN sudo ./aws/install

RUN go get -u github.com/go-swagger/go-swagger/cmd/swagger

コンテナは軽量なほうがいいだろうと、ベースイメージにはAlpine Linuxを選択。
後々Golangをビルドする時に『stdlib.hが足りない』と怒られるため、alpine-sdkbuild-baseをインストール。
go getの裏ではgitが動いているようなので、gitも前提パッケージとなります。
また、AWS CLIをインストールしたいが、Alpine Linuxではそのまま sudo ./aws/installしてもうまくいかないためglibcをインストールした上でAWS CLIをインストールしてます。
apkコマンドでインストール可能なパッケージはこちらのサイトから検索できます。
Alpine Linux packages

docker-compose.yml

version: "3"
services: 
  aws-sdk-go-container:
    build: # ビルドに使うDockerファイルのパス
      context: .
      dockerfile: ./build/Dockerfile
    container_name: aws-sdk-go-container
    volumes: # ホストのAWS認証情報を共有する
      - "~/.aws:/root/.aws"
  
  # LocalStack
  localstack: 
    image: localstack/localstack:latest
    environment:
      - SERVICE=dynamodb
      - DEFAULT_REGION=ap-northeast-1 # リージョンを設定
    ports:
      - 4566:4566 # サービスへのアクセスポートは4566

volumesではホスト側のC:\Users\<ユーザ名>\.awsに置かれているAWS認証情報を、 コンテナから探しにいけるようにしています。
また元記事ではdocker runコマンドでLocalStackを起動していますが、 せっかく開発環境をコンテナ上に構築しているので、 あわせて起動するようdocker-compose.ymlに記述します。
user_handler_test.goのdbEndpointをhttp://localstack:4566に書き換える事で 開発環境のコンテナからLocalStackのコンテナにアクセスできます。

curlコマンドを叩くと502が返ってきてお手上げに

コンテナ上でmake testがうまくいったため、make deployしてAWSにデプロイしました。
デプロイも成功したため、元記事の通り以下のコマンドを実行したところ、 502 InternalServerErrorExceptionが返ってくるように。

curl -i https://${rest-api-id}.execute-api.ap-northeast-1.amazonaws.com/test/v1/users

AWSマネジメントコンソールからAPI GatewayのCloudWatchログを有効化し、 ログを見てみるとno such file or directory: PathErrorとのこと。

alpineのイメージでAWS Lambda用のgoバイナリをビルドしたらPathErrorになった話 - Qiita

ググって出てきたこちらの記事を参考に、Makefile内のgo buildコマンドのオプションを書き換えて再デプロイ。
エラーメッセージがstring producer has not yet been implemented: apiErrorに変わったけれど、 Swaggerがよしなにやってくれている部分でエラーが出ているようで、写経しているレベルの初学者にはお手上げになりました。

WSL2がホストのメモリを浪費する現象の対策

502エラーに四苦八苦している時、PCのファンの音が大きくなり、ビルドに時間がかかるようになりました。
タスクマネージャーを開いてみるとCPU、メモリ、ディスクが100%近く張り付いています。
調べてみるとVmmemというプロセスがメモリを浪費していました。

WSL2によるホストのメモリ枯渇を防ぐための暫定対処 - Qiita

ググってみたところ、WSL2の不具合のようでした。
こちらの記事を参考に暫定対処を行いましたが、メモリスワップが 発生するとSSDのI/Oが100%になるのは怖いですね。

参考

開発環境系

Alpine Linux

メモリ浪費の暫定対処系