TECHNICAL BLOG

2021/1/15 # 入門 # やってみた # AWS # Lambda 2021/1 はじめてのGo言語でのAWS Lambdaの実装方法

こんにちは、本社所属の藤中です。前回は、Lambda関数でPythonを利用したLine Botの実装に関する記事を書かせていただきました。
今回業務においてGo言語(Golang)を用いてのLambda関数の実装を行ったのですが、Pythonとはかなり実装方法が異なっておりましたので、Go言語初学者の視点からその実装方法に関して簡単にまとめていこうと思います。
以降の目次に沿って進みますが、必要に応じて飛ばしてください。

目次

  1. はじめに
    0-1. Go言語とは
    0-2. パッケージとは
  2. 前準備
    1-1. Goインストール
    1-2. Go Modules
  3. コードの概要
  4. コード作成
  5. コードデプロイ
  6. AWS Lambdaの設定
  7. まとめ

0. はじめに

今回は以下の図のような形でHello Golang!と出力するコードを組み、AWS Lambdaへのアップロード、その実行を行っていきます。

Overview

まず簡単にGo言語についてまとめます。

1. Go言語とは

GO_LOGO

Go言語(Golang, Go)とは2009年にGoogleによって設計されたプログラム言語です。構文はC言語に似ていますが、様々な言語の良いところを合わせたような言語です。具体的には以下のような特徴があります。

  • JavaやC++のように静的に型付され、巨大なシステムでもスケールすることができる。
  • RubyやPythonなどの動的な言語のように生産性が高く、リーダブルであり過度なボイラープレートがない。
  • コンパイル・実行速度の速さがCやC++よりも早い。
    (By http://golang.jp/about_go)

今回は構文等の説明は省略させていただきますが、Go言語におけるパッケージについて少し触れておきます。

2. パッケージとは

パッケージとは、作成したコードを機能や種類(構造体や関数など)で分割して管理・使用するための仕組みです。
作成する各goファイルの一行目にpackage 〇〇のように記述することでパッケージを設定することができます。実行可能なアプリケーションをGo言語で作成するにはパッケージに関して以下のようなルールがあります。

  • すべてのgoファイルは何らかのパッケージが設定されていなければならない
  • アプリケーションの実行においてmainパッケージが必ず必要であり、作成するアプリケーションのディレクトリの直下に配置する必要がある。
  • goファイルが存在するディレクトリ名とパッケージ名は同じである必要がある。(mainは例外)

文章だけでは少しわかりづらいのですが、2.コードの概要以降で上記のルールに触れながら具体的に説明しているので簡単に理解できていれば読み進めてください。

それでは実際にGo言語での実装の準備を行っていきます。

1. 前準備

1. Go インストール

まず、こちらで自身の操作するデバイスに対応するファイルをダウンロードし、インストールまで行ってください。

go_download

インストールの際、Go言語のインストール先を指定する必要があるのですが、特に手を加えず、初期表示の場所で行ってください。変更することは可能なのですが、Go言語特有のGO ROOTというものを変更する必要があります。基本的には手を加えないのが吉だと思います。

ダウンロード後は以下のコードをターミナルで入力し、versionを確認してください。以下のように無事versionが出力された場合はインストール成功です。

go-version

さて、インストールしたGo言語のディレクトリは以下のようになっているはずです。

  • src :ソースコードが保存されています。今後作成するコードなどはこちらのディレクトリで管理します。
  • pkg:ビルドしたパッケージオブジェクトが格納されるディレクトリ。依存パッケージのオブジェクトファイルが格納されます。
  • bin :実行ファイルが格納されるディレクトリ。作成したバイナリファイルが格納されます。

実際にコードを組む際はsrc直下にディレクトリやソースを作成して、コマンドを実行することでbinやpkgにファイルが作成されるので、細かいところは気にしなくても大丈夫です。今後の作業は基本的にsrc直下で行います。

2. Go Modules

他の言語でもそうですが、言語にデフォルトで存在しないパッケージ(モジュール)を使用するにはそのモジュールをインポートする必要があります。
Go言語では外部のモジュールを使用するために、コマンドでgo get 〇〇と入力してローカル環境にそのモジュールをインポートする必要があります。しかし、複数のアプリケーションでそれぞれ異なるバージョンのモジュールを使用する際やアプリケーションごとで使用しているモジュールの管理をしたい際などにおいて幾つかの問題がありました。
Go1.11以降ではGo Modulesというものを使用することでその問題を解決することができます。このGo ModulesはGo言語の公式が推奨しているものでもありますので、今回はこちらを使用しての実装を行なっていきます。
今回の記事では主にGo Modulesを使用(初期化)するためにgo mod init、実際にモジュールをインポートするためにgo buildの二つのコマンドを使用します。その他の基本の使い方はこちらを参考にしてください。

2. コードの概要

今回は以下のような構成で”Hello Golang!”と出力するコードを組んでいきます。

 go/
   ├ bin/
   ├ pkg/
   └ src/
      └ TestLambda/
            ├ handler.go (package: main)
            │
            └ greeting/
                  │
                  └ hello.go (package: greeting)

まずはsrc直下にTestLambda(任意)というディレクトリを作成します。このディレクトリが一つのアプリケーションになります。また、今回はGo Modulesを使用するのでディレクトリを作成したタイミングでgo mod initを実行しておきましょう。ディレクトリ直下にgo.modファイルが作成されていればGo Modulesが使用できる状態になっています。

mkdir TestLambda
cd TestLambda
go mod init

Go言語のパッケージとはで説明した通り、このディレクトリの直下にはパッケージ名がmainで任意の名前のgoファイルを作成する必要があります。今回のように実装内容が少なければこのgoファイル内で全ての処理を記入しても良いのですが、より実務的なアプリケーションを作成する場合はコードのディレクトリ構成(アーキテクチャ)を考える必要があります。この記事ではLambda関数をGo言語で実装することを目的としているのですが、ディレクトリ構成を意識して自身で作成したパッケージの関数の呼び出しを行ってみようと思います。コードのディレクトリ構成については様々な手法や考え方がありますので、詳しくは「プログラム アーキテクチャ」等で調べてみてください。

今回はmainパッケージと別に、”Hello Golang!”と出力する機能を持つgreetingパッケージを作成します。アプリケーションのディレクトリ直下にgreetingというディレクトリを作成し、hello.go(package: greeting)ファイルを作成しましょう。なお、パッケージ名が同じであれば複数のファイルをgreetingディレクトリに配置することも可能です。以下簡単なアプリケーションの例を載せておきます。

 go/
   ├ bin/
   ├ pkg/
   └ src/
      └ TestLambda/
            ├ handler.go (package: main)
            ├ greeting/
            │    │
            │    ├ hello.go       (package: greeting)
            │    └ goodMorinig.go (package: greeting)
            │
            └ cooking/
                 └ sushi.go       (package: cooking)

複数のアプリケーションを作成したければ、src直下に他の名前でディレクトリを作成することで独立した別のアプリケーションを作成することができます。
それでは実際にコードを書いてみましょう。

3. コード作成

TestLambda/handler.go

//アプリケーションのディレクトリ直下はmainパッケージである必要があります。
package main
//①
import(
    “github.com/aws/aws-lambda-go/lambda”
    “TestLambda/greeting”
)
//②
func excuteFunction(){
    greeting.SayHello()
}
//mainパッケージはmain関数を保持している必要があります。
//lambda.Start()はlambda関数での実装において記述してある必要があります。
//引数の関数名(excuteFunction)は任意です。
func main(){
    lambda.Start(excuteFunction)
}

TestLambda/greeting/hello.go

//パッケージ名はディレクトリと同じものにする必要があります。
package greeting
import(
    “fmt”
)
//他のディレクトリ(パッケージ)から呼び出される関数は大文字から始まる必要があります。
//意外と間違えやすいポイントなので注意しましょう。
func SayHello(){
    fmt.Println(“Hello Golang!!”)
} 

① 関数内で使用するモジュール(ライブラリ)をimportします。
今回はGo言語でLambda関数を実装するために、”github.com/aws/aws-lambda-go/lambda”という外部モジュールと、自身で作成したTestLambda/greetingパッケージのインポートをします。自身で作成したモジュールをインポートする際に入力するパスはsrc直下をカレントディレクトリとした際の相対パスになります。
今回外部モジュールのインポートのためにGo Modulesを使用しているので、コードを作成後にコンソールでアプリケーション直下に移動して、go buildと入力してで”github.com/aws/aws-lambda-go/lambda”モジュールをインポートする必要があります。

#アプリケーションに移動
cd TestLambda
go build

この時、mainパッケージで呼び出すgreetingパッケージで使用(import)している外部モジュールも同時にインポートしてくれます。goファイルごとにgo buildを実行するのではなく、アプリケーションディレクトリの直下で実行するだけで良いことに気をつけてください。

② main関数で呼び出される関数です。
この関数では、設定できる引数や返り値について制限があります。今回の実装においては特に気にする必要はないのですが、必要に応じてAWSの公式ドキュメントを確認してください。


個人的にはTestLambda/handler.goはテンプレートのように固定しておき、作成したいアプリケーションの機能に応じて呼び出すパッケージの内容を変える今回のような方法がスマートで良いと考えています。

4. コードデプロイ

コードをAWS Lambdaに実際に実装するためには、作成したコードを実行可能ファイルにコンパイルし、zipファイルにする必要があります。

  • モジュールインポート
    Go Modulesでのインポートし忘れがないようにしましょう。go.sumというファイルが存在していればモジュールのインポートが完了しています。

    go build
  • コンパイル
    アプリケーション直下のmain関数を保持しているgoファイルをコンパイルして実行可能なバイナリファイルを作成します。
    以下のコマンドを入力することで指定したファイル(handler.go)で使用(import)されているパッケージのコードも全てコンパイルして一つの実行可能ファイルを作成することができます。
    この時作成されるバイナリファイル名はのちのAWS Lambdaの設定で使用しますのでメモしておきましょう。

#GOOS=linux go build -o バイナリファイル名 goファイル名
GOOS=linux go build -o handler handler.go
  • zip化
    AWS Lambdaにコードをアップロードするために、作成した実行可能バイナリファイルをzip化する必要があります。
#zip ファイル名 バイナリファイル名
zip function.zip handler

公式のドキュメントでも詳しく記述してあるのでぜひ読んでみてください。
ここまででディレクトリは以下のようになっているはずです。

directory

これで作成したアプリケーションをAWS Lambdaに実装する準備ができました。

5. AWS Lambdaの設定

それでは実際に作成したコードをLambda関数に実装していきます。なお、AWSアカウントを保持している前提で話を進めていきますので、ご了承ください。


  1. Lambda関数作成

AWSにログインして以下のようにLambda関数を作成します。

makeLambdaFunction

  • 関数名 :HelloGolang(任意)
  • ランタイム:Go 1.x(2020/11/29現在)

それ以外の箇所はデフォルトで作成してください。
*ただし、作成したコード内で他のAWSサービスにアクセスするような機能を実装している場合はその内容に応じて実行ロール(IAM)を編集し付与する必要があります。

  1. goファイルアップロード

作成したLambda関数にコンパイル、zip化したコードをアップロードします。関数コードという欄のアクションを押下し、「.zipファイルをアップロード」を選択します。

uploadZip

その後表示されるポップアップでアップロードを押下し、作成した.zipファイルを選択します。その後、保存ボタンを押下することで、Lambda関数に作成したコードをアップロードすることができます。

confirmUploadingZip

ここまででコードのアップロードまで行ったのですが、ハンドラ設定を行わなければ、まだ実行することができません。

  1. ハンドラ設定

Go言語におけるハンドラとは、実行可能ファイル名のことです。4.コードデプロイでzip化した実行可能バイナリファイル名を設定します。
まずは、ランタイム設定の編集を押下します。

settingRuntime

ハンドラの項目がデフォルトではhelloとなっているのですが、こちらを4.コードデプロイでメモしておいた、実行可能バイナリファイルの名前に変更します。

settingHandlerName

これで作成したコードをLambda関数に実装し、実行できる状態になりました。

  1. テスト実行

実際に実行してみて動作を確認してみましょう。テスト実行を行うにはテストイベントを作成する必要があります。右上にあるテストを押下してテストイベントを作成します。
イベント名を入力して作成を押下します。

makeTestEvent

これでテストイベントが作成されました。テストボタンの左側のセレクトボックスにて先ほど作成したテストイベント名が選択されていることを確認後、再度テストボタンを押下するとテスト実行をすることができます。

executeTestEvent

以下の画像のような結果になれば成功です。ログ出力の欄にHello Golang!!と出力されていることが確認できました。

testResult

今回はテストでの実行でしたが、AWSのEventBridgeというサービスを使用して毎日決まった時間に実行させたり、5分毎に実行させたりすることも可能です。他にもAPIによる実行等の様々な方法で実行することができるので、他にも調べて試してみてください。

6. まとめ

今回はGo言語でLambda関数を実装してみました。前回の記事のPythonでの実装とは違いデプロイやzip化の必要があったり、Go言語特有のパッケージの概念の理解に時間がかかったりしまったので同じように悩んでいる方の手助けになればと思います。
また、今回はコードのデプロイやAWS Lambdaへのアップロードを手動で行いましたが、Bitbucket PipelineやAWS CodePipeline等を使用してCI/CDを自動で行うことで人為的なミスを減らすこともできると思います。