2017/08/29


株式会社はてな さんから献本頂きました。ありがとうございます。

Mackerel サーバ監視[実践]入門 Mackerel サーバ監視[実践]入門
井上 大輔, 粕谷 大輔, 杉山 広通, 田中 慎司, 坪内 佑樹, 松木 雅幸
技術評論社 / ¥ 3,132 (2017-08-26)
 
発送可能時間:在庫あり。

書籍「Mackerel サーバ監視[実践]入門」を執筆しました | おそらくはそれさえも平凡な日々

書籍「Mackerel サーバ監視[実践]入門」を執筆しました Mackerel サーバ監視[実践]入門 やっと出せました。この本は、僕がはてなに入社してからずっと携わっている、 Mackerel と...

http://www.songmu.jp/riji/entry/2017-08-28-mackerel-book.html

僕が Mackerel を知ったのは2014年5月の事でした。

mackerel-agentをオープンソース化しました - Mackerel ブログ #mackerelio

本日、mackerel-agentを オープンソース 化しました。 リポジトリ は GitHub 上の mackerelio/mackerel-agent です。 mackerel-agentは Go...

https://mackerel.io/ja/blog/entry/2014/05/16/142556

昔からはてなの少しギーク寄りなサービスが大好きだったのと、mackerel-agent が僕の好きな Go言語 で書かれていた事もあり、いち早く GitHub からソースを git clone して眺めていました。初見から Windows に対応していない事を知った僕はすぐさますぐさま Windows porting に取り掛かり、プルリクエストを送りました。

Windows porting by mattn - Pull Request #8 - mackerelio/mackerel-agent - GitHub
https://github.com/mackerelio/mackerel-agent/pull/8

あれから3年経ちました。Mackerel はどんどん大きくなって SI 業や Web ホスティング業など色々な企業で使われる様になり、今や国内の SaaS 型サーバ監視サービスの先駆者的なポジションを確立したのではないかと思います。

Mackerel ではサービスだけでなく mackerel-agent も日々改良が加えられており、現在では僕が書いた Windows 向けのコードはほぼ無くなってしまったかもしれませんが、今思えば僕が Windows に移植した mackerel-agent が様々な企業で使われ、刻々とサーバのメトリクスを休む事なく Mackerel サーバに送り続け、問題が起きた Windows サーバのアラートを上げていると考えると「あぁ、いい仕事したな」と胸が熱くなると同時に、「あれ僕が Windows 移植したんだ」と誇らしい気持ちにさえなりました。

今回読んだ「Mackerel サーバ監視[実践]入門」はこう言った Mackerel の生い立ちだけでなく Mackerel そのものの概念、監視のイロハ、活用例など、数千のサーバを管理している彼らでないと書けない実践的な内容が包み隠さず書かれています。

Mackerel というサービスの利用者は一般ユーザではありません。インフラを管理する、どちらかと言えば開発者寄りの方達です。エンジニアである Mackerel の開発者が、開発者に向けて OSS として機能を提供しているので Mackerel には気の利いた機能が沢山あります。mackerel-agent が使用するプラグインもその一つです。サーバの監視で必要と思われる様なプラグインが既に沢山作られています。

GitHub - mackerelio/mackerel-agent-plugins: Plugins for mackerel-agent
https://github.com/mackerelio/mackerel-agent-plugins
GitHub - mackerelio/go-check-plugins: Check Plugins for monitoring written in golang
https://github.com/mackerelio/go-check-plugins

一度プラグインを作ってみられると分かると思いますが、ドキュメントもあり参考にできるプラグインも沢山あるので簡単に実装できると思います。アイデア次第では色々な使い方があると思います。例えば温度計や水位計と連動してアラートを上げたり、PC であれば Windows のセキュリティパッチが未インストールの場合にアラートを上げるなんて事も実に簡単に実装する事が出来ます。

Mackerel チェック機能の意外と知られていないテクニック集 - Qiita
http://qiita.com/mattn/items/b7099044263875ed1f73

本書ではコード例も付けて実際に mackerel-agent プラグインを作る手順が細かく書かれています。Go言語は言語仕様が難しくないので、この本片手に簡単にプラグインが作れると思います。

もう一つ僕が気に入っている Mackerel の機能が mkr です。mkr は Mackerel の設定やメトリクス値を簡単に操作できる CLI です。

GitHub - mackerelio/mkr: Command Line Tool for Mackerel
https://github.com/mackerelio/mkr

このツールを使えば監視しているホストの状態取得やメトリクス値を抜き出す事が出来ます。本書にもこの mkr を使ったテクニックが色々と書かれています。これは本書で知ったのですが、どうやら はてな社は Mackerel をホスト管理としても使っている様で、数千いるサーバのロールをうまくラベリングして運用されているとの事でした。API を直接叩いてホスト一覧を得る事もそれほど難しくはありませんが、mkr の様にコマンド一発で色々な情報を Mackerel から抜き出せる機能は、多くのサーバを管理しているエンジニアにとっては必須アイテムかと思います。Mackerel ユーザで mkr を使った事ない方はぜひ使ってみると良いです。

僕が本書で一番おもしろいと思ったのは、実は巻末部分でした。Mackerel というサービスがどの様に構成されているかが書かれています。なぜ PostgreSQL や Graphite を使っているのか、そして今後の為に Graphite から移行しようとしていると言った内容が惜しげもなく書かれており、今も進み続ける Mackerel らしさが良く分かる内容でとても興味深く読ませて頂きました。

特に Mackerel が今のアーキテクチャに至った経緯の解説をしている節では、Mackerel のサーバがどういった理由でこういう構成になっているのかが理由を交えて詳しく書かれており、「あぁ、Mackerel を大事に育ててるんだな」と凄く伝わる内容でした。

不慮の障害を避ける為には、監視は機械的でなければなりません。人間による、しかも属人的な監視は時に間違いを起こし、ミスは大きな事故に繋がります。単調な監視はシステムに任せ、我々開発者は安心して別の事に注力するのが今のあり方なのでしょうね。そしてその安心は、優秀な Mackerel 開発チームの「Mackerel 愛」があってこそ、実現されているのだろうな、と分かる1冊でした。

Posted at by



2017/08/25


gRPC は型の強い RPC を色々な言語を使って実装できる仕組みとライブラリです。

Big Sky :: Protocol Buffers を利用した RPC、gRPC を golang から試してみた。

grpc/grpc · GitHub gRPC - An RPC library and framework https://github.com/grpc/grpc gRPC は Google が開...

https://mattn.kaoriya.net/software/lang/go/20150227144125.htm

とても便利なのですが幾分手数が多いのが難点で、ちょっとしたサービスを gRPC で実装したいと思っていてもそう簡単に作る事が出来ませんでした。

ところが今回ご紹介する lile を使うと、とても簡単に gRPC を使った golang の実装を作れてしまいます。

GitHub - lileio/lile: Easily create gRPC services in Go

readme.md ALPHA: Lile is currently considered "Alpha" in that things may change. Currently I am gath...

https://github.com/lileio/lile

lile は gRPC のスケルトンを生成するコマンドとライブラリセットです。今日はこれを使って簡単に gRPC のサービスを作ってみます。お題は GENE95 辞書 を gRPC 経由で照会するサービスです。

まず lile をインストールするには以下のコマンドを実行します。

$ go get github.com/lileio/lile/...

Windows の人は今 pull-request を作ってるのでそちらを使って下さい。執筆時点でまだマージされてませんが。

lile をインストールしたらまずスケルトンを生成します。

$ lile new gene9go
Creating project in /home/mattn/go/src/github.com/mattn/lile-example/gene9go
Is this OK? [y]es/[n]o
yes
.
├── server
│   ├── server.go
│   └── server_test.go
├── subscribers
│   └── subscribers.go
├── gene9go
│   ├── cmd
│       ├── root.go
│       ├── serve.go
│       ├── subscribe.go
│       └── up.go
│   └── main.go
├── gene9go.proto
├── Makefile
├── Dockerfile
├── .travis.yml
└── .gitignore

git push できるくらいの物が生成されています。次に proto ファイルを編集して独自のインタフェースを作成します。元の proto ファイルは以下の様になっています。

syntax = "proto3";
option go_package = "github.com/mattn/lile-example/gene9go";
package gene9go;

message Request {
  string id = 1;
}

message Response {
  string id = 1;
}

service Gene9go {
  rpc Read (Request) returns (Response) {}
}

これを以下の様に編集しました。

syntax = "proto3";
option go_package = "github.com/mattn/lile-example/gene9go";
package gene9go;

message Request {
  string Word = 1;
}

message Response {
  string Text = 1;
}

service Gene9go {
  rpc Translate (Request) returns (Response) {}
}

辞書引きなので Translate メソッドを追加しています。編集し終えたら Makefile のある場所で make を実行します。するとこの proto ファイルから Translate メソッドのスケルトンが生成されます。

package server

import (
    "errors"

    "github.com/mattn/lile-example/gene9go"
    context "golang.org/x/net/context"
)

func (s Gene9goServer) Translate(ctx context.Context, r *gene9go.Request) (*gene9go.Response, error) {
    return nil, errors.New("not yet implemented")
}
尚、この時点でテストコードのスタブも生成されます。とても便利です。

package server

import (
    "testing"

    "github.com/mattn/lile-example/gene9go"
    "github.com/stretchr/testify/assert"
    context "golang.org/x/net/context"
)

func TestTranslate(t *testing.T) {
    ctx := context.Background()
    req := &gene9go.Request{}

    res, err := cli.Translate(ctx, req)
    assert.Nil(t, err)
    assert.NotNil(t, res)
}

さてでは Translate メソッドの中身を実装します。gene.txt ファイルを読み込んで単語にマッチする次の行を返しているだけです。gene.txt は utf-8 で保存しておいて下さい。

package server

import (
    "bufio"
    "os"
    "strings"

    "github.com/mattn/lile-example/gene9go"
    context "golang.org/x/net/context"
)

func translate(word string) (stringerror) {
    f, err := os.Open("gene.txt")
    if err != nil {
        return "", err
    }
    defer f.Close()

    scanner := bufio.NewScanner(f)
    found := ""
    for scanner.Scan() {
        first := scanner.Text()
        if !scanner.Scan() {
            break
        }

        if strings.ToLower(first) == strings.ToLower(word) {
            found = scanner.Text()
            break
        }
    }
    return found, scanner.Err()
}

func (s Gene9goServer) Translate(ctx context.Context, r *gene9go.Request) (*gene9go.Response, error) {
    text, err := translate(r.GetWord())
    if err != nil {
        return nil, err
    }
    return &gene9go.Response{Text: text}, nil
}

あとはクライアントとサーバを作ります。といっても Register 関数が用意されているのでこちらも簡単。まずはサーバ。

package main

import (
    "log"
    "net"

    "github.com/mattn/lile-example/gene9go"
    "github.com/mattn/lile-example/gene9go/server"
    "google.golang.org/grpc"
)

func main() {
    lis, err := net.Listen("tcp"":11111")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    srv := grpc.NewServer()

    gene9go.RegisterGene9goServer(srv, &server.Gene9goServer{})
    srv.Serve(lis)
}
そしてクライアント。
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/mattn/lile-example/gene9go"
    "google.golang.org/grpc"
)

func main() {
    conn, err := grpc.Dial("localhost:11111", grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := gene9go.NewGene9goClient(conn)
    req := &gene9go.Request{
        Word: os.Args[1],
    }
    resp, err := client.Translate(context.Background(), req)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(resp.Text)
}
後から気付いたのですが、gene9go の下にサーバコマンドも生成されてました。いたれりつくせり過ぎる。
A gRPC based service

Usage:
  gene9go [command]

Available Commands:
  help        Help about any command
  serve       Run the RPC server
  subscribe   Subscribe to and process queue messages
  up          up runs both RPC and pubub subscribers

Flags:
  -h, --help   help for gene9go

Use "gene9go [command] --help" for more information about a command.

これだけです。なにこれ超簡単じゃん。サーバを起動した状態で、クライアントに単語を付けて起動します。

$ client Go
1.〜に進行する,行く,をしに行く,動く,過ぎる,至る,及ぶ,2.〜と書いてある

これを実装するのにわずか20分程度しか掛かりませんでした。今まで gRPC を使ったサービスに興味があったけど実装難しいと思っていた方はぜひ lile を使ってみて下さい。あっと言う間に実装できるはずです。

この記事で作ったソースファイルは以下に置いておきます。

GitHub - mattn/lile-example
https://github.com/mattn/lile-example
Posted at by



2017/08/21


Windows では子プロセスが出力した標準出力を読み取る際にバッファリングが行われる。これは親プロセスからは無効に出来ない。子プロセスが setbuf を呼び出してバッファを無効にするか、fflush で強制的にバッファをフラッシュする必要がある。

#include <windows.h>
#include <stdio.h>

int
main(int argc, char* argv[]) {
  int n = 0;
  while (1) {
    printf("%d", n++);
    Sleep(1000);
  }
  return 0;
}

Vim に job/channel 機能があるが、これらもこの入出力バッファのせいで改行されるまでの出力を読み取る事が出来ない。:terminal が取り込まれてインタラクティブなプロセスを制御できる様になったけど、Windows の :terminal は winpty を使って画面の要素を読み取って実行されている。なので標準出力そのものをリアルタイム読み取るという事は出来ない。そこで CreateRemoteThread を使ってプロセスに DLL インジェクションし、そこで setbuf を呼び出す物を作った。

GitHub - mattn/vim-iobuf

README.md vim-iobuf On Windows, stdout has a buffer. As you can see, this can be controllable with s...

https://github.com/mattn/vim-iobuf

これを使って以下の様に呼び出すと、子プロセスのバッファリングが無効にされ改行コードを出力しなくても channel を使って読み取る事が出来る。

let job = job_start(['.\\tick.exe'], {
\  'out_mode''raw'
\  'out_cb':{ch,msg->execute('echo msg'1)},
\  'exit_cb':{jo,st->execute('echo "exited"'1)
\}})
call iobuf#nobuffer(job)
iobuf

プラグインから使うプラグインとして、お使い下さい。

setbuf はライブラリ依存なので実行するランタイムに依存します。

Posted at by