Travis CI で目当ての言語バージョンとOSの組合せを指定する
世の中のみんなは今日も頑張ってOSSのミドルウェアを書いていることと思いますが、そんな我々に欠かせないのがTravis CIというサービスでして、OSSならCI環境を無料で使わせてもらえる! マジで最高のサービスだと思います。日本からだとUSの人達が元気な時間帯とズレてるせいか、pushしたあとビルドが走るのが早くて本当にありがたい。*1
で、CIを回すからには対象の言語バージョンといっしょにOSの違いも確認したい。標準の環境の Linux (Ubuntu) 以外に最近ではなんとOSX環境もあるので、ミドルウェアプログラミング野郎としてはやっぱり両方で動かしておきたいわけです。言語バージョンももちろんあれやこれや、古いの*2から新しいのまで色々動かしたい。
しかしそこには問題があって、有限のリソースというものがある以上、野放図に指定はできないわけです。
普通にバージョンを指定
単に組合せで指定したい場合は、こう:
# .travis.yml language: ruby rvm: - 2.1 - 2.2.3 - 2.3.0 - ruby-head - rbx os: - linux - osx
この設定を与えると Travis CI は rvm(rubyのバージョン) と os の組合せを掛け算して、5バージョン 2環境 の10ビルドを実行する。ただしこれは失敗してもいいやというものはあって、例えば ruby-head や rbx ではビルドは動かしておきたい、がコケても失敗とは見做さない*3、とかしたいことがある。この場合は allow_failures というやつでその指定をすることになる。
matrix: allow_failures: - rvm: ruby-head - rvm: rbx
ビルド条件をもっと細かく特定して指定
ただしこれも色々あって、言語バージョンの指定が割とざっくり("2.1" とか)しているならいいんだけど、細かくバージョンを指定したくなった場合、実はあれこれ制約が出てくる。Ruby 2.1といっても2.1.xの最新版で実行したいとなったりすると、まずどのバージョンがTravis上で使えるのかを調べる必要がある。Rubyの場合は以下のページ(言語ごとにあるのでドキュメントページから探そう)。
Travis CI: Precompiled Ruby Versions
これを見るとわかるのが、各OSのバージョンごとに使える言語バージョンが決まっている、ということだ。細かい言語バージョンまで指定する場合、それが動く環境を選ぶようにしてやる必要がある。*4
ということで、例えば Ruby 2.1.10 でテストを実行したければ OSX 10.10 もしくは 10.11 を指定する必要がある……んだけど、Travis CIのドキュメントを見てもぱっとわかるページがない。困ってたところ教えてもらった。ここにあった。
Building an Objective-C Project - Travis CI
Objective-C! いやまあ実際にはObjective-C環境もXcode自体も(直接的には)要らないんだけど、これを経由して間接的にOSXのバージョンを指定してやることができる。
これが本当にMacでしか動かないでいいビルドなら設定全体に osx_image を指定してやればいい。つまり、こう。
language: ruby rvm: - 2.1 - 2.2.3 - 2.3.0 - ruby-head - rbx os: - osx osx_image: xcode7.3 matrix: allow_failures: - rvm: ruby-head - rvm: rbx
しかし我々はLinuxでも走らせたいのでこれでは困る。ちなみに上記の設定で単純に Linux でも動かすようにすると*5、以下のようになる(ビルドがこけてるのは別の理由)。もしかしたら実害はないのかもしれないが、キモいしギョッとするのでこれはイヤだ。
ということで、環境の組合せを直接指定することになって、これは matrix のもうひとつの機能であるところの includes を使う。
language: ruby # http://rubies.travis-ci.org/ # See here for osx_image -> OSX versions: https://docs.travis-ci.com/user/languages/objective-c matrix: include: - rvm: 2.1.10 os: linux - rvm: 2.2.4 os: linux - rvm: 2.3.0 os: linux - rvm: ruby-head os: linux - rvm: 2.1.0 os: osx osx_image: xcode7.3 # OSX 10.11 - rvm: 2.2.4 os: osx osx_image: xcode7.1 # OSX 10.10 - rvm: ruby-head os: osx osx_image: xcode 7.3 # OSX 10.11 allow_failures: - rvm: ruby-head
これは勝手な想像だけど、Travis CIの内部的には rvm や os の組合せから matrix を作っているんじゃないかなーという気がする。で、この指定方法はその内部表現(に近いもの)を直接指定していることになる、んじゃないかな、たぶん。
やること自体はこれだけだけど、探し回ってもあんまり良い設定サンプルが無かったので、ここに書いておこうと思った。これで求めているビルド環境の組合せが作れた。
なお、matrixの元になる掛け算対象の設定項目リストは、それぞれの言語のドキュメントページにその名も "Build Matrix" として項目がある。
GradleでMaven Centralにライブラリを公開する
前にこんなエントリを書いたが、このときはmavenから実行してた。今回はgradleから。参考にしたエントリはいくつかあったけど、ずばりというのがなくてつらかった。あれこれ試して組み合わせた。
はじめてのmaven central 公開 - たごもりすメモ
あとMaven Central(というかSonaType OSS)もちょっと変わってるのでそのへんも。なお環境は適当なMacに適当にgradleを入れた。以上。
gradle環境の設定
"$HOME/.gradle/gradle.properties" に行う。
org.gradle.daemon=true signing.keyId=DEADBEEF signing.password=mypassphrase_for_gpgkey signing.secretKeyRingFile=/Users/tagomoris/.gnupg/secring.gpg sonatypeUsername=tagomoris sonatypePassword=mypassword_for_sonatype
最初の1行は普通の便利設定で、他はJarの公開用。
パスワードやパスフレーズを毎回コンソールで聞かせる設定とかにする流儀もあるみたいだけど、とりあえずパス。ベタ書き。ここに書いておけばgradleの maven plugin が読んでくれる。
build.gradle の記述
がんばった。全体はここにあるので、ポイントをいくつか抜き出して書く。
apply plugin: "java" apply plugin: "application" apply plugin: "maven" apply plugin: "signing" group = "net.isucon.bench" archivesBaseName = "isucon-bench" version = "0.1.1" description = "Benchmark framework for Web applications, especially for ISUCON"
java と application プラグインはビルドと実行用のもの、maven と signing がmaven centralでの公開用のもの。その下はパッケージのメタデータ宣言用。あとで使う。
maven-publish プラグインというのもあるみたいだったが、あれこれ試したところそれは自分で maven repository server を作って運用するようなときに、そこにpushするためのものっぽい。code signingと組み合わせるとうまく動かないという話が多くて、自分もやってみたところどうもダメな感じだった。なんかものすごい色々コード書けばできる的な話もあったけど、何のためにプラグイン使ってんのか分からなくなる的な展開が見えて避けた。
//set build variables based on build type (release, continuous integration, development) def isDevBuild def isCiBuild def isReleaseBuild def sonatypeRepositoryUrl if (hasProperty("release")) { isReleaseBuild = true sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" } else if (hasProperty("ci")) { isCiBuild = true version += "-SNAPSHOT" sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" } else { isDevBuild = true version += "-SNAPSHOT" } repositories { mavenCentral() }
普通にビルドしただけでSonaTypeに公開されたらたまらんのでリリース作業用の設定を明示的に行うように変更。CIの場合はSNAPSHOTを公開用とは別の場所に上げるようにしている。まあCIまわしてないんだけど……他で(ちゃんとやるケースで)真似したくなったときのために。
dependency, compileJava, mainClassName, run あたりは普通に java, application なビルド/実行のためのものなので割愛。javadocJar, sourcesJar, artifacts は見ればわかるので割愛。
signing { required { isReleaseBuild } sign configurations.archives } uploadArchives { repositories { if (isDevBuild) { mavenLocal() } else { mavenDeployer { if(isReleaseBuild) { beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } } repository(url: sonatypeRepositoryUrl) { authentication(userName: sonatypeUsername, password: sonatypePassword) } pom.project { name 'isucon-bench' packaging 'jar' description 'Benchmark framework for Web applications, especially for ISUCON' url 'http://www.example.com/example-application' scm { url "scm:git@github.com:isucon/isucon-bench-java.git" connection "scm:git@github.com:isucon/isucon-bench-java.git" developerConnection "scm:git@github.com:isucon/isucon-bench-java.git" } licenses { license { name 'MIT' url 'https://opensource.org/licenses/MIT' } } developers { developer { id 'tagomoris' name 'Satoshi Tagomori' email 'tagomoris@gmail.com' } } } } } } }
このへんも見ればだいたいわかるが、丸っと書いてあるページがなくて難儀した。開発時ビルドにはスキップする設定で、書名とかパッケージのメタデータとかが書いてある。
この設定を書いておいたら、あとはコマンドでは "gradle clean && gradle uploadArchives -Prelease" すればSonaTypeのリポジトリにアップロードまでやれる。単に uploadArchives するとなんか変なゴミが含まれたJarが作られてしまった気がしたのでいちおう今のところはこうしている。それは不要だとか、綺麗なJarを一発で作る方法があったら教えてほしい。
まあなんにしろ、これでうまくいく。
$ gradle clean && gradle uploadArchives -Prelease :clean BUILD SUCCESSFUL Total time: 0.863 secs :compileJava 注意:入力ファイルの操作のうち、未チェックまたは安全ではないものがあります。 注意:詳細は、-Xlint:uncheckedオプションを指定して再コンパイルしてください。 :processResources UP-TO-DATE :classes :jar :startScripts :distTar :distZip :javadocJar :sourcesJar :signArchives :uploadArchives Could not find metadata net.isucon.bench:isucon-bench/maven-metadata.xml in remote (https://oss.sonatype.org/service/local/staging/deploy/maven2/) BUILD SUCCESSFUL Total time: 1 mins 3.332 secs
なんか通知ぽいものが出ているが、これはSonaTypeのリポジトリが新しくなって以降出る(ことがある)ようになったもの。とりあえず無視してよい。
SonaType Nexus Repositoryでの手続き
これが変わったところ。先程のコマンドでSonaTypeのリポジトリには格納された(stagingになった)はずだが、そこから先の処理をワンステップやらないといけない。新しいWeb UI (Nexus Repository)が登場する。
なお以下のページに案内がある。スクリーンショットとかあって前よりだいぶ分かりやすいので、読むとよい。
で、具体的にはこのページを開く: https://oss.sonatype.org/index.html#stagingRepositories
認証は作成してあるSonaTypeのアカウントで通れる。(開いてなければ)左側のメニューから "Staging Repositories" をクリックすると、いろいろ見られるリストの中に自分がアップロードしたっぽい名前があることを確認する。
選択すると上のメニューで "Close" と "Drop" のボタンが有効になるはず。なったら "Close" をクリックして、ダイアログから "confirm" する。このときパッケージになにか問題があるとエラーになるらしい(なったことがない)。問題がなければ該当の staging repository の状態が closed になる。問題なければそのまま "Release" ボタンが有効になるので、押してリリース。
完了したらリポジトリの管理チケットに完了したよとコメントを書くと、staging -> release の手続きの自動化を有効にしてもらえて、一件落着。リリースされたら、Nexus Repository内でパッケージ名で検索したとき、該当バージョンが releases 下にあるように表示されるからわかるはず。
CROSS 2016に行ってきた&しゃべってきた
先週金曜に横浜大さん橋ホールであったCROSS 2016に行ってきた。上記のセッションにスピーカーの一人として誘っていただいてたので、参加して、日本で・国をまたいで仕事をするということについて思うところをしゃべったりとか。
これは日本で働くか米国に移住するかみたいな二択では決してなく、いろいろ日本にいながらやれることはあるし、逆に最低限やっておかないといけないことももちろんある*1、でもまあ技術がちゃんとあっていいコード書けることも大事だよね、とか思いながらしゃべっていたが、ちゃんと伝わったかなあ。
いっしょに登壇してた @kuenishi さんの以下のエントリに @hasegaw さんのtweetが引用されてるけど、そうそう、そういう感じなんだよ! という気はします。
エンジニアCROSS 2016で登壇してきました - kuenishi's blog
興味があったら上記CROSSのセッションページに動画があるので、そちらをどうぞ。
CROSSについて
大勢の参加者がいてすばらしいことだけど、セッションのテーマが働きかたとか転職とかにだいぶ寄っているような気はして、(自分もそういう系ので話しておいてなんだけど)だいぶ食傷気味になる、という気がする。これが求められているということなら、自分の好みのカンファレンスとはちょっと違ってきたなあ、という感想になった。
あともうとにかく音響がひどい。自分たちがしゃべっているとき、比較的近くでデイリーポータル(?)の企画かなにかがすごい音量でアナウンスしててつらかった。後ろのほうの席の人、こちらの話し声が聞こえてなさそうだったしなあ。ホールの雰囲気が面白いのはわかるけどあれじゃ本末転倒だ。
終わったあと近くのビアバーで飲んでたけど、途中から合流した某社の若い人になんかおっさんくさいことを説教っぽくしゃべってしまった気がする。おっさん力の向上がつらい。
2015年を振り返ってみる
今年はとにかく仕事してた……。ということで、Blogエントリ数を見てみるとこれまでの数年に較べてだいぶ少ない。
転職した
大手インターネットサービス事業者から、いろいろあって結果的には西海岸のスタートアップ(の東京オフィス)に移った。
tagomoris.hatenablog.com
tagomoris.hatenablog.com
今年のいちばん大きな変化だったのは間違いない。このあとUSオフィスに少し行ってたり、この「いろいろあって」のときに考えてたことをエントリに書いたりもしていた。
結局その後に仕事をしていてこれまでと全く違う環境になったし、書くコードの種類や性質、考えかたなんかもけっこう変わったなという自覚はある。
あとこれまでに較べて格段に英語を使うようになった。おかげでだいぶ慣れてきたなとは思う。
書いたコード
OSSのコードはこれまでのプロダクトのメンテナンス系が多い、けど、Dockerのfluentd logging driverを書いたりmsgpack-rubyのメンテナをやることになったり、そこそこ新しいこともやってはいたかな。あんまり大物ではないけど、自分にとっては新しいものだったりそれなりに影響する範囲も広かったりで、面白い経験にはなっている。
- Shib v1について - たごもりすメモ
- Norikra v1.3.0: 大幅なアップデート - たごもりすメモ
- Docker 1.8 に Fluentd logging driver が入りました - たごもりすメモ
- LogStashからTreasure Dataにデータを投入する - たごもりすメモ
- LogstashからFluentdにデータを転送する - たごもりすメモ
あとはISUCON関係。これはあとで。
仕事関連だと社内向けの実装を直すことが多かったのがあって、ほとんどそういうエントリは書かなかったな。Hiveの挙動を一部乗っ取るためのコードをどこにどう書けばいいか、みたいな他社では役に立ちそうもないノウハウはだいぶ得たけど……。
発表やら執筆やら
海外のカンファレンスが2件、そのほか国内ではYAPC::Asia, RubyKaigi, 秋デブサミなどなど。秋デブサミのエントリは書き損ねてるな。7月まで大きいイベントや出張は皆無だったのに、8月からいくつも続いたのでだいぶばたばたしてた。いっぽう初めてヨーロッパに(2度も!)行けたのはいい経験だった。
- #norikra meetup 2 をやってきた - たごもりすメモ
- "Docker and Fluentd"の話をした - たごもりすメモ
- JRubyConf.EU と Eurucamp に行ってきた - たごもりすメモ
- YAPC::Asia Tokyo 2015 にいってきた&しゃべってきた - たごもりすメモ
- java-ja.OSS でOSSの話をしてきた - たごもりすメモ
- EuRuKo2015に行ってきた - たごもりすメモ
- RubyKaigi 2015に行ってきた&しゃべってきた - たごもりすメモ
割と概論や考え方についての話をするケースが多かったので、まあこれはこれで悪くはないんだけど、もうすこしコードの話をしたいなあと思ってはいる。そのためにはコード書かないとなー。いや書いてるんだけど、なんというか、もうすこし話にしやすいやつ……。
あと依頼があってWEB+DB PRESSにひとつ記事を寄稿した。個人的には記念にしたいくらいの思い出になりそう。
ISUCON
ISUCON5の出題をやった。9月末に予選、10月末に決勝というスケジュールを調整していたときは準備にたっぷり時間をかけられるはずだったんだけど、仕事の予定がズレ込んで、1年で(仕事だけで)いちばん忙しいという時期にISUCONの準備や各種発表が全部重なるという感じで、死にそうな2ヶ月になった。たぶんこれまでの人生でいちばん忙しくしてたと思う。
まあ、いろいろ不手際があって申し訳なかったとはいえ、いちおう成功したと言っていい終わりかただったのではないかとは思う。個人的にもJava8のコードをだいぶ書いたりで得たものは大きかった。ベンチマークツールはフレームワークになってるので、もうすこし直してちゃんと単独のプロダクトとして出すつもり。ちょこちょこまだ直している途中。
あれこれ
その他、ウケた記事もあった。YAPC::Asiaでの発表からの一連の流れはちょっと感激なくらいで、いろいろ考えてることをぼろっと外に出すと面白いことが起きることもある、といういい例になったかもしれない。
tagomoris.hatenablog.com
tagomoris.hatenablog.com
加えて転職の時期とISUCON後で2回も morisnite をやったり、いろんな人にお世話になってるなあ、と本当に思うことが多かった。みなさん本当にありがとうございました。
来年
Fluentd v0.14に向けたPlugin APIの作り直しを4〜5月頃に途中までやってたんだけど、その後に社内向けの大きい仕事にかかりきりになっていて済んでない。正直あれがblockerになってるので、まずいなあとは思ってる。2016年の最初の大きい仕事としてこれをやるつもり。
他に社内向けにはHadoop/Hiveよりも少しインフラっぽいことや便利ツールまわりをやろうとも思ってるので、そのへんの技術をさわってみることも増えると思う。というか、増やしたい。今年は春にひたすらChefと格闘してたくらいで、その後にそっちをあまり見られなくなってしまった。
あとは某分散処理をゴニョゴニョ。これは形になるのはもうちょっと先になるかな。面白いコードをあれこれ書ける予定があれこれあるので、楽しみたいと思う。
LogstashからFluentdにデータを転送する
このエントリはFluentd Advent Calendar 2015の12月25日分の記事です。4日遅れ!
で、本題ですが、もうタイトルそのまんま、logstash-output-fluentd を作りました。
logstash-output-fluentd | RubyGems.org | your community gem host
tagomoris/logstash-output-fluentd · GitHub
How to install / use
先日書いたものと全く一緒ですが、いちおう書いておきます。
とりあえずインストール。
$ bin/plugin install ../logstash-output-fluentd-0.1.0.gem Uninstalling logstash-output-fluentd Validating ../logstash-output-fluentd-0.1.0.gem Installing logstash-output-fluentd Installation successful
そしたら以下の設定ファイルを用意します。stdinから読み込んで 127.0.0.1 (のデフォルトポート 24224)に送るやつ。
input { stdin {} } output { fluentd { host => "127.0.0.1" tag => "logstash" } }
起動すればもう使えます。簡単ですね!
$ bin/logstash -f example.conf Settings: Default filter workers: 2 Logstash startup completed line 1 line 2 line 3 line 4
Yay!
注意点など
まだごくシンプルに指定したホストの in_forward に向けてデータを投げ付けるだけですが、まあ、使えなくもないかなというところです。Fluentdのforwardプロトコルにはいろいろと機能があるんですが、そのへんやover SSL*1やらはまだ全くしておりません。また複数ホストへのロードバランスやら死活確認(つまりHA)やらも対応しておりません。
そのうちやろうかなあ、どうしようかなあ。
バッファは logstash-output-treasure_data と同じでオンメモリに適当に持っているだけです。デフォルトは短めで50イベントもしくは5秒。これも flush_size および flush_interval で制御できます。正常にシャットダウンすればflushされるけどその時に失敗したり異常終了したりしたらイベントが失われるのも同じです。
まとめ
Enjoy logging!
*1:つまりin_secure_forward対応
LogStashからTreasure Dataにデータを投入する
このエントリは(2日遅れましたが)Treasure Data Advent Calendar 2015の12月25日分の記事です!
みなさんTreasure Dataにログ入れてますか? FluentdやEmbulkで入れてますよね?
しかし世界的に見てFluentdはまだまだ最有力のソフトウェアとは言えず、特にUSやヨーロッパのスタートアップ界隈ではLogStashが強い勢力を誇っています。Treasure Dataにログを投入するためだけにFluentdをインストールするのには抵抗のある向きもそちらには多いのです。
ということで、しょうがないから作りました! 本日から使えます。
logstash-output-treasure_data | RubyGems.org | your community gem host
tagomoris/logstash-output-treasure_data · GitHub
ただし現状ではdev releaseのライブラリに依存しており、この後ちゃんとメンテナンスリリースが出せるよう直すつもりです。また公式のlogstash-pluginsに入れてもらえるようにするつもりです。2016年かな。
How to install / use
LogStashを手元に展開したら、以下のコマンド一発でインストールできます。
$ bin/plugin install logstash-output-treasure_data Validating logstash-output-treasure_data Installing logstash-output-treasure_data Installation successful
そして設定ファイルを用意します。以下の設定は stdin を用いて標準入力から1行ずつ読み込み message というフィールドに入れ、そのイベントを treasure_data プラグインを用いてTreasure Dataの指定したデータベース/テーブルにアップロードします。(API Keyやデータベース名、テーブル名は実際に使用するものに置き換えてください)
input { stdin {} } output { treasure_data { apikey => "0/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" database => "dbname" table => "tablename" } }
準備できたら起動するだけです。コンソールにメッセージを適当に入力し、終わったら Ctrl-D で入力を終端できます。終端したら自動的にflushされ、Treasure Dataへのアップロードが行われます。
$ bin/logstash -f ~/example.conf Settings: Default filter workers: 2 Logstash startup completed line 1 line 2 line 3 line 4 line 5 Logstash shutdown completed
簡単ですね! やった!
注意点
LogStashはソフトウェアの機能としてはバッファリング機構を持っておらず、そのため logstash-output-treasure_data では他の多くのプラグインにあわせる形で、オンメモリでのキャッシュを行っています。
Treasure Dataにあまり細かい単位でbulk importを行うのは非常に効率が悪いため、現在はデフォルトでは10,000レコード、もしくは5分毎にflushを行うことになっています。*1
通常の手順でLogStashをシャットダウンすればその際にflush/uploadが行われますが、プロセスの異常終了などが起きた場合はメモリ内のデータは失われるのでご注意ください。
まとめ
Happy Logging!
*1:これらの設定は flush_size および flush_interval で制御できます。
WEB+DB PRESS Vol.90にコラムを書いた
明後日ですね、12月23日発売の WEB+DB PRESS Vol.90 の15周年記念エッセイ「私を変えたソフトウェア」にて2ページ、記事を書きました。
たまに(?)このblogでもエモいアレを書いたりしますが、なんかそういうやつです。原稿料をもらってこういうのを書いたのは初めてです。たぶん。
内容は本誌を読んでいただきたいんですが*1、要するに今現在に毎日ミドルウェアを読み書きして生活するようになったきっかけは何だったか、という話です。きっかけひとつというわけでもなく幾つか段階があるなあと思ったので、それについてつらつら思い出話のようなものを書いてます。
改めて読んでみると最後の1ステップ(現職への転職)が抜けているのでそこに何の迷いもなかったようになってますが、もちろん実際はそんなことはありません。が、まあそれはソフトウェア開発者としての転機というよりは人生どう生きるかみたいな部分なので、いいかなと思います。
しかし他の3氏がものすごくて、Rubyの人とHSPの人とLispの人ですよ……。ひええ。あと他がみんな1990年以前の話なのに、自分のやつだけ2010年代になってます。並んでるソフトウェア名も Emacs、スペースインベーダー、Lisp と並んで "FluentdとNorikra" とか、うああ。*2
他の方よりもうちょっと卑近な例ということで、これはこれで存在意義があるに違いないと自分に言い聞かせてます。
他の記事だと自分には特に特集2のドラクエXの話が興味深くて、ほほうKTにCassandraにExadataとな……みたいな感じです。まだざっと目を通しただけなのでじっくり読みたい。大規模MMORPGのバックエンドについて詳しく雑誌に書かれるとはすごい時代だなと思いますね。
ということで、興味があれば読んでみてくださいな、という話でした。