Kafka StreamsをSpring Bootで試してみた

この辺とか読みながら→ Introduction — Confluent Platform 3.1.0 documentation

Twitter4Jで適当に英語のツイートをとってきて、Kafkaに流し込む。(キーとかの設定はtwitter4j.propertiesに入れておいた)

@Component
public class TwitterToKafka implements CommandLineRunner {

  @Autowired
  private KafkaTemplate<String, String> template;

  @Override
  public void run(String... args) throws Exception {
    TwitterStream twitterStream = new TwitterStreamFactory().getInstance();
    twitterStream.addListener(new StatusAdapter() {
      @Override
      public void onStatus(Status status) {
        template.send("myTopic", status.getText());
      }
    });
    twitterStream.filter(filterQuery());
  }

  private FilterQuery filterQuery() {
    // 英語のツイートで"spring"を含んでいるものにしてみた。ちょうど良いくらいの量だったので。
    FilterQuery filter = new FilterQuery();
    filter.language("en");
    filter.track("spring");
    // filter.follow(TwitterFactory.getSingleton().showUser("bufferings").getId());
    return filter;
  }
}

myTopicって名前のTopicに流し込むようにしたので、今度はそれを処理する定義を書く。単語の数を数えてみた。これがWordCountStoreってストアに保存される。

@Configuration
@EnableKafkaStreams
public class KStreamConfig {

  @Bean(name = KafkaStreamsDefaultConfiguration.DEFAULT_STREAMS_CONFIG_BEAN_NAME)
  public StreamsConfig kStreamsConfigs(KafkaProperties kafkaProperties) {
    Map<String, Object> props = new HashMap<>();
    props.put(StreamsConfig.APPLICATION_ID_CONFIG, "testStreams");
    // Spring BootのAutoConfigurationに入ってるやつ使えばいいかなと思って
    props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
    props.put(StreamsConfig.KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
    props.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
    props.put(StreamsConfig.TIMESTAMP_EXTRACTOR_CLASS_CONFIG, WallclockTimestampExtractor.class.getName());
    // 動作確認用に1秒ごとにコミット
    props.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, "1000");
    return new StreamsConfig(props);
  }

  @Bean
  public KStream<String, String> kStream(KStreamBuilder kStreamBuilder) {
    KStream<String, String> stream = kStreamBuilder.stream(Serdes.String(), Serdes.String(), "myTopic");
    // @formatter:off
    stream
      // 単語に分割して
      .flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\\W+")))
      // 単語でGroupBy
      .groupBy((key, word) -> word, Serdes.String(), Serdes.String())
       // カウントを取る(KTable)
      .count("WordCountStore");
    // @formatter:on
    return stream;
  }

}

ついでに、KTableの中身を表示するのを書いてみた。

@RestController
public class StateStoreViewer {

  @Autowired
  private KStreamBuilderFactoryBean kStreamBuilder;

  @GetMapping
  public List<KeyValue<String, Long>> query() {
    final ReadOnlyKeyValueStore<String, Long> store = kStreamBuilder.getKafkaStreams().store("WordCountStore",
        QueryableStoreTypes.keyValueStore());

    final List<KeyValue<String, Long>> results = new ArrayList<>();
    final KeyValueIterator<String, Long> range = store.all();
    while (range.hasNext()) {
      results.add(range.next());
    }

    return results;
  }

}

ら、大量に出てきてしまった。しかも良くわからん感じ。

f:id:bufferings:20170608085740p:plain

でも、面白かったー!

KafkaのクライアントをJavaで試しに書いてみた。それとspring-kafka。

いつもありがとうございます。の気持ちとともにCLOVERを開いて、今日はKafkaのクライアントをJavaで試しに書いてみた。

d.hatena.ne.jp

KafkaTest1

かずひらさんと違うのは、localhostで立ち上げてるってところだけ。なのでこんなコードになった。ちょっとくらい触ろうかなと思って、シリアライザーとデシリアライザーをPropertiesに書いといた。

gist.github.com

なるほどなぁと思いながら写経した。設定とSerializer/Deserializerを指定して、KafkaProducer/KafkaConsumerをインスタンス化して、ProducerRecord/ConsumerRecordを送受信する感じか。

consumerも閉じるといいのかな?と思って一応閉じといた。

なんかよく分かんないのは、このConsumerのAUTO_OFFSET_RESET_CONFIGをearliestにしてるのに、2回目はもう取れてない感じ。group.idを変えたり、もういっかいProducer側を実行するとまた取れるので、オフセットがキープされてるってことなのかなぁ?Consumer Groupは生き残り続けてるってことなのかな?まいっか。

KafkaTest2

てことで、次はspring-kafkaのドキュメントを読んでみた。いや、というか逆で、最初にここを読もうとしてよく分かんなくてうろうろしてたらかずひらさんところにたどりついて「さすが師匠!」ってなったのであった。

http://docs.spring.io/spring-kafka/docs/1.2.1.RELEASE/reference/htmlsingle/#_very_very_quick

Spring Bootを使って依存関係を入れた。Kafkaが0.10.2.0なのでpom.xmlのpropertiesでバージョンを指定。

    <spring-kafka.version>1.2.1.RELEASE</spring-kafka.version>

dependenciesはこんなもんかな

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.kafka</groupId>
      <artifactId>spring-kafka</artifactId>
    </dependency>

で、コードを書き写してみた。KafkaTemplateがProducerをラッピングしてて、KafkaMessageListenerContainerがConsumerなのかな?

gist.github.com

KafkaTest3

それを、Configurationを使って書くとこうなる。

gist.github.com

@EnableKafkaをつけておくと、@KafkaListenerが使えるみたい。

KafkaTest4

Spring Bootの機能を使うとこうなる。基本的な設定はSpring Bootがやっといてくれる。EnableKafkaもなくて良いんかな。

gist.github.com

だから、送信する方は KafkaTemplate をDIすればよくて、受信する方は @KafkaListener をつけとくだけか。

実際のところ設定はBeanで定義しておくような気がするね。今日はこれくらいかな。おやすー。

追記

AutoConfigurationを見ておいた。なるほどな感じだった。

KafkaAutoConfigurationは、KafkaTemplateがあるときに有効になって、KafkaTemplate・ProducerListener(ログ出力)・ConsumerFactory・ProducerFactoryをBean定義してある。

https://github.com/spring-projects/spring-boot/blob/v1.5.3.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfiguration.java

設定はここに定義してあって、buildConsumerPropertiesとbuildProducerPropertiesを使う。

https://github.com/spring-projects/spring-boot/blob/v1.5.3.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java

んで、EnableKafkaの設定はここにあって、ConcurrentKafkaListenerContainerFactoryのBean定義もしてある。

https://github.com/spring-projects/spring-boot/blob/v1.5.3.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaAnnotationDrivenConfiguration.java

設定はプロパティファイルで済みそう?

だんだん開発スピードが遅くなっていくのをどうやってとめたら良かったんだろう?

先日、モブプロをやってきた。その中で、モブプロとは別で、いくつか感じたことがあって、今日はその中のひとつを思い浮かんだままにメモ。

bufferings.hatenablog.com

要件を満たすプロダクトをより早く出す

モブプロでTDDしながら、要件を満たすプロダクトをより早く出すことに集中してみた。例えば、第2ラウンドのお題はTDDBCなどでお馴染みの「自販機」。

「100円を入れてボタンを押すとコーラが1本買えること」

最初に「100円を入れてボタンを押すとコーラが1本買えること」と言われ。

assertThat(get(100), is("コーラ"));

みたいなテストを書いて。

String get(int money) {
  return "コーラ";
}

みたいな実装を書いた。爆速!

「200円を入れてボタンを押すとオレンジジュースが1本買えること」

次に「200円を入れてボタンを押すとオレンジジュースが1本買えること」と言われ。

assertThat(get(200), is("オレンジジュース"));

実装はこうなった。

String get(int money) {
  if(money == 200) {
    return "オレンジジュース";
  }
  return "コーラ";
}

その後

その後は

  • 200円でコーラが2本買えること→( ゚д゚)ハッ!「getOrangeJuice()とgetCoke()に分ける?」
  • コーラの在庫を追加することができること→( ゚д゚)ハッ!「・・・ざ、いこ・・・?」
  • 900円入れてコーラを2本とオレンジジュースを2本とビールを1本買って100円お釣りが出てくること→( ゚д゚)ハッ!「ボタンが必要・・・?てか、お、お釣り?」

とかなりながら、だんだん遅くなりながらも、要件を全て満たすだけのコードを書き続けた。

その結果として出来上がったもの

は、要件は満たすけど、色んなif文に溢れ、在庫を減らす処理はいくつかの場所に散らばって(たしかビールの在庫は減らなかったと思うw)、「あぁ・・・」という感じのものだった。

プロダクトオーナーのセリフ

僕らが「え?在庫なんてあるんですか?」というと、POは「ふつー自販機ならあるやろー」って言ってた。まぁ、自販機ならそりゃそうよなー、と思いつつ。でも普通のプロジェクトで「そんなの普通あたりまえでしょ?」とか言っちゃうPOだとたぶん開発うまくいかないだろうなーとか思いながら聞いてた。

それと、POはしきりに「聞いてくれていいよ」と言ってはくれてたけど、でも自分から「このプロダクトをチームと一緒に良くしたい!」という気持ちは見えなかったなぁw

チームのメンバーのセリフ

途中でメンバーの中では「色んな商品を取り扱えるように、Productクラスを作った方が良いのでは?」という意見が出てきた。その段階では、要望には、コーラとオレンジジュースしかなく、この後、本当に商品が増えていくのかどうかも分からなかった。

とか色んなこと

をバーっと1時間の中で経験して、みんなと「もうちょっと設計をちゃんとやった方が良かったかもしれないね」みたいな話もした。

帰り道

で、ぼーっと考えながら。「これって、うまくいかないプロジェクトの縮図っぽいなぁ」と思った。

  1. 僕らは、「自販機」というものが何をできるものなのかを知らない状態で開発を始めて、要望を満たすものを急いで作る。
  2. 次の要望くらいまでは大丈夫なんだけど、だんだん開発スピードが遅くなってくる。
  3. 動いている部分には極力触るなの精神で、if文で分岐を追加して新機能対応をする。
  4. その結果、例えば「在庫を減らす」という処理が散らばってしまい、バグを埋め込んでしまう。
  5. 本来、少しの変更で対応できそうなことが、その複雑さのためにすごく時間がかかるようになってしまう。

んで、おまけで、

  1. 最初の開発をやってた人がマネージャとかになる
  2. 「僕がやってたころは、もっと開発スピード速かったんだけどな」とか言ってる。
  3. その人しか知らない仕様やif文があって、結局全てその人に聞かないといけない状態になってる。

みたいなね。

じゃあどうすれば良かったのか?

  1. そういう経験をしてきた僕は「ちゃんと設計をしたらマシだったはずだ!」と、しっかり設計をしようとする。
  2. 「自販機」というものを誰も知らない状況で「これはジュースだけでなく、どんな商品でも購入できるように、汎用的に作らなければならない!」とか考え出しちゃう。
  3. これも必要かもしれない、これも今入れておかないといけない、とかでどんどん仕様が膨らんでいく
  4. だいぶ時間をかけてできあがったけど、ほとんどの機能が使われない
  5. 新機能を追加するのに、使われていない機能のテストもしないといけなくて、スピードがあがらない

あれ?(´・ω・`)

じゃあどうすれば良かったのか?

そのあいだなのかなー?

目の前のことに追われすぎない、かつ、分からない先のことを想像で考え過ぎない。

だから、何かに気づいたときに、それを元に全体をリファクタリングする。できるようにしておく。というのが大切なのかなって思った。

「在庫」という概念を発見したら「在庫をどうもたせるべきか」というのを考えてリファクタリング

たぶんそういう発見はブレイクスルー的なリファクタリングになるから、ため息をつきながら、プレッシャーでドキドキしながら、やる。

そのためには、スキルと勇気と変更容易性が必要。

というところなのかなー。

それと「自販機」というものを知って、早く小さくブレイクスルーを起こすために、POはもっと中から発言をすると良さそうかなー。

ということで

誰も知らない新しいものを作るときは「全員の知識を持ち寄って、変化(発見)に対応し続ける」というのができたら良いのかなーって思った。

おわり。

モブプロをやってみた。良さ。使いどころ。 #devkan

devlove-kansai.doorkeeper.jp

モブプログラミングやってきました!面白かったー。疲れたー。面白かったー。

モブプログラミングって?

チーム全員(プロダクトオーナー含む)が集まって、1人だけがコードを書いて、それがスクリーンに映しだされてて、その他の人みんなでやいやい言いながらものを作っていく。というスタイル。ルールは「ドライバー(コードを書いてる人)は、ナビゲーター(周りでやいやい言ってる人)に言われた通りにコードを書く。ドライバーが自分で考えてコードを書くのはダメ。」という感じ。

f:id:bufferings:20170529191606j:plain

やってみた

7人ぐらいのチームで、プロダクトオーナーからのお題に対してTDDで実装をやってみた。2部制でやったのだけど、第1部はKotlin + IntelliJ IDEAでローマ数字の計算を。Kotlin全く知らないし、IntelliJも全然慣れてない。第2部はJava自動販売機を。こっちもIntelliJだったな。1人7分で交代して次の人。だいたい1時間で一周して終了。

できたもの

ほとんどの人がKotlin知らないのに、なんか動くものが作れた。Javaの方も、動くものが作れたな。とは言っても、どちらも、とりあえず要件を満たして動くってだけで、うんこーど。せっかくの素振りだから楽しもうと思って!

おこったこと

今回はTDDの勉強会じゃないからそっちは置いといて。モブプログラミングで何が起こったか。と考えると、こんな感じかな:

お互いのことが分かった

コードのスタイルや、考え方、アドバイスのやり方、何を大事にしているか、どういう部分が苦手か、など。ペアプロと違うのは、それが僕じゃない別の誰かとの会話によってもたらされているということかな。僕だったら引き出せていないようなものを、別の人同士の会話の中から気づくことができた。

なぜそのコードになっているかを全員が知っている

今回は特にうんこーどを書いたんだけど、どういう経緯でそうなったのかが共通認識として持てている。もし、それなしにコードだけ見たら「はっ?」ってなるようなものも、「あー。あのときこういう経緯で進めたからねぇ。」って分かってる。

そのコードに対してどう思っているかを全員が知っている

書きながら「この辺はリファクタリングしたいね」とか「ここでもういっこジュースの種類が増えたらどうする?」「今はいいんじゃない?」とかそういう会話が出てくるので、全員がそのようなポイントについての共通認識を持てている。

技術的なことのキャッチアップができる

Kotlinのこと全く知らなかったので、僕がドライバーのときに「メソッドって何で始めるの?え?fun?」とか言いながら書いてて、そしたらナビゲーターで知ってる人が「そうそうそこでコロンだね」とか言いながら教えてくれて。Kotlin触ったことない別の人もそれを聞きながら「へー」とか言って、その人の番ではすらすら書いてた。

わかったこと

ドライバールールで意図が伝わる

ドライバーは自分で考えたらダメなので、全ての考えがナビゲーターからドライバーに会話で伝えられる。なので、意図が全員に伝わる。

ドライバーやるので置いてかれない

全員が交代でドライバーをするので、少なくとも自分の番のときに参加しなきゃいけない。なので、ナビゲーターとしてもしっかり理解しておこうと思うし、理解できてないことを自分の番で聞くことができる。

ペアプロよりも余裕がある

何人もいるので、ペアプロよりも余裕がある。他のナビゲーターにまかせて、自分は気になることを調べておくとかもできる。僕はビール飲んでた。

どういうときに使う?

以上をふまえて、どういうときに有効だろう?と考えてみる。けど、うまくまとまんないや。

全員で認識を合わせたいとき

は良さげ。ペアだけじゃなくてチーム全体として、結果としての情報じゃなくて、過程の情報を共有することができるから。

全員の知恵を持ち寄りたいとき

も良さげ。それぞれ得意分野が違ってて、それを合わせて一つのものを作りたいなってとき。

リーダーが自分の考えや方針をチーム全員に伝えたいとき

とかにも良さげ。そのリーダー1人が書きながら説明するよりも、モブプロ形式の方が全員手を動かしながら覚えられるし。あと、そのリーダーがドライバーのときには、他のメンバーが指示出さなきゃいけないからねw

不確定要素が多い段階でのモブプロ

も良さそうかな。POに「この場合はどうなるの?」「こっちはどうなるのが正解?」とか聞きながら進めて、それに対して「汎用的にするのは一旦待って、今は目の前の要件だけを考えるようにしようか」ってPOも含めてチームでそのやり取りの共通認識が持てるし。

細かくものを詰めていくというよりは

どこが課題か、何を考えないといけないか、どんな技術的な難しさがあるか、とかを全員で共通の認識を持つ、という感じに使うのが良いのかなと思った。その後はタスクの洗い出しをして、それぞれまたペアで作業をしていくような感じ。

今回は特にそっちに振り切ってやったんだけど、そうやって一旦どういう実装ができるかをばーって出して、それに対して「在庫どう持とうか?」「ジュースはEnumじゃきついね。動くけど。」とか一通りの経験と議論を共有したうえで、「よし!今日作ったものは全部捨てて、明日のモブプロでちゃんと実装していこうぜー!」みたいにできそうかなーって思ったりした。

贅沢だったなー!

ぷーさにさん、と、よーはつさんの二人がリードしてくれてるのも贅沢。みんなありがとー。面白かったー。癒やされたー!

今すぐは、やらないだろうけど、いつか「今、共通認識を築きたい」って思った時にすぐにできるように引き出しには入ったかな。

いつも通り

こざけさんはいつも通りでした。

お互いをつなぐものについて

こう…。

メンバーに「もっと責任感を持って仕事して欲しい」とか。マネージャーに「もっと窮屈じゃない感じで仕事させてほしい」とか。そういう状況を良くするために最初にやるのは、お互いに信頼すること。かなぁ。

とか、ぼーっと思った。

お互いに、良いものを作りたいと思ってる、良い環境にしたいと思ってる。ということを信頼する。だけど、お互いに完璧じゃなくて、まだまだ学ぶことがあるし、失敗もする。ということを受け入れる。

お互いを信頼でつなぐと、支え合って良くしていける感じある。

そこに踏み込まずに、楽な方にながれちゃうと、ルールでつないでしまうことになるのかなぁ。マネージャーとしてこうあるべき、とか、メンバーとしてこうするべき、とか。

そうすると、失敗をしないように。今の自分が安心できる範囲の中で過ごしてしまいそうで。それって、学び続けてもまだまだ学ぶことがあるなぁって思ってるので、僕は困る。

今は信頼しあえてる環境にいる(と思ってる)ので、困ってない!

信頼する。ってのは「裏切られるのが怖い」とか言ってた10年前の僕には分からなかったことで。今少し分かる気がするのは、妻と娘たちのおかげなんだろう。

とか、ぼーっと思った。今週は東京。がんばる!

Headless ChromeをDockerに入れてGebで遊んでみた

昨日書いたんだけど、Kafkaを触ろうと思ってるんだよ?でも、触ろう触ろうと思ってると、違うものが目に入ってくるのであった。ということで

Headless Chromeで遊んでみた

Kafka一切関係なく、この記事を見かけたから。

Getting Started with Headless Chrome  |  Web  |  Google Developers

この辺のこともあるので、ちょっと見とこうかなって。

Phantom.jsのメンテナー、プロジェクトの将来に疑問を呈し、その座を降りる

ただ、今手元にある環境でごにょごにょするのもなんか嫌だなぁ・・・って思ったので、無駄にDockerに詰め込んでGebで遊んでみた。そして、そのせいで疲れた(ヽ´ω`)

できあがったものは

これ。

https://github.com/bufferings/sandbox-gebheadlesschrome

docker-composeで起動すると

$ docker-compose run app

Chrome(beta)とChromeDriverとGroovyがインストールされた状態で、インタラクティブモードで起動する。

root@4013e872287a:/geb# google-chrome --version
Google Chrome 59.0.3071.29 beta

root@4013e872287a:/geb# chromedriver --version
ChromeDriver 2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5)

root@4013e872287a:/geb# groovy --version
Groovy Version: 2.4.11 JVM: 1.8.0_131 Vendor: Oracle Corporation OS: Linux

Chromeを動かしてみる

これで動くかな?と思って、試しに叩いてみたらエラーになった。

# google-chrome --headless

ので、エラーメッセージからぐぐって、 dockerでGUIのアプリを使用する(chromium) | ぴあっこ作業日誌 を参考にして --no-sandbox をつけてみた。Dockerで実行してることが原因みたいね。

# google-chrome --headless --no-sandbox

一歩進んだけど、まだエラーが出るので Getting Started with Headless Chrome  |  Web  |  Google Developers をもういちど良く見てみたら、途中の例には書いてないけど、最初の方に --disable-gpu が今は必要って書いてあった

# google-chrome --headless --no-sandbox --disable-gpu

(∩´∀`)∩ワーイエラー消えた。

キャプチャとってみる

Getting Started with Headless Chrome  |  Web  |  Google Developers のサンプルに --no-sandbox --disable-gpu をつけて実行

# google-chrome --headless --no-sandbox --disable-gpu \
                --screenshot --window-size=412,732 https://www.chromestatus.com/

したらscreenshot.pngってファイルが作られてた(rootの600だったので666にした)。

あら・・・。

f:id:bufferings:20170503175152p:plain

もう一回実行したら撮れた。撮れるときと先走るときがあるみたいね。

f:id:bufferings:20170503175202p:plain

Gebで実行

Groovy入れといたのはGebで実行してみようかなと思って。なので、Geb動作確認用のファイルも置いといた。実行するとGebがHeadless Chromeを使って、 Googleのホームページを開いてキャプチャとる。そんな感じ。初回実行時は依存ライブラリーをとりにいくのでしばらく待たされる。

# groovy Hello

Gebからの画面キャプチャは、フォントが入ってないからこんなんだけど。撮れた。

f:id:bufferings:20170503164750p:plain

Chromeのキャプチャ

ヘッドレスかどうかに関係なく、Chromeのキャプチャは見えてる範囲だけみたいよね。

↓のサイトに「こんな風にして全画面キャプチャーツールとして使えるよ」ってあるけどよく分かってない。

Using headless Chrome as an automated screenshot tool

僕の作ったDockerfileの注意点

キャプチャーとかの出力ファイルがrootで作られるので、ホスト側から触るときにちょっとめんどくさい。

これ以降はたぶん全く忘れてるであろう1年後の自分への解説。

SDKMAN!

SDKMAN! the Software Development Kit Manager

を使って、JDKとGroovyを入れた。全然知らなかったんだけど簡単だったよ。

# Java & Groovy
RUN curl -s "https://get.sdkman.io" | bash \
    && echo "gvm_auto_answer=true" >> ~/.sdkman/etc/config \
    && /bin/bash -c "source /root/.sdkman/bin/sdkman-init.sh && sdk install java && sdk install groovy"

Chrome

この辺を参考にしてHeadlessに対応してるベータ版を入れた。

https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile

# Chrome
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - \
    && sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' \
    && apt-get update \
    && apt-get install -y google-chrome-beta

ChromeDriver

ここを参考にして入れた。

docker-chromedriver/Dockerfile at master · RobCherry/docker-chromedriver · GitHub

# ChromeDriver
RUN CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` \
    && mkdir -p /opt/chromedriver-$CHROMEDRIVER_VERSION \
    && curl -sS -o /tmp/chromedriver_linux64.zip http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip \
    && unzip -qq /tmp/chromedriver_linux64.zip -d /opt/chromedriver-$CHROMEDRIVER_VERSION \
    && rm /tmp/chromedriver_linux64.zip \
    && chmod +x /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver \
    && ln -fs /opt/chromedriver-$CHROMEDRIVER_VERSION/chromedriver /usr/local/bin/chromedriver

Grape

Gebを試しに動かしてみるのにGrapeってGroovyの依存管理機能を初めて使ってみた。CLOVERを読みながらやってみた。

Groovyの簡易依存関係管理ツール、Grapeを使ってみる - CLOVER

@Grapes([
    @Grab("org.gebish:geb-core:1.1.1"),
    @Grab("org.seleniumhq.selenium:selenium-chrome-driver:3.4.0"),
    @Grab("org.seleniumhq.selenium:selenium-support:3.4.0")
])

import geb.Browser

Browser.drive {
    go "https://google.com/"
    report "sample"
}.quit()

Grapeがライブラリを置いておくディレクトリ(~/.groovy/grapes)は、ボリュームマウントしておくことにした。2回目以降取りに行かなくていいように。

Geb

この部分は

Browser.drive {
    go "https://google.com/"
    report "sample"
}.quit()

Googleのホームページを開いて(go)、"sample"って名前でキャプチャ(htmlとpng)を取って(report)、ブラウザを閉じる(quit)。ってことね。

GebConfig

んで、今回のメインのGebConfigだけどオプションを3つ指定して起動してるだけ。

import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.remote.DesiredCapabilities
import org.openqa.selenium.chrome.ChromeOptions

reportsDir = "target"

driver = {
  ChromeOptions options = new ChromeOptions()
  options.addArguments("--headless", "--no-sandbox", "--disable-gpu")

  DesiredCapabilities capabilities = DesiredCapabilities.chrome()
  capabilities.setCapability(ChromeOptions.CAPABILITY, options)

  new ChromeDriver(capabilities)
}

docker-compose

は、マウントとか指定するのが面倒だったから書いただけ。

version: '3'
services:
  app:
    build: .
    working_dir: "/geb"
    volumes:
    - ./geb:/geb
    - ./grapes:/root/.groovy/grapes

面白かった

Dockerじゃなかったらあっさりしてたと思う。おしまい。

2017-05-04 追記

やっぱり動かんかった。

root@42dbeb2a9909:/geb# google-chrome --no-sandbox --disable-gpu --screenshot --window-size=412,732 https://www.chromestatus.com/
root@42dbeb2a9909:/geb# [0503/235453.940351:ERROR:nacl_helper_linux.cc(311)] NaCl helper process running without a sandbox!
Most likely you need to configure your SUID sandbox correctly

Gebの方はこんな感じになった。

Caused by: org.openqa.selenium.WebDriverException: unknown error: Chrome failed to start: exited abnormally
  (Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.8.0-49-generic x86_64) (WARNING: The server did not provide any stacktrace information)

Kafka触るのにVagrantとAnsibleで砂場環境を準備

Dockerだと関係ないところでハマりそうな気がしたのでVagrantにしてみた。ファイルはここに置いといた。

https://github.com/bufferings/sandbox-kafka

Vagrant + Ansible Local Provisioner

以前に触った時は、Ansible2系が出たばかりで、ちょっと手を入れなきゃ動かなかったけど、もう大丈夫だった。のでこんな風にすっきり。

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"

  config.vm.provider "virtualbox" do |vb|
     vb.memory = "2048"
  end

  config.vm.provision "ansible_local" do |ansible|
    ansible.playbook = "playbook.yml"
  end
end

AnsibleでKafkaのQuickStartができる程度の環境を準備

っても、ファイルダウンロードして解凍するだけ。

playbookの書き方のルールとかよくわかってない。こんな感じに落ち着いた。

- hosts: all
  become: yes

  vars:
    scala_version: "2.11"
    kafka_version: "0.10.2.1"

    install_dir: "/opt"
    symlink_name: "kafka"
    kafka_name: "kafka_{{ scala_version }}-{{ kafka_version }}"
    kafka_download_url: "http://ftp.jaist.ac.jp/pub/apache/kafka/{{ kafka_version }}/{{ kafka_name }}.tgz"

  tasks:
  - name: ensure openjdk8 present
    apt:
      name: "openjdk-8-jdk"
      state: present
      update_cache: yes

  - name: ensure kafka present
    unarchive:
      remote_src: yes
      src: "{{ kafka_download_url }}"
      dest: "{{ install_dir }}"

  - name: ensure symlink present
    file:
      src: "{{ install_dir }}/{{ kafka_name }}"
      dest: "{{ install_dir }}/{{ symlink_name }}"
      mode: 0755
      state: link

ということで

クイックスタートする前に今日は力尽きた。動作確認を少ししただけ。

https://kafka.apache.org/quickstart

参照

以前にAnsible Local触った時:

bufferings.hatenablog.com

以前にKafka Streamsを触った時:

bufferings.hatenablog.com

それを読んだ時の僕: