логотип PurpleSchool
логотип PurpleSchool

Работа с Docker-контейнерами в Go

Автор

Александр Гольцман

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

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

Почему контейнеризация важна?

Использование контейнеров дает несколько ключевых преимуществ:

  • Изоляция — приложение работает в одном окружении независимо от операционной системы и установленных зависимостей.
  • Портативность — контейнер можно запустить на любом сервере с Docker без дополнительных настроек.
  • Масштабируемость — контейнеры легко управляются с помощью Kubernetes и других оркестраторов.

Теперь давайте посмотрим, как контейнеризировать Go-приложение.

Структура проекта

Прежде чем переходить к Docker, организуем структуру проекта. Пусть у нас есть простое HTTP-приложение:

/my-go-app
 ├── main.go
 ├── go.mod
 ├── go.sum
 ├── Dockerfile

Файл main.go:

package main

import (
    "fmt"
    "net/http"
    "os"
)

func handler(w http.ResponseWriter, r *http.Request) {
    env := os.Getenv("APP_ENV")
    fmt.Fprintf(w, "Приложение работает! Среда: %s", env)
}

func main() {
    http.HandleFunc("/", handler)
    port := "8080"
    fmt.Println("Сервер запущен на порту", port)
    http.ListenAndServe(":"+port, nil)
}

Теперь подготовим Dockerfile для контейнеризации.

Создание Dockerfile

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

Вот базовый Dockerfile для Go-приложения:

# Используем официальный образ Go
FROM golang:1.20 AS builder

# Устанавливаем рабочую директорию
WORKDIR /app

# Копируем файлы проекта
COPY . .

# Загружаем зависимости и собираем бинарник
RUN go mod tidy && go build -o app

# Используем минималистичный образ для финального контейнера
FROM alpine:latest

WORKDIR /root/
COPY --from=builder /app/app .

# Задаем переменную окружения
ENV APP_ENV=production

# Запускаем приложение
CMD ["./app"]

Здесь используется многоэтапная сборка (multi-stage build):

  1. В первом этапе приложение компилируется в контейнере на основе golang:1.20.
  2. Затем готовый бинарный файл копируется в легковесный образ alpine, что уменьшает размер контейнера.

Сборка и запуск контейнера

Теперь создадим Docker-образ и запустим контейнер.

Сборка образа

docker build -t my-go-app .

Флаг -t задает имя образа (my-go-app).

Запуск контейнера

docker run --rm -p 8080:8080 my-go-app

Флаг -p 8080:8080 пробрасывает порт, чтобы приложение было доступно локально.

Проверка работы контейнера

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

curl http://localhost:8080

Оптимизация размера образа

Если приложение не использует динамические библиотеки, его можно упаковать в нулевой образ (scratch), который не содержит ничего, кроме бинарного файла:

FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go mod tidy && go build -o app

FROM scratch
COPY --from=builder /app/app .
CMD ["/app"]

Такой контейнер будет еще меньше, но в нем нет оболочки и утилит ОС, поэтому он подходит не для всех приложений.

Передача переменных окружения

Переменные окружения позволяют управлять конфигурацией без изменения кода.

В Dockerfile можно задать их так:

ENV APP_ENV=production

При запуске контейнера можно передать переменные через флаг -e:

docker run -e APP_ENV=development my-go-app

В Go их можно прочитать с помощью os.Getenv:

env := os.Getenv("APP_ENV")
fmt.Println("Среда выполнения:", env)

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

Если приложение зависит от базы данных или других сервисов, удобнее использовать docker-compose.yaml.

Пример для приложения с PostgreSQL:

version: '3'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - APP_ENV=production
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    ports:
      - "5432:5432"

Запуск всех сервисов:

docker-compose up -d

Флаг -d запустит контейнеры в фоновом режиме.

Удаление контейнеров и образов

Чтобы очистить систему от ненужных контейнеров и образов, используйте:

docker stop my-go-app
docker rm my-go-app
docker rmi my-go-app

Можно также удалить все неиспользуемые ресурсы:

docker system prune -a

Заключение

В этой статье я показал, как запустить Go-приложение в Docker-контейнере, оптимизировать его размер и работать с переменными окружения. Мы разобрали:

  • Почему Go и Docker хорошо сочетаются.
  • Как написать Dockerfile и создать контейнер.
  • Как минимизировать размер образа с scratch.
  • Как передавать переменные окружения.
  • Как управлять зависимостями через docker-compose.

Использование Docker упрощает развертывание Go-приложений, делая их переносимыми и масштабируемыми.

Карта развития разработчика

Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile