Продвинутый

mattpocock/skills: Real Engineer Skills from .agents

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

Практическое руководство по реальным инженерным навыкам из .agents‑директории MattPocock, с примерами Docker, Kubernetes, systemd и домашней лаборатории. и м...

mattpocock/skills: Real Engineer Skills from .agents

Практическое руководство по реальным инженерным навыкам из .agents‑директории MattPocock, с примерами Docker, Kubernetes, systemd и домашней лаборатории. и мониторинг

Системный подход к инженерной работе

Определение задачи и анализ причин

Перед тем как приступать к решению, сформулируйте задачу в виде конкретного вопроса или требования. Например, если сервис nginx падает после обновления, задача может звучать так: «Почему nginx не стартует после перехода на Ubuntu 22.04 с nginx 1.25?».

Для анализа причин используйте следующие шаги:

  1. Сбор логиов.

    bash
    journalctl -u nginx.service -b --since "10 minutes ago"
    

    Логи показывают, какие ошибки произошли в текущем запуске.

  2. Проверка состояния юнита.

    bash
    systemctl status nginx.service
    

    Вывод содержит код возврата, последнюю строку журнала и список зависимостей.

  3. Сравнение конфигураций.
    Если проблема появилась после изменения файла /etc/nginx/nginx.conf, просмотрите diff:

    bash
    diff -u /etc/nginx/nginx.conf.old /etc/nginx/nginx.conf.new
    
  4. Тестирование в изолированной среде.
    Запустите контейнер с тем же образом, что и в продакшене, и проверьте запуск вручную:

    bash
    docker run --rm -p 8080:80 nginx:1.25
    

Если после анализа вы обнаружите, что ошибка связана с несовместимостью модуля stream и новой версией nginx, формулировка задачи может быть уточнена: «Настроить nginx так, чтобы модуль stream работал корректно в контейнере nginx:1.25».

Для более глубокой диагностики полезно посмотреть официальную документацию systemd: systemd manual.


Документирование и воспроизводимость

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

Хранение конфигураций.

bash
git init ~/nginx-config
cd ~/nginx-config
# пример конфигурационного файла
cat > nginx.conf <<'EOF'
worker_processes  auto;
events {
    worker_connections 1024;
}
http {
    include /etc/nginx/mime.types;
    server {
        listen 80;
        server_name example.com;
        location / {
            root /var/www/html;
        }
    }
}
EOF
git add nginx.conf
git commit -m "Initial nginx configuration"

Docker‑контейнеры.

dockerfile
# Dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
yaml
# docker-compose.yml
version: "3.8"
services:
  web:
    build: .
    ports:
      - "8080:80"

Запуск полностью воспроизводим одной командой:

bash
docker compose up -d

Ansible‑playbook (для автоматизации настройки сервера).

yaml
- hosts: servers
  become: true
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Deploy nginx configuration
      copy:
        src: nginx.conf
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx

  handlers:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted

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

Для справки по best‑practice в документировании инфраструктуры см. Docker docs.


Во-первых, чётко сформулируйте задачу, собирайте логи и проверяйте конфигурацию.
Во‑вторых, фиксируйте всё в Git, используйте Docker‑образы и Ansible‑playbooks — тогда воспроизводимость станет привычкой, а не исключением.

Если придерживаться этих принципов, ваши решения будут предсказуемыми, а инфраструктура — надёжной.

Инфраструктура как код

Инфраструктура как код — это подход, позволяющий управлять серверами и сервисами через дескриптивные файлы, а не через ручные команды. Он упрощает воспроизводимость, версионирование и совместную работу команды.

Ansible для конфигурации

Ansible — это агент‑less инструмент, который использует SSH для удалённого управления._playbook описывает желаемое состояние системы, а inventory содержит список узлов.

Пример инвентаря (inventory.ini):

ini
[home_lab]
winni-pukh ansible_host=192.168.1.10
p’yatchok ansible_host=192.168.1.11
sova ansible_host=192.168.1.12

Playbook для установки Nginx (install_nginx.yml):

yaml
---
- hosts: home_lab
  become: true
  tasks:
    - name: Установить пакет nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Скопировать конфиг Nginx
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'

    - name: Перезапустить сервис
      service:
        name: nginx
        state: restarted

Запуск playbook:

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

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

Официальная документация: Ansible documentation.

Terraform и управление облаком

Terraform — это инструмент инфраструктурного кода, позволяющий описывать облачные ресурсы в HCL (HashiCorp Configuration Language). Он подходит как для локального Proxmox, так и для публичных облаков AWS, GCP, Azure.

Инициализация проекта:

bash
terraform init

Пример файла main.tf (создание VM в Proxmox через API):

hcl
provider "proxmox" {
  token_url   = "https://your-proxmox:8006/api2/json"
  token       = "your-api-token"
  host        = "https://your-proxmox"
}

resource "proxmox_vm" "vm_winni" {
  name            = "winni-pukh"
  vmid            = 101
  memory          = 2048
  cores           = 2
  netif {
    device = eth0
    model  = "virtio"
    bridge = "vmbr0"
  }
  os_disk {
    volume_size = 20
    storage     = "local-lvm"
  }
}

Переменные (variables.tf):

hcl
variable "vm_name" {
  description = "Имя виртуальной машины"
  type        = string
}

Использование переменной:

hcl
resource "proxmox_vm" "vm_p’yatchok" {
  name   = var.vm_name
  vmid   = 102
  # остальные параметры …
}

Применение изменений:

bash
terraform apply -var="vm_name=winni-pukh"

Terraform сохраняет состояние в файле terraform.tfstate, что позволяет откатываться и планировать изменения. Для работы с публичными облаками достаточно заменить провайдер и параметры (например, aws → amazon.aws).

Официальная документация: Terraform docs.


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

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

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

Когда мы переходим от запуска одного контейнера к полноценной инфраструктуре, возникает вопрос управления связями между ними. Если Docker — это кирпичи, то Docker Compose — это чертёж здания. С практической точки зрения, использование Compose позволяет превратить разрозненный набор команд docker run в предсказуемый и воспроизводимый процесс.

В моей домашней лаборатории я часто использую Compose для развёртывания связок, например, когда нужно поднять веб-сервис вместе с базой данных и кэшем. Главное преимущество здесь — изоляция сетевого окружения. Каждый проект получает свою виртуальную сеть, и сервисы внутри неё могут обращаться друг к другу по именам, а не по IP-адресам.

Рассмотрим типичный пример конфигурации для связки Nginx, Python-приложения и PostgreSQL. Обратите внимание, как мы определяем зависимости и параметры перезапуска.

yaml
version: '3.8'

services:
  db:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: app_db
    volumes:
      # Храним данные на хосте, чтобы не потерять их при удалении контейнера
      - ./postgres_data:/var/lib/postgresql/data

  app:
    build: .
    restart: unless-stopped
    depends_on:
      - db
    environment:
      DATABASE_URL: postgresql://user:password@db:5432/app_db
    networks:
      - backend

  nginx:
    image: nginx:stable-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app
    networks:
      - backend

networks:
  backend:
    driver: bridge

При работе с таким конфигом важно соблюдать порядок. Сначала убедитесь, что директория для данных базы (./postgres_data) создана и имеет правильные права доступа. Запуск выполняется одной командой:

bash
docker compose up -d

Флаг -d переводит контейнеры в фоновый режим. Если что-то пошло не так, не пытайтесь угадать причину — используйте логи конкретного сервиса: docker compose logs -f app. Это гораздо эффективнее, чем просматривать общий поток вывода.

Kubernetes (k3s) в домашней лаборатории

Если Docker Compose — это решение для одного сервера, то Kubernetes — это способ управлять целым парком машин. Однако полноценный кластер Kubernetes (vanilla K8s) слишком требователен к ресурсам для домашнего сервера. Именно поэтому для домашних лабораторий я рекомендую k3s от Rancher. Это легковесная реализация, которая вырезает всё лишнее, оставляя только необходимые компоненты для работы оркестрации.

В моей конфигурации k3s работает на трёх нодах под управлением Proxmox. Это позволяет реализовать отказоустойчивость: если одна виртуальная машина уйдёт в ребут, планировщик Kubernetes просто перенесёт нагрузку на оставшиеся узлы.

Для начала работы на первой ноде (которая будет мастером) достаточно выполнить установку одной командой:

bash
curl -sfL https://get.k3s.io | sh -

После установки проверьте статус узлов:

bash
sudo k3s kubectl get nodes

С практической точки зрения, работа в k3s требует привыкания к манифестам. Вместо простого файла docker-compose.yml вам придётся описывать Deployment, Service и Ingress. Это кажется избыточным, но именно эта избыточность даёт предсказуемость. Вы точно знаете, как будет вести себя приложение при обновлении или при нехватке памяти.

Пример простого манифеста для развёртывания приложения:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2 # Запускаем две копии для отказоустойчивости
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
          requests:
            memory: "64Mi"
            cpu: "250m"

Обратите внимание на секцию resources. В домашней лаборатории это критически важно. Без явного указания лимитов один «прожорливый» контейнер может съесть всю оперативную память на хосте, что приведёт к срабатыванию OOM Killer и падению всей системы. Всегда определяйте requests (сколько ресурсов гарантировано) и limits (максимальный порог), чтобы ваш кластер оставался стабильным.

Мониторинг, логирование и надёжность

Мониторинг, логирование и надёжность

Если вы строите инфраструктуру, которая должна работать годами, а не только пока вы смотрите в монитор, мониторинг перестаёт быть «приятным дополнением» и становится базовой потребностью. С практической точки зрения, мониторинг — это не про красивые графики в Grafana, а про предсказуемость. Вы должны узнавать о проблеме раньше, чем она превратится в инцидент.

Я всегда придерживаюсь принципа: если у сервиса нет метрик и логов, значит, этого сервиса не существует. Вы не можете управлять тем, что не измеряете.

systemd как фундамент

Многие новички пытаются строить сложные системы оркестрации там, где достаточно стандартных средств Linux. Но прежде чем лезть в Kubernetes, нужно научиться правильно использовать systemd. Это не просто менеджер процессов, это мощнейший инструмент для обеспечения надёжности.

В моей домашней лаборатории почти все вспомогательные сервисы, которые не упакованы в Docker, работают через пользовательские или системные unit-файлы. Главная ошибка здесь — запускать скрипты напрямую через cron или просто в фоне. Это лишает вас контроля над перезапуском при сбое и нормального логирования.

Чтобы сервис был предсказуемым, его unit-файл должен чётко описывать зависимости. Например, если ваш сервис зависит от сетевого хранилища, он не должен пытаться стартовать раньше, чем смонтируется NFS-шара.

Пример надёжного конфига для простого Python-скрипта, который собирает данные:

ini
[Unit]
Description=Data Collector Service
# Ждём, пока поднимется сеть и смонтируются диски
After=network-online.target remote-fs.target
Wants=network-online.target

[Service]
Type=simple
User=collector
Group=collector
WorkingDirectory=/opt/collector
# Путь к исполняемому файлу и аргументы
ExecStart=/usr/bin/python3 /opt/collector/main.py
# Автоматический перезапуск при падении с задержкой 5 секунд
Restart=on-failure
RestartSec=5s
# Ограничение ресурсов, чтобы сервис не «съел» всю память сервера
MemoryMax=512M
CPUQuota=20%

[Install]
WantedBy=multi-user.target

После создания файла не забудьте обновить конфигурацию: sudo systemctl daemon-reload

И проверьте статус: systemctl status collector.service

Логи такого сервиса автоматически попадают в journald. Это избавляет от необходимости настраивать ротацию логов вручную для каждого мелкого приложения. Чтобы посмотреть последние 50 строк логов с отметкой времени, используйте: journalctl -u collector.service -n 50 --no-pager

Prometheus и Grafana в Proxmox

Когда инфраструктура разрастается до нескольких узлов Proxmox, мониторинг одного сервера становится бесполезным. Вам нужна единая точка обзора. В моей связке за это отвечают Prometheus (сбор метрик) и Grafana (визуализация).

Для Proxmox я использую подход с экспортерами. Вместо того чтобы пытаться «взломать» гипервизор, мы ставим рядом маленькие агенты, которые отдают данные в понятном формате.

  1. Node Exporter — для метрик самого железа (CPU, RAM, диск, сеть).
  2. Proxmox Exporter — чтобы видеть состояние виртуальных машин и контейнеров.

Если вы разворачиваете это в Docker (что я рекомендую для изоляции инструментов мониторинга), ваш docker-compose.yml для базового стека будет выглядеть примерно так:

yaml
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'
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:

Конфигурация prometheus.yml — это сердце системы. Здесь вы описываете, откуда забирать данные. Важно не просто добавить адрес, но и правильно настроить интервалы (scrape interval). Если опрашивать сервер слишком часто, сам мониторинг создаст нагрузку; если слишком редко — пропустите всплеск потребления ресурсов.

Пример секции для сбора метрик с узла Proxmox:

yaml
scrape_configs:
  - job_name: 'proxmox-nodes'
    static_configs:
      - targets: ['192.168.1.10:9100'] # IP вашего Proxmox и порт Node Exporter
    scrape_interval: 15s

После настройки Grafana, не тратьте время на рисование графиков с нуля. Сообщество уже сделало это за вас. Для Node Exporter существуют готовые дашборды на Grafana Labs, которые можно просто импортировать по ID.

Для полноценного мониторинга домашней лаборатории я рекомендую настроить Alertmanager. Без алертов в Telegram или на почту ваш мониторинг — это просто красивая картинка, которую вы увидите только тогда, когда всё уже сломалось. Настройте алерты на критические события: заполнение диска более чем на 90%, недоступность узла Proxmox или перегрев процессора. Это и обеспечит ту самую предсказуемость, к которой стоит стремиться.

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

Как обеспечить воспроизводимость конфигурации при развёртывании сервисов?

С практической точки зрения воспроизводимость достигается за счёт отказа от ручных правок на работающих серверах. Используйте Docker Compose для описания зависимостей и версий образов, а конфигурационные файлы храните в системе контроля версий. Если вы настраиваете сервис через systemd, обязательно сохраняйте unit-файлы в репозитории, чтобы при развёртывании на новом оборудовании можно было просто скопировать их в /etc/systemd/system/.

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

Во-первых, не пытайтесь угадать ошибку, а используйте инструменты диагностики, описанные в статье. Сначала проверьте синтаксис конфигурации (например, nginx -t для Nginx) и изучите вывод journalctl -u <имя_сервиса>, чтобы увидеть конкретный код ошибки. Если ошибка не очевидна, сравните текущий файл с предыдущей рабочей версией с помощью команды diff, чтобы исключить случайные опечатки или лишние символы.

Зачем использовать изолированные контейнеры для отладки проблем на основном сервере?

Запуск контейнера с параметром --rm позволяет создать чистую среду, которая никак не затронет состояние вашей основной системы или домашней лаборатории. Это помогает проверить, является ли проблема следствием специфики образа или же она вызвана внешними факторами, такими как права доступа к файлам на хосте или сетевые настройки. Такой подход обеспечивает предсказуемость: вы точно знаете, что в контейнере нет лишних зависимостей, которые могли бы исказить результаты теста.

Как правильно собирать логи для анализа инцидентов?

Для эффективного анализа важно не просто смотреть последние строки, а понимать контекст события. Используйте journalctl с флагами временного диапазона, например --since "1 hour ago", чтобы сопоставить время падения сервиса с другими системными событиями. Если сервис работает в Docker, используйте docker logs <container_id>, но помните, что для полноценной диагностики лучше настроить централизованное хранение логов, чтобы они не исчезли вместе с удалённым контейнером.

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