「みんなのGo言語」には現場で使える実践テクニックが本当に書いてあった

発売されたのは約1年前だけど,最近仕事で Golang を書いているので,実践的なテクニックを学ぶために今さらながら「みんなのGo言語」を読んだ.WEB+DB PRESS のようなカジュアルさのある本で,1日でサクッと読むことができた.読み終わった後に,学んだことを実際に試してみたりもして,とにかく即戦力のある1冊だと感じた.

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

golint -min_confidence

今までも golint は使っていたけど,各項目に Confidence というレベル設定があって -min_confidence オプションで閾値を決められるというのは知らなかった.golint の README にも書いてなかったけど,実際に golint.go を読んだら実装があり,デフォルトは 0.8 になっていた.もっと厳しくても良いのではないかなと思って,さっそく 0.6 に変更した.0.8 → 0.6 に変更したことによって追加で検知できたのは以下で,エラーメッセージの1文字目も小文字にするのがお作法なのかぁー,という気持ちだった.

error strings should not be capitalized or end with punctuation or a newline

golint -set_exit_status

デフォルトのまま golint を実行すると,コマンドの終了ステータスが 0 で返ってきてしまうため,今までは CircleCI で以下のようなワークアラウンド実装をしていた.

test:
  override:
    - test -z "$(golint $(glide novendor) | tee /dev/stderr)"

しかし -set_exit_status オプションを使うと,指摘が出た場合に 1 が返ると本書に書いてあった.よって,以下のように書き換えることができて非常に助かった.

test:
  override:
    - golint -set_exit_status $(glide novendor)

Makefile

最初は golint と go vet を circle.yml に直接書いていたけど,僕のチームに Golang のアドバイザーとして入ってもらっているメンバーに「Makefile で書きましょう」と教えてもらって,今は Makefile を使っている.まさに同じことが本書にも書かれていて,参考になった.なお make help の部分は本書で紹介されていた make2help は使ってなくて,以下の記事で紹介されていた方法を使っている.

postd.cc

ザッとこのような感じになり,CircleCI でも make lint を実行するだけで良いという状態になっている.

help:
    @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

setup: ## Setup some tools
    go get -u github.com/Masterminds/glide
    go get -u github.com/golang/lint/golint

lint: ## Run the Golint in all directories without /vendor
    go vet $$(glide novendor)
    golint -min_confidence 0.6 -set_exit_status $$(glide novendor)

goroutine の停止

goroutine をどのように停止するかという話があり,まさに似たような実装を書く予定があって,チャネルをクローズするイメージで考えていたので,Go 1.7 以降だと context を使うのが推奨であると知れたのは非常に良かった.

実際に本書に載っているコードを写経して動作確認をしてみて,これだけだとあまりチャネルをクローズする実装と変わらないと思ったけど,最後に context.WithTimeout を使えば,タイムアウトの考慮もできるようになるメリットがあるという紹介があった.

とは言え,本書だけだと記載が少なく概要レベルの紹介だったため,以下の記事を読んで,より理解を深められたような気がする.

ベンチマーク

テストの章では,ベンチマークの機能を知った.実際に写経をして試したりもした.

cat.go

package cat

import "bytes"

func cat(ss ...string) string {
    var r string
    for _, s := range ss {
        r += s
    }
    return r
}

func buf(ss ...string) string {
    var b bytes.Buffer
    for _, s := range ss {
        b.WriteString(s)
    }
    return b.String()
}

cat_test.go

package cat

import "testing"

func seed(n int) []string {
    s := make([]string, 0, n)
    for i := 0; i < n; i++ {
        s = append(s, "a")
    }
    return s
}

func bench(b *testing.B, n int, f func(...string) string) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        f(seed(n)...)
    }
}

func BenchmarkConcatenate(b *testing.B) {
    benchCases := []struct {
        name string
        n    int
        f    func(...string) string
    }{
        {"Cat", 3, cat},
        {"Buf", 3, buf},
        {"Cat", 100, cat},
        {"Buf", 100, buf},
        {"Cat", 10000, cat},
        {"Buf", 10000, buf},
    }
    for _, c := range benchCases {
        b.Run(fmt.Sprintf("%s%d", c.name, c.n),
            func(b *testing.B) { bench(b, c.n, c.f) })
    }
}

サブベンチマークという機能を使って書くことで,テーブルドリブンに書けるのは良かった.以下のような結果となり,文字列結合をする要素数が多くなればなるほど bytes.Buffer を使った方がパフォーマンスが良いということを確認することができた.すぐに仕事に活かせる知見だった.

$ go test -bench .
BenchmarkConcatenate/Cat3-8            10000000          164 ns/op         54 B/op         3 allocs/op
BenchmarkConcatenate/Buf3-8            10000000          181 ns/op        163 B/op         3 allocs/op
BenchmarkConcatenate/Cat100-8            300000         5794 ns/op       7520 B/op       100 allocs/op
BenchmarkConcatenate/Buf100-8           1000000         1938 ns/op       2160 B/op         4 allocs/op
BenchmarkConcatenate/Cat10000-8             200      7414202 ns/op   53327801 B/op     10000 allocs/op
BenchmarkConcatenate/Buf10000-8           10000       188879 ns/op     211424 B/op        11 allocs/op
PASS

その他

Windows の考慮,リフレクションの話などは,一通り読んだけど,実際に使う場面がありそうなときに改めて読み直そうかなと思っている.CLI の話は前に Togoo を実装したときに学んだ内容が多く含まれていて,復習の意味も兼ねて知識を整理することができた.

読んでいて少し気になったこと

初版だからある程度はしょうがないとは思うけど,少し誤植が多いなとは思った.また,誤植ではないものの,文字間隔が広くなっているのが気になる行もあった.これは組版の問題だとは思うけども.誤植一覧は以下に公開されていた.

まとめ

  • 仕事でさっそく使えるような,実践的なテクニックが学べる1冊だった
  • 実装のテクニックだけではなく,OSS として公開されているパッケージが多く紹介されているのも良かった

関連記事

紹介されていた「プログラミング言語Go」も購入したので,さっそく読むぞ!

suzuken.hatenablog.jp

Golang でジョブのスケジューリング実行ができる JobRunner を試した

最近 Golang でジョブを非同期にスケジューリング実行するような仕組みを実装していて,要件に合っていた JobRunner を使った.特徴としては,様々なスケジューリングを定義できる点と,API を起動してプロセスを常駐させる点にある.

github.com

基本実装

最初に動く実装を載せておく.ザッと書いてみると,このようになる.5秒間隔で MyJob を実行していて,標準出力にログを吐いている.また Gin を起動してプロセスを常駐させている.さらに JobRunner のステータスを API から取得できるようにもしている.

package main

import (
    "fmt"
    "net/http"

    "github.com/bamzi/jobrunner"
    "github.com/gin-gonic/gin"
)

// MyJob ...
type MyJob struct {
}

func main() {
    jobrunner.Start()
    jobrunner.Schedule("@every 5s", MyJob{})

    gin.SetMode(gin.ReleaseMode)
    r := gin.Default()
    r.GET("/jobrunner/status", JobJSON)
    r.Run(":8080")
}

// JobJSON ...
func JobJSON(c *gin.Context) {
    c.JSON(http.StatusOK, jobrunner.StatusJson())
}

// Run ...
func (e MyJob) Run() {
    fmt.Println("Run MyJob!")
}

実行すると,以下のように5秒間隔でログが出力できる.

$ go run main.go
[JobRunner] 2017/08/13 - 11:10:00 Started...
Run MyJob!
Run MyJob!
Run MyJob!
Run MyJob!
Run MyJob!
Run MyJob!

ステータスは以下のように取得できる.

$ curl -s http://localhost:8080/jobrunner/status | jq .
{
  "jobrunner": [
    {
      "Id": 1,
      "JobRunner": {
        "Name": "MyJob",
        "Status": "IDLE",
        "Latency": "77.204µs"
      },
      "Next": "2017-08-13T11:15:14+09:00",
      "Prev": "2017-08-13T11:15:09+09:00"
    }
  ]
}

スケジューリング

JobRunner では,様々なスケジューリングを定義できる.内部的には gopkg.in/robfig/cron.v2 を利用しているため,cron 形式でスケジューリングを定義することができたり,シンプルに一定間隔で実行することもできる.例えば,以下のように定義することができる.

// Schedule : 5秒間隔で実行する
jobrunner.Schedule("@every 5s", MyJob{})

// Schedule : 1時間間隔で実行する
jobrunner.Schedule("@hourly", MyJob{})

// Schedule : 2分間隔で実行する (cron 形式)
jobrunner.Schedule("* */2 * * * *", MyJob{})

// Every : 10分間隔で実行する
jobrunner.Every(10*time.Minute, MyJob{})

// In : 10秒後に実行する
jobrunner.In(10*time.Second, MyJob{})

// Now : 即時実行する
jobrunner.Now(MyJob{})

詳しくは cron.v2 のドキュメントに載っている.

godoc.org

API

今回は Gin で実装をしたが,JobRunner では README に書かれている通り,特定の API に依存することはなく,好きなものを選択することができる.

  • Gin
  • Echo
  • Martini
  • Beego

などなど.

並列実行

README にあまり詳しく書かれていないが,JobRunner は並列実行をサポートしている.init.go を読んで,実際に動作確認をして,やっと理解できた.

func Start(v ...int) {
    MainCron = cron.New()

    if len(v) > 0 {
        if v[0] > 0 {
            workPermits = make(chan struct{}, v[0])
        } else {
            workPermits = make(chan struct{}, DEFAULT_JOB_POOL_SIZE)
        }
    }

    if len(v) > 1 {
        if v[1] > 0 {
            selfConcurrent = true
        } else {
            selfConcurrent = false
        }
    }

    MainCron.Start()

    fmt.Printf("%s[JobRunner] %v Started... %s \n",
        magenta, time.Now().Format("2006/01/02 - 15:04:05"), reset)

}

まず jobrunner.Start() の引数は int の可変長引数になっている.結局のところ,第二引数を指定しない限りは selfConcurrent = false となるため,並列実行はされないようになっていた.

  • 第一引数 : プールサイズ(デフォルト : 10)
  • 第二引数 : 並列実行数(デフォルト : 並列実行なし)

検証 : jobrunner.Start(10)

まず,ログ出力を拡張して,30秒待機するようにしてみた.

package main

import (
    "fmt"
    "net/http"
    "time"

    "github.com/bamzi/jobrunner"
    "github.com/gin-gonic/gin"
)

// MyJob ...
type MyJob struct {
}

func main() {
    jobrunner.Start(10)
    jobrunner.Schedule("@every 5s", MyJob{})

    gin.SetMode(gin.ReleaseMode)
    r := gin.Default()
    r.GET("/jobrunner/status", JobJSON)
    r.Run(":8080")
}

// JobJSON ...
func JobJSON(c *gin.Context) {
    c.JSON(http.StatusOK, jobrunner.StatusJson())
}

// Run ...
func (e MyJob) Run() {
    fmt.Println("[Start] Run MyJob!")
    time.Sleep(30 * time.Second)
    fmt.Println("[End] Run MyJob!")
}

この状態で jobrunner.Start(10) を実行すると,以下のようになった.30秒の待機が優先されて,並列実行はされなかった.

$ go run main.go
[Start] Run MyJob!
[End] Run MyJob!
[Start] Run MyJob!
[End] Run MyJob!
[Start] Run MyJob!
[End] Run MyJob!
[Start] Run MyJob!
[End] Run MyJob!

検証 : jobrunner.Start(10, 5)

次に jobrunner.Start(10, 5) と書いて,並列実行数を指定してみた.

func main() {
    jobrunner.Start(10, 5)
}

すると,並列実行となった.JobRunner を使うときには,理解しておくべきポイントだと思う.

$ go run main.go
[Start] Run MyJob!
[Start] Run MyJob!
[Start] Run MyJob!
[Start] Run MyJob!
[Start] Run MyJob!
[Start] Run MyJob!
[End] Run MyJob!
[Start] Run MyJob!
[Start] Run MyJob!
[End] Run MyJob!
[Start] Run MyJob!
[End] Run MyJob!

まとめ

  • JobRunner を使うと柔軟なスケジューリング定義でジョブを実行することができる
  • Gin や Echo など,任意の API を起動してプロセスを常駐させる仕組みになっている
  • 並列実行をする場合は jobrunner.Start() の引数を意識する

Lambda (Python) をローカル環境で実行できる python-lambda-local を試した

Lambda (Python) のローカル環境を整えるため,python-lambda-local を試した.python-lambda-local を使うと,Python コードを Lambda にデプロイすることなく動作確認ができるようになる.Apex を使っていれば apex invoke でお手軽に実行することもできるけど,やはり Lambda に依存せず,単独で実行したいという要件はある.Lambda のローカル環境は誰に聞いても困っている印象がある.

github.com

前提

インストールは pip を実行するだけで良い.また,今回紹介するディレクトリ構成は Apex で Lambda をデプロイする前提にしている.

$ pip install python-lambda-local

BluePrint : hello-world-python

まずは AWS に依存せず,Python ライブラリにも依存せず,単体で実行できるコードとして,BluePrint の hello-world-python を試した.ディレクトリ構成は以下のようになっている.

.
├── event.json
├── functions
│   └── myfunc
│       └── main.py
└── project.json

myfunc/main.py は以下のようなコードにした.シンプルにイベントを受けて,表示するだけの実装にしている.

import json

print('Loading function')

def lambda_handler(event, context):
    print("function = myfunc, value1 = " + event['key1'])
    print("function = myfunc, value2 = " + event['key2'])
    print("function = myfunc, value3 = " + event['key3'])

ここで python-lambda-local で実行すると以下のようになる.オプションとしては --function でハンドラを指定して,-t でタイムアウトを指定する.あとは Python コードとイベントを指定する.

$ python-lambda-local --function lambda_handler --timeout 5 functions/myfunc/main.py event.json
Loading function
[root - INFO - 2017-07-30 16:16:36,272] Event: {u'key3': u'value3', u'key2': u'value2', u'key1': u'value1'}
[root - INFO - 2017-07-30 16:16:36,273] START RequestId: 9e683e91-821f-4680-b8e2-bcf37c7eb44f
function = myfunc, value1 = value1
function = myfunc, value2 = value2
function = myfunc, value3 = value3
[root - INFO - 2017-07-30 16:16:36,273] END RequestId: 9e683e91-821f-4680-b8e2-bcf37c7eb44f
[root - INFO - 2017-07-30 16:16:36,274] RESULT:
None
[root - INFO - 2017-07-30 16:16:36,274] REPORT RequestId: 9e683e91-821f-4680-b8e2-bcf37c7eb44f    Duration: 0.38 ms

簡単に動いた!

BluePrint : s3-get-object-python

次は AWS に依存していて,Python ライブラリにも依存するコードとして,BluePrint の s3-get-object-python を試した.ディレクトリ構成は以下のようになっている.

.
├── event.json
├── functions
│   └── myfunc
│       ├── main.py
│       ├── requirements.txt
│       └── (ライブラリいろいろ)
└── project.json

myfunc/main.py は以下のようなコードにした.S3 Put のイベントを受けて,Put されたファイルの Content-Type を表示している.なお,S3 Put のイベントは量が多いため,割愛するが,公式ドキュメントにちゃんと載っている.

docs.aws.amazon.com

from __future__ import print_function

import json
import urllib
import boto3

print('Loading function')

s3 = boto3.client('s3')

def lambda_handler(event, context):

    # Get the object from the event and show its content type
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key'].encode('utf8'))
    try:
        response = s3.get_object(Bucket=bucket, Key=key)
        print("CONTENT TYPE: " + response['ContentType'])
        return response['ContentType']
    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e

ここで python-lambda-local で実行すると以下のようになる.

$ python-lambda-local --function lambda_handler --library functions/myfunc --timeout 5 functions/myfunc/main.py event.json

[root - INFO - 2017-07-30 23:14:18,203] START RequestId: 07669e19-37af-4640-ae65-859dadd758ed
[botocore.vendored.requests.packages.urllib3.connectionpool - INFO - 2017-07-30 23:14:18,214] Starting new HTTPS connection (1): s3-ap-northeast-1.amazonaws.com
CONTENT TYPE: text/plain
[root - INFO - 2017-07-30 23:14:18,721] END RequestId: 07669e19-37af-4640-ae65-859dadd758ed
[root - INFO - 2017-07-30 23:14:18,722] RESULT:
text/plain
[root - INFO - 2017-07-30 23:14:18,722] REPORT RequestId: 07669e19-37af-4640-ae65-859dadd758ed    Duration: 517.71 ms

問題なく動いた!

気を付けるポイント

python-lambda-local を使ってみて,数点気を付けるポイントがあった.

1. /lib ではなくルートディレクトリにライブラリをインストールする

公式ドキュメントにも書いてある通り,pip で Python ライブラリをインストールする場合,Lambda の場合はルートディレクトリに配置する必要がある.よって,python-lambda-local のドキュメントにあるような /lib ディレクトリを用意するのではなく,main.py と同じルートディレクトリにそのままインストールする必要がある.具体的には以下のようなコマンドになる.

$ pip install --requirement functions/myfunc/requirements.txt --target functions/myfunc

docs.aws.amazon.com

2. requirements.txtboto3 を明示する必要がある

次も公式ドキュメントに書いてある内容だが,Lambda にはデフォルトで Boto 3 がインストールされているため,わざわざ requirements.txt に書く必要がないようになっている.ただし,ローカル環境で実行する場合には必要なので,結果的に書く必要があった.今回は以下のファイルを用意した.

urllib
boto3

まとめ

  • Lambda (Python) のローカル環境を整えるために python-lambda-local を試してみた
  • 簡単に使えたし AWS サービスとの接続もできたため一般的なユースケースは網羅できそう
  • ライブラリのインストールディレクトリなど少し気を付けるポイントはある

関連記事

Apex を使って複数環境にデプロイする方法は前にまとめてある.Apex ユーザーには参考になる内容だと思う.

kakakakakku.hatenablog.com

The Go Blog : JSON and Go を読んだ

今さらながら The Go Blog : JSON and Go を読んで,Golang で JSON を扱う方法を学んでいた.Golang を実践的に書いている人にとっては初歩的すぎる内容だけど,個人的にメモを残しておく.

encoding/jsonjson.Unmarshal を使うと,JSON をパースすることができる.json.Unmarshal は引数に []byteinterface{} を受けるため,定義した Struct にマッピングすることができる.もし JSON のキー名と Struct のフィールド名が異なる場合も問題なくて,Struct にタグを書けば,問題なくマッピングできる.

json.Unmarshal

The Go Blog に書いてある例を参考にすると,以下のように実装できる.ここまでは特に違和感もなくシンプルだった.

package main

import (
    "encoding/json"
    "fmt"
)

type Message struct {
    Name string
    Body string
    Time int64
}

func main() {
    b := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)

    var m Message
    if err := json.Unmarshal(b, &m); err != nil {
        return
    }

    // Name: Alice, Body: Hello, Time: 1294706395881547000
    fmt.Printf("Name: %s, Body: %s, Time: %d", m.Name, m.Body, m.Time)
}

任意の JSON をパースする

The Go Blog を読んでいて,interface{} を使えば,データ構造がわからない場合など,任意の JSON をパースできることを知った.実際に試してみたところ,JSON をそのまま map[string]interface{}json.Unmarshal できて,さらに Type Switch で型ごとに処理を書くことができた.実際に仕事で構造が可変な JSON をパースする必要があり,Twitter でも map が使えるとヒントを頂いたりもして,最終的にはこの interface{} を使うことで解決できた.

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)

    var f interface{}
    if err := json.Unmarshal(b, &f); err != nil {
        return
    }

    m := f.(map[string]interface{})

    // map[Name:Wednesday Age:6 Parents:[Gomez Morticia]]
    fmt.Println(m)

    for k, v := range m {
        switch vv := v.(type) {
        case string:
            // Name is string Wednesday
            fmt.Println(k, "is string", vv)
        case int:
            // Age is of a type I don't know how to handle
            fmt.Println(k, "is int", vv)
        case []interface{}:
            // Parents is an array:
            fmt.Println(k, "is an array:")
            for i, u := range vv {
                // 0 Gomez
                // 1 Morticia
                fmt.Println(i, u)
            }
        default:
            fmt.Println(k, "is of a type I don't know how to handle")
        }
    }
}

タグ

Struct の部分を調べていたら,多くの記事でタグ付きの Struct が紹介されていたが,The Go Blog を読みながら実際に試してみて,理解が深まった.基本的に JSON のキー名と Struct のフィールド名が一致しているのであれば,あえてタグを書く必要はないし,大文字と小文字の区別もしないため,JSON のキー名が小文字で,Struct のフィールド名の1文字目が大文字でも問題ない.ちなみに Struct のフィールド名は1文字目を大文字 (exported) にしないとダメと書いてある.

以下にサンプルコードを載せた.JSON のキー名が message_title の場合,大文字も小文字も区別しないため,Struct のフィールド名は Message_title でも Message_Title でも良い.ただし MessageTitle にする場合は JSON のキー名を異なってしまうため, json:"message_title" とタグを書くことによって,マッピングすることができる.タグの記法は他にもあって,nil などを除外する omitempty や,フィールドを無視する - もある.

package main

import (
    "encoding/json"
    "fmt"
)

type Message1 struct {
    Message_title string
}

type Message2 struct {
    Message_Title string
}

type Message3 struct {
    MessageTitle string `json:"message_title"`
}

func main() {
    b := []byte(`{ "message_title": "Hello!" }`)

    var m1 Message1

    if err1 := json.Unmarshal(b, &m1); err1 != nil {
        return
    }

    // Hello!
    fmt.Println(m1.Message_title)

    var m2 Message2

    if err2 := json.Unmarshal(b, &m2); err2 != nil {
        return
    }

    // Hello!
    fmt.Println(m2.Message_Title)

    var m3 Message3

    if err3 := json.Unmarshal(b, &m3); err3 != nil {
        return
    }

    // Hello!
    fmt.Println(m3.MessageTitle)
}

json.NewDecoder

The Go Blog の最後には json.NewDecoder も紹介されていた.特に API からレスポンスを受ける場合などは io.Readerioutil.ReadAll[]byte に変換するよりも,直接 json.NewDecoder を使った方が良さそう.ここは実際にベンチマークを取って確認する.

package main

import (
    "encoding/json"
    "fmt"
    "os"
)

func main() {
    dec := json.NewDecoder(os.Stdin)
    enc := json.NewEncoder(os.Stdout)
    for {
        var v map[string]interface{}
        if err := dec.Decode(&v); err != nil {
            return
        }
        for k := range v {
            if k != "Name" {
                delete(v, k)
            }
        }
        if err := enc.Encode(&v); err != nil {
            fmt.Println(err)
        }
    }
}

まとめ

1年半以上も前に Togoo を実装して以来,全然 Golang を書いてなかったけど,最近仕事で書く機会ができたので,改めて基礎部分から勉強し直している.The Go Blog も読むだけだと理解が深まらないなと思って,積極的に写経して試した.エディタは Gogland 1.0 EAP を試してみてるけど,非常に便利!とりあえず goimports を ⌥⌘I に Keymap して使っている.Golang はまだまだビギナーだから積極的に勉強していくぞ!社内に質問できる人も多くて非常に助かってる.

(ちなみに Togoo は個人的に今もずっと使ってる!)

kakakakakku.hatenablog.com

「JAWS-UG コンテナ支部 #9」で「ECS x Mackerel」をテーマに LT をした

昨日は「JAWS-UG コンテナ支部 #9」に参加して,LT もしてきた.ECS を中心に移行の話,デプロイの話,モニタリングの話などを聞けたし,懇親会にも参加させてもらって,カジュアルに話せて楽しかった.僕もコンテナ支部の運営をお手伝いしたいな!

jawsug-container.connpass.com

発表資料

今回 LT 枠で応募したら採択してもらえたので「ECS x Mackerel ~ 動的ポートマッピングに対応したタスクのメトリクスを取得する ~」という話をしてきた.内容としては,前にブログに書いた記事を整理した感じ.時間も少なくシュッと話したので,詳しくはブログを見てもらえればなと!

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

他の発表でも似た話があったけど,やっぱりタスクのメトリクスも取得したい場合は Datadog 一択という雰囲気があるので,CloudWatch や Mackerel でも気軽に低コストに取得できるようになると嬉しいなと思う.

speakerdeck.com

感想

ECS アップデート情報 @riywo

ECS はサービスのアップデートが多く,どんどん便利になっていく印象がある.CloudWatch Events から ECS の RunTask が実行できるのであれば,バッチ基盤としても柔軟に使えそうですごく気になる.タスク定義を実行時に上書きできる機能は初耳だった.試してみよう.

www.slideshare.net

乗換 Navitime のバックエンドをオンプレから ecs に移行した時の話

  • ECS Agent の接続チェックを aws ecs describe-container-instances を叩く
  • ECS Introspection API
  • ECS ID Mapper
  • タスクのメトリクスを取得するために Datadog を使っている

www.slideshare.net

AWS ECS のサービスを slack bot でデプロイする話 @h3_poteto

ecs-goploy はパッケージ実装になっているとのことで,Capistrano のような印象を受けた.僕も ECS のデプロイツールを選定するためにいろいろ調査したけど,最終的には silinternational/ecs-deploy にした.完全にシェルで実装されていることと,タスク定義の更新とサービス反映が簡単に行えることがメリットだった.発表でも話があったけど,確かに master の実装を見るとタスク定義だけの変更ができそうに見えるんだけど,実際に使ってみるとエラーになる(もしかしたら今はもう直ってるかも).CloudFormation を使ったデプロイはまだ試したことがないので,近いうちに試してみようかなと.

speakerdeck.com

GitHub + ECS で快適 Review 環境 @pataiji

  • デプロイ環境(特にステージング)の渋滞があり,もっとスケールさせたいと考えていた
  • Heroku Review Apps も検討した
  • GitHub から WebHook を受ける
  • branch を clone して,Rails アプリケーションを起動させる

以下の記事を前に読んでいて,興味があったので「あの話かー!」という感じだった.まだ一部自動化できていない部分もあるみたいだったけど,それでも素晴らしい試みだと思う.少し気になったのは,データベース変更が伴うプルリクで,この場合も特に考慮せずに動作確認できるようになっているのかな?

tech.speee.jp

speakerdeck.com

AWS CodeBuild カスタム Docker イメージを使ってビルドする

  • CodeBuild で buildspec.yml に書く version は 0.2 にする
  • 管理コンソールの手順解説

AWS の Code 系は使ったことがなく,おおー!という感じだった.うまく自動化できれば良さそう.

speakerdeck.com

関連記事

「JAWS-UG コンテナ支部」の参加は今回が3回目だった.引き続き参加したいと思う.

kakakakakku.hatenablog.com

kakakakakku.hatenablog.com

参加レポート

h3poteto.hatenablog.com

発表中!

f:id:kakku22:20170722173425j:plain