Debian Container & Virtualization: Docker, Kubernetes & KVM Guide
Modern infrastructure relies heavily on containerization and virtualization technologies. This comprehensive guide covers setting up Docker containers, Kubernetes orchestration, and KVM virtualization on Debian servers, providing you with the tools to build scalable and efficient infrastructure.
Docker Installation and Configuration
Installing Docker
# Update package index
sudo apt update
# Install prerequisites
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Add Docker repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Enable and start Docker
sudo systemctl enable docker
sudo systemctl start docker
# Add user to docker group
sudo usermod -aG docker $USER
Docker Configuration
# Configure Docker daemon
sudo nano /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
],
"dns": ["8.8.8.8", "8.8.4.4"],
"registry-mirrors": ["https://mirror.gcr.io"],
"insecure-registries": [],
"debug": false,
"experimental": true,
"features": {
"buildkit": true
},
"metrics-addr": "127.0.0.1:9323",
"default-runtime": "runc",
"runtimes": {
"runc": {
"path": "runc"
}
}
}
Restart Docker:
sudo systemctl restart docker
Docker Security Configuration
# Enable user namespace remapping
sudo nano /etc/docker/daemon.json
Add:
{
"userns-remap": "default"
}
Create subuid and subgid mappings:
sudo nano /etc/subuid
dockremap:100000:65536
sudo nano /etc/subgid
dockremap:100000:65536
Docker Compose Example
# Create project directory
mkdir -p ~/docker-projects/webapp
cd ~/docker-projects/webapp
# Create docker-compose.yml
nano docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
container_name: webapp-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
- ./app:/var/www/html
depends_on:
- php
networks:
- webapp-network
restart: unless-stopped
php:
build:
context: ./php
dockerfile: Dockerfile
container_name: webapp-php
volumes:
- ./app:/var/www/html
environment:
- PHP_MEMORY_LIMIT=256M
- PHP_MAX_EXECUTION_TIME=300
networks:
- webapp-network
restart: unless-stopped
mysql:
image: mysql:8.0
container_name: webapp-mysql
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
networks:
- webapp-network
restart: unless-stopped
redis:
image: redis:alpine
container_name: webapp-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
networks:
- webapp-network
restart: unless-stopped
volumes:
mysql-data:
redis-data:
networks:
webapp-network:
driver: bridge
Custom PHP Dockerfile
mkdir -p php
nano php/Dockerfile
FROM php:8.2-fpm
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
libzip-dev \
libgd-dev \
libcurl4-openssl-dev
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip curl
# Install Redis extension
RUN pecl install redis && docker-php-ext-enable redis
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www/html
# Copy custom PHP configuration
COPY php.ini /usr/local/etc/php/conf.d/custom.ini
# Create non-root user
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Change ownership
RUN chown -R www:www /var/www/html
# Switch to non-root user
USER www
EXPOSE 9000
CMD ["php-fpm"]
Kubernetes Installation and Setup
Installing Kubernetes with kubeadm
# Disable swap
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# Install prerequisites
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl
# Add Kubernetes repository
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Install Kubernetes components
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
# Enable kernel modules
sudo modprobe br_netfilter
sudo modprobe overlay
# Configure sysctl
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
Initialize Kubernetes Cluster
# Initialize control plane (master node)
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.1.100
# Configure kubectl for regular user
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# Install Flannel network plugin
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# For single-node cluster, allow pods on master
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
Join Worker Nodes
On worker nodes:
# Run the join command from master node output
sudo kubeadm join 192.168.1.100:6443 --token <token> --discovery-token-ca-cert-hash <hash>
Deploy Sample Application
# Create namespace
kubectl create namespace webapp
# Create deployment
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: webapp
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
limits:
memory: "128Mi"
cpu: "500m"
requests:
memory: "64Mi"
cpu: "250m"
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: webapp
spec:
selector:
app: nginx
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
EOF
Kubernetes Dashboard
# Deploy dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# Create admin user
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
# Get token
kubectl -n kubernetes-dashboard create token admin-user
Helm Package Manager
# Install Helm
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt update
sudo apt install helm
# Add repositories
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo update
# Install example application
helm install my-wordpress bitnami/wordpress --namespace webapp
KVM Virtualization Setup
Installing KVM
# Check virtualization support
egrep -c '(vmx|svm)' /proc/cpuinfo
# Install KVM and tools
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
# Add user to groups
sudo usermod -aG libvirt $USER
sudo usermod -aG kvm $USER
# Enable and start libvirtd
sudo systemctl enable libvirtd
sudo systemctl start libvirtd
Network Configuration for KVM
# Create bridge network
sudo nano /etc/network/interfaces
# Physical interface
auto eth0
iface eth0 inet manual
# Bridge interface
auto br0
iface br0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
dns-nameservers 8.8.8.8 8.8.4.4
bridge_ports eth0
bridge_stp off
bridge_fd 0
bridge_maxwait 0
Create Virtual Machine
# Download OS image
wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-11.6.0-amd64-netinst.iso
# Create VM using virt-install
sudo virt-install \
--name debian-vm \
--ram 2048 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/debian-vm.qcow2,size=20,format=qcow2 \
--os-variant debian11 \
--network bridge=br0 \
--graphics vnc,listen=0.0.0.0 \
--noautoconsole \
--cdrom debian-11.6.0-amd64-netinst.iso
# List VMs
sudo virsh list --all
# Start VM
sudo virsh start debian-vm
# Connect to VM console
sudo virsh console debian-vm
VM Management Scripts
# Create VM backup script
sudo nano /usr/local/bin/vm-backup.sh
#!/bin/bash
# KVM VM Backup Script
BACKUP_DIR="/backup/vms"
DATE=$(date +%Y%m%d_%H%M%S)
VMS=$(virsh list --all --name)
mkdir -p "$BACKUP_DIR"
for VM in $VMS; do
if [ ! -z "$VM" ]; then
echo "Backing up VM: $VM"
# Create snapshot
virsh snapshot-create-as --domain $VM backup-$DATE --disk-only --atomic
# Get disk path
DISK_PATH=$(virsh domblklist $VM | grep vda | awk '{print $2}')
# Backup disk
cp "$DISK_PATH" "$BACKUP_DIR/${VM}_${DATE}.qcow2"
# Remove snapshot
virsh snapshot-delete $VM backup-$DATE
# Backup XML configuration
virsh dumpxml $VM > "$BACKUP_DIR/${VM}_${DATE}.xml"
fi
done
# Cleanup old backups (keep 7 days)
find "$BACKUP_DIR" -name "*.qcow2" -o -name "*.xml" -mtime +7 -delete
VM Template Creation
# Create template script
sudo nano /usr/local/bin/create-vm-template.sh
#!/bin/bash
# VM Template Creation Script
TEMPLATE_NAME="debian-template"
TEMPLATE_DIR="/var/lib/libvirt/templates"
BASE_IMAGE="/var/lib/libvirt/images/debian-base.qcow2"
mkdir -p "$TEMPLATE_DIR"
# Create base VM
virt-install \
--name $TEMPLATE_NAME \
--ram 1024 \
--vcpus 1 \
--disk path=$BASE_IMAGE,size=10,format=qcow2 \
--os-variant debian11 \
--network bridge=br0 \
--graphics none \
--console pty,target_type=serial \
--location 'http://deb.debian.org/debian/dists/stable/main/installer-amd64/' \
--extra-args 'console=ttyS0,115200n8 serial'
# Customize template
virt-customize -a $BASE_IMAGE \
--run-command 'apt-get update' \
--install cloud-init,qemu-guest-agent \
--run-command 'systemctl enable qemu-guest-agent' \
--run-command 'apt-get clean' \
--run-command 'rm -rf /var/lib/apt/lists/*'
# Sysprep
virt-sysprep -a $BASE_IMAGE \
--enable ssh-hostkeys,ssh-userdir,machine-id,customize
# Convert to template
qemu-img convert -O qcow2 -c $BASE_IMAGE $TEMPLATE_DIR/$TEMPLATE_NAME.qcow2
echo "Template created: $TEMPLATE_DIR/$TEMPLATE_NAME.qcow2"
Container Orchestration with Docker Swarm
Initialize Docker Swarm
# Initialize swarm on manager node
docker swarm init --advertise-addr 192.168.1.100
# Join worker nodes (use token from init output)
docker swarm join --token <token> 192.168.1.100:2377
# List nodes
docker node ls
# Create overlay network
docker network create --driver overlay --attachable webapp-net
Deploy Stack
# Create stack file
nano docker-stack.yml
version: '3.8'
services:
web:
image: nginx:alpine
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
placement:
constraints:
- node.role == worker
ports:
- "80:80"
networks:
- webapp-net
volumes:
- web-content:/usr/share/nginx/html
configs:
- source: nginx-config
target: /etc/nginx/nginx.conf
visualizer:
image: dockersamples/visualizer:latest
ports:
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
placement:
constraints:
- node.role == manager
networks:
- webapp-net
volumes:
web-content:
driver: local
networks:
webapp-net:
external: true
configs:
nginx-config:
file: ./nginx.conf
Deploy stack:
docker stack deploy -c docker-stack.yml webapp
Container Registry Setup
Deploy Private Docker Registry
# Create registry with authentication
mkdir -p ~/docker-registry/{auth,certs,data}
# Generate certificates
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout ~/docker-registry/certs/domain.key \
-x509 -days 365 \
-out ~/docker-registry/certs/domain.crt \
-subj "/CN=registry.example.com"
# Create htpasswd file
docker run --rm --entrypoint htpasswd \
httpd:2 -Bbn admin password > ~/docker-registry/auth/htpasswd
# Run registry
docker run -d \
--restart=always \
--name registry \
-v ~/docker-registry/auth:/auth \
-v ~/docker-registry/certs:/certs \
-v ~/docker-registry/data:/var/lib/registry \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 5000:5000 \
registry:2
Configure Docker to Use Registry
# Add certificate to Docker
sudo mkdir -p /etc/docker/certs.d/registry.example.com:5000
sudo cp ~/docker-registry/certs/domain.crt /etc/docker/certs.d/registry.example.com:5000/ca.crt
# Login to registry
docker login registry.example.com:5000
# Tag and push image
docker tag myapp:latest registry.example.com:5000/myapp:latest
docker push registry.example.com:5000/myapp:latest
Monitoring and Management
Container Monitoring with Prometheus
# Create monitoring stack
mkdir -p ~/monitoring
cd ~/monitoring
# Prometheus configuration
nano prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'docker'
static_configs:
- targets: ['localhost:9323']
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
- job_name: 'cadvisor'
static_configs:
- targets: ['localhost:8080']
Docker Compose for monitoring:
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
ports:
- "9090:9090"
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: grafana
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
ports:
- "3000:3000"
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
ports:
- "9100:9100"
restart: unless-stopped
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
restart: unless-stopped
volumes:
prometheus-data:
grafana-data:
Backup and Recovery Script
sudo nano /usr/local/bin/container-backup.sh
#!/bin/bash
# Container Backup Script
BACKUP_DIR="/backup/containers"
DATE=$(date +%Y%m%d_%H%M%S)
CONTAINERS=$(docker ps -a --format "{{.Names}}")
mkdir -p "$BACKUP_DIR"
# Backup containers
for CONTAINER in $CONTAINERS; do
echo "Backing up container: $CONTAINER"
# Export container
docker export $CONTAINER | gzip > "$BACKUP_DIR/${CONTAINER}_${DATE}.tar.gz"
# Backup volumes
VOLUMES=$(docker inspect $CONTAINER | jq -r '.[0].Mounts[] | select(.Type=="volume") | .Name')
for VOLUME in $VOLUMES; do
docker run --rm -v $VOLUME:/data -v $BACKUP_DIR:/backup \
alpine tar czf /backup/${CONTAINER}_${VOLUME}_${DATE}.tar.gz -C /data .
done
done
# Backup Docker Compose projects
for PROJECT in ~/docker-projects/*; do
if [ -f "$PROJECT/docker-compose.yml" ]; then
PROJECT_NAME=$(basename $PROJECT)
tar czf "$BACKUP_DIR/compose_${PROJECT_NAME}_${DATE}.tar.gz" -C $PROJECT .
fi
done
# Cleanup old backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
echo "Container backup completed"
Security Best Practices
Container Security Scanning
# Install Trivy
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt update
sudo apt install trivy
# Scan image
trivy image nginx:latest
# Scan running container
docker export $(docker ps -q -f name=webapp) | trivy image --input -
Security Policies
# Create AppArmor profile for containers
sudo nano /etc/apparmor.d/docker-nginx
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network inet tcp,
network inet udp,
network inet icmp,
deny network raw,
deny /proc/sys/kernel/** wklx,
deny /sys/kernel/** wklx,
capability chown,
capability dac_override,
capability setuid,
capability setgid,
capability net_bind_service,
/usr/sbin/nginx ix,
/etc/nginx/** r,
/var/log/nginx/** rw,
/var/www/** r,
/run/nginx.pid rw,
}
Load profile:
sudo apparmor_parser -r /etc/apparmor.d/docker-nginx
Kubernetes Security
# Create security policy
kubectl apply -f - <<EOF
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
readOnlyRootFilesystem: false
EOF
Performance Optimization
Docker Performance Tuning
# Optimize Docker daemon
sudo nano /etc/docker/daemon.json
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true",
"overlay2.size=10G"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
},
"max-concurrent-downloads": 10,
"max-concurrent-uploads": 10,
"live-restore": true,
"userland-proxy": false
}
Resource Limits
# Docker Compose with resource limits
version: '3.8'
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
mem_swappiness: 0
cpu_shares: 1024
blkio_config:
weight: 500
Conclusion
Containerization and virtualization technologies provide the foundation for modern, scalable infrastructure. This guide covers the essential aspects of implementing Docker, Kubernetes, and KVM on Debian servers.
Key takeaways: - Start with Docker for simple containerization needs - Use Kubernetes for complex orchestration requirements - Implement KVM for full virtualization when needed - Always prioritize security in container deployments - Monitor resource usage and performance - Implement proper backup and recovery procedures - Keep all components updated
With these technologies properly configured, you can build flexible, efficient, and scalable infrastructure on Debian servers.