32 KiB
title, description, category, position
title | description | category | position |
---|---|---|---|
Начало работы с kubernetes | Как там это всё разворачивать в двух словах. | DevOps | 4 |
Проблема в изучении кубернетес
Многие люди, кто задавались вопросом "Как начать работать с кубернетес?", сталкивались с тем, что документация крайне большая сложная и нет нормального описания как завернуть маленький проект из одного контейнра в свой кластер или как развернуть сам кластер без боли.
А всё потому что вся документация нацелена на большие
production-ready системы с большим rps и тому подобным.
В данной статье я попробую исправить это вселенское
недопонимание используя k3s, свой комплюктер и немного знаний по кодингу.
Что такое кубернетес и почему это лучше докера
Многие ребята, кто хорошо знаком с докером и его возможностями, могут задаваться таким вопросом. Для тех кто в танке, напомню, что докер имеет вариант запуска в режиме кластера. Этот функционал называется docker swarm. В целом, swarm отдалённо напоминает kubernetes, так как в этом режиме докер худо-бедно умеет автоскейлится и запускаться в кластере, но это всё равно немного не то.
Также замечу, что кубер активно развивается и поддерживается. огромным количество компаний. А вот docker swarm уже по-немногу умирает и документация по нему не то чтобы супер хороша.По большей части кубернетес это система, которая будет управлять вашими приложениями, следить за их состоянием и помогать вам в их конфигурации. Кубер умеет очень много, поэтому все интересные способности я в этой статье не смогу осветить, но самую базу попробую рассказать.
Из чего состоит кубернетес
Так как я в этой статье хотел затронуть совсем базовые и практические вещи, то рассматривать мы будем только самые часто используемые компоненты.
- Container
- Pod
- Deployment
- Service
- Ingress
- Namespace
- Secret
- ConfigMap
А теперь рассмотрим каждый ресурс немного поподробнее.
Container
Контейнеры не являются чем-то специфичным для кубернетес.
С контейнерами вы можете быть знакомы из кучи других систем.
В контексте кубера они не обладают никакими дополнительными
свойствами. Это ровно то же, что и контейнеры containerd
или те, с которыми вы возились с докером. Ничего нового.
Собираются контейнеры для кубернетеса ровно тем же образом, что и для докера.
Важная ремарка. Кубер, начиная с 2021 года, не поддерживает
докер как бэкенд. Теперь он будет общаться с
containerd напрямую. Это значит, что перед использованием
контейнеров собранных на локальной машине надо будет импортировать их
в containerd
используя ctr image import
.
Как импортировать образы почитать можно в этой статье.
Pod
Поды - это логически связанные группы контейнеров. Это самая базовая еденица кубернетеса.
В поде находится от одного до множества контейнеров.
Интересная особенность пода в том, что все контейнеры
делят один сетевой адрес. Другими словами,
если у вас один из контейнеров открыл порт 3000
,
то другие контейнеры из пода этот порт использовать не смогут.
То есть, если вы хотите логически связанные приложения
поместить в под, то они могут ходить друг к другу через лупбек
адреса. Такие как localhost
или 127.0.0.1
.
Deployment
Поды это круто, но есть одно но. Данные объекты неизменяемые и сам под скейлить вручную занятие сомнительное.
Конечно, никто не мешает создать вам вручную ReplicaSet и самому крутить там нужные значения. Но в среднем вам с этим возиться не очень то хочется.
Deployment нужен именно для описания подов и создания некоторых ресурсов, нужных для скейлинга. Также с помощью деплойментов можно делать откаты приложения через механиз роллбеков. Я это рассматривать не буду. В этой статье только база.
Service
Сервис - это ресурс, с помощью которого поды могут общаться между собой. По факту сервис описывает, какие порты пода открыты и перенаправляет трафик на них.
Представьте себе под, который просто проксирует весь трафик с одного порта на какой-нибудь порт пода. Это и есть Service.
Выглядит сервис примерно так как показано на кртинке выше. Он направляет весь входной трафик с указанного порта на порты подов.
Также сервис выступает как балансировщик.
Если вы хотите сделать запрос от одного пода до дргого, внутри кластера, то вам придётся использовать сервис.Если вы попробуете сделать запрос напрямую по IP пода, то у вас, конечно же, получится, но это довольно странная идея из-за того, что при пересоздании пода у него может обновится IP внутри кластера. Поэтому лучше использовать сервисы. Также они потребуются для ингресса.
Ingress
Ингресс - это сервис описывающий куда пускать трафик, который поступает снаружи кластера.
Принцип работы ингресса следующий:
Вы указываете хост ингресса и различные пути. В зависимости от входящего запроса ингресс выбирает в какой сервис направить запрос и в какой порт.
Настройка ингресса достаточно гибкая и я не думаю, что вы можете столкнуться с какими либо проблемами.
Namespace
Неймспейсы это логические разделители уровня доступа.
Я предпочитаю использовать разные неймспейсы под различные логические группы приложений.
Например, в моём кластере есть отдельные неймспейсы для каждого приложения.
Скажем, у меня есть проект, в котором есть база данных и веб сервер. Я держу их в одном неймспейсе, который называется также, как и приложени.
А вот телеграм боты -- приложения достаточно лёгкие. Поэтому у меня есть неймспейс со всеми телеграм ботами.
Эта штука позволяет просто лучше организовать свой кубернетес кластер.
Также неймспейсы очень полезны для ограничивания возможностей конкретного пользователя. Например, вы можете создать правило, которое будет разрешать пользователю смотреть на поды в каком-то неймспейсе, но при этом ему нельзя будет что либо изменять.
Secret и ConfigMap
Данные ресурсы нужны только чтобы хранить внутри кластера какую-нибудь информацию.
Например вы можете сохранить в ConfigMap переменные среды и потом использовать их в подах в одном неймспейсе.
В секреты обычно кидают сертификаты или какие-нибудь ключи. Но так как секреты не особо секретные принято использовать Vault. Но так как эта статья затрагивает только основы рассматривать развертку и настройку Vault мы не будем, ну и также HashiCorp всё довольно подробно расписали сами.
Как развернуть k8s у себя
Для локального кубера есть пара вариантов.
- k3s (Недоступен под Windows)
- minikube
На первый взгляд minikube может показаться лучшим вариантом. И он действительно хорош тем, что его легко развернуть и почистить после своих экспериментов. Однако, там есть проблемы с ингрессами. По факту они не работают и там надо окольными путями получать адреса.
k3s - это легковесная production-ready реализация k8s. Ingress у него работают отлично, поэтому я буду использовать его.
Я не буду зацикливаться на установке minikube
, так как
он прост в установке и первоначальной настройке. Почитать подробнее можно в
официальном гайде от minikube.
С k3s
всё немного посложнее, но тоже достаточно просто, если немного разобраться. Первоначальная установка описана в официальной доке k3s.
Подключение кластера
После установки kubectl
в домашней дериктории должен был быть
сгенерирован файл .kube/config
. Этот файл содержит данные для
подключения к различным кластерам. minikube
Сам добавляет
ключи для подключения к .kube/config. K3S
никак не изменяет .kube/config
, поэтому надо будет это сделать вручную.
Для того, чтобы это сделать сначала разберёмся как выглядит конфиг.
apiVersion: v1
kind: Config
preferences: {}
# Массив кластеров.
# Каждый элемент -- данные для подключения
# Тут также есть названия для каждого кластера.
clusters:
- name: hyper
cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.1.55:6443
- name: k3s-local
cluster:
certificate-authority-data: DATA+OMITTED
server: https://127.0.0.1:6443
# Массив данных пользователя.
# Тут указаны пользователи с
# различными сертификатами.
# Обычно для разных серверов у вас будут
# Различные данные для входа.
users:
- name: hyper-s3rius
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- name: k3s-user
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
# Массив контекстов.
# Контекст - это связующее звено
# между кластероами и пользователями.
contexts:
- context:
cluster: hyper
user: hyper-s3rius
name: hyper
- context:
cluster: k3s-local
user: k3s-user
name: k3s
# Текущий контекст указывает какой
# контекст использовать по умолчанию.
current-context: "k3s"
Для работы с кубером из командной строки
можно использовать kubectl
. Чтобы сменить контекст в любой команде вы
можете передать параметр --context $CONTEXT_NAME
, где $CONTEXT_NAME
это название контекста.
Чтобы достать данные для подключения к k3s
надо посмотреть его конфиг
и скопировать данные. Либо выставить переменную среды KUBECONFIG
, которая будет
указывать конфиг k3s. Конфиг подключения k3s
лежит в файле /etc/rancher/k3s/k3s.yaml
.
Можете выполнить команду, которая будет просить kubectl использовать указанный конфиг:
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
Либо скопируйте нужные данные для подключения себе в .kube/config
.
После настройки выполните команду и проверьте что вам вернулось что-то подобное.
$ kubectl --context "my-context" get pods
No resources found in default namespace.
Это значит, что никаких ресурсов пока в кластере нет. Это мы исправим чуть позже. Пока что можно сказать, что подключение прошло успешно. Если же у вас выпадает ошибка, например такая:
The connection to the server localhost:8080 was refused - did you specify the right host or port?
То либо запустите кластер
sudo systemctl start k3s.service
Либо у вас неверные данные для входа.
Перепроверьте свой .kube/config
.
Мониторинг кластера
Для просмотра ресурсов и управления кластером используется kubectl
.
Но для того, чтобы с ней работать нужно получше понять, что вообще в кластере есть.
Для этого я советую использовать Lens. Это крайне удобный интерфейс для управления своим кластером. Также там есть очень клёвая настройка, которая сама включит мониторинг потребления памяти и процессора для всех подов и кластера в общем.
На локальной машине это не имеет смысла, а вот в проде было бы очень полезно.
Выглядит Lens примерно так:
Для изучения крайне настоятельно рекомендую настроить Lens.
Ваше первое приложение в кластере.
Вы можете следовать статье, а можете подсмотреть весь код в репозитории.
Сервер
Давайте создадим своё первое приложение.
Для этого я буду использовать express.js, так как он крайне популярен и прост.
Для этого сначала напишем сам сервер. Я буду использовать yarn, но можете и npm, сути не поменяет.
Создайте какую-нибудь папку, где вы будете эксперементировать, откройте в ней ваш любимый тектовый редактор и просто копируйте файлы ниже.
{
"name": "req_counter",
"version": "1.0.0",
// Указан модуль,
// чтобы использовать нормальны импорты,
// а не require.
"type": "module",
"license": "MIT",
"scripts": {
// Скрипт старта сервера.
"server": "node index.js"
},
// Зависимости проекта.
"dependencies": {
"express": "^4.17.1"
}
}
И само приложение
import express from "express";
import { hostname } from "os";
import { argv, exit } from "process";
// Серверное приложение.
const app = express();
// Глобальный счётчик входящих запросов.
let requests = 0;
// Обработка входящего запроса.
app.get("*", (req, res) => {
// Увеличиваем глобальный счётчик запросов.
requests += 1;
// Логгируем входящий запрос.
console.log(`${req.method} ${req.url}`);
// Возвращаем информацию о текущем хосте и количестве запросов.
res.json({
requests: requests,
hostname: hostname(),
});
});
// Аргументы командной строки.
// Напрмиер yarn run server 127.0.0.1 8000
const args = argv.slice(2);
// Если передано неверное количество аргументов.
if (args.length != 2) {
console.error("Usage: yarn run server {host} {port}");
exit(1);
}
// Простейший "парсинг" аргументов командной строки.
const host = args[0];
const port = args[1];
// Старт сервера.
app.listen(port, host, () => {
console.log(`Server listening at http://${host}:${port}`);
});
Это всё. Сервер готов.
Протестируем запуск сервера, выполнив команду ниже и открыв в своём любимом браузере http://localhost:8080.
yarn run server 0.0.0.0 8080
У меня всё работает и успешно отдаётся нужная информация.
{
"requests": 1,
"hostname": "s3rius-pc"
}
Docker образ
Теперь создадим докер образ приложения.
Добавим .dockerignore
, чтобы игнорировать ненужные файлы во время сборки образа.
node_modules/
И добавим в проект Dockerfile
для описания самого процесса сборки.
FROM node:17-alpine
WORKDIR /app
COPY . /app/
RUN yarn install
CMD [ "yarn", "run", "server", "0.0.0.0", "8000"]
Давайте соберём и запустим проект в контейнере.
docker build --tag="s3rius/req-counter-express:latest" .
docker run --rm -it -p 3400:8000 "s3rius/req-counter-express:latest"
Можете проверить, что приложение работает успешно, открыв в браузере http://localhost:3400.
У меня в ответ пришло то же сообщение. Только, как можно заметить,
hostname
поменялся. На самом деле контейнеры используют свои hostname
отличные от hostname
локальной машины.
{
"requests": 10,
"hostname": "8f23adadc640"
}
Вариантов как положить это приложение в K8S несколько.
- Вы можете запушить собранное приложение в Docker HUB и использовать его.
- Можете использовать мой образ
s3rius/req-counter-express:latest
- Сохранить собранный образ как tar файл и импортировать его в containerd напрямую. Как это сделать почитать можно в этой статье.
Деплой в k8s
Создайте папку kube
в папке проекта и теперь мы будем работать в ней.
Все ресурсы будут описаны yaml-файлами.
---
apiVersion: apps/v1
kind: Deployment
# метаданные самого деплоймента.
metadata:
name: req-counter-deployment
spec:
# Количество реплик пода.
replicas: 1
# Селектор, который выбирает
# какие поды принадлежат этому деплойменту.
selector:
matchLabels:
app: req-counter
# Шаблон пода,
# который будет использоваться при
# маштабировании.
template:
# Метаданные пода.
# тут обычно помещаются лейблы,
# с помощью которых деплоймент идентифицирует
# свои поды.
metadata:
labels:
app: req-counter
spec:
# Масссив контейнеров
containers:
# Название контейнера внутри пода.
- name: req-counter-app
# Образ приложения.
image: s3rius/req-counter-express:latest
# Ресурсы требуемые для работы приложения.
resources:
# Минимальное количество ресурсов,
# которое кластер гарантированно предоставит приложению.
# Также данные значения используются для того,
# чтобы выяснить на какой ноде запускать приложение.
requests:
memory: "50Mi"
cpu: "30m"
# Максимально возможные значения приложения.
# Если приложение выйдет за лимиты,
# то кубер убьёт приложение.
limits:
memory: "128Mi"
cpu: "100m"
# Порты на которых открыты приложения.
ports:
- containerPort: 8000
protocol: TCP
Теперь опишем сервис для управления трафиком.
---
apiVersion: v1
kind: Service
# Метадата сервиса.
metadata:
name: req-counter-service
spec:
# Селектор подов,
# которым будет пускаться трафик.
# Трафик может идти в любой под,
# который матчит данному селектору.
selector:
app: req-counter
# Порты для проксирования соединений.
ports:
# Порт сервиса
- port: 80
# Порт пода, куда будет идти трафик дальше.
targetPort: 8000
И в последнюю очередь опишем наш ингрес.
---
apiVersion: networking.k8s.io/v1
kind: Ingress
# Метаданные ингресса.
metadata:
name: req-counter-ingress
labels:
name: req-counter-ingress
spec:
# Правила роутинга.
rules:
# Требуемый хост.
- host: req-counter.local
http:
paths:
# Тип пути Prefix значит,
# что все запросы, которые начинаются c
# ${path} будут матчится тут.
- pathType: Prefix
# Сам путь.
path: "/"
backend:
service:
# Название нашего сервиса.
name: req-counter-service
# Порт сервиса, куда перенаправлять входящий трафик.
port:
number: 80
Перед тем, как создать все описанные ресурсы создадим неймспейс.
$ kubectl --context k3s create namespace req-couter-ns
namespace/req-couter-ns created
После того, как неймспейс создан самое время сделать последний штрих.
$ kubectl --context k3s --namespace req-couter-ns apply -f ./kube/
deployment.apps/req-counter-deployment created
ingress.networking.k8s.io/req-counter-ingress created
service/req-counter-service created
Готово. Теперь вы можете зайти в lens, выбрать свой кластер из списка и посмотреть как там поживает ваше приложение.
Также не забудьте указать неймспейс, в который вы деплоили приложение.
Выглядит это чудо примерно так:
Также можно использовать команду и вывести всё себе в терминал.
$ kubectl --context k3s --namespace req-couter-ns get all
NAME READY STATUS RESTARTS AGE
pod/req-counter-deployment-764476db97-dt2tc 1/1 Running 0 8m11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/req-counter-service ClusterIP 10.43.50.23 <none> 80/TCP 8m11s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/req-counter-deployment 1/1 1 1 8m11s
NAME DESIRED CURRENT READY AGE
replicaset.apps/req-counter-deployment-764476db97 1 1 1 8m11s
Маштабирование
В рамках демонстрации давате поменяем значение
replicas
в нашем файле деплоймента до 3
.
...
spec:
# Количество реплик пода.
replicas: 3
...
После изменения просто ещё раз вызовите команду apply.
$ kubectl --context k3s --namespace req-couter-ns apply -f ./kube/
deployment.apps/req-counter-deployment configured
ingress.networking.k8s.io/req-counter-ingress unchanged
service/req-counter-service unchanged
Как можно видеть, изменился только наш Deployment
.
Остальные ресурсы остались нетронутыми.
Давайте посмотрим на поды в нашем неймспейсе.
$ kubectl --context k3s --namespace req-couter-ns get pods
NAME READY STATUS RESTARTS AGE
req-counter-deployment-764476db97-dt2tc 1/1 Running 0 13m
req-counter-deployment-764476db97-tdjrb 1/1 Running 0 69s
req-counter-deployment-764476db97-x28fr 1/1 Running 0 69s
Как видно, всё правильно. Теперь у нас 3 пода нашего приложения.
Теперь можно выполнить кучу запросов по адресу http://req-counter.local/ и получить балансировку между подами из коробки, без дополнительных конфигураций.
Если у вас не получается найти адрес. Добавьте данный хост себе в
/etc/hosts
на линуксе или в C:\Windows\System32\drivers\etc\hosts
на windows,
дописав в конец файла следующее:
127.0.0.1 req-couter.local
И теперь вы можете открыть в браузере своё приложение и увидеть, как кубернетес заботливо направляет трафик туда, куда вы хотите.
Как мне очистить мой кластер?
Всё очень просто.
Так как у нас имеются описния ресурсов, то мы можем удалить всё сразу используя команду
$ kubectl --context k3s --namespace req-couter-ns delete -f ./kube/
deployment.apps "req-counter-deployment" deleted
ingress.networking.k8s.io "req-counter-ingress" deleted
service "req-counter-service" deleted
Таким образом k8s удалит все описанные ресурсы из вашего кластера.
До новых встреч.