gradle で Berkshelf こないだの続き

こないだの続きで最終的に Docker のイメージを作るんですけど、途中 Dockerfile じゃなくて Chef で作ってます。同じ構成コードで Docker のイメージ以外も作りたくて。というわけで Berkshelf です。

repositories {
  mavenCentral()
}

buildscript {
  repositories {
    jcenter()
  }

  dependencies {
    classpath 'org.gradle.api.plugins:gradle-docker-plugin:0.2'
  }
}

apply plugin: 'docker'
apply from: '../berks.gradle'

import org.gradle.api.plugins.docker.tasks.*
import org.gradle.api.plugins.docker.tasks.container.*
import org.gradle.api.plugins.docker.tasks.image.*

docker {
  serverUrl = 'http://127.0.0.1:5555'
}

こないだのに apply from: '../berks.gradle' ってのを追加しました。他のビルドプロジェクトでも使いたい。検索したらあんま見当たらなかったので作ってますけどみんな一緒のことをしてるからうまいことプラグインして切り出したいです。まだ方法がわからない。

task berks_install(type: Exec, group: 'berks') {
  inputs.file  'Berksfile'
  outputs.file 'Berksfile.lock'
  commandLine  'berks', 'install'
}

task berks_update(type: Exec, group: 'berks') {
  commandLine 'berks', 'update'
}

task berks_vendor(type: Exec, group: 'berks', dependsOn: berks_install) {
  inputs.file berks_install.outputs.file
  outputs.dir 'berks-cookbooks'
  commandLine 'berks', 'vendor'
  doFirst {
    new File('berks-cookbooks').deleteDir()
  }
}
$ gradle berks_vendor
:berks_install UP-TO-DATE
:berks_vendor UP-TO-DATE

BUILD SUCCESSFUL

Total time: 2.606 secs

これで良い感じと思われます。 UP-TO-DATE って言われると気分が良いです。 berks_update はその必要があるときに直接実行…でいいのかな…。ただこれまでこれ毎回やってたので(毎回やらない運用だと、 cookbook の変更を取り込み忘れてその後のビルドにかかる20分くらいを無駄にする上にはまる)、一回一回のビルドずいぶん早くなりました。

Packer で Chef な Docker に Dockerfile 重ねていきたいので Gradle その1

なにしろいま表題のような感じでいろいろ試してるんです。そうしたら、本当に38.9℃の熱を出して2日間寝込んでしまいました。

寝込んでいる間 Kindle Paperwhite で Manning の『 Gradle in Action 』を途中まで読みました。この作者の Benjamin Muschko さん、 Gradleware 社の方なんですが、なんと Docker のプラグインを書いています。

検索すると出てくるもう1本は build に特化したもので、 Muschko さんのは、うたい文句通り動けば Docker API の gradle クライアントです。ちなみに、僕のところではうたい文句通り動いていない部分があるので Groovy 勉強してパッチ見てもらいたいと思います…。

というわけで入れてみました。 Gradle 初めてなので、おかしなことやってるかもしれません。

Gradle のインストール

バイナリを落としてきて、 /opt/gradle に置いて、 /opt/gradle/bin/gradle に /usr/bin/gradle から ln -s しました。手癖です。

Docker デーモンが tcp 聞くように

Muschko さんのプラグインは Java の Docker API クライアントを使ってるんですが、こいつがまた tcp でしか Docker デーモンと話さないので、 /etc/init.d/docker 開けて、立ち上げてるところで

nohup $exec -d -H tcp://127.0.0.1:5555 -H unix:///var/run/docker.sock $other_args &>> $logfile &

しました。

build.gradle

repositories {
  mavenCentral()
}

buildscript {
  repositories {
    jcenter()
  }

  dependencies {
    classpath 'org.gradle.api.plugins:gradle-docker-plugin:0.2'
  }
}

apply plugin: 'docker'

import org.gradle.api.plugins.docker.tasks.*
import org.gradle.api.plugins.docker.tasks.container.*
import org.gradle.api.plugins.docker.tasks.image.*

docker {
  serverUrl = 'http://127.0.0.1:5555'
}

task info(type: DockerInfo)

次に build.gradle 書きました。一旦ちゃんと動くのか確かめたかったので info を試します。 root のスコープにも repositories 書いてるのは、 gradle-docker-plugin が依存している Docker API Java クライアントを落としてくる用。 serverUrl は http であっております。

$ gradle info
:info
Retrieving Docker info.
Debug                : false
Containers           : 0
Driver               : devicemapper
...

Docker Hub

次にプライベートなイメージを pull とか push とかしたいと思いましたがもう記憶に無いですがログインしろよとか出た気がします。 docker -H :5555 login します。 $HOME/.dockercfg が出来ます。ちなみに上の設定だと socket 経由なら sudo しますよね。もし万が一自分しかユーザのいないホストなら… sudo docker login もしておくとはまらないがちですが、やらないほうが良さそうです。

次に、ここがなんかアホな話なんですが、 Java クライアントが $HOME/.docker.io.properties を作れというので作ります。中身は Docker Hub のアカウント情報です。生パスワード様ご登場なので chmod 600 です。

docker.io.username=oogatta
docker.io.password=unko
docker.io.email=oogatta@gmail.com

これで理論上 pull と push ができますが、ちなみに、自分のところでは push は出来ても pull 失敗します。一見成功してるように見えるんですが、 /var/log/docker 見ると失敗してます。同じ状態で docker -H :5555 pull したら成功するので、 API 経由だと失敗するみたい。 Docker デーモンを -D で起動してログ見ると、全く同じリクエストを Docker Hub に飛ばして、 API からのだけこけているという。良い感じですね。

そもそも Docker Gradle プラグインが依存している Java クライアントのバージョンが古いので、つぶされてないバグがありそうです。この辺は気づいた人がつぶそうって感じでしょうか。ちなみに pull / push はそれぞれ

task pull(type: DockerPullImage) {
  imageId = 'oogatta/kidoibuki'
}

task push(type: DockerPushImage) {
  imageId = 'oogatta/kidoibuki'
}

こんな感じです簡単です。設定できる項目はプラグインのソース見るのが早そうです。

そうそうあと、最後になってしまったんですが、僕のところでは build も動かないんですよね…。これも一見通ってるんですが、 write tcp 172.31.2.33:46951: connection reset by peer みたいな感じです。なんか API クライアントのせいっぽいですね。

まあそんな感じでぐだぐだなんですが、 gradle の依存設定便利だし groovy 読みやすいし berkshelf なんかも一緒に出来たし得たこともありました。が、思ったより長くなってもう寝る時間なので続きは明日。

Dockerfile でどうやるのと思って調べた小さい話

このところ Docker やってます。前にやってたのが去年の10月でさすがに半年以上立ってるので進化していて Docker Hub ができてほんと助かります。 private な image の共有はサーバ自分で立てろ、とかだったので…。

Dockerfile 内で、 build を実行している環境の環境変数を参照したい

docker - Get environment variable value in Dockerfile - Stack Overflow

cat Dockerfile | envsubst | docker build -t my-target -

これ冴えてますよねえー。 envsubst yum も apt も gettext 入れると一発で入りました。ただ、標準入力から Dockerfile の内容を受け取る上の書式だと context 渡せないので、これで一回作って、さらに重ねる感じになりますかね…。

Dockerfile 内で、 build を実行している環境から SSH のエージェントフォワーディングをして RUN git pull とかしたい

コマンドの run ならできる。というか中に sshd 上がってるイメージに対して ssh -A すればいいんですけど、

How to SSH agent forward into a docker container

docker run --volume $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent ubuntu ssh-add -l

自分、 Docker コンテナの作成は今 EC2 上の Amazon Linux でやっているんですが、例えば build 中にコンテナ中で仕事の private リポジトリを git clone したいときに、鍵は手元の OSX にしかなくて、もちろんコンテナにも入れたくないし、という感じなのですよね…。 Dockerfile の VOLUME はホストとコンテナの共有には対応していないので、何かいい方法があるに違いない。

packer の既存 VM から box を作り直すやつは罠が多い

virtualbox-ovf

Ubuntu precise では、おそらく

Waiting for network configuration

が出てネットワークインターフェースを見失う。

sudo rm /etc/udev/rules.d/70-persistent-net.rules

消して、シャットダウンし、また起動すると再生成されてしまうので起動しないまま export して ova を作り、それを元に virtualbox-ovf builder を動かすと順調に遷移した。 packer 側が行っていると推測する再生成に、 OS 側がついていけてないということなのかな。いやいやちがった、 mac address 再生成しているのは virtualbox の clone 処理だと言うことです。

vmware-vmx

packer が使っている既存 vmx の clone コマンドが VMware Fusion 6 にはない。 VMware Fusion 6 Professional にはある。

patch 書いて送るか、あきらめるか、 Pro を買う。

ACM

もっと日記っぽいことも書いてゆきたいと思っております。

こないだ ACM のライブラリ付きの subscription 購入したんです。特にそんときは何も考えて無くて(たまにゃーアカデミックな論文も読みたいけど専門家がキュレートしたやつがいいなあ)てなもんだったんですが、アカデミックなプログラムの話、たとえば「パラダイム」とか「考え方」を勉強するの実は具体的なレイヤーで楽して仕事していくにも必要で、考え方わからないとそういうことができるってのがわかんねえとか、こう書きたいなあって思ってもなんて呼ぶかもわからねえから検索したり人に聞いたりも出来ない。(なんか目の外側がしくしくして胸が前にたたまれるような気がする…)って思っても「悲しい」って言葉知らないと辛い。

しかし、何年も同じ環境同じ言語でやってればそんなの勝手に身につくよって話もあるけれど、言語ってソフトウェアが求められる社会的環境やら、それを手助けできるパラダイムやらで勝手に変わっていくから、そこでまた「悲しい」って表現できるかでキャッチアップの速度が変わってくる。さらに言語環境の金銭面を含めた「楽さ」って、言語そのものがどうこうじゃなくて社会の需要とか、さらには「人気」なんてものに強く影響されるので、楽にいきたければそれだけアカデミックなレイヤーもやっとけってことになるんだなあ。

ちなみにこういうことに全く関係なく突き進む人々をまねすると「バカ」か「病人」になるんだよなあ。これも辛い。許された天才の枠数ってのは社会が決めている。あと、言語が勝手に新しいドメインを獲得して活躍しだしたって受け止められるようなたとえば JS みたいのは例外ってやつなのであんまりそれを未来に渡って期待するのは厳しい感じ。そもそもブラウザ JS から node って最初はかなりジャンプありましたよね…。

Vagrant の box を適当に作る

Vagrant Cloud 良いですよね。何が良いって private もあるところと、ゲストをインターネットに一発公開のあれです。 というわけで、横着していままでストックの precise64 から毎回大量の provisioning して作っていた環境を box にしてみました。あ、もちろん provider は VirtualBox です。

まっさらな状態から一回作る

$ vagrant destroy
$ vagrant up
$ vagrant halt

書き出し

$ vagrant package --base hogehoge_default_1222333344444_55555

hogehoge_default_1222333344444_55555 の部分は VirtualBox の管理するところの VM 名です。ここを間違うと

$ vagrant package --base oogatta-test
==> oogatta-test: VM not created. Moving on...

とかすごい悲しい感じのエラーになるです。

これでコマンドを実行したディレクトリに package.box が誕生しました。

box として登録

この box を Vagrant Cloud にあげるわけですけど、テストのため、あるいは自分一人だからこれで十分とかであれば

$ vagrant box add --name oogatta-test package.box

で追加できました。

$ vagrant box list
oogatta-test (virtualbox, 0)
precise64    (virtualbox, 0)

わーい。

RubyMine と Ruby 1.9

RubyMine のテストランナーを便利に利用する

RubyMine のテストランナーは進捗もゲージが増えていって爽快だし、テスト対象のファイルと項目がツリーになって表示されてどんどん緑ランプが付いて消えていくのも楽しいし、落ちたら赤いランプが付いてそのまま居残り、クリックしたらテスト及びテスト対象コードに飛べるし、飛んだ後ブレークポイント打って今度は右クリックすると対象のテストだけデバッグ実行できて(これこれ…これだ…)感あります。

ただし Ruby 1.9 だと、同梱されているものではない、新しい 'test-unit' gem ( Test::Unit 2 )を入れないとなりません。 Gemfile に書いておきます。

Test::Unit Special Notes

これしないと、あの悪名高き empty test suite. エラーです。 Ruby 2.0.0 に付いてる test-unit は 2.0.0 だったので2系が必要ってことなんでしょうか。

test-unit と RSpec

test-unit ってソース読むと Kernel#at_exit に引っかけて駆動してます。それは全然良いんですが、自分の環境だと RubyMine と CircleCI で RSpec の実行後に突然ユニットテストを実行しようとしてこけて全体が fail するという惨めな状況になりまして、検索したら RSpec を動かすコード中に require 'test-unit' している箇所があるとそうなるよと言われており、自分のコード中にそんなところは無く(多分)、 RubyMine がやってんのかなあ…。とほげーと思って結局 test-unit 側に「おめーこのあと実行走らすなよ」ってフラグを spec_helper.rb に書いておきました…。

Test::Unit::AutoRunner.need_auto_run = false if defined? Test::Unit::AutoRunner

相当切ないです。テスト混ぜるなやってことですか。そうですか。