Как я запускал minecraft в kubernetes
Minecraft в kubernetes Всем привет, в этой статье я поделюсь своим опытом запуска Minecraft в kubernetes. Основная проблема, возникающая при попытке запустить Minecraft в Kubernetes, заключается в том, что во время работы Minecraft происходит работа с файлами на локальном диске, а такой подход в Kubernetes является нежелательным, поскольку в случае сбоя ноды (назовем его нода A), на которой развернут под - под не сможет быть создан на другой ноде (нода B), так как известно, что под ранее работал на ноде A и, соответственно, все его постоянные файлы находятся там. Варианты решения проблем с использованием постоянных файлов В поисках решения этой проблемы, я определил возможные методы решения: отказ от постоянных файлов использовать Persistent Volume создать сетевой диск использовать csi-s3 Давайте подробнее рассмотрим каждый из вариантов Отказ от постоянных файлов Плюсы: Работает нативно Это лучшее решение, т.к. не будет проблем с пересозданием пода на другой ноде в случае сбоя Минусы: Не применимо к текущему приложению Использовать Persistent Volume Плюсы: Высокая скорость чтения/записи файлов Минусы: Все данные находятся на одной ноде, в случае его поломки pod не сможет подняться Создать сетевой диск Плюсы: Данные реплицируются на несколько нод Минусы: Более низкая скорость чтения/записи файлов Использовать csi-s3 Плюсы: Данные не находятся в кластере Минусы: Более низкая скорость чтения/записи файлов Из-за большого количества вызовов API может получиться высокая цена Анализ вариантов Основываясь на полученных данных, мы приходим к выводу, что на данный момент не существует решения, которое могло бы соответствовать нашим требованиям, а именно: высокая скорость чтения/записи файлов не должно быть привязки к определенной ноде минимальные затраты на реализацию данного функционала Решение проблемы с использованием постоянных данных Чтобы решить эту проблему, был подготовлен docker контейнер. Алгоритм работы контейнера Добавляются SSH-ключи. Ключи берутся из значений переменных ENV Репозиторий сервера клонируется из git с конфигурацией сервера в каталог - /app Последовательности MYSQL_.+ заменяются во всех файлах в /app/plugins на их значение ENV Игровой мир копируется из хранилища s3 и распаковывается Игровые плагины и ядро копируются из хранилища s3 Начинается /task_manager.py - который отвечает за выполнение задач cron, работу сервера и вывод syslog Cron задачи В задачах cron в настоящее время настроены следующие задачи: * * * * * root /git_pull.sh | logger * * * * * root /git_commit.sh | logger 0 * * * * root /copy_worlds.sh | logger Информация по каждой задаче: /git_pull.sh - выполняет git pull в каталоге /app /git_commit.sh - создает commit и отправляет его в репозиторий /copy_worlds.sh - создает копию мира и загружает ее в хранилище s3 вместо существующей Пример развертывания службы Настройка ingress контроллера Рассмотрим пример с traefik. Чтобы настроить ingres контроллер, вам нужно будет выполнить следующую команду: $ helm upgrade --values traefik-values.yaml traefik traefik/traefik -n kube-system Контент файла traefik-values.yaml: ports: traefik: port: 9000 expose: false exposedPort: 9000 protocol: TCP web: port: 8000 expose: true exposedPort: 80 protocol: TCP minecraft: port: 22565 expose: true exposedPort: 25565 protocol: TCP websecure: port: 8443 expose: true exposedPort: 443 protocol: TCP http3: enabled: false tls: enabled: true options: "" certResolver: "" domains: [] middlewares: [] metrics: port: 9100 expose: false exposedPort: 9100 protocol: TCP После этого подключения к LoadBalancer будут разрешены через порт 25565 $ kubectl get svc -n kube-system | grep -v "none" NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE traefik LoadBalancer 10.43.2.50 100.10.0.11 25565:30224/TCP,80:31380/TCP,443:30226/TCP 2d21h Deploy Пример окончательного манифеста, содержащего требуемую конфигурацию: --- apiVersion: v1 kind: Namespace metadata: name: minecraft --- apiVersion: v1 kind: Secret metadata: name: minecraft-secret namespace: minecraft type: Opaque data: SSH_KEY_PUBLIC: "SECRET" SSH_KEY_PRIVATE: "SECRET" MYSQL_ROOT_PASSWORD: "SECRET" MYSQL_DBS: "SECRET" MYSQL_HOST: "SECRET" MYSQL_USER: "SECRET" MYSQL_PASSWORD: "SECRET" S3_BUCKET: "SECRET" S3_ACCESS_KEY: "SECRET" S3_ACCESS_KEY_ID: "SECRET" --- apiVersion: apps/v1 kind: Deployment metadata: name: some_worlds_name namespace: minecraft spec: selector: matchLabels: app: some_worlds_name strategy: type: Recreate template: metadata: namespace: minecraft labels: app: some_worlds_name spec: imagePullSecrets: - name: github-registry containers: - image: ghcr.io/oldtyt/docker_minecraft imagePullPolicy: Always name: some_worlds_name resources: requests: cpu: 1000m memory: 5G limits: memory: 6G cpu: 1200m env: - name: XMX value: "5G" - name: "XMS" value: "512M" - name: MYSQL_HOST valueFrom: secretKeyRef: name: minecraft-secret key: MYSQL_HOST - name: MYSQL_USER valueFrom: secretKeyRef: name: minecraft-secret key: MYSQL_USER - name: MYSQL_DB valueFrom: secretKeyRef: name: minecraft-secret key: MYSQL_USER - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: minecraft-secret key: MYSQL_PASSWORD - name: S3_BUCKET valueFrom: secretKeyRef: name: minecraft-secret key: S3_BUCKET - name: S3_ACCESS_KEY valueFrom: secretKeyRef: name: minecraft-secret key: S3_ACCESS_KEY - name: S3_ACCESS_KEY_ID valueFrom: secretKeyRef: name: minecraft-secret key: S3_ACCESS_KEY_ID - name: SSH_KEY_PRIVATE valueFrom: secretKeyRef: name: minecraft-secret key: SSH_KEY_PRIVATE - name: SSH_KEY_PUBLIC valueFrom: secretKeyRef: name: minecraft-secret key: SSH_KEY_PUBLIC - name: PLUGINS_LIST value: "some,plugins,list" - name: GIT_REPO value: "git@github.com:USER/REPO.git" - name: KERNEL value: "KERNEL" - name: "WORLDS" value: "some_worlds_name" --- apiVersion: v1 kind: Service metadata: name: some_worlds_name namespace: minecraft labels: env: prod app: some_worlds_name owner: OldTyT spec: ports: - name: minecraft targetPort: 25565 port: 25565 protocol: TCP selector: app: some_worlds_name Команда для развертывания манифеста: $ kubectl apply -f minecraft.yaml