Олег Марков
Создание Go-приложений и Kubernetes
Введение
Современная разработка программного обеспечения все чаще использует контейнеризированные приложения и масштабируемые оркестрационные решения. Go остается одним из самых популярных языков для написания микросервисов и сервисных приложений благодаря скорости, компактности бинарных файлов и простоте параллельных вычислений. Kubernetes, в свою очередь, стал стандартом де-факто для управления развертыванием и масштабированием таких приложений.
В этой статье я подробно расскажу, как создавать приложения на Go, подготавливать их для запуска в Kubernetes, настраивать Docker-образы, описывать манифесты и использовать возможности кластера для обеспечения отказоустойчивости и обновляемости сервисов. В рамках каждого этапа вас ждут практические советы и примеры кода.
Разработка Go-приложения для Kubernetes
Базовая структура Go-приложения
При разработке сервиса для работы в Kubernetes важно помнить о следующих особенностях:
- Приложение должно быть самодостаточным (
stateless
), если только не требуется хранить состояние; - Логи рекомендуется выводить в стандартный поток вывода (stdout), чтобы их мог собирать Kubernetes;
- Важно предусмотреть реакцию на сигналы завершения, чтобы ваше приложение корректно завершалось по требованию Kubernetes.
Давайте создадим базовый HTTP сервер на Go:
package main
import (
"log"
"net/http"
"os"
"os/signal"
"syscall"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, Kubernetes!"))
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
}
// Канал для перехвата сигнала завершения работы (например, kill, SIGTERM)
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGTERM, syscall.SIGINT)
go func() {
log.Println("Starting server on :8080")
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
<-stop // Ждём сигнала завершения
log.Println("Server is shutting down...")
// Здесь можно добавить код для graceful shutdown, если нужно
}
В этом примере сервер выводит приветствие и завершится по сигналу от Kubernetes. Логи пишутся в стандартный вывод, что упрощает мониторинг.
Контейнеризация приложения
Kubernetes работает с контейнерами — чаще всего с Docker. Для деплоймента приложения на Go вам потребуется собрать исполняемый файл и описать способ его упаковки в Docker-образ.
Пример простого Dockerfile
для Go-приложения:
# Используем официальный образ Golang для сборки
FROM golang:1.21-alpine AS build
# Добавляем исходники
WORKDIR /app
COPY . .
# Копилируем бинарник для Linux и отключаем CGO
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# Минимальный образ для запуска
FROM alpine:3.18
# Копируем бинарник из билдер-образа
COPY --from=build /app/main /main
# Запускаем приложение
ENTRYPOINT ["/main"]
- Первый этап собирает бинарник внутри официального образа Golang.
- Второй этап создает минимальный контейнер для запуска — используем маленький alpine, что экономит место и уменьшает возможные уязвимости.
Соберите образ командой:
docker build -t my-go-app:latest .
Публикация контейнера в реестр
Чтобы Kubernetes мог скачать ваш образ, поместите его в публичный или приватный docker registry (например, DockerHub, GitHub Container Registry, Google Container Registry и т.д.). Пример для DockerHub:
docker tag my-go-app:latest yourdockerhubusername/my-go-app:latest
docker push yourdockerhubusername/my-go-app:latest
Следите за приватностью: если репозиторий не публичный — настройте Kubernetes на доступ к нему (секреты).
Деплой в Kubernetes
Базовые сущности Kubernetes
Для деплоя приложения потребуется создать несколько объектов:
- Deployment — управляет ReplicaSet и обновлением подов;
- Service — проксирует и балансирует трафик к вашим подам;
- ConfigMap/Secret — передают параметры конфигурации и секреты (по необходимости).
Пример манифеста Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-app
spec:
replicas: 3 # Количество экземпляров приложения
selector:
matchLabels:
app: go-app
template:
metadata:
labels:
app: go-app
spec:
containers:
- name: go-app
image: yourdockerhubusername/my-go-app:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
Здесь:
replicas
— количество одновременно работающих подов;probes
— проверки готовности и живости; они позволяют Kubernetes перезапускать приложение при сбоях или понять, когда оно готово принимать трафик.
Service для доступа к приложению
apiVersion: v1
kind: Service
metadata:
name: go-app-service
spec:
selector:
app: go-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP # можно заменить на NodePort или LoadBalancer, если нужен внешний доступ
Этот сервис позволит другим подам обращаться к вашему приложению по имени go-app-service
. Для внешнего доступа меняется type
.
Применение манифестов
Сохраните манифесты в yaml-файлы. Применяйте их командой:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Проверьте статус с помощью:
kubectl get pods
kubectl get deployments
kubectl get svc
Работа с конфигами и секретами
Go-приложения часто используют переменные окружения для настройки. Для их передачи через Kubernetes служат ConfigMap и Secret.
Пример ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: go-app-config
data:
GREETING: "Hello from ConfigMap!"
Использование переменных в Deployment
...
env:
- name: GREETING
valueFrom:
configMapKeyRef:
name: go-app-config
key: GREETING
...
Внутри приложения можно получить значение обычным способом:
greeting := os.Getenv("GREETING")
Масштабирование приложения
Масштабируйте приложение командой:
kubectl scale deployment go-app --replicas=5
Kubernetes автоматически запустит новых подов до нужного количества.
Журналирование и отладка
Логирование
Kubernetes собирает стандартный вывод контейнеров. Для просмотра логов запущенного пода:
kubectl logs <pod_name>
Для постоянного слежения:
kubectl logs -f <pod_name>
Отладка и доступ к поду
Если нужно попасть внутрь контейнера для отладки:
kubectl exec -it <pod_name> -- /bin/sh
Здесь вы можете попробовать отправлять запросы к приложению, посмотреть файлы или состояние окружения.
Проверка проб и обновлений
Если рестартов много — проверьте readinessProbe
и livenessProbe
:
- Проверьте, что ваше приложение возвращает HTTP 200 на нужном порте и URL.
- Убедитесь, что время и параметры инициализации соответствуют времени старта приложения.
Стратегии обновления
По умолчанию используется rolling update — без "выпадений" трафика:
- Новые поды запускаются постепенно, старые завершаются после успешного старта новых.
- Вы можете управлять стратегией обновления через параметры deployment, например, ускорив обновление или сделав его более безопасным для продакшна.
Рекомендации для production
- Health Checks: Настроить readiness и liveness пробы.
- Ресурсы: Указать лимиты по CPU и памяти в спецификации контейнера.
- Строгая политика логирования: Не храните логи в файлах, выводите в stdout/stderr.
- Graceful shutdown: Обрабатывайте SIGTERM для обеспечения корректного завершения работ сервиса.
- CI/CD: Автоматизируйте построение образов, проверяйте шаблоны Kubernetes на ошибки, внедряйте тестирование.
- Безопасность: Используйте секреты, ограничивайте права сервисных аккаунтов, минимизируйте используемые образы.
Итог
Создание и деплой Go-приложений в Kubernetes — это эффективный и масштабируемый способ разработки современных сервисов. Go отлично подходит для микросервисной архитектуры и работает с минимальными накладными расходами, а Kubernetes позволяет легко управлять жизненным циклом и масштабом ваших приложений. Используйте контейнеризацию, манифесты Kubernetes и лучшие практики для обеспечения устойчивой работы и простоты обновления ваших сервисов.
Частозадаваемые технические вопросы по теме и ответы на них
Вопрос 1: Как сделать так, чтобы Go-приложение правильно завершалось при обновлении пода в Kubernetes?
Ответ:
Добавьте обработку сигналов SIGTERM/SIGINT с помощью пакета os/signal
. Kubernetes сначала отправляет контейнеру SIGTERM, а через некоторое время SIGKILL. В этот промежуток ваше приложение должно успеть завершить работу, закрыть соединения и освободить ресурсы.
Вопрос 2: Как передать секретные данные (например, пароли или токены) в приложение на Go внутри Kubernetes?
Ответ:
Используйте Kubernetes Secret. Описываете секрет как объект Secret с base64-кодированными значениями, а затем монтируете его как переменные окружения или файлы в под. Доступ в Go-приложении через os.Getenv
или чтение файлов из определённой директории.
Вопрос 3: Почему мое приложение перезапускается в Kubernetes с ошибкой "CrashLoopBackOff"?
Ответ:
Проверьте, что приложение не завершает работу сразу после запуска, а остается "живым" — обычно это ожидание HTTP запросов или другой работы. Убедитесь, что корректно настроили probes, нет ошибок в настройках окружения, достаточной памяти и доступности зависимости.
Вопрос 4: Как работать с конфигурационными файлами в Go-приложении внутри контейнера?
Ответ:
Создайте ConfigMap и смонтируйте как volume или используйте переменные окружения. В Go используйте стандартные методы работы с файлами или получите значения через пакет os. Следите за путями — внутри контейнера они соответствуют расшаренной директории.
Вопрос 5: Как добавить внешний доступ к сервису, находящемуся в Kubernetes-кластере?
Ответ:
Измените Service на тип NodePort или LoadBalancer. NodePort делает порт доступным на всех нодах кластера. LoadBalancer подходит для облачных провайдеров — автоматически выделит внешний IP и пробросит порт. Пример:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
Следите за настройками безопасности при публикации сервисов вовне.
Постройте личный план изучения Kubernetes до уровня Middle — бесплатно!
Kubernetes — часть карты развития DevOps
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Kubernetes
Лучшие курсы по теме

Kubernetes и Helm
Антон Ларичев
Docker и Ansible
Антон Ларичев