LoginSignup
8
3

たった10分で始める!AWS における最速のアプリ開発方法

Last updated at Posted at 2023-12-16

はじめに

こちらは AWS for Games Advent Calendar 2023 18 日目の記事となります。

迅速なアイデアの実装と検証は、あらゆるソフトウェア開発において重要です。このブログでは、AWS における最速の開発手法を lambda-container-deploy を利用し解説します。あなたのアイデアを速やかに実現しましょう!

このブログで学べること:

  1. Go言語を用いた hello world のウェブアプリの作り方
  2. 10 分以内で aws にホスティングする手法
  3. IP 制限の実装
  4. AWS における分散トレーシング、ロギングの実現
  5. ローカル環境の構築

Go ウェブアプリケーションの準備

最初に、Go言語で hello world のプログラムを書きます。ポート 8000 番で Listen します。

main.go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func topHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("hello world")
	io.WriteString(w, "hello world")
}

func main() {
	http.Handle("/", http.HandlerFunc(topHandler))
	http.ListenAndServe(":8000", nil)
}

Dockerfile の作成

マルチステージビルドを利用します。今回は Lambda Container にホスティングするため、aws-lambda-adapter を使って実現しています。

Dockerfile
FROM golang:1.21 as builder

ENV GOPROXY=direct

WORKDIR /app

COPY go.* ./
RUN go mod download

COPY . ./

RUN go build -v -o myapp

FROM debian:stable-slim

WORKDIR /app

# tell lambda-adapter which port is listening
ENV PORT=8000

# handle lambda runtime api, convert requests to localhost:8000
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter
COPY --from=builder /app/myapp /app/myapp

CMD ["/app/myapp"]

Lambdaにデプロイ

Lamdba に必要なリソースの作成、更新、デプロイは lambda-container-deploy を利用します。lambda.sh deploy 一つのコマンドでビルドからデプロイまでを行います。入力項目としては、AWSのリージョンだけとなります。また、サービス名とリージョンは、.lambda/config.ini に自動保存されるため、2回目以降は入力なしで実行が可能です。

$ ./lambda.sh deploy
Deploying Lambda function...
Enter service name [lambda-container-blog]: 
Enter AWS region: ap-northeast-1
[+] Building 10.7s (18/18) FINISHED                                                                                                                  docker:orbstack
 => [internal] load .dockerignore                                                                                                                               0.0s
 => => transferring context: 2B                                                                                                                                 0.0s
 => [internal] load build definition from Dockerfile                                                                                                            0.0s
 => => transferring dockerfile: 542B                                                                                                                            0.0s
 => [internal] load metadata for public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1                                                                                2.3s
 => [internal] load metadata for docker.io/library/golang:1.21                                                                                                  2.7s
 => [internal] load metadata for docker.io/library/debian:stable-slim                                                                                           2.5s
 => [builder 1/6] FROM docker.io/library/golang:1.21@sha256:2ff79bcdaff74368a9fdcb06f6599e54a71caf520fd2357a55feddd504bcaffb                                    0.0s
 => => resolve docker.io/library/golang:1.21@sha256:2ff79bcdaff74368a9fdcb06f6599e54a71caf520fd2357a55feddd504bcaffb                                            0.0s
 => FROM public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1@sha256:97c8f81e19e64841df0882d3b3c943db964c554992c1bac26100f1d6c41ea0bb                                0.0s
 => [stage-1 1/4] FROM docker.io/library/debian:stable-slim@sha256:375fb84f3c64691a1b9a9ff5ff3905173dcd0c5e11bc2aebd5c3472a139fa2b4                             0.0s
 => [internal] load build context                                                                                                                               0.0s
 => => transferring context: 17.80kB                                                                                                                            0.0s
 => CACHED [builder 2/6] WORKDIR /app                                                                                                                           0.0s
 => CACHED [builder 3/6] COPY go.* ./                                                                                                                           0.0s
 => CACHED [builder 4/6] RUN go mod download                                                                                                                    0.0s
 => [builder 5/6] COPY . ./                                                                                                                                     0.0s
 => [builder 6/6] RUN go build -v -o myapp                                                                                                                      7.8s
 => CACHED [stage-1 2/4] WORKDIR /app                                                                                                                           0.0s
 => CACHED [stage-1 3/4] COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter                             0.0s
 => [stage-1 4/4] COPY --from=builder /app/myapp /app/myapp                                                                                                     0.0s
 => exporting to image                                                                                                                                          0.0s
 => => exporting layers                                                                                                                                         0.0s
 => => writing image sha256:6c2f79b6321f0cd9b4e7693763d1fb56208c85cc54edf4c2ea1ce9ea25ab4733                                                                    0.0s
 => => naming to docker.io/library/lambda-container-blog:1afe4a5                                                                                                0.0s
Login Succeeded
The push refers to repository [xxx.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-container-blog]
9bec1d67c6e9: Pushed 
a809e9675065: Pushed 
e7f40a6b4e08: Pushed 
03459226eed7: Pushed 
1afe4a5: digest: sha256:9affcfcb65c0846891960046a8c25aadf5d3ac24a0117eebc39349688a9b3d61 size: 1157
Lambda function lambda-container-blog does not exist. Creating function...
Function URL for lambda-container-blog does not exist. Creating Function URL...
Public Lambda Function URL: https://xxx.lambda-url.ap-northeast-1.on.aws/, you can change auth-type to AWS_IAM

あっという間に、外部からアクセス可能な URL が表示されました。https://xxx.lambda-url.ap-northeast-1.on.aws/ の部分です。

IP 制限機能

ここまでは、外部からだれもアプリケーションをアクセスすることができますが、IP 制限をかけたい場合も多いと思います。Golang で IP 制限機能を入れてみましょうー

main.go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"

	"github.com/jpillora/ipfilter"
)

func topHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("hello world")
	io.WriteString(w, "hello world")
}

func addWhiteListIPs(f *ipfilter.IPFilter) {
	// Get the WHITE_IP_LIST environment variable
	whiteIPList := os.Getenv("WHITE_IP_LIST")

	// Check if the environment variable is set
	if whiteIPList == "" {
		fmt.Println("WHITE_IP_LIST environment variable is not set")
		return
	}

	// Split the WHITE_IP_LIST into separate IP addresses/CIDR ranges
	ipList := strings.Split(whiteIPList, ",")

	// Iterate over each IP/CIDR in the list
	for _, ip := range ipList {
		fmt.Println("IP/CIDR:", ip)
		f.AllowIP(ip)
	}
}

func main() {
	f := ipfilter.New(ipfilter.Options{
		AllowedIPs:     []string{"192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12"},
		BlockByDefault: true,
		TrustProxy:     true,
	})

	addWhiteListIPs(f)
	http.Handle("/", f.Wrap(http.HandlerFunc(topHandler)))
	http.ListenAndServe(":8000", nil)
}

環境変数 WHITE_IP_LIST に自分の IP アドレスを入れて、デプロイします。

# .env に自分の IP を設定します。
$ cat << EOF > .env
WHITE_IP_LIST=$(curl --silent https://checkip.amazonaws.com)/32
EOF

$ cat .env
WHITE_IP_LIST=xx.xx.xx.xx/32

# --env-vars-file を利用し、Lambda の環境変数を設定し、デプロイします。
$ ./lambda.sh deploy --env-vars-file .env

これで、IP 制限を実現できました!

分散トレーシング、ロギング

開発はトラブルシューティングが一番時間かかっていること、皆さんはよくご存知かと思います。X-Ray のような分散トレーシングの機能を利用すれば、どの関数がエラー出ていたとか、その時関数が使用した時間、関連するログなどをワン画面で表示することができます。Lambda では、手軽くに利用することができます。ここで Go アプリからの利用方法をご紹介します。

トレーシングに関しては、xray.Handler()およびxray.NewFixedSegmentNamer()を入れるだけで完了です。

http.Handle("/", xray.Handler(xray.NewFixedSegmentNamer("myApp"), f.Wrap(http.HandlerFunc(topHandler))))

たとえば、必要なデバッグ情報を入れたりもできます。ここでは、xray.AddAnnotation()xray.AddMetadata()を使って、必要な情報を入れたり、xrayloggerの方は、ログとトレーシングを結びつけることができます。

main.go
func topHandler(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	log := xraylogger.New(ctx)

	// Add annotation and metadata
	xray.AddAnnotation(ctx, "User", "example-user2")
	xray.AddMetadata(ctx, "debug-info", "sample metadata2")

	log.Info("hello world")
	io.WriteString(w, "hello world")
}

Console の方はこのように表示されます。
xray.jpg

また、ローカルでログを確認するケースもありますので、こちらのコマンドを利用すると便利です。

# 直近1分以内のログを live tail しながら表示します
$ aws logs tail --follow --since 1m /aws/lambda/関数名

ローカルでのテスト

コンテナを利用するメリットの一つは、ローカルだけで確認したい場合でも、同じ Dockerfile を使うことができ、。docker-compose.yaml のサンプルコードはこちらとなります。

compose.yaml
version: '3.8'
services:
  my-lambda-app:
    build: .
    ports:
      - "8000:8000"

ローカル環境を起動

$ docker-compose build
$ docker-compose up -d

http://localhost:8000/ にアクセスすれば、hello world が表示されています。

まとめ

AWS Lambda コンテナを使用したアプローチは、開発者が迅速にアイデアを実装し、手軽に検証するための強力なソリューションです。より簡単に利用できるように lambda-container-deploy を開発し、設定やデプロイメントの手間を減らし、開発に集中できる環境を提供しました。開発がある程度進んでいたら、忘れずに IaC を利用して、環境を構築していきましょう。では楽しい開発を是非体験してください。

(免責) 本記事の内容はあくまでも個人の意見であり、所属する企業や団体は関係ございません。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3