ちょっと話題の記事

AWS Batch + Golangでサーバレスなバッチ処理をしてみる

2018.02.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

サーバレスなバッチ処理を行うことを考えると候補としてAWS Lambdaが上がってくるかと思います。ですがLambdaには執筆時現在、5分でタイムアウトするという制限があります。

そこで今回、AWS Batchを使用してタイムアウトを気にせずに行うバッチ処理を実装してみました。

実装した処理について

Golangでバッチ処理を実装して実行ファイル(バイナリファイル)を作成します。作成した実行ファイルをAWS Batchにて起動し、(タイムアウトを気にしないという意味で)6分間実行します。バッチの処理内容としてはcsvファイルをS3に出力するだけの簡易的なものです。

実装と実行の手順

実装の前に

AWS Batchの準備や実行方法が分からない場合は、弊社の以下の記事を参考に一連の流れを把握しておくことをお勧めします。

AWS Batchでシェルスクリプトを実行する典型的パターンのご紹介

私も大いに参考にさせて頂きました。

また上記記事に書かれているDocker、ECR、AWS Batchのジョブ実行環境・Job Queue・ジョブの定義について設定済みのものとします。IAMロールについては、S3への書き込み権限もつけておいてください。

バッチ処理

先に書いたようにバッチ処理はGolangで実装しました。ソースは以下となります。

package main

import (
	"bytes"
	"flag"
	"log"
	"os"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

const (
	s3Region    = "ap-northeast-1"
	s3Bucket    = "t-honda-dev"
	s3Key       = "go-s3-csv-batch/csv/result.csv"
	csvFileName = "result.csv"
)

var seconds int

func main() {
	log.Println("main start")

	flag.IntVar(&seconds, "s", 10, "continue seconds")
	flag.Parse()

	sess, err := session.NewSession(&aws.Config{Region: aws.String(s3Region)})
	if err != nil {
		log.Fatal(err)
	}

	err = createCsv(csvFileName)
	if err != nil {
		log.Fatal(err)
	}

	err = uploadFileToS3(sess, csvFileName)
	if err != nil {
		log.Fatal(err)
	}

	log.Println("main end")
}

func createCsv(fileDir string) error {
	file, err := os.Create(fileDir)
	if err != nil {
		return err
	}
	defer file.Close()

	for i := 0; i < seconds; i++ {
		output := "aaa,bbb,ccc\n"
		file.Write(([]byte)(output))
		time.Sleep(1 * time.Second)
	}

	return nil
}

func uploadFileToS3(sess *session.Session, fileDir string) error {
	file, err := os.Open(fileDir)
	if err != nil {
		return err
	}
	defer file.Close()

	fileInfo, _ := file.Stat()
	size := fileInfo.Size()
	buffer := make([]byte, size)
	file.Read(buffer)

	_, err = s3.New(sess).PutObject(&s3.PutObjectInput{
		Bucket: aws.String(s3Bucket),
		Key:    aws.String(s3Key),
		Body:   bytes.NewReader(buffer),
	})
	return err
}

引数で受け取った秒数のあいだループし、1秒ごとに一行、csvを出力するだけのプログラムです。このソースをAmazon Linuxで実行できるよう、以下のコマンドでビルドして実行ファイルを作成します。

GOOS=linux GOARCH=amd64 go build

実行ファイルの名前は、今回は「go-s3-csv-batch」としました。

起動シェル

上記でビルドしたGolangの処理を起動するためのシェルです。s3より実行ファイルをダウンロードし、実行権限を付与します。また引数で今回の実行時間である6分(360秒)を指定しています。

golang-job.sh
#!/bin/bash

env
echo "This is my golang test job!."
date
aws s3 cp s3://t-honda-dev/go-s3-csv-batch/binary/go-s3-csv-batch go-s3-csv-batch
ls -l
chmod +x go-s3-csv-batch
./go-s3-csv-batch -s 360
echo "finish!!"

実行ファイルのアップロード

先にビルドした実行ファイルと起動シェルをS3の任意の箇所のアップロードします。(今回はs3://t-honda-dev/go-s3-csv-batch/binary にアップロードしました)

AWS Batchの準備

AWS Batchでシェルスクリプトを実行する典型的パターンのご紹介の手順1〜8までを予め実行します。

最後にJobのSubmitでジョブを送信しますが、今回は先に作成した起動シェルを実行できるよう以下のように指定します。

  • Command : golang-job.sh
  • Environmemt variables:
    • 1. Key=BATCH_FILE_TYPE, Value=script
    • 2. Key=BATCH_FILE_S3_URL, Value=s3://t-honda-dev/go-s3-csv-batch/binary/golang-job.sh (アップロードした場所を指定)

実行結果

AWS Batchのジョブが起動し、引数で指定した秒数分の行があるcsvファイルがS3に出力されていれば成功です。

まとめ

以上のようにバッチ処理を実行できました。個人的には

  • シングルバイナリをS3に配置するだけ
  • EC2を直接触る必要がない(裏では動いていますが・・・)

が楽だと思いました。何かの役に立てば幸いです。