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, свой комплюктер и немного знаний по кодингу.
+
+
+
+# Что такое кубернетес и почему это лучше докера
+
+Многие ребята, кто хорошо знаком с докером и его
+возможностями могут задаваться таким вопросом.
+Для тех кто в танке, напомню, что докер
+имеет вариант запуска в режиме кластера.
+Этот функционал называется 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
+
+
+
+
+
+Поды - это логически связанные группы контейнеров.
+Это самая базовая еденица кубернетеса.
+
+В поде находится от одного до множества контейнеров.
+Интересная особенность пода в том, что все контейнеры
+делят один сетевой адресс. Другими словами,
+если у вас один из контейнеров открыл порт `3000`,
+то другие контейнеры из пода эти порты использовать не смогут.
+
+То есть, если вы хотите логически связанные приложения
+поместить в под, то они могут ходить друг к другу через лупбек
+адреса. Такие как `localhost` или `127.0.0.1`.
+
+## Deployment
+
+Поды это круто, но есть одно но. Данные объекты неизменяемые
+и сам под скейлить вручную занятие сомнительное.
+
+Конечно, никто не мешает создать вам вручную [ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/)
+и самому крутить там нужные значения. Но в среднем
+вам с этим возиться не очень то хочется.
+
+
+
+Deployment нужна именно для описания подов и создания
+некоторых ресурсов, нужных для скейлинга.
+Также с помощью деплойментов можно делать откаты приложения через
+механиз роллбеков. Я это рассматривать не буду. Тут только база.
+
+## Service
+
+Сервис - это ресурс, с помощью которого поды могут общаться
+между собой. По факту сервис описывает, какие порты пода
+открыты и перенаправляет трафик на них.
+
+Представьте себе под, который просто проксирует весь трафик с одного
+порта на какой-нибудь порт пода. Это и есть Service.
+
+
+
+Выглядит сервис примерно так как показано на кртинке выше.
+Он направляет весь входной трафик с указанного порта на
+порты подов.
+
+Также сервис выступает как балансировщик.
+
+
+ Если вы хотите сделать запрос от одного пода до дргого внутри кластера, то
+ вам придётся использовать сервис.
+
+ Если вы попробуете сделать запрос напрямую по IP пода, то у вас, конечно же,
+ получится, но это довольно странная идея из-за того, что при пересоздании
+ пода у него может обновится IP внутри кластера. Поэтому лучше использовать сервисы. Также они потребуются для ингресса.
+
+
+## Ingress
+
+Ингрессы это сервис описывающий куда пускать трафик,
+который поступает снаружи кластера.
+
+
+
+Принцип работы ингресса следующий:
+
+Вы указываете хост ингресса и различные регексы пути.
+В зависимости от входящего запроса ингресс выбирает в какой сервис
+направить его и в какой порт.
+
+Настройка ингресса достаточно гибкая и я не думаю,
+что вы можете столкнуться с какими либо проблемами.
+
+## 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.
+
+# Ваше первое приложение в кластере.
+
+### Сервер
+
+Давайте создадим своё первое приложение.
+
+Для этого я буду использовать [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
+```
+
+После того, как неймспейс создан самое время сделать последний штрих.
+
+
+
+```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, выбрать свой кластер из списка
+и посмотреть как там поживает ваше приложение.
+
+Также не забудьте указать неймспей, в который вы беплоиди приложение.
+
+Выглядит это чудо примерно так:
+
+
+
+
+
+
+
+Также можно использовать команду и вывести всё себе в терминал.
+
+```
+$ 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
+```
+
+И теперь вы можете открыть в браузере своё приложение и увидеть,
+как кубернетес заботливо направляет трафик туда, куда вы хотите.
+
+
+
+
+### Как мне очистить мой кластер?
+
+Всё очень просто.
+
+Так как у нас имеются описния ресурсов, то мы
+можем удалить всё сразу используя команду
+
+```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:
[](/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 @@
-
+
- S3rius' dev blog
+ Dev blog
@@ -24,7 +24,7 @@
:key="category"
class="is-hidden-desktop"
>
-
+
{{ category }}
{
let cats = new Map()
@@ -94,12 +95,11 @@ export default defineComponent({
diff --git a/static/images/k3s_start/ingress-overview.png b/static/images/k3s_start/ingress-overview.png
new file mode 100644
index 0000000..a912cb1
Binary files /dev/null and b/static/images/k3s_start/ingress-overview.png differ
diff --git a/static/images/k3s_start/kube_intro.jpg b/static/images/k3s_start/kube_intro.jpg
new file mode 100644
index 0000000..0bc3e34
Binary files /dev/null and b/static/images/k3s_start/kube_intro.jpg differ
diff --git a/static/images/k3s_start/kubectl-apply.png b/static/images/k3s_start/kubectl-apply.png
new file mode 100644
index 0000000..d29ba4c
Binary files /dev/null and b/static/images/k3s_start/kubectl-apply.png differ
diff --git a/static/images/k3s_start/lens-deployed.png b/static/images/k3s_start/lens-deployed.png
new file mode 100644
index 0000000..6547847
Binary files /dev/null and b/static/images/k3s_start/lens-deployed.png differ
diff --git a/static/images/k3s_start/lens-example.png b/static/images/k3s_start/lens-example.png
new file mode 100644
index 0000000..6619403
Binary files /dev/null and b/static/images/k3s_start/lens-example.png differ
diff --git a/static/images/k3s_start/pods.svg b/static/images/k3s_start/pods.svg
new file mode 100644
index 0000000..b63f415
--- /dev/null
+++ b/static/images/k3s_start/pods.svg
@@ -0,0 +1,484 @@
+
+
+
diff --git a/static/images/k3s_start/requests.gif b/static/images/k3s_start/requests.gif
new file mode 100644
index 0000000..aa989f7
Binary files /dev/null and b/static/images/k3s_start/requests.gif differ
diff --git a/static/images/k3s_start/service overview.png b/static/images/k3s_start/service overview.png
new file mode 100644
index 0000000..4dabcc0
Binary files /dev/null and b/static/images/k3s_start/service overview.png differ