Vagrant + Docker Provisioner ってハマるくない?

やっとdocker触り始めました。

まだ、うまく説明できるほどには馴染んでないけど。でも、凄い面白いなー。

って、Vagrant の Docker Provisioner 使おうとして。ハマった。これハマるくない?ちなみにProviderじゃなくてProvisionerの方ね。

特に誰かに何かを伝えるでもなく。イマココをだらだらと書いてみる。

docker provisionerをdockerのインストールだけに使う

shin1x1さんの記事(1年以上前の)を読みつつふむふむ。docker provisionerをdockerのインストールだけに使うって感じね。便利。1行書くだけでdocker使いたい放題!

ここでやめとけばよかったんや・・・。

docker provisionerを使ってみる

Docker - Provisioning - Vagrant Documentation

Vagrant.configure("2") do |config|
  config.vm.provision "docker" do |d|
    d.build_image "/vagrant/app",
        args: "-t bufferings/sample"
    d.run "bufferings/sample",
        args: "-v '/vagrant:/var/www' -p 1234:80"
  end
end

こんな感じでprovisionしたら動くやろーって思って。(∩´∀`)∩ワーイうごいたー!

・・・って、これリロードしても動きっぱなしなんかね?vagrant reloadぽちっと。

おぉ・・・起動せんね。

そらそっか。provisionせんと呼ばれんよね・・・。

じゃ。こうするしか・・・。

Vagrant.configure("2") do |config|
  config.vm.provision "docker" , run: "always" do |d|
    d.build_image "/vagrant/app",
        args: "-t bufferings/sample"
    d.run "bufferings/sample",
        args: "-v '/vagrant:/var/www' -p 1234:80"
  end
end

docker provisionerに

run: "always"

をつけた。えー。でもこれ、毎回ビルドされるんなんか嫌やなー。じゃ、こうするか。

Vagrant.configure("2") do |config|
  config.vm.provision "docker-build", type: "docker" do |d|
    d.build_image "/vagrant/app",
        args: "-t bufferings/sample"
  end

  config.vm.provision "docker-run", type: "docker", run: "always" do |d|
    d.run "bufferings/sample",
        args: "-v '/vagrant:/var/www' -p 1234:80"
  end
end

ふむふむ。これで毎回コンテナは起動するけどビルドはせんようになったぞぅ。Dockerfile変更した時はvagrant provisionすればいいんかね。やってみるか。

∑(゚Д゚)ガーン

port 1234は使われとるよ。・・・って、そらそうか。コンテナ起動中や。しかしどうしようか。こうですか・・・。

Vagrant.configure("2") do |config|
  config.vm.provision "docker-build", type: "docker" do |d|
    d.build_image "/vagrant/app",
        args: "-t bufferings/sample"
  end

  config.vm.provision "shell", inline: "(実行中のコンテナがあったら停止するコマンド)"

  config.vm.provision "docker-run", type: "docker", run: "always" do |d|
    d.run "bufferings/sample",
        args: "-v '/vagrant:/var/www' -p 1234:80"
  end
end

# すません。コマンド書くのめんどくさくなった。docker ps -qで起動中のコンテナがあれば、それをdocker stopにくわせて止める感じ。

なんかださいぞ!!あってるんかこれ?ま、動くしとりあえずはいっか。

ところで仕組みが気になった

どうやって、起動するコンテナを判別してるんだろー?とか。

d.build_imageは

  config.vm.provision "docker" do |d|
    d.build_image "/vagrant/app",
        args: "-t bufferings/sample"
  end

これで、/vagrant/app/Dockefileを使ってイメージをビルドして、イメージのタグをargsで指定してる。
実装はこれ:

vagrant/client.rb at master · mitchellh/vagrant · GitHub

      def build_images(images)
        @machine.communicate.tap do |comm|
          images.each do |path, opts|
            @machine.ui.info(I18n.t("vagrant.docker_building_single", path: path))
            comm.sudo("docker build #{opts[:args]} #{path}") do |type, data|
              handle_comm(type, data)
            end
          end
        end
      end

docker buildしてるだけすね。

d.runはコンテナを起動する:

  config.vm.provision "docker" do |d|
    d.run "bufferings/sample",
        args: "-v '/vagrant:/var/www' -p 1234:80"
  end

provisionerのソースはこんな感じ:

      def run(containers)
        containers.each do |name, config|
          cids_dir = "/var/lib/vagrant/cids"
          config[:cidfile] ||= "#{cids_dir}/#{Digest::SHA1.hexdigest name}"

          @machine.ui.info(I18n.t("vagrant.docker_running", name: name))
          @machine.communicate.sudo("mkdir -p #{cids_dir}")
          run_container({
            name: name,
            original_name: name,
          }.merge(config))
        end
      end

名前からハッシュファイル作って、中にコンテナIDを保持してるっぽい。
そのコンテナがあればstart。なければ作成するってか:

      def run_container(config)
        raise "Container's cidfile was not provided!" if !config[:cidfile]

        id = "$(cat #{config[:cidfile]})"

        if container_exists?(id)
          start_container(id)
        else
          create_container(config)
        end
      end

      def start_container(id)
        if !container_running?(id)
          @machine.communicate.sudo("docker start #{id}")
        end
      end

      def container_running?(id)
        lookup_container(id)
      end

      def create_container(config)
        name = config[:name]

        # If the name is the automatically assigned name, then
        # replace the "/" with "-" because "/" is not a valid
        # character for a docker container name.
        name = name.gsub("/", "-") if name == config[:original_name]

        args = "--cidfile=#{config[:cidfile]} "
        args << "-d " if config[:daemonize]
        args << "--name #{name} " if name && config[:auto_assign_name]
        args << config[:args] if config[:args]
        @machine.communicate.sudo %[
          rm -f #{config[:cidfile]}
          docker run #{args} #{config[:image]} #{config[:cmd]}
        ]
      end

ふむふむ。

てことは

変更してビルドしなおしたとしても、名前を変えない限り古い方のコンテナを使い続けるってことか。
んむー。なんかいけてない匂いがするなー。とはいえ、良い実装を考えるのも難しそうだな。
普通は、Dockerfileでビルドするんじゃなくて、Imageを事前にDockerHubにpushしといて使うんかな?

ふむ。ソース読むのにだいぶ疲れた。ruby力が必要だわ。

おは。