Предыстория
Всем привет! Я работаю DevOps-MLops инженером и мне нравится постоянно что-то новое изучать и пытаться применять эти технологии везде где только можно.
Как-то играя в ark я захотел играть на своем сервере вместе с друзьями и как раз под это дело у меня есть свой домашний сервер и выделенный ip, характеристик сервера более чем достаточно, в моем случае это ryzen 7 1700x, 32Gb RAM и 1500Gb дисковой памяти.
Как основу я взял контейнернизацию lxc и настроил внутри ark сервер по первой инструкции что нашёл в интернете, настроил service для ark чтобы руками его не запускать все время и в целом месяца 3 все работало ок, потом по неизвестной причине мой ark сервер стал грузить всё что-то постоянно и вообще стал вести себя странно, сидеть изучать что не так у меня не было особо желания, да и времени, поэтому я подумал а не лучше мне попробовать перенести сервер в более что-то удобное для обслуживания нежели держать его где-то в отдельной среде куда нужно еще подключаться, делать кучу действий чтобы произвести анализ.
Приступим!
И так чтобы сделать красиво (по своему мнению), я взял:
-
k3s — скажем так это как ванильный k8s только из него вырезали около 1000 строчек кода как заявляют разработчики, кстати он построен на containerD.
-
mettllb — это инструмент позволяющий нам создать свой 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.
-
Открываем сайт https://k3s.io, на нем нам говорят чтобы это не займет много времени.
-
Копируем команду и дописываем параметр который понадобится нам потом дальше, выполняем.
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --disable servicelb" sh -
-
Далее всего менее 30 секунд и кластер готов.
-
Прописываем sudo kubectl get nodes и видим что наш кластер готов!
-
Далее, чтобы мы могли работать удобно из под своей УЗ нам требуется скопировать 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 — пространство в котором мы запускаем поды, загружаем конфиги и в общем ведем всю свою деятельность (удобно для разделения разных проектов/приложений в одном кластере).
-
Начнем с того что создадим namespace
kubectl create ns ark
-
Далее нам нужно составить манифесты PVC в которых мы будем хранить данные файлы сервера ARK, а это в районе 20 GB и файлы состоянии сервера не более 1 GB.
Всем манифесты для kubernetes описываются в виде yaml, требуется внимательно следить за пробелами.
-
И так откроем редактор.
vi ark-pvc.yaml
-
И впишем
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
-
Далее применим наш манифест
kubeclt apply -f ark-pvc.yaml
Кстати чтобы удалить то что мы создали через манифест необходимо выполнить
kubeclt delete -f ark-pvc.yaml
После того как мы подготовили PVC можем начинать подготовку и запуск нашего deployment.
-
Подготовим наш 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
-
Давайте тут тоже опишем дополнительные моменты:
-
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
-
После применим его командой.
kubeclt apply -f deployment.yaml
И так после того как мы применили все манифесты, kubectl должен ответить что все было создано и нет проблем
Кстати мы можем проверить что всё корректно создалос.
kubectl get pvc -n ark
kubectl get deployment -n ark
-
Далее следует проверить что наш под запустился и не падает.
kubeclt get pods -n ark
Вывод должен быть примерно таким
-
Теперь проверим логи пода чтобы понять что нет проблем и идёт подготовка сервера.
kubeclt logs <> -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, пробросом портов и мы сможем сделать всё это красиво.
-
И так, шаг первый нужно установить 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/
-
Далее клонируем репозиторий и запускаем 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 применился и никаких ошибок нет.
-
Проверим что все поды metallb запустились и нет рестартов.
-
Теперь нужно добавить из какого пула адресов будет назначаться 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 — для нас сейчас достаточно просто запустить, чтобы более конкретно погрузиться что это и для чего стоит почитать на офф сайте.
-
Применим манифест.
kubectl apply -f metallb-IPAddressPool.yaml
-
Теперь нам нужно создать 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 сможет понять к какому поду подключить и перенаправлять трафик.
-
Применяем манифест
kubeclt apply -f service-ark.yaml
-
Проверим что наш service успешно создался.
kubeclt get svc -n ark
Отлично! Наш сервис создан и готов к работе.
Последние шаги.
-
Проверяем закончил ли подготовку сервера наш под (у меня заняло часа два, медленный HDD, может быть еще из за CPU).
Из вывода мы видим что сервер успешно запущен и готов принимать первых игроков.
Самое время подключиться! Добавляем в избранное через стим.
Вуаля!
Заходим на сервер (возможно ошибка при подключении, нужно через ark повторно подключиться просто).
Поздравляю! Теперь у вас есть свой сервер ark в kubernetes где вы может играть с друзьями! Или в одиночестве но с осознанием того что это ваш сервер =)
Итог
Я думал что это будет инструкция на одну страничку но вышло, длиннее…..
И так чем это лучше и проще чем просто взять ВМ и поставить туда сервер ark без всех эти надстроек?
-
Проще поднимать еще дополнительные серверы ark, достаточно просто поменять имена, ip в манифестах и применить.
-
Мы можем удобно отслеживать состояние pod в котором работает наше приложение и делать простую аналитику по потреблению памяти, цпу, сети, диска.
-
Удобно читать логи.
-
В случае сбоя работы приложения kubernetes просто перезапустит наш pod и никаких проблем не будет (почти не будет).
-
Наше приложение находится в изолированной среде, а это значит если мы будем делать какие либо изменения на хосте или внутри контейнера они не будут влиять друг на друга (почти не будут).
-
Всё что мы создаем, описывается декларативно в манифестах, то есть один раз написав, потом можно будет переиспользовать много раз или перенести на другой хост и поднять уже там.
-
Подход с декларативным описанием манифестов позволяет удобно менять настройки сервера, к примеру хотите поменять имя сервера, просто изменили его в манифесте и применили, даже не обязательно с того хоста где стоит наш kubernetes. В случае с ВМ нам нужно будет руками подключаться, менять значение, перезапускать скриптами или руками, в общем не очень удобно.
Всё что я описал это моя реализация, которая удобна мне. Может для других такие методы будут избыточны и бессмысленны.
Вообще мне очень нравится концепция контейнеров, ведь она нас освобождает от многих проблем, к примеру что мы минимизируем проблемы с зависимостями, так как каждое приложение живет в своём контейнере и не мешает другим, а так все это было бы в одном месте и создавало хаос, в котором сложно может быть найти виновника тормозов.
Спасибо что дочитали мой первый пост, надеюсь он будет вам полезен или просто интересен. Планирую в будущем еще писать всякие интересности, если дойду.