ν ν¬ λΈλ‘κ·Έ λͺ¨μ보기 κ°λ° κΈ°λ‘ (2) : Go μΈμ΄μ μλ²λ¦¬μ€ νλ μμν¬
μλ‘
μ΄ κΈμ Golang μ΄ν리μΌμ΄μ μ Serverless framework λ₯Ό μ΄μ©ν΄ AWS Lambda μ λ°°ν¬νλ κ³Όμ μ λ€λ£¬λ€.
νμ¬ μ΄μμ€μΈ μ¬μ΄λ νλ‘μ νΈ(π ν ν¬ λΈλ‘κ·Έ λͺ¨μ보기) λ Go μΈμ΄λ₯Ό κΈ°λ°μΌλ‘ AWS Lambda(μ΄ν λλ€)μ λ°°ν¬λμ΄ μλ€. μ²μ λλ€λ₯Ό λ°°μΈ λ AWS Console μμ μΌμΌμ΄ API Gateway λ₯Ό μ€μ νκ³ Lambda ν¨μμ μ½λλ₯Ό μ§μ μμ±ν κΈ°μ΅μ΄ λλλ°, λΉμλ§ ν΄λ κ°λ¨ν νμ΄μ¬ ν¨μλ₯Ό νΈμΆνλ μ λλΌ AWS Console μ μ½λλ₯Ό 볡λΆνλ μ λλ‘ λλ€λ₯Ό λ§λ³΄κΈ° νλ€.
νμ§λ§ μ€μ λ‘ μ΄μν μ ν리μΌμ΄μ
μ½λλ λ¨μ λ³΅μ¬ λΆμ¬λ£κΈ°λ‘ λλ€μ λ°°ν¬ν μ μμλ€. νμΌ μλ λ§μ λΏλλ¬ λ§€λ² λ°°ν¬ν λ λ§λ€ κ·Έλ° λ²κ±°λ‘μ΄ μΌμ ν μλ μκΈ° λλ¬Έμ΄λ€. 그리νμ¬ λλ€μ λ°°ν¬ μλνλ μ΄λ»κ² ν μ μμμ§λ₯Ό κ³ λ―Όνλ€ AWS CLI, AWS SAM, Serverless Framework λ± λ€μν ν΄μ μκ² λλ€. κ²°λ‘ μ μΌλ‘ νμλ Serverless Framework (μ΄ν μλ²λ¦¬μ€ νλ μμν¬) λ₯Ό μ ννκ³ κ°λ¨ν 컀맨λ μ
λ ₯λ§μΌλ‘ μ
λ°μ΄νΈκ° μμ λλ§λ€ κ°λ¨ν λ°°ν¬λ₯Ό μ§νν μ μκ² λλ€.
μ΄ κΈμμ μλ²λ¦¬μ€ νλ μμν¬λ 무μμΈμ§, Go/Gin μΉ νλ μμν¬λ‘ κ°λ°λ μ ν리μΌμ΄μ
μ λ°°ν¬νλ κ°λ΅μ μΈ λ°©λ²μ μμλ³΄κ² λ€.
Go μ AWS Lambda μ μ‘°ν©
go μΈμ΄ νΉμ± μ λλ€λ‘ λ°°ν¬νλ©΄ μ 격μ΄λΌκ³ λλ ν¬μΈνΈκ° λͺκ°μ§ μλ€.
μ°μ λΉ λ₯Έ μ΄κΈ° μ€ν μκ° (Cold start time)
Go λ μ»΄νμΌ μΈμ΄λ‘ λ°μ΄λ리 νμΌλ‘ λ³νλμ΄ λλ€μ μ 곡λλ€. μ΄λ λ°νμ μ λ³λμ μΈν°ν리ν°κ° νμ μμ΄ Python μ΄λ Node.js κ°μ μΈν°νλ¦¬ν° μΈμ΄λ³΄λ€ λλ€μμ λΉ λ₯΄κ² μ€νλ μ μλ€. μ½κ² λ§ν΄ μ€νν μ€λΉκ° μλ£λ μνλ‘ μ 곡λκΈ° λλ¬Έμ μ±λ₯λ©΄μμ ν¨μ¨μ μ΄λΌ λ³Ό μ μλ€.
λλ€λν¨μκ° νΈμΆλ λ μλ² μΈμ€ν΄μ€λ₯Ό μ¦μ μ€λΉνμ¬ μ€ννλλ°, μ΄ κ³Όμ μμ λλ€λ μλ‘κ² μμ±λ μΈμ€ν΄μ€μμ μ½λμ μ΄κΈ°νλ₯Ό μ§ννλ€. μ΄λ κ² μΈμ€ν΄μ€κ° μλ‘ μμλλ κ²μ μ½λ μ€ννΈ(Cold start) λΌκ³ νλ€. μ΄λ¬ν μ½λ μ€ννΈλ ν¨μκ° μ€λ μκ° λμ νΈμΆλμ§ μμκ±°λ μλ‘μ΄ μΈμ€ν΄μ€κ° μμ±λλ κ²½μ°μ λ°μνλ©°, μ΄ μ΄κΈ°ν κ³Όμ μμ μλ΅ μ§μ°μ΄ λ°μν μ μλ€. μ¬κΈ°μ Goμ μ»΄νμΌ μΈμ΄λΌλ νΉμ§μ λ³λμ λ°νμμ μꡬνμ§μκ³ κ³§λ°λ‘ μ€νν μ μκΈ° λλ¬Έμ μ½λ μ€ννΈ μκ°μ μ€μΌ μ μλ€. λν Go λ°μ΄λ리λ μΌλ°μ μΌλ‘ κ²½λμ΄κ³ λ©λͺ¨λ¦¬ μ¬μ©λμ μ΅μ ν λμ΄ μμ΄, μ΄κΈ°ν μ λ©λͺ¨λ¦¬ μ¬μ©λμ΄ μ μ΄ μ½λ μ€ννΈμ μν₯μ μ€μ΄λλ° λμμ΄ λλ€.
μμ λ°μ΄λ리 νμΌ ν¬κΈ°
λ€λ₯Έ μ»΄νμΌ μΈμ΄μΈ Java λ₯Ό μκ°ν΄λ³΄μ. Java λ μ»΄νμΌ λ ν λ°μ΄νΈ μ½λλ‘ λ³νλλ€. Java κ°λ°μλΌλ©΄ μ μλ€μνΌ μ΄ λ°μ΄νΈ μ½λλ₯Ό μ€ννλ €λ©΄ JVM μ΄λΌλ κ°μ λ¨Έμ μ νμλ‘ νλ€.
λ°λ©΄ Go λ λΉλ μ λ€μ΄ν°λΈ λ°μ΄λ리 νμΌμ μμ±νλ€. λ€μ΄ν°λΈ λ°μ΄λ리 νμΌμ΄λ νΉμ μ΄μ체μ μ CPU μ λ§κ² μ»΄νμΌλ μ€ν νμΌμ μλ―Ένλ€.
μ΄ λ°μ΄λ리 νμΌμ μμ€ν
μμ λ°λ‘ μ€νλ μ μμ΄ λ³λμ μΆκ° μννΈμ¨μ΄λ λ°νμ νκ²½μ νμλ‘ νμ§ μλλ€. μ¦ Go μ½λλ μ»΄νμΌ λλ©΄ μ체μ μΌλ‘ μ€ν κ°λ₯ν λ
립ν λ°μ΄λ리λ₯Ό μμ±νκ³ , μ΄λ₯Ό ν΅ν΄ AWS Lambda μμ λ³λμ μμ‘΄μ± μ€μΉ μμ΄ κ°λ¨ν λ°°ν¬ν μ μκ² λλ€. λν λ°μ΄λ리 νμΌμ ν¬κΈ°κ° μκΈ° λλ¬Έμ Lambda μ λ°°ν¬ ν¨ν€μ§ ν¬κΈ°κ° μμμ Έ μ
λ‘λ λ° λ°°ν¬ μλμλ ν¨μ¨μ μΈ μν₯μ μ€ μ μλ€.
Go μ Lambda
Go μ ν리μΌμ΄μ μ μν μΉ νλ μμν¬μλ μ¬λ¬ μ’ λ₯(Gin, Fiber, Echo etc)κ° μκ³ νμλ Gin μ μ ννλ€. Gin μ κ²½λ μΉ νλ μμν¬λ‘ λλ€μμ μ€ν μ Cold start κ° λ°μν λλ λΉκ΅μ λΉ λ₯΄κ² μ€νν μ μλ¨ μ₯μ μ΄ μμ΄ μ ννλ€. 무μλ³΄λ€ μ°Έκ³ ν μλ£κ° κ°μ₯ νλΆν μ λ μ νμ ν λͺ«νλ€.
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/awslabs/aws-lambda-go-api-proxy/gin"
"log"
)
var ginLambda *ginadapter.GinLambda
func main() {
log.Println("π Start Lambda")
lambda.Start(Handler)
}
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return ginLambda.ProxyWithContext(ctx, request)
}
main ν¨μ
lambda.Start ν¨μλ λλ€κ° μμλλ©΄ Handler ν¨μκ° νΈμΆλλλ‘ μ€μ νλ€. Handler λ λλ€μ μν΄ νΈλ¦¬κ±°λλ μ΄λ²€νΈλ₯Ό νΈλ€λ§ν ν¨μλ‘, μ½κ² λ§ν΄ λλ€κ° μ€νλ λ νΈμΆλλ ν¨μλΌκ³ 보면 λλ€.
Handler ν¨μ
ctx
: λλ€κ° μ 곡νλ context.Context κ°μ²΄λ‘, λλ€ μ€νμ κ΄λ ¨λ νμμμμ΄λ μ’
λ£ μ νΈ λ± μ¬λ¬ λ©νλ°μ΄ν°λ₯Ό λ΄κ³ μλ€.
request
: events.APIGatewayProxyRequest
νμ
μΌλ‘ API Gateway λ₯Ό ν΅ν΄ λ€μ΄μ¨ HTTP μμ²μ λνλΈλ€.
ginLambda.ProxyWithContext(ctx, request)
: ginLambda λ λλ€μ μ°λμ λλ ν¬νΌ κ°μ²΄λ‘, ProxyWithContext
λ©μλλ₯Ό μ¬μ©ν΄ request κ°μ²΄λ₯Ό gin νλ μμν¬μμ μ²λ¦¬ν μ μλ νμμΌλ‘ request λ₯Ό λ³ννκ³ , response κ²°κ³Όλ λ°νν΄μ€λ€.
Gin μΉνλ μμν¬λ₯Ό λλ€μμ μ¬μ©ν μ μλλ‘ κΈ°λ³Έμ μΈ μΈν μ μλ£νμΌλ, ping μ λ λ € ν΅μ ν μ μλλ‘ λΌμ°ν°λ₯Ό μ€μ ν΄λ³΄μ.
func init() {
log.Println("β
Starting initialization...")
gin.SetMode(gin.ReleaseMode)
config := cors.DefaultConfig()
config.AllowOrigins = []string{"*"} // Allow all origins
config.AllowMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization"}
config.ExposeHeaders = []string{"Content-Length"}
config.AllowCredentials = true
config.MaxAge = 12 * time.Hour
r.Use(cors.New(config))
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
ginLambda = ginadapter.New(r)
log.Println("β
Initialization complete")
}
init ν¨μ
Go μμ init ν¨μλ ν¨ν€μ§κ° λ‘λλ λ μλμΌλ‘ νΈμΆλλ νΉλ³ν ν¨μλ‘, λͺ μμ μΌλ‘ νΈμΆνμ§ μμλ ν¨ν€μ§κ° μ²μμΌλ‘ λ‘λλλ μμ μ μλμΌλ‘ νΈμΆλλ€. μ¦ main ν¨μλ³΄λ€ λ¨Όμ νΈμΆλλ€κ³ λ³Ό μ μλ€. init μ΄ νΈμΆλλ©΄μ Gin μλ² μ€μ κ³Ό λΌμ°ν° λ±λ‘, μμ‘΄μ± μ΄κΈ°ν λ±μ μμ μ μ€μ ν ν main ν¨μλ‘ λμ΄κ°κ² λλ€.
gin.SetMode(gin.ReleaseMode)
: Ginμ Release λͺ¨λλ‘ μ€μ νλ€. μ΄λ λ‘κ·Έλ₯Ό μ΅μννκ³ μ±λ₯μ λμ΄λ μ€μ μΌλ‘, AWS λλ€μ²λΌ 리μμ€ μ μ½μ΄ μλ νκ²½μμ ν¨κ³Όμ μ΄λ€.
gin.Default()
: Gin λΌμ°ν° κ°μ²΄λ₯Ό κΈ°λ³Έ μ€μ μΌλ‘ μμ±νλ€. Default ν¨μλ λ‘κΉ
κ³Ό 리컀λ²λ¦¬ λ―Έλ€μ¨μ΄λ₯Ό κΈ°λ³Έμ μΌλ‘ ν¬ν¨νλ€.
ginadapter.New(r)
: Gin λΌμ°ν°λ₯Ό λλ€μμ μ¬μ©ν μ μλ νΈλ€λ¬λ‘ λ³ννλ ν¨μλ‘, gin.Default() κ° ν λΉλ r λ³μλ₯Ό μΈμλ‘ λ겨 GinLambda κ°μ²΄λ₯Ό μμ±νλ€. Gin μΌλ‘ λ±λ‘ν μ¬λ¬ λΌμ°ν°λ₯Ό AWS λλ€μμ μ¬μ©ν μ μλλ‘ λ³ννλ ν¨μλΌ μκ°ν μ μλ€.
Serverless Framework λ‘ λ°°ν¬
λλ€λ‘ μ΄ν리μΌμ΄μ μ λ°°ν¬ν κ²½νμ λ§μ§ μμ§λ§, π Serverless Frameworkλ₯Ό μ¬μ©νλ©΄ κ°λ¨ν λ°°ν¬ν μ μλ€λ κ²μ μ΅ν λ€μ΄μ¨ν°λΌ μ΄λ² κΈ°νμ μ¬μ©ν΄λ³΄κΈ°λ‘ νλ€.
μλ²λ¦¬μ€ μ ν리μΌμ΄μ μ λ°°ν¬ μλν μ νμ§λ‘λ AWS CLI λ AWS SAM(Serverless Application Model) μ νμ©νλ λ°©λ²λ μμ§λ§, μ΅μ’ μ μΌλ‘ μλ²λ¦¬μ€ νλ μμν¬λ₯Ό μ ννλ€. AWS SAM λ λΉμ·ν μλν κΈ°λ₯μ μ 곡νμ§λ§ μλ²λ¦¬μ€ νλ μμν¬μ λΉκ΅νλ©΄ μ΄κΈ° λ°°ν¬κ³Όμ μ μ€μ κ³Ό λ λ§μ μ μΈμ λ°©μμ΄ νμν΄μ λ°°μ νλ€. AWS CLI λ νλ₯ν μ νμ΄μ§λ§ λ³λμ μ€μ νμΌ μμ΄ λͺ λ Ήμ΄ κΈ°λ°μΌλ‘ λμνκΈ°μ λ°λ³΅μ μΈ μμ μ λΉν¨μ¨μ μΌ κ²μ΄λΌ νλ¨νλ€.(μ¬μ€ λͺ λ Ήμ΄ λ°λ³΅μ΄μΌ shell script λ‘ λλ±λλ± νλ©΄ λ κ² κ°κΈ°λ..?)
κ²°μ μ μΌλ‘ Serverless Framework λ κ° μΈμ΄μ λ§λ νλ‘μ νΈ ν νλ¦Ώμ΄ μ μ 곡λμ΄ μμ΄ AWS CLI λ³΄λ€ λμ μ νμ΄λΌ νλ¨νλ€. μμμ μΈκΈν μ΄λ―Έμ§λ serverless.yml μ μΌλΆμΈλ° Go μ ν리μΌμ΄μ μ λΉλνκΈ° μν Go λΉλ λͺ λ Ήμ΄λ κΈ°λ³Έ ν νλ¦Ώμ ν¬ν¨ν μ μλ€.
μλ²λ¦¬μ€ νλ μμν¬λ μ΄λ€ μμΌλ‘ μλνκΈΈλ λλ€μ λ°°ν¬λ₯Ό νΈλ¦¬νκ² λμμ£Όλκ±ΈκΉ?
κ²°λ‘ λΆν° λ§νλ©΄ μλ²λ¦¬μ€ νλ μμν¬λ AWS μ Cloudformation μ΄λΌλ μλΉμ€λ₯Ό μ΄μ©νλ€. AWS λ΄μμ νμν μλΉμ€λ₯Ό JSON/YAML νμΌλ‘ νμμ λ§μΆ° μ λ¬νλ©΄ νμν μλΉμ€λ₯Ό μλμΌλ‘ μμ±/μμ ν΄μ£Όλ μλΉμ€μΈ μ
μ΄λ€.
μλλ νμκ° μλ²λ¦¬μ€ νλ μμν¬λ‘ μ ν리μΌμ΄μ μΌλ‘ λ°°ν¬νκΈ° μν΄ νμν yml νμΌμ μ λ¬Έμ΄λ€.
serverless.yml
service: serverless-go
provider:
name: aws
runtime: provided.al2
region: ap-northeast-2
stage: ${opt:stage, 'dev'}
functions:
api:
handler: bootstrap
timeout: 30 # Increase overall function timeout
events:
- http:
path: /{proxy+}
method: ANY
environment:
GO_LOG: info
package:
patterns:
- '!./**'
- './bootstrap'
custom:
golang:
cmd: GOOS=linux GOARCH=amd64 go build -o bootstrap main.go
serverless.yml νμΌκ³Ό λλΆμ΄ ν΄λΉ νμΌμ μ€ννλ μ€ν¬λ¦½νΈλ₯Ό μλνν Makefile λ μμ±ν΄μ£Όμ. μ΄λ₯Ό νμ©νλ©΄ λ§€λ² λΉλ λ° λ°°ν¬ λͺ
λ Ήμ΄λ₯Ό μΌμΌμ΄ μ
λ ₯ν νμμμ΄ λ¨μν make deploy
λͺ
λ Ήλ§μΌλ‘ Go μ ν리μΌμ΄μ
λΉλλΆν° Serverless Framework λ‘ Lambda μ λ°°ν¬κΉμ§ ννμ μ§νν μ μλ€.
Makefile
build:
GOOS=linux GOARCH=amd64 go build -o bootstrap main.go
chmod +x bootstrap
deploy: build
serverless deploy --stage prod
clean:
rm -f bootstrap
μ΄λ κ² λ°°ν¬κΉμ§ μ§ννλ©΄ AWS Console μμ μλΉμ€κ° λ°°ν¬λ κ²μ νμΈν μ μλ€.
λ€λ§ μ£Όμν μ μ ν΄λΉ μ ν리μΌμ΄μ μμ μΈλΆ connection μ λ§Ίλ κ³Όμ μ΄ μ λλ‘ μ΄λ£¨μ΄μ§μ§ μμΌλ©΄ λ°°ν¬μ μ€ν¨νλ€(λΉμ°ν μ리긴 ν¨). κ·Έλμ λ°°ν¬λ λλ€ ν¨μκ° μΈλΆ connection κ³Ό μ μ°κ²°λλμ§, ν¬νΈ κ°λ°©μ λμ΄ μλμ§ μ νμΈν΄λ΄μΌ νλ€.
κ²°λ‘
μλ²λ¦¬μ€ νλ μμν¬λ₯Ό νμ©νμ¬ Go μ ν리μΌμ΄μ μ λλ€μ λ°°ν¬νλ κ³Όμ μ μ 리νλ€. μκ°λ³΄λ€ κΈμ΄ μ§§κ² λλ¬λλ° κ·Έλ§νΌ μλ²λ¦¬μ€ νλ μμν¬λ₯Ό μ¬μ©νλ©΄ κ°λ¨νκ² Go μ΄ν리μΌμ΄μ μ λ°°ν¬ν μ μλ€. λ°°ν¬ μ΄μΈμλ μλ²λ¦¬μ€ νλ μμν¬λ₯Ό ν¨μ¨μ μΌλ‘ μ¬μ©ν μ μλ λ°©λ²μ 무μμ΄ μμμ§ μ’ λ κ³ λ―Όμ΄ νμν κ² κ°λ€. νμ¬λ λ¨μ λ°°ν¬ μ©λλ‘ μ¬μ©μ€μΈλ° μ΄ κ³Όμ μ μλννλ, μλ₯Ό λ€μ΄ Github ACtions μ μ°λνμ¬ μ격 λ ν¬μ§ν 리μ νΈμνκ±°λ main λΈλμΉμ λ¨Έμ§ν λλ§λ€ λ°°ν¬κ° λλλ‘ κ°μ ν΄ λ³Ό μ μκ² λ€.