VagrantとDockerについて名前しか知らなかったので試した - Qiita

この記事の想定読者

  • VagrantとDocker、どちらも名前だけは知ってるという方
  • インフラ構成のコード化と共有に興味があるけどまだ触ってないという方

各種ソフトの概要と利用シーンについて軽く触れつつ、調べた内容をまとめておきました。
また、それぞれの実運用経験がない人間が書いておりますので、ご留意下さい。

ゆくゆくこうなるとカッコいいなぁ像

※この記事ではそんなレベルまでたどり着いてません

  • チーム開発初期にはVagrantfileとDockerイメージを配布してスタート!
  • チーム内でDockerレジストリを立て、ローカルテスト環境・結合テスト環境をDockerイメージとして共有!

2.1. Vagrant

これはなに?

仮想マシン(以下 "VM")を動かす仮想化ソフトの超すごいラッパーツール。
VirtualBoxなどの仮想化ソフトの操作を、ものすごく親切に代行してくれます。

VMの構成を Vagrantfile というテキストに記述してVagrantに任せることで、ネットワークドライバの設定などを意識すること無く、ホスト環境に依存しない形で整備してもらえます。

なにに使う?

ゲストOSがインストールされたVMを整備するのに使います。

Vagrant Cloud にあらかじめ用意されている「box」というVMイメージを指定してコマンドを実行するだけで、OSインストール済みのVMを作成し、ネットワーク設定やSSH環境の整備までやってくれます。

また、VMの構成内容を Vagrantfile というテキストファイルとしてコード化できます。

具体的に何がどう楽になる?

VMの構成を Vagrantfile に記述して vagrant up コマンドを実行するだけで、すぐに完全な環境を整備できます。

VirtualBoxでVMを作って、GuestAdditionsを入れて、ゲストOSをダウンロード&インストールして、sshdを立てて、SSHでログインして、ミドルウェアを入れて…といった大変な作業がバッサリカットできます。

何もない状態からスタートするとしても、Vagrantを使った環境整備は10分もかかりません。

2.2 Docker

これはなに?

OSからミドルウェア、果てはファイルシステム全体を「イメージ」という単位で取り扱い、まるごとやりとり出来るツール。

Dockerでは、OSとミドルウェアの環境構成を「Dockerイメージ」という形で保管します。イメージをベースに「Dockerコンテナ」を生成して、実際のOSとして稼働させます。

コンテナは、ファイルシステム全体をGitコミットオブジェクトのような感覚で履歴管理できます。コンテナの特定の履歴に名前を付けて、新しいイメージとして保存することもできます。

具体的に何がどう楽になる?

イメージという単位で 特定の環境全体をまるまるやりとりできます。

これはVagrantfileの効用にも似ていますが、Dockerイメージは直接稼働するVMではないという点が違います。

実際に稼働するのはイメージから生成されたコンテナであり、 その生成・起動と破棄のサイクルは超高速に行うことができます。

例えばアプリケーションのテストにおいて、Webサーバーやデータベース、テストデータなど全て準備済みの状態をイメージとして保存し、そこから生成したコンテナをテストに使うようにすれば、テストデータ準備済みの環境を何度でも・簡単に・高速に再現することができます。

また、コンテナは一つのVMと同じように扱うことができる完全な環境ですので、Chefのような構成管理ツールの試運転にも最適です。
レシピの実行に失敗したらそのコンテナは破棄して、また新しいコンテナを生成することで、常にまっさらな状態で試すことができます。

Vagrantでも似たようなサイクルは実現できるかもしれませんが vagrant up で環境全体を整備するのは数分かかります。 Dockerコンテナの破棄と起動は、ものの数秒です。

VagrantとDockerをインストールして、Dockerイメージを作り、Dockerコンテナを起動させるところまでやってみます。

なぜDocker単体ではなくVagrantを挟むかというと、Dockerが64bit環境でしか動かないためです。完全にホスト環境依存を脱却するためにVagrantを挟みます。

また、VagrantにはゲストOS上でDockerを利用するためのサポート機能があり、Docker自体の導入がとても簡単になります。

3.1. Vagrant

おおまかな流れ

  1. VirtualBox, Vagrantのインストール
  2. VM用ディレクトリの作成
  3. vagrant init
  4. vagrant up

手順

1. VirtualBox, Vagrantのインストール

公式サイトからVirtualBoxVagrant をそれぞれインストールします。
お手元の作業PC環境にあわせてインストーラーを選択して下さい。

2. VM用ディレクトリの作成

インストールできたらVM用ディレクトリを作ります。
ここでは ~/vm/project-dir のような階層構造にしようと思います。

cd %UserProfile%
mkdir vm\hello-infra
cd vm\hello-infra

Windows以外の場合は適宜読み替えて下さい。

3. Vagrantfileの生成

次に、VMの構成を書く Vagrantfile を生成します。
ここでは私の好みでUbuntu14.04を使います。

vagrant init --minimal ubuntu/trusty64

--minimal は最小のVagrantfileを生成させます。
これをつけない場合は、記述例とコメントが入ったVagrantfileが生成されます。
ubuntu/trusty64Vagrant Cloud で公開されているUbuntu公式Boxの名前です。

【重要】ホストOSとゲストOSのCPUアーキテクチャが違う場合

ホストOSが32bitで、64bitのゲストOSを使用する、あるいはその逆を行う場合、次のようにVirtualBoxの設定を追記して、OSタイプを明示する必要があります。

Vagrantfile

 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   config.vm.box = "ubuntu/trusty64"
+  config.vm.provider "virtualbox" do |v|
+    v.customize ["modifyvm", :id, "--ostype", "Ubuntu_64"]
+  end
 end

これにより、作成されたVMのOSタイプが「Ubuntu 64bit」に設定されます。特に32bitのホストでDockerを試してみたい場合に必要なことが多いと思います。

この設定がないと、VMのアーキテクチャとゲストOSの要件がマッチせずに様々なトラブルの元になります。例えばゲストOSがいつまでたっても起動しなかったり、SSHDが起動していない旨のエラーが表示される等々…

いずれも事象と原因が結びつきにくいモノが多く、かなり悩まされます。
(つまり筆者はハマりました)

ちなみにVMのカスタマイズを行う v.customize ですが、VM名・CPU数・メモリサイズの3つについてはショートカットがあります。

  • v.name = "VM名" … VMの名前
  • v.cpus = 2 … VMのCPU数
  • v.memory = 1024 … VMのメモリサイズ

v.customize でカスタマイズ出来る内容はVirtualBoxの VBoxManage に準拠しますので、詳細は VBoxManageのマニュアル を参照して下さい。

modifyvm --ostype <ostype ID> で指定する値は、VirtualBoxのインストールディレクトリにある VBoxManage コマンドを使用して、 VBoxManage list ostypes で調べられます。特によく使われるであろうCentOSとUbuntuのものを下表に引用します。

ゲストOSの種類 ostype ID
Ubuntu 32bit Ubuntu
Ubuntu 64bit Ubuntu_64
CentOS 32bit RedHat
CentOS 64bit RedHat_64

4. VMの起動とSSHログイン

それではVMを起動してみましょう。

初回はBoxのダウンロードとVM生成があるため時間がかかります。

起動が完了すると、既にSSHログインできる状態になっています。
Vagrantの機能を通してログインしてみましょう。

そのままコンソールがsshに切り替わります。

Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-35-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

 System information disabled due to load higher than 2.0

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud


Last login: Thu Sep 11 09:39:22 2014 from 10.0.2.2
vagrant@vagrant-ubuntu-trusty-64:~$

あるいは vagrant ssh-config で表示される情報を使って、好みのSSHクライアントで接続することもできます。

VMのシャットダウンは vagrant halt, 休止は vagrant suspend です。

なにかトラブルがあって最初からやり直したい場合は、VMを削除してしまいましょう。

確認表示で y と返答すればVMが削除されます。

    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Destroying VM and associated drives...

その後、改めて vagrant up すればVMの生成から再スタートできます。

TIPS: ホストOSとのファイル共有

Vagrantでは、デフォルトではVagrantfileがあるホスト側ディレクトリがゲストOSの /vagrant にマウントされます。ゲストOSで /vagrant を見てみるとわかりやすいです。

vagrant@vagrant-ubuntu-trusty-64:~$ cd /vagrant
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ ls -Al
total 1
drwxrwxrwx 1 vagrant vagrant   0 Sep 12 04:47 .vagrant
-rwxrwxrwx 1 vagrant vagrant 386 Sep 12 07:23 Vagrantfile

このマウント先はVagrantfileの config.vm.synced_folder で指定できます。
詳細は VagrantマニュアルのSynced Folder を参照してください。

NOTE: Boxの取得について

Vagrant Cloud が立ち上がる以前に Vagrantbox.es という「みんなのBoxを持ち寄ってURLを共有する場」が広まりました。

導入解説サイトなどではこちらを利用している例も多く、Boxも多数登録済みですが、ここにあるのはあくまで 世界中の個人ユーザーが作ったBox です。
(各種BoxのダウンロードURLからもお分かりいただけるかと思います)

今後は特に理由がなければ Vagrant Cloud で始めましょう。


3.2. Docker

おおまかな流れ

  1. Docker導入のためにProvisionerをVagrantfileに追記
  2. vagrant provisionでDockerインストール
  3. Dockerイメージの構成記述ファイル Dockerfile の作成
  4. イメージをビルド
  5. コンテナを生成・起動

手順

1. Dockerの導入

Vagrantには、VMに自動で追加ソフトウェアをインストールする「Provisioner」という機構があります。この中にはDocker使うための「Docker Provisioner」がありますので、これを利用します。

先ほど作ったVagrantfileにProvisionerを追記します。

Vagrantfile

 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   config.vm.box = "ubuntu/trusty64"
   config.vm.provider "virtualbox" do |v|
     v.customize ["modifyvm", :id, "--ostype", "Ubuntu_64"]
   end
+  config.vm.provision "docker"
 end

2. Provisionerを実行してゲストOSにDockerをインストール

Provisionerは vagrant up でVMを起動した時に実行されますが、起動中のVMにProvisionerを実行させることもできます。

しばらく待ちます。

==> default: Running provisioner: docker...
    default: Installing Docker (latest) onto machine...
    default: Configuring Docker to autostart containers...

これでゲストOSに最新のDockerがインストールされ、Dockerデーモンが稼働するようになりました。簡単ですねー。

3. Dockerイメージの作成

コンテナを起動するにはイメージが必要です。

イメージを作る時は ベースイメージ が必要です。ベースイメージとは、独自のDockerイメージを作成する時の基礎となる、まっさらなOSのイメージです。Vagrant CloudにあるベースBoxみたいなものですね。

Dockerには Docker Hub という公式レジストリ(イメージを公開するためのリポジトリ)があり、CentOSやUbuntuといったベースイメージはここから入手できます。

これを元に、どんなソフトウェアをインストールするか、何番のポートを開けるか、起動時に何を実行するかといったことを Dockerfile に記述して ビルド することでイメージが完成します。

早速作りましょう。

Dockerfile

FROM ubuntu:trusty

MAINTAINER hidekuro

CMD ["/bin/bash"]

これはUbuntu trusty(14.04)のベースイメージと何も変わらないイメージです。

本来であればMAINTAINERのあとに RUN apt-get -y install nginx といった記述で環境のセットアップをしていくところですが、その辺は他の記事にお任せして、今回はとりあえずイメージのビルドとコンテナ起動を目的としましょう。

このファイルを、ホストOSのVagrantfileと同じディレクトリ %UserProfile%\vm\hello-infra\ においておきます。

4. Dockerイメージのビルド

イメージをビルドするには docker build コマンドを使います。

このコマンドにDockerfileが置いてあるディレクトリを渡してやります。
また、ビルドしたイメージにタグを付けて、名前で扱えるようにしておきます。

ゲストOS

vagrant@vagrant-ubuntu-trusty-64:~$ docker build -t hidekuro/my-first-container /vagrant

/vagrant は、今回はホスト側の %UserProfile%\vm\hello-infra フォルダがマウントされたディレクトリです。先ほどDockerfileをここに保管したので、それをビルドします。

-t でタグを付けます。タグは USER/TAG という形式にする慣習があります。このルールはDocker Hubでイメージを公開することを考慮してのものです。

さて、しばらく待つとビルドが完了します。

vagrant@vagrant-ubuntu-trusty-64:~$ docker build -t hidekuro/my-first-container /vagrant
Sending build context to Docker daemon 11.78 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:trusty
Pulling repository ubuntu
826544226fdc: Download complete
511136ea3c5a: Download complete
b3553b91f79f: Download complete
ca63a3899a99: Download complete
ff01d67c9471: Download complete
7428bd008763: Download complete
c7c7108e0ad8: Download complete
 ---> 826544226fdc
Step 1 : MAINTAINER hidekuro
 ---> Running in eb693c4b9dbc
 ---> 7046d5b306fc
Removing intermediate container eb693c4b9dbc
Step 2 : CMD["/bin/bash"]
 ---> Running in 536eed38a72d
 ---> 1767a92dba30
Removing intermediate container 536eed38a72d
Successfully built 1767a92dba30
vagrant@vagrant-ubuntu-trusty-64:/vagrant$

初回はベースイメージがpullされるので、ダウンロードに時間がかかります。

出力内容を見てみると、ステップごとにコミットハッシュのようなものがあります。また Removing intermediate container 「中間コンテナを削除」という表示が見えます。

Dockerがイメージをビルドする時はDockerfileの指示コマンドを一つ進めるごとにコンテナが作成&コミットされており、最終的に完成したコンテナにタグが付けられているということがわかります。

それでは、作成したコンテナを起動してみます。

vagrant@vagrant-ubuntu-trusty-64:/vagrant$ docker run -i -t -v /vagrant:/tmp/shared --name="hogehoge" hidekuro/my-first-container
root@246dc5e41ffd:/#

いま、 hidekuro/my-first-containerイメージから起動したhogehogeコンテナにて、rootユーザーでbashを起動した状態 になっています。ちょっと長いので順に見てみます。

  • docker run [OPTIONS] IMAGE [CMD] というコマンドです。
  • -i で対話モードにしています。
  • -t は擬似ターミナル割り当てです。
  • -v /vagrant:/tmp/shared で、/vagrant ディレクトリをコンテナ内の /tmp/shared としてマウントしています。今回の場合、ゲストOSの /vagrant はホストOSとも共有していますので、ホストOSのファイルを直接コンテナに共有することができます。
  • --name="hogehoge" でコンテナに名前を付けます。省略すると "condescending_colden" のようなランダム2語の名前がつきます。
  • 最後に hidekuro/my-first-container でイメージを指定しています。

[CMD] は指定していませんが、さきほどビルドしたDockerfileの最後の行で CMD ["/bin/bash"] と記述しています。
docker run でコマンド省略時はこれが実行されるので、bashが起動するようになっています。

例えばこれを docker run -it hidekuro/my-first-container echo 'hello!!' のようにすると、生成されたコンテナは echo 'hello!!' を実行して終了します。
(フォアグラウンドプログラムが終了するとコンテナも終了します)

ここまでで、コンテナを起動するところまでこれました。
とりあえず現在のbashを終了します。

root@246dc5e41ffd:/# exit
vagrant@vagrant-ubuntu-trusty-64:~$ 

前述のとおり、フォアグラウンドのプログラムが終了するとコンテナも終了します。
今回はbashを実行するコンテナだったので exit でbashとともにコンテナも終了します。

ここで、終了したコンテナを確認してみます。

vagrant@vagrant-ubuntu-trusty-64:~$ docker ps -a
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES
c149e0ff4fcf    hidekuro/my-first-container:latest    "/bin/bash"    About a minute ago    Exited (0)    About a minute ago    hogehoge

コンテナIDや名前、起動元のイメージ、ステータスなどが表示されました。
とりあえず試しただけのコンテナなので、捨ててみましょう。

vagrant@vagrant-ubuntu-trusty-64:~$ docker rm c149e0ff4fcf
c149e0ff4fcf
vagrant@vagrant-ubuntu-trusty-64:~$

消えました。

さて、Dockerコンテナを試せたまではいいのですが、このままだと
「Vagrantいれて、Vagrantfileをどっかに保存して、vagrant up してね!!」
だけで済ますことができません。

わざわざ起動してからDockerを操作するのも面倒なので、Vagrantfileに追記して vagrant up だけで全てやってもらえるようにします。

Vagrantfile

 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   config.vm.box = "ubuntu/trusty64"
   config.vm.provider "virtualbox" do |v|
     v.customize ["modifyvm", :id, "--ostype", "Ubuntu_64"]
   end
-  config.vm.provision "docker"
+  config.vm.provision "docker" do |d|
+    d.build_image "/vagrant", args: "-t hidekuro/my-image"
+    d.run "hidekuro/my-image", args: "-d -t -v /vagrant:/tmp/shared"
+  end
 end

Docker Provisionerのブロックで、上でコマンドを使って行った「イメージのビルド」「コンテナの起動」をやっているだけです。

d.build_imagedocker build に相当する処理を行います。
ゲストOS上での Dockerfileのパスと、引数でタグを指定しています。

d.rundocker run 相当の処理を行います。
起動するイメージ名と、先ほどと似たような引数を与えています。

一点違うのは -d で、これは入出力をデタッチしてバックグラウンドでコンテナを起動することを意味します。詳細は docker run -h を参照してください。

これでVMを再起動してみましょう。

vagrant reload --provision

これはプロビジョナーの実行アリでVMを再起動する指示で、 vagrant haltvagrant up とほぼ同じです。

...略...
==> default: Building Docker images...
==> default: -- Path: /vagrant
==> default: Starting Docker containers...
==> default: -- Container: hidekuro/my-image

VMの起動時にコンテナが起動されました。

今回起動したコンテナはbashを実行しているだけなので、デタッチしたら全く意味がありませんが、実用の際はこのような方法でApacheやnginx, GlassFish, MySQLといったサーバー系ソフトウェアを動作させます。

ここまでは、あまりVagrant+Dockerの恩恵を受けられないような小規模なデモでしたが、実際にどのようなシーンで使っていけばいいでしょうか。

例えば…

  • 開発環境、ローカル確認環境、ステージング環境、本番環境を全てイメージとして整備する。
  • 開発環境・ローカル確認環境は随時 build & run
  • ステージング環境は定期的に build & run
  • 本番環境は特定のトリガーに基づいて build & run

…と言った体系を組み上げ、更にそれぞれの環境ごとのイメージについても

  • ミドル環境を整えた基本イメージ
  • ↑をベースにアプリを git clone してデプロイするアプリ基本イメージ
  • ↑をベースに最新版を git pull してデプロイする実行イメージ

という段階を設け、各段階を build & run する頻度を分けることで運用していくとか。

…と、ここまで↓こちらの記事のパクリです。

Docker をプロダクトのデプロイに使う - Wed, 08 Jan 2014 05:07:37 GMT


まだまだ私も学習が必要な段階ですが、この記事が皆様のスタートの手助けになれば幸いです。

以上です。