キチログ

Keep it simple, stupid.

Protocol Buffer と Message Pack の違いとベンチマークを比較

TL;DR

恥ずかしながらProtocol BufferとMessage Pack触ったことない..。

ので、実際に触ってみて、自分なりにみて考えるみるのが目的。

コードはこちら https://github.com/uqichi/go-protobuf-msgpack

Protocol Buffer と Message Pack の違い

Protocol Bufferとは

https://developers.google.com/protocol-buffers/

What are protocol buffers? Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

データを構造化するための、言語中立的でプラットフォーム中立的な拡張システム。 XMLみたいな、けれども、小さくて早くてシンプルなやつ。 一度構造化してしまえば、自動生成されるコードを簡単に使えるし、言語を問わず様々なデータストリームの読み書きができる。

Message Packとは

https://msgpack.org/

It's like JSON. but fast and small. MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.

JSONみたいな、だけど早くて小さい、効率のいいバイナリシリアライゼーションフォーマット。 JSONのようにいろんな言語のデータ交換ができる上に、早くて小さい。 小さい整数はシングルバイトにエンコードされるし、よくある短い文字列はほんの数バイトの長さで済む。

それぞれの思想の違い

Protocol BufferとMessage Packはよく比較されますが、それぞれの公式サイトに記載されているこいつはなんぞやについての説明文(上記)の一文目を読むと、その根底が違うことが端的に表れているように思う。

Protocol Bufferはあくまでデータの構造化に重きを置いているのに対し、 Message Packはserialize/deserializeの速度とデータ長の圧縮をアドバンテージとしている。

Protocol Bufferの特徴(メリット・デメリット)

  • IDL (Interface Definition Language) によるシリアライザ実装コードの生成
  • 複数の言語間のデータ構造定義を共有できる
  • 静的型付け

    Message Packの特徴(メリット・デメリット)

  • 高速
  • データが小さくなる
  • 動的型付け

Protocol Bufferのproto2とproto3の違い

https://qiita.com/ksato9700/items/0eb025b1e2521c1cab79

根底の思想から仕様が変わる部分があるが、新しくProtocol Bufferの利用を開始する場合はあまり気にすることはない。

proto3は、 required オプションの指定ができなくなっていたり、デフォルト値の指定ができなくなっていたりと一見不便なアップデートに見えますが、端的に言えば、フォーマットの下位互換性を保つという目的のためのよう。

まだ試行錯誤の段階で恒久的な変更では内容ですが、新しい方針にしたがってproto3を使った方が良さそう。

また、proto3の新機能としてJSONへのマッピングが可能。

Protocol Bufferの使い方

IDLを定義する

必要なツール群をインスコ

brew install protobuf
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

.protoファイルを作成

syntax = "proto3";

package proto;

message Product {
    uint64 id = 1;
    string name = 2;
    string description = 3;
    int32 price = 4;
    repeated string colors = 5;
}

message ProductList {
    repeated Product products = 1;
}

コンパイルしてシリアライザのコードを生成

mkdir -p proto
protoc --go_out=proto *.proto

https://github.com/uqichi/go-protobuf-msgpack/blob/master/proto/product.pb.go が生まれました。

簡単にAPIを実装してみる

package main

import (
    "log"
    "net/http"

    "github.com/golang/protobuf/proto"
    _proto "github.com/uqichi/go-protobuf-msgpack/proto"
)

func main() {
    http.HandleFunc("/protobuf", handlerProtobuf)

    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal(err)
    }
}

func handlerProtobuf(w http.ResponseWriter, r *http.Request) {
    p := &_proto.Product{
        Id:          191919191919,
        Name:        "FIFA World Cup Soccer Ball",
        Description: "Lorem ipsum dolor sit amet,,,",
        Price:       18900,
        Colors:      []string{"red", "yello", "blue"},
    }

    res, err := proto.Marshal(p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }

    w.Header().Set("Content-Type", "application/protobuf")
    w.WriteHeader(http.StatusOK)
    w.Write(res)
}

Message Packの使い方

続いて、Message Packも実装してみます。

ライブラリは複数あるようですが、今回は github.com/vmihailenco/msgpack を使っています。

package main

import (
    "log"
    "net/http"

    "github.com/vmihailenco/msgpack"
)

func main() {
    http.HandleFunc("/protobuf", handlerProtobuf)

    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal(err)
    }
}

type product struct {
    Id          int64
    Name        string
    Description string
    Price       int32
    Colors      []string
}

func handlerMsgpack(w http.ResponseWriter, r *http.Request) {
    p := &product{
        Id:          191919191919,
        Name:        "FIFA World Cup Soccer Ball",
        Description: "Lorem ipsum dolor sit amet,,,",
        Price:       18900,
        Colors:      []string{"red", "yello", "blue"},
    }

    res, err := msgpack.Marshal(p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }

    w.Header().Set("Content-Type", "application/x-msgpack")
    w.WriteHeader(http.StatusOK)
    w.Write(res)
}

Protocol Bufferと比べて、IDLがないので、シリアライズする実装のみ書けば使える。

Protocol BufferとMessage Packのテストコード

https://github.com/uqichi/go-protobuf-msgpack/blob/master/serialize_test.go

Protocol BufferとMessage Packのベンチマーク

https://github.com/uqichi/go-protobuf-msgpack/blob/master/serialize_bench_test.go

ベンチをとるにあたって押さえておきたい前情報。 結果が変わりそうなものとか実際の運用で必ず使いそうな型とかを対象に含めるように意識してテストを作ります。

  • 整数が符号付きか符号無しか
  • 文字列の長さ。短いか長いか
  • 配列は結構使いそう
  • JSONも入れて比較してみる

ベンチマーク結果

  • Protocol Buffer
  • Message Pack
  • and JSON

Serialization

BenchmarkProtobuf-8      1000000              1640 ns/op            1392 B/op         12 allocs/op

BenchmarkMsgpack-8        500000              3400 ns/op            2600 B/op         24 allocs/op

BenchmarkJSON-8           200000              8348 ns/op            2224 B/op         18 allocs/op

Data length

Content-Type: application/protobuf
Date: Mon, 24 Sep 2018 14:49:51 GMT
Content-Length: 505

Content-Type: application/x-msgpack
Date: Mon, 24 Sep 2018 14:48:58 GMT
Content-Length: 539

Content-Type: application/json
Date: Mon, 24 Sep 2018 14:50:14 GMT
Content-Length: 563

serialize/deserializeの速度、データ長どちらもprotocol bufferの勝利となってしまった。 うーん。なんでだ...

条件を変えればmessage packに軍配が上がるのだろうけど、今回はこういったシンプルな条件でやってみた。

今度また再実験してみよう。

まとめと所感

Protocol Buffer, Message Pack, JSONを比較してみたわけだが、それぞれ使い所がありそう。

Protocol BufferとMessage PackがJSONより高速で軽いからといって、JSONが使えないことは決してなく、例えば外向けのAPIを提供する場合なんかはJSONが使われるのがもっとも効率的だと思う。

コンパイラ要らないし、読みやすい。 また、デバッグがしやすいというのもJSONの強み。バイナリ化されると普通は読めないのでHTTP Clientによる開発にはやはりJSONが返却された方がやりやすい。 逆に、Message Pack使うと結構大変そうだ。

Protocol BufferのIDLは多言語間でのデータのやり取りをする上でかなり役にたつ。近年はAPIサーバーを立てて、web・iOSアプリ・Androidアプリがぶら下がってるようなプロジェクト構成が一般的になりつつあるので、IDLによって構造を定義し、互換性のあるデータの交換やドキュメントにもなりうるのはとても嬉しい。 フィールド名も抽象化されるため、簡単にrenameできるし型変更だって容易。 Message Packにも、proto3からJSONにもコンパイルできるということなのでこれを使わない手はない。

Message Packは手軽に使えるというのが利点、なのかな。これは、IDLみたいなスキーマ定義がないということの裏返しになる。 スキーマがはっきりしないもの、構造化や正規化が難しいものには相性が良いみたい。 アプリケーションログなんかをファイルに書き出してやるとか。 データストアだと、RedisみたいなKVSとかドキュメント指向のMongoDBとかは、いいお友達になれるんじゃないか。

一番大きい違いと使い所の見極めは、スキーマ定義をするかしないかだと思いました。

するならProtocol Bufferは絶対使った方がいい。

しないなら、JSONかMessage Pack。JSONは可読性が高く扱いやすいし汎用的。MessagePackは高速で軽い。

って感じかー。

結局Protocol Buffer使うのが最良なんじゃないという結論に落ち着いたので、次回はgRPCを試してみようと思う。

MySQL5.7のインデックスを操作する方法

MySQL5.7のインデックス操作をメモ。

既存のテーブルにインデックスを追加する場合Syntax(書き方)が2通りあります。

  1. CRUDのSyntax
  2. ALTER TABLEのSyntax

カラムの変更などがあるときはALTER文で、インデックスの編集のみの場合はそれぞれのsyntaxで書けばいいと思います。

インデックスを作成する

CREATE INDEX idx_1 ON teams (created_at DESC)

or

ALTER TABLE teams ADD INDEX idx_2 (created_at DESC)

インデックスを削除する

DROP INDEX idx_1 ON teams

or

ALTER TABLE teams DROP INDEX idx_2

インデックス"名"を変更する

MySQL5.7からできるようになりました。 これはALTER TABLE Syntaxしか用意されてないようです。

ALTER TABLE teams RENAME INDEX idx_x TO idx_y

インデックスを変更する

キー部分の修正のことです。例えば、

(created_at DESC)としていたのを(user_id ASC, created_at DESC)に変えたいとか。もしくはそれと同時にインデックス名も変えたいとか。

ALTER TABLE構文でいうMODIFYやCHANGEをしたいみたいな。

しかし、インデックスではこれはできないので、一旦DROPしてから新たにCREATEするという手順になります。

DROP INDEX idx_1 ON teams;
CREATE INDEX idx_1_after ON teams (user_id ASC, created_at DESC);

参考

Github Pagesにカスタムドメインを追加する

必要なこと

やるべきことは、三つ。

  1. ドメインの取得
  2. Github Pagesに取得したドメインを追加
  3. DNSレコードの設定

https://help.github.com/articles/using-a-custom-domain-with-github-pages/

を読みつつ進めていく。

ドメインを取得する

王道のお名前.comで取得しました。正味値段はどこもそんなに変わらない。

一般的な.comがだいたい ¥1,200/年 って感じ。

https://www.onamae.com/service/d-price/ に基本料金が掲載されてあります。

ドメイン料金の落とし穴

安い!に突進してはいけません。

ドメインを撮る際に落とし穴となるのが、初年度料金と更新料金の差です。更新料はこちら https://www.onamae.com/service/d-renew/price.html

お名前.comなんかは年中キャンペーンをやっています。中でも目立つのが 30円 とか 99円 とかの安いドメイン

例えば、

  • 汎用性のたかそうな.site
  • 技術サービスやブログは需要ありそうな.tech
  • LINEなども取得しており最近人気の出てきてる.me

これらは、初年度は安いけど更新料は高くなる点に注意してください

Github Pagesでサポートしているドメイン

https://help.github.com/articles/about-supported-custom-domains/

その他の注意点

WHOIS代行サービスの登録

ドメイン取得時に、オプションとしてWHOIS代行に無料登録できますが、この時に登録しておかないと無料にならないようなので登録し忘れのないようにしましょう。

ドメイン更新料の自動更新設定

また、ドメインの更新料金の支払いはデフォルトで自動更新となっています。気づかぬうちに何年も使ってないドメインの更新料を支払い続けていた...とならないように注意してください。

とりあえず1年様子見という場合は自動更新を解除しておきましょう。

逆に、それをした場合の注意点としては、ドメインを1年以上使い続ける場合にはきちんと香辛料を払わないと失効します。ので、忘れないように注意!

Github Pagesに取得したドメインを追加する

https://help.github.com/articles/adding-or-removing-a-custom-domain-for-your-github-pages-site/

Before setting up or modifying your custom domain with your DNS provider, you should add or remove the custom domain on GitHub.

先に、当該レポジトリの「Settings」から取得したドメインを入力して更新しておきます。

この順序の理由としては、

https://help.github.com/articles/quick-start-setting-up-a-custom-domain/

Configuring your custom domain with your DNS provider without adding your custom domain to GitHub could result in someone being able to host a site on one of your subdomains.

DNSの設定に入る前にこの作業をしておかないと、赤の他人がGithubであんたのサブドメイン悪用する可能性あるよ。

ってことですね。

DNSレコードの設定する

お名前.comにはDNSのサービスがあるので、今回このままここを利用する。

AWSなどにNSを登録してネームサーバーを変更しそっちで管理しても良い。

レコードを追加

どんなドメインでサイト運営するかですが、二つあります。

僕はapex domainで設定します。

DNSプロバイダ(今回の場合はお名前.com)の管理画面にて、DNSレコード設定画面を開き、Aレコードを追加します。

GithubサーバーのIPは4つあります。 https://help.github.com/articles/setting-up-an-apex-domain/ に書いてますね。

上のように登録。DNSの反映までは結構時間かかります。24H~72H?

ターミナルでwatchして気長に待ちましょう。

watch dig +noall +answer xxxxx.com

以下のような出力が得られれば反映完了です。

xxxxx.com.       2105    IN  A   185.199.110.153
xxxxx.com.      2105    IN  A   185.199.111.153
xxxxx.com.      2105    IN  A   185.199.108.153
xxxxx.com.      2105    IN  A   185.199.109.153

ちなみにname serverも変わりますので、暇だったらこちらも覗いてみると良いかも。

nslookup -type=ns xxxxx.com

これで、取得したドメインにアクセスしてください。Github Pageサイトが見れるはず。

その他しておくべき設定

SSL対応

今回ドメインを変えたのでhttpsでみるように変更しておく。

「Settings」のEnforce HTTPSにチェックを入れるだけ。

ただ、アラートとhelpページを見ればわかるんですが、まず一度Custom domainを消す必要があります。

そして、再び同じものを入力して更新。すると、以下のようなステータスに変わります。

しばらくしたらhttpsで見れる。

wwwサブドメでもアクセスできるようにする

https://help.github.com/articles/setting-up-an-apex-domain-and-www-subdomain/

おなじみのwwwでもページが見られるようにします。

https://help.github.com/articles/setting-up-a-www-subdomain/

Follow your DNS provider's instructions to create a CNAME record that points to your default pages domain, such as YOUR-GITHUB-USERNAME.github.io to your subdomain.

Aレコードの時と同じように、レジストラ(お名前.com)のDNS設定で、CNAMEレコードを追加します。

Github Pagesサイトをホスティングしてるrepositoryのドメイン YOUR-GITHUB-USERNAME.github.iowww.xxxxx.comに向けます。以下のような感じ。

これまた反映までに、時間かかるので気長に待ちます。

watch dig www.xxxxx.com +nostats +nocomments +nocmd

以下のような出力が得られれば反映完了です。

; <xxxxx.com<>> DiG 9.10.6 <<>> www.xxxxx.com +nostats +nocomments +nocmd
;; global options: +cmd
;www.xxxxx.com.         IN  A
www.xxxxx.com.      3566    IN  CNAME   YOUR-GITHUB-USERNAME.github.io.
xxxxx.github.io.    277 IN  CNAME   sni.github.map.fastly.net.
sni.github.map.fastly.net. 2795 IN  A   185.199.111.153
sni.github.map.fastly.net. 2795 IN  A   185.199.108.153
sni.github.map.fastly.net. 2795 IN  A   185.199.109.153
sni.github.map.fastly.net. 2795 IN  A   185.199.110.153

www.xxxxx.comにアクセスすると、ちゃんとhttps://xxxxx.comにリダイレクトされています。

ちなみにこのリダイレクトはどうやってるかというとGithubのサーバが自動でよしなにやってくれてます

おわりに

これで、ひとまずブログ作成が全て完了しました。

それよりサイトの修正やソースコード周りのスクリプト作ったりするの優先しそうな予感。

そういう方が楽しいんだなー。ブログ書くって大変だー。

エンジニアブログ作るならGithub Pages x Hugoがおすすめ

そろそろ自分のブログを作るか。全然技術ブログなど書いてないけど。ということで、作ってみました。

TL;DR

Hugoを使って、Githubにブログをホスティングし公開しました。

やってるうちに色々メリットなど気づいたので、エンジニアさんがブログを開設するならばこの技術スタックでやることをお勧めします。(カレーブログとかテーマはなんでもいい)

キーワードはGithub PagesとHugoのみ。

公式ページがわかりやすくシンプルです。本記事はセットアップ方法について詳しくは説明しません。

Github Pagesとは

https://pages.github.com/

Websites for you and your projects. Hosted directly from your GitHub repository. Just edit, push, and your changes are live.

Githubのrepositoryに静的ファイルをおくだけでweb pageを公開できる機能。

長所

ページの更新が簡単

git push origin master するだけ。シンプルな方法で運用できる。

記事の投稿が簡単

.mdファイルを指定のディレクトリ配下におくだけでも、よしなにhtmlに変換して公開してくれます。

ブログ書いてるだけでGithub contribute

今のご時世、エンジニア採用の履歴書としてGithubがみられるから意外とありがたい福利。

無料、SSL対応、カスタムドメイン対応

これだけ対応されてれば問題ないだろう。

短所

画像を入れる場合は面倒なことも..

ローカルでmdファイル書いて、アップロードとする場合に画像を挿入したいこと必ずあると思います。

そんな時に、画像ファイルがローカルにあるものを使っていると、いざ公開サイトを確認した時に表示されない問題が発生するはずです。

なので、一度リモートのどこかに画像をアップロードして、記事ファイル内ではそのURLを参照させるする必要があります。

僕はこんな面倒なことしたくないので、Paper – Dropboxで記事を書き、エクスポートしたものを、配信ディレクトリに入れるという運用にする予定です。(詳しくは後述)

この場合、画像はDropboxPaperがクラウドホスティングしたもののURLを参照することになります。

利用制約がある

無料だから仕方ない。とはいえ、これから始めるブログビギナーにとってはあってないようなもの。 公式ページにてUsage Limitを説明されています。(2018/08/25時点)

https://help.github.com/articles/what-is-github-pages/#usage-limits

GitHub Pages source repositories have a recommended limit of 1GB .

ソースコードは1GBまでを推奨。 推奨と言っているのは、以下の項のためですね。

Published GitHub Pages sites may be no larger than 1 GB.

公開される静的ファイルのホスティングは1GBまで。

GitHub Pages sites have a soft bandwidth limit of 100GB per month.

ネットワーク通信料が100GBまで。 この制約がもっとも気をつけないとなところ。

ブログによってまちまちですが、1ページあたりのリクエスト100KBだとすると、最大PVは1,048,576になります。 まるめて、月におよそ100万PVまで。デイリーだと、33,333PV。 デイリーで一人当たり平均3PVとすると、11,111人が見ることのできるサイトとなります。

まあ1万人も読者が増えたら、その時にはちゃんと有料のレンタルサーバー借りて移行しましょう😎 ブログではなくECサイトなど商用利用する際はちょっと厳しくなってくるんじゃないかな。

GitHub Pages sites have a soft limit of 10 builds per hour.

1時間に10プッシュまで。 まあ大丈夫でしょう。 基本的にhugo serverでローカルチェックすると思うし。

Hugoとは

https://gohugo.io/

The world’s fastest framework for building websites Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again.

静的サイトを自動生成してくれるツールです。オープンソース製でGoで書かれています。

長所

高速ビルド

公式もこれをセールスポイントにしている。 数秒もかかりません。さすがgoといったところ。

マークダウンファイルに対応

hugoもまた.mdファイルの表示に対応しています。 Github Pagesに公開する場合は、ローカルビルドしてhtmlファイルを生成しそれらをデプロイする感じになります。

テーマが豊富

https://themes.gohugo.io/ hugoは色々な方々が作ってくれたテーマがあって、みていてたのしい。 一応Github Pagesにも用意されたテーマがあるのですが、少ないしょぼい。

短所

さしあたり特に無いなー。

インタフェースが技術者向け

強いて言うなら。コマンドで投稿を作成したりデプロイしたりするので管理画面やエディタ画面もなく、素人だと扱うのが少々難しい。 しかし今回の記事はエンジニア向けと言っちゃってるのでこれはデメリットにはならない!

いやはや、とてもいいツールだ。

このブログができるまでの流れ

HugoでGithub Pagesに公開するには以下の記事見るとよい。

Host on GitHub | Hugo

簡単に注意点など説明。

Githubのレポジトリは二つ作成

もちろん一つのrepositoryにまとめちゃってもおk。

好きなテーマを選ぶ

https://themes.gohugo.io/hugo-theme-cactus-plus/

テーマは git submodule add で追加して、configファイルで themes/{テーマ}を指定する。

テーマ触ってて自分で手入れたい部分が色々出てきたので、最初にforkしてから追加したほうが良い。 https://github.com/uqichi/hugo-theme-cactus-plus

ちなみに、このテーマはなぜかカテゴリが表示されない仕様(バグ?)があるので今度直したい。

ブログの運用方法

Dropbox Paperを使って記事を管理

僕は普段からDropbox Paperを活用していて、身の回りのメモや書評、勉強ノートなどにしている。

今回のブログの運用にも、相性が良さそう。

手順

  1. 記事を書く(Paperは現在ブラウザかスマホアプリのみ対応)
  2. mdファイルとしてエクスポート
  3. front-matterを追加(現状、手動)
  4. 配信ディレクトリに納める
  5. デプロイする

Paperは現在ブラウザかスマホアプリのみ対応

余談ですが、TwitterDropboxのサポートに投げてみた

数分で返信きて親切。てか、Twitter Cardの埋め込みできるんだー!

<blockquote class="twitter-tweet" data-lang="ja"><p lang="en" dir="ltr"><a href="https://twitter.com/Dropbox?ref_src=twsrc%5Etfw">@Dropbox</a> I hope the paper to sync my contents with my PC...</p>&mdash; ゆきち (@uqichi) <a href="https://twitter.com/uqichi/status/1032922233126047745?ref_src=twsrc%5Etfw">2018年8月24日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Paperの使い方

Dropbox Paperでは以下のようなディレクトリ構成にしてます。 Draft/Published/

  • Draft/ 下書きをここで書く。
  • Draft/Published/ デプロイ(記事公開)したらここにファイルを移動する。

その他のMarkdownエディタ選択肢

BearやMacDownも迷ったが、こちらはデスクトップアプリ。

複数端末で同期取れるメリットや本記事上の方で言及した画像URL問題を優先し、Paperに決定。

一応、BearやMacDownでも、エクスポートした出力ファイルをデプロイして、表示確認してみたところでは問題なかった。

運用をもっと簡単にする

Makefileを作った。https://github.com/uqichi/blog/blob/master/Makefile

例えば、make postで記事作成できる。 ファイル名を入力しなかったら、現在日時が適当に入る。

Tools for 'blog' project.

Usage:

        make command

The commands are:

        list    List all posts
        post    Add new post
        deploy    Deploy posts
        server    Run local server
        help    Show help
  1. make post で記事を作成
  2. make deployで記事をアップ

以上。

外から持ってきたmdファイルにfrontmatter入れるコマンドも欲しい。

おわりに

エンジニアにとってはコマンドべーすだったりGithubベースだったりと、管理しやすく使ってて楽しいと思います。

これを機に、ブログ書くのを楽しみながら続けられるといいな。

HomebrewでGolangのバージョンを最新にアップグレード

brew upgradeしたときの最新が古い

brew upgrade go

しても、現在の1.9.2が最新なんだと。ほう

Fomulaをアップデート

formulae.brew.sh

上のgoのformulaページ見るとStableが1.10になっている。ちゃんとなってるやん。

ということは自分のローカルのFormulaが古いようなのでpullする。

cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula
git pull origin master

ちなみにこのpullするのをコマンドでするにはどうすればいいんだろうか。

わからなかったけど、今回はこれでいい。

pullできたらもう一度、

brew upgrade go

==> Upgrading 1 outdated package, with result: go 1.9.2 -> 1.10.2 ==> Upgrading go ==> Downloading https://homebrew.bintray.com/bottles/go-1.10.2.high_sierra.bottle.tar.gz

################################################################## 100.0%

==> Pouring go-1.10.2.high_sierra.bottle.tar.gz [==> Caveats A valid GOPATH is required to use the go get command. If $GOPATH is not specified, $HOME/go will be used by default: https://golang.org/doc/code.html#GOPATH

You may wish to add the GOROOT-based install location to your PATH: export PATH=$PATH:/usr/local/opt/go/libexec/bin ==> Summary 🍺 /usr/local/Cellar/go/1.10.2: 8,161 files, 336.7MB

確認してみよう。

go version

go version go1.10.2 darwin/amd64

ちゃんと上がった。

ちなみに、Mac OS自体のバージョンもちゃんと最新にしておきましょう。じゃないと多分問題起きるよ。

Vimを使ってapiaryのapibファイルを快適に編集できるようにする

今回はじめてapiaryを使ってAPIドキュメントの作成をやっています。 API Blueprint 記法を学びながらわりと快適に作業していたのですが、問題発生。

発生した問題

  • 途中からapiraryのオンラインエディタのvalidationが遅すぎて死んだ
  • apiaryの一時的な接続問題か、ブラウザの問題かはわからないが
  • しかし、編集するたびにvalidateされるのはうっとうしい
  • apiaryのvalidationをdisableする手段が現状ない
  • ので、ローカルで編集してpushする方法に切り替えることにした

apiblueprint.vim導入

API Blueprintのシンタックスハイライトとlintチェックをやってくださるプラグイン

依存関係のsyntasticインスコ

Vim周りのいろんなシンタックスチェックをがんばってくれるプラグイン

call dein#add('vim-syntastic/syntastic')

set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*

let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0

依存関係のdrafterインスコ

API Blueprintのvalidationを手伝ってくれるコマンドラインツール。

$ brew install --HEAD \
  https://raw.github.com/apiaryio/drafter/master/tools/homebrew/drafter.rb

おまちかねのapiblueprint.vimインスコ

call dein#add('kylef/apiblueprint.vim')

便利なTagbarインスコ

クラスとか関数のoutlineを表示してくれるプラグイン

依存関係のExuberant ctags 5.5インスコ

$ brew install ctags-exuberant
call dein#add('majutsushi/tagbar')

nmap <F8> :TagbarToggle<CR>
nmap <F9> :TagbarShowTag<CR>

apiblueprint.vimjsonの構文チェックしてくれないのでvim-jsonインスコ

jsonシンタックスハイライト、構文チェックしてくれる。jsonの可読性をあげるconcealing機能が超いい。

call dein#add('elzr/vim-json')

以上。

ごにょごにょ作業に時間食ったが、快適に過ごせそう〜

gvm使ってCircleCIでGoのバージョン固定

目的

  • バージョンが勝手に変わると、アプリケーション動作の保障ができないので、任意のバージョンのgoをインスコして使う

対象環境

  • ビルド環境 (CircleCI)
  • 実行環境 (AWS EC2)

今回ビルド環境と実行環境を分ける予定であるため、その両方への作業が必要ですが、今回は、ひとつめの、CircleCIでの適用作業をします。

利用するライブラリ

gvm: https://github.com/moovweb/gvm

gvm使ってみる

gvmはgolangのバージョン管理ライブラリです。 あまり気に入ってないけど、他にいいやつあったらおしえてほしいです。

自分のローカルで先にやっても良かったのですが、まだVM環境になってないので、CicleCIのコンテナでためしました。

CircleCIはデバッグ用途で、30min限定でコンテナにsshできます。 ぶっ壊れてもいいので、今回の作業以外でも色々遊ぶといいです。

現在使っているシェルを確認

$ echo $SHELL

/bin/bash

現在使っているgoのバージョンを確認

$ go version

go version go1.6.2 linux/amd64

gvmをインストール

$ bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)

Cloning from https://github.com/moovweb/gvm.git to /home/ubuntu/.gvm Created profile for existing install of Go at "/usr/local/go" Installed GVM v1.0.22

Please restart your terminal session or to get started right away run source /home/ubuntu/.gvm/scripts/gvm

上記出力の通り、以下を読み込んであげましょう。

source /home/ubuntu/.gvm/scripts/gvm

インストールgo一覧の確認

$ gvm list

gvm gos (installed)

system

現状システムインストールされたgoだけが表示されています。CircleCIで使われてるイメージはデフォルトでgo入ってるので。すなわちsystemですね。

gvmで任意のバージョンのgoインストール

gvm install go1.4 -B

Installing go1.4 from binary source

ちゃんと入ったようなので、先ほどインストールしたバージョンを使います

gvm use go1.4 

Now using version go1.4

--defaultオプションをつけることもできます。たぶん基本的につけてつかうことになるでしょう。

goのバージョン確認

go version

go version go1.4 linux/amd64

ちゃんと1.4に変わってます。

元に戻す

gvm use system

ちなみにgvm経由でインストール可能なgoのバージョンの一覧は以下で得ることができます

gvm listall

バルスコマンド。インストールしたバイナリなどに加え、gvm自身も消え去ります。 けっきょく、${HOME}/.gvm (= ${GVM_ROOT}) をrm -rfすればおなじことです。

gvm implode

さて。もともと、やりたかったことは、CircleCIのビルド時のバージョン固定なので、これをcircle.ymlに落とし込みます。

CircleCIで任意のバージョンのgo使ってビルドする

https://circleci.com/docs/language-go/#environment

Since the install is in the default location, GOROOT is not set. If you install your own version of Go, make sure to set the location in GOROOT

任意のバージョン使いたければちゃんと$GOROOTセットしてねって書いてるけど、これはgvmが勝手にやってくれてました。

echo $GOROOT

これはありがたいのでしたが、しかし、とてもありがた迷惑だったのが、$GOPATHの変更まで勝手にやっちゃってくれます。

echo $GOPATH

e.g. /Users/a13533/.gvm/pkgsets/system/global

ここで結構はまります。 このGOPATHで頑張ってみてもいいのですが、circleciでは$HOME/.go_projectをGOPATHと扱うよう推奨してあります。推奨ではなくデフォルトですが、これをぼくは推奨として受け取ります。公式ドキュメントに沿っておいた方が後続のコーダーが理解しやすいとおもうので良。

https://circleci.com/docs/language-go/#environment

a symlink is placed to your project’s directory at /home/ubuntu/.go_project/src/github.com/\<USER>/\<REPO_NAME>

なので、gvmが決めるGOPATHにこちらが指定したいGOPATHを追加する方法で解決しました。

そもそもどこで環境変数を上書きしているのかを探します。結論は、当該シェルのrcファイルを改変してくれていたのでした。 bashインスコしたなら.bashrczshインスコしたなら.zshrcの末尾に以下が追記されていると思います。

[[-s "$HOME/.gvm/scripts/gvm"]] && source "$HOME/.gvm/scripts/gvm"

このなかを追って読むと、デフォルトのpkgsetに書かれてある環境変数がロードされていることがわかるとおもいます。 そこで、デフォルトのpkgsetの設定ファイル末尾に追記してGOPATHをoverrideします。

echo "export GOPATH; GOPATH=\"${GVM_ROOT}/pkgsets/${GVM_GOS}/global:${GOPATH}:${GOPATH_ORG}\"" >> ${GVM_ROOT}/environments/default

これで、こちらが設定したGOPATH_ORGがGOPATHに追加されるようになります。 以下、今回やった範囲の設定です。

machine:
  environment:
    GOROOT: ""
    PATH: "/usr/local/go/bin:/usr/local/go_workspace/bin:~/.go_workspace/bin:${PATH}"
    GOPATH_ORG: "${HOME}/.go_workspace:/usr/local/go_workspace:${HOME}/.go_project"
    GVM_GOS: "go1.6.2"
  post:
    - bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) && source ${HOME}/.gvm/scripts/gvm
    - gvm install ${GVM_GOS} -B
    - gvm use ${GVM_GOS} --default 
    # `gvm` arbitrarily changes GOPATH value :( so forcibly override it with its original value.
    - echo "export GOPATH; GOPATH=\"${GVM_ROOT}/pkgsets/${GVM_GOS}/global:${GOPATH}:${GOPATH_ORG}\"" >> ${GVM_ROOT}/environments/default

dependencies:
  pre:
    - go get github.com/golang/lint/golint
    - sudo add-apt-repository ppa:masterminds/glide -y && sudo apt-get update && sudo apt-get install glide -y
    - mkdir -p ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}
    - ln -sfnv ${HOME}/${CIRCLE_PROJECT_REPONAME} ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}
  override:
    - glide install
    - cd ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} && go build -v -race -o build/output

test:
  override:
      - cd ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} && go test -v -race -cover `glide nv`

今回なかなか強引に対応しましたがほかにいいやりかたないかな。 ghq使っている自分としてはGOPATH問題がなかなかめんどうなためやりづらい。 普通に単体で使えばちゃんと便利なんだけど。

参考