From 68dcb8da7454f82254d4da21d8104d13a2c4100f Mon Sep 17 00:00:00 2001 From: Pavel Kirilin Date: Thu, 9 Dec 2021 15:38:19 +0400 Subject: [PATCH] Added k3s page. Signed-off-by: Pavel Kirilin --- content/ru/docker-envs.md | 2 +- content/ru/makefiles.md | 2 +- content/ru/project-start.md | 47 +- content/ru/start-with-k8s.md | 753 +++++++++++++++++++ content/ru/traefik.md | 112 +-- layouts/default.vue | 14 +- nuxt.config.js | 2 +- pages/_slug.vue | 17 +- static/images/k3s_start/ingress-overview.png | Bin 0 -> 24681 bytes static/images/k3s_start/kube_intro.jpg | Bin 0 -> 64210 bytes static/images/k3s_start/kubectl-apply.png | Bin 0 -> 859607 bytes static/images/k3s_start/lens-deployed.png | Bin 0 -> 137320 bytes static/images/k3s_start/lens-example.png | Bin 0 -> 213237 bytes static/images/k3s_start/pods.svg | 484 ++++++++++++ static/images/k3s_start/requests.gif | Bin 0 -> 1164554 bytes static/images/k3s_start/service overview.png | Bin 0 -> 15485 bytes 16 files changed, 1345 insertions(+), 88 deletions(-) create mode 100644 content/ru/start-with-k8s.md create mode 100644 static/images/k3s_start/ingress-overview.png create mode 100644 static/images/k3s_start/kube_intro.jpg create mode 100644 static/images/k3s_start/kubectl-apply.png create mode 100644 static/images/k3s_start/lens-deployed.png create mode 100644 static/images/k3s_start/lens-example.png create mode 100644 static/images/k3s_start/pods.svg create mode 100644 static/images/k3s_start/requests.gif create mode 100644 static/images/k3s_start/service overview.png diff --git a/content/ru/docker-envs.md b/content/ru/docker-envs.md index 80e5b5b..e450755 100644 --- a/content/ru/docker-envs.md +++ b/content/ru/docker-envs.md @@ -1,7 +1,7 @@ --- title: Разделение докера на среды. description: Как работать с несколькими docker-compose. -position: 3 +position: 2 category: DevOps --- diff --git a/content/ru/makefiles.md b/content/ru/makefiles.md index 1a0508f..c9fa26a 100644 --- a/content/ru/makefiles.md +++ b/content/ru/makefiles.md @@ -1,7 +1,7 @@ --- title: Makefiles для чайников. description: Автоматизируем по старинке. -position: 1 +position: 3 category: 'DevOps' --- diff --git a/content/ru/project-start.md b/content/ru/project-start.md index fc51a07..767bc3f 100644 --- a/content/ru/project-start.md +++ b/content/ru/project-start.md @@ -246,25 +246,31 @@ $ wemake-python-styleguide --dev Теперь добавим конфигурационных файлов в корень проекта. Это мои конфигурации, которые я настроил под себя, можешь менять их как хочешь. -`.mypy.ini` для настройки валидации типов. -```ini{}[.mypy.ini] -[mypy] -strict = True -ignore_missing_imports=True -allow_subclassing_any=True -allow_untyped_calls=True -pretty=True -show_error_codes=True -implicit_reexport=True -allow_untyped_decorators=True -``` -`.isort.cfg` для конфигурации сортировки импортов. -```ini{}[.isort.cfg] -[isort] +Для конфигурации сортировки импортов и проверки типов добавим следющее +в наш основной файл проекта. + +Обычно я добавляю эти секции сразу после секции `[tool.poetry.dev-dependencies]`. + +```toml{}[pyproject.toml] +... + +[tool.mypy] +strict = true +ignore_missing_imports = true +allow_subclassing_any = true +allow_untyped_calls = true +pretty = true +show_error_codes = true +implicit_reexport = true +allow_untyped_decorators = true +warn_return_any = false + +[tool.isort] +profile = "black" multi_line_output = 3 -include_trailing_comma = true -use_parentheses = true + +... ``` `.flake8` - конфигурация линтинга. Тут довольно много. Это игнорирование ненужных кодов ошибок, которые не особо-то и ошибки. @@ -416,13 +422,6 @@ repos: entry: yesqa language: system types: [ python ] - - - id: pytest - name: pytest - entry: pytest - language: system - pass_filenames: false - types: [ python ] ``` И не забываем про `.gitignore`. Его можно найти [тут](https://github.com/github/gitignore/blob/master/Python.gitignore). diff --git a/content/ru/start-with-k8s.md b/content/ru/start-with-k8s.md new file mode 100644 index 0000000..02f6dcd --- /dev/null +++ b/content/ru/start-with-k8s.md @@ -0,0 +1,753 @@ +--- +title: Начало работы с kubernetes +description: Как там это всё разворачивать в двух словах. +category: DevOps +position: 4 +--- + +# Проблема в изучении кубернетес + +Многие люди, кто задавались вопросом как начать работать с +кубернетесом сталкивались с тем, что документация крайне +большая сложная и нет нормального описания как +завернуть маленький проект из одного проекта в свой кластер или +как развернуть свой кластер без боли. + +А всё потому что вся документация нацелена на большие +production-ready системы с большим rps и тому подобным. + +В данной статье я попробую исправить это вселенское +недопонимание используя k3s, свой комплюктер и немного знаний по кодингу. + +
+ +![Intro image](/images/k3s_start/kube_intro.jpg) + +
+ +# Что такое кубернетес и почему это лучше докера + +Многие ребята, кто хорошо знаком с докером и его +возможностями могут задаваться таким вопросом. +Для тех кто в танке, напомню, что докер +имеет вариант запуска в режиме кластера. +Этот функционал называется docker swarm. +В целом, swarm отдалённо напоминает kubernetes, +так как в этом режиме докер, худо-бедно но умеет +автоскейлится и запускаться в кластере, +но это всё равно немного не то. + + + Также замечу, что кубер активно развивается и поддерживается. + огромным количество компаний. А вот docker swarm уже по-немногу + умирает и документация по нему не то чтобы супер хороша. + + +По большей части кубернетес это система, +которая будет управлять вашими приложениями, +следить за их состоянием и помогать вам в их +конфигурации. Кубер умеет очень много, поэтому +все интересные способности я в этой статье не смогу осветить, +но самую базу попробую рассказать. + +# Из чего состоит кубернетес + +Так как я в этой статье хотел затронуть +совсем базовые и практические вещи, то рассматривать +мы будем только крайне полезные компоненты. + +- Container +- Pod +- Deployment +- Service +- Ingress +- Namespace +- Secret +- ConfigMap + +А теперь рассмотрим немного поподробнее. + +## Container + +Контейнеры не то чтобы часть специфичная для кубернетес. +С контейнерами вы можете быть знакомы из кучи систем. +В контексте кубера они не обладают никакими дополнительными +свойствами. Это ровно то же, что и контейнеры `containerd` +или те, с которыми вы возились с докером. Ничего нового. + +Собираются контейнеры для кубернетеса ровно тем же образом, +что и для докера. + + + + Важная ремарка. Кубер начиная с 2021 кубернетес не поддерживает + докер как бэкенд. Теперь кубер будет общаться с + containerd напрямую. Это значит, что теперь перед использованием + контейнеров собранных на локальной машине надо будет импортировать их + в `containerd` используя `ctr image import`. + +Как импортировать образы почитать можно в [этой статье](https://cwienczek.com/2020/06/import-images-to-k3s-without-docker-registry/). + + + +## Pod + +
+ Pods +
+ +Поды - это логически связанные группы контейнеров. +Это самая базовая еденица кубернетеса. + +В поде находится от одного до множества контейнеров. +Интересная особенность пода в том, что все контейнеры +делят один сетевой адресс. Другими словами, +если у вас один из контейнеров открыл порт `3000`, +то другие контейнеры из пода эти порты использовать не смогут. + +То есть, если вы хотите логически связанные приложения +поместить в под, то они могут ходить друг к другу через лупбек +адреса. Такие как `localhost` или `127.0.0.1`. + +## Deployment + +Поды это круто, но есть одно но. Данные объекты неизменяемые +и сам под скейлить вручную занятие сомнительное. + +Конечно, никто не мешает создать вам вручную [ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) +и самому крутить там нужные значения. Но в среднем +вам с этим возиться не очень то хочется. + +
+ +Deployment нужна именно для описания подов и создания +некоторых ресурсов, нужных для скейлинга. +Также с помощью деплойментов можно делать откаты приложения через +механиз роллбеков. Я это рассматривать не буду. Тут только база. + +## Service + +Сервис - это ресурс, с помощью которого поды могут общаться +между собой. По факту сервис описывает, какие порты пода +открыты и перенаправляет трафик на них. + +Представьте себе под, который просто проксирует весь трафик с одного +порта на какой-нибудь порт пода. Это и есть Service. + +
+ +![Service overview](/images/k3s_start/service%20overview.png) + +
+ +Выглядит сервис примерно так как показано на кртинке выше. +Он направляет весь входной трафик с указанного порта на +порты подов. + +Также сервис выступает как балансировщик. + + + Если вы хотите сделать запрос от одного пода до дргого внутри кластера, то + вам придётся использовать сервис. +
+ Если вы попробуете сделать запрос напрямую по IP пода, то у вас, конечно же, + получится, но это довольно странная идея из-за того, что при пересоздании + пода у него может обновится IP внутри кластера. Поэтому лучше использовать сервисы. Также они потребуются для ингресса. +
+ +## Ingress + +Ингрессы это сервис описывающий куда пускать трафик, +который поступает снаружи кластера. + +
+ +![Ingress overview](/images/k3s_start/ingress-overview.png) + +
+ +Принцип работы ингресса следующий: + +Вы указываете хост ингресса и различные регексы пути. +В зависимости от входящего запроса ингресс выбирает в какой сервис +направить его и в какой порт. + +Настройка ингресса достаточно гибкая и я не думаю, +что вы можете столкнуться с какими либо проблемами. + +## Namespace + +Неймспейсы это логические разделители уровня доступа. + +Я предпочитаю использовать разные неймспейсы под различные логические группы приложений. + +Например, в моём кластере есть отдельные неймспейсы для каждого приложения. + +Скажем, у меня есть проект, в котором есть база данных и веб сервер. +Я держу их в одном неймспейсе, который называется также, как и приложени. + +А вот телеграм боты -- приложения достаточно лёгкие. +Поэтому у меня есть неймспейс со всеми телеграм ботами. + +Эта штука позволяет просто лучше организовать свой кубернетес кластер. + +Также неймспейсы очень полезны для ограничивания возможностей +конкретного пользователя. +Например, вы можете создать правило, которое будет разрешать пользователю +смотреть на поды в каком-то неймспейсе, но при этом ему нельзя будет +что либо изменять. + +## Secret и ConfigMap + +Данные ресурсы нужны только чтобы хранить +внутри кластера какую-нибудь информацию. + +Например вы можете сохранить в ConfigMap +переменные среды и потом использовать их в подах в одном неймспейсе. + +В секреты обычно кидают сертификаты или какие-нибудь ключи. +Но так как секреты не особо секретные принято использовать [Vault](https://www.vaultproject.io/docs/platform/k8s). Но так как эта статья затрагивает только +основы рассматривать развертку и настройку Vault мы не будем, ну и также HashiCorp +всё довольно подробно расписали сами. + +# Как развернуть k8s у себя + +Для локального кубера есть пара вариантов. + +- k3s (Недоступен под Windows) +- minikube + +На первый взгляд minikube может показаться лучшим вариантом. +И он действительно хорош тем, что его легко почистить после +своих экспериментов. Однако, там есть проблемы с ингрессами. +По факту они не работают и там надо окольными путями получать +адреса приложения. + +
+ +k3s - это легковесная production-ready реализация k8s. Ingress у него работают +отлично, поэтому я буду использовать его. + +
+ +Я не буду зацикливаться на установке `minikube`, так как +он прост в установке и первоначальной настройке. Почитать подробнее можно в +[официальном гайде от minikube](https://minikube.sigs.k8s.io/docs/start/). + +С `k3s` всё немного посложнее, но тоже достаточно просто, если немного разобраться. Первоначальная установка описана в [официальной доке k3s](https://rancher.com/docs/k3s/latest/en/installation/install-options/). + +## Подключение кластера + +После установки в домашней дериктории должен был быть +сгенерирован файл `.kube/config`. Этот файл содержит данные для +подключения к различным кластерам. `minikube` Сам добавляет +ключи для подключения к .kube/config. `K3S` не изменяет +никаким образом `.kube/config`, поэтому надо будет это сделать вручную. + +Для того, чтобы это сделать сначала разберёмся как выглядит конфиг. + +```yaml{}[.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` надо посмотреть его конфиг +и скопировать данные. Либо выставить переменную среды, которая будет +указывать конфиг k3s. Конфиг подключения `k3s` лежит в файле `/etc/rancher/k3s/k3s.yaml`. + +Можете выполнить команду, которая будет просить kubectl использовать указанный конфиг: +`export KUBE_CONFIG=/etc/rancher/k3s/k3s.yaml` + +
+ +Либо скопируйте нужные данные для подключения себе в `.kube/config`. + +После настройки выполните команду и проверьте что вам вернулось +что-то подобное. Это значит, что никаких ресурсов пока в +кластере нет. Это мы исправим позже. Пока что можно сказать, +что подключение прошло успешно. + +```bash +$ 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 +``` + +Либо у вас неверные данные для входа. + +### Мониторинг кластера + +Для просматривания управления кластером используется cli утилита `kubectl`. +Но для того, чтобы с ней работать нужно получше понять что вообще в кластере есть. + +Для этого я советую использовать [Lens](https://k8slens.dev/). Это крайне +удобный интерфейс для управления своим кластером. +Также там есть очень клёвая настрока, которая сама включит мониторинг +потребления памяти и процессора для всех подов и кластера в общем. + +На локальной машине это не очень много смысла имеет, +а вот в проде было бы очень полезно. + +Выглядит Lens примерно так: + +
+ +![Lens example](/images/k3s_start/lens-example.png) + +
+ +Для изучения **крайне настоятельно рекомендую** настроить Lens. + +# Ваше первое приложение в кластере. + +### Сервер + +Давайте создадим своё первое приложение. + +Для этого я буду использовать [express.js](https://expressjs.com/), так как он крайне популярен и прост. + +Для этого сначала напишем сам сервер. Я буду использовать yarn, но можете и npm, +сути не поменяет. + +Создайте какую-нибудь папку, где вы будете эксперементировать, откройте в ней ваш любимый тектовый редактор и просто копируйте файлы ниже. + +```json{}[package.json] +{ + "name": "req_counter", + "version": "1.0.0", + // Указан модуль, + // чтобы использовать нормальны импорты, + // а не require. + "type": "module", + "license": "MIT", + "scripts": { + // Скрипт старта сервера. + "server": "node index.js" + }, + // Зависимости проекта. + "dependencies": { + "express": "^4.17.1" + } +} +``` + +И само приложение + +```js{}[index.js] +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 +``` + +У меня всё работает и успешно отдаётся нужная информация. + +```json +{ + "requests": 1, + "hostname": "s3rius-pc" +} +``` + +### Docker образ + +Теперь создадим докер образ приложения. + +Добавим `.dockerignore`, чтобы игнорировать ненужные файлы во время сборки образа. + +```gitignore{}[.dockerignore] +node_modules/ +``` + +И добавим в проект `Dockerfile` для описания самого процесса сборки. + +```dockerfile{}[Dockerfile] +FROM node:17-alpine + +WORKDIR /app +COPY . /app/ + +RUN yarn install + +CMD [ "yarn", "run", "server", "0.0.0.0", "8000"] +``` + +Давайте соберём и запустим проект в контейнере. + +```bash +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` локальной машины. + +```json +{ + "requests": 10, + "hostname": "8f23adadc640" +} +``` + +Вариантов как положить это приложение в K8S несколько. + +- Вы можете запушить собранное приложение в [Docker HUB](https://hub.docker.com/) и использовать его. +- Можете использовать мой образ `s3rius/req-counter-express:latest` +- Импортировать собранный образ как tar файл и импортировать его в containerd напрямую. + Как это сделать почитать можно в [этой статье](https://cwienczek.com/2020/06/import-images-to-k3s-without-docker-registry/). + +### Деплой в k8s + +Создайте папку `kube` в папке проекта и теперь мы будем работать в ней. +Все ресурсы будут описаны yaml-файлами. + +```yaml{}[kube/deployment.yml] +--- +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 +``` + +Теперь опишем сервис для управления трафиком. + +```yaml{}[kube/service.yml] +--- +apiVersion: v1 +kind: Service +# Метадата сервиса. +metadata: + name: req-counter-service +spec: + # Селектор подов, + # которым будет пускаться трафик. + # Трафик может идти в любой под, + # который матчит данному селектору. + selector: + app: req-counter + # Порты для проксирования соединений. + ports: + # Порт сервиса + - port: 80 + # Порт пода, куда будет идти трафик дальше. + targetPort: 8000 +``` + +И в последнюю очередь опишем наш ингрес. + +```yaml{}[kube/ingress.yml] +--- +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 +``` + +Перед тем, как создать все описанные ресурсы создадим неймспейс. + +```bash +$ kubectl --context k3s create namespace req-couter-ns +namespace/req-couter-ns created +``` + +После того, как неймспейс создан самое время сделать последний штрих. + +
+ +![Apply meme](/images/k3s_start/kubectl-apply.png) + +
+ +```bash +$ 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, выбрать свой кластер из списка +и посмотреть как там поживает ваше приложение. + +Также не забудьте указать неймспей, в который вы беплоиди приложение. + +Выглядит это чудо примерно так: + +
+ +![Deployed app in lens](/images/k3s_start/lens-deployed.png) + +
+ +Также можно использовать команду и вывести всё себе в терминал. + +``` +$ 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 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`. + +```yaml{}[kube/deployment.yml] +... +spec: + # Количество реплик пода. + replicas: 3 +... +``` + +После изменения просто ещё раз вызовите команду apply. + +```bash +$ 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 +``` + +И теперь вы можете открыть в браузере своё приложение и увидеть, +как кубернетес заботливо направляет трафик туда, куда вы хотите. + +
+ +![Making requests](/images/k3s_start/requests.gif) + +
+ + +### Как мне очистить мой кластер? + +Всё очень просто. + +Так как у нас имеются описния ресурсов, то мы +можем удалить всё сразу используя команду + +```bash +$ 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 удалит все описанные ресурсы из вашего кластера. + +До новых встреч. diff --git a/content/ru/traefik.md b/content/ru/traefik.md index c82f17b..f6d69ca 100644 --- a/content/ru/traefik.md +++ b/content/ru/traefik.md @@ -7,7 +7,6 @@ category: DevOps # Traefik - роутинг просто - Сегодня я бы хотел рассказать о такой клёвой штуке, как [Traefik](https://traefik.io/traefik/). Не так давно я перевёл все сервисы своего сервера на traefik и это буквально сделало жизнь проще. Сегодня я расскажу вам зачем он нужен, как его настроить, и покажу на примере, как именно он помогает. По факту, это nginx на стероидах, в плане конфигурации, потому что в traefik за тебя сделано гораздо больше. # Что такое traefik @@ -33,6 +32,7 @@ Traefik - это система, которая позволяет настро ## Архитектура + В официальной документации зарисована следующая схема работы traefik:
@@ -43,21 +43,24 @@ Traefik - это система, которая позволяет настро Как можно видеть по данному изображению, есть 5 основных составляющих traefik, а именно: -* entrypoints -* routers -* rules (часть routers) -* middlewares (чать routers) -* services +- entrypoints +- routers +- rules (часть routers) +- middlewares (чать routers) +- services ### Entrypoint + Являются основными слушателями входящих соединений. Через них проходит весь трафик. Если попробовать объяснить в двух словах: "Порт, на который должно прийти входящее соединение", - вполне себе неплохое объяснение. В данном туториале я создам 2 `entrypoint`, которые будут слушать на http и https. -### Routers, Rules и Middlewares -Роутер - связующее звено между `entrypoint` и сервисом, куда нужно направить трафик. Роутер хранит в себе информацию, куда направить входящий трафик, и +### Routers, Rules и Middlewares + +Роутер - связующее звено между `entrypoint` и сервисом, куда нужно направить трафик. Роутер хранит в себе информацию, куда направить входящий трафик, и правила, по которым можно определить, пускать ли трафик дальше. Rules - это и есть те самые правила, которые определяют, через какой роутер пустить трафик. Допустим, у вас в конфиге есть следующие правила: + ``` Host(`myapp.com`) && (PathPrefix(`/api`) || PathPrefix(`/memes`)) ``` @@ -75,7 +78,6 @@ Host(`myapp.com`) && (PathPrefix(`/api`) || PathPrefix(`/memes`)) Вы, конечно же, можете использовать локально установленную версию, всё будет работать, как часы. - Для нашего основного traefik на сервере мы создадим небольшой docker-compose.yml, файл конфига и папочку с сертификатами. Структура будет следующая: @@ -100,13 +102,20 @@ services: # The official v2.0 Traefik docker image image: traefik:v2.4.8 container_name: traefik_main - command: + command: + # Включает прослушивание докера на новые события и следит за лейблами контейнеров. - --providers.docker=true + # Отключает автоматическое создание роутеров ко всем контейнерам на сервере. - --providers.docker.exposedbydefault=false + # Сеть докера, по которой будет выполнятся подключение к контейнерам. - --providers.docker.network=traefik-shared + # Папка с конфигурационным файлом. - --providers.file.directory=/etc/traefik/dynamic + # Включает отслеживание изменений файла конфигурации. - --providers.file.watch=true + # Создаёт entrypoint с названием http и слушает 80 порт. - --entrypoints.http.address=:80 + # Создаёт entrypoint с названием https и слушает 443 порт. - --entrypoints.https.address=:443 ports: # The HTTP port @@ -119,12 +128,16 @@ services: environment: MAIN_HOST: 192.168.1.89 volumes: - # So that Traefik can listen to the Docker events + # Обязательный вольюм. Так траефик может слушать + # события происходящие в демоне докера. - /var/run/docker.sock:/var/run/docker.sock + # Вольюм с файлом конфигурации. - ./config.toml:/etc/traefik/dynamic/traefik.toml + # Папка сертификатов. - ./certs:/etc/certs/ networks: + # Сетка, внутри которой будут находится приложения. traefik-shared: name: traefik-shared @@ -132,16 +145,6 @@ networks: Вот какие параметры я передаю в `traefik-cli`. -| Параметр | Что делает | -|-------------------------------------------------|----------------------------------------------------------------------------------| -| `providers.docker=true` | Включает прослушивание докера на новые события и следит за лейблами контейнеров. | -| `providers.docker.exposedbydefault=false` | Отключает автоматическое создание роутеров ко всем контейнерам на сервере. | -| `providers.docker.network=traefik-shared` | Сеть докера, по которой будет выполнятся подключение к контейнерам. | -| `providers.file.directory=/etc/traefik/dynamic` | Папка с конфигурационным файлом. | -| `providers.file.watch=true` | Включает отслеживание изменений файла конфигурации. | -| `entrypoints.http.address=:80` | Создаёт entrypoint с названием http и слушает 80 порт. | -| `entrypoints.https.address=:443` | Создаёт entrypoint с названием https и слушает 443 порт. | - Конечно, вы всегда можете глянуть `traefik --help` и подобрать себе желаемые параметры. Также из docker-compose файла видно, что я создал докер сеть `traefik-shared`, которую в дальнейшем буду использовать на всех контейнерах, которым требуется свой домен. @@ -153,16 +156,19 @@ networks: Для генерации локальных сертификатов я использую тулу [mkcert](https://github.com/FiloSottile/mkcert). Для любого локального домена я делаю что-то типа: + ```bash mkcert "*.local" mv _wildcard.local-key.pem local.key mv _wildcard.local.pem local.pem ``` + И помещаю это в папочку `certs` рядом с `docker-compose.yml`. После того как я создал все нужные сертефикаты для всех доменов, их надо указать в файле `config.toml` в папочке traefik. Вот пример: + ```toml{}[config.toml] [tls.options] [tls.options.default] @@ -173,7 +179,7 @@ mv _wildcard.local.pem local.pem [[tls.certificates]] # Я тут указываю /etc/certs, потому что в docker-compose # у нас volume на эту папку. - certFile = "/etc/certs/local.pem" + certFile = "/etc/certs/local.pem" keyFile = "/etc/certs/local.key" ``` @@ -183,16 +189,18 @@ mv _wildcard.local.pem local.pem ```toml [[tls.certificates]] - certFile = "/etc/certs/" + certFile = "/etc/certs/" keyFile = "/etc/certs/" ``` После этого вы можете запускать traefik и наслаждаться доменами для ваших контейнеров. # Запуск приложений + Теперь сконфигурируем приложение таким образом, чтобы к нему можно было обращаться через доменное имя. Для примера возьмем мелкое приложение на nodejs со следующей структурой проекта: + ``` . ├── docker-compose.yml @@ -223,18 +231,18 @@ app.listen(PORT, () => { ``` ```json{}[package.json] -{ - "name": "express-test", - "version": "1.0.0", - "main": "index.js", - "author": "s3rius", - "license": "MIT", - "scripts": { - "runserver": "node index.js" - }, - "dependencies": { - "express": "^4.17.1" - } +{ + "name": "express-test", + "version": "1.0.0", + "main": "index.js", + "author": "s3rius", + "license": "MIT", + "scripts": { + "runserver": "node index.js" + }, + "dependencies": { + "express": "^4.17.1" + } } ``` @@ -260,10 +268,15 @@ services: server: build: . labels: + # Включить поддержку роутинга через traefik. - traefik.enable=true + # Поставить правило роутинга, если Host запроса равен test_node.local. - traefik.http.routers.test_node.rule=Host(`test_node.local`) + # Слушать на entrypoint http (80 порт, это было объявлено в параметрах запуска traefik). - traefik.http.routers.test_node.entrypoints=http + # Сервис, связанный с роутером test_node. - traefik.http.routers.test_node.service=node_test + # Порт, куда направлять запросы в сервис node_test. - traefik.http.services.node_test.loadbalancer.server.port=3000 command: runserver networks: @@ -271,6 +284,7 @@ services: networks: + # Докер сеть, в которой находится traefik. traefik-shared: name: traefik-shared external: true @@ -282,14 +296,6 @@ networks: Как вы видите, приложение слушает на порт `3000` и отвечает свим hostname и количеством обработанных запросов. А в docker-compose.yml, в отличие от обычного проекта, появились labels. -| Лейбл | Что делает | -|---------------------------------------------------------------|---------------------------------------------------------------------------------------| -| ``traefik.enable=true`` | Включить поддержку роутинга через traefik | -| ``traefik.http.routers..rule=Host(`test_node.local`)`` | Поставить правило роутинга, если Host запроса равен test_node.local | -| ``traefik.http.routers..entrypoints=http`` | Слушать на entrypoint http (80 порт, это было объявлено в параметрах запуска traefik) | -| ``traefik.http.routers..service=`` | Сервис, связанный с роутером test_node | -| ``traefik.http.services..loadbalancer.server.port=3000`` | Порт, куда направлять запросы в сервис node_test | -
@@ -300,14 +306,15 @@ networks: Обратите внимание на косые кавычки при указании хоста! Это обязательно. -В объявлении labels могут быть использованы переменные среды. Например: -``traefik.http.routers.test_node.rule=Host(`${APP_HOST}`)`` +В объявлении labels могут быть использованы переменные среды. Например: +`` traefik.http.routers.test_node.rule=Host(`${APP_HOST}`) `` Также можно видеть, что я подключил контейнер к сети, которую мы указывали в контейнере traefik. Здесь она помечена как external. Теперь мы можем спокойно запустить наш сервис. Для этого воспользуемся следующей командой: + ```bash docker-compose up --build --scale server=3 ``` @@ -328,6 +335,7 @@ $ curl -H "Host: test_node.local" "http://localhost" Как вы видите, traefik балансирует между контейнерами за нас. И я считаю, что это - победа. ## Подключение TLS и сертификатов + Тут всё не намного сложнее. Давайте немного поменяем лейблы нашего контейнера. ```yaml @@ -347,11 +355,12 @@ services: На данном этапе вам потребуется добавить свой хост в `/etc/hosts`, если вы используете нормальную систему. Но если вы всё же на windows, то вам потребуется добавить правило в `C:\Windows\System32\drivers\etc\hosts`. И добавляем в конец файла запись: + ``` 127.0.0.1 test_node.local ``` -И также вам потребуется cертификат на этот домен. +И также вам потребуется cертификат на этот домен. Для этого: 1. Создадим сертификат через `mkcert`, как упоминалось ранее; @@ -365,12 +374,12 @@ $ curl --insecure https://test_node.local {"request_num":0,"host":"7417ac8fda92"} ``` - ## Добавление локальных сервисов не из докера Все те флаги, которые мы указываем в labels, вы также можете указать в файле конфигурации рядом с docker-compose.yml, указав конкретный ip адрес. Например: + ```toml [http.routers] # Define a connection between requests and services @@ -387,9 +396,9 @@ $ curl --insecure https://test_node.local url = "http://192.168.1.89:8100" ``` - # Создание локального DNS -В данном пункте я бы хотел рассказать, как настроить свой DNS-сервер, чтобы ваши домены были доступны всем устройствам в локальной сети. Для этого я буду использовать dnsmasq. Пользователям винды он недоступен, поэтому советую развернуть маленький домашний сервер на линуксе. + +В данном пункте я бы хотел рассказать, как настроить свой DNS-сервер, чтобы ваши домены были доступны всем устройствам в локальной сети. Для этого я буду использовать dnsmasq. Пользователям винды он недоступен, поэтому советую развернуть маленький домашний сервер на линуксе. Для этого установите `dnsmasq` и найдите и раскомментируйте, либо добавьте следующие строчки в файл `/etc/dnsmasq.conf`: @@ -406,6 +415,7 @@ address=/./ Чтобы это узнать, посмотрите свой ip через роутер или выполните `ip addr`. Вообще, `dnsmasq` парсит файл `/etc/hosts` и вы можете туда добавлять записи, типа: + ``` 192.168.1.1 mydomain.local ``` @@ -413,6 +423,7 @@ address=/./ Но так как я указал `address`, то это необязательно. `dnsmasq` и без явного указания поддоменов должен будет работать отлично. Запустите `dnsmasq` в режиме сревиса: + ```bash sudo systemctl enable dnsmasq.service sudo systemctl start dnsmasq.service @@ -445,7 +456,7 @@ services: - traefik.http.routers.traefik_router.entrypoints=https - traefik.http.routers.traefik_router.tls=true - command: + command: - --api.dashboard=true ... ``` @@ -457,5 +468,4 @@ services: [![GIF](/images/traefik_imgs/traefik_web.png)](/images/traefik_imgs/traefik_web.png) - Разве это не круто? diff --git a/layouts/default.vue b/layouts/default.vue index 42bd8ba..80912f7 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,9 +1,9 @@