Help us understand the problem. What is going on with this article?

Packerを使ってAnsibleをDockerイメージにする

概要

Packerとは、簡単に言うと、マシンイメージを作成するオーケストレーションツールです。
Dockerイメージや、AWSのAMIを作成することもできます。
Vagrantなどを作っている、hashicorpのツールです。

https://www.packer.io/intro/index.html

今回は、このPackerを使って、AnsibleのPlaybookからDockerイメージを作成します。

仕組み

仕組みを簡単に説明すると、Packerでビルド環境を定義し、Packerが立ち上げたDockerコンテナでlocalモードでAnsibleを実行し、出来上がったコンテナをイメージ化するという仕組みになります。

環境

  • CentOS 7.5
  • packer 1.3.2

packerのインストール

以下から環境にあったバイナリをダウンロードします。
https://www.packer.io/downloads.html

Linux環境なので、以下を使います。

$ curl -O https://releases.hashicorp.com/packer/1.3.2/packer_1.3.2_linux_amd64.zip
$ unzip packer_1.3.2_linux_amd64.zip
$ sudo cp packer /usr/local/bin

ディレクトリ構成

後述のpacker.jsonの内容と合わせてご確認いただければと思いますが、こういうディレクトリ構成でpackerの環境を作ります。

.
├── packer.json
├── playbooks/
│   ├── group_vars/
│   ├── roles/
│   ├── site.yml
│   └── inventories
│       ├── group_vars/
│       └── hosts.sample
└── vault-password-file

ビルド環境と内容を定義

ビルド環境と内容をjsonで定義します。
jsonファイルの名前は何でも構いませんが、ここではpackage.jsonとします。

packger.json
{
    "builders":[{
        "type": "docker",
        "image": "centos:7",
        "export_path": "controller.tar",
        "privileged": "true",
        "run_command": ["-d", "-i", "-t", "{{.Image}}", "/sbin/init"]
    }],

    "provisioners":[{
        "type": "shell",
        "inline": [
            "yum clean all",
            "yum -y install epel-release",
            "yum -y install PyYAML",
            "yum -y install python-jinja2",
            "yum -y install python-httplib2",
            "yum -y install python-paramiko",
            "yum -y install python-setuptools",
            "yum -y install ansible",
            "mkdir -p /tmp/ansible-local"
        ]
    }, {
        "type": "file",
        "source": "playbooks/",
        "destination": "/tmp/ansible-local"
    }, {
        "type": "file",
        "source": "vault-password-file",
        "destination": "/tmp/ansible-local/vault-password-file.txt"
    }, {
        "type": "ansible-local",
        "playbook_file": "playbooks/site.yml",
        "staging_directory": "/tmp/ansible-local",
        "extra_arguments": [
            "-i /tmp/ansible-local/inventories/hosts.sample",
            "--vault-password-file=/tmp/ansible-local/vault-password-file.txt"
        ],
        "clean_staging_directory": true
    }],

    "post-processors": [{
        "type": "docker-import",
        "repository": "myname/image_name",
        "tag": "0.1"
    }]
}

内容を簡単に説明します。

packger.json
{
    # Ansibleを実行するDocker環境を定義します。docker-composeと大体同じです。
    "builders":[{
        "type": "docker",
        "image": "centos:7",
        "export_path": "controller.tar",
        "privileged": "true",
        "run_command": ["-d", "-i", "-t", "{{.Image}}", "/sbin/init"]
    }],
    # Ansibleを実行する環境を構築します。Dockerfileに当たる部分です。
    # Ansibleをインストールします。
    # また、作業ディレクトリとして/tmp/ansible-localを作成します。
    "provisioners":[{
        "type": "shell",
        "inline": [
            "yum clean all",
            "yum -y install epel-release",
            "yum -y install PyYAML",
            "yum -y install python-jinja2",
            "yum -y install python-httplib2",
            "yum -y install python-paramiko",
            "yum -y install python-setuptools",
            "yum -y install ansible",
            "mkdir -p /tmp/ansible-local"
        ]
    }, {
        # こちらは、ホストOSのファイルをコンテナ内にコピーする定義です。
        # Ansibleのファイルをすべてコピーします。
        "type": "file",
        # playbooks/{site.yml,roles,inventories,group_vars...}という構成だとします。
        "source": "playbooks/",
        # コンテナ内のコピー先ディレクトリです。
        "destination": "/tmp/ansible-local"
    }, {
        # vaultファイルを複合するパスワードが書かれたファイルです。
        "type": "file",
        # ホストOSで、複合パスワードを平文で書いたものを用意してください。
        "source": "vault-password-file.txt",
        "destination": "/tmp/ansible-local/vault-password-file.txt"
    }, {
        # Ansible実行時の引数などの定義です。
        # localモードで実行する場合は、ansible-local、sshの場合はansibleになります。
        "type": "ansible-local",
        # playbookのパスを指定します。
        "playbook_file": "playbooks/site.yml",
        # 以下にplaybookが設置されます。
        "staging_directory": "/tmp/ansible-local",
        "extra_arguments": [
            # localモードの場合、ansibleが勝手に-iオプションを付与しますが、それだけでは、inventories以下にあるgroup_varsが読み込まれません。
            # そのため、-iオプションが2つ付くことになりますが、以下でも定義します、。
            # また、inventory fileのパスを、「inventory_file」ディレクティブとして定義できますが、そうするとstaging_directory直下にコピーされ、意図した挙動になりません。
            "-i /tmp/ansible-local/inventories/hosts.sample",
            # vaultの複合パスワードが書かれたファイルを指定します。
            "--vault-password-file=/tmp/ansible-local/vault-password-file.txt"
        ],
        # ビルド完了後に、staging_directoryを削除するかどうかのフラグです。defaultはfalseです。
        "clean_staging_directory": true
    }],
    # 作成するDockerイメージの定義です。
    "post-processors": [{
        # Dockerイメージを作成し、ローカルに保存する。
        "type": "docker-import",
        # イメージ名
        "repository": "myname/image_name",
        # tag
        "tag": "0.1"
    }]
}

Ansible実行時の引数について

Dockerコンテナ内でroot権限でAnsibleを実行するので、sudoパスワードは不要です。
また、ansible-vaultの複合パスワードは、今回は--vault-password-fileでファイルで渡すようにします。(というか、interactiveなやり方がわかりません。)

ビルド実行

以下のコマンドを実行します。

$ sudo packer build -on-error=abort packer.json

そうすると、実際には以下のコマンドが実行されます。

$ cd /tmp/ansible-local && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 \
    ansible-playbook /tmp/ansible-local/site.yml \
    --extra-vars "packer_build_name=docker packer_builder_type=docker packer_http_addr=" \
    -i /tmp/ansible-local/inventories/hosts.sample \
    --vault-password-file=/tmp/ansible-local/vault-password-file.txt \
    -c local -i /tmp/ansible-local/packer-provisioner-ansible-local714008969

特にエラーがでなければ成功です。

Dockerイメージが作成されたか確認

ホストOS上にDockerイメージが作成されたことを確認します。

$ sudo docker images
REPOSITORY            TAG         IMAGE ID            CREATED            SIZE
myname/image_name     0.1         xxxxxxxxxxxx        1 minutes ago      846MB

connection: docker

ansibleのplaybookにconnection: dockerという行を追加すると、docker containerへの接続となる。
https://hiyoko-infratech.hateblo.jp/entry/2017/12/31/191043
https://dev.classmethod.jp/server-side/os/ansible-docker-connection-plugin/

- hosts: test_servers
  connection: docker
  tasks:
    - name: create directory
      file:
        path: /home/test/
        state: directory
        mode: 0755

    - name: create file
      file:
        path: /home/test/test.txt
        state: touch
        mode: 0644

コマンドにoptionを付ける場合

$ ansible-playbook -i "ansible-sync-test," reproduce.yml -c docker

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away