Деплой C++ на Kubernetes: базовое руководство для начинающих

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем деплоить C++ в Kubernetes?

C++ остается одним из самых производительных языков программирования, особенно в сферах высоконагруженных систем, игровых серверов, финансовых приложений и IoT. Однако в современном мире облачных технологий просто написать быстрый код недостаточно — его нужно уметь надежно разворачивать, масштабировать и обновлять без простоев. Именно здесь на помощь приходит Kubernetes (K8s) — система оркестрации контейнеров, которая автоматизирует развертывание, масштабирование и управление приложениями.

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

1. Подготовка: C++ приложение и Docker-контейнеризация

Прежде чем говорить о Kubernetes, нужно подготовить наше C++ приложение к работе в контейнере. Контейнеризация — это упаковка приложения вместе со всеми его зависимостями в изолированную среду. Для C++ это критически важно, так как скомпилированный бинарник может зависеть от конкретных версий библиотек (libc, libstdc++ и т.д.) на целевой машине.

1.1. Простое C++ приложение для деплоя

Создадим файл main.cpp — простой HTTP-сервер, который будет отвечать на запросы. Для простоты не будем использовать внешние библиотеки, а напишем примитивный TCP-сервер.

// main.cpp
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    const char* hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello, K8s!\n";

    // Создание сокета
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Привязка к порту 8080
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080);

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 3) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    std::cout << "Server listening on port 8080" << std::endl;

    while(true) {
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
            perror("accept");
            exit(EXIT_FAILURE);
        }
        read(new_socket, buffer, 1024);
        send(new_socket, hello, strlen(hello), 0);
        close(new_socket);
    }
    return 0;
}

1.2. Сборка статического бинарника

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

g++ -static -o server main.cpp -lpthread

Флаг -static включает все необходимые библиотеки прямо в исполняемый файл. Флаг -lpthread нужен для работы с потоками (хотя в нашем простом примере он не обязателен, но для реальных проектов пригодится).

1.3. Dockerfile для C++ приложения

Теперь создадим Dockerfile. Используем многоступенчатую сборку (multi-stage build): сначала компилируем код в большом образе с компилятором, затем копируем готовый бинарник в минимальный образ scratch (пустой образ).

# Первый этап: сборка
FROM gcc:latest AS builder
WORKDIR /app
COPY main.cpp .
RUN g++ -static -o server main.cpp -lpthread

# Второй этап: минимальный образ
FROM scratch
WORKDIR /app
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]

Соберите образ и проверьте его локально:

docker build -t cpp-k8s-server:latest .
docker run -p 8080:8080 cpp-k8s-server

Откройте браузер и перейдите на http://localhost:8080 — вы должны увидеть "Hello, K8s!".

2. Основы Kubernetes: Pod и Deployment

Kubernetes работает с контейнерами через абстракции. Самая маленькая единица — это Pod (обертка над одним или несколькими контейнерами). Однако напрямую создавать Pod'ы не рекомендуется — для управления жизненным циклом используется Deployment, который автоматически перезапускает Pod при сбоях и позволяет делать обновления.

2.1. Создание Deployment манифеста

Создайте файл deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cpp-server-deployment
  labels:
    app: cpp-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cpp-server
  template:
    metadata:
      labels:
        app: cpp-server
    spec:
      containers:
      - name: cpp-server
        image: cpp-k8s-server:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

Что здесь важно: