Деплой C++ на Kubernetes: базовое руководство

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

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

Начать курс

Введение: почему C++ и Kubernetes — это мощный тандем

Современная облачная инфраструктура всё чаще строится на микросервисной архитектуре и контейнеризации. Kubernetes стал стандартом де-факто для оркестрации контейнеров. Однако многие разработчики ошибочно полагают, что Kubernetes — это удел только интерпретируемых языков (Python, JavaScript) или JVM. На самом деле, деплой C++ приложений в Kubernetes не только возможен, но и даёт значительные преимущества: высокая производительность, минимальное потребление памяти и полный контроль над ресурсами.

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

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

Прежде чем говорить о Kubernetes, нужно подготовить само C++ приложение. Для примера возьмём простой HTTP-сервер на базе библиотеки libmicrohttpd. Этот сервер будет отвечать на запросы и читать конфигурацию из переменных окружения — это стандартный подход для 12-factor приложений.

1.1. Код простого HTTP-сервера на C++

Создадим файл main.cpp:

#include <iostream>
#include <string>
#include <microhttpd.h>
#include <cstdlib>

static enum MHD_Result answer_to_connection(void *cls, struct MHD_Connection *connection,
                                            const char *url, const char *method,
                                            const char *version, const char *upload_data,
                                            size_t *upload_data_size, void **con_cls) {
    const char *page = "<html><body>Hello from C++ on Kubernetes!</body></html>";
    struct MHD_Response *response = MHD_create_response_from_buffer(strlen(page),
                                                                     (void*)page,
                                                                     MHD_RESPMEM_PERSISTENT);
    int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
    MHD_destroy_response(response);
    return ret == MHD_YES ? MHD_OK : MHD_NO;
}

int main() {
    const char* port_str = std::getenv("PORT");
    unsigned int port = port_str ? std::stoul(port_str) : 8080;

    std::cout << "Starting server on port " << port << std::endl;
    struct MHD_Daemon *daemon = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
                                                 port, nullptr, nullptr,
                                                 &answer_to_connection, nullptr,
                                                 MHD_OPTION_END);
    if (nullptr == daemon) {
        std::cerr << "Failed to start daemon" << std::endl;
        return 1;
    }

    // Бесконечное ожидание
    while (true) {
        sleep(1);
    }

    MHD_stop_daemon(daemon);
    return 0;
}

Этот код читает порт из переменной окружения PORT (значение по умолчанию — 8080) и запускает HTTP-сервер. Обратите внимание: мы используем бесконечный цикл, чтобы процесс не завершался — это важно для работы в контейнере.

1.2. Сборка приложения

Для сборки нам понадобится CMake. Создадим CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(cpp-k8s-server)

find_package(libmicrohttpd REQUIRED)

add_executable(server main.cpp)
target_link_libraries(server ${LIBMICROHTTPD_LIBRARIES})
target_include_directories(server PRIVATE ${LIBMICROHTTPD_INCLUDE_DIRS})

Теперь приложение готово к контейнеризации.

2. Создание Docker-образа для C++ приложения

Kubernetes работает с контейнерами, поэтому нам нужен Docker-образ. Для C++ приложений критически важен выбор базового образа: мы хотим минимизировать размер и повысить безопасность.

2.1. Многоступенчатая сборка (Multi-stage build)

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

# Этап 1: Сборка
FROM ubuntu:22.04 AS builder

RUN apt-get update && apt-get install -y \
    cmake \
    g++ \
    libmicrohttpd-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY CMakeLists.txt main.cpp ./
RUN cmake . && make

# Этап 2: Финальный образ
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y \
    libmicrohttpd12 \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/server /usr/local/bin/server

EXPOSE 8080
CMD ["server"]

В финальном образе остаётся только бинарник и минимальный набор рантайм-зависимостей. Размер такого образа будет около 50-70 МБ, что значительно меньше, чем если бы мы использовали полный образ с компилятором.

3. Деплой C++ приложения в Kubernetes

Теперь, когда у нас есть Docker-образ (например, myrepo/cpp-server:latest), мы можем развернуть его в Kubernetes. Нам понадобятся два основных манифеста: Deployment и Service.

3.1. Манифест Deployment

Deployment управляет репликами подов. Создадим файл deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cpp-server-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cpp-server
  template:
    metadata:
      labels:
        app: cpp-server
    spec:
      containers:
      - name: cpp-server
        image: myrepo/cpp-server:latest
        ports:
        - containerPort: 8080
        env:
        - name: PORT
          value: "8080"
        - name: GREETING
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: greeting
        livenessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"

Блоги

Рекомендаций по книгам