Build Atomic Host with Customized Packages: Ansible Automation

Trishna Guha

Associate Software Engineer

Ansible by Red Hat

Red Hat Summit @ Sites, 2017

What is Project Atomic ?

  • Minimal Os
  • Intended to run Docker containers
  • Less number of packages to maintain
  • No more half way Upgrade of system

RPM-OSTree

Git for Operating system binaries

How we compose own tree?

Why Ansible?

  • Agentless
  • Simple
  • Secured
# requirements.yml

---
# This playbook installs requirements
- name: Install the list of required packages
  package: name={{ item }} state=installed
  with_items:
  - genisoimage
  - libvirt
  - libselinux-python
  - qemu-img
  - rpm-ostree
  - ostree
  - virt-install

- name: Download daemon rpm and Install
  command: "{{ item }}"
  with_items:
    - wget -P /tmp http://libslack.org/daemon/download/daemon-0.6.4-1.x86_64.rpm
    - rpm --install /tmp/daemon-0.6.4-1.x86_64.rpm
  ignore_errors: yes

Requirements

# setup.yml

- name: Creates Directory Structure - Installs requirements - Initializes OStree - Starts HTTP Server
  hosts: localhost
  become: yes
  become_method: sudo

  vars_files:
    - vars/buildrepo.yml

  tasks:
    - include: requirements.yml

    - name: Create directory structure
      file: path={{ item }} state=directory mode=750
      with_items:
        - "/srv/repo"
        - "/srv/cache"
        - "{{ abs_path }}"

    - name: Initialize OSTree repository
      command:
        ostree --repo=/srv/repo init --mode=archive

    - name: Running HTTP server at Port 35000, Use `ip addr` to check the IP Address...
      shell:
        /usr/local/bin/daemon -- ostree trivial-httpd -P 35000 --log-file - /srv

Directory Structure - Install requirements - Initialize OStree - Start HTTP Server

# compose.yml

---
# This playbook composes OSTree

- include_vars:
    file: vars/buildrepo.yml

- name: Clone Fedora-Atomic buildscripts
  git:
    repo={{ repo }}
    version={{ branch }}
    depth=1
    dest={{ abs_path }}/{{ repodir }}

- name: Create JSON file for adding packages
  file:
    path: '{{ abs_path }}/{{ repodir }}/{{ custommanifest }}'
    state: touch

- name: Template render for customized packages
  template:
    src: ./templates/customized-packages-template.j2
    dest: '{{ abs_path}}/{{ repodir }}/{{ custommanifest }}'
    owner: root
    group: root
    mode: 0700

- name: Compose Fedora-Atomic Tree
  command:
    chdir='{{ abs_path}}/{{ repodir }}'
    rpm-ostree compose tree --cachedir=/srv/cache --repo=/srv/repo ./{{ custommanifest }}

Compose OSTree

# create-vm.yml

---
# This playbook creates VM from the QCOW2 Image

- include_vars:
    file: vars/guests.yml

- name: Start libvirtd
  service: name=libvirtd state=started enabled=yes
  register: libvirtd

- name: Wait for libvirtd to start
  wait_for:
    timeout: 30
  when: libvirtd.changed

- name: Create tmpdir for cloud-init
  shell:
    mktemp -d
  register: tmpdir

- debug: msg="{{ tmpdir.stdout }}"

- name: Create cloud-init meta-data and user-data destinations
  file: path={{ item }} state=touch
  with_items:
    - '{{ tmpdir.stdout }}/meta-data'
    - '{{ tmpdir.stdout }}/user-data'

- name: cloud-init meta-data
  template:
    src: ./templates/meta-data.j2
    dest: '{{ tmpdir.stdout }}/meta-data'
    owner: root
    group: root
    mode: 0700

- name: cloud-init user-data
  template:
    src: ./templates/user-data.j2
    dest: '{{ tmpdir.stdout }}/user-data'
    owner: root
    group: root
    mode: 0700

- name: Create qcow2 file for the instance
  file:
    path: /var/lib/libvirt/images/{{ domain }}.qcow2
    state: touch

- name: Create iso file for the instance
  file:
    path: /var/lib/libvirt/images/{{ domain }}.cidata.iso
    state: touch

- name: Copy qcow2 image to the instance qcow2
  copy:
    src: '{{ path }}/{{ image }}.qcow2'
    dest: /var/lib/libvirt/images/{{ domain }}.qcow2

- name: Create a cloud-init metadata ISO Image
  shell: genisoimage 
         -input-charset default 
         -output /var/lib/libvirt/images/{{ domain }}.cidata.iso
         -volid cidata
         -joliet
         -rock
         -quiet
         {{ tmpdir.stdout }}/user-data
         {{ tmpdir.stdout }}/meta-data

- name: Create VM from QCOW2
  shell: virt-install --quiet --import --name={{ domain }}
         --os-variant={{ os.variant }} --ram={{ mem }} --vcpus={{ cpu }} 
         --disk path=/var/lib/libvirt/images/{{ domain }}.qcow2,format=qcow2,bus=virtio 
         --disk path=/var/lib/libvirt/images/{{ domain }}.cidata.iso,device=cdrom,readonly=on 
         --network network=default --noautoconsole

- name: Make sure that the VM is running
  virt: name={{ domain }} command=start
  ignore_errors: yes

Create VM from QCOW2 Image

# main.yml

---
# This Playbook installs requirements and Compose OSTree, Performs SSH Setup and Rebase on Own OSTree

- name: Main Playbook
  hosts: localhost
  become: yes
  become_method: sudo

  tasks:
  - include: create-vm.yml
  - include: compose.yml

  - name: Wait for 10s to retrieve the IP
    wait_for:
      timeout: 10

  - name: Retrieve the IP
    shell: virsh domifaddr atomic-node | tail -2| awk '{print $4}' | cut -d/ -f1
    register: vm_ip

  - debug: msg="{{ vm_ip.stdout }}"

  - name: Retrieve Absolute path for SSH public key
    shell: getent passwd 1000 | cut -d {{':'}} -f 6
    register: abs_dest

  - name: ssh-copy-id to the Atomic host
    shell: ssh-copy-id -i "{{ abs_dest.stdout }}"/.ssh/id_rsa.pub atomic-user@$"{{ vm_ip.stdout }}"

  - name: Add IP to atomichost group
    add_host: hostname="{{ vm_ip.stdout }}" groupname=atomichost ansible_ssh_user=atomic-user


- name: Rebase OSTree
  hosts: atomichost
  become: yes
  become_method: sudo

  vars_files:
    - vars/atomic.yml

  tasks:
  - name: Create remote named {{ atomicname }}
    command:
      ostree remote add "{{ atomicname }}" http://"{{ httpserver }}":35000/repo --no-gpg-verify

  - name: Rebase on my tree
    command:
      rpm-ostree rebase "{{ atomicname }}":"{{ basehost }}"

SSH Setup and Rebase on OSTree

# vars/atomic.yml

---
# Variables for Atomic host

atomicname: my-atomic
basehost: fedora-atomic/25/x86_64/docker-host
httpserver: 192.168.122.1

Variables for atomic host and domain

# vars/buildrepo.yml

---
# Variables for Atomic repository/OSTree packages

repo: https://pagure.io/fedora-atomic.git
branch: f25
repodir: fedora-atomic
abs_path: /workspace                                  # The absolute path to the git repo.
custommanifest: customized-atomic-docker-host.json    # The manifest that goes into the custom host(ostree) content that we are going to build.
sourcemanifest: fedora-atomic-docker-host.json        # The manifest that goes into the actual Base Fedora host(ostree) content.
packages: '"vim-enhanced", "git"'                     # Packages you want to have in your Atomic host.

Variables for building repo, adding packages

# vars/guests.yml

# Variables for Creating VM

domain: atomic-node
image: Fedora-Atomic-25-20170228.0.x86_64
cpu: 1
mem: 1536
os:
  variant: fedora23
path: /tmp

Variables for VM

Demo Time!

 

Tap the Button! Now don't YELL at me :-(!

Thank you

Red Hat Summit @ Sites, 2017 - Build Atomic Host with Customized Packages: Ansible Automation

By Trishna Guha

Red Hat Summit @ Sites, 2017 - Build Atomic Host with Customized Packages: Ansible Automation

Red Hat Summit @ Sites, 2017

  • 1,099