Networking & DevOps

Docker Introduction

In the last few weeks I’ve been learning and playing with Docker.

Introduction

Docker is an open-source platform that enables you to build, ship and run applications in containers. A container is a lightweight and portable self-sufficient unit that includes:

  • The application code
  • Runtime
  • System tools and libraries
  • Settings

The beauty of containers is that it doesn’t matter what it runs on; the container could be running on Windows, Linux, a laptop, RasPi, or anything that can install Docker. As it’s self sufficient, it’s completely agnostic to the underlying OS/compute. It’s another way to virtualise compute resources that isn’t as heavy-handed as having to install a whole new Virtual Machine.

One or many containers can run on a single host, virtualising the host’s resources and creating separate runtimes for each container.

Networking

Docker networking is most interesting to me for some reason, and there a few different options you can distribute and network containers.

  • bridge (default): An isolated network for containers on a single host
  • host: Shares the host’s network stack
  • overlay: Enables multi-host networking (used in Docker Swarm)
  • macvlan: Assigns MAC addresses to containers for direct LAN access
  • none: No networking

Overlay is the type that Docker swarm uses, and is used to link containers hosted on different hosts, for a more distributed deployment.

Test Deployment

I followed a tutorial to spin up a NGINX reverse proxy which farms out requests to multiple flask servers. See diagram below.

I used a simple docker compose YAML file to spin up both the NGINX and Flask app.py containers, allowing me to create a distributed infrastructure with one command.

docker-compose.yml 
--
version: '3'
services:
web:
    build: ../flask_app
web2:
    build: ../flask_app
nginx:
    image: nginx:alpine
    ports:
    - 80:80
    volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
    - web
    - web2

The nginx container takes a prepopulated nginx config file to create the reverse proxy, distributing the HTTP requests across both Flask servers.
# nginx.conf
events {}
http {
    upstream app_servers {
        server web:5000;
        server web2:5000;
    }
    server {
        listen 80;
        location / {
            proxy_pass http://app_servers;
        }
    }
}

The Flask server hosts a simple Python that returns "Hello Docker!" and the hostname name of the container responding to the request:
#!/usr/bin/python3

# app.py
from flask import Flask
import socket
app = Flask(__name__)
@app.route('/')
def home():
    newstring = "Hello, Docker! Hostname is {0}\n".format(socket.gethostname())

    return newstring
if __name__ == '__main__':
    app.run(host='0.0.0.0')

Sending multiple requests shows that Nginx is load balancing the requests across each Flask server:

root@mgmt-host:~/docker# curl localhost
Hello, Docker! Hostname is d9bcfe1b35d0

root@mgmt-host:~/docker# curl localhost
Hello, Docker! Hostname is 36f88db2fc2b

root@mgmt-host:~/docker# curl localhost
Hello, Docker! Hostname is d9bcfe1b35d0

root@mgmt-host:~/docker# curl localhost
Hello, Docker! Hostname is 36f88db2fc2b

root@mgmt-host:~/docker# curl localhost
Hello, Docker! Hostname is d9bcfe1b35d0

root@mgmt-host:~/docker# curl localhost
Hello, Docker! Hostname is 36f88db2fc2b

Next steps is to create a truly distributed setup; using multiple different hosts to create a converged deployment!

Ansible

Ansible is a great bit of software that can streamline and remove human error in deploying and managing a large network. It automates the management of remote systems and controls their desired state.

  • The Control node is where Ansible is installed. This is where the network inventory is created and stored.
  • The Managed node(s) are the remote managed systems

I have deployed Ansible on a server that can reach my Arista nodes’ mgmt network:

Ansible obviously needs mgmt connectivity to the devices, it should fit in nicely along other network management resources which of course are firewalled with 5-tuple rules, zoned approapriatly alongside same-trust-level resources, and only have strictly-controlled web-proxy-based outbound access to the Internet (if at all).

My root Ansible directory contains a config file and 3 folders:

drwxr-xr-x   5 root root  4096 Jul  3 11:05 .
drwxr-xr-x 101 root root  4096 Jul  7 13:44 ..
-rw-r--r--   1 root root 40643 Jul  3 09:25 ansible.cfg
drwxr-xr-x   3 root root  4096 Jul  7 13:44 inventory
drwxr-xr-x   2 root root  4096 Jul  3 11:21 playbooks
drwxr-xr-x   2 root root  4096 Jul  3 11:05 templates

  • Inventory - this is where you define and organise your remote systems
  • Playbooks - playbooks are files that contain instructions and tasks to acheive the device management and configuration
  • Templates - if using JinJa2 in playbooks, this is where the device configuration templates are stored ready for access by the playbook

Inventory

The inventory can be organised in various different ways; I organise by device vendor and/or CLI language so I can easily apply playbooks to the whole vendor estate

root@mgmt-host:/etc/ansible/inventory# ls
arista

root@mgmt-host:/etc/ansible/inventory# cd arista/
root@mgmt-host:/etc/ansible/inventory/arista# ls
hosts.yaml  host_vars

Hosts.yaml is the inventory file where the devices are listed and organised into groups. “All” contains all the devices in this file, but you can use “children” to organise into arbitrary groups

root@mgmt-host:/etc/ansible/inventory/arista# cat hosts.yaml
all:
hosts:
    leaf1:
    leaf2:
    spine1:
children:
    leafs:
    leaf1:
    leaf2:
    spines:
    spine1:
vars:
    ansible_connection: ansible.netcommon.network_cli
    ansible_network_os: arista.eos.eos

“vars” contains variables common to the specific groups; here I’ve listed vars under “all” to apply to all the hosts in the inventory file.

root@mgmt-host:/etc/ansible/inventory/arista# cd host_vars/
root@mgmt-host:/etc/ansible/inventory/arista/host_vars# ls
leaf1.yaml  leaf2.yaml  spine1.yaml

“host-vars” contains files that allow arbitrary per-device variables; here I’ve defined hostname and vtep_ip. These can be accessed in the Jinja2 templating module

root@mgmt-host:/etc/ansible/inventory/arista/host_vars# cat leaf1.yaml
hostname: leaf1
vtep_ip: 10.0.0.1

Playbooks

Playbooks are lists of tasks and actions to perform on the remote devices. They are YAML files, and can have 1 or many tasks.

Ansible content collections can add content not included in the Ansible “core”. They are a way to add vendor or other content without building it directly into Ansible.

The Arista EOS collection is called “arista.eos” - there are many modules in the collection, including an arbitrary CLI and HTTP API module.

Ansible Arista EOS Collection

See an example of a Playbook below, updating all the management interface descriptions:

---

- name: Configure interface descriptions
hosts: all
gather_facts: false

tasks:

    - name: Add interface desc
    arista.eos.eos_interfaces:
        config:
        - name: Management1
            description: "configured by Ansible"
        state: merged
    become: true

When the playbook is run against the “arista” inventory, you get an output based of the Playbook’s success

root@mgmt-host:/etc/ansible# ansible-playbook playbooks/pb_interface_desc.yaml -i inventory/arista  -uadmin

PLAY [Configure interface descriptions] ***********************************************************************************************************

TASK [Add interface desc] *************************************************************************************************************************
changed: [leaf1]
changed: [spine1]
changed: [leaf2]

PLAY RECAP ****************************************************************************************************************************************
leaf1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
leaf2                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
spine1                     : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

root@mgmt-host:/etc/ansible#

Summary

There’s lots to unpack, but this is what’s meant as “infrastructure as code”; an entire network can be managed and maintained purely by building inventories and playbooks. Once you’ve got to grips with host_vars, the size of the network almost becomes irrelevant.

Current Skills

In my last post I posted the DevOps knowledge journey, allowing engineers to learn all the skills necessary. Here I want to do a gap analysis; what skills do I have compared to the Roadmap, and where do I need to direct my learning.

Learn Git

I have beginner to intermediate skills and knowledge of Git.

I have a public GitHub with various repos of personal and professional projects:

Tristan’s GitHub Profile

This is a great guide to introduce into git.

Git Introduction Guide

 

Learn Programming Languages

I have been using and learning Python for over 10 years in my capacity as Network Engineer/Design/Architect. I have completed various projects using Python that have led to a high success rate of the project

Using Python to interact with Juniper routers and switches proves especially powerful to manage a large estate

from jnpr.junos import Device 
   with Device(host=host_ip,user=username,passwd=password,port=22) as dev:
   test

DevOps Roadmap

A friend recommended to me the DevOps roadmap - a learning journey to develop the various DevOps and Infrastructure skills that are in most demand.

https://github.com/milanm/DevOps-Roadmap

My First Post

Overview

I wanted to set up a site that demonstrated my foray into DevOps skills, so as a project in and of itself, I’ve created this platform as a challenge and demonstration of my infrastructure skills.

  • Hosted on a Free SKU Azure VM
  • Uses GitHub for basic Version Controlling
  • Uses my bendall.co domain to create a Dynamic DNS entry for “devops.bendall.co” using ddclient package
  • VM is protected with a NSG to lockdown access for mgmt and web
  • VM is secured with non-standard SSH port and public/private keys enabling secure and passwordless login
  • NGINX is used as a reverse proxy for Hugo easy site builder (as I’m terrible at front-end)
  • Let’s Encrypt TLS Certificate used instead of self-signed
  • VSCode with Remote-SSH used for remote development; let’s face it, no-one should be coding in a terminal these days