Pull to refresh

Не только работой едины — ARK+K3S+MetalLB

Reading time10 min
Views5.8K

Предыстория

Всем привет! Я работаю DevOps-MLops инженером и мне нравится постоянно что-то новое изучать и пытаться применять эти технологии везде где только можно.

Как-то играя в ark я захотел играть на своем сервере вместе с друзьями и как раз под это дело у меня есть свой домашний сервер и выделенный ip, характеристик сервера более чем достаточно, в моем случае это ryzen 7 1700x, 32Gb RAM и 1500Gb дисковой памяти.

Как основу я взял контейнернизацию lxc и настроил внутри ark сервер по первой инструкции что нашёл в интернете, настроил service для ark чтобы руками его не запускать все время и в целом месяца 3 все работало ок, потом по неизвестной причине мой ark сервер стал грузить всё что-то постоянно и вообще стал вести себя странно, сидеть изучать что не так у меня не было особо желания, да и времени, поэтому я подумал а не лучше мне попробовать перенести сервер в более что-то удобное для обслуживания нежели держать его где-то в отдельной среде куда нужно еще подключаться, делать кучу действий чтобы произвести анализ.

Приступим!

И так чтобы сделать красиво (по своему мнению), я взял:

  • k3s - скажем так это как ванильный k8s только из него вырезали около 1000 строчек кода как заявляют разработчики, кстати он построен на containerD.

  • metalLB - это инструмент позволяющий нам создать свой loadbalancer и использовать его, далее про него более подробно расскажу.

  • ubuntu 20.04 LTS - собственно то на чем всё будет крутится.

k3s

K3s (с офф сайт) — это сертифицированный дистрибутив Kubernetes с высокой доступностью, предназначенный для производственных рабочих нагрузок в автоматических, ограниченных по ресурсам, удаленных местах или внутри устройств IoT. (взято с офф сайт).

k3s (описание от меня) - это оркестратор контейнеров который может:

  • Запускать контейнеры (для контейнера мы можем также задать переменные, директории с нашего хоста или вообще с удаленного сервера, контейнер который будет запускаться до старта нашего основного контейнера, благодаря чему мы можем как пример подготовить какие либо файлы или выполнить скрипт для корректной работы нашего основного контейнера).

  • Ставить лимиты по ресурсам для контейнеров. (Как пример что приложение не сможет потреблять больше 2 Gb RAM или 2 CPU).

  • Задавать политики рестарта контейнеров.

  • Управлять доступностью к приложениям по сети.

  • Позволяет удобно читать логи.

  • Анализировать метрики.

  • Автомасштабированием контейнеров при повышении нагрузки ( к примеру если наше приложение в контейнере начинает грузится скажем под 80% по RAM или CPU оркестратор поднимет еще контейнеры и будет балансировать автоматическим сам нагрузку между ними).

  • И т.д.

  • И самое главное для нас мы можем описать это в манифесте один раз и потом переиспользовать и даже передавать кому угодно чтобы они могли запустить у себя или же наоборот мы можем запустить у себя чужой манифест.

И так я поставил чистую ubuntu 20.04 LTS, выделил ей 4 CPU 8 GB RAM, 50GB Disk и на неё первым делом мы будем ставить k3s.

  1. Открываем сайт https://k3s.io, на нем нам говорят чтобы это не займет много времени.

  1. Копируем команду и дописываем параметр который понадобится нам потом дальше, выполняем.

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable servicelb" sh -
  1. Далее всего менее 30 секунд и кластер готов.

  1. Прописываем sudo kubectl get nodes и видим что наш кластер готов!

  1. Далее, чтобы мы могли работать удобно из под своей УЗ нам требуется скопировать kubeconfig в свою домашнюю папку и выполнить пару команд:

Kubeconfig - это манифест в котором описано подключение к кластеру kubernetes. Вы можете его скопировать на любую машину у которой есть сетевой доступ до кластера и от туда им управлять.

mkdir ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown ark:ark .kube/config 
echo "export KUBECONFIG=~/.kube/config" >> ~/.bashrc
source ~/.bashrc
kubectl get node
  • Cоздаем папку где будет хранится кубконфиг по умолчанию.

  • Копируем kubeconfig в ранее созданную папку.

  • Задаем права для kubeconfig на нашего пользователя и группу.

  • Загружаем в bashrc строчку которая будет задавать kubeconfig по умолчанию для kubectl при каждом входе в систему.

  • И смотрим готов ли наш кластер с одной нодой.

Все управление кластером осущетвляется через утилиту kubeclt

И так буквально пару действий и у нас есть есть готовый кластер k3s почти со всеми преимуществами kubernetes готовый к работе.

Запускаем ARK server

И так давайте для тех кто не сильно погружен в специфику kubernetes я обозначу четыре сущности с которыми мы будем работать:

Pods - Это абстрактный объект Kubernetes, представляющий собой «обертку» для одного или группы контейнеров. Контейнеры в поде запускаются и работают вместе, имеют общие сетевые ресурсы и хранилище. Kubernetes не управляет контейнерами напрямую, он собирает их в поды и работает с ими.

Deployments - Это ресурс предназначенный для для развертывания приложений и их обновления декларативным образом.

PVC - Это запрос на создание постоянного хранилища (Если по очень простому и для данной задачи нам этого понимания более чем достаточно. Вообще отмечу еще что PVC обращается к PV. В kubernetes все контейнеры что запускаются не имеют своего постоянного хранилища, то есть если он упадет всё данные пропадут, для этого нам и нужен будет PVC).

StorageClass - позволяет описать классы хранения, которые предлагают хранилища. (в k3s уже есть по умолчанию это rancher.io/local-path).

namespace - пространство в котором мы запускаем поды, загружаем конфиги и в общем ведем всю свою деятельность (удобно для разделения разных проектов/приложений в одном кластере).

  1. Начнем с того что создадим namespace

kubectl create ns ark
  1. Далее нам нужно составить манифесты PVC в которых мы будем хранить данные файлы сервера ARK, а это в районе 20 GB и файлы состоянии сервера не более 1 GB.

Всем манифесты для kubernetes описываются в виде yaml, требуется внимательно следить за пробелами.

  1. И так откроем редактор.

vi ark-pvc.yaml
  1. И впишем

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ark-server
  namespace: ark
spec:
  storageClassName: local-path
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 30G

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ark-save
  namespace: ark
spec:
  storageClassName: local-path
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10G

В данном манифесте мы описали сразу два PVC, для нас имеет значение сейчас такие значения как:

  • name - имя нашего PVC

  • kind - описание того что будем заводить в kubernetes

  • spec - под ним описываем более детально нашу сущность

  • namespace - наше пространство

  • storageClassName - local-path по умолчанию есть в k3s, если будете использовать не k3s то нужно будет заменить на тот что есть у вас (или можно смонтировать папку с хоста)

  • storage - размер PVC

  1. Далее применим наш манифест

kubeclt apply -f ark-pvc.yaml

Кстати чтобы удалить то что мы создали через манифест необходимо выполнить kubeclt delete -f ark-pvc.yaml

После того как мы подготовили PVC можем начинать подготовку и запуск нашего deployment.

  1. Подготовим наш yaml

vi deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ark-deploy
  namespace: ark
  labels:
    app: ARK
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ARK
  template:
    metadata:
      labels:
        app: ARK
    spec:
      containers:
      - name: ark-server
        image: hermsi/ark-server
        resources:
          requests:
            memory: "500Mi"
            cpu: "250m"
          limits:
            memory: "5000i"
            cpu: "2000m"
        volumeMounts:
          - mountPath: /home/steam/ARK-Backups
            name: ark-save
          - mountPath: /app
            name: ark-server
        env:
        - name: SESSION_NAME
          value: ark_world
        - name: SERVER_MAP
          value: TheIsland
        - name: SERVER_PASSWORD
          value: iAmSuperman
        - name: ADMIN_PASSWORD
          value: iAmSuperman
        - name: MAX_PLAYERS
          value: "20"
        ports:
          - name: server-list
            containerPort: 27015
            protocol: UDP
          - name: game-client
            containerPort: 7777
            protocol: UDP
          - name: udp-socket
            containerPort: 7778
            protocol: UDP
          - name: rcon
            containerPort: 27020
            protocol: UDP
      volumes:
      - name: ark-save
        persistentVolumeClaim:
          claimName: ark-save
      - name: ark-server
        persistentVolumeClaim:
          claimName: ark-server
  1. Давайте тут тоже опишем дополнительные моменты:

  • spec - под первым мы описываем параметры deployment, по втором мы описываем работу нашего pod.

  • containers - описываем с какими параметрами будет подниматься наш контейнер.

  • image - какой образ нужно будет использовать.

  • selector - задаем по ним лейбл который нам понадобится дальше.

  • resources - описываем сколько выделяем фиксированно ресурсов для пода и сколько максимум он может забрать, то есть его лимиты и реквесты.

  • volumeMounts - прописываем куда монтировать наши PVC.

  • volumes - объявляем наши PVC.

  • env - тут задаем переменные для нашего приложения (сервера ark), в нашем случае это пароли, карты, имя сервера.

  • port - указываем какие порты открыть из контейнеры.

Замечу что ark server весьма прожорливый, когда мы запускаем в первый раз он потребляет в районе 3 GB. Мой сервер с 1800 днями уже 7 GB

Вот что пишут в интернете:

8GB RAM
20GB Disk Space Minimum (50-75GB Recommended)
2 CPU Cores @ 3.0GHz+ (For 10-15 players)
64 Bit Windows or Linux OS (CentOS Linux Recommended for a standalone server)
A reliable network connection, 100Mbps+ recommended

  1. После применим его командой.

kubeclt apply -f deployment.yaml

И так после того как мы применили все манифесты, kubectl должен ответить что все было создано и нет проблем

Кстати мы можем проверить что всё корректно создалось

kubectl get pvc -n ark

kubectl get deployment -n ark

  1. Далее следует проверить что наш под запустился и не падает.

kubeclt get pods -n ark

Вывод должен быть примерно таким

  1. Теперь проверим логи пода чтобы понять что нет проблем и идёт подготовка сервера.

kubeclt logs <<name-pod>> -n ark

По выводу можно понять что всё работает и наш сервер ark начал загружать файлы, остается только подождать.

metalLB

Пока загружается наш сервер самое время озаботится тем как мы будет получать доступ к серверу. Для этого нам нужен service который будет перенаправлять трафик с пода наружу и наоборот.

Собственно он так и называется service, через него мы и можем перенаправить трафик наружу, и так service бывает:

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

NodePort - открывает определенный порт на всех узлах в кластере, и любой трафик, отправляемый на этот порт, перенаправляется в службу. Доступ к службе невозможен с IP-адреса кластера. 

LoadBalancer — это стандартный способ предоставления службы Kubernetes извне, чтобы к ней можно было получить доступ через Интернет. Если вы используете Google Kubernetes Engine (GKE), это создает балансировщик сетевой нагрузки с одним IP-адресом, к которому могут получить доступ внешние пользователи, а затем они перенаправляются на соответствующий узел в вашем кластере Kubernetes. Доступ к LoadBalancer можно получить так же, как к ClusterIP или NodePort.

То есть получается нам тут подходит только два варианты NodePort и LoadBalancer, но NodePort ввиду того что может использовать порты только c 30,000 по 32,767 не очень удобен в эксплуатации, остается только LoadBalancer, однако данная услуга поставляется обычно в облаке.

И тут на помощь приходит metalLB! Мы развернем дома свой LoadBalancer, направим его на наш домашний роутер и наш под будет прям с него получать свой IP. Нам не придется мучаться с NodePort, пробросом портов и мы сможем сделать всё это красиво.

  1. И так, шаг первый нужно установить helm:

Helm — это средство упаковки с открытым исходным кодом, которое помогает установить приложения Kubernetes и управлять их жизненным циклом.

Если упросить благодаря ему можно шаблонизировать наши манифесты таким образом чтобы из одних и тех же манифестов можно поднять разные инстансы приложения, достаточно в одном файле поменять значения или при установки helm указать новое имя и у нас уже отдельно приложение со своими сущностями.

Кстати если на первом шаге не был отключен встроенный servicelb то будет конфликт и MetalLB не будет работать, тут описано как его отключить если вы не отключили его ранее.

Ставим helm

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

Взято отсюда https://helm.sh/docs/intro/install/

  1. Далее клонируем репозиторий и запускаем helm с metalLB и запускаем.

git clone https://github.com/general-rj45/helm-ark-survival-evolved.git
helm install metallb helm-ark-survival-evolved/charts/metallb/ --create-namespace --namespace metallb

После выполнения команды мы увидим что helm применился и никаких ошибок нет.

  1. Проверим что все поды metallb запустились и нет рестартов.

  1. Теперь нужно добавить из какого пула адресов будет назначаться ip для наших подов и добавить еще один конфиг для MetalLB.

vi metallb-IPAddressPool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb
spec:
  addresses:
  - 192.168.1.100-192.168.1.200

---

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb
  • IPAddressPool - задаем диапазон ip для подов.

  • addresses - указываем диапазон ip.

  • L2Advertisement - для нас сейчас достаточно просто запустить, чтобы более конкретно погрузиться что это и для чего стоит почитать на офф сайте.

  1. Применим манифест.

kubectl apply -f metallb-IPAddressPool.yaml
  1. Теперь нам нужно создать service типа loadbalancer.

vi service-ark.yaml
apiVersion: v1
kind: Service
metadata:
  name: ark-service
  namespace: ark
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.183
  selector:
    app: ARK
  ports:
    - port: 27015
      targetPort: 27015
      protocol: UDP
      name: server-list
      # Port for connections from ARK game client
    - port: 7777
      targetPort: 7777
      protocol: UDP
      name: game-client
      # Raw UDP socket port (always Game client port +1)
    - port: 7778
      targetPort: 7778
      protocol: UDP
      name: always-game-client
      # RCON management port
    - port: 27020
      targetPort: 27020
      protocol: UDP
      name: rcon-management-port
  • type - тут задается тип service.

  • port - порт который будет выведен наружу.

  • targetPort - порт пода который будет перенаправляться.

  • protocol - задаем протокол tcp или udp

  • selector - тут указывается лейб по которому service сможет понять к какому поду подключить и перенаправлять трафик.

  1. Применяем манифест

kubeclt apply -f service-ark.yaml
  1. Проверим что наш service успешно создался.

kubeclt get svc -n ark

Отлично! Наш сервис создан и готов к работе.

Последние шаги.

  1. Проверяем закончил ли подготовку сервера наш под (у меня заняло часа два, медленный HDD, может быть еще из за CPU).

Из вывода мы видим что сервер успешно запущен и готов принимать первых игроков.

Самое время подключиться! Добавляем в избранное через стим.

Вуаля!

Заходим на сервер (возможно ошибка при подключении, нужно через ark повторно подключиться просто).

Поздравляю! Теперь у вас есть свой сервер ark в kubernetes где вы может играть с друзьями! Или в одиночестве но с осознанием того что это ваш сервер =)

Итог

Я думал что это будет инструкция на одну страничку но вышло, длиннее.....

И так чем это лучше и проще чем просто взять ВМ и поставить туда сервер ark без всех эти надстроек?

  • Проще поднимать еще дополнительные серверы ark, достаточно просто поменять имена, ip в манифестах и применить.

  • Мы можем удобно отслеживать состояние pod в котором работает наше приложение и делать простую аналитику по потреблению памяти, цпу, сети, диска.

  • Удобно читать логи.

  • В случае сбоя работы приложения kubernetes просто перезапустит наш pod и никаких проблем не будет (почти не будет).

  • Наше приложение находится в изолированной среде, а это значит если мы будем делать какие либо изменения на хосте или внутри контейнера они не будут влиять друг на друга (почти не будут).

  • Всё что мы создаем, описывается декларативно в манифестах, то есть один раз написав, потом можно будет переиспользовать много раз или перенести на другой хост и поднять уже там.

  • Подход с декларативным описанием манифестов позволяет удобно менять настройки сервера, к примеру хотите поменять имя сервера, просто изменили его в манифесте и применили, даже не обязательно с того хоста где стоит наш kubernetes. В случае с ВМ нам нужно будет руками подключаться, менять значение, перезапускать скриптами или руками, в общем не очень удобно.

Всё что я описал это моя реализация, которая удобна мне. Может для других такие методы будут избыточны и бессмысленны.

Вообще мне очень нравится концепция контейнеров, ведь она нас освобождает от многих проблем, к примеру что мы минимизируем проблемы с зависимостями, так как каждое приложение живет в своём контейнере и не мешает другим, а так все это было бы в одном месте и создавало хаос, в котором сложно может быть найти виновника тормозов.

Спасибо что дочитали мой первый пост, надеюсь он будет вам полезен или просто интересен. Планирую в будущем еще писать всякие интересности, если дойду.

Tags:
Hubs:
Total votes 10: ↑9 and ↓1+11
Comments8

Articles