goa勉強会 in 六本木一丁目でLTしてきましたので内容の補足など
LTしてきました。色々学びあって楽しかったです。
会場提供のistyleさんありがとうございました。
少しだけ補足説明とexampleコードを書いたので紹介しようと思います。
LTした資料
注意点
- goとgoaについてすでにある程度理解がある人向けです
- goaのversionは、v1.2.0の前提で話しています
go generate使ってコード生成して開発する流れについて
–forceをつけることで毎回mainのgenerateは作り直してます。
//go:generate goagen -o gen main --force -d github.com/kyokomi/example_goa_api/_design //go:generate goagen -o gen app -d github.com/kyokomi/example_goa_api/_design //go:generate goagen -o gen swagger -d github.com/kyokomi/example_goa_api/_design
_design下を変更したときの作業手順は以下のような感じになります。
- _design下を編集する
- ルートディレクトリでgo generateする
- 生成された
gen/main.go
から追加したリソースのMountのコードをルートディレクトリのmain.go
にコピペする gen/
下に生成されたリソースのControllerをapplication/controllers
下にコピーしてimport等を直す(リソース追加ではなく編集の場合は、必要なMethodだけコピペする)
swaggerの配布について
werckerのworkflowの設定はこんな感じです。
build
のworkflowはexampleのリポジトリをご覧ください(https://github.com/kyokomi/example_goa_api/blob/master/wercker.yml)。 build
のworkflowが終わったあとに design/*
のブランチ名だったら put-swagger
のworkflowが実行される感じになってます。
put-swagger: box: golang:1.8 push-to-ecr: steps: - script: name: set timezone and deploy tag code: | export VERSION=`cat $WERCKER_ROOT/VERSION` echo $VERSION - edgecaseadmin/install-aws-cli: key: $AWS_ACCESS_KEY_ID secret: $AWS_SECRET_ACCESS_KEY region: $AWS_REGION // s3にswagger.jsonをコピーする - script: name: s3 put swagger code: | aws s3 cp $WERCKER_ROOT/gen/swagger/ s3://swager.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/ --recursive // s3に配置したswaggerのjsonをurlに指定したswagger-uiのリンクをpullRequestコメントする - script: name: swagger github comment code: | go get github.com/kyokomi/gh-rice gh-rice \ -t "$GITHUB_TOKEN" \ -o "$WERCKER_GIT_OWNER" \ -r "$WERCKER_GIT_REPOSITORY" \ -b "$WERCKER_GIT_BRANCH" \ -c "http://swagger.example.dev/?url=http://swagger.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/swagger.json"
※s3のパスとかは適当です
exampleコード
ヘルスチェックのAPIくらいしか書いてないのであれですが、参考になればと。
gormのCallbackをつかってexplain結果をログ出力する
はじめに
explainは基本的に実装時に自分で叩いてると思いますが、dev環境とかそこそこデータとか利用頻度が高い環境で雑に垂れ流したいなーと思ってgormのDebug実装を眺めていたらCallbackという仕組みがあったので、それを使ってみました。
作ったもの
使い方
gormのexampleをにちょこっと追加してみました。
package main import ( "fmt" _ "github.com/go-sql-driver/mysql" // mysqlを使う "github.com/jinzhu/gorm" explain "github.com/kyokomi/gorm-explain" ) type options struct { user string password string host string port int dbName string location string } func buildDataSourceName(opts options) string { const format = "%s:%s@tcp(%s:%d)/%s?parseTime=True&loc=%s" return fmt.Sprintf(format, opts.user, opts.password, opts.host, opts.port, opts.dbName, opts.location) } // Product product table struct type Product struct { gorm.Model Code string Price uint } func main() { opts := options{ user: "test-user", password: "test-user", host: "127.0.0.1", port: 3306, dbName: "test_db", location: "UTC", } db, err := gorm.Open("mysql", buildDataSourceName(opts)) if err != nil { panic("failed to connect database") } defer db.Close() db = db.Debug() // query trace db.Callback().Query().Register("explain", explain.Callback) // add explain callback // Migrate the schema db.AutoMigrate(&Product{}) // Create db.Create(&Product{Code: "L1212", Price: 1000}) // Read var product Product db.First(&product, 1) // find product with id 1 db.First(&product, "code = ?", "L1212") // find product with code l1212 // Update - update product's price to 2000 db.Model(&product).Update("Price", 2000) // Delete - delete product db.Delete(&product) }
実行した結果
(あと、gormのDebug機能をつかうとqueryがログに吐かれるようになるのでこれを合わせて使うと便利です)
(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:53) [2017-05-05 18:16:38] [1.08ms] INSERT INTO `products` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ('2017-05-05 18:16:38','2017-05-05 18:16:38',NULL,'L1212','1000') (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:57) [2017-05-05 18:16:38] [0.95ms] SELECT * FROM `products` WHERE `products`.`deleted_at` IS NULL AND ((`products`.`id` = '1')) ORDER BY `products`.`id` ASC LIMIT 1 +-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +=======+================+==========+===============+=========+==================+========+============+========+=========+=============+========================================================+ | 1 | SIMPLE | | | | | | | | | | Impossible WHERE noticed after reading const tables | +-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+ 1 Fix example to add query trace (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:58) [2017-05-05 18:16:38] [1.11ms] SELECT * FROM `products` WHERE `products`.`deleted_at` IS NULL AND ((code = 'L1212')) ORDER BY `products`.`id` ASC LIMIT 1 +-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +=======+================+=============+===============+=========+============================+============================+============+==========+=========+=============+=======================================+ | 1 | SIMPLE | products | | ref | idx_products_deleted_at | idx_products_deleted_at | 5 | const | 2 | 100 | Using index condition; Using where | +-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+ (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:61) [2017-05-05 18:16:38] [0.86ms] UPDATE `products` SET `price` = '2000', `updated_at` = '2017-05-05 18:16:38' WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7' (/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:64) [2017-05-05 18:16:38] [0.88ms] UPDATE `products` SET `deleted_at`='2017-05-05 18:16:38' WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7'
注意点
さっと書いたので、fmtで出力してます。ちゃんと自前のロガーとかで出力したい場合は、このコード参考に自分で書いてください 🙏🏻 (もしくはPullRequestまってます 😇 )
おわり
explainの結果に filesortが含まれていたら〜みたいなHandlerを設定できるような感じに改修とかすると便利そうだな〜とか思ってます。
esaのAlfred WorkflowをGoで作りました
作ったもの
とりあえずtokenとチーム名を登録して記事を検索できるだけです。
注意点
ちなみに、esaのAPIは利用制限があるので検索しすぎにご注意ください
https://docs.esa.io/posts/102#2-3-0
現時点では、ユーザ毎に15分間に75リクエストまで受け付けます。 とのこと
alfred-esa-workflow作ったんだけど、 API利用制限が15分間に75リクエストということで速攻で使えなくなった :innocent: pic.twitter.com/0BRWGTXaM9
— きょこみ@k02 (@k_yokomi) January 28, 2017
参考にしたもの
使ったライブラリ
filterで一覧表示するのがこんなに楽に実装できて感謝
esaのClientすでに作っている方がいてesaのAPI仕様はあまり見ないでもサクッと作れました! 感謝
最初、直接 Open in Finder
したフォルダ内にバイナリを放り込んで開発してたんですが、 普通に xxx.alfredworkflow
をダブルクリックしてInstallして動作確認したかったので、 alfred-workflow-packager
を使うようにしました。(まあ、色々replaceしてzipに固めるだけっぽいですが)
ただ、これはMacでAlfred3がインストールされている前提のコードがあるので、ちょっと書き直してCIで動くようにしようかなと思ってます。
GoでAlfredWorkflowを作る手順(雑メモ)
AlfredWorkflowつくるぞ〜と思ってから最初どうすればいいかわからない具合いが結構すごかったです。
とりあえず自分は、似てるworkflowをinstallして Alfred上で Open in Finder
して中身を漁ってつくりを把握してつくってみました。
思ったより単純な構造で、基本的には以下の流れでつくればいいのかなという理解。
- 普通にCLIツールをGoで作る
-
Script filter
での出力はgo-alfred
等を使ってxmlをPrintする Run Script
してPost Notification
に表示する出力は普通にfmt.Print
でOK
-
- Alfred上でkeywordsとかをとりあえず作成して
./<バイナリ名> search "{query}"
みたいな感じでCLIツールの呼び出しする Open URL
とか適当なActionを紐付けて、とりあえず動くところまでいくOpen in Finder
してicon.png
とinfo.plist
だけコピーしてGoのProjectにコピーする- あとは
alfred-workflow-packager
を使ってxxx.alfredworkflow
を作成して、ダブルクリックしてインストールして動作確認していく感じ
まとめ
Alfred便利なのでどんどん作っていくぞ!!!!!!
昔作ったgaego-initをglide対応した
gaego-initとは以下のように、GAE/Goの開発環境をコマンド一発でgenerateするやつです。
$ gaego-init -app hoge-app $ tree hoge-app/ hoge-app/ ├── Makefile ├── README.md ├── appengine │ ├── app.yaml │ └── init.go └── circle.yml 1 directory, 5 files
以前作ったときは、vendoringの対応をどうするかべきかわからず、結局shellでgoapp getするという荒業をしていたのでそれを修正しました。
# Makefile 22:26:56 APP_ROOT=appengine VENDOR_GOPATH=$(shell pwd)/$(APP_ROOT)/gopath/vendor APP_GOPATH=$(shell pwd)/$(APP_ROOT)/gopath RUN_GOPATH=$(APP_GOPATH):$(VENDOR_GOPATH) install: glide init glide install mkdir -p $(APP_ROOT)/gopath/vendor ln -s $(shell pwd)/vendor/ $(VENDOR_GOPATH)/src run: GOPATH=$(RUN_GOPATH) goapp serve $(APP_ROOT) clean-run: GOPATH=$(RUN_GOPATH) goapp serve --clear_datastore $(APP_ROOT) deploy: GOPATH=$(RUN_GOPATH) goapp deploy $(APP_ROOT)
こちらの記事を参考にglideでのvendoringを行えるようにしました。
感謝 🙏 🙏 🙏 🙏 🙏
Arukasを使って無料でGo製のslackbotを運用する
はじめに
SlackbotをHerokuで一日中動かすと課金が発生するので色々ハックが必要となったりするので、なんとかしたくArukasに出会いました。
今回は、自分が作ったgo製のslackbotを元につくったbotをgithubにpushしてWercker経由でdockerImageを作ってArukasへのDeployを行う方法を紹介します。
今回のコードとか設定をexampleとして公開してますので、よろしければ参考にしていただければと。
Arukasとは?
いまのところ無料でDockerをホスティングできるサービスです。
一応CLIツールも公開されていて、結構便利です。
※注意点としては、CMDで起動したプロセス監視とかは無いので自分でとかgo-server-starterとかを入れてプロセス死んだときに再起動する仕組みを入れる必要があるところです。
流れ
- 以前作ったgo製のslackbotのdockerImageをWerckerで作成する kyokomi.hatenablog.com kyokomi.hatenablog.com
- Arukasのコンソールでアプリケーションを作成し、dockerImageを指定する
- WerckerのworkflowでArukasのrestartを設定する
slackbotのコード自体はDockerとか意識しない形になっているので、wercker.ymlとかを見ていただければと。
example-go-slackbot/wercker.yml at master · kyokomi/example-go-slackbot · GitHub
Werckerの設定
README.md に書いてある通り6つの環境変数の登録が必要です。
- DockerHubまわりは、普通に自分のアカウントとかリポジトリを入れてもらえればOKです
- Arukasまわりは、 https://app.arukas.io/settings/api-keys で発行したAPIKeyとSecretを設定してください
ポイントはdocker-pushのworkflowで github.com/lestrrat/go-server-starter/cmd/start_server
をinstallしてます。
まあ別にバイナリをdonwloadでもいいんですが面倒だったので...
docker-push: box: golang steps: - glide-install - setup-go-workspace - script: name: install deamontools code: | go get github.com/lestrrat/go-server-starter/cmd/start_server - script: name: install application code: | go install - internal/docker-push: username: $DOCKER_HUB_USERNAME password: $DOCKER_HUB_PASSWORD tag: latest repository: $DOCKER_HUB_REPOSITORY registry: https://registry.hub.docker.com
arukas-deployのworkflowでは、 arukasのバイナリをdownloadして適当にPATHを通してstop -> startしてます。
sleep 10
を挟んでいるのは、stopする前にstartするとエラーになるので仕方なくです。
arukas-deploy: box: golang steps: - script: name: install tools code: | sudo apt-get update sudo apt-get -f install sudo apt-get install -y wget unzip curl tree - script: name: arukas install code: | mkdir -p $HOME/lib export PATH=$PATH:$HOME/lib cd $HOME/lib wget https://github.com/arukasio/cli/releases/download/v0.1.2/arukas_v0.1.2_linux_amd64.zip unzip arukas_v0.1.2_linux_amd64.zip rm arukas_v0.1.2_linux_amd64.zip - script: name: arukas restart code: | arukas stop ${ARUKAS_CONTAINER_ID} sleep 10 arukas start ${ARUKAS_CONTAINER_ID}
Arukasの設定
- SlackbotのTokenは、
Bots
かhubot
で作ったTokenを使ってください - docomoのAPIは雑談で使ってます(必須じゃないので不要なら設定しなくてOKです)
- REDISTOGO_URLは、
redis://<id>:<password>@<host>:<port>
の形式で指定します- 雑談のContext保持とCronコマンドの保存に使っています
- Redis To Go のfree planを使うのがおすすめです
- CMDでgo-server-stater経由でbotを起動するように指定してます
おまけ
ちゃんとプロセス死んで再起動するかの動作確認ですが、昔ネタでいれたcommandを実行させるプラグインが役に立ちました。(危険)
何度でも蘇る。
2016年振り返り
主にTwitterの全ツイート履歴を見て振り返る。
振り返り
半分くらいは業務コードと思われる.
開発合宿とかPC持っていく旅行
- 1月: おんやど恵 kyokomi.hatenablog.com
- 2月: 足湯しながら開発
twitter.com足湯しながら開発するぞい pic.twitter.com/DT9Vvbiepo
— きょこみ@公認錬金術師 (@k_yokomi) 2016年2月27日- 7月: 熱海へ(blog書いてなかったな...)
— きょこみ@公認錬金術師 (@k_yokomi) 2016年7月9日
twitter.com- 9月: おふろcafe kyokomi.hatenablog.com
結構同じメンバー何回も開発合宿行ってて、会社が別になっても気軽に集まれる関係なのがいいなと思いました。 来年も行きましょう!!!
各月のサマリー
2〜7月: ひたすらAndroidして退職
- Droidkaigiに行った
- 業務でAndroidエンジニアが居なくて困っていたので、Androidエンジニアにコンバートした
- ひたすらAndroid書いてた。とにかく仕様バグとかクラッシュを削りながらな日々
twitter.comよほどクリティカルなコードじゃない限りテスト書くの諦めてる。
— きょこみ@公認錬金術師 (@k_yokomi) 2016年4月28日
スピード/改善/安全=3/3/4くらいのバランスで維持してる。もう少し落ち着いたらtest書いたりして安全の比率を上げたい。- 去っていた人のコードを当時いた人の気持ちになってリーディングするサイコメトリーをマスターする
twitter.com@axross_ 全身で感じる…! pic.twitter.com/yNv4htPouC
— きょこみ@公認錬金術師 (@k_yokomi) 2016年4月21日
twitter.comば、馬鹿な・・・このソースコードは、サイコメトリーできない。。。
— きょこみ@公認錬金術師 (@k_yokomi) 2016年5月26日- Androidそこそこできるようになったので、Androidエンジニアの採用を頑張っていた
- 制約と誓約を意識して、Goを書かないで全ステータスをAndroidに振っていた
- 7月一杯でGunosyを退職
8月〜9月
- フリーランスとして3社くらい手伝っていた
- この頃からクラスター社でお仕事をしていた
twitter.comなう #cluster #zega #cluster https://t.co/G7AY6zX88u pic.twitter.com/L5mq7tnwNG
— きょこみ@公認錬金術師 (@k_yokomi) 2016年8月31日- 今まで直近の業務にすぐに役立ちそうにないなーと思って積んでいた本をここで一気に消化
- Dockerを使う機会が増える
- Unity再入門
- クラスター株式会社のバックエンドAPIにgoaを導入(それまでは、echoだった)
- ISUCON6予選に出場してギリギリ予選突破
10月〜12月
- クラスター株式会社に入社 kyokomi.hatenablog.com
- HTC VIVEを自宅に導入
twitter.comついに我が家にもHTCViveきました! pic.twitter.com/ZOczCTVeEE
— きょこみ@公認錬金術師 (@k_yokomi) 2016年10月1日- ISUCON6本戦に出場したが敗退 kyokomi.hatenablog.com
- WebAPI書きつつ、AWSメインでterraform書いたり、Lambdaとか触ったりなどなど
- 使っていた折りたたみ自転車のサドルが盗まれたので、これを機にクロスバイクを購入(TREK FX3を買った)
- ついに食洗機を購入
その他
麻雀
2016年は10回くらい打った気がする。役満は1回。 メンツは結構バラバラだったけど、トータル勝ち越した気がする。
twitter.com国士無双上がったぞい pic.twitter.com/jBaLeKmUzh
— きょこみ@公認錬金術師 (@k_yokomi) 2016年3月28日
関係者各位また定期的にやりましょう!!!
個人開発
- GAE/Goでちょっとしたキュレーションシステム
- Androidアプリ2つ(キュレーションのクライアントと、Twitterの画像をひたすら表示するやつ)
- 放置ゲーのAPIサーバーをGAE/Go -> ECSに移行
- 放置ゲーのクライアントをUnityでプロトタイプ作成
相変わらずエターなってるけど、作ったものを自分では使っているのでそこそこ満足している。
まとめ
- 2016年は発表とかブログとかアウトプット少なめで本を読んだり技術検証したりでインプット多めだった気がする
- 温泉行って開発合宿すると疲労を回復しながら、開発が捗るので2017年もやっていきたい
- 2017年はリリースもあるので、Tipsとか開発フローとかについてとか発表したり、ブログでの情報発信とかもしっかりやっていくぞ
werckerでprivate repositoryを含むglide installする方法
手順
- glide.ymlでsshのURLを指定する
- wercker上でApplication Environmentか Organization settings Environmentで
+ Generate SSH Keys
してssh keyを環境変数に登録する - 「2.」で登録したssh keyをgithub上の Settingsの
SSH keys
で登録する - wercker.ymlに
add-ssh-key
とadd-to-known_hosts
を記載する
glide.ymlとwercker.yml
fingerprintはこちらを参考に入力しましょう。
What are GitHub's SSH key fingerprints? - User Documentation
wercker Generate SSH Keys
Application Environment
Organization settings Environment
こっちで指定すると全部のアプリケーションで使いまわせます。