Skip to content

Alexxfromgit/DevOps_Automatic_deployment_of_a_2_VM_infrastructure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Task 10.12.3 — Automated 2-VM Infrastructure Deployment

Overview

A bash script that fully automates the deployment of a two-VM KVM infrastructure using libvirt, virt-install, and cloud-init. A single script run provisions three virtual networks, creates and boots both VMs with correct network configurations, sets up a VxLAN overlay tunnel between them, installs Docker on both machines, and deploys an NGINX reverse proxy (with TLS termination) on vm1 forwarding traffic to an Apache container on vm2.

This task builds on:

  • task10_12 — infrastructure creation and container deployment
  • task6_7 — NGINX reverse proxy with SSL/TLS termination

Infrastructure Topology

Assignment Topology
Figure 1 — Assignment Topology

Networks

Three libvirt networks are created from XML templates:

Network Type DHCP Notes
external NAT Yes Single static DHCP entry for vm1's MAC address
internal Isolated No No IP addresses assigned; used only for VM-to-VM L2 connectivity
management Isolated No Host IP assigned to provide L3 connectivity between host and VMs for SSH access

Virtual Machines

VM Interfaces Role
vm1 external · internal · management NGINX reverse proxy, NAT gateway for vm2
vm2 internal · management Apache backend

cloud-init configuration

Both VMs are configured at first boot via cloud-init ISO drives:

vm1

  • Hostname set to vm1
  • Public SSH key injected
  • external interface: DHCP
  • internal interface: static IP
  • management interface: static IP
  • IP forwarding + iptables NAT so that vm2's traffic reaches the internet through vm1's external interface
  • VxLAN tunnel to vm2 created on boot
  • docker-ce installed from the official repository
  • NGINX container (nginx:1.13) started via Docker Compose with mounted nginx.conf, TLS certificates, and log directory

vm2

  • Hostname set to vm2
  • Public SSH key injected
  • internal interface: static IP; default gateway = vm1's internal IP; DNS = 8.8.8.8
  • management interface: static IP
  • VxLAN tunnel to vm1 created on boot
  • docker-ce installed from the official repository
  • Apache container (httpd:2.4) started via Docker Compose, listening on the VxLAN interface

Scripts

task10_12_3.sh

Entry point — run as root from the repository directory. Performs the following in order:

  1. Sources config and creates all required working directories
  2. Generates an RSA SSH key pair at the path specified by SSH_PUB_KEY
  3. Generates an OpenSSL SAN config, a root CA (root-ca.crt / root.crt), and a signed server certificate (web.crt / web.key) for vm1's external IP
  4. Generates docker/etc/nginx.conf pointing to vm2's VxLAN IP and the configured Apache port
  5. Generates Docker Compose files for both vm1 (NGINX) and vm2 (Apache)
  6. Generates user-data and meta-data cloud-init files for both VMs
  7. Copies Docker assets into the vm1 config-drive staging directory
  8. Builds ISO config drives for both VMs with mkisofs
  9. Creates and starts the three libvirt networks from generated XML files
  10. Downloads the Ubuntu 16.04 base image, copies and resizes it for each VM
  11. Provisions vm1 with virt-install, waits 5 minutes for cloud-init to complete
  12. Provisions vm2 with virt-install

down.sh

Teardown script — destroys both VMs and all three networks, then removes their disk image directories so the environment can be rebuilt from scratch:

bash down.sh

Required Repository Structure

The following hierarchy must exist after task10_12_3.sh completes:

WORKDIR
├── config                          # configurable parameters file
├── task10_12_3.sh                  # main script (entry point)
├── config-drives
│   ├── vm1-config
│   │   ├── meta-data               # vm1 cloud-init meta-data
│   │   └── user-data               # vm1 cloud-init user-data
│   └── vm2-config
│       ├── meta-data               # vm2 cloud-init meta-data
│       └── user-data               # vm2 cloud-init user-data
├── docker
│   ├── etc
│   │   └── nginx.conf              # NGINX configuration file
│   └── certs
│       ├── root.crt                # root CA certificate
│       ├── web.crt                 # NGINX server certificate (chain)
│       └── web.key                 # NGINX private key
└── networks
    ├── external.xml                # external network XML definition
    ├── internal.xml                # internal network XML definition
    └── management.xml              # management network XML definition

Both script-generated and pre-committed files are acceptable. Additional files and directories are permitted.


Configuration Reference

All deployment parameters are read from the config file in the repository root. Example with default values:

# Libvirt networks
EXTERNAL_NET_NAME=external
EXTERNAL_NET_TYPE=dhcp
EXTERNAL_NET=192.168.123
EXTERNAL_NET_IP=${EXTERNAL_NET}.0
EXTERNAL_NET_MASK=255.255.255.0
EXTERNAL_NET_HOST_IP=${EXTERNAL_NET}.1
VM1_EXTERNAL_IP=${EXTERNAL_NET}.101

INTERNAL_NET_NAME=internal
INTERNAL_NET=192.168.124
INTERNAL_NET_IP=${INTERNAL_NET}.0
INTERNAL_NET_MASK=255.255.255.0

MANAGEMENT_NET_NAME=management
MANAGEMENT_NET=192.168.125
MANAGEMENT_NET_IP=${MANAGEMENT_NET}.0
MANAGEMENT_NET_MASK=255.255.255.0
MANAGEMENT_HOST_IP=${MANAGEMENT_NET}.1

# VMs global parameters
SSH_PUB_KEY=/home/jenkins/.ssh/id_rsa.pub
VM_TYPE=hvm
VM_VIRT_TYPE=kvm
VM_DNS=8.8.8.8
VM_BASE_IMAGE=https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img

# VxLAN overlay
VXLAN_NET=10.255.0
VID=12345
VXLAN_IF=vxlan0

# vm1
VM1_NAME=vm1
VM1_NUM_CPU=1
VM1_MB_RAM=512
VM1_HDD=/var/lib/libvirt/images/vm1/vm1.qcow2
VM1_CONFIG_ISO=/var/lib/libvirt/images/vm1/config-vm1.iso
VM1_EXTERNAL_IF=ens3
VM1_INTERNAL_IF=ens4
VM1_MANAGEMENT_IF=ens5
VM1_INTERNAL_IP=${INTERNAL_NET}.101
VM1_MANAGEMENT_IP=${MANAGEMENT_NET}.101
VM1_VXLAN_IP=${VXLAN_NET}.101

# vm2
VM2_NAME=vm2
VM2_NUM_CPU=1
VM2_MB_RAM=512
VM2_HDD=/var/lib/libvirt/images/vm2/vm2.qcow2
VM2_CONFIG_ISO=/var/lib/libvirt/images/vm2/config-vm2.iso
VM2_INTERNAL_IF=ens3
VM2_MANAGEMENT_IF=ens4
VM2_INTERNAL_IP=${INTERNAL_NET}.102
VM2_MANAGEMENT_IP=${MANAGEMENT_NET}.102
VM2_VXLAN_IP=${VXLAN_NET}.102

# Docker containers
NGINX_IMAGE="nginx:1.13"
APACHE_IMAGE="httpd:2.4"
NGINX_PORT=17080
APACHE_PORT=13254
NGINX_LOG_DIR=/srv/log/nginx

Implementation Notes

  1. Create libvirt networks from XML using virsh net-define followed by virsh net-start.
  2. Use virt-install to provision both VMs.
  3. A unique MAC address is required for the DHCP static entry. Generate one with:
    MAC=52:54:00:`(date; cat /proc/interrupts) | md5sum | sed -r 's/^(.{6}).*$/\1/; s/([0-9a-f]{2})/\1:/g; s/:$//;'`
  4. The base image URL is read from config (VM_BASE_IMAGE) — do not hardcode it.
  5. Resize the VM disk after copying the base image with qemu-img resize.
  6. Use the meta-data file to configure hostname, SSH key, and network interfaces.
  7. Use the user-data file for VxLAN tunnel creation, Docker installation, and container deployment.
  8. Build the cloud-init ISO with mkisofs -V cidata -r -J.
  9. Refer to task6_7 and task10_12_2 for the apache2+nginx configuration.
  10. A Docker Compose file is used here for convenience but is not strictly required by the assignment.

Environment Setup Guide

Prerequisites: Ubuntu 16.04 host (bare-metal or VM) with qemu-kvm, libvirt-bin, virtinst, bridge-utils, and genisoimage installed. All commands run as root.

Running the deployment

# 1. Clone the repository
git clone https://github.com/<your-username>/task10_12_3
cd task10_12_3

# 2. Adjust config if needed (e.g. update SSH_PUB_KEY path)
vi config

# 3. Run the deployment script (~10 minutes total)
bash task10_12_3.sh

The script prints ###### ALL DONE ###### when finished.

Verifying the HTTPS endpoint

# Source config to get variables
source ./config

# Test the NGINX reverse proxy — response must contain "It works!"
curl --cacert docker/certs/root.crt https://${VM1_EXTERNAL_IP}:${NGINX_PORT}

Verifying VMs and networks from the host

virsh list          # vm1 and vm2 must appear as "running"
virsh net-list      # external, internal, management must be "active"

Verifying vm1 (SSH via Management network)

source ./config
ssh -i $(echo ${SSH_PUB_KEY} | sed 's/\.pub//') ubuntu@${VM1_MANAGEMENT_IP}

Inside vm1:

docker ps                        # nginx container must be running
ip addr show vxlan0              # VxLAN interface with IP 10.255.0.101
ping 10.255.0.102 -c 3           # VxLAN connectivity to vm2
curl http://10.255.0.102:${APACHE_PORT}  # Apache page via VxLAN (run after sourcing config)

Verifying vm2 (SSH via Management network)

source ./config
ssh -i $(echo ${SSH_PUB_KEY} | sed 's/\.pub//') ubuntu@${VM2_MANAGEMENT_IP}

Inside vm2:

docker ps                        # apache container must be running
curl http://example.com          # external access — routed exclusively through vm1

Tearing down the environment

bash down.sh

This destroys both VMs and all three networks and removes their disk image directories.


Verification Checklist

Check Command (run from project root) Expected result
VMs are running virsh list vm1 and vm2 listed as running
Networks are active virsh net-list external, internal, management listed as active
HTTPS endpoint responds curl --cacert docker/certs/root.crt https://<VM1_EXTERNAL_IP>:<NGINX_PORT> Response body contains It works!
vm1 SSH access ssh ubuntu@<VM1_MANAGEMENT_IP> with configured key Shell prompt
vm2 SSH access ssh ubuntu@<VM2_MANAGEMENT_IP> with configured key Shell prompt
VxLAN tunnel on vm1 ip addr show vxlan0 (inside vm1) IP 10.255.0.101/24 assigned
VxLAN tunnel on vm2 ip addr show vxlan0 (inside vm2) IP 10.255.0.102/24 assigned
VxLAN connectivity ping 10.255.0.102 -c 3 (inside vm1) 0% packet loss
docker-ce on vm1 docker --version (inside vm1) Version string printed
docker-ce on vm2 docker --version (inside vm2) Version string printed
NGINX container on vm1 docker ps (inside vm1) Container from nginx:1.13 running
Apache container on vm2 docker ps (inside vm2) Container from httpd:2.4 running
NGINX volume mounts docker inspect <nginx-container> (inside vm1) nginx.conf, certs/, log dir all mounted
NGINX access log cat /srv/log/nginx/access.log (inside vm1) Entries present after HTTPS request
vm2 external routing curl http://example.com (inside vm2) Response received (via vm1 NAT)

Verification Procedure

Environment

  • OS: Ubuntu Xenial 16.04 Server (xenial-server-cloudimg-amd64-disk1.img)
  • User: root
  • Pre-installed packages: qemu-kvm, libvirt-bin, virtinst, bridge-utils, genisoimage

Execution Rules

  • The repository is cloned by name — the repository must be named task10_12_3.
  • task10_12_3.sh is launched from the repository root — a different script name or location results in automatic failure.
  • The config file must be present in the repository root and all parameters read from it correctly.

What is checked automatically

Network and VM presence

  • libvirt networks external, internal, and management exist and are active
  • VMs vm1 and vm2 exist and are connected to the correct networks

Network interface configuration (verified via SSH over Management network, login ubuntu)

  • All interfaces have the correct static or DHCP-assigned IP addresses
  • Both VMs have access to the external network
  • The VxLAN tunnel is configured and reachable between vm1 and vm2

Docker

  • docker-ce package is installed on both VMs
  • NGINX container (image nginx:1.13) is running on vm1
  • Apache container (image httpd:2.4) is running on vm2
  • NGINX container has correct volume mounts: nginx.conf, certificates directory, log directory
  • /srv/log/nginx/access.log contains entries on the Docker host (vm1)

Required files and directories

  • networks/ directory with all three XML files
  • config-drives/ directory with vm1-config/ and vm2-config/ subdirectories
  • docker/etc/nginx.conf
  • docker/certs/ directory

End-to-end HTTPS check

An HTTPS request is made to https://<VM1_EXTERNAL_IP>:<NGINX_PORT> using docker/certs/root.crt as the trusted root. The connection must succeed and the response body must contain the Apache2 default page (It works!).


Submission Requirements

  1. The completed assignment must be in a public GitHub repository named task10_12_3 (e.g. https://github.com/<username>/task10_12_3).
  2. The grader VM is a freshly installed Ubuntu 16.04 image from https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img.
  3. Pre-installed packages: qemu-kvm, libvirt-bin, virtinst, bridge-utils, genisoimage.
  4. Any additional packages required must be installed by the script itself.
  5. The script is run as root.
  6. Deadline: 23:59 on 13/05/2018.

About

Automatic deployment of a 2-VM infrastructure

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages