Ansible
Putting IaC in Your CI/CD

Presented by Zackary Lowery
on June 22nd, 2020 at Leading EDJE.

Available online at https://presentations.xcjs.com/

Press your space bar or swipe to navigate down and then forward. ยป

Traits of IaC Tooling

Selectively sourced from Gruntwork's Why we use Terraform and not Chef, Puppet, Ansible, SaltStack, or CloudFormation

The Primary Trait of IaC Tooling

Configuration Management Provisioning
Manage existing infrastructure through software installation and configuration. Supply new infrastructure such as servers, network routes, or other systems.

Secondary Traits of IaC Tooling

Trait Types
Mutability Mutable vs. Immutable
Syntax Declarative vs. Procedural
Mastered Master vs. Masterless
Agented Agented vs. Agentless

Utilized Technologies

Configuration Management
Version Control and CI/CD
Ansible Logo
GitLab Logo

Ansible Traits

  • Configuration Manager*
    * primarily out of the box
  • Declarative*
    * when used as a configuration manager
  • Mutable
  • Masterless
  • Agentless

What do I like about Ansible?

  • Supports Idempotent Operations
  • Utilizes SSH
  • Cross-Platform
  • Iterative

Why am I Using Ansible?

Host OS Primary Role
X99 Windows 10 Logo Primary Client
USERV Ubuntu Logo Personal Server
XCJS Ubuntu Logo Personal Web Site
CLOUD Ubuntu Logo Cloud Server
UDEV2 Ubuntu Logo Software Development
MDEV Windows 10 Logo Third Monitor
MDEV2 Windows 10 Logo Software Development
GNUBEE Debian Logo NAS
AR4 Windows 10 Logo Gaming Console
XPS410 Ubuntu Logo Folding@home Client
LAKKA Lakka Logo Emulation Gaming Console
K8S[0-4] Raspbian Logo Kubernetes Cluster
XPSM170 Ubuntu Logo Retired (x86 Only)

Getting Started with Ansible

Inventory File

  • Lists hosts available to configure
  • Available in INI or YAML flavors
  • You're going to want to use YAML
  • Hosts can be grouped
  • Usually named "hosts", "hosts.ini", or "hosts.yml"

Roles

  • Interdependent or independent collection of variables, tasks, files, templates, or modules
  • Basic building block of a playbook

Tasks

  • Call to an Ansible module
  • Each task usually provides a set of return values that can optionally be used later

Playbook

  • Assigns roles to hosts from the inventory
  • Assignments can be listed by host, several hosts, or a group
  • Usually named "site.yml" or by another logical grouping such as geographical location

Variables

  • Variables can be referenced, assigned, or looped over
  • Accessed via the Jinja2 template language within tasks or file templates

Variable Assignment

  • In the playbook
  • Task result via the "register" keyword
  • Set from the command line

Variable Scope

  • Global
  • Playbook
  • Host

Facts

Facts are variables gathered from the host before Ansible begins the playbook.

Typical Filesystem Hierarchy

  • roles/
    • example-role/
      • files/
      • tasks/
        • main.yml
      • templates/
  • hosts.yml
  • site.yml

Using Ansible at Home

Inventory Example

hosts.yml


							all:
							  hosts:
							    ungrouped-host:
							      ansible_*: Override Ansible config/fact
							      vars:
							        host_var: example value
							  vars:
							    global_var: example value

							  children:
							    group:
							      hosts:
							        grouped-host:
							          # Each host can have its own vars/overrides.
						

My Inventory

inventory.yml (instead of hosts.yml)

  • Specialized hosts such as GNUBEE and LAKKA are ungrouped
  • Remaining hosts are grouped as either clients or servers

Applications as Roles

roles/build-essential/tasks/main.yml


							---
							- name: Install build-essential
							  apt:
								name: build-essential
								update_cache: yes
						

Dependent Roles

roles/nodejs/tasks/ubuntu.yml


							---
							- name: Require the build-essential role
							  include_role:
							    name: build-essential

							- name: Execute the Node.js 12 LTS install script
							  script: node_setup_12.x

							- name: Install nodejs
							  apt:
							    name: nodejs
							    update_cache: yes
						

Containers as Roles

roles/plex/tasks/ubuntu.yml


							# ...
							- name: Start the plex container
							  docker_compose:
							    build: yes
							    project_src: /tmp/ansible/roles/plex/files/
							  environment:
							    # Lines that begin with Jinja variables need to
							    # be quoted to avoid defining a YAML dictionary
							    DOCKER_DATA: "{{ docker_data }}"
							    PLEX_GID: "{{ plex_group.gid }}"
							    PLEX_UID: "{{ plex_user.uid }}"
							    PLEX_MEDIA: "{{ plex_media }}"
						

Containers as Roles: Host Variables

inventory.yml


							---
							all:
							  vars:
							    docker_data: /srv/
							    plex_media: /mnt/cloud/media
							  hosts:
							    children:
							      servers:
							        hosts:
							          cloud.xcjs.com:
							            vars:
							              hostname: CLOUD
						

Containers as Roles: Return Values

roles/plex/tasks/ubuntu.yml


							# ...
							- name: Create the plex user
							  user:
							    name: plex
							    create_home: no
							    system: yes
							    group: plex
							  # Registers task result as a variable named plex_user
							  register: plex_user
							# ...
							# Use plex_user.uid later to get the user's ID
						

Supporting Offline Hosts

site.yml


							# ...
							- name: Clients
							  hosts: clients
							  ignore_errors: true
							  ignore_unreachable: true
							  roles:
							# ...
					

Supporting Windows in the Inventory

  1. Windows 10 added OpenSSH support in April 2018
  2. Ansible assumes Linux = SSH, Windows = WinRM
OpenSSH as a feature of Windows 10

Supporting Windows in the Inventory: Example

inventory.yml


							# ...
							x99.local:
							  ansible_become: false
							  ansible_connection: ssh
							  ansible_distribution: Microsoft Windows 10 Professional
							  ansible_shell_type: cmd
							  ansible_os_family: windows
							  ansible_user: Zack
							  vars:
							    docker_data: C:/srv/
							# ...
						

Supporting Windows in Tasks: Main

roles/firefox/tasks/main.yml


							---
							- name: Execute the Ubuntu task list
							  when: ansible_distribution == "Ubuntu"
							  include_tasks: ubuntu.yml

							- name: Execute the Windows task list
							  when: ansible_os_family == "windows"
							  include_tasks: windows.yml
						

Supporting Windows in Tasks: Ubuntu

roles/firefox/tasks/ubuntu.yml


							---
							- name: Install firefox
							  apt:
							    name: firefox
							    update_cache: yes

						

Supporting Windows in Tasks: Windows

roles/firefox/tasks/windows.yml


							---
							- name: Install firefox
							  win_chocolatey:
							    name: firefox
							    state: latest
						

Future Endeavors

  • Discover/reverse-engineer specialized configurations
  • Certificate management
  • More roles
  • More Docker (Ed LeGault intensifies)
  • More environment variables with persistence
  • Object storage for artifact acquisition

CI/CD with GitLab

Branching Strategy

Branch Stages Jobs
feature/*
(from master)
(to staging)
  1. Lint
  2. Test
  1. Lint
  2. Test Ubuntu 18.04 VM
  3. Test Windows 10 VM
staging
(to master)
  1. Lint
  2. Test
  1. Lint
  2. Ansible Dry Run
master
  1. Lint
  2. Test
  3. Deploy
  1. Lint
  2. Ansible Dry Run
  3. Deploy

Lint

.gitlab-ci.yml


							---
							# ...
							Lint Playbooks:
							  stage: Lint
							  script:
							    - ansible-playbook -i 127.0.0.1, --syntax-check site-test-ubuntu.yml
							    - ansible-playbook -i 127.0.0.1, --syntax-check site-test-windows10.yml
							    - ansible-playbook -i inventory.yml --syntax-check site.yml
							# ...
						

Test: Virtual Machine Run

.gitlab-ci.yml


							---
							# ...
							Test Ubuntu 18.04:
							  except:
							    refs:
							      - master
							      - staging
							  stage: Test
							  before_script:
							    - vagrant destroy --force
							    - vagrant plugin install vagrant-vbguest
							    - vagrant box update
							  script:
							    - vagrant up ubuntu
							  after_script:
							    - vagrant destroy --force
							# ...
						

Test: Dry Run

.gitlab-ci.yml


							---
							# ...
							Dry Run:
							  only:
							    refs:
							      - master
							      - staging
							  stage: Test
							  script:
							    - ansible-playbook -i inventory.yml --check site.yml
							# ...
						

Deploy

.gitlab-ci.yml


							---
							# ...
							Deploy:
							  only:
							    refs:
							      - master
							  stage: Deploy
							  script:
								- ansible-playbook -i inventory.yml site.yml
							# ...
						

End

Continue to CI/CD Optimization with a Case Study Involving Ansible and GitLab.

Return to the rest of the presentations.