Цели модуля

После модуля студент сможет:

  • Выбирать модель деплоя под задачу (Serverless, VDS, Kubernetes)
  • Проектировать single binary стратегию для Go-сервисов
  • Понимать различия между cron и timer triggers
  • Реализовывать graceful shutdown для long-running процессов
  • Применять стратегии rollout: canary, blue-green, rolling
  • Использовать feature flags для безопасных релизов
  • Понимать принципы GitOps

4.1 Модели деплоя: обзор

Три основные модели

МодельОписаниеКогда использовать
ServerlessФункции по запросуНестабильная нагрузка, малый трафик
VDSВиртуальный серверПростые сервисы, стабильная нагрузка
KubernetesОркестрация контейнеровСложные системы, автоскейлинг

Критерии выбора

  1. Характер нагрузки

    • Пиковая/нестабильная: Serverless
    • Стабильная: VDS
    • Растущая: Kubernetes
  2. Операционные возможности команды

    • Нет DevOps: Serverless или managed VDS
    • Есть DevOps: Kubernetes
  3. Стоимость

    • Малый трафик: Serverless
    • Средний трафик: VDS
    • Большой трафик: Kubernetes
  4. Требования к latency

    • Критична: VDS или Kubernetes (нет cold start)
    • Не критична: Serverless
  5. Сложность сервиса

    • Простой: Serverless
    • Средний: VDS
    • Сложный (много зависимостей): Kubernetes

Эволюция деплоя

Рекомендуемый путь:

Прототип -> Serverless -> VDS -> Kubernetes

Не начинайте с Kubernetes если нет реальной необходимости.


4.2 Serverless deployment model

Что такое Serverless

Serverless — модель где облачный провайдер управляет инфраструктурой. Код выполняется в функциях (FaaS) по запросу.

Примеры:

  • AWS Lambda
  • Yandex Cloud Functions
  • Google Cloud Functions
  • Azure Functions

Характеристики Serverless

  • Оплата за выполнение (per invocation)
  • Автоскейлинг от 0 до N
  • Cold start (задержка при первом вызове)
  • Ограничения по времени выполнения (обычно 15 минут)
  • Stateless по умолчанию
  • Managed инфраструктура

Архитектура Serverless сервиса

API Gateway
    |
    v
Cloud Function (api)
    |
    v
Cloud Function (git-gateway)
    |
    v
Timer Trigger (outbox-relay)
    |
    v
Timer Trigger (saga-orchestrator)
    |
    v
Timer Trigger (hmac-rotator)

Пример структуры для Yandex Cloud Functions

cmd/
├── api/main.go              # REST API функция
├── git-gateway/main.go      # Git Smart HTTP функция
├── outbox-relay/main.go     # Outbox relay (timer trigger)
├── saga-orchestrator/main.go # Saga orchestrator (timer trigger)
└── hmac-rotator/main.go     # HMAC rotator (timer trigger)

Terraform для Serverless

hcl
resource "yandex_function" "api" {
  name               = "api-function"
  user_hash          = data.archive_file.api.output_base64sha256
  runtime            = "golang123"
  entrypoint         = "Handle"
  memory             = "256"
  execution_timeout  = "30"
  service_account_id = yandex_iam_service_account.function_sa.id


  environment = {
    DATABASE_DSN = var.database_dsn
    LOG_LEVEL    = "info"
  }

  secrets {
    id                   = yandex_lockbox_secret_version.hmac_key.id
    version_id           = yandex_lockbox_secret_version.hmac_key.version_id
    key                  = "hmac_key"
    environment_variable = "HMAC_KEY"
  }
}

resource "yandex_function_trigger" "outbox_relay" {
  name = "outbox-relay-trigger"

  function {
    id = yandex_function.outbox_relay.id
  }

  timer {
    cron_expression = "*/5 * * * *"
  }
}

Плюсы Serverless

  • Нет управления инфраструктурой
  • Автоскейлинг от 0
  • Оплата только за использование
  • Быстрый старт
  • Встроенная отказоустойчивость

Минусы Serverless

  • Cold start (100ms-2s задержка)
  • Ограничения по времени выполнения
  • Сложность отладки
  • Vendor lock-in
  • Сложность для long-running процессов
  • Ограничения по памяти/CPU

Когда использовать Serverless

  • API с нестабильной нагрузкой
  • Обработка событий (webhooks, очереди)
  • Периодические задачи (cron)
  • Прототипы и MVP
  • Малый трафик

4.3 VDS deployment model

Что такое VDS

VDS (Virtual Dedicated Server) — виртуальный сервер с выделенными ресурсами. Полный контроль над окружением.

Характеристики VDS

  • Фиксированная оплата (месяц/год)
  • Полный контроль над ОС
  • Long-running процессы
  • Нет cold start
  • Нужна настройка и поддержка
  • Ручной или полуавтоматический скейлинг

Архитектура VDS сервиса

Nginx (reverse proxy)
    |
    v
git-server (HTTP server + workers)
    |
    ├── HTTP server (api + git-gateway)
    ├── Outbox relay worker
    ├── Saga orchestrator worker
    └── HMAC rotator worker

Пример структуры для VDS

cmd/
└── app/
    ├── main.go              # Entry point
    └── commands/
        ├── serve.go         # HTTP server
        ├── worker.go        # Background workers
        ├── migrate.go       # Миграции
        └── version.go

Systemd units

# /etc/systemd/system/git-server.service
[Unit]
Description=Git Server
After=network.target postgresql.service

[Service]
Type=simple
User=git-server
ExecStart=/usr/local/bin/git-server serve
Restart=always
RestartSec=5
LimitNOFILE=65536
Environment=APP_CONFIG_PATH=/etc/git-server/config.yaml

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/git-server-worker.service
[Unit]
Description=Git Server Worker
After=network.target postgresql.service

[Service]
Type=simple
User=git-server
ExecStart=/usr/local/bin/git-server worker
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Ansible playbook для деплоя

yaml
- name: Deploy git-server
  hosts: staging
  become: yes
  tasks:
    - name: Create app user
      user:
        name: git-server
        system: yes
        shell: /usr/sbin/nologin

    - name: Copy binary
      copy:
        src: bin/git-server
        dest: /usr/local/bin/git-server
        mode: "0755"
      notify: restart git-server

    - name: Copy config
      template:
        src: templates/config.yaml.j2
        dest: /etc/git-server/config.yaml
        mode: "0640"
      notify: restart git-server

    - name: Copy systemd units
      copy:
        src: systemd/git-server.service
        dest: /etc/systemd/system/
      notify:
        - reload systemd
        - restart git-server

    - name: Enable and start service
      systemd:
        name: git-server
        enabled: yes
        state: started

  handlers:
    - name: reload systemd
      systemd:
        daemon_reload: yes

    - name: restart git-server
      systemd:
        name: git-server
        state: restarted

Плюсы VDS

  • Полный контроль
  • Нет cold start
  • Long-running процессы
  • Предсказуемая стоимость
  • Простота для небольших сервисов
  • Легко отлаживать

Минусы VDS

  • Ручное управление инфраструктурой
  • Нет автоскейлинга
  • Нужно настраивать мониторинг
  • Single point of failure (без дополнительных мер)
  • Патчинг ОС и безопасности

Когда использовать VDS

  • Стабильная нагрузка
  • Простые сервисы
  • Long-running процессы
  • Ограниченный бюджет
  • Команда без DevOps экспертизы

4.4 Kubernetes deployment model

Что такое Kubernetes

Kubernetes — оркестратор контейнеров. Автоматизирует деплой, скейлинг, управление контейнеризированными приложениями.

Характеристики Kubernetes

  • Декларативная конфигурация
  • Автоскейлинг (HPA, VPA)
  • Self-healing (перезапуск подов)
  • Service discovery
  • Rolling updates
  • Rich экосистема

Архитектура Kubernetes сервиса

Ingress
    |
    v
Service
    |
    v
Deployment (replicas: 3)
    ├── Pod 1 (git-server)
    ├── Pod 2 (git-server)
    └── Pod 3 (git-server)
    |
    v
CronJob (outbox-relay)
CronJob (saga-orchestrator)

Пример Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: git-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: git-server
  template:
    metadata:
      labels:
        app: git-server
    spec:
      containers:
        - name: git-server
          image: registry.example.com/git-server:v1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: APP_DATABASE_DSN
              valueFrom:
                secretKeyRef:
                  name: git-server-secrets
                  key: database-dsn
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /readyz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5

Пример CronJob

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: outbox-relay
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: outbox-relay
              image: registry.example.com/git-server:v1.0.0
              command: ["/usr/local/bin/git-server", "worker", "--job=outbox-relay"]
          restartPolicy: OnFailure

Плюсы Kubernetes

  • Автоскейлинг
  • Self-healing
  • Rolling updates без downtime
  • Service discovery
  • Rich экосистема (Helm, Istio, ArgoCD)
  • Portability между облаками

Минусы Kubernetes

  • Высокая операционная сложность
  • Крутая кривая обучения
  • Требует DevOps экспертизы
  • Избыточен для простых сервисов
  • Стоимость (control plane, мониторинг)

Когда использовать Kubernetes

  • Много микросервисов
  • Нужен автоскейлинг
  • Есть DevOps команда
  • Сложные требования к деплою
  • Multi-cloud или hybrid

4.5 Single binary strategy

Что это

Весь сервис компилируется в один бинарник с subcommands.

bash
git-server serve      # HTTP server
git-server worker     # Background workers
git-server migrate    # Миграции
git-server fsck       # DR check
git-server version    # Версия

Реализация с cobra

go
// cmd/app/main.go
package main

import (
    "github.com/spf13/cobra"
)

func main() {
    rootCmd := &cobra.Command{
        Use:   "git-server",
        Short: "Git server application",
    }

    rootCmd.AddCommand(
        newServeCmd(),
        newWorkerCmd(),
        newMigrateCmd(),
        newFsckCmd(),
        newVersionCmd(),
    )

    if err := rootCmd.Execute(); err != nil {
        os.Exit(1)
    }
}

// cmd/app/commands/serve.go
func newServeCmd() *cobra.Command {
    return &cobra.Command{
        Use:   "serve",
        Short: "Start HTTP server",
        RunE:  runServe,
    }
}

func runServe(cmd *cobra.Command, args []string) error {
    ctx, stop := signal.NotifyContext(context.Background(),
        syscall.SIGINT, syscall.SIGTERM)
    defer stop()

    cfg, err := config.Load()
    if err != nil {
        return fmt.Errorf("load config: %w", err)
    }

    app, err := app.Build(ctx, cfg)
    if err != nil {
        return fmt.Errorf("build app: %w", err)
    }
    defer app.Close()

    return runHTTPServer(ctx, app, cfg)

}

Преимущества single binary

  1. Простота деплоя: один артефакт
  2. Версионирование: одна версия для всех компонентов
  3. Тестирование: один бинарник для всех сценариев
  4. Rollback: переключение на предыдущую версию
  5. Docker: один образ для всех ролей

Когда использовать

  • Один сервис с несколькими ролями (HTTP + workers)
  • Нужна простота деплоя
  • Малая или средняя команда

Когда НЕ использовать

  • Независимое масштабирование компонентов
  • Разные команды владеют разными компонентами
  • Разные требования к ресурсам

4.6 Cron vs timer triggers

Cron (VDS/Kubernetes)

Cron — планировщик задач по расписанию. Задачи выполняются периодически внутри процесса или через CronJob.

Примеры cron expressions:

* * * * * *      # каждую секунду
0 * * * * *      # каждую минуту
*/5 * * * * *    # каждые 5 минут
0 0 * * * *      # каждый час
0 0 0 * * *      # каждый день в полночь

Timer triggers (Serverless)

Timer trigger — облачный сервис который вызывает функцию по расписанию.

hcl
resource "yandex_function_trigger" "outbox_relay" {
  timer {
    cron_expression = "*/5 * * * *"
  }
}

Сравнение

КритерийCron (VDS/K8s)Timer Triggers (Serverless)
Где выполняетсяВ процессе / CronJobОблачная функция
Cold startНетДа
СостояниеМожет бытьStateless
МасштабированиеРучное / HPAАвтоматическое
СтоимостьФиксированнаяPer invocation
МониторингSelf-hostedManaged

Когда что использовать

Cron (VDS/K8s):

  • Long-running задачи
  • Нужен доступ к состоянию
  • Стабильная нагрузка

Timer triggers (Serverless):

  • Короткие периодические задачи
  • Нестабильная нагрузка
  • Нет инфраструктуры

Реализация cron в Go (robfig/cron)

go
import "github.com/robfig/cron/v3"

type Scheduler struct {
    cron *cron.Cron
}

func NewScheduler() *Scheduler {
    return &Scheduler{
        cron: cron.New(cron.WithSeconds()),
    }
}

func (s *Scheduler) Register(name, schedule string, job func()) error {
    _, err := s.cron.AddFunc(schedule, func() {
        log.Info("job started", "job", name)
        job()
        log.Info("job completed", "job", name)
    })
    return err
}

func (s *Scheduler) Start() {
    s.cron.Start()
}

func (s *Scheduler) Stop() {
    ctx := s.cron.Stop()
    <-ctx.Done()
}

4.7 Graceful shutdown (углублённо)

Что такое graceful shutdown

При получении сигнала завершения (SIGINT, SIGTERM) сервис должен:

  1. Перестать принимать новые запросы
  2. Дождаться завершения текущих запросов
  3. Закрыть соединения с БД
  4. Освободить ресурсы
  5. Завершиться чисто

Почему это важно

Без graceful shutdown:

  • Активные запросы обрываются
  • Транзакции остаются открытыми
  • События теряются
  • Клиенты получают ошибки

Реализация для HTTP сервера

go
func runHTTPServer(ctx context.Context, app *App, cfg *Config) error {
    srv := &http.Server{
        Addr:         cfg.HTTPAddr,
        Handler:      app.Router,
        ReadTimeout:  cfg.ReadTimeout,
        WriteTimeout: cfg.WriteTimeout,
        IdleTimeout:  cfg.IdleTimeout,
    }

    // Запуск в горутине
    go func() {
        log.Info("server starting", "addr", cfg.HTTPAddr)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Error("server error", "error", err)
        }
    }()

    // Ждём сигнала
    <-ctx.Done()
    log.Info("shutdown signal received")

    // Graceful shutdown с таймаутом
    shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout)
    defer cancel()

    if err := srv.Shutdown(shutdownCtx); err != nil {
        log.Error("graceful shutdown failed, forcing close", "error", err)
        return srv.Close()
    }

    log.Info("server stopped gracefully")
    return nil
}

Graceful shutdown для workers

go
func runWorker(ctx context.Context, app *App, cfg *Config) error {
    worker := app.Worker

    // Запуск в горутине
    go func() {
        log.Info("worker starting")
        worker.Run(ctx)
    }()

    // Ждём сигнала
    <-ctx.Done()
    log.Info("shutdown signal received")

    // Graceful shutdown с таймаутом
    shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout)
    defer cancel()

    if err := worker.Shutdown(shutdownCtx); err != nil {
        log.Error("worker shutdown failed", "error", err)
        return err
    }

    log.Info("worker stopped gracefully")
    return nil
}

Drain connections

При shutdown нужно дождаться завершения активных запросов:

go
func (s *Server) Shutdown(ctx context.Context) error {
    // 1. Закрываем listener (не принимаем новые соединения)
    s.listener.Close()

    // 2. Ждём завершения активных запросов
    s.waitGroup.Wait()

    // 3. Закрываем idle соединения
    s.closeIdleConnections()

    return nil
}

Systemd и graceful shutdown

Systemd по умолчанию отправляет SIGTERM и ждёт TimeoutStopSec.

[Service]
ExecStart=/usr/local/bin/git-server serve
KillSignal=SIGTERM
TimeoutStopSec=30

Если сервис не завершился за 30 секунд — SIGKILL.

Kubernetes и graceful shutdown

Kubernetes отправляет SIGTERM при удалении пода.

terminationGracePeriodSeconds: 30

PreStop hook для drain:

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 5"]

Это даёт time для service discovery обновления.


4.8 Стратегии rollout

Rolling update

Постепенная замена подов/инстансов.

Deployment:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1

Плюсы:

  • Без downtime
  • Простота

Минусы:

  • Смешанные версии во время rollout
  • Нет быстрого rollback

Blue-Green

Две идентичные среды: blue (текущая) и green (новая). Переключение трафика мгновенно.

Blue (v1.0)  <- трафик
Green (v1.1) <- standby

Переключение:
Blue (v1.0)  <- standby
Green (v1.1) <- трафик

Плюсы:

  • Мгновенный rollback
  • Нет смешанных версий
  • Полное тестирование перед переключением

Минусы:

  • Двойные ресурсы
  • Сложность с stateful данными

Canary

Постепенное увеличение трафика на новую версию.

Шаг 1: 5% трафика на v1.1, 95% на v1.0
Шаг 2: 25% трафика на v1.1, 75% на v1.0
Шаг 3: 50% трафика на v1.1, 50% на v1.0
Шаг 4: 100% трафика на v1.1

Плюсы:

  • Минимальный blast radius
  • Мониторинг на каждом шаге
  • Быстрый rollback

Минусы:

  • Сложность настройки
  • Долгий rollout

Сравнение стратегий

СтратегияDowntimeRollbackСложностьКогда
RollingНетМедленныйНизкаяПростые сервисы
Blue-GreenНетМгновенныйСредняяКритичные сервисы
CanaryНетБыстрыйВысокаяВысокий риск

Реализация canary в Kubernetes

Через Istio или Argo Rollouts:

yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: git-server
spec:
  replicas: 3
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 5m }
        - setWeight: 25
        - pause: { duration: 5m }
        - setWeight: 50
        - pause: { duration: 5m }
        - setWeight: 100

4.9 Feature flags

Что это

Feature flags позволяют включать/выключать функциональность без деплоя.

Зачем нужны

  • Безопасные релизы (постепенное включение)
  • A/B тестирование
  • Экстренное отключение фичи
  • Тестирование в production

Типы feature flags

ТипНазначениеПример
ReleaseПостепенный rolloutНовая фича для 10% пользователей
ExperimentA/B тестированиеДва варианта UI
OpsЭкстренное отключениеKill switch
PermissionДоступ по ролямPremium фичи

Реализация в Go

go
type FeatureFlags struct {
    mu    sync.RWMutex
    flags map[string]bool
}

func NewFeatureFlags() *FeatureFlags {
    return &FeatureFlags{
        flags: make(map[string]bool),
    }
}

func (f *FeatureFlags) IsEnabled(name string) bool {
    f.mu.RLock()
    defer f.mu.RUnlock()
    return f.flags[name]
}

func (f *FeatureFlags) Set(name string, enabled bool) {
    f.mu.Lock()
    defer f.mu.Unlock()
    f.flags[name] = enabled
}

Использование

go
func (h *Handler) CreateRepo(w http.ResponseWriter, r *http.Request) {
    if h.featureFlags.IsEnabled("new_repo_validation") {
        // Новая валидация
        if err := validateV2(req); err != nil {
            h.writeError(w, err)
            return
        }
    } else {
        // Старая валидация
        if err := validateV1(req); err != nil {
            h.writeError(w, err)
            return
        }
    }

    // Остальная логика
}

Управление feature flags

Для production используйте специализированные сервисы:

  • LaunchDarkly
  • Unleash
  • Flagsmith
  • ConfigCat

Или храните в конфиге с hot reload:

yaml
# config.yaml
features:
  new_repo_validation: true
  advanced_search: false
  webhook_v2: true

4.10 GitOps

Что это

GitOps — подход где желаемое состояние системы описано в git. Оператор (ArgoCD, Flux) синхронизирует кластер с git.

Принципы GitOps

  1. Declarative: состояние описано декларативно
  2. Versioned: состояние в git (версионировано)
  3. Pulled: оператор pull'ит изменения из git
  4. Reconciled: оператор continuously reconciles состояние

ArgoCD пример

yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: git-server
spec:
  project: default
  source:
    repoURL: https://github.com/org/git-server
    targetRevision: main
    path: deploy/kubernetes
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Процесс деплоя через GitOps

  1. Разработчик мержит PR в main
  2. CI собирает образ и пушит в registry
  3. CI обновляет manifest в git (image tag)
  4. ArgoCD видит изменение
  5. ArgoCD синхронизирует кластер с git
  6. Кластер обновляется

Плюсы GitOps

  • Аудит: все изменения в git
  • Rollback: git revert
  • Consistency: кластер всегда соответствует git
  • Automation: нет ручных kubectl apply

Минусы GitOps

  • Требует Kubernetes
  • Кривая обучения
  • Избыточен для простых деплоев

Привязка к шаблону проекта

Где deployment в шаблоне

deploy/
├── terraform/
│   ├── main.tf
│   ├── vpc.tf
│   ├── compute.tf           # VDS instance
│   ├── disk.tf
│   ├── iam.tf
│   ├── lockbox.tf
│   ├── storage.tf
│   ├── backups.tf
│   ├── monitoring.tf
│   ├── alerts.tf
│   ├── outputs.tf
│   └── variables.tf
├── ansible/
│   ├── playbook.yml
│   ├── inventory.yml
│   ├── roles/
│   │   ├── app/
│   │   ├── nginx/
│   │   └── monitoring/
│   └── group_vars/
└── cloud-init/
    └── bootstrap.sh

configs/systemd/
├── git-server.service
└── git-server-worker.service

Что изучаем на примере шаблона

  1. Terraform для VDS инфраструктуры
  2. Ansible для конфигурации
  3. Systemd units для сервисов
  4. Single binary strategy
  5. Graceful shutdown в коде

Задание: изучите deploy/

  • Какая инфраструктура описана в Terraform?
  • Что делает Ansible playbook?
  • Какие systemd units?
  • Как настроен graceful shutdown?

💱 Компромиссы

  • Serverless vs VDS: функции масштабируются от нуля и платятся по вызовам, но cold start даёт 100ms-2s и лимит времени выполнения; у VDS нет cold start и есть long-running процессы, но оплата фиксированная, а автоскейлинга нет.
  • Kubernetes vs VDS: автоскейлинг, self-healing и rolling updates без downtime, но операционная сложность и нужен DevOps; VDS прост и предсказуем по стоимости, но скейлинг ручной, а сервер без репликации — single point of failure.
  • Rolling update vs Blue-Green: rolling не удваивает ресурсы, но во время rollout сосуществуют смешанные версии и откат медленный; blue-green переключает трафик мгновенно, но до полного переключения платятся двойные ресурсы.
  • Cron vs timer triggers: cron через robfig/cron или CronJob живёт в вашем процессе без cold start и с состоянием, но инфраструктура на вас; timer trigger управляется провайдером и платится per invocation, но каждый запуск — это cold start, а функция stateless.
  • Single binary vs раздельные артефакты: один бинарник с subcommands (serve, worker, migrate) даёт один артефакт, одну версию и простой rollback, но роли нельзя масштабировать по отдельности; раздельные бинарники масштабируют каждую роль под её нагрузку, зато артефактов и версий для отслеживания больше.

Практическое задание

Задание 1: Single binary

Реализуйте single binary с subcommands:

bash
git-server serve
git-server worker
git-server migrate
git-server version

Используйте cobra для CLI.

Задание 2: Graceful shutdown

Реализуйте graceful shutdown для HTTP сервера:

go
func runHTTPServer(ctx context.Context, app *App, cfg *Config) error

С:

  • Signal handling (SIGINT, SIGTERM)
  • Drain connections
  • Timeout на shutdown
  • Логирование

Задание 3: Systemd unit

Создайте systemd unit для вашего сервиса:

[Unit]
Description=My Service
After=network.target postgresql.service

[Service]
Type=simple
User=app
ExecStart=/usr/local/bin/myapp serve
Restart=always
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target

Задание 4: Ansible playbook

Напишите Ansible playbook для деплоя:

yaml
- name: Deploy myapp
  hosts: staging
  become: yes
  tasks:
    - name: Copy binary
    - name: Copy config
    - name: Copy systemd unit
    - name: Enable and start service

Задание 5: Feature flags

Реализуйте feature flags middleware:

go
func FeatureFlags(flags *FeatureFlags) func(http.Handler) http.Handler

Используйте для постепенного rollout новой фичи.

Задание 6: Canary deployment

Напишите Kubernetes manifest для canary deployment через Argo Rollouts:

yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 5m }
        - setWeight: 25
        - pause: { duration: 5m }
        - setWeight: 100

Задание 7: Сравнение моделей деплоя

Для вашего проекта напишите ADR:

# ADR-002: Выбор модели деплоя

## Статус
Accepted

## Контекст
- Характер нагрузки: стабильная, 100 RPS
- Команда: 3 человека, без DevOps
- Бюджет: ограниченный

## Решение
Используем VDS потому что...

## Альтернативы
- Serverless: cold start критичен
- Kubernetes: избыточно для 3 человек

## Последствия
- Простота деплоя
- Предсказуемая стоимость
- Нет автоскейлинга

Quiz "Проверь себя"

1. Что делает сервис при graceful shutdown по SIGTERM?
2. Чем timer trigger отличается от cron в VDS/Kubernetes?
3. Что такое GitOps?
4. Какую модель деплоя выбрать для стабильной нагрузки и простой команды без DevOps?
5. Зачем нужны feature flags? — выберите все подходящие
6. Чем canary deployment отличается от blue-green?
7. Что такое single binary strategy?
8. Какие стратегии rollout существуют? — выберите все подходящие

✅ Итоги модуля

Студент должен:

  • Выбирать модель деплоя под задачу
  • Проектировать single binary стратегию
  • Понимать cron vs timer triggers
  • Реализовывать graceful shutdown
  • Применять стратегии rollout (canary, blue-green)
  • Использовать feature flags
  • Понимать принципы GitOps
  • Ответить на все вопросы Quiz

🔗 Мостик к Модулю 5

Следующий модуль: Модуль 5. Observability at Scale - metrics design (RED, USE), high-cardinality labels, tracing in distributed systems, log correlation, alerting strategy, SLI/SLO/SLA, error budgets. Привязка к internal/observability/ шаблона.