PyAll. Or a Docker Container built with Packer and Ansible with all Python versions

  • Fri 02 January 2015

  • DISCLAIMER

    This ONLY works with an older version of Docker & Packer. My build environment is running Docker 1.2.0 and Packer 0.7.1. The latest Docker is 1.4.1 and Packer is at 0.7.5. I upgraded one of my systems to 1.4.1 and 0.7.5 and I can no longer build my containers. As I write this, I wonder how many ppl will be pissed off that they can't do what I'm about to show. Imagine how I feel!

    I'll often use a pet project as an excuse to play with a new technology, so this was largely a proof of concept of Packer.

    A pet project of mine needs an environment with all the versions of Python (2.6, 2.7, 3.1, 3.2, 3.4 and PyPy). I built a Docker container back in the day with a DockerFile. Dockerfiles absolutely work, there were some limitations, I haven't followed the project closely to know if they've been "fixed".

    Packer uses a JSON template to define a VM or Container. A template is made up of builders, provisioners, post-processors and variables https://packer.io/docs/templates/introduction.html. Packer has many builders and one of the supported builders is Docker. A builder simply defines the platform or technology (EC2/DO/Docker/GCE/OS/Paralles/QEMU/VB/VMware/Custom) to build the VM or Container on.

    If you're familiar with Vagrant (same project creator), Packer also has provisioners. Supported provisioners are shell scripts, file uploads, Ansible, Chef Client & Solo, both Puppet Masterless and Server, as well as Salt and a custom provisoner. Ansible is my go-to configuration management tool today (though the more I use it, the more I miss Salt and Chef).

    Here is what my template.json looks like:

           {
            "builders": [
                    {
                            "type": "docker",
                            "image": "ansible/ubuntu14.04-ansible:stable",
                            "commit": true
                    }
            ],
            "provisioners": [
                    {
                            "type": "ansible-local",
                            "playbook_file": "provisioning/container.yml"
                    }
            ],
            "post-processors":[
                    [
                    {
                            "type": "docker-tag",
                            "repository": "esacteksab/pyall",
                            "tag": "0.12"
                    }
                    ]
            ]
            }
    

    A quick "what does this all mean":

    In builders I use the type of docker and am using Ansible's Docker image from their Github repo (this gets us past the need for an initial shell provisioner in the Packer template to install Ansible before we use the Ansible provisioner).

    I define an ansible-local provisioner in the provisioners block, then define the path to my Ansible playbook.

    Once the container is created, and the playbook is provisioned, I leverage a post-processor with a type of docker-tag which simply tags the container and makes it easier to manage from Docker's perspective.

    How to build container:

    You build the container with something like:

    $ sudo packer build template.json
    

    Once the container is created, I can simply do a:

    sudo docker push esacteksab/pyall:0.12
    

    Then the container exists here on the Docker registry

    The Ansible playbook looks like this:

            - hosts: all
              tasks:
                - apt_repository: repo='ppa:fkrull/deadsnakes'
    
                - apt_repository: repo='ppa:pypy/ppa'
    
                - apt: pkg={{item}} state=installed update_cache=yes
                  with_items:
                      - python2.6
                      - python3.1
                      - python3.2
                      - python3.3
                      - python3.4
                      - pypy
                      - build-essential
                      - python-dev
                      - python-pycurl
                      - python-apt
                      - python-virtualenv
                      - python-pip
    
                - name: create virtualenvs
                  shell: /usr/bin/virtualenv -p {{ item.version }} {{ item.name }} chdir=/opt
                  with_items:
                      - { version: python2.6, name: py26 }
                      - { version: python2.7, name: py27 }
                      - { version: python3.2, name: py31 }
                      - { version: python3.2, name: py32 }
                      - { version: python3.3, name: py33 }
                      - { version: python3.4, name: py34 }
                      - { version: pypy, name: pypy }
    

    A quick "what's this do?"

    1. hosts: all -- I normally despise this and would rather target a specific host group or host(s), but since I know that I'm only targeting a single container, this is safe.
    2. Under tasks: I'm using the apt_repository module http://docs.ansible.com/apt_repository_module.html to add the repos necessary to install all the Python versions including PyPy.
    3. I'm using the apt module (Apt Module) to install the Python versions as well as common or required packages necessary to manage/leverage virtualenvs.
    4. I then use the shell module (Shell Module) to create the virtualenvs in /opt with the chdir=/opt parameter.

    Comments !

    social