Podman-compose-to-kube

Материал из ALT Linux Wiki

podman-compose-to-kube как средство миграция docker-compose решений в kubernetes

Одной из основных проблем миграции docker-compose (docker swarm) решений в kubernetes является генерация kubernetes-манифестов из YAML-файлов описания стека сервисов. Существует достаточно бедный набор инструментов, решающий данную проблему. Данный документ описывает решение данной проблемы путем использования команд podman-compose, podman-compose-to-kube.

В качестве примера разворачивания стека будет использоваться docker-compose стек hello-python проекта podman-compose.

Будут рассмотрены вопросы разворачивания миграции как rootfull так и rootless-решений.

Установка ПО, создание пользователей, разворачивание kubernetes

Для разворачивания docker-compose стеков необходимо установить пакеты podman-compose, podman-compose-to-kube.

rootfull-окружение

Разворачивание решений в roofull окружении производится под пользователем root. В создании других пользователей необходимости нет. Разворачивание roofull-kubernetes описано в документе Kubernetes.

rootless-решение

Разворачивание rooless-kubernetes описано в документе Rootless kubernetes. В процессе его разворачивания создается пользователь u7s-admin. Вы можете разворачивать rootless podman-compose стек либо в рамках этого пользователя либо создать пользователя, имеющий право загружать образы с внешний репозиториев. В защищенных платформах c10f. это пользователи, входящие в группу podman_dev. Пользователь u7s-admin входит в эту группу.

Разворачивание docker-compose стека в podman-compose

Загрузка описания стека сервисов hello-python

Скопируйте содержимое каталога hello-python.

Если у Вас установлен git это можно сделать командами:

# git clone -n --depth=1 --filter=tree:0 https://github.com/containers/podman-compose.git
# cd podman-compose/
# git sparse-checkout set --no-cone examples/hello-python
# git checkout

После выполнения команд в каталоге podman-compose/examples/hello-python развернется содержание указанного выше каталога.

Разворачивание стека сервисов

Описание стека сервисов

Перейдите в каталог podman-compose/examples/hello-python. В каталогн присутсвует файл docker-compose.yml описание стека сервисов:

---
version: '3'
volumes:
  redis:
services:
  redis:
    read_only: true
    image: docker.io/redis:alpine
    command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]
    volumes:
    - redis:/data
  web:
    read_only: true
    build:
      context: .
    image: hello-py-aioweb
    ports:
    - 8080:8080
    environment:
      REDIS_HOST: redis

В сервисе redis запускается контейнер с томом redis и портом для внешнего доступа 6379.

В сервисе web собирается образ hello-py-aioweb, получающий имя localhost/hello-py-aioweb и на его основе запускается контейнер, обеспечивающий прием HTTP-запросов по порту 8080. Образ localhost/hello-py-aioweb собирается на основе Dockerfile:

FROM python:3.9-alpine

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "-m", "app.web" ]
EXPOSE 8080

При запуске контейнера запускается python-скрипт app/web.py, HTTP-принимающий запросы, формирующий счетчик запросов в redis-базе и возвращающий текст с номером запроса.

Запуск стека сервисов

Перед запуском стека сервисов необходимо уточнить файл docker-compose.yml:

version: '3'
volumes:
  redis:
  redis1:
services:
  redis:
    read_only: true
    image: docker.io/redis:alpine
    command: ["redis-server", "--appendonly", "yes", "--notify-keyspace-events", "Ex"]
    volumes:
    - redis:/data
    ports:
    - 6379
  web:
    read_only: true
    build:
      context: .
    image: hello-py-aioweb
    ports:
    - 8080:8080
    environment:
      REDIS_HOST: redis
      REDIS_PORT: 6379

В файл внесены два изменения:

  1. В сервис redis добавлено описание порта 6379.
  2. В сервис web добавлено описание переменной REDIS_PORT: 6379.

Оба эти изменения необходимы при разворачивании kubernet-сервисов в режиме Deployment.

Первое изменения связано с тем, что если описание порта отсутствует, то при генерации из за отсутствия информации не сгенерируется YML-файл описания kubernet-сервиса и redis-контейнер будет недоступен из контейнера web.

Второе изменение связано с тем, что в режиме Deployment в сконтейнер web экпортируется переменная REDIS_PORT в формате http://<ip>:<port>. Приложение App/web.py обрабатывает это значение в формате <port>.

Для запуска стека сервисов наберите команду:

podman-compose --in-pod counter -p counter up -d

При использовании podman-compose версии >= 1.0.7 параметр --in-pod необязателен.

Параметр -p задает имя проекта - суффикс имени POD'а (pod_counter) и префикс имен контейнеров и томов. Если параметр -p отсутствует в качестве имени проекта принимается имя текущего каталога (в нашем случае hello-python).

В процессе работы podman-compose выводит список запускаемых команд:

...
podman volume inspect counter_redis || podman volume create counter_redis
...
podman pod create --name=pod_counter --infra=false --share=
...
podman run --name=counter_redis_1 -d --pod=pod_counter --read-only --label ...
...
podman run --name=counter_web_1 -d --pod=pod_counter --read-only --label ...
...

После запуска POD'а и контейнеров состояние можно посмотреть командами. - список запущенных POD'ов:

podman pod ls
POD ID        NAME         STATUS      CREATED        INFRA ID    # OF CONTAINERS
d37ba3addeb3  pod_counter  Running     9 minutes ago              2
  • Логи контейнеров POD'а:
podman pod logs pod_counter
b5bdc8d1977f 1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
...
b5bdc8d1977f 1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp
  • Список запущенных контейнеров:
podman ps
CONTAINER ID  IMAGE                             COMMAND               CREATED         STATUS         PORTS                   NAMES
...
b5bdc8d1977f  docker.io/library/redis:alpine    redis-server --ap...  27 minutes ago  Up 27 minutes                          counter_redis_1
49f6f5141b24  localhost/hello-py-aioweb:latest  python -m App.web     27 minutes ago  Up 27 minutes  0.0.0.0:8080->8080/tcp  counter_web_1
...
  • Логи контейнера базы данных redis
podman logs counter_redis_1
1:C 18 Jan 2024 11:04:20.309 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
...
1:M 18 Jan 2024 11:04:20.312 * Ready to accept connections tcp

  • Логи контейнера WEB-интерфейса web:
podman log counter_web_1
Проверка работы стека сервисов

Для проверки работы стека последовательно пошлите запросы командой curl на порт 8080:

# curl localhost:8080/
counter=1
# curl localhost:8080/
counter=2
# curl localhost:8080/
counter=3
...

Экспорт запущенного POD'а в kubernetes-манифесты и их запуск

Разворачивание в виде kubernetes POD

Генерация манифестов производится командой podman-compose-to-kube. Формат ее вызова:

podman-compose-to-kube \
  [--type(-t) <deployment type>]\
  [--namespace(-n) <namespace>]
  [--dir(-d) <manifests_directory>]\
  [--pvpath <PersistentVolume_directory>] \
  [--user <rootless_user>]\
  [--group <rootless_group>]\
  [--debug <debug_level>]\
  <POD_name>\
  <docker-compose_file_name>
Генерация манифестов

Генерация манифестов для POD-разворачивания производится командой:

podman-compose-to-kube --debug=1 pod_counter docker-compose.yaml
Generate a POD manifest based on the specified POD
Generate a list of scalar yml elements ending with name(Name)
Generate a jq request to replace symbols _ with symbols - in selected elements
Generate list of services in docker-compose file
Add descriptions of the environment variables to the container web
Removing the deployment directory manifests/default/counter/Pod
Generate common POD YML file
Generate PersistentVolumeClaims and PersistentVolumes:
        manifests/default/counter/Pod/PersistentVolumeClaim/counter-redis.yml
        manifests/default/counter/Pod/PersistentVolume/default-counter-redis.yml
        /mnt/PersistentVolumes/default/counter-redis
Generate a deploy file manifests/default/counter/Pod/counter.yml of the Pod type:

Если в выводе шагов генерации нет необходимости флаг --debug=1 можно опустить.

Первый параметр pod_counter указывает имя поднятого podman-POD'а. Второй docker-compose.yaml - имя YAML-файла из которого поднят контейнер.

После вызова команды в текущем каталоге создастся подкаталог manifests следующей структуры:

manifests/
└── default
    └── counter
        └── Pod
            ├── counter.yml
            ├── PersistentVolume
            │   └── default-counter-redis.yml
            ├── PersistentVolumeClaim
            │   └── counter-redis.yml
            └── Service
                └── counter.yml

На первом уровне создастся каталог default имя которого задает kubernetes-namespace в котором будет запускаться POD.

В подкаталоге default создается подкаталог counter имя которого берется из имени генерируемого POD'а отбрасыванием префикса pod_.

В подкаталоге counter создается подкаталог по имени разворачивания - Pod.

В каталоге типа разворачивания Pod генерируются:

  • файл описания Pod-контейнера counter.yml;
  • подкаталог описания kubernet-сервиса Service
  • подкаталог PersistentVolumeClaim описания kubernet-запроса на монтируемые тома
  • подкаталог PersistentVolume описания томов для данного разворачивания.

Файл описания Pod-контейнера counter.yml

Файл описания Pod-контейнера counter.yml выглядит следующим образом:

# Created with podman-compose-to-kube 1.0.0-alt1
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: '2024-01-27T11:05:26Z'
  labels:
    app: counter
  name: counter
  namespace: default
spec:
  containers:
    - args:
        - redis-server
        - --appendonly
        - 'yes'
        - --notify-keyspace-events
        - Ex
      image: docker.io/library/redis:alpine
      name: counterredis1
      ports:
        - containerPort: 6379
      securityContext:
        readOnlyRootFilesystem: true
      volumeMounts:
        - mountPath: /data
          name: counter-redis-pvc
    - env:
        - name: REDIS_HOST
          value: redis
        - name: REDIS_PORT
          value: '6379'
      image: localhost/hello-py-aioweb:latest
      name: counterweb1
      ports:
        - containerPort: 8080
      securityContext:
        readOnlyRootFilesystem: true
  volumes:
    - name: counter-redis-pvc
      persistentVolumeClaim:
        claimName: counter-redis
  hostAliases:
    - ip: 127.0.0.1
      hostnames:
        - redis
        - web

Файл описывает в namespace: default в POD'е с именем <counter два контейнера: counterredis1, counterweb1.

Контейнер counterredis1 принимает запросы по порту 6379 и монтирует каталог /data на том, получаемый по запросу (PersisnentVolumeClaim) с именем (claimName) counter-redis.

Контейнер counterweb1 принимает запросы по порту 8080. В его среду экспортируются две переменные: REDIS_HOST и REDIS_PORT.

Так как в разворачивании типа POD оба контейнера стартуют в одном POD'е с локальным адресом 127.0.0.1, к YML-файлу добавляется описание hostAliases, привязывающий короткие DNS-имена web, redis к локальному адресу 127.0.0.1. Таким образом контейнер redis доступен из контейнера web под именем redis через локальный интерфейс lo POD'а.

Подкаталог описания kubernet-сервиса Service

Так как в рамках разворачивания запускается всего один POD подкаталог описания kubernet-сервиса Service содержит всего один файл counter.yml:

# Created with podman-compose-to-kube 1.0.0-alt1
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: '2024-01-27T11:05:26Z'
  labels:
    app: counter
  name: counter
  namespace: default
spec:
  ports:
    - name: '6379'
      nodePort: 32717
      port: 6379
      targetPort: 6379
    - name: '8080'
      nodePort: 31703
      port: 8080
      targetPort: 8080
  selector:
    app: counter
  type: NodePort

Файл описывает для POD'а с именем counter в namespace: default два порта для внешнего доступа:

  • 6379 - с node-портом для внешнего доступа 32717;
  • 8080 - с node-портом для внешнего доступа 31703.

Если внешний доступ к контейнеру counterredis1 не требуется описание порта 6379 можно удалить.

Подкаталог PersistentVolumeClaim описания kubernet-запроса на монтируемые тома

docker-compose YML файл содержит описание только одного внешнего тома для сервиса redis. Данное описание генерирует запрос на выделение тома, содержащееся в файле counter-redis.yml:

# Created with podman-compose-to-kube 1.0.6-alt1
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  annotations:
    volume.podman.io/driver: local
  creationTimestamp: '2024-01-27T11:05:27Z'
  name: counter-redis
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: manual

Файл для запроса counter-redis в namespace: default запрашивает том объемом 1Gi.

Подкаталог PersistentVolume описания томов для данного разворачивания

Для каждого запроса на том в каталоге PersistentVolume формируется описание тома на локальном диске узла. Файл default-counter-redis.yml содержит следующую информацию:

# Created with podman-compose-to-kube 1.0.6-alt1
apiVersion: v1
kind: PersistentVolume
metadata:
  name: default-counter-redis
  labels:
    type: local
spec:
  storageClassName: manual
  claimRef:
    name: counter-redis
    namespace: default
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /mnt/PersistentVolumes/default/counter-redis

Для запроса (claimRef) с именем counter-redis в каталоге /mnt/PersistentVolumes/default/counter-redis выделяется 1Gi дискового пространства. Имя корневого каталог томов /mnt/PersistentVolumes/ можно изменить указав друглй каталог в параметре --pvpath.

Запуск манифестов

Разворачивание в виде kubernetes Deployment

Генерация манифестов
Запуск манифестов

Особенности запуска в rootless окружении

Копирование локальных образов в rootless окружении

В rootless-окружении образы, созданные podman-compose хранятся в каталоге /var/lib/u7s-admin/.local/share/containers/storage/. Образы же для kubernetes хранятся в другом каталоге /var/lib/u7s-admin/.local/share/usernetes/containers/storage/. Для образов, загружаемых с регистраторов это несущественно, так как они подгружаются при запуске POD'а. Образы же, созданные локально, как в нашем случае образ localhost/hello-py-aioweb необходимо перенести в container-storage для kubernetes-образов командой skopeo:

# skopeo copy \
  containers-storage:[/var/lib/u7s-admin/.local/share/containers/storage/]localhost/hello-py-aioweb \ 
  containers-storage:[/var/lib/u7s-admin/.local/share/usernetes/containers/storage/]localhost/hello-py-aioweb

и изменить собственника перенесенного образа с root на u7s-admin:

# chown -R u7s-admin:u7s-admin /var/lib/u7s-admin/.local/