LoginSignup
4
2

More than 5 years have passed since last update.

dockerによるgolang開発にトライ w/ AWS SDK

Posted at

AWS上で高速に動くETLアプリケーションを、さっくり書く必要があったので調査結果をまとめつつ。
(goは触ったばかりなので非効率なところがあるかも)

前提

利用シーン

S3から複数のオブジェクトをダウンロード、加工、格納するアプリケーションを想定。
環境調査が目的なので、まずはlistobjectが出来る状態を目指す。

検証データには、s3に格納されているオープンデータ、Global Surface Summary of Dayを利用。

環境類

  • go v1.12
  • aws sdk for go v2
    現在Developer previewだが調査も兼ね(S3周りはそこまでIF変わっていなそうな印象を受けた)

dockerやaws cli、クレデンシャル類も設定しておく。

準備

1. ディレクトリ構成

https://github.com/golang-standards/project-layout を参考。
小さなプロジェクトにはOverkillだぜ!と書いてあるけど、変な並びになるのも気持ち悪いので、必要最低限だけ参考に。

.
├── README.md
├── build  // CI/CDなどに利用するスクリプト類
│   └── docker  
│       ├── Dev.Dockerfile
│       └── Prod.Dockerfile
├── cmd  // アプリケーションのメイン
│   └── simple-etl-go-aws
│       └── main.go
└── internal  // プロジェクト内部でしか使わないコード類
    └── s3
        └── listobjects.go

外部から利用されるコード類は /pkg を作ってその配下に置くとのこと。

2. 開発用docker環境

https://hub.docker.com/_/golang よりバージョンタグのみのものを利用。
単にバージョン指定のみの場合、 x.x.x-streach という名称のdebianベースのものが選択される。

Dev.dockerfile
FROM golang:1.12

RUN apt-get update && apt-get install -y \
  python3-dev \
  python3-pip

RUN pip3 install \
  awscli  # デバッグ用

WORKDIR /simple-etl-go-aw

ルートディレクトリから下記コマンドで開発用イメージビルド。

docker build -t simple-etl-go-aws:dev -f build/docker/Dev.Dockerfile .

さらにルートディレクトリから、下記コマンドでコンテナ内へ。
~/.aws 以下にクレデンシャルが設定されていることを前提。環境変数として注入してローカルで開発をする)

docker run -it --rm \
  -e AWS_DEFAULT_REGION=ap-northeast-1 \
  -e AWS_ACCESS_KEY_ID=$(aws --profile default configure get aws_access_key_id) \
  -e AWS_SECRET_ACCESS_KEY=$(aws --profile default configure get aws_secret_access_key) \
  -v $(pwd):/simple-etl-go-aws \
  simple-etl-go-aws:dev \
  /bin/bash

3. go modules初期化

v1.13からはコレが標準の外部モジュール管理のツールになるとのこと。v1.12でも使えるので利用する。

# 初期化
go mod init github.com/leo-mon/simple-etl-go-aws

これによりgo.modが生成される。GitHubなどのユーザー名+プロジェクト名を使うと、後ほど必要なモジュールを入れ込んだ際にいい感じに名前が揃うが、公開するプロジェクトでなければ階層は深くしなくてもいい気がする。

開発

コード類

https://github.com/aws/aws-sdk-go-v2/tree/master/example/service/s3/listObjects
を元に、2ファイルへ分割(配置場所は上記ディレクトリ)

main.go
package main

import (
    "fmt"
    "github.com/leo-mon/simple-etl-go-aws/internal/s3"
    "os"
)

func exitErrorf(msg string, args ...interface{}) {
    fmt.Fprintf(os.Stderr, msg+"\n", args...)
    os.Exit(1)
}

func main() {
    if len(os.Args) < 2 {
        exitErrorf("you must specify a bucket")
    }
    s3.ListObjects(os.Args[1])
}
listobject.go
package s3

import (
    "fmt"
    "os"

    "github.com/aws/aws-sdk-go-v2/aws/endpoints"
    "github.com/aws/aws-sdk-go-v2/aws/external"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func exitErrorf(msg string, args ...interface{}) {
    fmt.Fprintf(os.Stderr, msg+"\n", args...)
    os.Exit(1)
}

// Lists all objects in a bucket using pagination
//
// Usage:
// listObjects <bucket>
func ListObjects(bucketname string) {

    cfg, err := external.LoadDefaultAWSConfig()
    if err != nil {
        exitErrorf("failed to load config, %v", err)
    }

    cfg.Region = endpoints.UsEast1RegionID

    svc := s3.New(cfg)

    req := svc.ListObjectsRequest(&s3.ListObjectsInput{Bucket: &bucketname})
    p := req.Paginate()
    for p.Next() {
        page := p.CurrentPage()
        for _, obj := range page.Contents {
            fmt.Println("Object: ", *obj.Key)
        }
    }

    if err := p.Err(); err != nil {
        exitErrorf("failed to list objects, %v", err)
    }
}

main.goのimport文で分かるように、go mod init で設定したパスを起点にimportを行なっている。

走行

go run cmd/simple-etl-go-aws/main.go aws-gsod

と打つと、

go: finding github.com/aws/aws-sdk-go-v2 v0.7.0
...
go: downloading github.com/aws/aws-sdk-go-v2 v0.7.0
go: extracting github.com/aws/aws-sdk-go-v2 v0.7.0
...
Object:  1929/030050-99999.csv
Object:  1929/030750-99999.csv
Object:  1929/030910-99999.csv
...

のように、import文に含めたモジュールを自動で検知、ダウンロードしてくれる。
(この際、このコンテナ内では/go/pkg以下に入る)

ビルド

go build cmd/simple-etl-go-aws/main.go

とするとmainがビルドされる。


コンテナを破棄するたびに再度パッケージをダウンロードする必要があるのがネックではあるが、コンテナ内/go/pkgを適当なディレクトリにマウントしてあげれば良い、はず。

4
2
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
4
2