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

  • 2061
    いいね
  • 5
    コメント

筆者注

(2016年11月末追記)
たくさんの反響ありがとうございます。予想以上に「いいね」「ストック」が伸び続けていたため、記事中のよろしくない部分を修正しました。

(2016年11月初頭追記)
本記事は筆者自身も未熟な時期に書いたもので、今読み返してみるとやや不正確な表現があったり、既により良い手段に置き換えられている内容が含まれます。足がかり程度にお読みください。

1. はじめに

この記事の想定読者

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

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

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

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

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

2. 各種ソフトウェア概要

2.1. Vagrant

これはなに?

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

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

また、VMの構成を Vagrantfile というテキストファイルに記述しておけるので、これを Git などのソースコードリポジトリで管理することができます。

なにに使う?

  • ゲストOSがインストールされたVMを整備するのに使います。
  • VMを使う目的は、ホストマシン(作業する Windows/Mac 等のPC)に依存せずに、開発環境やテスト環境を構築するためです。

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

  • コマンド1発でVMを作成できるようになります。破棄&再作成も簡単です。
  • 構成を Vagrantfile に記述できるので、秘伝の環境構築手順書とおさらばできます。

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

何もない状態からスタートするとしても、 Vagrant を使った環境整備は10分もかかりません。(Vagrant自体のダウンロード時間などは除く)

また、構成内容をそのままコマンドに食わせるテキストファイル(実態は ruby コード)にできるので、 環境構築手順.docx のような秘伝のタレ・深淵の闇とおさらばできます。

2.2 Docker

これはなに?

  • 特定のプロセスの実行環境を「Dockerイメージ」という単位で保管するツール。
  • イメージを「Dockerコンテナ」として再生・実行し、コンテナ同士を連携させるツール。

Apache HTTP サーバー、MySQL サーバーなど、あるプロセスを動かすための環境をまるごとイメージという形に封印し、それをコンテナとして再生・実行するということが出来るツールです。

音楽で例えるならば

  • ある楽曲が「再生したいプロセス」
  • それを録音したCD等のメディアが「Dockerイメージ」
  • 再生するためのプレーヤー機器が「Dockerホスト」
  • プレーヤーによって再生される楽曲音声が「Dokcerコンテナ」

となります。(あくまで雰囲気)

CDに焼いてしまえば、CDを再生できる機器なら楽曲が聴けるはずです。
イメージを作ってしまえば、Dockerがある環境ならコンテナが実行できるはずです。

Dockerとは、それを実現してくれるツールです。

NOTE

Dockerを学習する際は、 「Dockerとはプロセスを実行するツールなのだ」 ということを常に意識しておくと良いと思います。

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

  • プロセスを動かすための環境自体をイメージ化することで、「実行できないんだけど…」といったことが無くなる。
  • イメージから実行されるコンテナの実体は単なる1つのプロセスであり、起動や破棄が超高速。
  • そのイメージを作るためにどんな手順を踏んだのかを Dockerfile というテキストとして保管できる。

ジャンルで言うと Vagrant と同じく仮想化の国の人ですが、こちらはプロセスを実行するツールです。

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

Vagrant でも似たようなことは実現できますが、 vagrant up で環境全体を整備するのは数分かかります。
Dockerコンテナは単なるプロセスなので、その破棄と起動はものの数秒です。

また、プロセスごとにイメージを構築・保管し、組み合わせて実行するスタンスなので、アプリ全体を構成するうちの一部のミドルウェアだけをメンテナンスすることも容易です。

3. スタートガイド

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/trusty64Atlas で公開されている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を起動してみましょう。

vagrant up

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

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

vagrant ssh

そのままコンソールが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を削除してしまいましょう。

vagrant destroy

確認表示で 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の取得について

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

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

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


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を実行させることもできます。

vagrant provision

しばらく待ちます。

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

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

3. Dockerイメージの作成

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

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

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

ベースイメージのサイズはディスク容量の消費量に直結するので、 Alpine のような軽量 Linux の人気が高まっています。
同じ apt 系でも Ubuntu より Debian が好まれるなど、少容量化の流れがあるようです。

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

「これはなに?」の項で軽く触れたとおり、Dockerはプロセスを実行するツールであり、イメージには「このイメージはなんのプロセスを実行するものか」を記しておきます。

早速作りましょう。

Dockerfile
FROM ubuntu:trusty

MAINTAINER hidekuro

CMD ["/bin/bash"]

これはUbuntu trusty(14.04)のベースイメージと何も変わらないイメージです。
bash を単体で実行しても意味がありませんが、まぁ練習ということで。

本来であれば 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-bash /vagrant

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

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

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

vagrant@vagrant-ubuntu-trusty-64:~$ docker build -t hidekuro/my-bash /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-bash
root@246dc5e41ffd:/#

いま、 hidekuro/my-bash イメージから起動した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-bash でイメージを指定しています。

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

例えばこれを docker run -it hidekuro/my-bash 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-bash: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-bash"
+    d.run "hidekuro/my-bash", 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-bash

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

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

4. 本格的な利用について考える

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

例えば…

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

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

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

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

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

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


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

以上です。