使用Packer制作Docker镜像
使用Docker非常方便。
当我们不想在主机上弄乱机器时,虽然不至于太夸张,但通过Docker运行容器非常方便。
为了使用Docker,我们需要一个镜像,而默认情况下可以使用 Dockerfile 进行配置。但如果可以的话,最好使用最近流行的专用工具Ansible来进行配置。(因为可以共享playbook)。如果使用由Vagrant知名公司HashiCorp发布的Packer,就可以实现这一点。
Pakcer是什么?
Packer不仅仅是Docker,还专门用于创建用于虚拟容器的映像的自动执行工具。
-
- EC2 (AMI)
-
- Azre
-
- DigitalOcan
-
- Docker
-
- Google Compute Engine
-
- OpenStack
-
- Parallels
-
- QEMU
-
- VirtualBox
- VMware
几乎所有虚拟化容器都能被支持。
需要的东西
只需要从下载页面安装软件包就可以了。
使用方法 (shǐ fǎ)
我們將創建一個包含有關如何建立映像的Packer設置文件(json)。
設定文件由以下三個部分組成。
-
- builders: どの仮想コンテナ用のコンテナを作成するか。
-
- provisioners: どうプロビジョニングするか。
- post-processors: 作成したイメージをどうするか?
{
"builders":[
...
],
"provisioners":[
...
],
"post-processors": [
...
]
}
每个部分都可以指定多个项目。
建筑商部分
{
"builders":[{
"type": "docker",
"image": "basebox/common:0.1",
"export_path": "controller.tar",
"pull": false
}]
}
这次我们将创建一个Docker镜像,因此在type字段中指定”docker”,并将基础镜像的名称指定为image字段。
export_path字段用于指定用于临时保存镜像的文件名,但最后会自动删除,所以只需指定一个任意的名称即可。
如果使用本地存储库的镜像,则需要指定 pull: false。
提供者部分的部分
这个类型指定了要使用什么样的配置器。
在ansible的情况下,可以指定以下两种类型。
-
- ansible-local: イメージ内に既にインストールされているansibleを使って、自分自身に対してプロビジョニングする。
-
- ansible: 対象イメージにsshでリモート接続してプロビジョニングする。(通常のansibleと同じ)
EC2などはこちらを使う。
{
"provisioners":[{
"type": "shell",
"inline": [
"apt-get -y update",
"apt-get install -y --no-install-recommends ansible",
"mkdir -p /tmp/ansible-local"
]
}, {
"type": "file",
"source": "files",
"destination": "/tmp/ansible-local"
}, {
"type": "ansible-local",
"playbook_file": "playbook.yml",
"staging_directory": "/tmp/ansible-local"
}]
}
这次我们将使用前者的ansible-local方法。
因此,在ansible(-local)提供者之前,我们需要放置一个shell提供者来安装ansible。
此外,为了在ansible中使用copy模块,需要在镜像内创建一个保存目标文件的目录(/tmp/ansible-local),并通过file提供程序将文件传输到该目录。
如果准备工作做得足够充分,剩下的就会很简单了。
只需使用ansible(本地)配置管理工具,指定playbook即可。
ansible的清单文件将由packer自动生成。
后处理器部分 qì
{
"post-processors": [{
"type": "docker-import",
"repository": "category/image",
"tag": "0.1"
}]
}
指定如何处理生成的映像。
这次我们决定使用”docker-import”将映像注册到本地。
最后的packer.json
{
"builders":[{
"type": "docker",
"image": "basebox/common:0.1",
"export_path": "controller.tar",
"pull": false
}],
"provisioners":[{
"type": "shell",
"inline": [
"apt-get -y update",
"apt-get install -y --no-install-recommends ansible",
"mkdir -p /tmp/ansible-local"
]
}, {
"type": "file",
"source": "files",
"destination": "/tmp/ansible-local"
}, {
"type": "ansible-local",
"playbook_file": "playbook.yml",
"staging_directory": "/tmp/ansible-local"
}],
"post-processors": [{
"type": "docker-import",
"repository": "category/image",
"tag": "0.1"
}]
}
运行Packer。
执行非常简单。
$packer build <ビルドファイル>.json
从容器的生成到自动进行配置和保存映像。
执行结果
$ packer build -on-error=abort packer.json
docker output will be in this color.
==> docker: Creating a temporary directory for sharing data...
==> docker: Pulling Docker image: ubuntu:16.10
docker: 16.10: Pulling from library/ubuntu
docker: Digest: sha256:063e14a48414f6e66ec0e6570ae0f846623e08ff9416b280f560898bac7e7e63
docker: Status: Image is up to date for ubuntu:16.10
==> docker: Starting docker container...
docker: Run command: docker run -v /Users/h_toda/.packer.d/tmp/packer-docker793412964:/packer-files -d -i -t ubuntu:16.10 /bin/bash
docker: Container ID: dd34a7aeeba9a3371d6c7ba84a215b145afaaea5d013676d6d1111f5647f9f12
==> docker: Provisioning with shell script: /var/folders/d1/c3mwd9kn20vdng53mywr181xm5hflj/T/packer-shell812644666
docker: Get:1 http://security.ubuntu.com/ubuntu yakkety-security InRelease [92.2 kB]
docker: Get:2 http://archive.ubuntu.com/ubuntu yakkety InRelease [247 kB]
docker: Get:3 http://security.ubuntu.com/ubuntu yakkety-security/universe Sources [3526 B]
(snip)
docker: Setting up ansible (2.1.1.0-1) ...
docker: Processing triggers for libc-bin (2.24-3ubuntu1) ...
docker: Processing triggers for ca-certificates (20160104ubuntu1) ...
docker: Updating certificates in /etc/ssl/certs...
docker: 173 added, 0 removed; done.
docker: Running hooks in /etc/ca-certificates/update.d...
docker: done.
==> docker: Uploading files => /tmp/ansible-local
==> docker: Provisioning with Ansible...
docker: Creating Ansible staging directory...
docker: Creating directory: /tmp/ansible-local
docker: Uploading main Playbook file...
docker: Uploading inventory file...
docker: Executing Ansible: cd /tmp/ansible-local && ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 ansible-playbook /tmp/ansible-local/playbook.yml -c local -i /tmp/ansible-local/packer-provisioner-ansible-local607385102
docker:
docker: PLAY [all] *********************************************************************
docker:
docker: TASK [setup] *******************************************************************
docker: ok: [127.0.0.1]
docker:
docker: TASK [debug] *******************************************************************
docker: ok: [127.0.0.1] => {
docker: "msg": "Hellow, World!"
docker: }
docker:
docker: PLAY RECAP *********************************************************************
docker: 127.0.0.1 : ok=2 changed=0 unreachable=0 failed=0
docker:
==> docker: Exporting the container
==> docker: Killing the container: dd34a7aeeba9a3371d6c7ba84a215b145afaaea5d013676d6d1111f5647f9f12
==> docker: Running post-processor: docker-import
docker (docker-import): Importing image: Container
docker (docker-import): Repository: test/test:0.1
docker (docker-import): Imported ID: sha256:8421edb057bd9ca2bb8c33cba75979f0beb64476a957fb195c3d0b8b7927e67b
Build 'docker' finished.
调试方法
默认情况下,当Packer内部发生错误时,Docker容器会被删除,导致无法找出错误原因。如果添加-on-error=abort选项,容器将不会被删除,这样就可以使用docker exec进入容器中进行调查。
$packer build -on-error=abort <构建文件>.json
与Dockerfile相比的优点
在Dockerfile中,每个命令都会提交一个镜像。
删除上传后的图像中的文件也不会使其变小,所以没有必要这样做。
RUN apt-get update && \
apt-get install <なんとか> && \
apt-get remove <なんとか> && \
rm -r <なんとか>
就像使用串联方式将每个命令使用“&&”连接起来,作为一个步骤执行的批处理技巧一样,但在Packer中,不需要担心这样的问题。
请注意
在启动Docker容器时,我们要启动bash。如果使用像Alpine Linux这样默认没有安装bash的镜像,则需要在run_command中指定替代的shell(/bin/sh)。
"builders":[{
"type": "docker",
"image": "alpine:edge",
"export_path": "controller.tar",
"run_command": ["-d", "-i", "-t", "{{.Image}}", "/bin/sh"]
}]
请参阅
- Pakcer ドキュメント