Продвинутый

Навыки реальных инженеров: практические практики из .agents

Алексей Кузнецов
Алексей Кузнецов
Системный администратор30 октября 2026 г.10 мин чтения

Практическое руководство по навыкам реальных инженеров, взятые из .agents: Docker, Kubernetes, Proxmox, ZFS, Ansible и другие инструменты для надежной инфрас...

Навыки реальных инженеров: практические практики из .agents

Практическое руководство по навыкам реальных инженеров, взятые из .agents: Docker, Kubernetes, Proxmox, ZFS, Ansible и другие инструменты для надежной инфраструктуры.

Основные принципы инженерии в реальном мире

Системный подход к решению задач

Реальный инженер не «гасит пожар», а сначала формирует карту состояния.
Он собирает логи, проверяет зависимости и только потом ищет причину сбоя.

Пример диагностики сетевого сервиса

bash
# убедиться, что порт 443 свободен
ss -tlnp | grep :443

# посмотреть последние записи журнала systemd
journalctl -u nginx -n 20 --no-pager

Если сервис не отвечает, проверяем стек зависимостей:

bash
# список загруженных модулей nginx
systemctl list-units --type=service | grep nginx

Для изоляции проблем удобно использовать Docker‑Compose, чтобы запустить «чистый» экземпляр без влияния хоста:

yaml
version: "3.8"
services:
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "8443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro

Запуск:

bash
docker compose up -d

Такой подход позволяет воспроизводить окружение на любой машине и быстро проверять гипотезы.


Предсказуемость конфигураций

Предсказуемость достигается, когда конфигурация хранится в версии‑контроле, проверяется тестами и применяется одинаково на всех узлах.

Системаd‑unit для автоматического старта сервиса

ini
[Unit]
Description=My custom service
After=network.target

[Service]
ExecStart=/usr/local/bin/my-service --config /etc/my-service.conf
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Помещаем файл в /etc/systemd/system/my-service.service, затем:

bash
systemctl daemon-reload
systemctl enable my-service
systemctl start my-service

ZFS‑снимок перед изменением

bash
# создать снимок pool/volume@pre_change
zfs snapshot pool/volume@pre_change

# при необходимости откатиться
zfs rollback pool/volume@pre_change

Proxmox‑конфиг ноды (пример VM‑настроек)

ini
# /etc/pve/nodes/node1/qemu.conf
maxdisks: 10
onstart: stop

Такой файл гарантирует, что параметры узла не изменятся «на коленке», а применяются одинаково при каждом запуске виртуальной машины.

В итоге, системный подход и предсказуемость конфигураций позволяют построить инфраструктуру, которая работает стабильно годами, а не «по‑настоящему» каждый раз по‑своему.

Контейнеризация и оркестрация: Docker и Kubernetes

Docker Compose для микросервисов

Во-первых, Docker Compose — удобный инструмент для описания и запуска нескольких контейнеров, которые взаимодействуют через сети и общие тома. На примере небольшого проекта «веб‑сайт + БД + кеш» покажем, как быстро собрать стек в домашней лаборатории.

yaml
# docker-compose.yml
version: "3.9"
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./web:/usr/share/nginx/html:ro
    depends_on:
      - db
    environment:
      - DB_HOST=db
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: demo
    volumes:
      - pg_data:/var/lib/postgresql/data
  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
volumes:
  pg_data:

Команды для работы с Compose:

bash
# запуск стека в фоне
docker compose up -d

# проверка статуса контейнеров
docker compose ps

# остановка и удаление контейнеров, сетей и томов
docker compose down -v

Практическая точка зрения: если требуется изменить конфигурацию веб‑сервера, достаточно отредактировать файл ./web/index.html и перезапустить контейнер web через docker compose restart web. Docker Compose автоматически подхватит изменения без полной перезагрузки стека.

Для ARM‑платформ (например, Raspberry Pi) используйте теги с указанием архитектуры, например nginx:alpine — образ уже содержит нужный набор инструкций.

Документация Docker Compose

k3s: лёгкий кластер на Raspberry Pi

k3s — это серверный пакет Kubernetes, оптимизированный для небольших ресурсов и простого развертывания. Он подходит для домашней лаборатории, где нужен кластер из нескольких нод без излишней сложности.

Установка k3s на первой ноде (мастер)

bash
# Подготовка системы
sudo apt-get update && sudo apt-get install -y curl

# Запуск k3s (используется системныйd service)
curl -sfL https://get.k3s.io | sh -

# Проверка статуса
sudo systemctl status k3s

k3s автоматически использует встроенный контейнерный runtime (containerd), поэтому отдельный Docker не требуется.

Добавление агентов (worker‑нод)

На каждой дополнительной Raspberry Pi выполняем:

bash
curl -sfL https://get.k3s.io | K3S_URL=https://<MASTER_IP>:6443 K3S_TOKEN=<TOKEN> sh -

После этого в kubectl можно проверить наличие агентов:

bash
kubectl get nodes

Пример простого манифеста

yaml
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Применяем манифест:

bash
kubectl apply -f nginx-deployment.yaml

Проверяем, что поды запустились и сервис доступен:

bash
kubectl get pods
kubectl get svc

Практическая точка зрения: k3s обеспечивает предсказуемую работу кластера за счёт минимального набора зависимостей и автоматического управления systemd‑юнитами. Это позволяет сосредоточиться на самом приложении, а не на настройке инфраструктуры.

Официальная документация k3s

Таким образом, Docker Compose упрощает локальный запуск отдельных микросервисов, а k3s дает возможность масштабировать их в надёжный кластер, сохраняя при этом лёгкость и предсказуемость.

Виртуализация и хранилище: Proxmox, ZFS, TrueNAS

Виртуализация и хранилище: Proxmox, ZFS, TrueNAS

Proxmox VE: настройка кластера из трёх нод

Во-первых, убедитесь, что все три машины работают под одинаковой версией Proxmox VE (на момент написания — 8.1). Рекомендую использовать одинаковый ядро и одинаковый набор пакетов, иначе будет «непредсказуемость» при миграции виртуальных машин.

Сначала подготовьте сеть: статическая IP‑адресация, одинаковый подnet (например, 192.168.10.0/24) и свободный порт 3890 для коммуникации между узлами. Откройте его в фаерволе, если он включён:

bash
# на всех узлах
iptables -A INPUT -p tcp --dport 3890 -j ACCEPT
iptables -A INPUT -p udp --dport 3890 -j ACCEPT

Далее, на первом узле (назовём его pnx01) создаём кластер:

bash
pvecm create mycluster

На втором узле (pnx02) добавляем первый узел в кластер:

bash
pvecm add pnx01:3890

На третьем узле (pnx03) повторяем ту же команду, указав IP первого узла:

bash
pvecm add pnx01:3890

Проверить состояние кластера можно командой:

bash
pvecm status

Если всё в порядке, вы увидите список всех узлов и их роль (master, slave).

Практическая точка зрения: после создания кластера разместите основные контейнеры или виртуальные машины на узле‑мастер, а резервные копии — на остальные. Это повышает отказоустойчивость и упрощает балансировку нагрузки.

Пример /etc/hosts (на всех узлах):

text
192.168.10.10   pnx01
192.168.10.11   pnx02
192.168.10.12   pnx03

После настройки кластера включите Ceph или ZFS‑based storage, если планируете использовать общий диск.


ZFS на TrueNAS: резервное копирование и сжатие

Справедливо, ZFS — это не просто файловая система, а набор функций, которые делают её идеальной для долгосрочного хранения. На TrueNAS вы получаете готовый UI, но при работе с консолью удобно использовать несколько команд.

Сжатие

Включить сжатие можно уже при создании dataset. Рекомендую алгоритм lz4 — он обеспечивает хорошее соотношение «скорость‑компактность» без значительного нагрузки на CPU.

bash
zfs create -o compression=lz4 -o atime=off tank/data

Параметр atime=off отключает запись времени доступа к файлам, что экономит I/O и усиливает предсказуемость работы.

Резервное копирование

Самый простой способ — периодические snapshot‑ы и их репликация на второй TrueNAS (или внешний диск).

  1. Создание snapshot (например, ежедневно в 02:00):
bash
0 2 * * * /sbin/zfs snapshot tank/data@$(date +\%Y\%m\%d)
  1. Отправка и получение (на втором TrueNAS, доступном по SSH):
bash
zfs send -R tank/data@$(date +\%Y\%m\%d) | ssh user@backup-host zfs receive -R backup/tank/data

Эту команду удобно добавить в cron того же TrueNAS, где выполняются snapshot‑ы.

Автоматическая очистка старых snapshot‑ов

Чтобы не заполнять диски бесконечными данными, настройте prune‑скрипт, который будет удалять snapshot‑ы старше, скажем, 30 дней:

bash
0 3 * * * /sbin/zfs list -t snapshot -o name -s creation tank/data | \
  grep -v '@auto' | sort -r | tail -n +31 | xargs -n1 zfs destroy

Этот скрипт удаляет все snapshot‑ы, кроме последних 30, тем самым поддерживая предсказуемость использования диска.

Итого: ZFS на TrueNAS позволяет комбинировать высокий сжатие, мгновенные снимки и простую репликацию, что делает её надёжным хранилищем для контейнеров Proxmox, баз данных и резервных копий.


Если понадобится более детальная настройка Ceph‑pool или интеграция ZFS с Proxmox, дайте знать — подготовлю отдельный гайд.

Автоматизация и мониторинг: Ansible, Prometheus

### Ansible для автоматической конфигурации

Ansible — удобный способ описать желаемое состояние серверов без постоянного вручную ввода команд. В домашней лаборатории я использую его для развертывания базовых пакетов, настройки сетевых интерфейсов и применения конфигураций ZFS.

Инвентори (файл inventory.ini):

ini
[homelab]
winnie-pc ansible_host=192.168.1.10
piglet ansible_host=192.168.1.11
sova ansible_host=192.168.1.12

Playbook для установки Docker CE и Docker‑Compose на всех узлах:

yaml
# site.yml
- hosts: homelab
  become: true
  tasks:
    - name: Обновить apt‑кеши
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Установить необходимые пакеты
      apt:
        name:
          - ca-certificates
          - curl
          - gnupg
          - lsb-release
        state: present

    - name: Добавить официальный репозиторий Docker
      apt_key:
        url: https://download.docker.com/linux/ubuntu/gpg
        state: present

    - name: Добавить репозиторий Docker
      apt_repository:
        repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
        state: present
        update_cache: yes

    - name: Установить Docker Engine
      apt:
        name: docker-ce
        state: present

    - name: Установить Docker‑Compose (v2)
      pip:
        name: docker-compose
        state: present

    - name: Запустить и включить Docker
      systemd:
        name: docker
        enabled: yes
        state: started

Запуск:

bash
ansible-playbook -i inventory.ini site.yml

Для более сложных сценариев удобно использовать роли из Ansible Galaxy, например geerlingguy.docker, и переопределять переменные в group_vars/all.yml.

Практический совет – держите инвентори и playbook в Git, чтобы отслеживать изменения и легко возвращаться к рабочей версии.

Документация Ansible


### Prometheus + Grafana: метрики и алерты

Prometheus — система сбора метрик, а Grafana — визуализация и оповещение. В моём кластере Proxmox я запускаю node_exporter на каждом сервере и Prometheus в отдельном контейнере, а Grafana — на хосте sova.

docker‑compose для Prometheus и node_exporter:

yaml
# docker-compose.yml
version: "3.8"

services:
  prometheus:
    image: prom/prometheus:v2.53.0
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"

  node-exporter:
    image: prom/node-exporter:v1.8.1
    container_name: node-exporter
    restart: unless-stopped
    network_mode: host
    pid: host
    privileged: true

volumes:
  prometheus_data:

prometheus.yml (минимальная конфигурация):

yaml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['winnie-pc:9100', 'piglet:9100', 'sova:9100']

Запуск:

bash
docker compose up -d

Alertmanager (можно добавить как отдельный сервис в compose) — обрабатывает правила оповещений. Пример правила в файле alerts.yml:

yaml
groups:
  - name: node.rules
    rules:
      - alert: HighCPUUsage
        expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "CPU usage > 80% on {{ $labels.instance }}"
          description: "Average CPU idle time is below 20% for 2 minutes."

Grafana

  1. Установите Grafana (Docker‑image grafana/grafana:10.2.0).
  2. В веб‑интерфейсе добавьте datasource Prometheus с URL http://prometheus:9090.
  3. Импортируйте готовый дашборд, например Node Exporter Full (ID 1860) через Dashboard → Import.

Alerting в Grafana (встроенная):

yaml
# rule in Grafana UI → Alerting → Create alert
- Name: CPU usage alert
  Condition: WHILE sum(rate(node_cpu_seconds_total{mode="idle"}[5m])) < 20
  For: 2m
  Labels: { severity: critical }
  Annotations: {
    summary: "High CPU usage on {{ $labels.instance }}",
    description: "CPU idle time dropped below 20% for more than 2 minutes."
  }

Настройте получателя (email, Slack, Telegram) в Alerting → Notification channels.

Практический совет – включайте мониторинг только после того, как убедились, что метрики собираются корректно. Проверьте, что node_exporter доступен по http://<host>:9100/metrics.

Официальная документация Prometheus
Grafana Alerting

Часто задаваемые вопросы

Как правильно использовать Docker‑Compose для изоляции окружения при отладке сетевого сервиса?

Создайте docker-compose.yml, где сервис запускается в отдельном контейнере без доступа к файлам хоста, если это не требуется. Пример конфигурации показывает монтирование только нужных конфигурационных файлов в режиме ro, а порты пробрасываются явно. Запуск docker compose up -d позволяет воспроизвести окружение на любой машине и проверять гипотезы без риска повредить основной сервис.

Что делать, если после изменения конфигурации сервиса он не стартует, но в журналах нет ошибок?

Сначала проверьте, что unit‑файл systemd корректен: systemctl daemon-reload и systemctl status my-service. Убедитесь, что все зависимости, такие как сеть и файловая система, доступны, используя systemctl list-dependencies. Если сервис всё равно не стартует, запустите его в режиме отладки (ExecStart=/usr/local/bin/my-service --config /etc/my-service.conf --debug) и посмотрите вывод в journalctl -u my-service -f.

Как обеспечить предсказуемость конфигураций при работе с Ansible и systemd‑units?

Храните файлы unit‑ов и конфиги в репозитории Git, применяйте роли Ansible, которые копируют их в /etc/systemd/system/ и сразу вызывают systemctl daemon-reload. После изменения добавьте проверку через ansible-playbook --check и запустите тестовый запуск на отдельном узле. Такой подход гарантирует одинаковую конфигурацию на всех узлах и упрощает откат при проблемах.

Почему важно делать ZFS‑снимок перед применением изменений в хранилище и как быстро откатить?

Снимок zfs snapshot pool/volume@pre_change фиксирует состояние данных в момент изменения, позволяя откатиться без потери целостности. При необходимости выполните zfs rollback pool/volume@pre_change. Этот шаг минимизирует риск потери данных и ускоряет восстановление в случае неожиданного сбоя.

Поделиться:TelegramX / TwitterVK