ニクニクドットミー

カッコいいおっさんを目指すエンジニアの厳かなブログ

kamakura.go#2に参加してきた!

28日に鎌倉で行われたGoの勉強会 kamakura.go #2に参加してきました。

第一回目の開催には所用があった為、参加できず、二回目開催してほしいなーとtwitterでつぶやいていたら、二回目開催されるとのことだったので、即申し込みしました。

ちなみにiichiさんで開催されているのですが、iichiさんのslackのGoのチャンネルに参加させて頂いてます。

勉強会としてはもくもく会で、最後に成果発表がありました。

成果発表時にやったこととkamakura.goの感想をslideにしたので、張っておきます。

会場はiichiさんで、場所は鎌倉にあります。小町通にあるので、休日は人がすごいです。

僕はよく鎌倉には行くので、人ごみは想定内でしたが、一緒に行った同僚の方は初鎌倉で面食らってました(笑)

とても天気がよくて鎌倉を散策するにはもってこいな日でした。

勉強会終了後の懇親会で鎌倉の焼き鳥屋に行ったのですが、そこの大将がすごく人に絡む人で、同僚が気に入られてしまい、ずっとその話で盛り上がりました。(おつかれさまでした^^;)

夜の鎌倉は初めてで、だいぶ人も減り、小町通もシャッターがすぐおりてました。昼間とはガラっと雰囲気変わるので驚きでした。

kamakura.go #3あれば参加します!

鎌倉で気になるお店も出来ました。尾崎、ブルールーム。これは行ってみたい!

やっぱ鎌倉いいとこだ。

【資料公開】社内LTで「プログラミング学習の3つのポイント」を話しました。

社内でLTを月に一回ぐらいのペースで開催していて、久々に登壇しました。

久々に発表用資料を作りましたが、頭の中を整理するのにちょうどいいですね。あとLTで意識しているのは「緊張感を楽しむ」ということ。 これは中学時代の部活(実はバレー部のキャプテン)で先生に教えてもらった言葉ですが、いまも意識しています。

大した話ではないですが、資料公開しておきます。 (今年は発表に力入れているので)

1.SIMPLE シンプルに継続する。

継続は力なりという言葉の通り、続けるとすこしずつ成長はする。 運動もそうで、ジムに週6日くらい通っている奥さんの方が、だんぜん動ける。。。頑張ろう。

2.EASY&SMALL 小さく簡単に作る。

少しでも動くとやっぱ楽しい。いきなりでかいものは作れないので、少しずつ進めていく。 そして、簡単なものでいいから作る。

3.READ コードを読む。

コード読むのはメリットだらけ。次第に読めてくると楽しいし、アイディアも湧く。 尊敬・憧れのエンジニアのコードだと尚更。

憧れのエンジニアはメルカリの久保さん。これはdotsSummit2015の発表を見て、カッコイイと思いました。 発表も冷静沈着な印象で、僕とは違う感じ(笑) slackboardの話で、イラっとしてGoでささっと書いたという話が印象的で、インフラでしっかりコードが書ける久保さんのようになりたいと思いました。 なので、slackboardのコードはgithubで読んでます。

ということで、pocket-for-Golangを頑張るぞ。

Goでtoml.DecodeFileしたときにハマったことメモ

182b65b9 539c 4d24 9b1f ee4a1a8756f1 TOMLという設定ファイルの為のミニ言語があり、それをGoで試してみた所、うまく読み込まれずにハマってしまったので、メモ。

config_test.go


package main

import (
    "github.com/BurntSushi/toml"
    "testing"
    "fmt"
)

type ConfToml struct {
    Keys SectionKeys `toml:"keys"`
}

type SectionKeys struct {
    Consumer_key string `toml:"consumer_key"`
    access_token string `toml:"access_token"`
}

// config の read
func readSettingsConfig(path string, config *ConfToml) {
    _, err := toml.DecodeFile(path, config)
    if err != nil {
        panic(err)
    }
}

func Test_readSettingsConfg(t *testing.T) {
    var conftoml ConfToml

    _, err := toml.DecodeFile("config.toml", &conftoml) 
    if err != nil {
        panic(err)
    }
    
    fmt.Println(conftoml.Keys)

    // Printlnする為に、あえてerrorにしている。
    if conftoml.Keys.Consumer_key != "aab" {
        t.Error("Not Match")
    }
    if conftoml.Keys.Consumer_key != "bbb" {
        t.Error("Not Match")
    }

}

config.toml


[keys]
consumer_key = "aaa"
access_token = "bbb"

実行結果


{aaa }
--- FAIL: Test_readSettingsConfg (0.00s)
        config_test.go:38: Not Match
        config_test.go:41: Not Match
FAIL
FAIL    command-line-arguments  0.005s

この太字にしたところがポイントで、これはtomlファイルを読み込んだ構造体をPrintlnしているのですが、aaaという値だけ格納されている状態です。 本来ならば、{aaa bbb}となるはずです。


type SectionKeys struct {
    Consumer_key string `toml:"consumer_key"`
    access_token string `toml:"access_token"`
}

このSectionKesyという構造体のフィールド名が小文字と大文字で違っているのが原因です。 どうやらDecodeFileはフィールド名が小文字だと値が設定されないようです。 ※Access_tokenと大文字で設定すると正しく値は設定されました。

恐らくですが、github.com/BurntSushi/tomlパッケージ外の構造体へのアクセス権限がない(エクスポートできない)のが原因かと思っています。 値が設定されず、やだな〜やだな〜こわいなーこわいな〜とハマっておりました。

謝った認識かもしれませんので、ご指導ご鞭撻のほどよろしくお願いします。

参考サイト

【個人メモ】設定ファイルフォーマットにはTOMLがいいのかも Go言語での構造体実装パターン

【AWS】ElastiCacheRedisをMulti-AZ構成にするとAOFの設定ができないっぽい?

Redis 今日はElastiCacheRedisを触ることが多かったので、本日二回目のElastiCacheのメモ。

ElastiCacheRedisがMulti-AZ対応したので、Multi-AZ構成でクラスタを作成していたのですが、 なんとParameterGroupでappendonly yesにしたgroupを設定するとエラーとなってしまうのです!ドン!(多分だけど。。。)


Cannot have redis persistence(appendonly) and Multi-AZ both on

ちなみにこんなエラー。

どうやら同じエラーになった方がいたようで、こちらの記事を参考にさせて頂きました。 [AWS]マルチAZなElastiCache Redisの永続性についてメモ

実は今年の一月ぐらいに他のインスタンスで同じMulti-AZ構成にしたクラスタがあったのですが、そちらはなんとAOFの設定が出来ているのです!(さらに深まる謎)

結局のところ

埒が明かないので、AOFの設定無しのParameterGroupを設定したところ、クラスタの作成が問題なく出来ました。 参考にさせて頂いた記事にありましたが、Multi-AZではインスタンスが死んだら、そのインスタンスは破棄するので、AOFを残す意味がないから?という仮説を立てられておりました。 自分もその意見には納得です。

だれかご存知の方いますかーーーーー?

追記 2015/02/18 この件についてAWSに問い合わせしたところ早速回答がきました! Multi-AZ構成でAOF設定は出来ない仕様 さらに英語のみですが、ドキュメントに記載されているとのこと。  - Best Practices for Implementing Amazon ElastiCache / Mitigating Out-of-Disk-Space Issues When Using AOF  http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/BestPractices.html#BestPractices.FaultTolerance

Enabling Redis Multi-AZ as a Better Approach to Fault Tolerance  If you are enabling AOF to protect against data loss, consider using a replication group with Multi-AZ enabled instead of AOF. In any Redis replication group, if a replica fails, it is automatically replaced and synchronized with the primary cluster. If Multi-AZ is enabled on a Redis replication group and the primary fails, it fails over to a read replica. This functionality is much faster than rebuilding the primary from an AOF file. For maximum fault tolerance, even against a hardware fault in an underlying physical server, launch your primary and replica clusters in different Availability Zones. Because there is no need for AOF in this scenario, you do not risk running out of disk space. AOF ですがローカルディスクに書き込みを行う都合上、物理的な障害には対応できません。また、ディスクに書き込みを行う都合上、更新が非常に多いとローカルディスクを使い切り書き込みができなくなる事例も報告されております。この影響を回避するため、ベストプラクティスとしては AOF ではなく Multi-AZ 構成によって耐障害性を考慮頂くことをお勧めしております。 Multi-AZ 構成により十分な耐障害性が確保できることから、ElastiCache の仕様としては AOF との併用ができないような仕様となっております。

やはりMulti-AZになったことでAOFファイルつくる必要がないという判断のようです。

早急に連絡くれたサポートに感謝!

【AWS】S3のrdbファイルからElastiCache Redisを作成する時の些細な注意点

Redis S3にアップしたrdbファイルからElastiCacheを作成することってよくありますよね。ありますよね。

僕は今回初めて対応しましたが、たった些細な設定が漏れていた為、大ハマりしました。お世話になりました。

ドキュメント

バックアップと復元の管理 (Redis)

RDB ファイルへの読み込みアクセスを ElastiCache に許可 次のステップでは、Amazon S3 にコピーしておいたスナップショットファイルへの読み込みアクセスを ElastiCache に許可します。 Amazon S3 にコピーしておいたスナップショットへの読み込みアクセスを ElastiCache に許可するには AWS Management Console にサインインして Amazon S3 コンソールを開きます(https://console.aws.amazon.com/s3)。 [All Buckets] をクリックし、RDB ファイルを含む Amazon S3 バケットの名前をクリックします。 RDB ファイルを含むフォルダの名前をクリックします。 RDB ファイルの名前をクリックし、[Actions] ドロップダウンメニューをクリックして [Properties] を選択します。 [Permissions] をクリックして [Add more permissions] をクリックします。 [Grantee] ボックスに、E メールアドレスaws-scs-s3-readonly@amazon.comを入力します。 [Open/Download] をクリックし、[Save] をクリックします。 Note aws-scs-s3-readonly@amazon.com アカウントは、Amazon S3 から Redis スナップショットデータをアップロードするお客様だけが使用します。

aws-scs-s3-readonly@amazon.comを許可

はい。これを許可してなかったので、No permission to access S3ってエラーになってましたとさ。

ドキュメント大事。

(ってかこれハマる人結構いるんじゃね)

GOTRACEBACKのメモ

182b65b9 539c 4d24 9b1f ee4a1a8756f1

GOTRACEBACKという環境変数があり、それを設定することによって、go runしたときのプログラムのトレースバックが変わるようです。

The GOTRACEBACK variable controls the amount of output generated when a Go program fails due to an unrecovered panic or an unexpected runtime condition. By default, a failure prints a stack trace for every extant goroutine, eliding functions internal to the run-time system, and then exits with exit code 2. If GOTRACEBACK=0, the per-goroutine stack traces are omitted entirely. If GOTRACEBACK=1, the default behavior is used. If GOTRACEBACK=2, the per-goroutine stack traces include run-time functions. If GOTRACEBACK=crash, the per-goroutine stack traces include run-time functions, and if possible the program crashes in an operating-specific manner instead of exiting. For example, on Unix systems, the program raises SIGABRT to trigger a core dump.

Google先生で翻訳したところ、

GOTRACEBACK = 0は、単位のゴルーチンスタックトレースが完全に省略している場合は終了コード2で終了します。

GOTRACEBACK = 1の場合、デフォルトの動作が使用されます。

GOTRACEBACK = 2場合は、単位のゴルーチンスタックトレースは、実行時の機能が含まれます。

以下のサンプルコードで試してみました。


package main


import (
        "log"
        "runtime"
)

func main(){
        log.Println(runtime.NumGoroutine()) //動いているゴルーチンの数
        select{}
}

GOTRACEBACK=0


[root@localhost vagrant]# GOTRACEBACK=0 go run sample.go
2015/02/12 13:39:00 4
fatal error: all goroutines are asleep - deadlock!
exit status 2

終了ステータスコードとエラーメッセージが出力されます。

GOTRACEBACK=1


[root@localhost vagrant]# GOTRACEBACK=1 go run sample.go
2015/02/12 13:40:02 4
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:
main.main()
    /vagrant/sample.go:11 +0xd5
exit status 2

終了ステータスコードとどの行でエラーになったか出力されます。

GOTRACEBACK=2


[root@localhost vagrant]# GOTRACEBACK=2 go run sample.go
2015/02/12 13:40:17 4
fatal error: all goroutines are asleep - deadlock!

runtime stack:
runtime.throw(0x54f7e3)
    /usr/local/go/src/runtime/panic.go:491 +0xad fp=0x7fff416d3b58 sp=0x7fff416d3b28
checkdead()
    /usr/local/go/src/runtime/proc.c:2854 +0x1f8 fp=0x7fff416d3ba8 sp=0x7fff416d3b58
mput(0x551e40)
    /usr/local/go/src/runtime/proc.c:3175 +0x47 fp=0x7fff416d3bb0 sp=0x7fff416d3ba8
stopm()
    /usr/local/go/src/runtime/proc.c:1176 +0xea fp=0x7fff416d3bd0 sp=0x7fff416d3bb0
findrunnable(0xc208012000)
    /usr/local/go/src/runtime/proc.c:1487 +0x562 fp=0x7fff416d3c08 sp=0x7fff416d3bd0
schedule()
    /usr/local/go/src/runtime/proc.c:1575 +0x151 fp=0x7fff416d3c38 sp=0x7fff416d3c08
runtime.park_m(0xc2080006c0)
    /usr/local/go/src/runtime/proc.c:1654 +0x113 fp=0x7fff416d3c60 sp=0x7fff416d3c38
runtime.mcall(0x42d204)
    /usr/local/go/src/runtime/asm_amd64.s:186 +0x5a fp=0x7fff416d3c70 sp=0x7fff416d3c60

goroutine 1 [select (no cases)]:
runtime.gopark(0x0, 0x0, 0x4ef830, 0x11)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc20802df08 sp=0xc20802ded8
runtime.block()
    /usr/local/go/src/runtime/select.go:176 +0x46 fp=0xc20802df30 sp=0xc20802df08
main.main()
    /vagrant/sample.go:11 +0xd5 fp=0xc20802df98 sp=0xc20802df30
runtime.main()
    /usr/local/go/src/runtime/proc.go:63 +0xf3 fp=0xc20802dfe0 sp=0xc20802df98
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20802dfe8 sp=0xc20802dfe0

goroutine 2 [force gc (idle)]:
runtime.gopark(0x42edb0, 0x5516e0, 0x4e49d0, 0xf)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc20801a798 sp=0xc20801a768
runtime.goparkunlock(0x5516e0, 0x4e49d0, 0xf)
    /usr/local/go/src/runtime/proc.go:136 +0x48 fp=0xc20801a7c0 sp=0xc20801a798
runtime.forcegchelper()
    /usr/local/go/src/runtime/proc.go:99 +0xce fp=0xc20801a7e0 sp=0xc20801a7c0
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20801a7e8 sp=0xc20801a7e0
created by runtime.init·4
    /usr/local/go/src/runtime/proc.go:87 +0x25

goroutine 3 [GC sweep wait]:
runtime.gopark(0x42edb0, 0x558978, 0x4e21f0, 0xd)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc20801df98 sp=0xc20801df68
runtime.goparkunlock(0x558978, 0x4e21f0, 0xd)
    /usr/local/go/src/runtime/proc.go:136 +0x48 fp=0xc20801dfc0 sp=0xc20801df98
runtime.bgsweep()
    /usr/local/go/src/runtime/mgc0.go:98 +0xbc fp=0xc20801dfe0 sp=0xc20801dfc0
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc20801dfe8 sp=0xc20801dfe0
created by gc
    /usr/local/go/src/runtime/mgc0.c:1383

goroutine 4 [finalizer wait]:
runtime.gopark(0x42edb0, 0x558970, 0x4e4550, 0xe)
    /usr/local/go/src/runtime/proc.go:130 +0x105 fp=0xc208019730 sp=0xc208019700
runtime.goparkunlock(0x558970, 0x4e4550, 0xe)
    /usr/local/go/src/runtime/proc.go:136 +0x48 fp=0xc208019758 sp=0xc208019730
runtime.runfinq()
    /usr/local/go/src/runtime/malloc.go:727 +0xba fp=0xc2080197e0 sp=0xc208019758
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc2080197e8 sp=0xc2080197e0
created by runtime.createfing
    /usr/local/go/src/runtime/malloc.go:707 +0x5e
exit status 2

長いです。終了ステータスコードと他に動いてるゴルーチンが出力されます。

こう思った

GOTRACEBACK=2で他のゴルーチンがわかるのは便利かも。 なぜall goroutines are asleep - deadlock!となるのかは謎。。。

GoをCentOS-6.5に入れてみた!

182b65b9 539c 4d24 9b1f ee4a1a8756f1 Goで生きていくと決めて数時間経ちました。皆さんいかがお過ごしでしょうか。

つい先日行われたdotssummit 2015でたくさんのスタートアップ企業がプレゼンを行いました。

多くのスタートアップが、Goへ舵を切り始めているのが印象的でした。

  1. パフォーマンスがいい
  2. 単一バイナリなので管理が楽
  3. 並立処理が簡単

gunosyではappサーバはGoで動いていて、ピーク時のレスポンスタイムは30msだそうです。はやい。

メルカリは部分的にGoを使っていて、発表者の久保さん@cubicdaiyaのGo使いぶりが凄まじく、「めっちゃかっこいいな」と思い、Goで生きて行こうと決めたのでした。なので、数時間というか2日くらい経ってます。

Go自体はMacに入れていたのですが、昨日Vagrant Cloudを試していて作ったCentOS-6.5の環境があるので、そちらに改めて入れてみました。

Goのダウンロード


cd /usr/local/src

wget --no-check-certificate https://storage.googleapis.com/golang/go1.4.1.linux-amd64.tar.gz

tar zxvf go1.4.1.linux-amd64.tar.gz

でGoがダウンロード出来ましたので、PATHが通っている所に設置します。 シンボリックリンクを張ってみました。


ln -s /usr/local/src/go/bin/go /usr/bin/go

GOPATH,GOROOTを設定

.bash_profileに以下を書きます。


export GOROOT=/usr/local/src/go
export GOPATH=$HOME/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT
export PATH=$PATH:$GOPATH
export PATH=$PATH:$GOPATH/bin

※追記 GOPATHの指定が誤っていました。binまで付けてしまうとgo getした際に/bin/bin/とbin以下にさらにbinディレクトリを作ってしまう為、PATHが通らなくなってしまいます。 GOROOTはGoを設置してるディレクトリを指定します。 GOPATHはgo getしたときに格納されるディレクトリになります。

ちなみにGOPATHを設定しないまま、go getすると怒られます。

package github.com/cubicdaiya/nginx-build: cannot download, $GOPATH not set. For more details see: go help gopath

GOPATHについて詳しく説明してくれている記事がありましたので、引用。

GOPATH には2つ役割があります。 ビルド時のインポートパスとして、GOPATH に指定したすべてのディレクトリを参照する(コロン区切り) go get コマンドで外部パッケージを読み込んだ時、 GOPATH の先頭のディレクトリにダウンロードする そして、公式ドキュメントにもありますが、Goで自分のプロジェクトを書く時にも、このGOPATHの下で書くのが良いとされています。 例えば、自分が githubソースコードを管理していて、ユーザー名が foo、リポジトリが bar だったら $GOPATH/src/github.com/foo/bar というディレクトリを作ってそこで作業します。

ここまでくればGoは動くはずです。


$ go version
go version go1.4.1 linux/amd64

go getしてみる

せっかくなので、go getしてモジュールを手に入れましょう。 dotssummitで衝撃だった@cubicdaiyaさんのnginx-buildを入れてみます。 nginx-buildでnginxをビルドしよう


go get -u github.com/cubicdaiya/nginx-build

mkdir -p ~/opt/nginx
nginx-build -d ~/opt/nginx
cd ~/opt/nginx/1.7.9/nginx-1.7.9
sudo make install
objs/nginx -v

無事インストールされました! ※途中make installする必要がありました。

参考

これからGoを始める人のためのTips集

こう思った

Goの時代が来てます。Goはバイナリを配るだけですぐ使えるので、インフラエンジニアにもってこいな言語です。 ポインタの概念があるので、そこはしっかり覚えないといけませんが、その分効率的なプログラムが書けると思います。 Goで生きて行けるようにがんばるゾ!