Slide 1

Slide 1 text

Lambda関数を Goで実装してみた話 株式会社ユニクエスト 長谷川広樹 github.com/hiroki-it @Hiroki__IT

Slide 2

Slide 2 text

自己紹介 ▼ お仕事 最近:クラウドインフラ、IaC、CICD、... 以前:DDD ▼ 関心のある技術領域 ・クラウドインフラ ・IaC ・DDD github.com/hiroki-it @Hiroki__IT 長谷川 広樹 (はせがわ ひろき) 株式会社ユニクエスト

Slide 3

Slide 3 text

目次 ■ Goの概要 ■ ディレクトリ構造 ■ Goの基本文法 ■ Goファイルの要素 ■ Lambdaを使用した通知処理

Slide 4

Slide 4 text

Goの概要(1) 手続き型言語のため,オブジェクト機能なし. 第9回 UMTPモデリング技術 ワークショップ https://umtp-japan.org/

Slide 5

Slide 5 text

Goの概要(2) 静的型付け言語のため,ソースの実行前にビルドが必要. ビルドの成果物 = アーティファクト https://www.atmarkit.co.jp/ait/articles/1105/23/news128.html

Slide 6

Slide 6 text

Goの概要(3) 実装方法が強制されるため,可読性が高く,後続者が開発がしやすい 実装がスケーリングしやすい 静的解析のルールが厳しいため,バグを事前に見つけられる バグが許されない基盤部分に適している

Slide 7

Slide 7 text

ディレクトリ構造(1) ・bin  ビルドされたアーティファクト(バイナリファイル)を配置. ・pkg  アーティファクトとは別に生成されるファイルを配置. ・src  ソースコードを配置. $GOPATH # 決まりは無いが,$HOME/go とすることが多い ├── bin ├── pkg └── src

Slide 8

Slide 8 text

ディレクトリ構造(2) notify-slack-of-amplify-events # srcに相当 │ ├── build │  └── Dockerfile ├── cmd │  ├── main.go │  ├── controllers │  ├── entities │  └── usecases │ ├── config ├── test ├── .air.toml ├── docker-compose.yml ├── go.mod └── go.sum https://github.com/hiroki-it/notify-slack-of-amplify-events ディレクトリ構造ベストプラクティス:https://github.com/golang-standards/project-layout

Slide 9

Slide 9 text

ディレクトリ構造(3) ・build  Dockerfileを配置. ・cmd  main.goファイルや,サブmainパッケージを配置. src # ソースコードを配置 ├── build └── cmd    ├── main.go    ├── controllers    ├── entities    └── usecases

Slide 10

Slide 10 text

Goファイルの要素(1) package <パッケージ名> // 名前空間の宣言 import "<パッケージ名>" // パッケージの読み込み func Xxx(){ // 関数 } ・一つのディレクトリ内では一つのパッケージ名のみ. ・関数名は,頭文字が大文字だとパブリック,小文字だとプライベートになる.

Slide 11

Slide 11 text

Goファイルの要素(2) package main func main(){ } ・エントリポイントは,cmdディレクトリまたはその子ディレクトリに配置. ・パッケージ名はmain. ・関数名はmain.

Slide 12

Slide 12 text

Goファイルの要素(3) func Division(x int, y int) (int, int) { // 商を計算する. quotient := x / y // 余りを計算する. remainder := x % y // 商と余りを返却する. return quotient, remainder } ・引数と返却値の型に厳格. ・複数の値を返却可能.

Slide 13

Slide 13 text

Goの環境構築(1) FROM golang:1.15 ENV CGO_ENABLED=0 # C言語製のライブラリの有効化 ENV GOOS=linux # Goが稼働するOS(※GoはOSに縛られない) ENV GOARCH=amd64 # CPUアーキテクチャ ENV GO111MODULE=on # go.modの有効化 WORKDIR ${GOPATH}/src 最近,『go.mod(≒ composer.json)』『go.sum(≒ composer.lock)』が導入

Slide 14

Slide 14 text

Goの環境構築(2) module github.com/hiroki-it/notify-slack-of-amplify-events go 1.15 require ( # プロトコルを除いたURLとバージョンでパッケージを必ず指定 github.com/aws/aws-lambda-go v1.23.0 github.com/stretchr/testify v1.7.0 ) replace ( # mainパッケージの共通部品も,インターネット上に存在することを強制 github.com/hiroki-it/notify-slack-of-amplify-events/cmd/entities/xxx => /cmd/entities/xxx ) あらゆるパッケージはインターネット上にリリースされるべきという思想 go.modファイル

Slide 15

Slide 15 text

Goの環境構築(3) # インストールのキャッシュを活用するためにコピーしておく. COPY go.mod go.sum ./ # パッケージをインストールする. RUN go get -u github.com/cosmtrek/air \ && go mod download -x COPY . . ・mod downloadコマンド  go.modファイルに実装したパッケージをインストール

Slide 16

Slide 16 text

Goの環境構築(4) https://github.com/cosmtrek/air ソースコード変更の度にビルドを行うホットリロードツール『Air』

Slide 17

Slide 17 text

Goの環境構築(5) # ビルドのアーティファクトを/go/binに配置する. RUN go build -x -o go/bin ./cmd CMD ["/go/bin/cmd"] ・cmdディレクトリ  ベストプラクティスに則り,cmdディレクトリにソースコードを配置. ・buildコマンド  ソースコードをビルドし,binディレクトリにアーティファクトを配置.

Slide 18

Slide 18 text

Lambdaを使用した通知処理(1) package main import ( "github.com/aws/aws-lambda-go/lambda" "github.com/hiroki-it/notify-slack-of-amplify-events/cmd/controllers/handler" ) // lambdaパッケージからStartメソッドをコール func main() { lambda.Start(handler.HandleRequest) // slackパッケージのHandler関数をコール } main.goファイル

Slide 19

Slide 19 text

Lambdaを使用した通知処理(2) func HandleRequest(request Request) error { var event eventbridge.Event // EventBridgeから転送されたJSONを受信し,構造体にマッピングします. err := json.Unmarshal([]byte(request.Records[0].EventBridge.Event), &event) slackClient := slack.NewSlackClient() message := slackClient.BuildMessage(event) return slackClient.PostMessage(message) } 構造体?? 構造体とJSONのマッピング?? handler.goファイル

Slide 20

Slide 20 text

Lambdaを使用した通知処理(3) type Person struct { Name string // 構造体が持つデータの型 } func main() { person := Person{"Hiroki"} // 構造体にデータを設定する. fmt.Printf("%#v\n", person.Name) // "Hiroki" } ・構造体とは,メソッドは持たず,データのみを持つもの. ・関数に構造体を関連付けることで,オブジェクトを擬似的に表現可能.

Slide 21

Slide 21 text

Lambdaを使用した通知処理(4) type Person struct { // 構造体 Name string } func (person Person) SetName(name string) { // セッター関数を構造体に関連付ける person.Name = name } func (person Person) GetName() string { // ゲッター関数を構造体に関連付ける return person.Name } func main() { person := Person{Name: "Gopher"} person.SetName("Hiroki") // セッター関数で値を上書きする fmt.Printf("%#v\n", person.GetName()) // "Gopher" }

Slide 22

Slide 22 text

Lambdaを使用した通知処理(5) type Event struct { Version string `json:"version"` ... Detail struct { AppId string `json:"appId"` BranchName string `json:"branchName"` JobId string `json:"jobId"` JobStatus string `json:"jobStatus"` } `json:"detail"` } JSONと構造体で相互変換するため,構造体の定義時にJSONマッピングが必要 { "event": { "version": "0", ... "detail": { "appId": "dd31ugx1agx51", "branchName": "feature/293_deploy_to_amplify", "jobId": "2", "jobStatus": "SUCCEED" } } }

Slide 23

Slide 23 text

Lambdaを使用した通知処理(6) func Handler(request Request) error { var event eventbridge.Event err := json.Unmarshal([]byte(request.Records[0].EventBridge.Event), &event) slackClient := slack.NewSlackClient() // 通知メッセージのJSONを構成します. message := slackClient.BuildMessage(event) return slackClient.PostMessage(message) } https://github.com/hiroki-it/notify-slack-of-amplify-events/blob/develop/cmd/ buildMessageメソッドで構成される構造体は,slackのtype.goの閲覧を推奨󰢛

Slide 24

Slide 24 text

Lambdaを使用した通知処理(7) func postMessage(message Message) error { json, err := json.Marshal(message) // マッピングを元に,構造体をJSONに変換する. request, err := http.NewRequest( // リクエストメッセージを定義する. "POST", os.Getenv("SLACK_API_URL"), bytes.NewBuffer(json), ) ... ・JSONをMessage構造体にマッピング. ・POSTリクエストの必要なパラメータを設定. post.goファイル

Slide 25

Slide 25 text

Lambdaを使用した通知処理(8) … request.Header.Set("Content-Type", "application/json") // ヘッダーを定義する. request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("SLACK_API_TOKEN"))) client := &http.Client{} response, err := client.Do(request) // HTTPリクエストを送信する. defer response.Body.Close() // deferで宣言しておき,HTTP通信を必ず終了できるようにする. return nil } ・クライアントを起動し,リクエストを送信. ・defer宣言された関数は,たとえ処理が停止しても最後に必ず実行される.

Slide 26

Slide 26 text

おまけ:例外処理 json, err := json.Marshal(...) if err != nil { return err } request, err := http.NewRequest(...) if err != nil { return err } ・Goの思想では例外処理は無駄なものとされ,try-catchが無い. ・多くの関数は二つ目の返却値にエラーを返すため,毎回検証するしかない.

Slide 27

Slide 27 text

最後に Gopherくん 超かわいい!!! by Takuya Ueda (https://twitter.com/tenntenn) The Gopher character is based on the Go mascot designed by Renée French.