2

Reuse Ansible Roles To Build Docker Images - DZone Cloud

 2 years ago
source link: https://dzone.com/articles/how-to-reuse-your-ansible-roles-to-build-docker-im
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How To Reuse Your Ansible Roles To Build Docker Images With Packer

Follow this step-by-step tutorial to learn how to reuse your Ansible roles to build Docker images using Packer.

Join the DZone community and get the full member experience.

Join For Free

In this article, I will show you how to reuse your Ansible roles to build Docker images using Packer.
If you are like me, you have probably used Ansible for a while. You probably have Ansible roles that you use to install and manage software on-premise and VM. Now you want to reuse those roles to build your own custom Docker images to use for development, testing, or production.

This is possible and very useful with minor changes to the Ansible roles. In fact, with Docker, you start with a very minimalistic environment and you can't take for granted some components that you will find preinstalled on major Linux distros.

Prerequisites

  • You must know Ansible and have some scripts to run.
  • You must have Docker and Packer installed. Refer to the respective sites in order to install them.

In my setup, I've used a CentOS 7 box with Docker and Packer installed, because I know for sure that my Ansible roles run fine on that distro.

To verify that they are correctly installed, you can issue the following commands on the CentOS 7 bash:

vagrant@centosDocker ~> docker --version

Docker version 20.10.6, build 370c289

vagrant@centosDocker ~> packer --version

1.7.2

Ansible Roles and Packer Files

My Ansible roles are organized following the standard Ansible conventions. I have a directory called "provision" in which I have my Ansible roles and Packer files.

For example, I will create a MariaDB version 10.4 Docker image customized for my needs. The directory structure is as follows:

├── packer_centos7_mariaDB104.json

├── packer_MariaDB104-playbook.yml

├── roles

│ ├── cleanup

│ │ ├── defaults

│ │ ├── files

│ │ ├── tasks

│ │ │ └── main.yml

│ │ └── templates

│ ├── disable_firewalld

│ │ ├── defaults

│ │ │ └── main.yml

│ │ ├── files

│ │ ├── tasks

│ │ │ └── main.yml

│ │ └── templates

│ ├── MariaDB104_multi

│ │ ├── comandi.txt

│ │ ├── defaults

│ │ │ └── main.yml

│ │ ├── files

│ │ │ ├── master.zip

│ │ │ └── mysqlinit.sql

│ │ ├── tasks

│ │ │ ├── main.yml

│ │ │ └── sub.yml

│ │ └── templates

│ │ ├── mariadb.service

│ │ ├── my.cnf

│ │ ├── mysqlinit.sh

│ │ ├── mysql.server

│ │ ├── mysql_wrapper

│ │ └── swap_porte_mysql

│ │ └── main.yml

.

.

.

├── vars

Let's explain the relevant files.

Packer Template JSON

packer_centos7_mariaDB104.json is our entry point, and is a JSON file that describes what we are building and how (see this link).

This is the content of the file:

{
        "builders": [
                {
                        "name": "docker",
                        "type": "docker",
                        "image": "centos/systemd",
                        "commit": true,
                        "privileged": true,
                        "run_command": ["-d", "-i", "-t", "--entrypoint=/usr/sbin/init", "--", "{{.Image}}"]
                }
        ],
        "provisioners": [
                {
                        "type": "shell",
                        "script": "packer_install_python.sh"
                },
                {
                        "type": "ansible",
                        "playbook_file": "packer_MariaDB104-playbook.yml"
                }
        ],
        "post-processors": [
                {
                        "type": "docker-tag",
                        "repository": "mariadb104-image",
                        "tag": "1.0"
                }
        ]
}

Where:

builders: This contains an array of all the builders that Packer should use to generate machine images for the template. In this case, I'm using Docker to build my machine image. At this point, I've encountered my first problem with reusing my Ansible roles. The majority of my roles rely heavily on systemd (in the case of RedHat derivatives like CentOS) to launch and control my services; for example, MySQL. On a standard CentOS 7 Docker image, systemd is not present by default, because usually with Docker you start with a script for your single service.

To overcome this problem I've found two solutions:

  1. Use a Docker image with systemd installed like in the previous example. Note: In this case, the container must be run with privileged set to true. You must be very careful about this if you intend to use your image in production (see Docker documentation about this).
  2. Use this script that replaces systemctl command and doesn't need privileged rule. I suggest always using this approach because it is more secure and more general. With the first method, you have to find a Docker image of your preferred distro with systemd or other service manager installed, or create your own.

provisionersare the provisioners of our image. In this case, we have: 

  1. packer_install_python.sh is a Shell script that Packer will run in order to install Python (used by Ansible).
  2. packer_MariaDB104-playbook.yml is my Ansible playbook. The relevant aspect is that this is the same playbook that I use to install on bare-metal, Vagrant, and, thanks to Packer, everywhere!

Packer Template HCL2

As of Packer version 1.7.0, HCL2 is the preferred way to write Packer templates. You can use the hcl2_upgrade command to transition your existing Packer JSON template to HCL2. See this.

We use the command hcl2_upgrade to convert the previous file into HCL2 format:

packer hcl2_upgrade packer_centos7_mariaDB104.json

We obtain:

vagrant@centosDocker /v/c/a/provision> cat packer_centos7_mariaDB104.json.pkr.hcl

source "docker" "autogenerated_1" {
  commit      = true
  image       = "centos/systemd"
  privileged  = true
  run_command = ["-d", "-i", "-t", "--entrypoint=/usr/sbin/init", "--", "{{ .Image }}"]
}

build {
  sources = ["source.docker.autogenerated_1"]

  provisioner "shell" {
    script = "packer_install_python.sh"
  }

  provisioner "ansible" {
    playbook_file = "packer_MariaDB104-playbook.yml"
  }

  post-processor "docker-tag" {
    repository = "mariadb104-image"
    tag        = "1.0"
  }
}

The generated HCL2 template file contains three blocks:

  1. A source block that defines the builder Packer will use to build the image
  2. A build block
  3. A composite block that defines what Packer will execute when running packer build packer_centos7_mariaDB104.json.pkr.hcl

Packer Template HCL2 Update

We have run the hcl2_upgrade command to automatically map and generate the relevant HCL2 block.

We rename these blocks to accurately represent and describe the resource:

  1. We add a variable block that defines the image variable.
  2. We changed the source and build blocks.
  3. Since this Packer template uses the Docker v0.0.7 plugin, we add the required_plugins Packer block to the top of the file. This ensures Packer will retrieve the Docker plugin that fulfills the version constraint, so we can consistently generate an image from this template.
  4. We have to correct the post-processor "docker-tag" that is not accurately converted.

The final file is:

packer {
  required_plugins {
    docker = {
      version = ">= 0.0.7"
      source = "github.com/hashicorp/docker"
    }
  }
}

variable "image" {  
  type    = string  
  default = "centos/systemd"
}


source "docker" "centos_systemd" {
  commit      = true
  image       = "${var.image}"
  privileged  = true
  run_command = ["-d", "-i", "-t", "--entrypoint=/usr/sbin/init", "--", "{{ .Image }}"]
}

build {
  sources = ["source.docker.centos_systemd"]

  provisioner "shell" {
    script = "packer_install_python.sh"
  }

  provisioner "ansible" {
    playbook_file = "packer_MariaDB104-playbook.yml"
  }

  post-processor "docker-tag" {
    repository = "mariadb104-image"
    tag        = ["1.1"]
  }
}

Build the Image

First, we initialize the template.

vagrant@centosDocker /v/c/a/provision> packer init packer_centos7_mariaDB104.json.pkr.hcl
Installed plugin github.com/hashicorp/docker v1.0.3 in "/home/vagrant/.packer.d/plugins/github.com/hashicorp/docker/packer-plugin-docker_v1.0.3_x5.0_linux_amd64"

We can now build the image.

vagrant@centosDocker /v/c/a/provision> packer build packer_centos7_mariaDB104.json.pkr.hcl
docker.centos_systemd: output will be in this color.

==> docker.centos_systemd: Creating a temporary directory for sharing data...
==> docker.centos_systemd: Pulling Docker image: centos/systemd
    docker.centos_systemd: Using default tag: latest
    docker.centos_systemd: latest: Pulling from centos/systemd
    docker.centos_systemd: Digest: sha256:09db0255d215ca33710cc42e1a91b9002637eeef71322ca641947e65b7d53b58
    docker.centos_systemd: Status: Image is up to date for centos/systemd:latest
    docker.centos_systemd: docker.io/centos/systemd:latest
==> docker.centos_systemd: Starting docker container...

...

    docker.centos_systemd:
    docker.centos_systemd: PLAY RECAP *********************************************************************
    docker.centos_systemd: default                    : ok=61   changed=41   unreachable=0    failed=0    skipped=0    rescued=0    ignored=1
    docker.centos_systemd:
==> docker.centos_systemd: Committing the container
    docker.centos_systemd: Image ID: sha256:095b3d3c2b9435a0e5eab51b02a99df0cff2a24106b40afb1a17080363f43e2f
==> docker.centos_systemd: Killing the container: 5e98ca0d8b54c107682001f77ab5befc8f685b58b67db58b499079afe4f824fe
==> docker.centos_systemd: Running post-processor:  (type docker-tag)
==> docker.centos_systemd (docker-tag): Deprecation warning: "tag" option has been replaced with "tags". In future versions of Packer, this configuration may not work. Please call `packer fix` on your template to update.
    docker.centos_systemd (docker-tag): Tagging image: sha256:095b3d3c2b9435a0e5eab51b02a99df0cff2a24106b40afb1a17080363f43e2f
    docker.centos_systemd (docker-tag): Repository: mariadb104-image:1.1
Build 'docker.centos_systemd' finished after 11 minutes 23 seconds.

==> Wait completed after 11 minutes 23 seconds

==> Builds finished. The artifacts of successful builds are:
--> docker.centos_systemd: Imported Docker image: sha256:095b3d3c2b9435a0e5eab51b02a99df0cff2a24106b40afb1a17080363f43e2f
--> docker.centos_systemd: Imported Docker image: mariadb104-image:1.1 with tags mariadb104-image:1.1

As you can see, Docker has invoked Ansible and provisioned the container from which a Docker image has been committed.

If we list the Docker images, we will find our newly created image ready to be used:

vagrant@centosDocker /v/c/a/provision> docker images
REPOSITORY                     TAG       IMAGE ID       CREATED          SIZE
mariadb104-image               1.1       095b3d3c2b94   28 minutes ago   3.68GB

That is all!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK