読者です 読者をやめる 読者になる 読者になる

きょこみのーと

元六本木でGo書いてました。今はVRでGo書いてます。

Amazon Lambdaにデフォルトでインストールされているライブラリを最新にして使う方法の1つ

Go Amazo ImageMagick ghostscript Lambda

業務でAmazonLambda上でPDFを画像に分割処理を実装したのですが、その時ghostscriptのVersionが古くて、 特定のPDF変換でエラーになるという現象でハマったのでその時調べたことと、 対応策を紹介したいと思います。

前提

  • Apex (Go)で動かしてます
  • ImageMagickがghostscriptを呼び出している
  • ghostscriptが古くてエラー

Apexについては前に書いた記事をご覧ください。

kyokomi.hatenablog.com

Lambdaのamiについて

Amazon Linux AMI 2014.09 Packages

ghostscript-8.70
ghostscript-fonts-5.50

フォーラムでの質問

https://forums.aws.amazon.com/message.jspa?messageID=611159

AWS Lambda doesn't provide a way for functions to update the Amazon Linux AMI. 
As has been suggested, you can include alternative versions of libraries, executables, 
and language runtimes in your function's ZIP file if the default ones available don't address your needs.

We'll also be looking at ways to make that process easier in the future through simplified deployment options. 
Until then, apologies for the inconvenience of having to copy this in with your code.
  • AWS LambdaではAMIをupdateする術を提供してない
  • もしversionを更新したいならzipに含んで頑張ってくれ

と解釈。/(^o^)\

解決策

functionの構成

pdf2images
├── bin
│   └── gs            // 上記のdownloadしたバイナリ
└── main.go

コード抜粋

func main() {
    apex.HandleFunc(func(event json.RawMessage, ctx *apex.Context) (interface{}, error) {
        os.Setenv("PATH", os.Getenv("LAMBDA_TASK_ROOT")+"/bin"+":"+os.Getenv("PATH"))
 
        // TODO: 〜 色々省略 〜

        outputLog, err := exec.Command("convert", "/tmp/input/hoge.pdf", "/tmp/output/hoge.png").CombinedOutput()
        if err != nil {
            return nil, err
        }
        log.Println(string(outputLog))

        return nil, nil
    })
}

Lambda x Apexで、Goで書いたラムダ関数を楽に動かす

Go Lambda Amazon Apex

業務でAmazonLambdaを使う機会があり、Apex(Go)を試していい感じだったのでその紹介とハマりポイントを幾つか紹介しようかと。

github.com

導入

すごく簡単で、 https://github.com/apex/apex#installation に書いてある通りインストールして apex init で対話式でプロジェクト名とか入力するだけです。

ExampleのGo実装をそのままコピって apex deploy すれば速攻でdeployも出来ます。

exampleはこちら -> https://github.com/apex/apex/tree/master/_examples/go

package main

import (
    "encoding/json"

    "github.com/apex/go-apex"
)

type message struct {
    Hello string `json:"hello"`
}

func main() {
    apex.HandleFunc(func(event json.RawMessage, ctx *apex.Context) (interface{}, error) {
        var m message

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

        return m, nil
    })
}

かなりシンプルに書けて楽です。

概要・Tips

  • functions下がラムダ関数と1対1になります
    • 下記の例だと pdf2imagesresizeImage になります
  • GOPATH下にプロジェクトを置けば共通の実装とかをパッケージ化して使いまわせるので便利
    • 下記の例だと resize パッケージと aws パッケージ
  • glide使ってライブラリ固定もできます
  • ちゃんと調べてないんですが、 os.stdoutを呼び出すとNode.js側でエラーになるので fmt.Print ではなく log.Print を使いましょう
  • 外部ライブラリも exec.Commnad で呼び出せます
  • 環境変数の変更は os.Setenv() を使うと exex.Commandで呼び出すときにも反映されます
  • /tmp 下が書き込み可能になっているので、一時ファイル系の処理で積極的に使っていきましょう

プロジェクト構成例

example_lambda
├── functions
│   ├── pdf2images // PDFを画像に変換するラムダ関数
│   │   ├── main.go
│   └── resizeImage // 画像を元にサムネイルを作成するラムダ関数
│       └── main.go
├── glide.lock
├── glide.yaml
├── project.json
├── resize        // resizeする共通処理
│   └── resize.go
├── aws          // aws-sdk-go周りの共通処理
│   ├── aws.go
│   └── message.go
└── vendor   // vendoring

ISUCON6本戦初出場で敗退しました @ご注文はpoyoですか?

isucon 感想

ISUCON参加は今年で3回目。毎年運営の方々はお疲れ様です。 今年は初の本戦出場が出来て本当に楽しかったです。

仕事とかでバタバタしてて、ブログ書くのが遅くなってしまった...

チームメンバーと役割

過去2年はk02というチームで出ていたのですが、メンバーの一人がセブ島にいるという体たらくで雑に集まったメンバーで参加しました。

実際、雑に集まった突貫チームだけど、前職で一緒に仕事してたこともあり、それぞれの得意分野とか性格とか把握してたので事前準備とか打ち合わせ無しでも特に問題がありませんでした。

f:id:kyokomi:20161103140723p:plain

※窓側の席でした

当日の準備

  • 無事に起床
  • 本戦参加者だけが貰えるネームカードをGETしてテンションが上がる
  • GitHubのRepositoryを作ってコラボレーターに招待
  • sshのPrivateKeyを共有したり
  • インスタンスで使うgithubアカウントの用意など

ちゃんと会社の宣伝してきました ^^

※右の写真のひきこもりTシャツ

地味に弊社を知っている人が居て驚きましたね。

今回のお題

  • フロントエンドサーバーとしてReact.jsを使ったnode.js
  • そこからリバースプロキシされる形でアプリが存在し、それには各言語の実装(Goを選択)
  • DBは、MySQL
  • すべて docker になっていて、 docker-compose で動いている
  • 5台あるけど1台で全部のdockerが動いてる状況

反省点

  • Dockerを捨てるかどうかの判断が遅かった(午前中くらい)
    • nginxをdocker-composeに混ぜるのでハマった
    • そもそもなんで docker-compose...頑張ってるんだ... ってなるまでに時間かかった
  • 5台インスタンスを使い切る前に時間切れ(時間配分)
    • ある程度チューニングしたら横に並べるつもりだったが時間配分ミス
  • Reactのフロントエンドサーバーは飾りだと思って、軽視してた
    • 一応、チラっと見たけど先にセオリー通りまずはサーバーを〜って優先度を雑に決めてしまった
  • 絵チャット掲示板的なやつという前情報でなんとなくわかったつもりになってしまった
    • あまりサービスそのものを触る時間を取らなかった
    • 触ってると、ここはAPI通信必要そうだな〜とかここは最適化できそうみたいなのが湧いてきて、本戦終了して改めて触ってると「なるほど、こうなってたのか〜」という感じだった

所感

本戦は初出場なんですが、予選と違って裁量がかなり多くてやることも多いけど一番効果的なものをやろうという感じで、今までの経験と頭をフルにつかって取り組む感じがめっちゃ楽しかったです。

結果は11位という形でやり残しもたくさんありますが、来年も開催されるだろうと思い腕を磨いて行きたいと思います。 来年も本戦出るぞ。来年こそ優勝だ !!!

ひきこもりを加速するクラスター株式会社に入社しました!!!

転職 日記

f:id:kyokomi:20161003205936p:plain

2016年7月一杯で約1年半働いた株式会社Gunosyを退職し、2ヶ月ほどフリーランスという形で数社のお仕事を手伝っていました。 クラスター株式会社はその中の一社であり、Founder & CEOの加藤さんとは4年ほど?前にCocos2d-xで個人ゲーム開発をしていたときにTwitterや勉強会でお世話になっていた経緯で退職したという話をしたらちょっと遊びに来ません?という流れから今に至ります。

人のつながりというのは本当に大事ですねと実感します。

約2ヶ月フリーランスをやった話について

がっつりフリーランスで稼いでいくぞ〜というよりは、色々な会社を回って自分に合っている&やりたいことをできそうな会社を探したかったのが目的でフリーランスをやっていました。 (もちろん、契約させていただいた各社様には予め長期的に働くというよりは短期的に〜ということで了承を頂いていました)

※知人で似たような形でフリーランスをやっていた人がいたので参考にさせていただきました。感謝!

細かい話をするとキリがないのですが、雑に学びがあったことを箇条書きするとこんな感じです。

  • 市場の金額感
  • 各社エンジニアリングやチームビルディングなど
  • 色々な会社の制度や働き方
  • 日本の納税義務や税務署、開業届など
  • マネーフォワード便利すぎるのでは?とか
  • クレジットカードやオートチャージSuicaは個人事業主用につくっておくと捗る!とか
  • 交通費は税抜きでした

Clusterの紹介と入社の決めてついて

cluster.mu

軽く紹介

サービス内容は↑に書いてあるとおりで、 ひきこもりを加速する ― クラスターは家から出たくないすべてのクリエイターのためのバーチャル集会アプリ ですね。 現在は、Oculus RiftMac/Windowsしか対応していませんが、HTC Vive等の対応も行っている最中ですので今後にご期待ください。

VR空間でイベントを開催したりイベントに参加したりできるので、距離の問題や収容人数の問題とかを解決できてハッピーになります。

入社の決めてとか

自分は、たぶん5人目の社員です。ちなみに約2ヶ月間フリーランスとして週3日くらい働いて以下を肌で感じて判断しました。

  • サービス
    • VRサービスの将来性を感じた
    • 最初に話を聞いたときにもめっちゃ面白そうと思った
    • チームの雰囲気も近すぎず遠すぎずでバランス良い
    • 将来的にリモートワークも視野にいれている雰囲気
    • アジャイルな開発フローも自分に合っている気がした
    • 開発速度・品質のバランス感
  • 技術
    • 技術選定のモダン感や尖りすぎず枯れすぎず柔軟な感じ
    • サーバーサイドがGoで、今までのAPI開発経験が役に立ちそう
    • Unityでの開発は前々から興味あったけどソシャゲ以外でやれそうなので

まだ数名ですが社員募集中

募集要項はたぶん以下みたいな感じです(近いうちにcluster.募集要項が更新されるはず...!?)

  • DDDやTeam Geekアジャイルサムライに書いてあるようなことに理解がある
  • 業務経験等でGoでAPI or Unityでクライアントがしっかり開発できる
  • VR開発に興味がある
  • サーバーとクライアント両方やっていきたい

ほしいものリスト

www.amazon.co.jp

一応貼っておきますが...

退職時にたくさん門出を祝ってのプレゼントを頂いたので、すでに送っている方はお気持ちだけで結構ですので!!!! (さすがに、恐縮しちゃいます... (^_^;)

おふろcafe utataneで格安開発合宿をした

合宿 Unity 日記 感想

ofurocafe-utatane.com

また合宿してました。もう今年5〜6回は合宿してる気がする。

一泊する合宿では最安値。移動とか食事とかビールとか込みで、トータル1万円くらい

(圧倒的コスパ感)

宿のポイント

Good

  • 1ヶ月前に予約したら早割で1泊1人2900円という安さ
  • 無限コーヒー2種類
  • 管内着がゆったり
  • なんか雰囲気がよい
  • サウナ・露天風呂あり。風呂結構広い
  • Cafeが結構料理とかクオリティ高い(1食800〜1500円くらい)
  • 24:00以降も利用できて、フロントでビールとかツマミを買ってダラダラ飲めて良かった

Bad

  • 値段が安いせいか、学生とか女子会?っぽいメンバーが多い
  • 席がなくて通路とか床に座ってる人がいた
  • 荷物とか本を置きっぱなしにして強引に席を確保して回転率を下げている集団が多かった
  • ランチの回転率が圧倒的に悪かった(2時間制とかなんかうまく運用でカバーしないとやばそう)

今回の予定と準備

作るもの

放置ゲーのクライアントをUnityで開発する

準備

  • 当日までにざっくりペーパープロトタイプっぽいのを作成しておいた
  • 空プロジェクトを作ってUniRxとか最低限必要なプラグインとかは準備したものをGithubにpush済み
  • 当日のTODOリストとやらないことリストを作成して集中できるようにしておいた

やるやらのメモ

# やるやら

#### やること

- スタート画面-> ホーム画面 -> 探索開始 -> 探索中 の流れをざっくり作る
- 各画面に必要な情報をできるだけ揃える

#### やらないこと

- ログインの実装は今回やらない(固定ユーザーでいい)
- ショップとかステータス割り振りは実装しない
- カッコイイ見た目とか実装しない
- カッコイイアニメーションとか実装しない
- APIの呼び出しはやらずRepository層でmockデータを返すようにしておく

なんとなく画面遷移とか流れとか

f:id:kyokomi:20160922140859j:plain

f:id:kyokomi:20160911175327j:plain

9/19(月) 1日目

10:00 到着

10〜20人ほど並んでいたが、宿泊客は別の列でささっと受付をすませる。 とりあえず風呂へ

10:15〜11:00 風呂1

結構広かった。露天もあるしサウナもあった。 風呂の種類も5種類くらいあったきがする。

11:00〜11:30 管内をうろうろ

結構人が多くて座れる場所がなかったのでウロウロしながら席を探す。

11:30〜12:30 昼食

ウロウロしてるときにJKの集団が「お昼めっちゃ混むから早めに食べよう」という会話をしていたので、便乗して早めに食べることに。 案の定12:30以降くらいから食べようとしている人は2〜3時間待ちくらいになってた。

f:id:kyokomi:20160922142934j:plain

12:30〜18:00 開発1

テーブル席がとれなかったので膝Macbookという微妙な席だったけど結構集中して作業ができた。

18:00 チェックイン

18:00からチェックイン可能で荷物を部屋にもっていき風呂へ向かう

18:15〜18:45 風呂2

2度目の風呂。膝Macbookで汗をかいてしまったので気持ちよかった。

18:45〜20:30 夜ご飯

30分作業しながら席が空くまで呼び出し待ち。 晩御飯とビールを1杯。大体1000〜1500円くらい

20:30〜23:00 開発2

テーブル席空かなかったので、そのまま食事とった席で開発(この時間になるともう食事待ち列はなくて空いてたので)

23:00〜23:30 風呂3

さすがに3回目なのでサッと入った。

23:30〜25:30 ビール

フロントでビールを買って暖炉付近で適当まったり晩酌。

f:id:kyokomi:20160922142909j:plain

f:id:kyokomi:20160922142901j:plain

25:30 就寝

寝心地はまあまあ

9/20(火) 2日目

7:00 起床

風呂へ

7:15〜7:45 風呂4

雨が強かったので露天は避けた。

8:00〜9:00 朝食

ビッフェスタイルで中々良かった。800円くらい

f:id:kyokomi:20160922142847p:plain

10:00 チェックアウト&入館

一旦チェックアウトして外へ出て、宿泊じゃない一般客としてフリープラン(1300円くらい)で再入場。

10:15〜11:30 開発3

最初からテーブル席を確保してがっつり作業。捗った。

11:30〜12:30 お昼

さすがに平日なので結構空いていた。

12:30〜18:30 開発4

ひたすら開発。めっちゃ捗る。やはり二日目の効率の良さよ。

18:30〜19:00 風呂4

最後にひとっ風呂入ってから帰ることに

19:00 終了

1時間毎に大宮行きのシャトルバスがあったけど満員だったので歩いて最寄り駅までいって帰りました。

開発環境とか

  • Github private repositoryでソースコードは管理
  • 適当なプロジェクトネーム(琥珀さん => 琥珀 => Amber)
  • 開いたときにテンションあがる画像をREADME.mdにおいておく
  • 今までだったらZenHubを使ってカンバンしてたけど、今回はためしにGithubのProjectsを使ってみた

f:id:kyokomi:20160922141046p:plain

f:id:kyokomi:20160922141249p:plain

成果物など

大体13時間くらい集中して開発できた。

f:id:kyokomi:20160922144703p:plain

想定してた画面のプロトタイプはざっくり作れた。あとはAPI呼び出しとか中身を作り込むのと見た目をかっこよくする感じ。

f:id:kyokomi:20160922144210p:plain

所感・雑感

  • UnityでもMVP+CleanArchitecture全然いける感じある(UniRx感謝)
  • やはり開発合宿は2日間あると余裕があって良い(1日だと、エンジンかかったころに終わってしまう)
  • 1泊2日でも2日目が帰るだけじゃなくて開発にあてるのは大変良い
  • 事前準備は大事
  • おふろcafeもうちょっと値段あげて、入場客を絞ればいい環境だなーという感じ
  • おふろcafe雰囲気とか設備は大変良いので、最近できた熊谷店もちょっと気になる

GoAccess+Nginxでアクセスログを計測

nginx goaccess

こちらの記事が、GoAccessのバージョンが古くてformatでハマったのでメモがてら書いておきます。

qiita.com

環境

  • CentoOS 7
  • GoAccess - 0.9.8.
  • nginx/1.10.1

nginx.ltsv

一応Qiitaの記事との比較もあわせて記載してます。

goaccess + nginxのやつ http://qiita.com/msykiino/item ...

おわり

わーい

f:id:kyokomi:20160917195052p:plain

CircleCI上でdynamodb-localを使ったgo testを実行する

golang Go dynamodb CircleCI test

はじめに

本当は、dynamodbを呼び出す箇所をinterface化してgolang/mockとかでmockしてtestするほうが良い思います。

ただ、そうもいかない状況とかもあるのでdynamodb-localでtest用のregionを使ってtestする方法を紹介したいと思います。

例)とあるテーブルにupdateとgetするコード

以下の updateExampleDatafetchExampleDataをテストしたいという状況。

package main

import (
    "fmt"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/guregu/dynamo"
    "github.com/labstack/gommon/log"
)

const (
    region    = "us-west-2"
    endpoint  = "http://127.0.0.1:7777"
    tableName = "example-table"
)

var (
    dyn *dynamo.DB
)

type exampleDynamoTable struct {
    ID      int `dynamo:"ID,hash"`
    Count   int
    Message string
}

func main() {
    dyn = dynamo.New(session.New(), &aws.Config{
        Region:   aws.String(region),
        Endpoint: aws.String(endpoint),
    })

    if err := dyn.CreateTable(tableName, exampleDynamoTable{}).Run(); err != nil {
        fmt.Println() // 二度目のtable作成は雑にスルーしてる
    }

    // tableのデータを更新
    id := 1
    if err := updateExampleData(id, 100, "test"); err != nil {
        log.Fatal(err)
    }

    // 更新したデータを取得
    exampleData, err := fetchExampleData(id)
    if err != nil {
        log.Fatal(err)
    }
 
    fmt.Printf("%#v\n", exampleData)
}

func updateExampleData(id int, count int, message string) error {
    updater := dyn.Table(tableName).Update("ID", id)
    updater.Set("Count", count)
    updater.Set("Message", message)
    return updater.Run()
}

func fetchExampleData(id int) (exampleDynamoTable, error) {
    var e exampleDynamoTable
    return e, dyn.Table(tableName).Get("ID", id).One(&e)
}

testコード

package main

import (
    "fmt"
    "testing"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/guregu/dynamo"
    "github.com/stretchr/testify/assert"
)

func TestExampleData(t *testing.T) {
    as := assert.New(t)

    dyn = dynamo.New(session.New(), &aws.Config{
        Region:   aws.String("test-region"),
        Endpoint: aws.String(endpoint),
    })

    // 前回のデータを雑に削除
    if err := dyn.Table(tableName).DeleteTable().Run(); err != nil {
        fmt.Println()
    }
    // テーブルを雑に作成
    if err := dyn.CreateTable(tableName, exampleDynamoTable{}).Run(); err != nil {
        fmt.Println()
    }

    testCase := exampleDynamoTable{
        ID:      2,
        Count:   300,
        Message: "hoge",
    }

    as.NoError(updateExampleData(testCase.ID, testCase.Count, testCase.Message))

    exampleData, err := fetchExampleData(testCase.ID)
    as.NoError(err)
    as.EqualValues(exampleData, testCase)
}

ローカル環境でのtest

以下のようなdocker-compose.ymlを用意して、dockerでdynamodb-localを立ち上げてtestを実行すればOKです。 (別にbrewとかで入れても良いです)

version: '2'
services:
  dynamodb:
    image: tray/dynamodb-local
    command: tray/dynamodb-local -port 7777
    ports:
      - "7777:7777"

test実行

$ go test . -v
=== RUN   TestExampleData
{
  ExpressionAttributeNames: {
    #sQ291bnQ: "Count"
  },
  ExpressionAttributeValues: {
    :v0: {
      N: "300"
    },
    :v1: {
      S: "hoge"
    }
  },
  Key: {
    ID: {
      N: "2"
    }
  },
  ReturnValues: "NONE",
  TableName: "example-table",
  UpdateExpression: "SET #sQ291bnQ = :v0, Message = :v1"
}
--- PASS: TestExampleData (0.21s)
PASS
ok      github.com/kyokomi/example-circleci-dynamodb-go 0.235s

CircleCIの設定

circle.yml

# dynamodb-local
# https://discuss.circleci.com/t/how-to-install-dynamodb-local/2018/2
machine:
  environment:
    _JAVA_OPTIONS: "-Xms512m -Xmx1024m"
  java:
    version: openjdk7
  post:
    - curl -k -L -o dynamodb-local.tgz http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.tar.gz
    - tar -xzf dynamodb-local.tgz
    - java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -port 7777:
        background: true

checkout:
  pre:
    - go env
    - go version

コンソールからは、 AWS permissionだけ設定しておきましょう

f:id:kyokomi:20160909143730p:plain

awsのライブラリとdynamodb-localが作成するDBの名前とかに使うだけなので、上記のような適当な値でOKです。

実行結果

f:id:kyokomi:20160909145406p:plain

f:id:kyokomi:20160909145348p:plain

https://circleci.com/gh/kyokomi/example-circleci-dynamodb-go/2

ライブラリ

ちなみにdynamodbのライブラリはこちらを使ってます。mgoっぽく使えて使いやすくて便利です。

github.com

サンプルコード

サンプルコードはすべて、こちらに上げておきましたので宜しければご覧ください。

github.com