DevOps инфраструктура на Proxmox с Docker и Ansible

DevOps инфраструктура на Proxmox с Docker и Ansible

Въведение

В съвременния свят на разработката, способността да изграждаш, тествaш и внедряваш приложения бързо и надеждно е ключова. DevOps културата трансформира начина, по който работят екипите, но професионалните cloud решения често са скъпи за експериментиране и обучение. Тук на сцената излиза Proxmox VE – мощна, безплатна виртуализационна платформа, която може да се превърне във ваш личен DevOps playground или дори production environment.

В тази статия ще изградим цялостна DevOps инфраструктура, която включва:

  • Git repository management (Gitea)
  • CI/CD pipeline (Jenkins)
  • Container registry
  • Автоматизирано внедряване с Ansible
  • Monitoring и logging

Независимо дали сте студент, който иска да научи DevOps практики, или професионалист, търсещ self-hosted решение, тази статия ще ви покаже пътя от нула до production-ready система.

Защо Proxmox за DevOps?

Преди да се потопим в техническите детайли, нека разберем защо Proxmox е идеалният избор:

1. Хибридна виртуализация Proxmox поддържа както KVM виртуални машини, така и LXC контейнери. Това ви дава гъвкавостта да изберете оптималното решение – VM за изолация или LXC за ефективност.

2. Web-базиран интерфейс Цялото управление става през интуитивен браузър интерфейс. Никакви сложни команди за начинаещите, но и пълен API достъп за автоматизация.

3. Open Source и безплатен За разлика от VMware или Hyper-V, Proxmox е напълно безплатен. Перфектен за learning и home lab setup.

4. Enterprise features Clustering, high availability, backup, migration – всичко това идва out of the box.

5. Отлична интеграция с automation tools Terraform providers, Ansible modules, REST API – автоматизацията е first-class citizen.

Архитектура на нашата система

Преди да започнем, нека визуализираме какво ще изградим:

graph TB
    subgraph Proxmox Host
        subgraph DevOps VM
            A[Gitea - Git Server]
            B[Jenkins - CI/CD]
            C[Docker Registry]
            D[Traefik - Reverse Proxy]
        end
        
        subgraph Production VM
            E[Docker Swarm Manager]
            F[App Containers]
        end
        
        subgraph Monitoring VM
            G[Prometheus]
            H[Grafana]
            I[Loki - Logs]
        end
    end
    
    J[Developer] -->|git push| A
    A -->|webhook| B
    B -->|build & push| C
    B -->|deploy| E
    E --> F
    G -->|scrape metrics| F
    H -->|visualize| G
    I -->|collect logs| F
    D -->|route traffic| A
    D -->|route traffic| B
    D -->|route traffic| H

Обяснение на компонентите:

  • Gitea: Self-hosted Git сървър (алтернатива на GitHub)
  • Jenkins: Automation server за CI/CD pipelines
  • Docker Registry: Частен registry за Docker images
  • Traefik: Reverse proxy с автоматичен SSL (Let's Encrypt)
  • Prometheus & Grafana: Monitoring и визуализация
  • Loki: Централизиран log aggregation

Prerequisite: Подготовка на Proxmox

Преди да започнем, нуждаете се от:

  • Hardware: Минимум 16GB RAM, 4 CPU cores, 200GB storage
  • Proxmox VE: Инсталиран и конфигуриран (версия 8.x)
  • Network: Конфигуриран bridge (обикновено vmbr0)
  • Достъп: SSH и web UI достъп до Proxmox

Първоначална конфигурация

SSH към вашия Proxmox host и изпълнете:

# Update системата
apt update && apt upgrade -y

# Инсталирай необходимите tools
apt install -y git ansible python3-pip

# Конфигурирай network bridge (ако не е готов)
cat >> /etc/network/interfaces << EOF
auto vmbr1
iface vmbr1 inet static
    address 10.0.0.1/24
    bridge-ports none
    bridge-stp off
    bridge-fd 0
    post-up echo 1 > /proc/sys/net/ipv4/ip_forward
    post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j MASQUERADE
EOF

# Restart networking (внимавайте с SSH сесията!)
systemctl restart networking

Стъпка 1: Създаване на DevOps VM с Cloud-Init

Вместо ръчно да инсталираме всяка VM, ще използваме Cloud-Init template за автоматизация.

Създаване на Ubuntu Cloud-Init template

# Изтегли Ubuntu cloud image
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img

# Създай VM template
qm create 9000 --name ubuntu-cloud-template --memory 2048 --net0 virtio,bridge=vmbr0 --cores 2

# Import на диска
qm importdisk 9000 jammy-server-cloudimg-amd64.img local-lvm

# Attach диска
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0

# Cloud-Init drive
qm set 9000 --ide2 local-lvm:cloudinit

# Boot order
qm set 9000 --boot c --bootdisk scsi0

# Serial console
qm set 9000 --serial0 socket --vga serial0

# Добави SSH key (замени с твоя)
qm set 9000 --sshkey ~/.ssh/id_rsa.pub

# Конвертирай във template
qm template 9000

echo "✓ Template създаден успешно!"

Клониране на VM за DevOps

# Създай DevOps VM от template
qm clone 9000 100 --name devops-main --full

# Конфигурирай ресурси
qm set 100 --memory 8192 --cores 4

# Увеличи диска
qm resize 100 scsi0 +50G

# Cloud-Init конфигурация
qm set 100 --ipconfig0 ip=10.0.0.10/24,gw=10.0.0.1
qm set 100 --nameserver 8.8.8.8

# Стартирай VM
qm start 100

echo "⏳ Изчакай 30-60 секунди за boot..."
sleep 60

# Тествай SSH достъп
ssh [email protected] "echo 'VM е готова!'"

Стъпка 2: Автоматизирана инсталация с Ansible

Сега идва магията – ще автоматизираме цялата инсталация на DevOps stack с Ansible.

Инсталация на Ansible (на Proxmox host)

pip3 install ansible
ansible-galaxy collection install community.docker

Ansible Inventory

Създай файл inventory.ini:

[devops]
devops-main ansible_host=10.0.0.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[production]
prod-01 ansible_host=10.0.0.11 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[monitoring]
monitor-01 ansible_host=10.0.0.12 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[all:vars]
ansible_python_interpreter=/usr/bin/python3
domain=devops.local

Master Playbook

Създай deploy-devops.yml:

---
- name: Setup DevOps Infrastructure
  hosts: all
  become: yes
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install prerequisites
      apt:
        name:
          - apt-transport-https
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
          - python3-pip
        state: present

    - name: Add Docker GPG key
      apt_key:
        url: https://download.docker.com/linux/ubuntu/gpg
        state: present

    - name: Add Docker repository
      apt_repository:
        repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
        state: present

    - name: Install Docker
      apt:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
          - docker-compose-plugin
        state: present

    - name: Start Docker service
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: Add user to docker group
      user:
        name: "{{ ansible_user }}"
        groups: docker
        append: yes

    - name: Install Docker SDK for Python
      pip:
        name: docker
        state: present

- name: Deploy DevOps Stack
  hosts: devops
  become: yes
  vars:
    stack_dir: /opt/devops-stack
  tasks:
    - name: Create stack directory
      file:
        path: "{{ stack_dir }}"
        state: directory
        mode: '0755'

    - name: Create Docker networks
      docker_network:
        name: "{{ item }}"
        state: present
      loop:
        - devops-network
        - monitoring-network

    - name: Deploy Docker Compose stack
      copy:
        dest: "{{ stack_dir }}/docker-compose.yml"
        content: |
          version: '3.8'
          
          services:
            traefik:
              image: traefik:v2.10
              container_name: traefik
              restart: unless-stopped
              security_opt:
                - no-new-privileges:true
              networks:
                - devops-network
              ports:
                - 80:80
                - 443:443
              volumes:
                - /etc/localtime:/etc/localtime:ro
                - /var/run/docker.sock:/var/run/docker.sock:ro
                - ./traefik/traefik.yml:/traefik.yml:ro
                - ./traefik/acme.json:/acme.json
              labels:
                - "traefik.enable=true"
                - "traefik.http.routers.traefik.rule=Host(`traefik.{{ domain }}`)"
                - "traefik.http.routers.traefik.service=api@internal"

            gitea:
              image: gitea/gitea:latest
              container_name: gitea
              restart: unless-stopped
              networks:
                - devops-network
              volumes:
                - gitea-data:/data
                - /etc/timezone:/etc/timezone:ro
                - /etc/localtime:/etc/localtime:ro
              ports:
                - "3000:3000"
                - "2222:22"
              labels:
                - "traefik.enable=true"
                - "traefik.http.routers.gitea.rule=Host(`git.{{ domain }}`)"
                - "traefik.http.services.gitea.loadbalancer.server.port=3000"

            jenkins:
              image: jenkins/jenkins:lts
              container_name: jenkins
              restart: unless-stopped
              networks:
                - devops-network
              volumes:
                - jenkins-data:/var/jenkins_home
                - /var/run/docker.sock:/var/run/docker.sock
              environment:
                - JENKINS_OPTS=--prefix=/jenkins
              labels:
                - "traefik.enable=true"
                - "traefik.http.routers.jenkins.rule=Host(`jenkins.{{ domain }}`)"
                - "traefik.http.services.jenkins.loadbalancer.server.port=8080"

            registry:
              image: registry:2
              container_name: docker-registry
              restart: unless-stopped
              networks:
                - devops-network
              volumes:
                - registry-data:/var/lib/registry
              labels:
                - "traefik.enable=true"
                - "traefik.http.routers.registry.rule=Host(`registry.{{ domain }}`)"
                - "traefik.http.services.registry.loadbalancer.server.port=5000"

          networks:
            devops-network:
              external: true

          volumes:
            gitea-data:
            jenkins-data:
            registry-data:

    - name: Create Traefik configuration
      file:
        path: "{{ stack_dir }}/traefik"
        state: directory

    - name: Configure Traefik
      copy:
        dest: "{{ stack_dir }}/traefik/traefik.yml"
        content: |
          api:
            dashboard: true
          
          entryPoints:
            web:
              address: :80
            websecure:
              address: :443
          
          providers:
            docker:
              endpoint: "unix:///var/run/docker.sock"
              exposedByDefault: false

    - name: Create acme.json for SSL
      file:
        path: "{{ stack_dir }}/traefik/acme.json"
        state: touch
        mode: '0600'

    - name: Start Docker Compose stack
      community.docker.docker_compose:
        project_src: "{{ stack_dir }}"
        state: present

- name: Deploy Monitoring Stack
  hosts: monitoring
  become: yes
  vars:
    monitor_dir: /opt/monitoring
  tasks:
    - name: Create monitoring directory
      file:
        path: "{{ monitor_dir }}"
        state: directory

    - name: Deploy monitoring stack
      copy:
        dest: "{{ monitor_dir }}/docker-compose.yml"
        content: |
          version: '3.8'
          
          services:
            prometheus:
              image: prom/prometheus:latest
              container_name: prometheus
              restart: unless-stopped
              volumes:
                - ./prometheus.yml:/etc/prometheus/prometheus.yml
                - prometheus-data:/prometheus
              ports:
                - "9090:9090"
              command:
                - '--config.file=/etc/prometheus/prometheus.yml'

            grafana:
              image: grafana/grafana:latest
              container_name: grafana
              restart: unless-stopped
              volumes:
                - grafana-data:/var/lib/grafana
              ports:
                - "3001:3000"
              environment:
                - GF_SECURITY_ADMIN_PASSWORD=admin
                - GF_USERS_ALLOW_SIGN_UP=false

            loki:
              image: grafana/loki:latest
              container_name: loki
              restart: unless-stopped
              ports:
                - "3100:3100"
              volumes:
                - loki-data:/loki

          volumes:
            prometheus-data:
            grafana-data:
            loki-data:

    - name: Create Prometheus config
      copy:
        dest: "{{ monitor_dir }}/prometheus.yml"
        content: |
          global:
            scrape_interval: 15s
          
          scrape_configs:
            - job_name: 'prometheus'
              static_configs:
                - targets: ['localhost:9090']
            
            - job_name: 'docker'
              static_configs:
                - targets: ['10.0.0.10:9323']

    - name: Start monitoring stack
      community.docker.docker_compose:
        project_src: "{{ monitor_dir }}"
        state: present

Изпълнение на Ansible

# Тествай connectivity
ansible all -i inventory.ini -m ping

# Изпълни playbook
ansible-playbook -i inventory.ini deploy-devops.yml

# След 5-10 минути всичко ще е готово!

Стъпка 3: Конфигурация на DevOps Stack

След успешното deployment, нека конфигурираме всеки компонент.

Gitea Setup

  1. Отвори браузър: http://10.0.0.10:3000
  2. Първоначална настройка:
    • Database: SQLite3
    • Server Domain: git.devops.local
    • SSH Port: 2222
    • Admin Account: създай потребител

Jenkins Setup

  1. Достъп: http://10.0.0.10:8080
  2. Вземи initial password:
ssh [email protected] "sudo docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword"
  1. Инсталирай suggested plugins
  2. Инсталирай допълнително:
    • Docker Pipeline
    • Gitea Plugin
    • Ansible Plugin

Създаване на CI/CD Pipeline

Ето пример на Jenkinsfile за автоматизирано внедряване:

pipeline {
    agent any
    
    environment {
        DOCKER_REGISTRY = 'registry.devops.local'
        APP_NAME = 'myapp'
        VERSION = "${env.BUILD_NUMBER}"
    }
    
    stages {
        stage('Checkout') {
            steps {
                git branch: 'main',
                    url: 'http://git.devops.local/user/myapp.git'
            }
        }
        
        stage('Build') {
            steps {
                script {
                    docker.build("${DOCKER_REGISTRY}/${APP_NAME}:${VERSION}")
                }
            }
        }
        
        stage('Test') {
            steps {
                sh 'docker run --rm ${DOCKER_REGISTRY}/${APP_NAME}:${VERSION} npm test'
            }
        }
        
        stage('Push to Registry') {
            steps {
                script {
                    docker.withRegistry("http://${DOCKER_REGISTRY}") {
                        docker.image("${DOCKER_REGISTRY}/${APP_NAME}:${VERSION}").push()
                        docker.image("${DOCKER_REGISTRY}/${APP_NAME}:${VERSION}").push('latest')
                    }
                }
            }
        }
        
        stage('Deploy to Production') {
            steps {
                ansiblePlaybook(
                    playbook: 'deploy-app.yml',
                    inventory: 'inventory.ini',
                    extraVars: [
                        app_version: "${VERSION}",
                        app_name: "${APP_NAME}"
                    ]
                )
            }
        }
    }
    
    post {
        success {
            echo "✓ Deployment successful! Version: ${VERSION}"
        }
        failure {
            echo "✗ Deployment failed!"
        }
    }
}

Deployment Playbook

Създай deploy-app.yml:

---
- name: Deploy Application
  hosts: production
  become: yes
  vars:
    registry: registry.devops.local
  tasks:
    - name: Pull latest image
      docker_image:
        name: "{{ registry }}/{{ app_name }}:{{ app_version }}"
        source: pull

    - name: Stop old container
      docker_container:
        name: "{{ app_name }}"
        state: stopped
      ignore_errors: yes

    - name: Remove old container
      docker_container:
        name: "{{ app_name }}"
        state: absent
      ignore_errors: yes

    - name: Start new container
      docker_container:
        name: "{{ app_name }}"
        image: "{{ registry }}/{{ app_name }}:{{ app_version }}"
        state: started
        restart_policy: unless-stopped
        ports:
          - "80:3000"
        networks:
          - name: devops-network

Стъпка 4: Bash скрипт за бърза автоматизация

За случаите, когато искате бързо да deploy-нете без Ansible:

#!/bin/bash
# deploy-quick.sh - Бърз deployment скрипт

set -e

APP_NAME="myapp"
VERSION="${1:-latest}"
REGISTRY="registry.devops.local"
PROD_HOST="10.0.0.11"

echo "🚀 Starting deployment of ${APP_NAME}:${VERSION}"

# Build image
echo "📦 Building Docker image..."
docker build -t ${REGISTRY}/${APP_NAME}:${VERSION} .

# Push to registry
echo "⬆️  Pushing to registry..."
docker push ${REGISTRY}/${APP_NAME}:${VERSION}

# Deploy to production
echo "🎯 Deploying to production..."
ssh ubuntu@${PROD_HOST} << EOF
    docker pull ${REGISTRY}/${APP_NAME}:${VERSION}
    docker stop ${APP_NAME} 2>/dev/null || true
    docker rm ${APP_NAME} 2>/dev/null || true
    docker run -d \
        --name ${APP_NAME} \
        --restart unless-stopped \
        -p 80:3000 \
        ${REGISTRY}/${APP_NAME}:${VERSION}
EOF

echo "✅ Deployment completed successfully!"
echo "🌐 Application available at: http://${PROD_HOST}"

Направи го executable и използвай:

chmod +x deploy-quick.sh
./deploy-quick.sh v1.0.0

Стъпка 5: Monitoring и Observability

Grafana Dashboards

  1. Достъп до Grafana: http://10.0.0.12:3001
  2. Login: admin/admin
  3. Добави Prometheus data source:
    • URL: http://prometheus:9090
  4. Import готов dashboard:
    • ID: 1860 (Node Exporter Full)
    • ID: 179 (Docker Monitoring)

Loki за логове

Инсталирай Promtail на production VM:

ssh [email protected] << 'EOF'
sudo docker run -d \
  --name promtail \
  --restart unless-stopped \
  -v /var/log:/var/log:ro \
  -v /var/lib/docker/containers:/var/lib/docker/containers:ro \
  grafana/promtail:latest \
  -config.file=/etc/promtail/config.yml
EOF

Best Practices и Production Tips

1. Security Hardening

# Firewall на Proxmox host
ufw allow 22/tcp
ufw allow 8006/tcp  # Proxmox Web UI
ufw enable

# SSH key-based authentication only
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

# Docker security
echo '{"live-restore": true, "userland-proxy": false}' > /etc/docker/daemon.json
systemctl restart docker

2. Backup Strategy

# Automated VM backups
cat > /usr/local/bin/backup-vms.sh << 'EOF'
#!/bin/bash
vzdump 100 --mode snapshot --storage local --compress zstd
vzdump 101 --mode snapshot --storage local --compress zstd
vzdump 102 --mode snapshot --storage local --compress zstd
EOF

chmod +x /usr/local/bin/backup-vms.sh

# Cron job за нощни backups
echo "0 2 * * * /usr/local/bin/backup-vms.sh" | crontab -

3. Resource Management

Мониторинг на ресурсите:

# CPU и RAM usage на VM
qm status 100 --verbose

# Resize VM resources динамично
qm set 100 --memory 16384
qm set 100 --cores 8

4. Network Optimization

# Enable jumbo frames за по-добра performance
ip link set vmbr0 mtu 9000

# TCP tuning
cat >> /etc/sysctl.conf << EOF
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
EOF

sysctl -p

Troubleshooting

Проблем: VM не стартира

# Проверка на status
qm status 100

# Проверка на logs
tail -f /var/log/pve/qemu-server/100.log

# Ръчно стартиране с verbose output
qm start 100 --verbose

Проблем: Docker контейнер не се стартира

# SSH към VM
ssh [email protected]

# Logs на контейнера
docker logs jenkins --tail 100

# Inspect на контейнера
docker inspect jenkins

# Resource usage
docker stats

Проблем: Ansible connection timeout

# Test connectivity
ansible all -i inventory.ini -m ping -vvv

# Test SSH manual
ssh -vvv [email protected]

# Check firewall
sudo ufw status

Проблем: Registry push/pull failures

# Check registry status
curl http://10.0.0.10:5000/v2/_catalog

# Docker daemon insecure registry (за testing)
cat > /etc/docker/daemon.json << EOF
{
  "insecure-registries": ["registry.devops.local:5000"]
}
EOF

systemctl restart docker

Заключение и Next Steps

Честито! Изградихте функционална DevOps инфраструктура на Proxmox. Сега имате:

✅ Self-hosted Git server
✅ Automated CI/CD pipeline
✅ Private container registry
✅ Production deployment automation
✅ Monitoring и logging

Какво следва?

За начинаещи:

  1. Експериментирайте с различни приложения
  2. Разберете Jenkins pipelines задълбочено
  3. Научете Docker networking и volumes
  4. Практикувайте Git workflows

За напреднали:

  1. Имплементирайте Kubernetes вместо Docker Swarm
  2. Добавете GitOps с ArgoCD
  3. Infrastructure as Code с Terraform
  4. Service mesh с Istio
  5. Security scanning (Trivy, SonarQube)
  6. High Availability setup с Proxmox clustering

Полезни ресурси

Финални мисли

DevOps не е просто набор от tools – това е култура на автоматизация, collaboration и continuous improvement. Тази инфраструктура е вашата playground за експериментиране и учене. Не се страхувайте да чупите нещата – всеки failure е learning opportunity.

Proxmox ви дава свободата да изграждате, тествате и внедрявате без constraints на cloud costs. Използвайте го максимално!


Автор бележка: Всички конфигурации в тази статия са тествани и работят. За production use, задължително имплементирайте SSL certificates, proper authentication и regular backups.

Въпроси? Експериментирайте, гледайте логове, четете документация. DevOps е journey, не destination! 🚀

Федя Серафиев

Федя Серафиев

Федя Серафиев e собственик на уебсайта urocibg.eu. Той намира удовлетворение в това да помага на хората да решават и най-сложните технически проблеми. Сегашната му цел е да пише лесни за следване статии, така че подобни проблеми изобщо да не възникват.

Благодарим ви за прочитането на статията! Ако намерихте информацията за полезна, можете да дарите посредством бутоните по-долу:

Подобни статии