Ymsrの壮大な送別会

ブログ書くまでがなんとやらで。遅くなりますた。

さてYmsrの送別会に逝ってきた。いろんな人が色々書いているので詳細は書かないけど、花火もあがり、MBAで包丁もしたし、ベルギービールではれてMBAも破壊して、これでみんな前向けるんじゃないかなと。というかそのための会だったと思いたい。太一、よしおり、Mesoたん、他幹事みなさん本当にお疲れさまでした。とても良い会だった。

山城と僕は実はそんなに親しい関係というわけじゃない。ただちょっとアレがアレな感じでwww、お互い意識せざるを得ない関係であった事は間違いないw 一時期は確実に一歩ひかれていた。「おー、山城元気」(山城一歩あとずさりする)、みたいな。しかし、このネタ、当の本人がこの世を去ったあたでもまだ引っ張るのかと思いつつ、まあそれはそれで関係があってうれしい気もする。まさかお別れの会でもこのゲスいネタをやるとは思いもしなんだよ。。。

正直ここ数年の彼はちょっと心配だった。FBやTwitterの書き込みをみても、なんだか前を向いているのかがよくわかんなかったのが正直な感想だ。でも人それぞれな部分あるし、いつか会って話を聞いてみるまでは勝手な判断はやめようと思ってた。で、そこでConmameが開催まで持ってきてくれたAWS Casual Talksに山城がきて、本当に久しぶりの再会だった。これがおそらく亡くなる一ヶ月前くらいだったように思う。

そのときの彼は前を向いてた。色々あったみたいだったけど、会社やるかもとか将来にすっごい前向きだった気がする。しかもなかなか楽しそうで、純粋になんかそれがうれしかった。だから噂レベルで亡くなったという話が聞こえてきたときは本当に残念だった。他の人のように泣きまくったり、何も手つかずとかそういう感じではないのだけど、少なくともしばらく毎日の足取りが重くなり、考えてもどうしようもできない類いのもので、聞くにも聞けずといった感じだった。特に太一とよしおりには。なかなか言葉で言い表すのは難しい。

今回の送別会は本当に太一らしいなと思った。あいつらしく、カオスでグダグダな部分もあって、キチガイじみてて、だけど皆相当笑ってたし、泣いてたな。ジコマン結構じゃん。スライドもなしにいろんな人がしゃべりはじめたときは、なんかわからんが感動した。話は結構がゲスい気もしたけど、なぜか感動した。そして太一がMacを買ったことに宗教上の観点から驚きを禁じ得ない。

最後に勝手な自己満をたれる。山城は少なくとも僕が会った最後の感じはとても前向きだった。だから、前を向こうと思うし、前を向いてほしいなと思う(そこのあなたですよ)。自分が他の人と同じようなつらさなのかはよくわかんない。多分違うんじゃないかなと思う。でもあえて言うと、前を向いてほしい。あと、山城がくっそー参加できなくて、くやしい!おまえら!!!と思うくらいどうしようもない感じの何かをjava-jaで続けてほしいと思う。java-jaってそこでしょ。子持ちになって色々なものに参加が難しい昨今だけど、なるべく時間の許すかぎり参加したいと思う。そして、たまにはばか騒ぎしたい。

P.S.子供のノロ疑惑で先に帰ってすまんかった。。。
そしてスライドはパブリックにはあげれねえw

山城、んじゃね。
shot6

AWS Advent Calendar 12/13 -Go言語でのAWS、特にDynamoDBの利用-

AWSアドベントカレンダーの12月13日として、私が興味あるGo言語からAWSをどうやって利用するかを述べようかなと思います。
ここ最近Goは暇な時間を見つけては好きに書いているだけなので、大して書けるわけではないです。そこんとこご了承ください。

Goって?

Googleが開発しているオープンソースプログラミング言語で、非常にシンプルなのにも関わらず必要十分な機能があります。
詳しくは http://golang.org/ まで。はい、おしまい。

シンプルなコードで簡単なWebアプリ書くならこんな感じになります。これはGo言語のチュートリアルかなんかに従って作ったWebアプリです。

	
package main
import (
        "fmt"
        "io/ioutil"
        "net/http"
        "html/template"
        "regexp"
        "errors"
        "log"
        "os"
)

/*
 * Struct prestenting wiki page
 */
type Page struct {
        Title string
        Body []byte
}

/*
 * Template caching for some templates which heavily used.
 */
var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

/*
 * valid path regex
 *  - m1: edit, view, save
 *  - m2: [a-zA-Z0-9]+
 */
var validPath = regexp.MustCompile("^/(edit|view|save)/([a-zA-Z0-9]+)$")

/*
 * Simple logger from golang logging feature.
 */
var logger = log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

/*
 * Make http.HandlerFunc for editing, viewing, saving by title given by URL
 */
func MakeHandler(fn func(w http.ResponseWriter, r *http.Request, title string)) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
                 m := validPath.FindStringSubmatch(r.URL.Path)
                 if m == nil {
                         http.NotFound(w, r)
                         return
                 } else {
                         fn(w, r, m[2])
                 }
        }
}

/*
 * View handler
 */
 func ViewHandler(w http.ResponseWriter, r *http.Request, title string) {
         p, err := LoadPage(title)
         if err != nil {
                 http.Redirect(w, r, "/edit/" + title, http.StatusFound)
                 return
         } else {
                RenderTemplate(w, "view", p)
        }
 }

/*
 * Edit Handler
 */
 func EditHandler_ugly(w http.ResponseWriter, r *http.Request, title string) {
         p, err := LoadPage(title)
         if err != nil {
                 p = &Page{Title:title}
         }
         fmt.Println("ugly", p.Body)
 }

/*
 * Edit Handler
 */
 func EditHandler(w http.ResponseWriter, r *http.Request, title string) {
         p, err := LoadPage(title)
         if err != nil {
                 p = &Page{Title:title}
         }
         RenderTemplate(w, "edit", p)
 }


/*
 * Save Handler
 */
func SaveHandler(w http.ResponseWriter, r *http.Request, title string) {
        body := r.FormValue("body")
        page := &Page{Title:title, Body:[]byte(body)}
        err := page.Save()
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        } else {
                http.Redirect(w, r, "/view/" + title, http.StatusFound)
        }
}

/*
 * save page 
 */
func (p *Page) Save() error {
        filename := p.Title + ".txt"
        return ioutil.WriteFile(filename, p.Body, 0600)
}

/*
 * load page as Page struct
 */
func LoadPage(title string) (*Page, error) {
        filename := title + ".txt"
        body, error := ioutil.ReadFile(filename)
        if error != nil {
                return nil, error
        }
        return &Page{Title:title, Body:body}, nil
}

/*
 * Render using template loaded from name.
 */
func RenderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
        err := templates.ExecuteTemplate(w, tmpl + ".html", p)
         if err != nil {
                 http.Error(w, err.Error(), http.StatusInternalServerError)
         }
}

/*
 * bootstrap
 */
func main() {
        logger.Println("start web server...")
        logger.Println("register http.HandlerFunc.")
        http.HandleFunc("/view/", MakeHandler(ViewHandler))
        http.HandleFunc("/edit/", MakeHandler(EditHandler))
        http.HandleFunc("/save/", MakeHandler(SaveHandler))

        logger.Println("bootstrap web server...")
        http.ListenAndServe(":8080", nil)
}

GoからのAWS利用方法

現状残念ながらAWSからの正式SDKは出ていませんが、比較的アクティブに動いているプロジェクトは幾つかあります。
例えばGoamz(https://launchpad.net/goamz)とかですね。その中で特にDynamoDBを利用するには、今回紹介するGodynamoがなんかよさげにみえます。

Godynamoの前に

今回のre:InventでAWSのヘビーユーザであるSmugmugはMySQLからDynamoDBへ移行した話をしてくれました。

Smugmugは大きく分けて3つ今回新しいオープンソースを出しています。

  1. Godynamo : DynamoDBをGoから使うライブラリ
    1. https://github.com/smugmug/godynamo
  2. bbpd : DynamoDBをHTTPプロキシしてくれるライブラリというかサーバ?
    1. https://github.com/smugmug/bbpd
  3. goawsroles : GoからIAM Roleをシームレスに使うためのライブラリ。
    1. https://github.com/smugmug/goawsroles

時間が出来ればbbpdは試してみたいですねー!bbpdがあると

curl -X POST -d '{"TableName":"mytable","Key":{"Date":{"N":"20131001"},"UserID":{"N":"1"}}}' "http://localhost:12333/GetItem?indent=1&compact=1"

こんな感じでDynamoのデータを参照できます。便利!

Godynamo

GodynamoはAWSのヘビーユーザであるSmugmugが実際に使っているGo言語用のDynamoDBライブラリになります。
実際にMySQLからの移行から使われているので実績は既にあるものになります。

Godynamoを使うには3つのことをする必要があります。

  1. Goのインストール
  2. 必要なライブラリのインストール
    1. go get github.com/smugmug/godynamo
    2. go get github.com/smugmug/goawsroles
    3. go get github.com/bradclawsie/slog
  3. クレデンシャル情報の設定

クレデンシャル情報の設定はjsonファイルにて行います。
大きく分けて、アクセスキーとシークレットキーを使う方法と、IAM Roleを使う方法があります。
設定ファイル自体は~/.aws-config.jsonとしておきます。


設定ファイルはこんな感じ。

{
    "extends":[],
    "services": {
        "default_settings":{
            "params":{
                "access_key_id":"your_access_key",
                "secret_access_key":"your_secret_key",
                "use_sys_log":true
            }
        },
        "dynamo_db": {
            "host":"dynamodb.ap-northeast-1.amazonaws.com",
            "zone":"ap-northeast-1",
            "iam": {
                "use_iam":false,
                "role_provider":"file",
                "access_key":"role_access_key",
                "secret_key":"role_secret_key",
                "token":"role_token",
                "base_dir":"/dir/where/you/update/role_files",
                "watch":true
            }
        }
    }
}

本来はIAM Roleを使うべきですが、今回は割愛します。本番ではIAM Roleを使いましょう!

コードは非常にシンプルに作ることが出来ます。私が気に入ってるのは、

  • 直感的でわかりやすい
  • レスポンスが生のJSONで帰ってくるので、どう扱うかは利用者側にゆだねられている
  • 早い

のあたりです。特に生のJSONファイルなのでとりまわしは正直楽で、かつパースは後からやればいいので高速です。このあたりのシンプルさが好きですね。


例えばQueryのコードはこんな感じになります。ハッシュキーを指定してレンジキーを1万件取得している感じです。

package main

import (
        "fmt"
        "encoding/json"
        ep "github.com/smugmug/godynamo/endpoint"
        query "github.com/smugmug/godynamo/endpoints/query"
        "github.com/smugmug/godynamo/conf"
        "github.com/smugmug/godynamo/conf_file"
)

func main() {
        conf_file.Read()
        if conf.Vals.Initialized == false {
                panic("the conf.Vals global conf struct has not been initialized")
        }

        tn := "revaluate"
        q := query.NewQuery()
        {
                q.TableName = tn
                q.Select = ep.SELECT_ALL
        }
        k_v1 := "209963"
        var kc query.KeyCondition
        {
                kc.AttributeValueList = make([]ep.AttributeValue,1)
                kc.AttributeValueList[0] = ep.AttributeValue{N:k_v1}
                kc.ComparisonOperator = query.OP_EQ
        }
        q.Limit = 10000
        q.KeyConditions["id"] = kc
        json,_ := json.MarshalIndent(q, "", "")
        fmt.Printf("JSON:%s\n",string(json))
        body,code,err := q.EndpointReq()
        fmt.Printf("%v\n%v\n%v\n",body,code,err)
}

レスポンスはJSONで帰ってくるので必要な部分だけをパースすればよいと思います。

2013/12/13 17:03:38 read conf from: /Users/shot6/.aws-config.json
JSON:{
"AttributesToGet": null,
"ConsistentRead": false,
"ExclusiveStartKey": null,
"IndexName": null,
"KeyConditions": {
"id": {
"AttributeValueList": [
{
"N": "209963"
}
],
"ComparisonOperator": "EQ"
}
},
"Limit": 10000,
"ReturnConsumedCapacity": "NONE",
"ScanIndexForward": true,
"TableName": "revaluate",
"Select": "ALL_ATTRIBUTES"
}
{"Count":17,
"Items":[{"id":{"N":"209963"},"date":{"N":"1366807742203"},"user":{"S":"209963:231131"}},......]}
200
<nil>

まとめ

いかがでしたでしょうか?非常に簡単に使える感覚がわかってもらえたら嬉しいです。
私自身も試し始めたばかりですが、そこそこ使えているので、ぜひ試しにやってみてください。

また、DynamoDB自身もいよいよ待望のグローバルセカンダリインデックスが対応されましたので、こちらも試してみてください!

http://aws.typepad.com/aws/2013/12/now-available-global-secondary-indexes-for-amazon-dynamodb.html

re:Inventおすすめセッション3日目

最終日、早いものです。

では超個人的なお奨めを。

STG306:CloudStorage for App Developers

Dropboxの人の講演。これは楽しみですね。最近増えてきましたが、なかなか語られる事のないので期待値高めです。
9時からです。おそらくかなり開発者よりではないかと思われます。

BDT204 GraphLab: Large Scale Machine Learning on Graphs

こちらは機械学習のセッションですね。GraphLabsは名前は知っていたのですが中身はまだ詳しくないのでしっかり学習しようかなと。

MBL303:Gaming-Ops: Running High-Performance Ops for Mobile Gaming

ゲームの運用についての話。こちらはGree Internationalの方の講演です。
11時30分から。生々しい話が聞けるといいなあー

Unmeltable Infrastructure at Scale:Using Kafka, Storm, and ElasticSearch on AWS


Logglyのセッション。ログ収集のIngestion側の話ですね。第2世代のログ収集システムということで、色々具体的な話が聞けそうです。

DAT304:Mastering NoSQL:Advanced DynamoDB Design Patterns for ultra-high performance apps

DynamoDBを使ってスケールを出すためのパターンの話。DynamoDBをうまく使いたい人にはかなり好さそう!

MBL304:Building a World in the Clouds:MMO Architecture on the AWS

AWS上でMMOを作った事例。僕はこれに出る予定。

ARC312:SmugMug's Zero Downtime Migration to AWS

SmugMugがずっとハイブリッドだったのを100% AWSに持ってきたという良さげな話。
これはリアルにありそうな話なのでかなり現実味のある話を期待!


こんなところでしょうか。
Enjoy the SHOW!

re:Inventおすすめセッション

初日すっかり忘れてましたが、2日目から。なんだか聞かれることが多かったので。

CPN401:A Day in the Life of a Billion Packets

11時からで、もう始まってしまいますが、間に合う人がいればぜひ。
今ここにいます。

TLS304:Becoming a Command Line Expert with the AWS CLI

これも面白そう。同じく11時からです。実際に運用に携わる人はみておいてよいかなと。


SPOT401:Leading the NoSQL Revolution

DynamoDBが何故できたか、どう違うのかなどの詳細が知りたい人はこちらを強くお勧めします。
ウチのメンバーとはスプリットしてこれをみています。
11時からです、

DAT204 SmugMug: From MySQL to DynamoDB

こちらも事例でしかもRDBMSからNoSQLという意味でも面白そうです。
これは13時30から。

SPOT201 Managing the Pace of the Innovation:Behind the Scences of AWS

これは13時30から。AWSがどうやってイノベーションのペースを落とさずにやっているかをテクノロジーのトップがしゃべる、おそらくかなりレアな機会だと思います。
私はここに参加。

STG302-R Maximizing EC2 and EBS Disk Performance(repeat)

EBSのPIOPSおよびhi1/hs1のSSDを使うならば、このセッションは確実に見ておいた方が良いと思います。
初日に見たのですが相当おすすめです。13時30分から。

DAT306 How Amazon.com Is Leveraging Amazon Redshift

Redshiftに興味ある方はこちらは見てみると良いかと思います。相当面白いはず
3時からですね。

SPOT205 Why Scale Matters and How the Cloud is Really Different

おなじみのJames Hamilton先生のセッション。当然これは出ます。
4時15分から。

ARC301 Controlling the Flood:Massive Message Processing with SQS/DynamoDB

こちらはどうやって大量のメッセージ処理をさばくかですね。SQSとDynamoDBのアドバンスドな使い方が見れそうです。
5時30分からですね。

MBL307 How Parse Built a Mobile Backend as a Service on AWS

keynoteでも出てきたParseの事例。MBaaS事例としてもみるべきかと。
こちらも5時30分からですね


これ以外にも大量にありすぎるのでw、ぜひ自分が面白そうなセッションを探して参加してみてください。
なんか2日目だけでうまってしまったのでこの辺で!

AWS CLIとjqを使った簡易管理ツール

AWSの新しいユニファイドCLIは最新バージョンである0.5.1でかなりサポートされたサービスが増えています。RDS, ELB, EMRなどの主力サービスはだいたいサポートされてきています。ところがどっこい、unified cliは基本的に返すレスポンスが基本JSONで返ってきます。なかなか人間様には見にくいので少々こねくりまわす必要があります。そこでJSONを簡単にパースしてくれるjqコマンドでうまいこと見たいものだけをとるのが望ましいです。


というわけで温泉ハッカソンで簡単なコンソール表示をEC2, ELB, RDSでやってみました。

EC2インスタンスのタイプ、状態、URLを見る

初級編。

aws ec2 describe-instances|
    jq '.reservationSet.instancesSet|
        .instanceType + " / " + .instanceState.name, .dnsName, 
    "---------------------------------------------------------"'


結果はこんな感じ。

"m2.xlarge / stopped"
""
"---------------------------------------------------------"
"m1.medium / stopped"
""
"---------------------------------------------------------"
"m1.medium / stopped"
""
"---------------------------------------------------------"
"t1.micro / running"
"ec2-54-249-133-52.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"m1.medium / running"
"ec2-54-228-151-12.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"m1.medium / running"
"ec2-54-228-155-116.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"t1.micro / running"
"ec2-54-228-38-217.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"t1.micro / running"
"ec2-54-229-153-38.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"t1.micro / running"
"ec2-173-34-19-211.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"t1.micro / running"
"ec2-46-54-220-111.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"t1.micro / running"
"ec2-54-223-50-57.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"
"m1.small / running"
"ec2-54-223-11-57.ap-northeast-1.compute.amazonaws.com"
"---------------------------------------------------------"

ELBのざっくりとした概要を知る

少し慣れてきたのでELBではもうちょい細かく知りたい感じだったので、ぶら下がってるインスタンスと、ポートのフォワードとヘルスチェックも。
もうちょいやるなら、インスタンスの詳細も欲しいな。

aws elb describe-load-balancers|
    jq '.LoadBalancerDescriptions|
        .DNSName, 
        "Instance", 
            "    " + .Instances.InstanceId, 
         "AZ", 
            "    " + .AvailabilityZones, 
         "Listen port config", 
            "    " + .ListenerDescriptions.Listener.Protocol + ":" + (.ListenerDescriptions.Listener.LoadBalancerPort|tostring) + "->" + .ListenerDescriptions.Listener.InstanceProtocol + ":" + (.ListenerDescriptions[].Listener.InstancePort|tostring),
         "Healthcheck",
            "    " + .HealthCheck.Target, 
"====================================================================="'

さっくり力技。

"myfrontend-1709110016.ap-northeast-1.elb.amazonaws.com"
"Instance"
"    i-0948680a"
"    i-0d48680e"
"    i-0f48680c"
"    i-6d4a6a6e"
"    i-c14868c2"
"    i-c74868c4"
"AZ"
"    ap-northeast-1a"
"    ap-northeast-1b"
"    ap-northeast-1c"
"Listen port config"
"    HTTP:80->HTTP:80"
"Healthcheck"
"    HTTP:80/index.html"
"====================================================================="
"yourapp442.ap-northeast-1.elb.amazonaws.com"
"Instance"
"AZ"
"    ap-northeast-1a"
"Listen port config"
"    HTTP:80->HTTP:80"
"Healthcheck"
"    HTTP:80/index.html"
"====================================================================="

RDSでもざっくり概況を知る

RDSでは、エンジンやバージョン、ストレージだけでなく、マルチAZかどうか、リードレプリカなども。

aws rds describe-db-instances|
    jq '.DBInstances[]| 
        .DBInstanceIdentifier + "(" + .Engine + " " + .EngineVersion + ", " + (.AllocatedStorage|tostring) + "GB)" + "[" + .DBInstanceStatus + "]",
        .AvailabilityZone, 
        .Endpoint.Address + ":" + (.Endpoint.Port|tostring),
        if .MultiAZ == true then "MAZ" else "Single" end,
        if .ReadReplicaSourceDBInstanceIdentifier then "RR : " + .ReadReplicaSourceDBInstanceIdentifier  else "" end,
"======================================================================" '

結果はこんなん。

"hoge(mysql 5.5.8, 5GB)[available]"
"ap-northeast-1c"
"hoge.cb1tkvzxk6ev.ap-northeast-1.rds.amazonaws.com:3306"
"MAZ"
""
"======================================================================"
"mydb(mysql 5.5.27, 5GB)[available]"
"ap-northeast-1b"
"mydb.cb1tkvzxk6ev.ap-northeast-1.rds.amazonaws.com:3306"
"Single"
""
"======================================================================"
"mydb-rr(mysql 5.5.27, 5GB)[available]"
"ap-northeast-1b"
"mydb-rr.cb1tkvzxk6ev.ap-northeast-1.rds.amazonaws.com:3306"
"Single"
"RR : mydb"
"======================================================================"


まあほとんどjqの練習になってしまいましたが、まあいいでしょう。

Asgardの更によいとこ

Asgard君、驚くべきことにきちんとデフォルトでAPIをサポートしています。


東京リージョンでのリソース一覧

curl http://asgardhost:8080/ap-northeast-1/list.json

東京リージョンのインスタンス一覧

curl http://asgardhost:8080/ap-northeast-1/instance/list.json

作成したアプリケーション"simpleaa"のクラスタリソース一覧

curl http://asgardhost:8080/ap-northeast-1/cluster/show/simpleapp.json

東京リージョンでのアプリケーション一覧

curl http://asgardhost:8080/ap-northeast-1/application/list.json

セキュリティグループなどの設定一覧

curl http://asgardhost:8080/ap-northeast-1/security/list.json

今回突っ込んではやらなかったですが、POSTを使ってリソースのコントロールをAsgard経由でプログラマブルにも可能です。
詳細は下記のURLです。


https://github.com/Netflix/asgard/wiki/REST-API


えっと、言葉がないですが、これでよくないですかw?