Цели модуля
После модуля студент сможет:
- Проектировать CI/CD pipeline для Go-сервисов
- Настраивать test pipeline с unit, integration, e2e тестами
- Интегрировать security scanning в CI
- Управлять build artifacts и версионированием
- Контейнеризировать сервисы для production
- Автоматизировать инфраструктуру через Terraform
- Управлять конфигурацией через Ansible
- Реализовывать deployment automation
- Понимать принципы GitOps
10.1 CI/CD pipeline design
Что такое CI/CD
CI (Continuous Integration):
- Разработчики часто мержат код в main
- Каждый merge запускает автоматические проверки
- Проблемы обнаруживаются рано
CD (Continuous Delivery / Deployment):
- Delivery: код всегда готов к деплою, деплой вручную
- Deployment: каждый merge автоматически деплоится
Принципы хорошего pipeline
-
Быстрая обратная связь
- Unit тесты: < 5 минут
- Линтеры: < 2 минуты
- Полные тесты: < 15 минут
-
Fail fast
- Сначала быстрые проверки (lint, unit)
- Потом долгие (integration, e2e)
- Если быстрая проверка упала — долгие не запускаем
-
Детерминизм
- Одинаковый результат при одинаковом коде
- Фиксированные версии зависимостей
- Изолированное окружение
-
Кэширование
- Go modules cache
- Docker layers cache
- Test results cache
Этапы pipeline
textCommit -> Lint -> Unit Tests -> Build -> Integration Tests -> Security Scan -> Docker Build -> Push to Registry -> Deploy Staging -> E2E Tests -> Approval -> Deploy Production
Пример GitHub Actions workflow
yamlname: CI/CD on: push: branches: [main] pull_request: branches: [main] env: GO_VERSION: "1.23" REGISTRY: registry.example.com IMAGE_NAME: git-server jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: golangci-lint uses: golangci/golangci-lint-action@v4 with: version: latest test: runs-on: ubuntu-latest needs: lint services: postgres: image: postgres:16-alpine env: POSTGRES_USER: test POSTGRES_PASSWORD: test POSTGRES_DB: testdb ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} - name: Run unit tests run: go test -race -coverprofile=coverage.out ./... - name: Run integration tests run: go test -tags=integration ./... env: TEST_DATABASE_DSN: postgres://test:test@localhost:5432/testdb?sslmode=disable - name: Upload coverage uses: codecov/codecov-action@v4 with: file: coverage.out build: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v4 - name: Build binary run: | CGO_ENABLED=0 GOOS=linux go build \ -ldflags="-w -s -X main.version=${{ github.sha }}" \ -o bin/git-server ./cmd/app - name: Upload artifact uses: actions/upload-artifact@v4 with: name: git-server path: bin/git-server
Разделение workflows
Для разных событий — разные workflows:
.github/workflows/
├── ci.yml # Lint, test, build (на каждый PR)
├── security.yml # Security scanning (ежедневно + на PR)
├── deploy-staging.yml # Деплой в staging (на merge в main)
├── deploy-prod.yml # Деплой в production (по тегу)
├── dr-test.yml # DR тесты (еженедельно)
└── e2e.yml # E2E тесты (на staging)
10.2 Test pipeline
Пирамида тестирования
/ E2E Tests \ <- Мало, медленные
/-------------------\
/ Integration Tests \ <- Средне
/-----------------------\
/ Unit Tests \ <- Много, быстрые
| Тип | Скорость | Покрытие | Зависимости |
|---|---|---|---|
| Unit | Миллисекунды | Функции, методы | Mock'и |
| Integration | Секунды | Взаимодействие компонентов | Тестовая БД |
| E2E | Минуты | Полные сценарии | Реальное окружение |
Unit tests в CI
yaml- name: Run unit tests run: go test -race -cover ./internal/...
Флаги:
- -race: race detector
- -cover: покрытие кода
- -short: пропускать долгие тесты
Integration tests с testcontainers
go// internal/adapters/postgres/repo_repository_integration_test.go //go:build integration func TestRepoRepository_Integration(t *testing.T) { ctx := context.Background() // Запуск PostgreSQL в Docker container, err := postgres.Run(ctx, "postgres:16-alpine", postgres.WithDatabase("testdb"), postgres.WithUsername("test"), postgres.WithPassword("test"), ) require.NoError(t, err) defer container.Terminate(ctx) dsn, err := container.ConnectionString(ctx) require.NoError(t, err) pool, err := pgxpool.New(ctx, dsn) require.NoError(t, err) defer pool.Close() // Применение миграций db := stdlib.OpenDBFromPool(pool) goose.SetBaseFS(migrations.FS) goose.Up(db, ".") // Тесты repo := postgres.NewRepoRepository(pool) // ... }
Запуск в CI:
yaml- name: Run integration tests run: go test -tags=integration -race ./internal/adapters/...
E2E tests
go// cmd/e2e/main.go func main() { baseURL := os.Getenv("E2E_BASE_URL") // Тест: создание репозитория resp := createRepo(baseURL, CreateRepoRequest{ Name: "e2e-test-repo", OwnerID: "user-123", }) assertStatus(resp, 201) // Тест: получение репозитория repo := getRepo(baseURL, resp.ID) assertEqual(repo.Name, "e2e-test-repo") // Тест: удаление репозитория deleteRepo(baseURL, resp.ID) assertStatus(resp, 204) }
Запуск в CI после деплоя в staging:
yaml- name: Run E2E tests run: go run ./cmd/e2e env: E2E_BASE_URL: https://staging.example.com
Test strategy для API
| Что тестируем | Тип теста | Где |
|---|---|---|
| Domain models | Unit | Всегда |
| Services | Unit с mocks | Всегда |
| Use cases | Unit с mocks | Всегда |
| Handlers | Integration с httptest | Всегда |
| Repositories | Integration с testcontainers | На PR |
| Полный API | E2E | После деплоя в staging |
10.3 Security scanning в CI
Инструменты security scanning
| Инструмент | Что проверяет |
|---|---|
| gosec | Статический анализ Go кода |
| govulncheck | Известные уязвимости в зависимостях |
| Trivy | Уязвимости в Docker образах и файловой системе |
| Semgrep | Кастомные правила безопасности |
| gitleaks | Секреты в git истории |
gosec: статический анализ
bash# Установка go install github.com/securego/gosec/v2/cmd/gosec@latest # Запуск gosec ./...
Пример вывода:
[gosec] G101: Potential hardcoded credentials
--> internal/config/config.go:25
const defaultPassword = "secret123"
govulncheck: уязвимости в зависимостях
bash# Установка go install golang.org/x/vuln/cmd/govulncheck@latest # Запуск govulncheck ./...
Пример вывода:
Vulnerability #1: GO-2024-0001
Found in: golang.org/x/net@v0.1.0
Fixed in: golang.org/x/net@v0.7.0
HTTP/2 rapid reset attack
Trivy: сканирование Docker образов
bash# Сканирование образа trivy image --severity HIGH,CRITICAL myapp:latest # Сканирование файловой системы trivy fs --security-checks vuln ./ # Фейл при наличии CRITICAL trivy image --exit-code 1 --severity CRITICAL myapp:latest
gitleaks: секреты в git
bash# Установка brew install gitleaks # Запуск gitleaks detect --source . -v
Находит:
- Захардкоженные пароли
- API ключи
- Приватные ключи
- Токены
Security workflow в GitHub Actions
yamlname: Security on: push: branches: [main] pull_request: schedule: - cron: '0 2 * * *' # Ежедневно в 2:00 jobs: gosec: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: "1.23" - name: Run gosec run: gosec ./... govulncheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: "1.23" - name: Run govulncheck run: | go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck ./... trivy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build image run: docker build -t myapp:${{ github.sha }} . - name: Scan with Trivy uses: aquasecurity/trivy-action@master with: image-ref: myapp:${{ github.sha }} format: table exit-code: 1 severity: CRITICAL,HIGH gitleaks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Run gitleaks uses: gitleaks/gitleaks-action@v2
Policy для security findings
| Severity | Действие |
|---|---|
| CRITICAL | Блокирует merge/deploy, фикс немедленно |
| HIGH | Блокирует merge, фикс в текущем спринте |
| MEDIUM | Фикс в следующем спринте |
| LOW | По возможности |
10.4 Build artifacts и versioning
Что такое build artifact
Артефакт сборки — результат CI который можно задеплоить:
- Бинарник (для VDS)
- Docker образ (для Kubernetes)
- Пакет (deb, rpm)
Версионирование
Semantic Versioning (SemVer):
MAJOR.MINOR.PATCH
| | |
| | +-- Bug fixes (backward compatible)
| +-------- New features (backward compatible)
+--------------- Breaking changes
Примеры:
- 1.0.0: первый релиз
- 1.1.0: добавлена новая фича
- 1.1.1: исправлен баг
- 2.0.0: breaking change
Version injection при сборке
bash# Inject version через ldflags VERSION=$(git describe --tags --always) COMMIT=$(git rev-parse HEAD) BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ) go build -ldflags=" -X main.version=$VERSION -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME " -o bin/git-server ./cmd/app
В коде:
gopackage main var ( version = "dev" commit = "unknown" buildTime = "unknown" ) func main() { if len(os.Args) > 1 && os.Args[1] == "version" { fmt.Printf("version: %s\n", version) fmt.Printf("commit: %s\n", commit) fmt.Printf("build time: %s\n", buildTime) return } // ... }
Docker image tagging
bash# Build с тегами docker build -t $REGISTRY/$IMAGE_NAME:$VERSION . docker build -t $REGISTRY/$IMAGE_NAME:$COMMIT . docker build -t $REGISTRY/$IMAGE_NAME:latest . # Push всех тегов docker push $REGISTRY/$IMAGE_NAME:$VERSION docker push $REGISTRY/$IMAGE_NAME:$COMMIT docker push $REGISTRY/$IMAGE_NAME:latest
Правила:
- Каждый образ имеет уникальный тег (commit SHA или version)
- latest всегда указывает на последнюю стабильную версию
- Никогда не перезаписываем существующие теги
Artifact storage
Для бинарников:
- GitHub Releases
- S3 bucket
- Artifact registry (Nexus, Artifactory)
Для Docker образов:
- Docker Hub
- GitHub Container Registry
- AWS ECR, Yandex Container Registry
10.5 Containerization для production
Production-ready Dockerfile
dockerfile# Stage 1: Builder FROM golang:1.23-alpine AS builder RUN apk add --no-cache git ca-certificates WORKDIR /app # Кэширование зависимостей COPY go.mod go.sum ./ RUN go mod download # Копирование исходников COPY . . # Сборка с version injection ARG VERSION=dev ARG COMMIT=unknown RUN CGO_ENABLED=0 GOOS=linux go build \ -ldflags="-w -s -X main.version=$VERSION -X main.commit=$COMMIT" \ -o /app/server \ ./cmd/app # Stage 2: Runtime FROM alpine:3.19 RUN apk add --no-cache ca-certificates tzdata # Non-root пользователь RUN adduser -D -u 1000 appuser # Копирование бинарника и конфигов COPY /app/server /usr/local/bin/server COPY /app/configs /etc/app USER appuser EXPOSE 8080 HEALTHCHECK \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/healthz || exit 1 ENTRYPOINT ["server"] CMD ["serve"]
Multi-stage для разных целей
dockerfile# Development: с отладчиком FROM builder AS dev RUN go install github.com/go-delve/delve/cmd/dlv@latest CMD ["dlv", "exec", "/app/server", "--", "serve"] # Production: минимальный FROM alpine:3.19 AS prod COPY /app/server /usr/local/bin/server CMD ["server", "serve"]
Security best practices
-
Минимальный базовый образ: alpine или distroless
-
Non-root пользователь
-
Фиксированные версии образов
-
.dockerignore для исключения секретов
-
Сканирование через Trivy
-
Подпись образов через cosign
Подпись образа
cosign sign --key cosign.key $REGISTRY/$IMAGE_NAME:$VERSION
Проверка подписи
cosign verify --key cosign.pub $REGISTRY/$IMAGE_NAME:$VERSION
10.6 Infrastructure as Code (Terraform)
Что такое IaC
Infrastructure as Code — описание инфраструктуры в коде:
- Воспроизводимость: одинаковая инфраструктура везде
- Версионирование: изменения в git
- Автоматизация: нет ручных операций
- Документация: код = документация
Структура Terraform проекта
deploy/terraform/
├── main.tf # Provider и базовые настройки
├── vpc.tf # Сеть
├── compute.tf # VDS instances
├── database.tf # PostgreSQL
├── storage.tf # Object storage
├── iam.tf # Service accounts
├── monitoring.tf # Мониторинг
├── outputs.tf # Outputs
└── variables.tf # Variables
Пример: VPC и сеть
hcl# vpc.tf resource "yandex_vpc_network" "main" { name = "main-network" } resource "yandex_vpc_subnet" "app" { name = "app-subnet" zone = "ru-central1-a" network_id = yandex_vpc_network.main.id v4_cidr_blocks = ["10.0.1.0/24"] } resource "yandex_vpc_subnet" "db" { name = "db-subnet" zone = "ru-central1-a" network_id = yandex_vpc_network.main.id v4_cidr_blocks = ["10.0.2.0/24"] }
Пример: VDS instance
hcl# compute.tf resource "yandex_compute_instance" "app" { name = "app-server" zone = "ru-central1-a" resources { cores = 2 memory = 4 } boot_disk { initialize_param { image_id = "fd81234567890" # Ubuntu 22.04 size = 20 } } network_interface { subnet_id = yandex_vpc_subnet.app.id nat = true } metadata = { user-data = file("${path.module}/cloud-init/bootstrap.sh") } labels = { environment = var.environment service = "git-server" } }
Пример: PostgreSQL
hcl# database.tf resource "yandex_mdb_postgresql_cluster" "main" { name = "main-postgres" environment = "PRODUCTION" network_id = yandex_vpc_network.main.id resources { resource_preset_id = "s2.micro" disk_type_id = "network-ssd" disk_size = 20 } database { name = "gitdb" owner = "app" } user { name = "app" password = var.db_password } host { zone = "ru-central1-a" subnet_id = yandex_vpc_subnet.db.id } }
Variables и outputs
hcl# variables.tf variable "environment" { description = "Environment name" type = string default = "staging" } variable "db_password" { description = "Database password" type = string sensitive = true } # outputs.tf output "instance_ip" { description = "App server IP" value = yandex_compute_instance.app.network_interface[0].nat_ip_address } output "db_connection_string" { description = "PostgreSQL connection string" value = yandex_mdb_postgresql_cluster.main.connection_string sensitive = true }
Terraform workflow
bash# Инициализация terraform init # План изменений terraform plan # Применение terraform apply # Удаление (осторожно!) terraform destroy
Remote state
Для командной работы используйте remote state:
hclterraform { backend "s3" { bucket = "terraform-state" key = "git-server/terraform.tfstate" region = "ru-central1" } }
Или для Yandex Cloud:
hclterraform { backend "s3" { endpoint = "storage.yandexcloud.net" bucket = "terraform-state" key = "git-server/terraform.tfstate" region = "ru-central1" } }
Drift detection
Drift — расхождение между кодом и реальной инфраструктурой.
bash# Проверка drift terraform plan -detailed-exitcode # Exit code 2 = есть изменения (drift)
Автоматическая проверка в CI:
yaml- name: Check for drift run: | terraform plan -detailed-exitcode if [ $? -eq 2 ]; then echo "Drift detected!" exit 1 fi
10.7 Configuration management (Ansible)
Что такое Ansible
Ansible — инструмент управления конфигурацией:
- Установка пакетов
- Настройка сервисов
- Деплой приложений
- Управление systemd units
Структура Ansible проекта
deploy/ansible/
├── playbook.yml
├── inventory.yml
├── roles/
│ ├── app/
│ │ ├── tasks/main.yml
│ │ ├── templates/
│ │ │ ├── app.service.j2
│ │ │ ├── config.yaml.j2
│ │ └── handlers/main.yml
│ ├── nginx/
│ │ ├── tasks/main.yml
│ │ └── templates/
│ │ └── site.conf.j2
│ └── monitoring/
│ ├── tasks/main.yml
│ └── templates/
│ └── prometheus.yml.j2
└── group_vars/
└── all.yml
Inventory
yaml# inventory.yml all: children: staging: hosts: staging-server: ansible_host: 10.0.1.10 ansible_user: deploy production: hosts: prod-server-1: ansible_host: 10.0.1.20 ansible_user: deploy prod-server-2: ansible_host: 10.0.1.21 ansible_user: deploy
Playbook
yaml# playbook.yml - name: Deploy git-server hosts: staging become: yes vars: app_version: "{{ lookup('env', 'APP_VERSION') }}" registry: registry.example.com roles: - app - nginx - monitoring ### Role: app ```yaml # roles/app/tasks/main.yml - name: Create app user user: name: git-server system: yes shell: /usr/sbin/nologin - name: Create app directory file: path: /opt/git-server state: directory owner: git-server group: git-server - name: Pull Docker image docker_image: name: "{{ registry }}/git-server:{{ app_version }}" source: pull - name: Copy config template: src: config.yaml.j2 dest: /etc/git-server/config.yaml owner: git-server mode: "0640" notify: restart git-server - name: Copy systemd unit template: src: app.service.j2 dest: /etc/systemd/system/git-server.service notify: - reload systemd - restart git-server - name: Enable and start service systemd: name: git-server enabled: yes state: started
yaml# roles/app/handlers/main.yml - name: reload systemd systemd: daemon_reload: yes - name: restart git-server systemd: name: git-server state: restarted
Templates
j2# roles/app/templates/app.service.j2 [Unit] Description=Git Server After=network.target [Service] Type=simple User=git-server ExecStart=/usr/bin/docker run --rm \ --name git-server \ -p 8080:8080 \ -v /etc/git-server:/etc/app:ro \ {{ registry }}/git-server:{{ app_version }} serve Restart=always RestartSec=5 [Install] WantedBy=multi-user.target # roles/app/templates/config.yaml.j2 server: http_addr: ":8080" database: dsn: "{{ database_dsn }}" max_conns: 25 logging: level: "{{ log_level | default('info') }}" format: json
Запуск Ansible
bash# Деплой в staging ansible-playbook -i inventory.yml playbook.yml --limit staging # Деплой в production ansible-playbook -i inventory.yml playbook.yml --limit production # С проверкой (dry run) ansible-playbook -i inventory.yml playbook.yml --check --diff
10.8 Deployment strategies automation
Rolling deployment
yaml# GitHub Actions: deploy to staging - name: Deploy to staging run: | ansible-playbook -i inventory.yml playbook.yml --limit staging - name: Wait for health check run: | for i in {1..30}; do if curl -f https://staging.example.com/healthz; then echo "Service is healthy" exit 0 fi sleep 10 done echo "Service failed to start" exit 1
Canary deployment
yaml# Шаг 1: Деплой на 1 инстанс - name: Deploy canary run: | ansible-playbook -i inventory.yml playbook.yml \ --limit prod-server-1 \ -e app_version=$NEW_VERSION # Шаг 2: Мониторинг 15 минут - name: Monitor canary run: | sleep 900 # Проверяем error rate ERROR_RATE=$(curl -s https://prometheus.example.com/api/query?query=... | jq -r '.data.result[0].value[1]') if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then echo "Error rate too high, rolling back" exit 1 fi # Шаг 3: Деплой на все инстансы - name: Deploy to all run: | ansible-playbook -i inventory.yml playbook.yml \ --limit production \ -e app_version=$NEW_VERSION
Blue-Green deployment
yaml# Blue = текущая версия, Green = новая версия # Шаг 1: Деплой Green - name: Deploy green run: | ansible-playbook -i inventory.yml playbook.yml \ --limit green \ -e app_version=$NEW_VERSION # Шаг 2: Health check Green - name: Health check green run: | curl -f https://green.example.com/healthz # Шаг 3: Переключение трафика - name: Switch traffic run: | # Обновляем load balancer ansible-playbook switch-traffic.yml -e target=green # Шаг 4: Откат если проблемы - name: Rollback if needed if: failure() run: | ansible-playbook switch-traffic.yml -e target=blue
Automated rollback
yaml# Rollback при неудачном health check - name: Deploy new version run: | ansible-playbook -i inventory.yml playbook.yml \ --limit staging \ -e app_version=$NEW_VERSION continue-on-error: true id: deploy - name: Health check if: steps.deploy.outcome == 'success' run: | curl -f https://staging.example.com/healthz id: healthcheck - name: Rollback on failure if: steps.healthcheck.outcome == 'failure' run: | ansible-playbook -i inventory.yml playbook.yml \ --limit staging \ -e app_version=$PREVIOUS_VERSION
10.9 GitOps
Принципы GitOps
- Declarative: желаемое состояние в git
- Versioned: git history = audit log
- Pulled: оператор pull'ит изменения
- Reconciled: continuous reconciliation
ArgoCD для Kubernetes
yamlapiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: git-server namespace: argocd 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 syncOptions: - CreateNamespace=true
Flux для Kubernetes
yamlapiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository metadata: name: git-server namespace: flux-system spec: interval: 1m url: https://github.com/org/git-server ref: branch: main apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: git-server namespace: flux-system spec: interval: 5m path: ./deploy/kubernetes prune: true sourceRef: kind: GitRepository name: git-server
GitOps workflow
1. Разработчик мержит PR в main
2. CI собирает образ и пушит в registry
3. CI обновляет image tag в git (deploy/kubernetes/deployment.yaml)
4. ArgoCD/Flux видит изменение в git
5. ArgoCD/Flux синхронизирует кластер с git
6. Кластер обновляется автоматически
Преимущества GitOps
- Аудит: все изменения в git
- Rollback: git revert
- Consistency: кластер всегда соответствует git
- Automation: нет ручных kubectl apply
- Self-healing: оператор восстанавливает drift
Привязка к шаблону проекта
Где CI/CD в шаблоне
.github/workflows/
├── test.yml # go test -race
├── security.yml # govulncheck + Trivy
├── deploy.yml # terraform apply + drift detection
├── dr-test.yml # DR тест с git fsck
└── e2e.yml # E2E тесты
deploy/terraform/
├── main.tf
├── compute.tf
├── database.tf
├── storage.tf
└── ...
deploy/ansible/
├── playbook.yml
├── inventory.yml
└── roles/
├── app/
├── nginx/
└── monitoring/
docker/
├── Dockerfile
├── docker-compose.yml
└── docker-compose.prod.yml
Что изучаем на примере шаблона
- GitHub Actions workflows для CI/CD
- Terraform для инфраструктуры
- Ansible для конфигурации
- Multi-stage Dockerfile
- Health checks и readiness probes
Задание: изучите .github/workflows/ и deploy/
- Какие workflows существуют?
- Что делает каждый workflow?
- Как настроен Terraform?
- Какие роли в Ansible?
💱 Компромиссы
- Continuous Delivery vs Continuous Deployment: ручной approval перед production даёт контроль над каждым релизом, но замедляет доставку; автодеплой каждого merge в main быстр, однако баг, не пойманный тестами, сразу едет к пользователям.
- Много unit vs много e2e: unit-тесты с mocks дёшевы и быстры, но не ловят рассинхрон компонентов; e2e на staging проверяет реальный сценарий целиком, однако платит минутами выполнения и flaky-падениями.
- Alpine/distroless vs полный образ: минимальный runtime меньше весит и сужает поверхность CVE, но без shell трудно отладить упавший контейнер; полноценный образ удобен для debug, однако тянет лишние уязвимости.
- Canary vs полный деплой: выкат новой версии на один инстанс с мониторингом ловит проблему на малой доле трафика, но удлиняет релиз; деплой сразу на все инстансы быстрее, однако ошибка бьёт по всем пользователям.
- GitOps vs push из CI: ArgoCD с
selfHealдержит кластер в состоянии git и даёт rollback черезgit revert, но добавляет оператора и задержку синка; прямойansible-playbookиз CI проще, однако drift серверов никто автоматически не чинит.
Практическое задание
Задание 1: CI pipeline
Создайте GitHub Actions workflow для CI:
yamlname: CI on: [push, pull_request] jobs: lint: # golangci-lint test: # go test -race -cover build: # go build
Добавьте кэширование Go modules.
Задание 2: Security scanning
Добавьте security workflow:
yamlname: Security on: push: schedule: - cron: '0 2 * * *' jobs: gosec: # gosec ./... govulncheck: # govulncheck ./... trivy: # trivy image
Задание 3: Docker build в CI
Добавьте сборку и push Docker образа:
yaml- name: Build image run: docker build -t $REGISTRY/$IMAGE_NAME:${{ github.sha }} . - name: Scan with Trivy run: trivy image --exit-code 1 --severity CRITICAL $REGISTRY/$IMAGE_NAME:${{ github.sha }} - name: Push to registry run: docker push $REGISTRY/$IMAGE_NAME:${{ github.sha }}
Задание 4: Terraform для VDS
Напишите Terraform для:
- VPC network
- Subnet
- VDS instance
- PostgreSQL
- Security groups
Примените: terraform plan, terraform apply
Задание 5: Ansible playbook
Напишите Ansible playbook для:
- Создания app user
- Копирования бинарника
- Копирования конфига
- Настройки systemd unit
- Запуска сервиса
Примените: ansible-playbook -i inventory.yml playbook.yml
Задание 6: Deployment automation
Создайте deploy workflow:
yamlname: Deploy on: push: branches: [main] jobs: deploy-staging: # Build image # Deploy to staging # Health check # Run E2E tests deploy-production: needs: deploy-staging environment: production # Deploy to production # Health check
Задание 7: Automated rollback
Добавьте автоматический rollback:
yaml- name: Deploy id: deploy run: ansible-playbook deploy.yml continue-on-error: true - name: Health check id: healthcheck run: curl -f https://example.com/healthz - name: Rollback if: steps.healthcheck.outcome == 'failure' run: ansible-playbook rollback.yml
Задание 8: Drift detection
Добавьте проверку drift в CI:
yaml- name: Check for drift run: | terraform plan -detailed-exitcode if [ $? -eq 2 ]; then echo "Drift detected!" exit 1 fi
Quiz "Проверь себя"
- name: Health check
id: healthcheck
run: curl -f https://staging.example.com/healthz
- name: Rollback
if: steps.healthcheck.outcome == 'failure'
run: ansible-playbook rollback.yml$ terraform plan -detailed-exitcode
...
Exit code 21.2.3 в semantic versioning?✅ Итоги модуля
Студент должен:
- Проектировать CI/CD pipeline для Go-сервисов
- Настраивать test pipeline с разными типами тестов
- Интегрировать security scanning
- Управлять build artifacts и версионированием
- Контейнеризировать сервисы для production
- Автоматизировать инфраструктуру через Terraform
- Управлять конфигурацией через Ansible
- Реализовывать deployment automation с rollback
- Понимать принципы GitOps
- Ответить на все вопросы Quiz
🔗 Мостик к Модулю 11
Следующий модуль: Модуль 11. Architecture Documentation - ADR writing, decision records, runbooks, migration plans, defense of technical decisions, C4 model. Привязка к docs/adr/ шаблона.