From Zero to Production: Complete WordPress on K3s Kubernetes with Jenkins CI/CD

Table of Contents

  1. Why This Guide?
  2. Architecture Overview
  3. Part 1: Setting Up K3s Kubernetes
  4. Part 2: Test WordPress (Non-Persistent)
  5. Part 3: Production WordPress (With Persistent Storage)
  6. Part 4: Kubernetes Dashboard
  7. Part 5: Jenkins CI/CD Integration
  8. Part 6: Connecting Jenkins to K3s
  9. Part 7: Scaling and Management
  10. Part 8: Cloud Provider Comparison
  11. Troubleshooting & Best Practices

Why This Guide?

When I started learning Kubernetes, I was overwhelmed by the complexity of cloud providers like EKS, GKE, and AKS. I wanted to understand the fundamentals without spending hundreds of dollars on managed services. So I built everything on a simple VPS using K3s — a lightweight, certified Kubernetes distribution that runs in under 100MB.

What you’ll achieve by the end of this guide:

  • ✅ A fully functional WordPress site running on Kubernetes
  • ✅ Production instance with persistent storage (data survives restarts)
  • ✅ Test instance for experiments (ephemeral storage)
  • ✅ Jenkins CI/CD pipeline to manage deployments
  • ✅ Kubernetes Dashboard for visual monitoring
  • ✅ Secure remote access via Tailscale

Architecture Overview

text

┌─────────────────────────────────────────────────────────────────────────────┐
│                         Xserver VPS (Ubuntu 22.04)                           │
│                                                                              │
│  ┌──────────────────────────────────────────────────────────────────────┐   │
│  │                         K3s Kubernetes Cluster                        │   │
│  │                                                                       │   │
│  │  ┌─────────────────────┐  ┌─────────────────────┐  ┌───────────────┐ │   │
│  │  │  Namespace: default │  │ Namespace: wordpress│  │  Namespace:   │ │   │
│  │  │  TEST WordPress     │  │      -PROD          │  │  kubernetes-  │ │   │
│  │  │  (No PVC)           │  │  PRODUCTION WP      │  │   dashboard   │ │   │
│  │  │  Port: 30080        │  │  (With PVC - 20GB)  │  │  Port: 30082  │ │   │
│  │  │  MySQL 5.7          │  │  Port: 30081        │  │               │ │   │
│  │  │  WordPress 5.8      │  │  MySQL 8.0          │  │               │ │   │
│  │  │  Ephemeral storage  │  │  WordPress Latest   │  │               │ │   │
│  │  └─────────────────────┘  └─────────────────────┘  └───────────────┘ │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                    │                                         │
│  ┌─────────────────────────────────┼─────────────────────────────────────┐  │
│  │                         Jenkins CI/CD                                  │  │
│  │                         (Docker Container)                             │  │
│  │                         Port: 8081                                     │  │
│  │                         kubectl installed                              │  │
│  └─────────────────────────────────┼─────────────────────────────────────┘  │
│                                    │                                         │
│                              ┌─────▼─────┐                                   │
│                              │ Tailscale │                                   │
│                              │   VPN     │                                   │
│                              └───────────┘                                   │
└─────────────────────────────────────────────────────────────────────────────┘

Access Points:

ServiceURLPort
Jenkins CI/CDhttp://your-vps.tailscale.ts.net8081
Production WPhttp://your-vps.tailscale.ts.net30081
Test WPhttp://your-vps.tailscale.ts.net30080
K8s Dashboardhttps://your-vps.tailscale.ts.net30082

Part 1: Setting Up K3s Kubernetes

What is K3s?

K3s is a lightweight, certified Kubernetes distribution by SUSE Rancher. It’s packaged as a single binary under 100MB and is perfect for:

  • Edge computing
  • Resource-constrained environments
  • Learning Kubernetes fundamentals
  • Production workloads at scale

Installation

bash

# SSH into your VPS
ssh root@your-vps-ip

# Install K3s (single command)
curl -sfL https://get.k3s.io | sh -

# Verify your cluster is running
sudo kubectl get nodes

# Expected output:
# NAME             STATUS   ROLES           AGE   VERSION
# x162-43-92-249   Ready    control-plane   30s   v1.35.4+k3s1

# Configure kubectl for your user
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
chmod 600 ~/.kube/config

Why This Works

K3s runs as a systemd service and includes:

  • Built-in local-path storage class (automatically provisions persistent volumes)
  • Embedded etcd (for clusters with 3+ nodes)
  • Core add-ons like Traefik ingress controller
  • SQLite as default datastore (instead of etcd for single-node clusters)

Part 2: Test WordPress (Non-Persistent)

Purpose

This deployment serves as a testing sandbox. Data does NOT persist across pod restarts, making it perfect for experimenting with themes, plugins, or configuration changes without risking production data.

The YAML Explained

yaml

# wordpress-test.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-pass
type: Opaque
data:
  # 'wordpress123' base64 encoded
  password: d29yZHByZXNzMTIz

---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
spec:
  ports:
  - port: 3306
  selector:
    app: wordpress-mysql

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
spec:
  selector:
    matchLabels:
      app: wordpress-mysql
  template:
    metadata:
      labels:
        app: wordpress-mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: MYSQL_DATABASE
          value: wordpress
        ports:
        - containerPort: 3306

---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30080
    targetPort: 80
  selector:
    app: wordpress

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress:5.8-apache
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_USER
          value: root
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: WORDPRESS_DB_NAME
          value: wordpress
        ports:
        - containerPort: 80

Deployment Commands

bash

# Apply the configuration
kubectl apply -f wordpress-test.yaml

# Check deployment status
kubectl get pods
kubectl get svc

# Create database
MYSQL_POD=$(kubectl get pods -l app=wordpress-mysql -o jsonpath='{.items[0].metadata.name}')
kubectl exec $MYSQL_POD -- mysql -uroot -pwordpress123 -e "CREATE DATABASE IF NOT EXISTS wordpress;"

# Install WordPress using WP-CLI
kubectl exec deployment/wordpress -- bash -c "
cd /tmp && \
curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
chmod +x wp-cli.phar && \
su -s /bin/bash -c 'php /tmp/wp-cli.phar core install \
  --path=/var/www/html \
  --url=\"http://your-tailscale-url:30080\" \
  --title=\"Test WordPress on K8s\" \
  --admin_user=test_admin \
  --admin_password=TestPassword123! \
  --admin_email=test@example.com' www-data
"

# Set correct site URLs
kubectl exec $MYSQL_POD -- mysql -uroot -pwordpress123 wordpress -e "
UPDATE wp_options SET option_value = 'http://your-tailscale-url:30080' WHERE option_name = 'siteurl';
UPDATE wp_options SET option_value = 'http://your-tailscale-url:30080' WHERE option_name = 'home';
"

Key Kubernetes Concepts Demonstrated

ConceptExplanationIn This Deployment
PodSmallest deployable unit in K8sWordPress and MySQL each run in their own pod
ServiceStable network endpoint for podswordpress service (NodePort:30080), wordpress-mysql (ClusterIP:3306)
DeploymentManages pod lifecycle and scalingManages WordPress and MySQL pods
SecretStores sensitive data safelyMySQL root password stored as base64
NodePortExposes service on static portPort 30080 accessible from outside the cluster

Part 3: Production WordPress (With Persistent Storage)

Why PVC (PersistentVolumeClaim)?

Without PVC, all database data disappears when the MySQL pod restarts. PVC ensures:

  • ✅ Data survives pod failures and restarts
  • ✅ Safe upgrades (can replace MySQL without data loss)
  • ✅ Backup and restore capabilities
  • ✅ Production-grade reliability

Creating a Separate Namespace

bash

# Isolate production from test environment
kubectl create namespace wordpress-prod

# Verify namespace created
kubectl get namespaces

Production Deployment with PVC

yaml

# wordpress-prod-pvc.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: mysql-pass
  namespace: wordpress-prod
type: Opaque
data:
  # 'SecurePassword2024!' base64 encoded
  password: U2VjdXJlUGFzc3dvcmQyMDI0IQ==

---
# PersistentVolumeClaim - Requests persistent storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  namespace: wordpress-prod
spec:
  accessModes:
    - ReadWriteOnce        # Can be mounted as read-write by a single node
  resources:
    requests:
      storage: 20Gi        # Request 20GB of storage

---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  namespace: wordpress-prod
spec:
  ports:
  - port: 3306
  selector:
    app: wordpress-mysql

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  namespace: wordpress-prod
spec:
  selector:
    matchLabels:
      app: wordpress-mysql
  template:
    metadata:
      labels:
        app: wordpress-mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: MYSQL_DATABASE
          value: wordpress
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql      # Where MySQL stores data
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim      # Bind to the PVC

---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  namespace: wordpress-prod
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30081
    targetPort: 80
  selector:
    app: wordpress

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  namespace: wordpress-prod
spec:
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress:latest
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_USER
          value: root
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password
        - name: WORDPRESS_DB_NAME
          value: wordpress
        ports:
        - containerPort: 80

Deploy Production WordPress

bash

# Deploy to namespace
kubectl apply -f wordpress-prod-pvc.yaml

# Wait for pods to be ready
kubectl get pods -n wordpress-prod -w

# Install WordPress
kubectl exec -n wordpress-prod deployment/wordpress -- bash -c "
cd /tmp && \
curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
chmod +x wp-cli.phar && \
su -s /bin/bash -c 'php /tmp/wp-cli.phar core install \
  --path=/var/www/html \
  --url=\"http://your-tailscale-url:30081\" \
  --title=\"Production WordPress on K8s\" \
  --admin_user=prod_admin \
  --admin_password=ProdSecurePass2024! \
  --admin_email=prod@example.com' www-data
"

# Set correct site URLs
kubectl exec -n wordpress-prod deployment/wordpress-mysql -- mysql -uroot -pSecurePassword2024! -e "
USE wordpress;
UPDATE wp_options SET option_value = 'http://your-tailscale-url:30081' WHERE option_name = 'siteurl';
UPDATE wp_options SET option_value = 'http://your-tailscale-url:30081' WHERE option_name = 'home';
"

Testing Data Persistence

bash

# Create a test post
kubectl exec -n wordpress-prod deployment/wordpress -- bash -c "
wp post create --post_title='PVC Test' --post_content='This should survive!' --post_status=publish --allow-root
"

# Restart MySQL pod (simulates failure)
kubectl delete pod -n wordpress-prod -l app=wordpress-mysql

# Wait for new pod
sleep 30

# Verify post still exists
kubectl exec -n wordpress-prod deployment/wordpress -- wp post list --allow-root
# ✅ The test post is still there - PVC is working!

Test vs Production Comparison

FeatureTest WordPressProduction WordPress
Namespacedefaultwordpress-prod
Port3008030081
StorageEphemeral (lost on restart)Persistent (20GB PVC)
MySQL Version5.78.0
WordPress Version5.8Latest
Use CaseTesting, experimentsLive production
Data Safety❌ Not safe✅ Safe

Part 4: Kubernetes Dashboard

What is the Kubernetes Dashboard?

A web-based UI for managing and monitoring your Kubernetes cluster. Provides visual access to:

  • All pods, deployments, and services
  • Real-time logs
  • Resource usage metrics
  • Container shells (exec)
  • Storage and configuration management

Installation

bash

# Deploy the official dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

# Create admin user
cat > dashboard-admin.yaml << 'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard-admin
  namespace: kubernetes-dashboard

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dashboard-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: dashboard-admin
  namespace: kubernetes-dashboard
EOF

kubectl apply -f dashboard-admin.yaml

# Expose dashboard via NodePort
kubectl patch svc kubernetes-dashboard -n kubernetes-dashboard -p '{"spec": {"type": "NodePort", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30082}]}}'

# Generate access token
kubectl -n kubernetes-dashboard create token dashboard-admin --duration=8760h

Access the Dashboard

  • URLhttps://your-tailscale-url:30082
  • Login: Token authentication (paste the token from above)
  • Note: Browser will show a security warning (self-signed certificate) — it’s safe to proceed

What You Can Do in the Dashboard

SectionCapabilities
ClusterView nodes, namespaces, resource quotas
WorkloadsPods, Deployments, ReplicaSets, Jobs, CronJobs
ServicesServices, Ingresses, Network Policies
StoragePersistentVolumeClaims, StorageClasses
ConfigConfigMaps, Secrets
LogsClick any pod → Logs tab → Real-time streaming
ExecClick any pod → Exec tab → Shell into container

Part 5: Jenkins CI/CD Integration

Why Jenkins on Docker?

Running Jenkins in a Docker container provides:

  • ✅ Isolation from the host system
  • ✅ Easy updates (just pull a new image)
  • ✅ Persistent data through Docker volumes
  • ✅ No Java installation required on the host

Installation

bash

# Install Docker (if not installed)
apt update && apt install -y docker.io
systemctl start docker && systemctl enable docker

# Run Jenkins container with host network
# Using port 8081 because 8080 is often occupied
docker run -d \
  --name jenkins \
  --restart unless-stopped \
  --network host \
  -v jenkins_home:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /etc/rancher/k3s:/etc/rancher/k3s:ro \
  -e JENKINS_OPTS="--httpPort=8081" \
  jenkins/jenkins:lts

# Wait for Jenkins to start (1-2 minutes)
sleep 60

# Get initial admin password
docker logs jenkins 2>&1 | grep -A 5 "Initial Admin Password"

Jenkins Initial Setup

  1. Access Jenkinshttp://your-tailscale-url:8081
  2. Enter the initial password from the logs
  3. Install suggested plugins (wait for completion)
  4. Create admin user:
    • Username: jenkins-admin
    • Password: Jenkins2024!
  5. Set Jenkins URLhttp://your-tailscale-url:8081

Part 6: Connecting Jenkins to K3s

Install kubectl in Jenkins Container

bash

# Install kubectl
docker exec --user root jenkins apt-get update
docker exec --user root jenkins apt-get install -y kubectl

# Verify installation
docker exec jenkins kubectl version --client

Configure Kubeconfig for Localhost Access

bash

# Create a kubeconfig that uses localhost (host networking)
cat > /tmp/kubeconfig << 'EOF'
apiVersion: v1
kind: Config
clusters:
- cluster:
    insecure-skip-tls-verify: true
    server: https://localhost:6443
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
users:
- name: default
  user:
    client-certificate-data: <YOUR_CERT_DATA>
    client-key-data: <YOUR_KEY_DATA>
EOF

# Copy to Jenkins container
docker cp /tmp/kubeconfig jenkins:/var/jenkins_home/.kube/config
docker exec --user root jenkins chown jenkins:jenkins /var/jenkins_home/.kube/config
docker exec jenkins chmod 600 /var/jenkins_home/.kube/config

# Test connection
docker exec jenkins kubectl get nodes

Why Jenkins Needs Kubernetes Credentials

Without proper credentials, Jenkins cannot:

  • Access the Kubernetes API
  • Execute kubectl commands
  • Monitor pod status
  • Manage deployments

Our kubeconfig file contains:

  • Client certificate – Proves Jenkins’ identity to the cluster
  • Client key – Private key for encrypted communication
  • Server address – K3s API endpoint (localhost:6443)

Sample Jenkins Pipeline

groovy

pipeline {
    agent any
    
    environment {
        KUBECONFIG = '/var/jenkins_home/.kube/config'
    }
    
    stages {
        stage('Check WordPress Status') {
            steps {
                sh 'kubectl get pods -n wordpress-prod'
                sh 'kubectl get pods -n default'
            }
        }
        
        stage('Restart Production') {
            steps {
                sh 'kubectl rollout restart deployment/wordpress -n wordpress-prod'
                sh 'kubectl rollout status deployment/wordpress -n wordpress-prod'
            }
        }
    }
}

Part 7: Scaling and Management

Manual Scaling

bash

# Scale OUT to 3 pods (handle more traffic)
kubectl scale deployment wordpress -n wordpress-prod --replicas=3

# Watch pods come online
kubectl get pods -n wordpress-prod -l app=wordpress -w

# Scale IN to 1 pod (save resources)
kubectl scale deployment wordpress -n wordpress-prod --replicas=1

Horizontal Pod Autoscaler (Automatic Scaling)

bash

# Install metrics server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# Create autoscaler (scales based on CPU usage)
cat << 'EOF' | kubectl apply -f -
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: wordpress-hpa
  namespace: wordpress-prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: wordpress
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
EOF

# Check autoscaler status
kubectl get hpa -n wordpress-prod

Backup and Restore

bash

# Backup production database
kubectl exec -n wordpress-prod deployment/wordpress-mysql -- \
  mysqldump -uroot -pSecurePassword2024! wordpress > backup-$(date +%Y%m%d).sql

# Restore from backup
kubectl exec -n wordpress-prod deployment/wordpress-mysql -- \
  mysql -uroot -pSecurePassword2024! wordpress < backup.sql

Useful Management Commands

CommandPurpose
kubectl get pods -AList all pods in all namespaces
kubectl logs -f deployment/wordpress -n wordpress-prodStream WordPress logs
kubectl describe pod <pod-name> -n wordpress-prodDetailed pod information
kubectl exec -it deployment/wordpress -n wordpress-prod -- bashShell into WordPress container
kubectl top pods -n wordpress-prodShow pod resource usage

Part 8: Cloud Provider Comparison

Where to Deploy This Architecture in Production

ComponentCurrent (VPS+K3s)AWSGoogle CloudAzureAkamai (Linode)
Kubernetes ServiceK3s (self-managed)Amazon EKSGKEAKSLKE (Linode K8s Engine)
Control Plane Cost$0 (self-managed)$0.10/hrFree (Autopilot)FreeFree
Egress BandwidthVaries by VPS$0.09/GB$0.12/GB$0.087/GB$0.005/GB
Managed DatabaseSelf-managed MySQLRDS / AuroraCloud SQLAzure DB for MySQLManaged Database
Object StorageLocal diskS3Cloud StorageBlob StorageObject Storage (S3-compatible)
CDN IntegrationNoneCloudFrontCloud CDNAzure CDNAkamai CDN (world’s largest)
Best ForLearning, controlAWS ecosystemAI/ML, K8s featuresMicrosoft shopsEdge, cost-sensitive

Decision Guide

If you prioritize…Choose…
Learning Kubernetes fundamentalsCurrent K3s on VPS
Lowest cost for small/medium workloadsAkamai LKE or Azure AKS
Global audience, latency-sensitiveAkamai LKE + Akamai CDN
AWS ecosystem (S3, Lambda, etc.)AWS EKS
Latest Kubernetes featuresGoogle GKE
Microsoft enterprise integrationAzure AKS

Migration Example: From K3s to Akamai LKE

yaml

# For Akamai LKE (change only storage class and service type)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: linode-block-storage  # Changed from local-path
  # ... rest remains the same

---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-throttle: "20"
spec:
  type: LoadBalancer  # Changed from NodePort
  # ... rest remains the same

Troubleshooting & Best Practices

Common Issues and Solutions

Issue 1: Jenkins can’t connect to K3s

bash

# Symptom: kubectl get nodes times out
# Solution: Use host networking
docker stop jenkins && docker rm jenkins
docker run -d --name jenkins --network host ...

Issue 2: WordPress shows “Database Error”

bash

# Check if database exists
kubectl exec -n wordpress-prod deployment/wordpress-mysql -- \
  mysql -uroot -pSecurePassword2024! -e "SHOW DATABASES;"

# Create if missing
kubectl exec -n wordpress-prod deployment/wordpress-mysql -- \
  mysql -uroot -pSecurePassword2024! -e "CREATE DATABASE wordpress;"

Issue 3: PVC stuck in “Pending”

bash

# Check storage class
kubectl get storageclass

# Check local-path provisioner
kubectl get pods -n kube-system | grep local-path

Issue 4: Port already in use

bash

# Find what's using the port
sudo netstat -tlnp | grep 8080

# Kill the process or use different port
sudo kill -9 <PID>

Security Best Practices

  1. Never commit secrets to Git
  2. Use Kubernetes Secrets for passwords
  3. Enable RBAC (Role-Based Access Control)
  4. Regularly rotate credentials
  5. Use Tailscale for network isolation
  6. Monitor pod security contexts
  7. Keep K3s and containers updated

Production Readiness Checklist

  • ✅ Persistent storage configured (PVC)
  • ✅ Regular backups scheduled
  • ✅ Monitoring and alerting set up
  • ✅ Rolling update strategy defined
  • ✅ Resource limits set for all containers
  • ✅ Health checks (liveness/readiness probes) configured
  • ✅ Network policies for pod isolation
  • ✅ SSL/TLS for external access
  • ✅ Disaster recovery plan documented

Final Status Verification

bash

cat << 'EOF' > final-status.sh
#!/bin/bash
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║     WordPress on K3s with Jenkins CI/CD - Final Status      ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "🎯 K3s Cluster:"
kubectl get nodes
echo ""
echo "🎨 Production WordPress (Port 30081 - with PVC):"
kubectl get pods -n wordpress-prod
echo ""
echo "🧪 Test WordPress (Port 30080 - ephemeral):"
kubectl get pods -n default
echo ""
echo "💾 Persistent Storage:"
kubectl get pvc -n wordpress-prod
echo ""
echo "🔧 Jenkins Status:"
docker ps | grep jenkins
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║                      Access URLs                            ║"
echo "╠══════════════════════════════════════════════════════════════╣"
echo "║ Jenkins CI/CD:      http://your-tailscale-url:8081          ║"
echo "║ Production WP:      http://your-tailscale-url:30081         ║"
echo "║ Test WP:            http://your-tailscale-url:30080         ║"
echo "║ K8s Dashboard:      https://your-tailscale-url:30082        ║"
echo "╚══════════════════════════════════════════════════════════════╝"
EOF

chmod +x final-status.sh
./final-status.sh

Conclusion

You’ve successfully built a complete WordPress infrastructure on Kubernetes!

What you’ve accomplished:

  • ✅ K3s Kubernetes cluster running on a VPS
  • ✅ Two WordPress instances (test + production)
  • ✅ Persistent storage for production database
  • ✅ Kubernetes Dashboard for visual management
  • ✅ Jenkins CI/CD pipeline with kubectl integration
  • ✅ Secure remote access via Tailscale
  • ✅ Understanding of YAML, PVCs, Deployments, and Services

Key Takeaways:

  1. Kubernetes skills are transferable across all cloud providers
  2. Persistent storage is essential for production databases
  3. CI/CD pipelines automate deployment and management
  4. Namespaces provide isolation between environments
  5. Understanding fundamentals first makes cloud adoption easier

Next Steps:

  1. Set up SSL/HTTPS with Let’s Encrypt
  2. Configure automated backups
  3. Add monitoring with Prometheus + Grafana
  4. Implement GitOps with ArgoCD
  5. Set up alerting for pod failures

Additional Resources


*This guide was compiled from a complete production deployment of WordPress on K3s, including test and production environments, persistent storage, and CI/CD integration with Jenkins.*

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注