Содержание

Docker и Docker Compose для Go-проектов

Docker помогает запускать проект одинаково на разных машинах. Если на вашем ноутбуке Go одной версии, PostgreSQL другой, а у teammate вообще другая операционная система, Docker уменьшает количество "у меня не запускается".

Docker упаковывает приложение и его окружение в image. Контейнер - это запущенный image. Docker Compose связывает несколько контейнеров в одну локальную систему: backend, PostgreSQL, Redis, RabbitMQ, MinIO.

Зачем Docker Go-проекту

Go сам по себе компилируется в удобный бинарник. Но backend редко состоит только из одного бинарника. Ему нужны:

  • база данных;
  • переменные окружения;
  • миграции;
  • Redis;
  • очереди;
  • объектное хранилище;
  • одинаковые команды запуска.

Docker полезен не потому, что Go без него не работает. Он полезен потому, что проект с зависимостями становится воспроизводимым.

Dockerfile для Go

Простой multi-stage Dockerfile:

FROM golang:1.22-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go build -o server ./cmd/server FROM alpine:3.20 WORKDIR /app COPY --from=builder /app/server ./server EXPOSE 8080 CMD ["./server"]

Первый этап собирает бинарник. Второй этап содержит только то, что нужно для запуска. Так итоговый образ меньше и не содержит весь набор инструментов Go.

Почему COPY go.mod отдельно

В Docker build есть cache. Если сначала скопировать go.mod и go.sum, а потом выполнить go mod download, Docker сможет переиспользовать слой с зависимостями, пока эти файлы не меняются.

COPY go.mod go.sum ./ RUN go mod download COPY . .

Если сразу сделать COPY . ., любое изменение исходного файла будет сбрасывать cache зависимостей.

Слушайте 0.0.0.0

Внутри контейнера backend должен слушать не только localhost, а 0.0.0.0:

http.ListenAndServe("0.0.0.0:8080", handler)

localhost внутри контейнера означает сам контейнер. Если сервис слушает только loopback, Docker port mapping может не дать доступ с хоста.

docker-compose.yml

Пример с API и PostgreSQL:

services: api: build: . ports: - "8080:8080" environment: DATABASE_URL: postgres://app:secret@postgres:5432/app?sslmode=disable depends_on: postgres: condition: service_healthy postgres: image: postgres:16 environment: POSTGRES_USER: app POSTGRES_PASSWORD: secret POSTGRES_DB: app ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U app -d app"] interval: 5s timeout: 5s retries: 5

Внутри Compose сервисы обращаются друг к другу по имени: postgres, redis, rabbitmq.

Ports

Запись:

ports: - "8080:8080"

означает:

host_port:container_port

Левая часть доступна на вашей машине. Правая часть - порт внутри контейнера.

Для PostgreSQL:

ports: - "5432:5432"

Это позволяет подключиться к базе с хоста, например через psql или GUI-клиент.

Networks

Compose автоматически создает network для сервисов. Поэтому api может подключиться к postgres по имени сервиса:

postgres:5432

Не нужно узнавать IP контейнера. IP может меняться, имя сервиса остается стабильным.

Volumes

Чтобы данные PostgreSQL не исчезали после перезапуска:

volumes: postgres_data: services: postgres: volumes: - postgres_data:/var/lib/postgresql/data

Без volume контейнер можно удалить вместе с данными. Для базы это почти всегда неприятный сюрприз.

Healthcheck

depends_on без healthcheck ждет только запуска контейнера, а не готовности сервиса. PostgreSQL может быть запущен как процесс, но еще не готов принимать подключения.

healthcheck: test: ["CMD-SHELL", "pg_isready -U app -d app"] interval: 5s timeout: 5s retries: 5

Healthcheck делает старт API более предсказуемым.

Переменные окружения

Для локальной разработки можно использовать .env, но не коммитить секреты:

services: api: env_file: - .env

В репозитории оставляйте .env.example:

DATABASE_URL=postgres://app:secret@postgres:5432/app?sslmode=disable PORT=8080

Так человек понимает, какие переменные нужны, но реальные секреты не попадают в Git.

Полезные команды

docker compose up --build docker compose up -d docker compose logs api docker compose logs -f api docker compose exec postgres psql -U app -d app docker compose down docker compose down -v

down -v удаляет volumes, то есть данные БД. Используйте осторожно.

Частые ошибки

  1. Использовать localhost вместо имени сервиса внутри Compose.
  2. Не добавить volume для базы.
  3. Считать, что depends_on без healthcheck ждет готовности PostgreSQL.
  4. Коммитить .env с секретами.
  5. Слушать 127.0.0.1 внутри контейнера.
  6. Собирать image без .dockerignore, отправляя в build context лишние файлы.

Чеклист

  1. Dockerfile использует multi-stage build.
  2. go.mod и go.sum копируются до исходников для cache.
  3. Сервис слушает 0.0.0.0, а не только localhost.
  4. Внутри Compose сервисы ходят друг к другу по имени.
  5. База имеет volume.
  6. У зависимостей есть healthcheck.
  7. Секреты не закоммичены.
  8. Есть .env.example.
  9. В README есть команды docker compose up и docker compose down.

Следующий шаг после статьи

Закрепите тему в реальном проекте и продолжайте обучение на курсах Praxis.

Продолжить изучение

Выбери следующую статью по маршруту или углубись в смежную тему.

Похожие статьи