たごもりすメモ

コードとかその他の話とか。

RubyConf 2014にいってきた&しゃべってきた

出していたproposalが通ったので Rubyconf2014 に行ってきていた。旅費および会期中の宿泊費は現勤務先のLINE株式会社に出してもらいました。いつもいつもありがたいことです。

サンディエゴの会場付近はとにかくリゾート地っぽい感じで、あちこちに背の高いヤシがぽんぽん立っており、空も海も青いし、なるほどこれは国民性も変わろうというものだ、という感じ。初のアメリカ行きがこれだったので、USに対してだいぶ変なバイアスがかかった可能性がある。

そこでまたNorikraの話をした。何回目だと言われるかもだけど、英語だとまだ2回目だったし、英語圏でのカンファレンスでは初めてだったので……。

人によっては*1多少ウケたっぽいのでよかったよかった、ということにする。が、これ以上無闇に話しにいってもしょうがない気もするので、この活動は当面ではここまでかなー。

RubyConfに参加しての感想とか印象に残ったセッションとかは、ちょうどSFにいったときにゲストに呼んでもらった rebuild.fm 68 でしゃべったので、そっちを聞いていただくのが書く面倒がなくていいです。
ぶっちゃけて言ってしまうと、技術の話を聞くなら日本国内のカンファレンスのほうが面白いなあ、という印象。だからたぶん、ああいうところには、そもそも異なる文化についての見聞を広めるつもりで行くのが正しいんだろうと思う。

サンディエゴは海軍に縁の深い場所ということで、退役した空母ミッドウェーが丸ごとそのまま博物館として公開されてたりして、これがまた超面白かった。日本では絶対に見られない感じで非常によい。

食事は……まあ、選べばおいしいところはある、かなあ、というくらい。写真のメキシコ料理屋はすごくおいしかったが、この前日に行ったメキシコ料理屋はいまいちだった。あとスパゲティ専門店に行ったら、噂に漏れ聞いた「アルデンテという言葉を理解しているのはイタリアの外には日本人しかない」という言葉の信憑性が大幅に増した。

今回はせっかく太平洋を渡ったので、休暇をくっつけて日程を延ばし、ベイエリアにもちょっと行ってきた。写真はMountain Viewでフラグ立てとばかりに行ってきたコンピュータ歴史博物館。超面白かった。あれは一度行くといいが、閉館まで2時間半という時刻に行ったのがちょっと残念だった。半日丸々確保しておくべきだった。

そのほか、サンフランシスコ市内の知り合いに連れていってもらってステーキ食べた*2り、別の人に案内を頼んで市内観光したり、前述のとおりrebuild.fmに出ることになって録音したり。なんかいろいろだった。皆様ほんとうにお世話になりました。おかげさまで短いながらも濃くて楽しい滞在でした。

ただまあ、あそこに自分が住むことはないだろうなあ、と思う。出掛けると東京は自分に合っているということを再認識できるなあ。

あとはアレだ、アメリカ西海岸といえばクラフトビールですよ。

ビールビールビールビールビールビール。計8泊の滞在で30杯(20数種類)くらいは飲んだと思うが記憶が定かではない。

鞄を亡くすこともコーヒーをかぶることもなく無事帰ってこられました。よかったよかった。

*1:既にストリーム処理をなにがしかやっている人とかだと

*2:これがまためっちゃうまかった

最近のHadoop distcpについて

Hadoopクラスタ間でデータを移動するdistcpについては実はHadoop2系で新しくなっており*1、いろいろ機能が増えている。
たとえば以下のようなコマンドが実行できる。

hadoop distcp -i -m 20 -pb -bandwidth 2 webhdfs://cluster.old.local/path/of/data/dir hdfs://cluster.new.local/path/of/data/dir

このコマンドラインには、従来のdistcpでは不可能だったふたつの変更が含まれている。

bandwidth

前は指定できなかったが、今は mapper あたりが使う帯域をMB/sec単位で指定できる。べんり。

webhdfs://

前はバージョンの異なるHadoopクラスタ間でdistcpする場合、新しいクラスタでdistcpを実行し、古いクラスタ(source側)のパスは hftp:// で指定しろ、というのが普通であった。
Hadoop2だとwebhdfsが使えるようになっているはずなので、これを使うがよい、ということになっているようだ。

なおいつものようにoza先生に教えてもらった。

source側のデータ総量が大きくなっていると以下のような例外が出て失敗する状況になっており、大変困っていた。oza先生によるとhftpのglobのバグのように見える、とのことだった。

14/11/27 15:59:44 ERROR tools.DistCp: Exception encountered 
java.lang.NumberFormatException: For input string: "p"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Short.parseShort(Short.java:118)
	at java.lang.Short.valueOf(Short.java:174)
	at java.lang.Short.valueOf(Short.java:200)
	at org.apache.hadoop.hdfs.web.HftpFileSystem$LsParser.startElement(HftpFileSystem.java:437)
	at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:509)
	at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:182)

これが webhdfs:// を使うと何の問題もなく動いた。すばらしい。グレート。

*1:なおHadoop1系でも distcp2 と指定すれば使えるケースがあるようだ

MRv2/Tezで簡単にクエリのベンチをとった

Hiveしか使ってないので以下のオプションを設定するだけで使える。楽。

SET hive.execution.engine=tez;

なお HDP 2.1 with Hive 0.10, Tez 0.4 での話です。クラスタの概要は以下の通り。

  • master x3
  • slave x20
    • Xeon(R) CPU E5-2630L v2 (6core 12Threads) x2
    • RAM 64GB
    • HDD x12

シナリオ

データの流れとしては以下のようなシナリオを想定する。

  1. 外部から非圧縮plain text tsvでHDFS内のファイルにデータが書かれる
  2. LOADで hourly テーブルに読み込む
  3. dailyで INSERT により daily テーブルに書き込む
    • このときファイルフォーマット変換や圧縮を同時に行う
    • hourly テーブルの変換済みパーティションおよび元の生データは削除する
  4. 普段のクエリは daily テーブルに対して行う

テーブルのスキーマは以下のような感じで、まあ普通にあるWebサーバのアクセスログが入っていると思おう。あとパース時にいくつかフラグを立ててBOOLEANを追加してある感じ。

-- hourly
CREATE TABLE hourly (
       hhmmss STRING,
       vhost STRING,
       path STRING     COMMENT 'with query',
       method STRING,
       status SMALLINT,
       bytes BIGINT    COMMENT 'response body size',
       duration BIGINT COMMENT 'micro sec',
       referer STRING,
       rhost STRING,
       userlabel STRING,
       agent STRING,
       flag BOOLEAN,
       f_redirection BOOLEAN,
       f_errors BOOLEAN,
       f_internal BOOLEAN,
       f_miscfile BOOLEAN,
       f_imagefile BOOLEAN,
       f_bot BOOLEAN,
       query STRING    COMMENT 'request query param json',
       opts STRING     COMMENT 'log optional values json'
)
PARTITIONED BY (service STRING, yyyymmddhh STRING, loadseq STRING)
ROW FORMAT DELIMITED
 FIELDS TERMINATED BY '\t'
 LINES TERMINATED BY '\n'
STORED AS TEXTFILE;

-- daily
CREATE TABLE daily (
       hhmmss STRING,
       vhost STRING,
       path STRING     COMMENT 'with query',
       method STRING,
       status SMALLINT,
       bytes BIGINT    COMMENT 'response body size',
       duration BIGINT COMMENT 'micro sec',
       referer STRING,
       rhost STRING,
       userlabel STRING,
       agent STRING,
       flag BOOLEAN,
       f_redirection BOOLEAN,
       f_errors BOOLEAN,
       f_internal BOOLEAN,
       f_miscfile BOOLEAN,
       f_imagefile BOOLEAN,
       f_bot BOOLEAN,
       query STRING    COMMENT 'request query param json',
       opts STRING     COMMENT 'log optional values json'
)
PARTITIONED BY (service STRING, yyyymmdd STRING)
STORED AS RCFILE;

評価

INSERT

hourlyからdailyへのINSERTにかかる所要時間を計測する。
以下のクエリを MRv2 と Tez で共に用いる。MRv2用のオプションも含めてすべて共通で設定する*1。日時については適当な範囲で指定。

SET hive.exec.dynamic.partition.mode=nonstrict;
SET hive.optimize.sort.dynamic.partition=false;
SET hive.exec.reducers.max=4096;
SET mapreduce.job.reducer=2048;
 
FROM hourly
INSERT OVERWRITE TABLE daily PARTITION (service,yyyymmdd)
  SELECT hhmmss,vhost,path,method,status,bytes,duration,referer,rhost,userlabel,agent,
         flag,f_redirection,f_errors,f_internal,f_miscfile,f_imagefile,f_bot,
         query,opts,
         service, 'yyyymmdd'
  WHERE service LIKE '%' AND yyyymmddhh LIKE 'yyyymmdd%'

非圧縮で約650GB程度のデータを対象にこのクエリを実行したところ、以下のような結果だった。

  • MRv2
    • 17分48秒 (1068秒)
  • Tez
    • 11分53秒 (713秒)

713/1068 = 0.667.. なので、まさに1.5倍ちょうど速くなっている。

SELECT

続けてこのようにして生成したdailyテーブルに以下のような集計クエリをMRv2、Tezでそれぞれ実行し、結果が出るまでの時間を計測した。このときは特に mapreduce.job.reduce などのオプションは指定していない。

SELECT
  hhmmss, COUNT(*) AS c
FROM access_log1
WHERE service LIKE '%' AND yyyymmdd='20141114'
GROUP BY hhmmss
ORDER BY c DESC LIMIT 10

結果は以下の通り。

  • MRv2
    • 436秒
  • Tez
    • 179秒

179/436 = 0.410.. なので2.5倍速い。このクエリはMapReduceだと2段のmapreduceに分解されて実行されることはHive大好きなみなさんならクエリを見た瞬間にわかると思うが、やはりそのようなクエリにおいては1ジョブで効率的に実行できるTezが更に有利になる。

そしてこのような単純な集計クエリは非常に多く実行されている。

まとめ

Tezいいんじゃないですかね! たまに落ちるという話も聞きますが!

ただResourceManagerからHistory URLたどっていっても何も見えないとか、いろいろ実行状況わかんないのは自分の設定のせいなのかなあ。これはトラブル時にだいぶ困る感じがしますね。

*1:当然だが hive.execution.engine=tez は除く

Hive dynamic partition insertsにまつわるいくつかの問題と対処について

だいぶ前のHiveの機能準拠で作ってたクラスタを大幅に作り直したので、ついでにETL処理をdynamic partition inserts一発でやればMapReduce 1ジョブで済んで超効率的に! やった! と思ったらいくつかハマったのでメモ。

なおdynamic partition insertsについては説明が面倒なので公式Wikiの該当ページを読むとよい、が、簡単に言うとHiveでパーティションにINSERTするときにINSERT先のパーティション指定をSELECTクエリの出力により行う、というもの。

なお断りがない限りは HDP2.1 with Hive 0.13 の環境で試したものとする。(移行元はCDH4)

クエリの書き方

単純に言うと、パーティションとして指定したいカラムは SELECT 句の最後に置かなければならない。

簡単に言うと year=INT/month=INT/day=INT というパーティション階層になっているテーブルにINSERTするとき、month と day をSELECT句から取り出したい場合には、以下のようにクエリを書かなければならない。

INSERT OVERWRITE TABLE tbl PARTITION (year=2014, month, day)
SELECT
  data1, data2, data3, data4,
  month, day
FROM source_table WHERE ...

dynamicに指定したいカラムだけを書いておく。

ハマった

いちばんわかりにくくハマったのは INSERT クエリを流したとき、なぜか毎回いくつかのReducerだけが他のReducerよりもはるかに長く動き続けており、全体の処理の規模が大きいときはそれこそ永遠にお待ちください状態になる、という挙動。

最初は MRv2 on YARN 移行に伴うものかと思ったが、違うっぽい。よく見てみると毎回 shuffle で莫大な時間を消費しているようだった。*1設定をあれこれ見ていたらこんなのが見付かった。

hive.optimize.sort.dynamic.partition

  • Default Value: true in Hive 0.13.0 and 0.13.1; false in Hive 0.14.0 and later (HIVE-8151)
  • Added In: Hive 0.13.0 with HIVE-6455

When enabled, dynamic partitioning column will be globally sorted. This way we can keep only one record writer open for each partition value in the reducer thereby reducing the memory pressure on reducers.

Configuration Properties - Apache Hive - Apache Software Foundation

実は最初にCDH4のクラスタでほぼ同じことをやったときは問題なく終わってて、HDP 2.1 のクラスタでやったときにはまった。このせいっぽい。Hive 0.13 においてはこのオプションがデフォルトで有効になっており、dynamic partition insertsを使うときに自動的に出力をsortにかける。しかもその record writer は必ずひとつのreducerとなる。
このため、出力先のパーティションごとに考えたときにこのサイズが巨大なものがあると、sortがいつまでも終わらず非常に長い時間がかかる、ということらしい。

気付いてこれを false にしたらパーティション全体に対するsortが行われなくなり、reducerが走らずそのまま出力がパーティションに書かれるようになったので、妙に時間がかかるようなこともなく終わるようになった。めでたしめでたし。

なお Hive 0.14 ではデフォルト false になっている。やっぱみんなハマったんじゃないかな。

他にもいくつか

Hive on MRv2 on YARNで気になったところがあったので書いておく。

hive.exec.dynamic.partition
  • Default Value: false
  • Added In: Hive 0.6.0
  • Whether or not to allow dynamic partitions in DML/DDL.

有効にしましょう。

hive.exec.dynamic.partition.mode
  • Default Value: strict
  • Added In: Hive 0.6.0
  • In strict mode, the user must specify at least one static partition in case the user accidentally overwrites all partitions. In nonstrict mode all partitions are allowed to be dynamic.

簡単に言うと、dynamic partition inserts時、strict になっていれば、少なくとも1段のパーティションについてはクエリ内で明示的に指定されていなければならない(SELECT結果を使わずに指定しなければならない)。

これのパーティション指定用カラムをトップレベルからすべて動的に指定したい場合、このモードを nonstrict として指定してやる必要がある。

SET hive.exec.dynamic.partition.mode=nonstrict;

INSERT OVERWRITE TABLE tbl PARTITION (year, month, day)
SELECT
  data1, data2, data3, data4,
  year, month, day
FROM source_table WHERE ...

トップレベルのパーティションを無尽蔵に増やしてしまうと管理がものすごく大変になるから、親切心でデフォルトを strict にしてあるんだと思う。DROP PARTITIONも面倒になるし。覚悟がある人だけ nonstrict にしましょう。

hive.exec.max.dynamic.partitions
  • Default Value: 1000
  • Added In: Hive 0.6.0
  • Maximum number of dynamic partitions allowed to be created in total.

1回のdynamic partition insertsクエリで生成されるパーティション数の上限。日付とかでpartitionを作っている分には超えないが、何かしらの商品コード等を用いてパーティション作成する場合にはけっこうひっかかるかもしれない。注意。
ひっかかったらクエリ失敗時に例外にメッセージ出てるはずだから、増やそう。

hive.exec.max.dynamic.partitions.pernode
  • Default Value: 100
  • Added In: Hive 0.6.0
  • Maximum number of dynamic partitions allowed to be created in each mapper/reducer node.

1回のdynamic partition insertsクエリにおいて、ひとつのmapper/reducerで作られるパーティション数の上限。前項と同じでひっかかるケースはあると思う。注意。ひっかかったら上げる、でいいと思う。

hive.exec.reducers.max と mapreduce.job.reducer

hive.exec.reducers.max はデフォルトが999で、この数字は mapreduce.job.reducer よりも優先される。これがわかってなかったので、なぜか毎回 reducers が999になり、あれー? と思っていた。注意。 oza先生に教えておもらいました。多謝。

が、これは前述の hive.optimize.sort.dynamic.partition=false により意味がなくなった。

結論

みんなで hive.execution.engine=tez を指定しよう!

*1:これMRv1 JobTrackerの画面に較べてMRv2の画面に出てこなくなったので、だいぶ長いこと気付かなかった

ISUCON4 いってきた&勝ってきた! #isucon

連覇だ! ヒャッホウ!!!


特にkazeburoさんのエントリに最終的な状況についての詳細が書いてありますので、ぜひそちらもどうぞ。sugyanは自分で力不足とか言ってますが、ISUCON本戦という場で、業務でほぼ使ったことがないはずのRedisメインのコード改造をごりごりやってちゃんと動かす人なので、チーム外のみなさんは騙されてはいけません。それできるの超すごいんやで。

主催のLINE株式会社、あれこれ提供いただいていたデータホテル改め株式会社テコラス様、問題作成担当 @mirakui, @rosylilly, @sora_h の3氏、本当にありがとうございました。たのしかった!

だいたいこんなんで

大雑把に時系列の経緯だけ書くとこんな感じ。

  • 開始-12時頃
    • ssh鍵の導入など環境の準備、コード読む、全体の設計変更の議論
  • 12時-14時過ぎ
  • 14時半-18時半過ぎ
    • NW帯域詰まってどうにもならず呪いの言葉を吐き続ける
    • kazeburo親子を見てほっこりする
    • 散歩
  • 18:43-19:00
    • Cache-Control!

実際のところ14時半には複数台構成への変更が完了し1Gbpsをほぼ使い切った状況までいってたようで、その他の、スコアに影響がほとんどないけど普通のWebアプリとしてはやったほうがよさそうな変更をやりながら悶々と考えていたのが17時頃まで。
ほぼ諦めかけて投げやりな気分になったところで33万点を目にし、現実的に動画を配信しないでスコアを上げる方法があるらしい、ということで可能性を片っぱしから列挙しては自分で却下したりしてた。その中に Cache-Control も含まれていたが、この時は設定ミスで気付かず。もはや万策尽きた、というところになりかけたが、最後の最後でなんとか間に合ったのでした。

なおlocal ipを --hosts に指定できること、それによりNW帯域を稼げることは気付いてなかった。Cache-Control のほうが間に合わなかったら5位に入ってなかったんじゃないかと思う。(ので結果発表が怖過ぎた。)

そういえば最後が劇的すぎて忘れてたけど、途中で突然ベンチマークツールが登録に来なかった advertiser id についてレポートが無いといってFAILにされてた時間帯があったんだけど、あれはなんだったんだろ。裏側でベンチ用データがバグってたとかなんじゃないかと思ってたんだけど。だいぶ長いことデバッグログを入れたりしながら唸ってたんだけど、そのうち勝手に直った。

変更した構成

最初に考えた構成で、結局最後までこのまま。帯域が詰まらなくてrps勝負になったときに威力を発揮した構成なんじゃないかと思うんだけど、確認できず。

  • 動画は(例によって)WebDAVでnginxから配信する
    • アップロードリクエストが来たらレスポンスを返す前にnginx全台にpush
    • /asset リクエストが来たらnginxが直接返す
  • isu401 は1CPUなので、基本的にはRedis専門サーバとする
    • ただしappも立てておいて、広告の登録やレポート集計にはこちらを使う
    • レポートのリクエストはベンチ走行中も来ていたようなので、その負荷の隔離先
  • isu402, isu403 で /ads, /ad, /count, /redirect の処理
    • /ads -> /ad のリダイレクトを行わず、直接jsonを返してもベンチは文句を言わなかったのでそうした*1
    • ログはファイルに吐かれていたのをRedisを使うように

他に悶々時間帯にささやかなチューンとして、公平性ボーナスの条項を満たし次第*2、あとはバイト数が最も小さな動画広告だけを返す、などということもしていた。ほぼ結果に影響なかったみたいだけど。

いちおう何がどうなってもいいよう、isu401 のnginx設定やperlのコードは isu402/3 と全く同じ状態で動くようになっている。こういうふうにしておくと、ボトルネックが移動して問題を再考しないといけない場合にも便利。

ブレークスルー案として検討した項目

動画が1ファイル5.3MBから5.4MB、1Gbpsを使い切ったあたりでremoteなスコアが8000前後、アクセスログによるとだいたい700回くらいのレスポンスを返していたようだった。
で、このスコアを33倍以上にするために動画リクエストをどうすればいいかと考えたときの候補。

  • いきなり206を返す(ダメ)
  • レギュレーションによると25%のERRORが許可されているので、動画の25%をエラーにしつつclickを増やす
    • 動画を返さないとそもそもclickに来ない、ダメ
  • 動画ファイルのmd5コリジョンを発見
    • ムリ……というかアップロードされてくるのでいちいちそんなもん見付けてられるか
  • 動画の先頭とかの部分だけ返してチェック通すとか?
  • ETag, Cache-Control (最終的にはこれだった)
  • ベンチ走行中の1分間のあいだは動画リクエストが来たら全部 sleep して60秒待たせる
    • 60秒経過後に溜まりに溜まったリクエストに順にレスポンスを返せば、60秒間で稼いだコネクション数だけのスコアが稼げるのでは?
    • と思ったけど33倍ってことはつまりベンチ実行合計時間が33分以上になるってことで、いやそりゃダメだろと
    • 結局その戦略はsleepしてる間にレポートのリクエストが来て不整合でFAILするらしい

他にもいくつかあった気がするけど忘れた。ひたすら会場内をうろうろ散歩したり座ってぶつぶつ独り言いいながら検討してた。

問題設定について

Cache-Control つけるだろ、というのはまあ冷静になればわかるんだけど、もうちょっとヒントがあっても良かったかなあという気はする。例えばレギュレーションの impression の説明のところに、有効なインプレッションとして認められるレスポンスコードを列挙しておくとか。

終わってみると、Cache-Controlに気付くか気付かないかで消費するにはもったいない問題だったんじゃないのという気がして、そこはちょっと残念。超大量のアクセスをどう捌くかこそがISUCONのいちばん脳汁出るところで、その戦いがもっと多くのチームでできればよかった。redirectをひとつ削減したやつとかもそこで効くはずだったし、Redis::Jet や Chobi が火を噴いただろうに。
とりあえずうちのチームの得点、最後のベンチ走行時にどこがボトルネックになってたかすら全く見られてない*3んだけど、たぶんちゃんと調整すれば workload 8 で100万点いったんじゃないかなと思う。

参考資料

今年もマスタリングnginxにはお世話になりました。

が、もうちょっといい解説があった気がするんだよなー、なんだっけかなー、と30分くらい悩んだところで気付きました。自分でちょっと前に書いたSoftware DesignのNginx特集の記事だ!!!!!!!

これまじ分かりやすくて超便利でした。みんなも買うといいよ……と思ったけど、Amazonだと中古だけだな。PDF版を買うといいですね。いい時代だ。

Software Design 2014年7月号 | Gihyo Digital Publishing

まとめと今後の話

なんにしろ、面白かった。じつにすばらしいイベントでしたね!

で、2回勝ったので、まだあんまりちゃんとチームで合意をとってないけど、もうこのチームで出ることはないんじゃないかなーという気がする。出るなら自分も、もうちょっと他の人とやるISUCONを経験してみたい気がするし。何より勝ち逃g(ry

で、来年も開催できるらしいですね! 2回出場側にまわって、そろそろまた出題側に戻ってもいい気がしてきた。気力が回復した。w
これもまた別途確認ですが、やれれば出題担当しようかなーと思う。ちょうど勝ったことだし。

また ISUCON Makers Casual Talks というイベントを近々やろうかなと思います。いろいろ話したいことがあるんですよ! 出題をやった側にしかわかんない苦労とかがですね! そういうのをビールを飲みながら赤裸々に話したい!

さて

来週はRubyConfでサンディエゴだ!

*1:スコアに関係ないHTTPリクエストの削減

*2:つまり該当slotの広告を1度ずつ返したら

*3:なんと去年に続いて今年も……。。。。

Hokkaido.pm #12 いってきた&しゃべってきた

https://atnd.org/events/57038



YAPC::Asia Tokyo 2014 ベストスピーカー2位ということで地方pmに3回行かせてもらえる権利のうち1回目を行使しました。ありがとうございます。
主催の @aloelight さん、会場を提供してくださったクリプトン・フューチャー・メディア株式会社さま、ありがとうございました。ボーカロイド作ってる会社!!! とかテンション上がっておりました。

で、せっかく行ったので自分も自分にできる話をしました。

ちょっと散漫な内容になりましたが、思うところをあれこれ突っ込んであります。障害対応とはつまり過負荷障害の対応シミュレーションだったんだよ!!!

という冗談は(半分くらい本気ですが)置いておくとしても、運用系モニタリングとサービス改善のためのデータ分析は実際に技術領域としてかぶるところがあるなあとは最近よく思っています。

みなさんにもぜひ、どちらかの目的だけでもいいから始めてみていただきたい。

お手軽なデータ収集からでも。けっこう効果があると思います。

最近はFluentdとかもあるしね!



当日聞いた話、内容自体はもう普通に普段東京で聞くような勉強会の話と変わらず、面白かった。あんまり普段聞かない話を聞いた気がする。

SECCON CTFを主にやっています、みたいな人の話は普段東京の勉強会だと聞く機会があまりなく、コミュニティが細分化されちゃうとそういう弊害があるなあ、というか、もっと他所のコミュニティに顔を出せよという話なんだけどいやいや、みたいなことをぐるぐる考えていた。

あと会場で聞いてみたところ、やっぱりWebサービス業者は東京に集まっているのか、そういう仕事の人はあまり多くないみたい。とはいえ請負一辺倒というわけでもなく、パッケージソフトウェア開発の人も……って、よく考えたらクリプトンの人がいたからそりゃそうだな。今頃気付いた。懇親会でもその話をしてたのに。

あとは聞いたところによるとRubyのコミュニティ(?)はもうちょっと大きいらしい、とか、結局コミュニティ運営は主催者のやる気が、みたいな話も。そのあたりはどこでも変わらないなあ。

今回のHokkaido.pmはATND上で13人という参加者数で、ちょっとさびしい気もするが、そうは言ってもヒカリエのPerl Casualも100人集まらないし、時代なのかどうなのか、いやいや、みたいな話も。うーん。人数多くなるとそれはそれで開催が面倒になる部分もあるけど。

印象的だったのは函館や釧路からの参加者がいたということで、200kmとか400kmとか恐ろしい距離を勉強会のためにやってくるということ。すごい。そのへんの距離感は聞いてるとまったく理解できなくて、会話のなかでしばしば混乱してた。400kmって……。

YAPC::Asiaは日本のみならず世界中から開発者が集まってくるけど、国内のあちこちでも、こうやって近隣から人が集まってくるイベントがあれこれあるといいなあ、色んな人と話せるし、などと無責任に思う。都合が許すならそういうところでふらふら遊びに行きたい。(あと2回権利がある!)

まあそういう感じで、技術にまみれた旅行でした。天国かと思った。楽しかった。







あと2回権利があるので、ぜひ地方pmに呼んでください! 行きます!

はじめてのmaven central 公開

前置き:このエントリはJavaおよびJava周辺の*1開発環境に全く縁の無い人間が、可能な限り依存ソフトウェアを少なく手順をシンプルに保ったままやろうとしたものであり、知識・経験のある人にとっては全く最適な手段でなかろうことをお断りします。

先日のエントリ で書いたとおり woothee 1.0.0 をリリースした。Perl, Ruby, Node.js および PHP などはそれぞれの言語毎のモジュールリポジトリに登録されている。
が、Javaについては自分が Maven Central の勝手がわからず、されてると便利だよなーとは思いつつ放置していた。

が、なんと @making さんからMaven Central登録用の pull requestがきた 。きてしまった。これで最大の問題(xmlを書く)はおおむね解決されてしまったので、覚悟を決めて登録作業をすることにした。
せっかくやったので、アカウント登録からfirst releaseまでの手順をここにまとめてみる。

なお環境は以下の通り。

  • 適当なバージョンのMac OSX (10.8.5)
  • 適当なバージョンのJDK (1.7.0_55)
  • 適当なバージョンのMaven (3.2.1)
  • できる限りCLIで頑張る
  • XMLEmacsで編集する

JDKMavenは適当にインストールする。javacとmvnコマンドが動けば多分大丈夫。Mavenについては自分はtarballを展開してパスを通した。

なお英語のドキュメントはここにあるが、割とJavaに慣れた人向けのドキュメントな気がする。つらかった。

パッケージ名前空間を決める

Maven Centralへのモジュール登録は、人(アカウント)ごとにアップロードできるパッケージの名前空間が決まっている。Java書いてると出てくる package com.mycompany.softwarename; っていうアレ。
確保する必要があるので、何か決めよう。自分は tagomor.is っていうドメインを持ってたのでそれを使った*2。これ、誰がどうやって正当性を確認しているんだろうな……。

なお登録時に共同メンテナみたいなのも登録できるので、1パッケージを一人しかデプロイできないということではない。安心。

アカウント登録

誰がどのパッケージを登録できるのか、みたいな権限はOSSRH JIRAで申請して変更してもらう。ので、まずそのJIRAのアカウントを作る必要がある。このアカウント(ユーザ名/パスワード)は実際のバイナリのアップロード時にも使う。

https://issues.sonatype.org/secure/Signup!default.jspa

このアカウント作成はすぐ終わるはず。終わったらパッケージアップロード権限的なのをもらうためのチケットを作成する。

https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&pid=10134

SummaryとDescriptionは、なんか適当に書けばいいんじゃないでしょうか。自分の場合はこれだけど、プロジェクト名くらいしか書いてない。

groupId には前で決めたパッケージの名前空間を書く。例えば is.tagomor と書いておけば is.tagomor.** なパッケージは自由にデプロイできることになる。

Project URLとSCM uriはそのOSSプロジェクトのWebページとリポジトリURL。GitHubの場合は普通にViewのURLと .git のHTTPS URLを書いとけばいい。

Username(s) は共同開発者がいる場合はその人の JIRA username を書く。とりあえず自分だけでいいなら空欄。

Already Synced to Central は初めて申請するなら No でいい。これは多分既に申請通ってたりする分の修正とかの場合に選ぶものだと思う。

これを埋めたら Create する。すると誰かがなんか作業をやってくれる。ドキュメントには2日は待てみたいに書いてあるけど、自分の場合は8時間で完了した。

完了すると登録したメールアドレスに "Resolution: Fixed" と書かれたメールが来る。
なおメールの末尾には "please comment on this ticket when you promoted your first release, thanks" とも書かれてた。こうしておかないといけない事情が忍ばれる。

pom.xml の記述

これはやってもらいました! すまん!!!
https://github.com/woothee/woothee-java/pull/1

これも基本的には公式ドキュメントにある記述を pom.xml に入れていけばいいと思うんだけど、ひとつだけ問題があって、このままだとビルド時にGPG Keyを要求するので Travis-CI が mvn install したときにコケる。

ググったらStackOverflowの記事がヒットした。これをそのままパクり、普通に mvn install や mvn test したときにはGPG署名用プラグインを呼ばないようにpom.xmlを変更。これで Travis-CI がこけなくなる。また、この時点で mvn install やら mvn test するときに、まだGPGのことを考えなくて済む。

で、この状態でとにかくテストを通せるようにするとか、とか。

GPGのセットアップ

Maven Centralにバイナリを上げるにはGnuPGで署名する必要があるらしい。ので、インストールする。以下のURLから "Download GPG Suite" をクリックしてdmgをダウンロード、展開してインストールした。

https://gpgtools.org/

インストールすると勝手に起動したので Key -> Generate して鍵ペアを作る。Full NameとEmail addressを入れて Pass Phraseを入れるだけで基本的には終わり。
作ったら公開鍵の方は "Send public key to keyserver" しておく。

またインストールが完了したら、作業中の端末で "gpg2" コマンドが動くことを確認しておく。動かなかったらshellを起動し直すなどする。

アカウント・パスフレーズの設定

OSSRHやGPG鍵を使えるよう、Mavenの設定をしてやる必要がある。設定は settings.xml というXMLに書くが、これはユーザ毎にあるものらしい。自分の場合は ~/.m2/settings.xml に置けば読みこまれた。

OSSRHとGPGの設定が両方必要。まとめるとこんな感じ。

<settings>
  <servers>
    <server>
      <id>ossrh</id>
      <username>YOUR_JIRA_USERNAME</username>
      <password>YOUR_JIRA_PASSWORD</password>
    </server>
  </servers>
  <profiles>
    <profile>
      <id>ossrh</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <gpg.executable>gpg2</gpg.executable>
        <gpg.passphrase>GPG_KEY_PASSPHRASE</gpg.passphrase>
      </properties>
    </profile>
  </profiles>
</settings>

YOUR_JIRA_USERNAME, YOUR_JIRA_PASSWORD とか GPG_KEY_PASSPHRASE のところは自分のものを埋めること。
これを書いておけば maven plugin が勝手に読んでくれる。

デプロイ

以上で手順は完了。「pom.xmlの記述」で書いたとおり、指定したときにしかGPG署名をしないような記述にしたので、デプロイ時にはそのスイッチを有効にしてやる必要がある。

$ mvn clean deploy -DperformRelease=true

うまくいったら最終的にはこんな表示が出て終了するはず。

[INFO]  * Upload of locally staged artifacts finished.
[INFO]  * Closing staging repository with ID "istagomor-1002".

Waiting for operation to complete...........

[INFO] Remote staged 1 repositories, finished with success.
[INFO] Remote staging repositories are being released...

Waiting for operation to complete..............

[INFO] Remote staging repositories released.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:33 min
[INFO] Finished at: 2014-10-28T16:07:10+09:00
[INFO] Final Memory: 30M/363M
[INFO] ------------------------------------------------------------------------

この時点ですぐに以下のURLを見たところ、ちゃんとパッケージ名をたどったところにアップロードされていた。
https://oss.sonatype.org/content/groups/public/

うまくアップロードできたことが確認できたら、JIRAのチケットのほうにコメントを書いておく*3。自分の場合はこんな感じで適当に書いた。

I've just promoted my first release, woothee-java-1.0.0.jar:
https://oss.sonatype.org/content/groups/public/is/tagomor/woothee/woothee-java/1.0.0/

完了!

これで(特に問題がなければ)定期バッチによりmaven centralに同期されるらしい。作業の進捗を示すものだと思うけど、以下のようなメールが順番に来てた。たぶんみっつめが来たら全部の処理が完了。

  • "Nexus: Staging Repository Dropped"
  • "Nexus: Staging Completed"
  • "Nexus: Promotion Completed"

見事以下のURLから見えるようになってた。
http://repo1.maven.org/maven2/is/tagomor/woothee/woothee-java/1.0.0/

おつかれさまでした!

*1:特にIDEやGradleなどなど

*2:申請時の例はソフトウェア名を含めろという感じになってたので is.tagomor.woothee で申請したんだけど、処理する人が is.tagomor でいいよって変更した。いいのか。

*3:誰かの処理がこれで行われるのかな?