Пару дней назад в CodeX проводился митап по основам Kubernetes. За полтора часа сложно освоить данную технологию. Решено было взять боевую задачу и получить опыт самостоятельно.
Описание задачи
В CodeX есть проект Codex.Bot – это наш умный помощник, который сообщает в Telegram чате о новых коммитах в репозитории, напоминает о незакрытых задачах, скидывает статистику посещений сайтов и уведомления о новых ошибках.
Проект уже 5 лет работает без существенных изменений кода. Но недавно наша команда наконец завернула его в Docker образ.
Теперь наша задача запустить его в Kubernetes кластере.
Поднимать будем ядро нашего бота. Оно использует следующие сервисы:
- Систему очередей RabbitMQ
- Базу данных MongoDB
- Общение с Telegram через Webhook
Устанавливаем софт
Загрузим следующее ПО:
- minikube – локальный Kubernetes движок
- kubectl – консольный клиент для общения с Kubernetes
- Lens – IDE для удобного мониторинга Kubernetes
- Helm – менеджер пакетов для Kubernetes
По умолчанию в данной статье представлены команды и инструкции для MacOS.
Minikube
В первую очередь нам понадобится сам Kubernetes. Для первого опыта достаточно установить локальный minikube.
curl -LO <https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64>
sudo install minikube-darwin-amd64 /usr/local/bin/minikube
minikube start
Команды для установки на Linux:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
В консоли вы увидите что-то вроде:
😄 minikube v1.25.1 на Darwin 12.0.1
✨ Automatically selected the docker driver. Other choices: hyperkit, virtualbox, ssh
👍 Запускается control plane узел minikube в кластере minikube
🚜 Скачивается базовый образ ...
🔥 Creating docker container (CPUs=2, Memory=2200MB) ...
🐳 Подготавливается Kubernetes v1.23.1 на Docker 20.10.12 ...
▪ kubelet.housekeeping-interval=5m
▪ Generating certificates and keys ...
▪ Booting up control plane ...
Kubectl
Далее нам понадобится kubectl, чтобы выполнять команды в Kubernetes:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
sudo chown root: /usr/local/bin/kubectl
Команды для установки на Linux и других ОС:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Выполним команду
и проверим, что он подхватил запущенную в minikube ноду.kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane,master 106m v1.23.1
Lens
Нам часто нужно будет заглядывать во внутренности кластера. Чтобы не мучиться с различными kubernetes-dashboard, скачаем гораздо более удобный инструмент: Lens. Он позволит нам:
- Фильтровать запущенные ресурсы
- Удалять ресурсы
- Видеть состояние каждого ресурса, их количество и названия
- Смотреть логи каждого контейнера
- Подключаться в контейнер нажатием кнопки и выполнять там команды
- Видеть всю мета-информацию (в том числе переменные окружения) каждого ресурса
После установки достаточно будет запустить программу и найти там minikube кластер. Для удобства его можно запинить в левом меню.
Helm
В Kubernetes есть свой пакетный менеджер Helm, который можно использовать для загрузки готовых зависимостей (RabbitMQ, MongoDB) и для упаковки своего приложения.
Для установки на ОС отличной от MacOS выберите соответствующий релиз (например, замените darwin
на linux
).
- Скачаем готовый релиз https://get.helm.sh/helm-v3.8.0-darwin-amd64.tar.gz
- Распаковываем бинарный файл
tar -zxvf helm-v3.8.0-darwin-amd64.tar.gz
- Устанавливаем его
mv linux-amd64/helm /usr/local/bin/helm
- Проверяем версию
helm version
version.BuildInfo{Version:"v3.8.0", GitCommit:"d14138609b01886f544b2025f5000351c9eb092e", GitTreeState:"clean", GoVersion:"go1.17.5"}
Скачиваем зависимости
Сначала создадим Helm Chart (пакет нашего приложения) с помощью команды:
helm create k8s-codex-bot
В текущей директории появится папка
, заполненная шаблонными файлами, которые мы будем редактировать.k8s-codex-bot
Первым шагом удалим тесты, которые обязательно сломаются, когда мы изменим приложения с дефолтного Nginx на своё.
rm -rf k8s-codex-bot/templates/tests/
Теперь загуглим готовые зависимости (Helm Chars) для MongoDB и RabbitMQ на сайте с зависимостями: https://artifacthub.io/
- https://artifacthub.io/packages/helm/bitnami/rabbitmq
- https://artifacthub.io/packages/helm/bitnami/mongodb
Там можно узнать текущую актуальную версию и общие настройки, которые можно редактировать
Откроем файл
и внесем в него в конец (после k8s-codex-bot/Chart.yml
) информацию о зависимостях:appVersion**: **"1.16.0"
dependencies:
- name: mongodb
version: 11.0.x
repository: https://charts.bitnami.com/bitnami
- name: rabbitmq
version: 8.27.x
repository: https://charts.bitnami.com/bitnami
Зайдите в директорию проекта
и выполните команду k8s-codex-bot
:helm dependency update
> helm dependency update
Update Complete. ⎈Happy Helming!⎈
Saving 2 charts
Downloading mongodb from repo <https://charts.bitnami.com/bitnami>
Downloading rabbitmq from repo <https://charts.bitnami.com/bitnami>
Deleting outdated charts
Helm обновит реестр зависимостей и скачает файлы с чартами в виде
архивов в папку tar.gz
.charts
У различных чартов есть обязательные параметры, которые нужно определить (например, логины и пароли). Заполним их в файле
. Для этого в конец добавим следующие секции:values.yml
mongodb:
auth:
enabled: false
rabbitmq:
auth:
username: "user"
password: "user"
erlangCookie: "hg8+qOWd2/fv0f/fP4Jeghec3YGNLJqNOdW4MRx8"
- Отключили аутентификацию для MongoDB
- Установили логин и пароль доступа к RabbitMQ:
user/user
- Установили случайный мусор в качестве значения
(обязательный параметр)erlangCookie
Названия секций (mongodb, rabbitmq) должны соответствовать значениям поля name из секции dependencies.
Пробный запуск Helm Chart
В Kubernetes все ресурсы удобно логически разделять на разные кучи (namespaces
), чтобы фильтровать их при выводе. Будем работать в рамках namespace’а под названием codex-bot
. Создадим его вручную:
kubectl create ns codex-bot
Теперь достаточно выполнить команду для запуска чарта с именем k8s-codex-bot
в пространстве имён codex-bot
.
helm install k8s-codex-bot ./ -n codex-bot
Изучаем базовый образ
В Lens вы уже сможете увидеть что-то вроде:
Нажмите на контейнер со странным именем, в правом окне найдите кнопку проброса порта (Forward) и нажмите Start.
У вас в браузере откроется страница веб-сервера Nginx. Это потому, что Nginx идет по умолчанию как демонстрационный образ при создании Helm чарта.
Подключаемся к MongoDB
Чтобы подключиться к порту любого контейнера, достаточно прописать команду:
kubectl port-forward service/k8s-codex-bot-mongodb 28015:27017 -n codex-bot
В данном случае мы проксировали порт 27017
контейнера k8s-codex-bot-mongodb
на локальный порт 28015
(подставьте любой случайный незанятый порт). К нему теперь можно подключиться с локального компьютера через MongoDB Compass:
mongodb://localhost:28015/
Подключаемся к RabbitMQ
У RabbitMQ есть своя веб админка. Найдите порт 15672
в Lens и проксируйте его себе в браузер.
В появившемся окне введите логин user
и пароль user
для доступа к панели администрирования RabbitMQ.
Редактируем шаблон приложения
В данной секции опишем:
- Изменение основных настроек приложения в файле
values.yml
- Добавление загрузки переменных окружения в файле
deployment.yml
- Удаление
иlivenessProbe
readinessProbe
- Перечисление переменных окружения в новом файле
env-configmap.yml
- Накатим новые изменения
Основные настройки приложения
Укажем свой Docker image, тег и Docker registry для приложения в файле
.values.yml
image:
repository: ghcr.io/codex-team/codex-bot-core
pullPolicy: Always
tag: "stage"
нам понадобится, чтобы Helm после каждого запуска проверял и подхватывал свежий образ приложения.pullPolicy: Always
Заполним желаемое название приложения в fullnameOverride: "core-bot"
Выберем порт, на котором будет слушать приложение:
service:
type: ClusterIP
port: 1337
В файле
указаны секции deployment.yml
и livenessProbe
. Они необходимы для проверки успешности работы контейнера и его перезапуска. Сейчас CodeX.Bot их не поддерживает, поэтому удалим их пока, чтобы не мешали и не перезапускали лишний раз работающий контейнер.readinessProbe
Передача переменных окружения
Любое приложение нужно конфигурировать. CodeX Bot конфигурируется через передачу переменных окружения (env). Хранить мы их будем в сущности
кубернетеса.ConfigMap
Откроем файл deployment.yml
, содержащий шаблон приложения и внесем изменения в секцию
:spec.template.spec.containers
ports:
- name: http
containerPort: 1337
protocol: TCP
envFrom:
- configMapRef:
name: env-configmap
Здесь мы отредактировали порт, на котором слушает приложение, а также добавили указание брать переменные окружения из ConfigMap
с именем
.env-configmap
Создадим файл
в папке env-configmap.yml
и заполним его нужными значениями:templates
apiVersion: v1
kind: ConfigMap
metadata:
name: env-configmap
data:
SERVER_HOST: "0.0.0.0"
SERVER_PORT: "1337"
RABBITMQ_URL: "amqp://user:user@k8s-codex-bot-rabbitmq/"
DATABASE_NAME: default
DATABASE_HOST: k8s-codex-bot-mongodb
DATABASE_PORT: "27017"
TELEGRAM_CALLBACK_ROUTE: /telegram/callback
TELEGRAM_BOT_NAME: "<...>"
TELEGRAM_API_TOKEN: "<...>"
TELEGRAM_API_URL: "https://api.telegram.org/bot"
SLACK_BOT_NAME: ""
SLACK_CLIENT_ID: ""
SLACK_CLIENT_SECRET: ""
Здесь мы пока не будем париться с шифрованием секретов, а напишем все пароли и чувствительные данные в открытом виде. Данный Helm чарт будет располагаться в закрытом репозитории.
Накатываем изменения
Для того, чтобы обновить запущенные образы достаточно выполнить команду:
helm upgrade k8s-codex-bot ./ -n codex-bot
Helm сам разберется, какие файлы изменились, какие контейнеры нужно остановить, а какие перезапустить.
Если вы теперь выберете контейнер с именем
, то сможете увидеть переменные окружения:core-bot-...
В Lens также удобно смотреть логи любого контейнера.
Учимся дебажить
Очень часто возникает ситуация, что контейнер падает без каких-либо логов об ошибках. Если вы не можете разобраться в чем дело, очень удобно зайти по-старинке в bash и запустить все руками, посмотрев истинную причину падения.
Для этого добавим в
в секцию deployment.yaml
spec.template.spec.containers
следующие два значения:
command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]
Теперь контейнер стартует успешно, и вы сможете просто подключиться к нему, нажав кнопку Pod shell
.
Не забудьте убрать
и command
перед тем, как продолжить.args
Telegram Webhook с помощью Ngrok
CodeX Bot использует вебхук для получения сообщений из Telegram. Чтобы протестировать данную связку на локальном компьютере, предлагаем использовать Ngrok.
Первым делом нужно зарегистрироваться на сайте и получить Authtoken
в личном кабинете.
Ngrok – это обычное приложение, завернутое в Docker. Создадим файл
в папке ngrok-deployment.yml
.templates
apiVersion: apps/v1
kind: Deployment
metadata:
name: ngrok
spec:
selector:
matchLabels:
app: ngrok
replicas: 1
template:
metadata:
labels:
app: ngrok
spec:
containers:
- name: ngrok
image: wernight/ngrok
command: ["ngrok"]
args: ["http", "core-bot:1337", "--authtoken", "<...>"]
ports:
- containerPort: 4040
Тут важно указать свой authtoken
в качестве аргумента. Пробрасывать пользователя мы будем на приложение core-bot
. Это имя соответствует полю
из fullnameOverride
.values.yml
Применим изменения при помощи команды helm upgrade k8s-codex-bot ./ -n codex-bot
В Lens можно увидеть новый контейнер с именем ngrok. Пробросим его порт 4040
и скопируем публичный URL для вебхука.
Осталось передать этот параметр приложению.
Для этого добавим в
ключ и значение:values.yml
url: "https://052e-92-100-146-207.ngrok.io/"
А в
в секцию env-configmap.yml
data
:
URL: {{ .Values.url }}
Теперь приложение будет получать вебхук через переменную окружения прямиком из файла values.yml
.
Вручную удалим основное приложение
, нажав в Lens кнопку core-bot
.Delete
Бот сам перезапустится и теперь с ним можно будет пообщаться в Telegram чате.
Выводы
Мы взяли небольшой Docker образ приложения, которое требует MongoDB и RabbitMQ для своей работы и заставили это приложение работать в Kubernetes.
Попутно мы изучили огромный стек технологий и теперь примерно понимаем как это все работает:
- minikube для локального запуска Kubernetes
- kubectl для общения с Kubernetes
- Lens для мониторинга Kubernetes кластера
- Helm для создания собственных чартов и загрузки готовых зависимостей
- Ngrok для пробрасывания локального порта на публичный URL
Мы научились создавать свои Helm чарты, пробрасывать переменные окружения внутрь приложения, шаблонизировать Kubernetes конфигурации, настраивать взаимодействие между контейнерами, пробрасывать порты и многое другое.
Теперь самое время открыть документацию, изучить терминологию и познавать теорию Kubernetes.