Random changes for many files.
Signed-off-by: Pavel Kirilin <win10@list.ru>
This commit is contained in:
162
content/ru/docker-envs.md
Normal file
162
content/ru/docker-envs.md
Normal file
@ -0,0 +1,162 @@
|
||||
---
|
||||
title: Разделение докера на среды.
|
||||
description: Как работать с несколькими docker-compose.
|
||||
position: 3
|
||||
category: DevOps
|
||||
---
|
||||
|
||||
|
||||
# Разделение докера на среды
|
||||
|
||||
Должен ли ты разделять среды докера в несколько `docker-compose` файлов?
|
||||
Определенно! В некоторых случаях невозможно разобраться что разработчики хотели сделать или почему ничего не работает. Настройка раздельных сред может стать настоязей мешанино. В этой статье я покажу как настроить `docker-compose` и избежать миллиона проблем.
|
||||
|
||||
Как мы вообще можем разделить среды для локальной разработки и продовые?
|
||||
Отет прост: Требуется декомпозировать проект и создать отдельные файлы под каждую из сред или даже сервисов.
|
||||
|
||||
Это нормально, если у тебя будет больше 2-ч `docker-compose` файлов.
|
||||
|
||||
Например:
|
||||
```
|
||||
deploy
|
||||
├── docker-compose.autotests.yml
|
||||
├── docker-compose.db.yml
|
||||
├── docker-compose.dev.yml
|
||||
├── docker-compose.yml
|
||||
├── dockerfiles
|
||||
│ ├── api.Dockerfile
|
||||
│ └── front.Dockerfile
|
||||
└── scripts
|
||||
├── start-autotests.sh
|
||||
├── start-backend.sh
|
||||
└── start-migrations.sh
|
||||
```
|
||||
|
||||
### Как это работает?
|
||||
Докер умеет работать с множеством `docker-compose` файлов одновременно.И мы можем использовать это для разделения сред.
|
||||
|
||||
Выглядит это следующим образом.
|
||||
```bash
|
||||
docker-compose \
|
||||
-f "deploy/docker-compose.yml" \
|
||||
-f "deploy/docker-compose.db.yml" \
|
||||
-f "deploy/docker-compose.dev.yml" \
|
||||
up
|
||||
```
|
||||
|
||||
В каждом из этих файлов определен какой-то кусок конфигурации, который не пересекается. Например в `docker-compose.yml` определено приложение и некоторые необходимые сервисы, а в остальных файлах добавляются сервисы или меняются значения предыдущих.
|
||||
|
||||
Наверное, тут проще на примере пояснить.
|
||||
|
||||
Допустим у нас есть проект, у которого поднимается бекенд с параметрами, которые отличаются на проде и локально.
|
||||
|
||||
Для простоты создадим простецикий проект со следующей структурой.
|
||||
|
||||
```
|
||||
proj
|
||||
├── deploy
|
||||
│ ├── docker-compose.yml
|
||||
│ └── dockerfiles
|
||||
│ └── script.Dockerfile
|
||||
└── script.py
|
||||
```
|
||||
|
||||
Для начала напишем скрипт, который будет в центре всего.
|
||||
```python{}[script.pt]
|
||||
from sys import argv # это аргуметы переданные в скрипт
|
||||
|
||||
def main():
|
||||
print("; ".join(argv[1:])) # выводитна экран все аргументы программы
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
|
||||
После того, как скрипт готов и отлажен давайте завернем его в докер,
|
||||
создав `Dockerfile`.
|
||||
|
||||
```dockerfile{}[deploy/dockerfiles/script.Dockerfile]
|
||||
from python:3.8 # asd
|
||||
|
||||
WORKDIR /app
|
||||
COPY script.py /app/script.py
|
||||
CMD python /app/script.py
|
||||
```
|
||||
|
||||
Как вы видите, Dockerfile очень прост. Теперь добавим главный `docker-compose.yml`.
|
||||
|
||||
```yaml{}[deploy/docker-compose.yml]
|
||||
---
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
script:
|
||||
build: # Собираем приложение используя наш dockerfile.
|
||||
dockerfile: ./deploy/dockerfiles/script.Dockerfile
|
||||
context: .
|
||||
# Запускаем его с командой для продового запуска.
|
||||
command: python script.py this is prod
|
||||
```
|
||||
|
||||
Теперь запустим всё это чудо.
|
||||
|
||||
```bash
|
||||
d-test docker-compose \
|
||||
-f "./deploy/docker-compose.yml" \
|
||||
--project-directory "." \
|
||||
run --rm script
|
||||
```
|
||||
|
||||
Вот что будет выведено на экран.
|
||||
```log
|
||||
Creating d-test_script_run ... done
|
||||
this; is; prod
|
||||
```
|
||||
|
||||
Как мы видим, на экран вывелось сообщение, которое мы указали в нашем `docker-compose.yml`
|
||||
|
||||
А теперь для локальной разработки мы не будем ничего менять в нашем файле композиции, а создадим новый рядом.
|
||||
|
||||
```yaml{}[deploy/docker-compose.dev.yml]
|
||||
---
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
script:
|
||||
command: python script.py this is dev
|
||||
```
|
||||
|
||||
Теперь добавим ещё один файл в нашу команду запуска.
|
||||
|
||||
```bash
|
||||
d-test docker-compose \
|
||||
-f "deploy/docker-compose.yml" \
|
||||
-f "deploy/docker-compose.dev.yml" \
|
||||
--project-directory "." \
|
||||
run --rm script
|
||||
```
|
||||
|
||||
Вот что будет выведено на экран:
|
||||
```log
|
||||
Creating d-test\_script\_run ... done
|
||||
this; is; dev
|
||||
```
|
||||
|
||||
Как можно заметить, конфигурация запуска перезаписалась в порядке вызова.
|
||||
|
||||
То есть, каждый последующий файл композиции может добавлять сервисы и **частично** изменять конфигурацию предыдущих.
|
||||
|
||||
Итоговая структура проекта:
|
||||
```
|
||||
proj
|
||||
├── deploy
|
||||
│ ├── docker-compose.dev.yml
|
||||
│ ├── docker-compose.yml
|
||||
│ └── dockerfiles
|
||||
│ └── script.Dockerfile
|
||||
└── script.py
|
||||
```
|
||||
|
||||
### Где это применимо?
|
||||
Ну, в любом проекте, сложнее того, который мы рассмотрели. Потому что в реальной жизни не всё так радужно и локальная версия приложения может отличаться не только параметрами запуска, но и целыми сервисами, которые требуются для локальной копии приложения.
|
132
content/ru/makefiles.md
Normal file
132
content/ru/makefiles.md
Normal file
@ -0,0 +1,132 @@
|
||||
---
|
||||
title: Makefiles для чайников.
|
||||
description: Автоматизируем по старинке.
|
||||
position: 1
|
||||
category: 'DevOps'
|
||||
---
|
||||
|
||||
# Makefiles для чайников
|
||||
Если описывать в двух словах, то это просто описание команд для упрощения работы с проектом. Изначально делались для того, чтобы удобно компилировать всякие проекты на любых языках.
|
||||
|
||||
## Как они работают
|
||||
Достаточно просто.
|
||||
|
||||
Вот пример `Makefile`:
|
||||
|
||||
```makefile{}[Makefile]
|
||||
# Сгенерируем простой файл
|
||||
test.gen:
|
||||
@echo 'echo "Hi!"' > "test.gen"
|
||||
|
||||
# Команда для запуска файла test.gen
|
||||
.PHONY: run
|
||||
run: test.gen
|
||||
sh "test.gen"
|
||||
```
|
||||
|
||||
До двоеточий обазначены команды (tagets). Например тут это: `test.gen` и `run`.
|
||||
Данные таргеты можно запускать через `make ${target}`.
|
||||
|
||||
Например, если ввести `make run` в папке с `Makefile`, то мы получим следующее:
|
||||
```console
|
||||
$ sh "test.gen"
|
||||
Hi!
|
||||
```
|
||||
|
||||
Как видно из выхлопа данной команды, у нас успешно запустился файл `test.gen`, хотя мы не запускали команду `make test.gen`. Что произошло? Давайте разбираться.
|
||||
|
||||
## Зависимости таргетов
|
||||
|
||||
На строчке объявления таргета `run` видно, что объявлен `test.gen`. Это зависимость данного таргета и она будет вызвана до того, как выполнится скрипт описываемого таргета. Таких зависимостей может быть много, перечисляются они чере пробел.
|
||||
|
||||
Например:
|
||||
```makefile{}[Makefile]
|
||||
.PHONY: target1
|
||||
target1:
|
||||
echo "1"
|
||||
|
||||
.PHONY: target2
|
||||
target2: target1
|
||||
echo "2"
|
||||
|
||||
.PHONY: target3
|
||||
target3: target1 target2
|
||||
echo "memes"
|
||||
```
|
||||
|
||||
При вызове `make target3` будет выведено:
|
||||
```console
|
||||
$ make target3
|
||||
echo "1"
|
||||
1
|
||||
echo "2"
|
||||
2
|
||||
echo "memes"
|
||||
memes
|
||||
```
|
||||
|
||||
Как можно видеть, он построил граф зависимостей и не выполнил `target1` дважды.
|
||||
|
||||
## Сокрытие вывода команд
|
||||
|
||||
В предыдущем примере можно заметить, что он написал все команды в терминал. Для того, чтобы этого избежать следует добавить "@" в начало команды и она не будет напечатана.
|
||||
|
||||
```makefile{}[Makefile]
|
||||
.PHONY: target1
|
||||
target1:
|
||||
@echo "1"
|
||||
|
||||
.PHONY: target2
|
||||
target2: target1
|
||||
@echo "2"
|
||||
|
||||
.PHONY: target3
|
||||
target3: target1 target2
|
||||
@echo "memes"
|
||||
```
|
||||
|
||||
Теперь при вызое `make target3` будет показано следующее:
|
||||
```cosole
|
||||
$ make target3
|
||||
1
|
||||
2
|
||||
memes
|
||||
```
|
||||
|
||||
|
||||
## Валидация сгенерированных файлов
|
||||
Зачастую `Makefile` используют для компиляции С и зачастую требуется
|
||||
собрать какую-либо часть проект и пропустить сборку этой части, если эта часть уже собрана.
|
||||
Раскрою секрет, в Makefile это базовый функционал.
|
||||
|
||||
Давайте немного поменяем первый Makefile и запустим дважды.
|
||||
```makefile{}[Makefile]
|
||||
# Сгенерируем простой файл
|
||||
test.gen:
|
||||
echo 'echo "Hi!"' > "test.gen"
|
||||
|
||||
# Команда для запуска файла test.gen
|
||||
.PHONY: run
|
||||
run: test.gen
|
||||
sh "test.gen"
|
||||
```
|
||||
|
||||
Теперь вызываемая команда таргета `test.gen` выводится на экран.
|
||||
|
||||
Позапускаем.
|
||||
|
||||
```console
|
||||
$ make run
|
||||
echo 'echo "Hi!"' > "test.gen" # Наша командабыла вызвана.
|
||||
sh "test.gen"
|
||||
Hi!
|
||||
$ make run
|
||||
sh "test.gen" # Наша команда не вызвана.
|
||||
Hi!
|
||||
```
|
||||
|
||||
Дело в том, что названия таргетов - названия файлов, которые должны сгенерировать эти самые таргеты.
|
||||
То есть, в данном случае таргет `test.gen` должен сгенерировать файл `test.gen` по окончании выполнения. Если этот файл уже присутствует, то команда выполнена не будет. Именно поэтому у нас она не запустилась второй раз, так как в первый запуск был создан треубемый файл и его никто не удалял между запусками.
|
||||
|
||||
А что если я вот не хочу чтобы команда создавала и проверяла файлы?
|
||||
Для этого пишут `.PHONY: ${target}`. Например у нас так объявлен таргет `run` и, даже если файл с названием `run` будет присутствовать в директории цель не будет выполняться.
|
584
content/ru/project-start.md
Normal file
584
content/ru/project-start.md
Normal file
@ -0,0 +1,584 @@
|
||||
---
|
||||
title: Как стартовать проект
|
||||
description: Лучший вариант начала проекта на питоне.
|
||||
category: Python
|
||||
position: 1
|
||||
---
|
||||
|
||||
# Как стартовать проект
|
||||
|
||||
В целом, вариантов несколько. Например, можно просто хранить несколько скриптов на github и быть довольным, можно дописать к ним `setup.py` и выложиться на pypi. Но лучший способ - структурированный проект с `poetry`.
|
||||
|
||||
Почему?
|
||||
|
||||
Особенностей у poetry много. Сейчас расскажу про то, как использовать и что делать.
|
||||
|
||||
Для начала создадим проект.
|
||||
|
||||
```console
|
||||
$ poetry new new_proj
|
||||
Created package new_proj in new_proj
|
||||
$ cd new_proj
|
||||
```
|
||||
|
||||
Данная команда сгенерирует следующую структуру:
|
||||
```
|
||||
new_proj
|
||||
├── new_proj
|
||||
│ └── __init__.py
|
||||
├── pyproject.toml
|
||||
├── README.rst
|
||||
└── tests
|
||||
├── __init__.py
|
||||
└── test_new_proj.py
|
||||
```
|
||||
|
||||
Это наш новый проект с небольшими заготовками. В целом, тут нет ничего необычного. Весь код библиотеки/проекта будет в папке с названием проекта. `README.rst` - дефолтный README. `pyproject.toml` - мета-данные о проекте, такие как: зависимости, описание, дополнительные установочные опции и многое другое. Ты можешь прочитать побольше о `pyproject.toml` [тут](https://python-poetry.org/docs/pyproject/).
|
||||
|
||||
## Как работать с poetry
|
||||
|
||||
Вся информация есть в [официальной документации](https://python-poetry.org/docs) poetry. Тут я расскажу про основные моменты.
|
||||
|
||||
### Управление зависимостями
|
||||
|
||||
Чтобы добавить зависимость проекта достаточно выполнить
|
||||
```console
|
||||
$ poetry add ${dependency}
|
||||
```
|
||||
|
||||
Данная команда найдет последнюю нужную версию и запишет её в `pyproject.toml` и в `poetry.lock`.
|
||||
|
||||
Для того, чтобы установить зависимость для разработки (линтеры например), достаточно добавить флаг `--dev`.
|
||||
|
||||
```console
|
||||
$ poetry add ${dependency} --dev
|
||||
```
|
||||
|
||||
Чтобы удалить зависимость достаточно просто `add` заменить на `remove`.
|
||||
|
||||
Примеры работы:
|
||||
```console
|
||||
$ poetry add loguru
|
||||
Using version ^0.5.3 for loguru
|
||||
|
||||
Updating dependencies
|
||||
Resolving dependencies... (0.1s)
|
||||
|
||||
Writing lock file
|
||||
|
||||
Package operations: 1 install, 0 updates, 0 removals
|
||||
• Installing loguru (0.5.3)
|
||||
$ poetry remove loguru
|
||||
Updating dependencies
|
||||
Resolving dependencies... (0.1s)
|
||||
|
||||
Writing lock file
|
||||
|
||||
Package operations: 0 installs, 0 updates, 1 removal
|
||||
• Removing loguru (0.5.3)
|
||||
```
|
||||
### Выполнение команд
|
||||
|
||||
При использовании virtualenv для входа в оболочку всегда надо было вводить
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
Однако с poetry это излишне. Он сам создает и менеджит виртуальные среды.
|
||||
Чтобы выполнить одиночную команду можно вызвать `run`.
|
||||
|
||||
Например:
|
||||
```console
|
||||
$ poetry install black --dev
|
||||
Using version ^20.8b1 for black
|
||||
• Installing appdirs (1.4.4)
|
||||
• Installing click (7.1.2)
|
||||
• Installing mypy-extensions (0.4.3)
|
||||
• Installing pathspec (0.8.1)
|
||||
• Installing regex (2020.11.13)
|
||||
• Installing toml (0.10.2)
|
||||
• Installing typed-ast (1.4.2)
|
||||
• Installing typing-extensions (3.7.4.3)
|
||||
• Installing black (20.8b1)
|
||||
$ poetry run black .
|
||||
reformatted new_proj/new_proj/__init__.py
|
||||
reformatted new_proj/tests/test_new_proj.py
|
||||
All done! ✨ 🍰 ✨
|
||||
2 files reformatted, 1 file left unchanged.
|
||||
```
|
||||
|
||||
Для выполнения нескольких комманд подряд, можно войти в shell. Это аналог `source venv/bin/activate`.
|
||||
|
||||
```console
|
||||
$ poetry shell
|
||||
Spawning shell within /home/s3rius/.cache/pypoetry/virtualenvs/new-proj-eutP4v0O-py3.9
|
||||
$ black .
|
||||
reformatted new_proj/new_proj/__init__.py
|
||||
reformatted new_proj/tests/test_new_proj.py
|
||||
All done! ✨ 🍰 ✨
|
||||
2 files reformatted, 1 file left unchanged.
|
||||
```
|
||||
|
||||
### Версионирование
|
||||
|
||||
Менять версии пакета вручную больше не нужно.
|
||||
|
||||
У poetry есть кое что для тебя.
|
||||
```console
|
||||
$ poetry version patch
|
||||
Bumping version from 0.1.0 to 0.1.1
|
||||
$ poetry version preminor
|
||||
Bumping version from 0.1.1 to 0.2.0-alpha.0
|
||||
$ poetry version minor
|
||||
Bumping version from 0.2.0-alpha.0 to 0.2.0
|
||||
$ poetry version premajor
|
||||
Bumping version from 0.2.0 to 1.0.0-alpha.0
|
||||
$ poetry version major
|
||||
Bumping version from 1.0.0-alpha.0 to 1.0.0
|
||||
```
|
||||
|
||||
## pyproject.toml
|
||||
|
||||
Как было сказано ранее, данный файл содержит в себе мета-информацию пакета.
|
||||
|
||||
Пример `pyproject.toml`
|
||||
```toml{}[pyproject.toml]
|
||||
[tool.poetry]
|
||||
name = "new_proj"
|
||||
version = "0.1.0"
|
||||
description = "Test library for example"
|
||||
readme = "README.rst"
|
||||
homepage = "https://test_url.com/"
|
||||
repository = "https://github.meme/"
|
||||
authors = ["Pavel Kirilin <win10@list.ru>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^6.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
```
|
||||
|
||||
Для того чтобы устновить все зависимости требуется просто ввести
|
||||
```console
|
||||
$ poetry install
|
||||
```
|
||||
|
||||
Данная команда создаст виртуальную среду сама и установит **все** зависимости. Включая dev-зависимости. Чтобы установить зависимости только для приложения можно добавить `--no-dev` ключ.
|
||||
|
||||
## Как паковать и публиковать на pypi
|
||||
Всё очень просто.
|
||||
Давайте добавим какую-нибудь функцию в проект.
|
||||
|
||||
```python{}[new_proj/main.py]
|
||||
def ab_problem(a: int, b: int) -> int:
|
||||
return a + b
|
||||
```
|
||||
|
||||
```python{}[new_proj/__init.py]
|
||||
from new_proj.main import ab_problem
|
||||
|
||||
__all__ = [
|
||||
'ab_problem'
|
||||
]
|
||||
```
|
||||
|
||||
Теперь соберем проект.
|
||||
|
||||
```console
|
||||
$ poetry build
|
||||
Building new_proj (0.1.0)
|
||||
- Building sdist
|
||||
- Built new_proj-0.1.0.tar.gz
|
||||
- Building wheel
|
||||
- Built new_proj-0.1.0-py3-none-any.whl
|
||||
```
|
||||
|
||||
Та-да. Это готовый к публикации на pypi пакет. Лежит он в папке dist.
|
||||
|
||||
Давайте проверим, что всё работает корректно.
|
||||
|
||||
```console
|
||||
$ pip install "./dist/new_proj-0.1.0-py3-none-any.whl"
|
||||
Processing ./dist/new_proj-0.1.0-py3-none-any.whl
|
||||
Installing collected packages: new-proj
|
||||
Successfully installed new-proj-0.1.0
|
||||
|
||||
$ python
|
||||
Python 3.9.1 (default, Feb 1 2021, 04:02:33)
|
||||
[GCC 10.2.0] on linux
|
||||
Type "help", "copyright", "credits" or "license" for more information.
|
||||
$ from new_proj import ab_problem
|
||||
$ ab_problem(1,33)
|
||||
34
|
||||
```
|
||||
|
||||
Как можно видеть всё работает корректно и теперь мы можем использовать наш пакет.
|
||||
|
||||
Для публикации следует использовать:
|
||||
```console
|
||||
$ poetry publish -u "user" -p "password"
|
||||
```
|
||||
|
||||
Подробнее можно почитать [тут](https://python-poetry.org/docs/cli/#publish).
|
||||
|
||||
# Конфигурация проекта
|
||||
Конечно, такого рода конфигурация проекта всё равно никуда не годиться.
|
||||
Давайте настроим автоматический линтинг.
|
||||
|
||||
```console
|
||||
$ poetry add \
|
||||
$ flake8 \
|
||||
$ black \
|
||||
$ isort \
|
||||
$ mypy \
|
||||
$ pre-commit \
|
||||
$ yesqa \
|
||||
$ autoflake \
|
||||
$ wemake-python-styleguide --dev
|
||||
---> 100%
|
||||
```
|
||||
|
||||
Теперь добавим конфигурационных файлов в корень проекта.
|
||||
Это мои конфигурации, которые я настроил под себя, можешь менять их как хочешь.
|
||||
|
||||
`.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]
|
||||
multi_line_output = 3
|
||||
include_trailing_comma = true
|
||||
use_parentheses = true
|
||||
```
|
||||
|
||||
`.flake8` - конфигурация линтинга. Тут довольно много. Это игнорирование ненужных кодов ошибок, которые не особо-то и ошибки.
|
||||
```ini{}[.flake8]
|
||||
[flake8]
|
||||
max-complexity = 6
|
||||
inline-quotes = double
|
||||
max-line-length = 88
|
||||
extend-ignore = E203
|
||||
docstring_style=sphinx
|
||||
|
||||
ignore =
|
||||
; Found `f` string
|
||||
WPS305,
|
||||
; Missing docstring in public module
|
||||
D100,
|
||||
; Missing docstring in magic method
|
||||
D105,
|
||||
; Missing docstring in __init__
|
||||
D107,
|
||||
; Found class without a base class
|
||||
WPS306,
|
||||
; Missing docstring in public nested class
|
||||
D106,
|
||||
; First line should be in imperative mood
|
||||
D401,
|
||||
; Found `__init__.py` module with logic
|
||||
WPS412,
|
||||
; Found implicit string concatenation
|
||||
WPS326,
|
||||
; Found string constant over-use
|
||||
WPS226,
|
||||
; Found upper-case constant in a class
|
||||
WPS115,
|
||||
; Found nested function
|
||||
WPS430,
|
||||
; Found using `@staticmethod`
|
||||
WPS602,
|
||||
; Found method without arguments
|
||||
WPS605,
|
||||
; Found overused expression
|
||||
WPS204,
|
||||
; Found too many module members
|
||||
WPS202,
|
||||
; Found too high module cognitive complexity
|
||||
WPS232,
|
||||
; line break before binary operator
|
||||
W503,
|
||||
; Found module with too many imports
|
||||
WPS201,
|
||||
; Found vague import that may cause confusion: X
|
||||
WPS347,
|
||||
; Inline strong start-string without end-string.
|
||||
RST210,
|
||||
; subprocess call with shell=True seems safe, but may be changed in the future.
|
||||
S602,
|
||||
; Starting a process with a partial executable path.
|
||||
S607,
|
||||
; Consider possible security implications associated with subprocess module.
|
||||
S404,
|
||||
; Found nested class
|
||||
WPS431,
|
||||
; Found wrong module name
|
||||
WPS100,
|
||||
; Found too many methods
|
||||
WPS214,
|
||||
; Found too long ``try`` body
|
||||
WPS229,
|
||||
; Found function with too much cognitive complexity
|
||||
WPS231,
|
||||
|
||||
; all init files
|
||||
__init__.py:
|
||||
; ignore not used imports
|
||||
F401,
|
||||
; ignore import with wildcard
|
||||
F403,
|
||||
; Found wrong metadata variable
|
||||
WPS410,
|
||||
|
||||
per-file-ignores =
|
||||
; all tests
|
||||
test_*.py,tests.py,tests_*.py,*/tests/*:
|
||||
; Use of assert detected
|
||||
S101,
|
||||
|
||||
exclude =
|
||||
./.git,
|
||||
./venv,
|
||||
./cached_venv,
|
||||
./var,
|
||||
```
|
||||
|
||||
`.pre-commit-config.yaml` - конфигураци хуков для запуска всех линтеров перед коммитом.
|
||||
```yaml{}[.pre-commit-config.yaml]
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: trailing-whitespace
|
||||
- id: check-toml
|
||||
- id: end-of-file-fixer
|
||||
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v2.1.0
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: black
|
||||
name: Format with Black
|
||||
entry: black
|
||||
language: system
|
||||
types: [python]
|
||||
|
||||
- id: autoflake
|
||||
name: autoflake
|
||||
entry: autoflake
|
||||
language: system
|
||||
types: [ python ]
|
||||
args: [ --in-place, --remove-all-unused-imports, --remove-duplicate-keys ]
|
||||
|
||||
- id: isort
|
||||
name: isort
|
||||
entry: isort
|
||||
language: system
|
||||
types: [ python ]
|
||||
|
||||
- id: flake8
|
||||
name: Check with Flake8
|
||||
entry: flake8
|
||||
language: system
|
||||
pass_filenames: false
|
||||
types: [ python ]
|
||||
args: [--count, .]
|
||||
|
||||
- id: mypy
|
||||
name: Validate types with MyPy
|
||||
entry: mypy
|
||||
language: system
|
||||
types: [ python ]
|
||||
|
||||
- id: yesqa
|
||||
name: Remove usless noqa
|
||||
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).
|
||||
|
||||
ОН НЕОБХОДИМ ЧТОБЫ pre-commit РАБОТЛ МАКСИМАЛЬНО КОРРЕКТНО.
|
||||
|
||||
Теперь установим хуки в репозиторий.
|
||||
```console
|
||||
$ git init
|
||||
Initialized empty Git repository in .git/
|
||||
$ poetry shell
|
||||
$ pre-commit install
|
||||
pre-commit installed at .git/hooks/pre-commit
|
||||
$ git commit
|
||||
... # Упадет с кучей ошибок
|
||||
```
|
||||
|
||||
Теперь поправим все возникшие проблемы.
|
||||
|
||||
Во первых исправим тесты.
|
||||
|
||||
В `tests/test_new_proj.py` напишем следующее:
|
||||
```python{}[tests/test_new_proj.py]
|
||||
from new_proj import ab_problem
|
||||
|
||||
|
||||
def test_ab() -> None:
|
||||
"""AB problecm success case."""
|
||||
assert ab_problem(1, 2) == 3
|
||||
```
|
||||
|
||||
Добавим описания в `__init__` файлы.
|
||||
|
||||
```python{}[tests/__init__.py]
|
||||
"""Tests for new_proj."""
|
||||
```
|
||||
|
||||
```python{}[new_proj/__init__.py]
|
||||
"""Project for solving ab problem."""
|
||||
from new_proj.main import ab_problem
|
||||
|
||||
__all__ = [
|
||||
"ab_problem",
|
||||
]
|
||||
```
|
||||
|
||||
Пофиксим основной файл проекта.
|
||||
|
||||
```python{}[new_proj/main.py]
|
||||
def ab_problem(first: int, second: int) -> int:
|
||||
"""
|
||||
Solve AB problem.
|
||||
|
||||
The function sums two integers.
|
||||
|
||||
:param first: a argument.
|
||||
:param second: b argument.
|
||||
:returns: sum.
|
||||
"""
|
||||
return first + second
|
||||
```
|
||||
|
||||
Теперь вы можете сделать свой первый коммит.
|
||||
|
||||
```console
|
||||
$ git commit
|
||||
Check python ast................Passed
|
||||
Trim Trailing Whitespace........Passed
|
||||
Check Toml......................Passed
|
||||
Fix End of Files................Passed
|
||||
Add trailing commas.............Passed
|
||||
Format with Black...............Passed
|
||||
autoflake.......................Passed
|
||||
isort...........................Passed
|
||||
Check with Flake8...............Passed
|
||||
Validate types with MyPy........Passed
|
||||
Remove usless noqa..............Passed
|
||||
pytest..........................Passed
|
||||
```
|
||||
|
||||
Теперь ты знаешь как создавать шедевры.
|
||||
# Создание CLI-приложения
|
||||
А что если я хочу cli-приложение?
|
||||
Ты не представляешь насколько это просто.
|
||||
|
||||
Пойдем модифицируем наш основной файл.
|
||||
|
||||
```python{}[new_proj/main.py]
|
||||
import argparse
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""
|
||||
Parse CLI arguments.
|
||||
|
||||
:returns: parsed namespace.
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("a", type=int)
|
||||
parser.add_argument("b", type=int)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def ab_problem(first: int, second: int) -> int:
|
||||
"""
|
||||
Solve AB problem.
|
||||
|
||||
The function sums two integers.
|
||||
|
||||
:param first: a argument.
|
||||
:param second: b argument.
|
||||
:returns: sum.
|
||||
"""
|
||||
return first + second
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main function."""
|
||||
args = parse_args()
|
||||
print(ab_problem(args.a, args.b)) # noqa: WPS421
|
||||
|
||||
```
|
||||
|
||||
Теперь поправим pyproject.toml таким образом чтобы он создал cli для нашей функции.
|
||||
|
||||
Добавим следующую секцию куда-нибудь в `pyproject.toml`:
|
||||
```toml
|
||||
[tool.poetry.scripts]
|
||||
ab_solver = "new_proj.main:main"
|
||||
```
|
||||
|
||||
Теперь нам доступна программа `ab_solver` внутри shell.
|
||||
|
||||
```console
|
||||
$ poetry install
|
||||
$ poetry shell
|
||||
$ ab_solver 1 2
|
||||
3
|
||||
```
|
||||
|
||||
Хочешь установить? Пожалуйста.
|
||||
```console
|
||||
$ poetry build
|
||||
Building new_proj (0.1.0)
|
||||
- Building sdist
|
||||
- Built new_proj-0.1.0.tar.gz
|
||||
- Building wheel
|
||||
- Built new_proj-0.1.0-py3-none-any.whl
|
||||
$ pip install "./dist/new_proj-0.1.0-py3-none-any.whl"
|
||||
Processing ./dist/new_proj-0.1.0-py3-none-any.whl
|
||||
Installing collected packages: new-proj
|
||||
Successfully installed new-proj-0.1.0
|
||||
$ ab_solver 1 2
|
||||
3
|
||||
```
|
||||
|
||||
Если запаблишить проект, то у пользователя тоже установится ваша cli-программа.
|
461
content/ru/traefik.md
Normal file
461
content/ru/traefik.md
Normal file
@ -0,0 +1,461 @@
|
||||
---
|
||||
title: Traefik - роутинг просто.
|
||||
description: Изучите как использовать traefik.
|
||||
position: 2
|
||||
category: DevOps
|
||||
---
|
||||
|
||||
# Traefik - роутинг просто
|
||||
|
||||
|
||||
Сегодня я бы хотел рассказать о такой клёвой штуке, как [Traefik](https://traefik.io/traefik/). Не так давно я перевёл все сервисы своего сервера на traefik и это буквально сделало жизнь проще. Сегодня я расскажу вам зачем он нужен, как его настроить, и покажу на примере, как именно он помогает. По факту, это nginx на стероидах, в плане конфигурации, потому что в traefik за тебя сделано гораздо больше.
|
||||
|
||||
# Что такое traefik
|
||||
|
||||
Traefik - это система, которая позволяет настроить маппинг между доменными именами и конкретными приложениями. Допустим, у вас есть контейнер frontend-приложения, которое вы хотели бы разместить на домене `myapp.com`, и также у вас есть контейнер backend-приложения, которое вы бы хотели разместить на домене `api.mayapp.com`. [Traefik](https://traefik.io/traefik/) поможет вам это сделать без лишних файлов конфигурации.
|
||||
|
||||
## Сравнение с другими инструментами
|
||||
|
||||
Если бы вы решили сделать это через [Nginx](https://www.nginx.com/), то вы бы создали новые файлы конфигураций под каждый домен, в папочке с конфигурациями для всех доменов, положили куда-нибудь сертификат от домена `*myapp.com` и подключали бы его вручную в каждом файле доменов. А если бы вам надо было увеличить количество контейнеров отдельного приложения, вы бы указывали сервера в директиве `upstream` и перечисляли там адреса инстансов приложения вручную.
|
||||
|
||||
Любители [Apache HTTP Server](https://httpd.apache.org/) на этом моменте обычно берут дробовик и разносят себе голову хорошим зарядом дроби.
|
||||
|
||||
А господа, которые используют [Traefik](https://traefik.io/traefik/), просто дописывают лейблы контейнеру в `docker-compose.yml` и идут дальше дегустировать вино.
|
||||
|
||||
<b-message type="is-info" has-icon>
|
||||
Если же ваше приложение построено на вебсокетах, то тут уже фанаты Nginx тянутся за дробовиком. Ну а для ценителей traefik ничего не меняется, ведь в нём встроена поддержка HTTP/2.0.
|
||||
</b-message>
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## Архитектура
|
||||
В официальной документации зарисована следующая схема работы traefik:
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
Как можно видеть по данному изображению, есть 5 основных составляющих traefik, а именно:
|
||||
|
||||
* entrypoints
|
||||
* routers
|
||||
* rules (часть routers)
|
||||
* middlewares (чать routers)
|
||||
* services
|
||||
|
||||
### Entrypoint
|
||||
Являются основными слушателями входящих соединений. Через них проходит весь трафик. Если попробовать объяснить в двух словах: "Порт, на который должно прийти входящее соединение", - вполне себе неплохое объяснение. В данном туториале я создам 2 `entrypoint`, которые будут слушать на http и https.
|
||||
|
||||
### Routers, Rules и Middlewares
|
||||
Роутер - связующее звено между `entrypoint` и сервисом, куда нужно направить трафик. Роутер хранит в себе информацию, куда направить входящий трафик, и
|
||||
правила, по которым можно определить, пускать ли трафик дальше.
|
||||
|
||||
Rules - это и есть те самые правила, которые определяют, через какой роутер пустить трафик.
|
||||
Допустим, у вас в конфиге есть следующие правила:
|
||||
```
|
||||
Host(`myapp.com`) && (PathPrefix(`/api`) || PathPrefix(`/memes`))
|
||||
```
|
||||
|
||||
Это можно читать как "Этот роутер пустит в моё приложение, если запрос пришёл на хост `myapp.com` и путь запроса начинается с `/api` или `/memes`"
|
||||
|
||||
Довольно просто, не так ли?
|
||||
|
||||
Ну а middlewares - это некоторая логика, что нужно выполнить с запросом до того, как он попадёт в ваше приложение. В этой статье я не буду рассказывать про существующие `middlewares`, тут потребуется самостоятельное ознакомление с [документацией](https://doc.traefik.io/traefik/middlewares/overview/).
|
||||
|
||||
# Конфигурация
|
||||
|
||||
<b-message type="is-info" has-icon>
|
||||
В данной статье я буду использовать Docker для конфигурации traefik.
|
||||
Вы, конечно же, можете использовать локально установленную версию, всё будет работать, как часы.
|
||||
</b-message>
|
||||
|
||||
|
||||
Для нашего основного traefik на сервере мы создадим небольшой docker-compose.yml, файл конфига и папочку с сертификатами.
|
||||
Структура будет следующая:
|
||||
|
||||
```
|
||||
.
|
||||
├── certs
|
||||
│ ├── local.key
|
||||
│ └── local.pem
|
||||
├── config.toml
|
||||
└── docker-compose.yml
|
||||
```
|
||||
|
||||
Теперь разберем каждый файл по порядку.
|
||||
Вот основной docker-compose нашего traefik.
|
||||
|
||||
```yaml{}[docker-compose.yml]
|
||||
---
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
traefik-proxy:
|
||||
# The official v2.0 Traefik docker image
|
||||
image: traefik:v2.4.8
|
||||
container_name: traefik_main
|
||||
command:
|
||||
- --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
|
||||
- --entrypoints.https.address=:443
|
||||
ports:
|
||||
# The HTTP port
|
||||
- "80:80"
|
||||
# The HTTPS port
|
||||
- "443:443"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- traefik-shared
|
||||
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
|
||||
|
||||
```
|
||||
|
||||
Вот какие параметры я передаю в `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`, которую в дальнейшем буду использовать на всех контейнерах, которым требуется свой домен.
|
||||
|
||||
Далее следует папка с сертификатами. Для реальных доменов я использую cloudflare и скачиваю сертификаты и ключи с панели администратора. Также не забываю выставлять мод шифрования на strict.
|
||||
|
||||

|
||||
|
||||
Для генерации локальных сертификатов я использую тулу [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]
|
||||
# Эта опция вообще для strict cloudflare tls encryption,
|
||||
# Но я включаю её и на локальных доменах.
|
||||
sniStrict = true
|
||||
|
||||
[[tls.certificates]]
|
||||
# Я тут указываю /etc/certs, потому что в docker-compose
|
||||
# у нас volume на эту папку.
|
||||
certFile = "/etc/certs/local.pem"
|
||||
keyFile = "/etc/certs/local.key"
|
||||
```
|
||||
|
||||
<b-message type="is-info" has-icon>
|
||||
Для добавления новых сертификатов в данный файл достаточно добавить:
|
||||
</b-message>
|
||||
|
||||
```toml
|
||||
[[tls.certificates]]
|
||||
certFile = "/etc/certs/<certFile>"
|
||||
keyFile = "/etc/certs/<keyFile>"
|
||||
```
|
||||
|
||||
После этого вы можете запускать traefik и наслаждаться доменами для ваших контейнеров.
|
||||
|
||||
# Запуск приложений
|
||||
Теперь сконфигурируем приложение таким образом, чтобы к нему можно было обращаться через доменное имя.
|
||||
|
||||
Для примера возьмем мелкое приложение на nodejs со следующей структурой проекта:
|
||||
```
|
||||
.
|
||||
├── docker-compose.yml
|
||||
├── Dockerfile
|
||||
├── .dockerignore
|
||||
├── index.js
|
||||
├── package.json
|
||||
└── yarn.lock
|
||||
```
|
||||
|
||||
```js{}[index.js]
|
||||
const express = require('express')
|
||||
|
||||
let req_count = 0;
|
||||
const hostname = process.env.HOSTNAME;
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
app = express();
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
console.log(`GET / ${hostname}`)
|
||||
res.send({request_num: req_count++, host: hostname})
|
||||
})
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is listening on port ${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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```dockerfile{}[Dockerfile]
|
||||
FROM node:16-alpine3.11
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json yarn.lock /app/
|
||||
|
||||
RUN yarn install
|
||||
|
||||
COPY . /app/
|
||||
|
||||
ENTRYPOINT ["yarn", "run"]
|
||||
```
|
||||
|
||||
```yaml{}[docker-compose.yml]
|
||||
---
|
||||
version: '3.7'
|
||||
|
||||
|
||||
services:
|
||||
server:
|
||||
build: .
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.test_node.rule=Host(`test_node.local`)
|
||||
- traefik.http.routers.test_node.entrypoints=http
|
||||
- traefik.http.routers.test_node.service=node_test
|
||||
- traefik.http.services.node_test.loadbalancer.server.port=3000
|
||||
command: runserver
|
||||
networks:
|
||||
- traefik-shared
|
||||
|
||||
|
||||
networks:
|
||||
traefik-shared:
|
||||
name: traefik-shared
|
||||
external: true
|
||||
```
|
||||
|
||||
<b-message type="is-info" has-icon>
|
||||
Файл yarn.lock генерируется командой `yarn install`. Если у вас не установлен `yarn`, то ничего страшного. Просто это будет занимать чуть больше времени на сборку образа.
|
||||
</b-message>
|
||||
|
||||
Как вы видите, приложение слушает на порт `3000` и отвечает свим hostname и количеством обработанных запросов. А в docker-compose.yml, в отличие от обычного проекта, появились labels.
|
||||
|
||||
| Лейбл | Что делает |
|
||||
|---------------------------------------------------------------|---------------------------------------------------------------------------------------|
|
||||
| ``traefik.enable=true`` | Включить поддержку роутинга через traefik |
|
||||
| ``traefik.http.routers.<router>.rule=Host(`test_node.local`)`` | Поставить правило роутинга, если Host запроса равен test_node.local |
|
||||
| ``traefik.http.routers.<router>.entrypoints=http`` | Слушать на entrypoint http (80 порт, это было объявлено в параметрах запуска traefik) |
|
||||
| ``traefik.http.routers.<router>.service=<service>`` | Сервис, связанный с роутером test_node |
|
||||
| ``traefik.http.services.<service>.loadbalancer.server.port=3000`` | Порт, куда направлять запросы в сервис node_test |
|
||||
|
||||
<hr/>
|
||||
<b-message type="is-danger" has-icon>
|
||||
|
||||
Роутеры, сервисы и хосты явно создавать нигде не нужно.
|
||||
Они сами создаются, когда вы их указываете.
|
||||
|
||||
Название сервиса и роутера могут совпадать.
|
||||
|
||||
Обратите внимание на косые кавычки при указании хоста! Это обязательно.
|
||||
|
||||
В объявлении labels могут быть использованы переменные среды. Например:
|
||||
``traefik.http.routers.test_node.rule=Host(`${APP_HOST}`)``
|
||||
|
||||
</b-message>
|
||||
|
||||
Также можно видеть, что я подключил контейнер к сети, которую мы указывали в контейнере traefik. Здесь она помечена как external.
|
||||
|
||||
Теперь мы можем спокойно запустить наш сервис. Для этого воспользуемся следующей командой:
|
||||
```bash
|
||||
docker-compose up --build --scale server=3
|
||||
```
|
||||
|
||||
В данной команде мы указали, что хотим поднять 3 инстанса нашего сервиса. Остальным пусть занимается traefik.
|
||||
|
||||
Теперь попробуем некоторое количество раз выполнить запрос на наш сервис.
|
||||
|
||||
```console
|
||||
$ curl -H "Host: test_node.local" "http://localhost"
|
||||
{"request_num":0,"host":"7417ac8fda92"}
|
||||
```
|
||||
|
||||
Результат должен быть примерно таким:
|
||||
|
||||
[](/images/traefik_imgs/curls.gif)
|
||||
|
||||
Как вы видите, traefik балансирует между контейнерами за нас. И я считаю, что это - победа.
|
||||
|
||||
## Подключение TLS и сертификатов
|
||||
Тут всё не намного сложнее. Давайте немного поменяем лейблы нашего контейнера.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
server:
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.test_node.rule=Host(`test_node.local`)
|
||||
- traefik.http.routers.test_node.entrypoints=https
|
||||
- traefik.http.routers.test_node.tls=true
|
||||
- traefik.http.routers.test_node.service=node_test
|
||||
- traefik.http.services.node_test.loadbalancer.server.port=3000
|
||||
```
|
||||
|
||||
Как вы видите, я поменял entrypoints на `https`. Как вы можете помнить, это entrypoint, который слушает на 443 порт. И также я включил поддержку tls лейблом `traefik.http.routers.<router>.tls=true`
|
||||
|
||||
На данном этапе вам потребуется добавить свой хост в `/etc/hosts`, если вы используете нормальную систему. Но если вы всё же на windows, то вам потребуется добавить правило в `C:\Windows\System32\drivers\etc\hosts`.
|
||||
|
||||
И добавляем в конец файла запись:
|
||||
```
|
||||
127.0.0.1 test_node.local
|
||||
```
|
||||
|
||||
И также вам потребуется cертификат на этот домен.
|
||||
Для этого:
|
||||
|
||||
1. Создадим сертификат через `mkcert`, как упоминалось ранее;
|
||||
2. Поместим ключ и сертификат в папку certs;
|
||||
3. Добавим ключ и сертификат для вашего домена в config.toml (Формат указан выше).
|
||||
|
||||
Теперь вы можете обращаться к вашему приложению напрямую через локальное доменное имя.
|
||||
|
||||
```console
|
||||
$ 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
|
||||
[http.routers.<router_name>]
|
||||
rule = "Host(`my_app.local`)"
|
||||
entrypoints = "https"
|
||||
service = "<service_name>"
|
||||
[http.routers.<router_name>.tls]
|
||||
|
||||
[http.services]
|
||||
# Define how to reach an existing service on our infrastructure
|
||||
[http.services.<service_name>.loadBalancer]
|
||||
[[http.services.<service_name>.loadBalancer.servers]]
|
||||
url = "http://192.168.1.89:8100"
|
||||
```
|
||||
|
||||
|
||||
# Создание локального DNS
|
||||
В данном пункте я бы хотел рассказать, как настроить свой DNS-сервер, чтобы ваши домены были доступны всем устройствам в локальной сети. Для этого я буду использовать dnsmasq. Пользователям винды он недоступен, поэтому советую развернуть маленький домашний сервер на линуксе.
|
||||
|
||||
Для этого установите `dnsmasq` и найдите и раскомментируйте, либо добавьте следующие строчки в файл `/etc/dnsmasq.conf`:
|
||||
|
||||
```conf{}[/etc/dnsmasq.conf]
|
||||
# Never forward plain names (without a dot or domain part)
|
||||
domain-needed
|
||||
# Never forward addresses in the non-routed address spaces.
|
||||
bogus-priv
|
||||
address=/.local/192.168.1.89
|
||||
address=/.<other_domain>/<your_local_ip>
|
||||
```
|
||||
|
||||
У меня traefik развернут на хосте `192.168.1.89`. У вас ip может отличаться.
|
||||
Чтобы это узнать, посмотрите свой ip через роутер или выполните `ip addr`.
|
||||
|
||||
Вообще, `dnsmasq` парсит файл `/etc/hosts` и вы можете туда добавлять записи, типа:
|
||||
```
|
||||
192.168.1.1 mydomain.local
|
||||
```
|
||||
|
||||
Но так как я указал `address`, то это необязательно. `dnsmasq` и без явного указания поддоменов должен будет работать отлично.
|
||||
|
||||
Запустите `dnsmasq` в режиме сревиса:
|
||||
```bash
|
||||
sudo systemctl enable dnsmasq.service
|
||||
sudo systemctl start dnsmasq.service
|
||||
```
|
||||
|
||||
Теперь пойдите в настройки вашего роутера и найдите DNS-сервера. Добавьте туда ваш ip. Также не забудьте сделать локальный ip вашего устройства статичным.
|
||||
|
||||
Для примера, в роутерах keenetic это можно сделать, зарегистрировав устройство в меню 'Список устройств' и нажав на галочку 'Постоянный IP-адрес'.
|
||||
|
||||
И добавьте свой DNS-сервер в список DNS серверов вашего роутера.
|
||||

|
||||
|
||||
Готово. Вы можете попробовать зайти на свой домен с другого устройства в локальной сети, и это должно работать.
|
||||
|
||||
# Мониторинг работы traefik
|
||||
|
||||
Вообще, traefik имеет WEB интерфейс для отслеживания его работы.
|
||||
Для того чтобы подключить его, давайте поменяем `docker-compose.yml`.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik-proxy:
|
||||
# The official v2.0 Traefik docker image
|
||||
image: traefik:v2.4.8
|
||||
container_name: traefik_main
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.traefik_router.rule=Host(`traefik.local`)
|
||||
- traefik.http.routers.traefik_router.service=api@internal
|
||||
- traefik.http.routers.traefik_router.entrypoints=https
|
||||
- traefik.http.routers.traefik_router.tls=true
|
||||
|
||||
command:
|
||||
- --api.dashboard=true
|
||||
...
|
||||
```
|
||||
|
||||
Я добавил ключ `--api.dashboard=true` и лейблы для роута до `traefik`.
|
||||
Замечу, service определён заранее - это `api@internal`. Укажите его в `traefik.http.routers.<router_name>.service`.
|
||||
|
||||
Теперь вы можете зайти на `traefik.local` через свой браузер и увидеть конфигурацию traefik.
|
||||
|
||||
[](/images/traefik_imgs/traefik_web.png)
|
||||
|
||||
|
||||
Разве это не круто?
|
Reference in New Issue
Block a user