Chefを使ったDockerのイメージファイル作成を自動化できるツール「Packer」

図1 PackerのWebサイト

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

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

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

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

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

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

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

 builderではQEMU(KVMやXen)およびVirtualBox、VMware、Parallelsといった仮想化ソフト向けやAmazon EC2(AMI)、Google Compute Engine、OpenStackといったクラウド環境向け、そしでDocker向けイメージの作成に対応しており、またprovisionerとしてはシェルスクリプトを実行するものや任意のファイルをコピーするといったシンプルなもの、そして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形式で記述し、一般的には次のような構成になっている。

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

 設定ファイル内には複数のbuilderやprovisioner向けの設定を同時に記述することが可能で、これを利用して1つの設定ファイルで複数の仮想化ソフトウェア/コンテナ管理ツール向けのイメージを作成できる。なお、「builder」以外の項目はオプションなので、不必要な場合は省略が可能だ。

Packerのインストール

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

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

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

 それでは、実際にPackerを使ってイメージファイルを作成する流れを見てみよう。まずはシンプルなものとして、Docker向けのイメージファイルを作成する例を紹介する。この場合、設定ファイルにはDocker用のbuilderである「docker」用の設定を記述すれば良い。

 なお、Docker用のイメージファイルを作成するには、Packerrを実行する環境にDockerがインストールされている必要がある。Docker向けイメージファイルの作成については『「docker import」コマンドで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ファイルとして保存される。なお、Dockerの各コマンドの実行にはroot権限が必要であるため、この場合packerもroot権限で実行する必要がある。

$ 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: Trying to pull repository docker.io/library/centos ... centos7: Pulling from library/centos
    docker: 47d44cb6f252: Pulling fs layer
  
  
    docker: 0e0217391d41: Pull complete
    docker: library/centos:centos7: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
    docker: Digest: sha256:a5cca7b18f9b2d291d6078541f3808cffc3cbdf1b381393d4fe344dc0ac90aaf
    docker: Status: Downloaded newer image for docker.io/centos:centos7
==> docker: Starting docker container...
    docker: Run command: docker run -v /root/.packer.d/tmp/packer-docker114697718:/packer-files -d -i -t centos:centos7 /bin/bash
    docker: Container ID: c43a18525b887b94541569d4813393ae1f00280a73a405e621ed4df3c4ee1ad1
==> docker: Exporting the container
==> docker: Killing the container: c43a18525b887b94541569d4813393ae1f00280a73a405e621ed4df3c4ee1ad1
Build 'docker' finished.

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

 ここで作成されたtarファイルには、コンテナで使用されるファイルツリーが格納されており、「docker import」コマンドでDockerイメージとしてインポートできる。

# docker import - <登録するイメージ名> < <インポートするtarファイル>

 たとえば、先ほど作成したイメージを「mycentos7」という名前でインポートする場合、次のように実行する。

$ sudo docker import - mycentos7 < packer-centos7-docker.tar
ad6d0a32d9fc973ee7fe450716e3f09eee9d0cfaffad6bdfcdf24c5cf4d028b6

 インポートされたdockerイメージは、次のように「docker images」コマンドで確認できる。

$ sudo 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を使ってプロビジョニング処理を実行した上でイメージファイル化する、という処理を行ってみよう。なお、Chefについては『サーバー設定ツール「Chef」の概要と基礎的な使い方』という記事で説明しているので、詳しくはこちらを参照してほしい。

 Chefによるプロビジョニングを実現するには、設定ファイルにprovisionerに関する項目を追加すれば良い。作成した設定ファイルは次のようになる。

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

 リスト中で強調表示されているのが新たに追加した部分だ。ここでは、chef-soloを使ってプロビジョニングを行う「chef-solo」というprovisionerの設定を追加している。chef solo用provisionerの設定項目については、Chef Solo Provisionerドキュメントにまとめられているので詳しくはこれらを参照してほしい。

 chef-solo provisionerの設定では、実行するCookbookのパスやレシピを指定する。ここで指定したCookbookがイメージ内にコピーされ、指定されたレシピが実行される仕組みだ。

 chef-solo provisionerではchef-soloのインストール作業についても自動的に実行してくれる。そのため、あらかじめイメージ内やpackerを実行する環境にChefをインストールしておく必要はない。Chefのレシピを作成する方法はいくつかあるが、今回は以下のようにレシピファイルの作成のみを行った。

$ mkdir -p cookbooks/webserver/recipes/
$ vi cookbooks/webserver/recipes/default.rb

 今回用意したレシピは、次のように「httpd」パッケージをインストールするだけのシンプルなものだ。

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

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

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

==> docker: Creating a temporary directory for sharing data...
  ↓イメージのダウンロード(Pull)
==> docker: Pulling Docker image: centos:centos7
  
  
  ↓コンテナの起動
==> docker: Starting docker container...
    docker: Run command: docker run -v /root/.packer.d/tmp/packer-docker228486574:/packer-files -d -i -t centos:centos7 /bin/bash
    docker: Container ID: fac401834d82c5eb1d0d9e4a757e2919eb95215f3e1c3d5bc9c67d3250b7e43d
  ↓Chefのインストール
==> docker: Provisioning with chef-solo
    docker: Installing Chef...
  
  
    docker: Thank you for installing Chef!
    docker: Creating directory: /tmp/packer-chef-client
    docker: Creating directory: /tmp/packer-chef-client/cookbooks-0
    docker: Creating configuration file 'solo.rb'
    docker: Creating JSON attribute file
  ↓Chefの実行
    docker: Executing Chef: chef-solo --no-color -c /tmp/packer-chef-client/solo.rb -j /tmp/packer-chef-client/node.json
  
  
    docker: [2016-02-23T12:08:42+00:00] INFO: Processing yum_package[httpd] action install (webserver::default line 6)
    docker: [2016-02-23T12:08:53+00:00] INFO: yum_package[httpd] installing httpd-2.4.6-40.el7.centos from base repository
  ↓コンテナのエクスポート
==> docker: Exporting the container
==> docker: Killing the container: fac401834d82c5eb1d0d9e4a757e2919eb95215f3e1c3d5bc9c67d3250b7e43d
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)向けイメージの作成を例に紹介する。