インフラエンジニアがGo言語でオペレーションツールを書くことについて

ウェブオペレーションエンジニアの id:y_uuki です。
はてなの東京オフィスで先月開催されたGo 1.6 Release Partyで、「Writing Tools in Go For Ops Engineers」というタイトルで発表しました。

発表では、最近作ったGo製ツールを紹介し、なぜGoがインフラエンジニアにとって良い言語であると感じているかを話しました。

最近作ったGo言語のツールの紹介

以下の4つのツールを作りました。いずれもはてなでのproduction利用を想定したものになります。

mkr

mkrははてなで開発しているサーバ管理ツールMackerelのAPIをたたくためのCLIです。

Mackerelはサーバの管理台帳APIサーバとしての側面があり、プログラマブルにインフラを扱えます。mkrはコマンドラインから気軽にAPIを叩き、jqと組み合わせてMackerelがもつ情報を加工し、他のツールに渡すというような、てこのような役割を果たします。

手元からも利用できるのはもちろん、サーバに配置するとより便利に使えます。mackerel-agentが既に動作しているサーバに配置すると、mackerel-agentの設定ファイルを勝手に読み込むことで、APIキーの設定を不要にします。さらに、mkrを叩いたホストの情報を、hostIdを指定せずに、参照ないし変更できます。

mkrを作ったときのことは以下のエントリで紹介しています。


Grabeni

GrabeniはEC2のENIを用いたフェイルオーバを補助するためのCLIツールです。

ENIはEC2で利用できるネットワークインタフェースであり、APIで操作できます。ENIに付与されているIPアドレスをVIPとみなして、ENIの付け替えによりスイッチオーバ/フェイルオーバ時にアプリケーションからの参照先を変更できます。

GrabeniはコマンドラインからENIを操作できます。grab、attach、detachなどのサブコマンドを実装しています。AWS APIを叩くときのリトライや最大リトライ回数などを制御できるようになっています。

はてなではKeepalivedと組み合わせて利用しています。MHAやHertbeatなど、他のHAツールと組み合わせて使うこともできます。

Droot

DrootはDockerでつくったイメージをchroot jail環境として実行するものです。結構複雑な背景があるので、詳しくは下記のエントリを読んでください。ひとことで言うと、Dockerの本番運用を考えるのに疲れて、chrootに行き着きました。


gokc

gokcはKeepalivedのシンタックスチェッカです。
Keepalivedがシンタックスチェックをまじめにやってくれないという問題がありました。
その問題を解決するための既存ツールもありましたが、いろいろあってGoで一から作りました。

gokcはサーバ上に配置し、initスクリプトでreload前にgokcを走らせて、チェックの結果がfailになると、reloadは実行されないという使い方をしています。

gokcについては、以下のエントリで詳しく紹介しています。


インフラエンジニアがGoを利用することのメリット

前述したツール群は、基本的にサーバ上で動作させることを意図しています。これはインフラエンジニアが作るツールならではの特徴だと思います。

そこで、なぜGoで書いたのか、インフラエンジニアにとってなぜGoがよいのかを紹介します。

それインフラエンジニアに限らないよ、または、それGo以外でもできるよ、といった話はもちろんあると思います。とはいえ、Goで実際に書いてみてよかったと感じたことは間違いなくあります。

1. サーバへの配布が簡単

Goでビルドしたバイナリであれば、多くの環境でバイナリを一つ配置すればそのまま動くというケースも珍しくありません。よく知られている話ですが、Goでビルドしたバイナリはワンバイナリと呼ばれています。ワンバイナリは依存なしでサーバにバイナリを配置すれば動きます。(cgoに依存している場合や特定OSでしか利用できないシステムコールを直接呼び出しているなどのケースを除く)

はてなには数千台のサーバがあり、サーバの構成は各サーバ間で統一されている保証はありません。5年前に構築したサーバもあれば、一週間前に構築したサーバもあります。このような環境では、配布物のポータビリティが非常に重要になってきます。

2. サーバ上で高速開発できる

Goはバイナリ一つで完結するという性質に加えて、クロスコンパイルできます。インフラエンジニアはサーバ上で動作するツールや特定OS専用のツールを書くことも多いため、これらの性質のおかげで、サーバ上での動作確認が非常に楽です。

例えば、手元のOSXマシンからLinux用にクロスコンパイルして、scpでサーバに設置して簡単に動かすことができます。

GOOS=linux GOARCH=amd64 go build 
scp ./binary hostname:/path/to/binary
ssh hostname /path/to/binary

3. 最終的に成果物をはやく作れる

Goは言語仕様がシンプルで覚えることが少ないため、その分迷いが少なく、最終的な成果物まで持って行きやすいと感じています。

もちろん、単にコーディング速度だけを競うなら、スクリプト言語のほうが速く書けるとは思います。ただし、多くの言語仕様や複数のパラダイムをサポートする言語の場合、処理は書けるんだけど、どの書き方がベストなのかを迷うことがあります。

それも含めてプログラミングの楽しさだと思います。しかし、少なくとも自分は、インフラエンジニアとして最終的な成果物が動いて、モニタリングできたり、手動オペレーションを自動化できることに喜びを感じます。

その他

その他、Goの中で気に入ってる点をいくつか挙げます。

  • Goroutineによる並行性
    • 特にUNIXパイプの概念を使える io.Pipe() と Goroutineとの組み合わせ
    • drootでは、io.Pipe() を使って、DockerイメージのビルドからS3にアップロードするまでストリーム処理をしている。
  • 高速なランタイム
    • 速いことは単純にうれしい
  • 返り値によるエラーハンドリング
    • 賛否両論ありますが、自分は例えばスクリプト言語よりはちゃんとエラーハンドリングをやるようになっている気がします。
  • defer
    • オペレーション用スクリプトとか書いていると、一旦tmpファイルに書き出してあとで掃除するということをよくやったりします。deferを使うと、掃除処理を書きやすいので気に入っています。

発表資料


あとがき

Perl以外のプログラミング言語系のコミュニティイベントでおそらく初めて発表しました。
今年はコードを書くぞと決めているので、今後はGo言語方面にも積極的にアウトプットしていきたいと思います。

はてなでは、Goでアプリケーションを書きたい人や、ツールを作りたい人を募集しています。