PROJECT 4

Ansible & Docker Integration Lab

Install Ansible, configure hosts inventories, establish passwordless loops, configure Docker engine containers, and deploy multi-container microservice applications.

Environment
Ansible / Docker Engine
Difficulty
Intermediate
Course Module
Chapters 6–7: CM & Docker
Deliverables
Playbook & Compose Stack
1. System Architecture & Workflow

The system topology diagram below represents how the Ansible Control Node orchestrates host configuration management tasks, and maps the Docker Compose multi-container bridged network layout.

Ansible Control node Inventory: hosts Playbook: deploy.yml Orchestrates Localhost / Nodes via SSH Loopback Connection Docker Engine Workspace Docker Network: app-net Web Service Container: node-app Port: 3000 Database Container: mongo-db Port: 27017 SSH Loop
2. Part 1: Step-by-Step Action Items & Key Execution Steps
STEP 1

Install Ansible & Configure Passwordless Loopback Authentication

Install the Ansible automation engine on the control VM and map local SSH authorization parameters to bypass passphrase challenges.

$ sudo apt update && sudo apt install -y ansible
This command updates repository packages metadata lists and installs the Ansible configuration management framework tools on the guest VM workstation.
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub localhost
This command appends the local public SSH key metadata block to the localhost authorized_keys list, allowing passwordless connections to the local machine.
STEP 2

Configure Ansible Inventory and Run Ad-hoc Checks

Write host target files defining destination environments and verify connectivity using basic modules.

$ echo -e "[local]\nlocalhost ansible_connection=local" > ~/Projects/hosts
This command writes a simple inventory configuration mapping localhost to the local group, configuring Ansible to execute commands without using SSH wrappers.
$ ansible all -i ~/Projects/hosts -m ping
This command uses the ping module to test target connectivity, returning a "pong" response to verify the setup.
$ ansible all -i ~/Projects/hosts -m shell -a "hostname"
This command runs the shell command hostname on target hosts, validating module execution and command response pipelines.
STEP 3

Install Docker Engine and Compose Plugins

Register Docker's official distribution keys, write package lists, and compile Docker Engine services.

$ sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
This command updates the local package list and installs core dependency libraries required to handle secure downloads and encrypt public keys.
$ sudo install -m 0755 -d /etc/apt/keyrings && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
This command creates a secure directory for cryptographic keys, downloads Docker's official GPG verification key, and imports it for package validation.
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
This command writes Docker's package repository entry into the source listings, ensuring the system pulls updates directly from official Docker mirrors.
$ sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
This command updates package indexes to include Docker's repositories and installs the Docker runtime, command-line interface, container runtime daemon, and Compose plugins.
$ sudo systemctl start docker && sudo systemctl enable docker
This command starts the Docker background service daemon immediately and registers it to automatically start up whenever the host system boots.
STEP 4

Configure Docker Groups and Verify Container Execution

Grant socket access permissions to standard accounts to run containers without needing root elevation prefixes.

$ sudo usermod -aG docker $USER
This command appends the current user account to the docker group, granting permission to read and write to the Docker UNIX socket.
$ newgrp docker
This command refreshes group associations within the active terminal session, enabling Docker socket permissions without needing to restart the shell.
$ docker run hello-world
This command downloads and runs a test image to verify that the container engine can run containers on the system.
3. Automation Architecture

The workflow diagram below outlines the automated orchestration steps. The playbook verifies target nodes, manages keys, configures engines, copy-pastes sources, and runs Docker Compose services.

1. Run Playbook Apply configuration playbook yml ansible-playbook 2. Copy Sources Write Dockerfiles and code files Docker/JS code 3. Build Compose Compose compiles images locally docker compose build 4. Stack Running Containers live on app-net network docker compose up -d
4. Part 2: Complete Deliverable Assets & Production Templates

To deploy the multi-container application stack, we will write a Node.js web application, containerize it, and coordinate its services using Docker Compose. Below is a step-by-step breakdown of how these configuration files are built, followed by the final consolidated templates.

Step-by-Step Template Construction

Step 1

Declare Application Dependencies

Setup package descriptors listing the frameworks needed for the web server and database client connection.

{ "name": "node-app", "version": "1.0.0", "main": "server.js", "dependencies": { "express": "^4.18.2", "mongoose": "^7.3.1" } }
This package.json file defines metadata for the project and specifies that Express (routing framework) and Mongoose (MongoDB object modeler) must be installed.
Step 2

Write the Web Server and Database Connection Logic

Implement routing logic and connection strings referencing the MongoDB container hostname alias.

const express = require('express'); const mongoose = require('mongoose'); const app = express(); const PORT = 3000; const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/devops-db'; mongoose.connect(MONGO_URI) .then(() => console.log('Connected to MongoDB database.')) .catch(err => console.error('Database connection error:', err)); app.get('/', (req, res) => { res.json({ status: 'ONLINE', message: 'Stack Running Successfully!' }); }); app.listen(PORT);
This code initializes Express, connects to MongoDB using a dynamic connection string, sets up a home page route, and starts listening on port 3000.
Step 3

Write the Dockerfile to Containerize the Application

Define the execution image steps, copy files, run package installers, expose ports, and set execution scripts.

FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install --production COPY . . EXPOSE 3000 CMD ["node", "server.js"]
This Dockerfile specifies using a lightweight Node.js base image, sets the working directory, copies dependency files to run production installs, copies the source code, exposes port 3000, and configures the default run script.
Step 4

Write the Docker Compose Orchestration Manifest

Define database and web application service configurations, map networking routes, and configure storage volumes.

version: '3.8' services: node-app: build: . ports: - "3000:3000" environment: - MONGO_URI=mongodb://mongo-db:27017/devops-db depends_on: - mongo-db networks: - app-net mongo-db: image: mongo:6.0 ports: - "27017:27017" volumes: - mongo-data:/data/db networks: - app-net volumes: mongo-data: networks: app-net:
This compose file coordinates both services, sets environment variables, routes host port 3000 to the container, mounts the database volume, and connects both containers to a bridged network.

Combined Complete Configuration Files

Create these files inside your project folder (~/Projects), build, and start the services:

1. docker-compose.yml

version: '3.8' services: node-app: build: context: . dockerfile: Dockerfile container_name: node-app ports: - "3000:3000" environment: - MONGO_URI=mongodb://mongo-db:27017/devops-db depends_on: - mongo-db networks: - app-net mongo-db: image: mongo:6.0 container_name: mongo-db ports: - "27017:27017" volumes: - mongo-data:/data/db networks: - app-net volumes: mongo-data: driver: local networks: app-net: driver: bridge

2. Dockerfile

FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install --production COPY . . EXPOSE 3000 CMD ["node", "server.js"]

3. server.js

const express = require('express'); const mongoose = require('mongoose'); const app = express(); const PORT = 3000; const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/devops-db'; mongoose.connect(MONGO_URI) .then(() => console.log('Connected to MongoDB database.')) .catch(err => console.error('Database connection error:', err)); app.get('/', (req, res) => { res.json({ status: 'ONLINE', message: 'Multi-Container Application Running Successfully via Docker Compose!', timestamp: new Date() }); }); app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });

4. package.json

{ "name": "node-app", "version": "1.0.0", "main": "server.js", "dependencies": { "express": "^4.18.2", "mongoose": "^7.3.1" } }
5. Deliverables Summary

Verify that the following configurations and outputs exist inside your project workspace.

Created Files / Templates

  • /home/devops/Projects/docker-compose.yml - Orchestration template for multi-container services.
  • /home/devops/Projects/Dockerfile - Node.js build configuration file.
  • /home/devops/Projects/server.js - Node.js source application code file.
  • /home/devops/Projects/package.json - Javascript dependency list file.

Verification Artifacts / Execution Proof

  • Output of ansible localhost -m ping confirming successful ping.
  • Output of docker ps showing node-app and mongo-db containers running.
  • Successful HTTP request response from host browser accessing http://localhost:3000/.
6. Closing Explanation: Why We Did This & What It Accomplishes

Architectural Intent & Operational Impact

Why We Did This

What This Accomplishes