- name
- devops-engineer
- description
- Definitive, practical guide to DevOps and Platform Engineering covering CI/CD, Terraform, Ansible, Vagrant, Kubernetes, Virtual Machines, Backstage IDP, GitOps, Observability, and Golden Paths.
# 🚀 DevOps & Platform Engineering
The role of the **DevOps & Platform Engineer** has evolved from simple automation of deployment scripts into building robust, scalable, and resilient platforms that enable the *self-service* model for software development teams.
This guide consolidates architecture standards, essential tools, production code, and best practices for designing, implementing, and operating modern cloud-oriented infrastructure and hybrid environments.
---
## 🏗️ 1. Infrastructure as Code (IaC) with Terraform
Terraform is the industry-standard tool for declarative provisioning of multi-cloud infrastructure. Code design must prioritize modularity, immutability, and state isolation.
### 📁 Module Directory Structure
A professional architecture separates reusable modules from environment definitions (*live/environments*):
```text
terraform-architecture/
├── modules/
│ └── vpc/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── versions.tf
└── environments/
├── dev/
│ ├── backend.tf
│ ├── main.tf
│ ├── terraform.tfvars
│ └── variables.tf
└── prod/
├── backend.tf
├── main.tf
├── terraform.tfvars
└── variables.tf
```
### 🔒 Remote State with S3 and DynamoDB (AWS)
Remote state storage guarantees safe collaboration with concurrent *state locking*.
```hcl
# environments/prod/backend.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40.0"
}
}
backend "s3" {
bucket = "company-terraform-state-prod"
key = "core-infra/vpc/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "company-terraform-locks-prod"
}
}
```
### 📦 Reusable VPC Module Example
```hcl
# modules/vpc/variables.tf
variable "vpc_cidr" {
description = "The CIDR block for the VPC"
type = string
default = "10.0.0.0/16"
validation {
condition = can(cidrnetmask(var.vpc_cidr))
error_message = "The vpc_cidr must be a valid CIDR block."
}
}
variable "environment" {
description = "Deployment environment name"
type = string
}
variable "public_subnet_cidrs" {
description = "List of public subnet CIDR blocks"
type = list(string)
}
variable "private_subnet_cidrs" {
description = "List of private subnet CIDR blocks"
type = list(string)
}
variable "availability_zones" {
description = "List of availability zones to distribute subnets"
type = list(string)
}
```
```hcl
# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "vpc-${var.environment}"
Environment = var.environment
ManagedBy = "Terraform"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "igw-${var.environment}"
Environment = var.environment
}
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "subnet-${var.environment}-public-${count.index + 1}"
Environment = var.environment
Type = "Public"
}
}
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = {
Name = "subnet-${var.environment}-private-${count.index + 1}"
Environment = var.environment
Type = "Private"
}
}
```
```hcl
# modules/vpc/outputs.tf
output "vpc_id" {
description = "Identifier of the created VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of IDs for public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "List of IDs for private subnets"
value = aws_subnet.private[*].id
}
```
### 🔄 Lifecycle and Drift Detection
1. **Validation and Formatting**:
```bash
terraform fmt -recursive
terraform validate
```
2. **Safe Execution in CI**:
```bash
terraform plan -out=tfplan.binary
terraform apply tfplan.binary
```
3. **Drift Detection on a Cron Schedule**:
```bash
# Exit code 2 indicates a drift occurred
terraform plan -detailed-exitcode -no-color
```
---
## ⚙️ 2. Configuration Management & Automation with Ansible
Ansible provides *agentless* automation over SSH/WinRM, guaranteeing **idempotency** in configuring operating systems and applications.
### 📐 Directory Structure and Roles
```text
ansible-project/
├── ansible.cfg
├── inventory/
│ ├── production/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ │ ├── all.yml
│ │ └── webservers.yml
│ └── staging/
│ └── hosts.yml
├── roles/
│ └── webserver/
│ ├── defaults/main.yml
│ ├── tasks/main.yml
│ ├── handlers/main.yml
│ ├── templates/nginx.conf.j2
│ └── vars/main.yml
└── site.yml
```
### 🏷️ Variable Precedence and Ansible Vault
Ansible's precedence order ranges from role defaults (lowest priority) to command-line arguments (`-e`, highest priority).
To encrypt secrets:
```bash
# Encrypt a secret variable
ansible-vault encrypt_string --vault-password-file .vault_pass 'supersecretpassword' --name 'db_password'
# Run playbook with vault file
ansible-playbook -i inventory/production/hosts.yml site.yml --vault-password-file .vault_pass
```
### 📄 Idempotent Playbook and Role Example
```yaml
# inventory/production/hosts.yml
all:
children:
webservers:
hosts:
web-node-01.internal:
ansible_host: 10.0.10.21
web-node-02.internal:
ansible_host: 10.0.10.22
vars:
http_port: 80
server_name: api.production.internal
```
```yaml
# roles/webserver/tasks/main.yml
---
# Install and configure NGINX web server
- name: Ensure NGINX package is installed
ansible.builtin.package:
name: nginx
state: present
- name: Deploy NGINX configuration from template
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Restart NGINX service
- name: Ensure NGINX service is enabled and running
ansible.builtin.service:
name: nginx
state: started
enabled: true
- name: Deploy web root content
ansible.builtin.copy:
content: "OK - Managed by Ansible\n"
dest: /var/www/html/healthz
owner: www-data
group: www-data
mode: '0644'
```
```yaml
# roles/webserver/handlers/main.yml
---
- name: Restart NGINX service
ansible.builtin.service:
name: nginx
state: restarted
```
```yaml
# site.yml
---
- name: Configure Production Web Fleet
hosts: webservers
become: true
roles:
- role: webserver
```
---
## 💻 3. Local Development Environments with Vagrant
Vagrant lets you provision reproducible, portable development environments locally, integrating with hypervisors (VirtualBox, VMware, Libvirt, Hyper-V).
### ⚙️ Multi-Machine Vagrantfile with Shell and Ansible Provisioning
```ruby
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/jammy64"
config.vm.box_check_update = true
# Global synced folder
config.vm.synced_folder "./app", "/var/www/app", type: "nfs", mount_options: ["actimeo=1"]
# Global VM provider configuration
config.vm.provider "virtualbox" do |vb|
vb.gui = false
vb.linked_clone = true
end
# Node 1: Web Application Server
config.vm.define "app-node" do |app|
app.vm.hostname = "app-node.local"
app.vm.network "private_network", ip: "192.168.56.10"
app.vm.network "forwarded_port", guest: 80, host: 8080, auto_correct: true
app.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
vb.cpus = 2
end
# Bootstrap with shell provisioner
app.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
# Ubuntu-family guest (the box is Ubuntu): `apt`, right here. Not
# copy-pasteable to another family — manager and package names both differ.
apt-get update && apt-get install -y curl ufw git
ufw allow 80/tcp
SHELL
# Configure using Ansible Local provisioner
app.vm.provision "ansible_local" do |ansible|
ansible.playbook = "ansible/site.yml"
ansible.install_mode = "pip"
end
end
# Node 2: Database Server
config.vm.define "db-node" do |db|
db.vm.hostname = "db-node.local"
db.vm.network "private_network", ip: "192.168.56.11"
db.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
vb.cpus = 1
end
db.vm.provision "shell", inline: <<-SHELL
# Ubuntu-family guest (the box is Ubuntu): `apt`, right here. Not
# copy-pasteable to another family — manager and package names both differ.
apt-get update && apt-get install -y postgresql postgresql-contrib
systemctl enable --now postgresql
SHELL
end
end
```
---
## ☸️ 4. Kubernetes Orchestration and Operations
Kubernetes is the execution engine for modern microservices. Corporate configurations demand resilience with **HPA**, **PDB**, and **Resource Quotas**, plus continuous delivery via **GitOps**.
### 📜 Production Application Manifest
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: production
labels:
app.kubernetes.io/name: order-service
app.kubernetes.io/part-of: ecommerce
spec:
replicas: 3
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: app
image: ghcr.io/company/order-service:v2.4.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: order-service-secret
key: database-url
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: order-service-config
key: log-level
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /healthz/ready
port: http
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz/live
port: http
initialDelaySeconds: 15
periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: production
spec:
type: ClusterIP
selector:
app: order-service
ports:
- name: http
port: 80
targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-service-ingress
namespace: production
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-production"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- orders.company.com
secretName: order-service-tls
rules:
- host: orders.company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
name: http
```
### 📈 High Availability: HPA and PDB
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: order-service-pdb
namespace: production
spec:
minAvailable: 2
selector:
matchLabels:
app: order-service
```
### 🐙 GitOps with an ArgoCD Application
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service-prod
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: 'https://github.com/company/gitops-manifests.git'
targetRevision: main
path: overlays/production/order-service
destination:
server: 'https://kubernetes.default.svc'
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
```
---
## 🖥️ 5. Virtual Machine Management & Golden Images
Although containers dominate ephemeral workloads, **Virtual Machines (VMs)** are the foundation for hypervisors, bare-metal databases, Kubernetes clusters (control plane / worker nodes), and workloads requiring strict kernel isolation.
### 📊 Comparison: Virtual Machines vs Containers
| Characteristic | Virtual Machines (VMs) | Containers (Docker / Podman) |
| :--- | :--- | :--- |
| **Isolation** | Hypervisor (Hardware-level / Type 1 or 2) | Namespaces and cgroups (OS-level) |
View on GitHub