「Packer」でDocker用のイメージファイルを作ってみよう

KVMやXen、VMware、VirtualBoxといった仮想化ソフトウェアやDockerなどのコンテナ管理ツールを利用する際には、それら仮想マシンやコンテナを起動するためのディスクイメージファイルが必要となる。今回はさまざまな仮想化ソフトウェア/コンテナ管理ソフトウェアで利用できるディスクイメージファイルを作成できる「Packer」というツールを紹介する。

複数の形式のディスクイメージを統一的な操作で作成できる

KVMやXen、VMware、VirtualBoxといった仮想化ソフトウェアやDockerなどのコンテナ管理ツールではそれぞれ独自のディスクイメージ形式が採用されており、その作成には専用のツールを利用する。そのため、複数の仮想化ソフトウェアを併用している場合、各ツールごとに設定ファイルを作成したり、対応するコマンドを実行して個々のディスクイメージを作成する必要があった。こういった作業を効率化できるツールが、今回紹介するPackerである(図1)。

図1 PackerのWebサイト
図1 PackerのWebサイト

Packerはユーザーが用意した設定ファイルに従って、指定された仮想マシン/コンテナ向けのディスクイメージや仮想マシンイメージ、コンテナイメージ(以下、まとめて「イメージファイル」と呼ぶ)の作成とプロビジョニングを行うツールだ。

Packerの特徴として、「builder」と呼ばれるイメージファイルの作成処理を行う部分や「provisioner」と呼ばれるプロビジョニング処理を行う部分がモジュール化されている点がある。これらを分離することで、異なる仮想化ソフトウェアやコンテナ管理ツールに対し、同一のプロビジョニング処理を容易に実行できるようになっているのだ(図1)。

図1 Packerのアーキテクチャ
図1 Packerのアーキテクチャ

デフォルトではDockerやQEMU(KVMやXen)、VirtualBox、VMwareなどに対応するbuilderが用意されており、またprovisonerとしてはシェルスクリプトを実行するものや任意のファイルをコピーするもの、そしてChefやPuppet、Ansibleといった設定ツールを実行するものが用意されている。これにより、さまざまな仮想化ソフトウェアやコンテナ管理ツールに対し、同じ環境を含むイメージファイルを作成できる。

こういったアーキテクチャを見ると、以前紹介した仮想環境構築ツール「Vagrant」に似ていると思った人もいるだろう。実はPackerはVagrantと同じ開発者が開発を行っており、そのため設定ファイルやその使い方が似通っている。ただしPackerはVagrantと組み合わせても利用できるが、Vagrant専用のツールではない。そのため、ほかの仮想化ソフトウェアやツールを利用しているユーザーにとっても有用だ。

Packerではできないこと

「さまざまな仮想化ソフトウェアやコンテナ管理ツール向けのイメージファイルを作成できる」と聞くと、汎用的な万能ツールのようにも聞こえるが、Packerは実際には各仮想化ソフトウェアやコンテナ管理ツールのコマンドを内部的に実行してイメージファイルを作成する処理を行うだけなので、制約も多い。

たとえば、VirtualBoxやQEMU向けのbuilderではISOイメージからのイメージファイル作成が可能だが、これはVirtualBoxやQEMUを実行して仮想環境を作成し、そのうえでISOイメージからインストーラを起動することで実現されている。そのため、インストール作業を完全に自動化するにはKickstartなどの自動インストール技術の知識が必要だ。

また、既存のイメージファイルをベースに新たなイメージファイルを作成することは可能だが、イメージファイルをほかの仮想化/コンテナ技術向けに変換する機能などは用意されていない。

Packerを利用するメリット

Packerにはある程度の制約があるのだが、その上でPackerを利用するメリットとしては、インストール後に自動的にあらかじめ指定しておいたプロビジョニング処理を実行できるという点がある。また、一度設定ファイルを作成すれば、その後は同一の環境や似たような環境を容易に構築できるのも利点だ。

特にPackerの利点が生きるのは、provisionerを活用する場合だろう。環境構築をできるだけprovisioner側で行うように設定しておくことで、少ない手間でさまざまな仮想化ソフトウェアやコンテナ管理ツールに対応できるようになる。

Packerでのイメージファイル作成の流れ

Packerでイメージファイルを作成するには、イメージファイル作成の手順などを記載した設定ファイルが必要となる。設定ファイルはJSON形式で記述し、次のような構成になっている。設定ファイル内には複数のbuilderやprovisioner向けの設定を同時に記述することが可能で、これを利用して1つの設定ファイルで複数の仮想化ソフトウェア/コンテナ管理ツール向けのイメージを作成できる。

{
  "description": "<ここにテンプレートの説明文を記述する>",
  "min_packer_version": "<ここで必要とするPackerのバージョンを指定する>",
  "variables": {
    <このテンプレート内で使用する変数を定義する>
    
    
  },
  "builders": [
    {
      <1つ目のbuilderに対する設定を記述する>
    },
    {
      <2つ目のbuilderに対する設定を記述する>
    },
    
    
  ],
  "provisioners": [
    {
      <1つ目のprovisionersに対する設定を記述する>
    },
    {
      <2つ目のprovisionersに対する設定を記述する>
    },
    
    
  ],
  "post-processors": [
    {
      <イメージファイルの作成後に行う1つ目の処理を記述する>
    },
    {
      <イメージファイルの作成後に行う2つ目の処理を記述する>
    },
    
    
  ]
}

なお、「builder」以外の項目はオプションなので、不必要な場合は省略が可能だ。

Packerのインストール

Packer公式Webサイトのダウンロードページでは、Mac OS XおよびLinux、Windows、FreeBSD、OpenBSD向けのバイナリが配布されている。また、ソースコードはGitHubで公開されているが、コンパイルにはGo言語環境が必要であるため、通常はバイナリをダウンロードして使用するほうが良いだろう。

配布されているバイナリはZIP形式で圧縮されており、中にはDockerの実行ファイルがそのまま入っている。適当なディレクトリにこれを展開するだけでインストールは完了だ。なお、以下では「~/packer/」ディレクトリに展開したものとして作業を進めている。

PackerでDocker用のイメージファイルを作成する

それでは、実際にPackerを使ってイメージファイルを作成する流れを見てみよう。まずはシンプルなものとして、Docker向けのイメージファイルを作成する例を紹介する。この場合、設定ファイルにはDocker用のbuilderである「docker」用の設定を記述すれば良い。なお、Docker用のイメージファイルを作成するには、Packerrを実行する環境にDockerがインストールされている必要がある。Docker向けイメージファイルの作成やについては「Docker向けのコンテナをゼロから作ってみよう」という記事で紹介しているので、そちらも参照して欲しい。

設定ファイルを作成する

docker builderで必要となる設定項目についてはドキュメントのDocker Builderページに記載されているが、最低限必要となるのはdocker builderを利用することを指定する「type」と、出力先ファイルを指定する「export_path」、ベースとするDockerのイメージファイルを指定する「image」という項目だ。今回はDockerが提供しているCentOS 7イメージを使用し、作成したイメージファイルは「packer-centos7-docker.tar」というファイルに保存することとした。この場合、設定ファイルは以下のようになる。

{
  "builders": [
    {
      "type": "docker",
      "image": "centos:centos7",
      "export_path": "packer-centos7-docker.tar"
    }
  ]
}

このファイルを「centos7.json」というファイルに保存し、packer buildコマンドを実行すると、DockerHubからCentOS 7環境が含まれたイメージがダウンロードされ、その中身が展開されてtarファイルとして保存される。

$ sudo ~/packer/packer build centos7.json
docker output will be in this color.

==> docker: Creating a temporary directory for sharing data...
==> docker: Pulling Docker image: centos:centos7
    docker: Pulling repository centos
==> docker: Starting docker container...
    docker: Run command: docker run -d -i -t -v /tmp/packer-docker698088109:/packer-files centos:centos7 /bin/bash
    docker: Container ID: d9a0f879085b9d04d046eb8a3e43dde3a342327c90006fa684a2d6bd828d3079
==> docker: Exporting the container
==> docker: Killing the container: d9a0f879085b9d04d046eb8a3e43dde3a342327c90006fa684a2d6bd828d3079
Build 'docker' finished.

==> Builds finished. The artifacts of successful builds are:
--> docker: Exported Docker file: packer-centos7-docker.tar

なお、Dockerの各コマンドの実行にはroot権限が必要であるため、この場合packerもroot権限で実行する必要がある。

ここで作成されたtarファイルは、docker importコマンドでローカルのdockerにインポートできる。

$ sudo docker import - mycentos7 < packer-centos7-docker.tar
ad6d0a32d9fc973ee7fe450716e3f09eee9d0cfaffad6bdfcdf24c5cf4d028b6
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mycentos7           latest              ad6d0a32d9fc        29 seconds ago      243.7 MB

provisionerと組み合わせて使う

Packerを使ってDocker用イメージファイルを作成してみたが、これだけではdockerの機能を使ってイメージファイルを作成するのと何ら変わりがない。そこで、続いてはprovisionerを使い、chef-soloを使ってプロビジョニング処理を実行した上でイメージファイル化する、という処理を行ってみよう。

これを実現するには、設定ファイルにprovisionerに関する項目を追加すれば良い。作成した設定ファイルは次のようになる。

{
  "builders": [
    {
      "type": "docker",
      "image": "centos:centos7",
      "export_path": "packer-centos7-docker.tar"
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": ["yum install -y hostname"]
    },
    {
      "type": "chef-solo",
      "cookbook_paths": ["cookbooks"],
      "run_list": ["webserver"],
      "prevent_sudo": true
    }
  ]
}

ここでは、「shell」および「chef-solo」という2つのprovisionerの設定を追加している。shell provisionerの設定項目についてはShell Provisionerドキュメント、chef solo用provisionerの設定項目については、Chef Solo Provisionerドキュメントにまとめられているので、詳しくはこれらを参照してほしいが、shell provisionerは指定したコマンドを実行するもの、chef-solo provisionerはchef soloの実行を行うものだ。

まず、shell provisionerでは「yum install -y hostname」というコマンドを実行させている。Dockerが公式に用意しているCentOS 7イメージにはhostnameコマンドが含まれておらず、そのためchef-soloコマンドの実行に失敗するため、これを回避するための処理だ。

続いてのchef-solo provisionerでは、packerを実行したディレクトリ内の「cookbook」ディレクトリ内にあるChefのCookbookをイメージ側にコピーし、その中の「webserver」というRecipeを実行する、という処理を行わせている。

なお、chef-solo provisionerではchef-soloのインストール作業についても自動的に実行してくれる。そのため、あらかじめイメージ内にchef-sollをインストールしておく必要は無い。

また、これらファイルに加えて使用するChefのCookbookやRecipeも用意しておく。Chefについては以前『サーバー設定ツール「Chef」の概要と基礎的な使い方』という記事で説明しているので、詳しくはこちらを参照してほしいが、今回は次のように「knife cookbook create」コマンドでCookbookを作成し、Recipeを編集している。

$ mkdir cookbook
$ knife cookbook create webserver -o cookbooks/
** Creating cookbook webserver
** Creating README for cookbook: webserver
** Creating CHANGELOG for cookbook: webserver
** Creating metadata for cookbook: webserver
$ vi cookbooks/webserver/recipes/default.rb  # defualt.rbにRecipeを記述する

Recipeの内容は次のように「httpd」パッケージをインストールするだけというシンプルなものとした。

#
# Cookbook Name:: webserver
# Recipe:: default
#
# install apache http server
yum_package "httpd" do
  action :install
end

これら設定ファイルを用意した上でpacker buildコマンドを実行すると、イメージファイルの作成や、コンテナの起動、hostnameパッケージのインストールやchef-soloのインストールおよび実行といった処理が自動的に行われ、そのコンテナがイメージファイル化される。

$ sudo ~/packer/packer build centos7.json
docker output will be in this color.
  ↓イメージのダウンロード(Pull)
==> docker: Creating a temporary directory for sharing data...
==> docker: Pulling Docker image: centos:centos7
    docker: Pulling repository centos
==> docker: Starting docker container...
  ↓コンテナの起動
    docker: Run command: docker run -d -i -t -v /tmp/packer-docker549661982:/packer-files centos:centos7 /bin/bash
    docker: Container ID: 6d98246a2284f8241b7b926c468ca9c74993dc7251d0f57628b80228da87d278
==> docker: Provisioning with shell script: /tmp/packer-shell419395520
  ↓hostnameパッケージのインストール
    docker: Loaded plugins: fastestmirror
    docker: Determining fastest mirrors
  
  
    docker: Installed:
    docker: hostname.x86_64 0:3.13-3.el7
    docker:
    docker: Complete!
  ↓chef-soloのインストール
==> docker: Provisioning with chef-solo
    docker: Installing Chef...
  
  
    docker: Thank you for installing Chef!
  ↓cookbookをコンテナ内にコピー
    docker: Creating directory: /tmp/packer-chef-solo
    docker: Creating directory: /tmp/packer-chef-solo/cookbooks-0
    docker: Creating configuration file 'solo.rb'
    docker: Creating JSON attribute file
  ↓cookbookの実行
    docker: Executing Chef: chef-solo --no-color -c /tmp/packer-chef-solo/solo.rb -j /tmp/packer-chef-solo/node.json
  
  
    docker: [2014-08-11T10:43:41+01:00] INFO: Processing yum_package[httpd] action install (webserver::default line 11)
    docker: [2014-08-11T10:43:42+01:00] INFO: yum_package[httpd] installing httpd-2.4.6-18.el7.centos from updates repository
    docker: [2014-08-11T10:44:18+01:00] INFO: Chef Run complete in 37.576576759 seconds
  
  
  ↓コンテナのエクスポート
==> docker: Exporting the container
==> docker: Killing the container: 6d98246a2284f8241b7b926c468ca9c74993dc7251d0f57628b80228da87d278
Build 'docker' finished.

==> Builds finished. The artifacts of successful builds are:
--> docker: Exported Docker file: packer-centos7-docker.tar

次回に続く

さて、今回はDockerのイメージ作成を例にPackerによるイメージ作成方法を紹介したが、Packerを利用することで、このようにコンテナのプロビジョニング作業が非常に簡単に実行できる。ChefやPuppet、Ansibleなどのデプロイツールを使っているなら、これだけでもPackerを導入する価値があるだろう。

さらに、Packerではbuilderに関する設定を追加するだけで、同様のプロビジョニングを異なる仮想化ソフトウェア/コンテナ管理ツールに対しても実行できるようになる。これについては、次回VirtualBoxやQEMU(Xen/KVM)向けイメージの作成を例に紹介する。

おしらせ

banner_vps