A brief introduction

Christian Claus

Agenda

  • Introduction 
  • Theory and demo
  • Best practices
  • Useful notes
  • Wrap up

Introduction

Use cases

  • Provisioning
  • Provisioning
  • Config management
  • App deployment
  • Continuous Delivery
  • Security & Compliance
  • Orchestration

What it's all about

  • Agentless: ssh and python
  • Single point of truth
  • Declarative yaml 
  • Templating
  • Idempotency
  • Encryption capabilities
  • Open Source

A very brief history

  • 2012: Developed by Michael DeHaan (func & Cobbler)
  • 2015: Acquired by Red Hat Inc.
  • September 2017: Ansible Tower was opensourced as AWX
  • Today: Ansible Engine was introduced as enterprise product

More keyfacts

  • 1,300+ modules
  • 25,000+ Github stars
  • 250+ Meetups worldwide
  • 2,900+ Community contributors 

Theory and demo

Describe your Infrastructure

web1, web2

mail

db1

Describe your Infrastructure

mail.example.com

[webservers]
web1.example.com
web2.example.com

[dbservers]
db1.example.com
inventory.ini
all:
  hosts:
    mail.example.com:
  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:
    dbservers:
      hosts:
        db1.example.com:
inventory.yml

Directory layout

.
├── ansible.cfg               # global settings 
├── group_vars/               # variables shared for whole group
│   ├── all.yml
│   ├── dbservers.yml
│   └── webservers.yml
├── host_vars/                # variables for specific hosts
│   ├── db1.example.com.yml
│   ├── mail.example.com.yml
│   ├── web1.example.com.yml
│   └── web2.example.com.yml
├── roles/                    # shared behaviors
    ├── ...
├── inventory.yml             # infrastructure definition
├── dbservers.yml             # dbservers playbook
└── webservers.yml            # webservers playbook

Ad-hoc commands

  • Execute single task on specific hosts
  • or even on groups

Modules

Playbooks

  • An ordered set of tasks
  • Declarative state definitions
  • Not used for programming - just yaml!
  • Self documented

Playbooks in a nutshell

---

- name: Provision webservers
  hosts: webservers
  gather_facts: yes
  serial: 1
  tasks:
  - name: Ensure nginx is installed
    apt:
      name: nginx
      state: present
      update_cache: yes
  - name: Ensure nginx is started and enabled to start at boot
    service:
      name: nginx
      state: started
      enabled: yes

Variables

hostname: foo.example.com

api:
  base_path: /api
  user: bar
  password: secret
- name: Call status endpoint 
  get_url:
    url: "https://{{ hostname }}{{ api.base_path }}/status"
    dest: /var/app.status
    url_username: "{{ api.username }}"
    url_password: "{{ api.password }}"
https://foo.example.com/api/status

Example: Database setup

  • Postgresql
  • Unix-User app1, app2, ...
  • Unix-User shall have one or multiple authorized ssh-keys
  • Each Unix-User is also a Postgres-User
  • Each Unix-User has a database with the same name

Jinja2 Templates

# {{ ansible_managed }}

# ======================== Elasticsearch Configuration =========================
cluster.name: {{ elasticsearch.cluster.name }}
node.name: {{ ansible_hostname }}
path.data: {{ elasticsearch.path.data }}
path.logs: {{ elasticsearch.path.logs }}
network.host: ["127.0.0.1", "{{ internal_network_address }}"]
http.port: {{ elasticsearch.http.port }}
discovery.zen.ping.unicast.hosts: [ "{{ groups['elasticsearch'] | map('extract', hostvars, ['internal_network_address']) | join('", "') }}"]
# Ansible managed

# ======================== Elasticsearch Configuration =========================
cluster.name: es-dev-cluster
node.name: es-dev1
path.data: /home/es/data
path.logs: /home/es/logs
network.host: ["127.0.0.1", "10.0.0.1"]
http.port: 9200
discovery.zen.ping.unicast.hosts: ["10.0.0.1", "10.0.0.2", "10.0.0.3"]

Handlers

...

tasks:
- name: Update nginx config
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: restart nginx

handlers:
- name: restart nginx
  service:
    name: nginx
    state: restarted

Reusable playbooks

  • Import other files (include vs. import)
  • Use roles for complete encapsulated functionality
  • Share functionality between playbooks
  • DRY-principle
  • Reduces playbook complexity

Variable encryption

  • Encrypt single fields
  • Encrypt / Decrypt whole files
  • RFC2104 style HMAC encryption

Ansible galaxy

Best practices

Setup

  • Use one control node
  • Add your files to VCS
  • Let the directory structure fit your needs
    (multiple stages, inventories, etc.)

Conventions

  • Use meaningful variable names
  • Use pure yaml
  • Name your tasks!
  • Seperate playbooks for:
    • provisioning
    • configuration
    • deployment
  • Add verbosity parameter for debug output

Complexity kills

  • Keep playbooks simple
  • Better to repeat than to increase abstraction level
  • Don't code, think declarative
  • Solve your use case, not the world's

Test your services

  • Check if a service is up and running
  • Check if a service responds correctly
- name: Wait until server is started
  wait_for: 
    port: "{{ server.port }}" 
    delay: 10
    sleep: 5
    timeout: 60
  
- name: Check for valid response
  uri:
    url: http://localhost/api/status
    return_content: yes
  register: result
  until: '"status: up" in result.content'
  retries: 10
  delay: 1

Useful notes

Wrap up

  • Powerful tool for (Dev-)Ops 
  • Easy to learn and understand
  • Self-documented and transparent
  • Traceable config and system changes

Thanks :)

deck

By chclaus

deck

  • 1,314