Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Современный CI/CD пайплайн для python микросервисов

Современный CI/CD пайплайн для python микросервисов

Я работаю в команде, где довольно много разработчиков и инженеров. Мы разрабатываем почти в 30 репозиториях, поэтому командный набор CI/CD пайплайнов имеет множество стадий и шагов, а также глубоко централизован.

В своем докладе я постараюсь рассказать историю о том, как он разрабатывался, как эволюционировал и как он выглядит сейчас. Я расскажу, почему мы выбираем централизацию, как и зачем мы используем докер внутри докера, как мы собираем контейнеры без докера, как он помогает нам всегда жить на самом свежем python. Покажу основной набор линтеров для всего на свете, как мы в сотню строчек кода сделали автоматическое версионирование без заморочек. Как чистить за собой реестры пакетов и контейнеров (это не так тривиально, как кажется — покажу почему), как защититься от сбоев пайплайна (ну или попытаться), как разделять среды и переменные (и почему это непросто) и опишу некоторые другие особенности нашего конвейера, раскрывая почти все его детали.

Доклад будет полезен тем, кто только стартует разработку своего конвейера или масштабируется от одного-двух проектов и хочет видеть практически полный список необходимых шагов в CI/CD пайплайне, а также тем, кто никогда ещё не делал конвейеры и не автоматизировал процесс разработки, но хочет или хотя бы интересуется.

https://www.youtube.com/watch?v=7--05FF8VAk
https://pycon.ru/sovremennyj-ci-cd-pajplajn-dlya-python-mikroservisov

Denis Anikin

May 03, 2023
Tweet

More Decks by Denis Anikin

Other Decks in Programming

Transcript

  1. Денис Аникин
    https://xfenix.ru/
    Современный CI/CD
    пайплайн для python
    микросервисов

    View Slide

  2. Денис Аникин
    2
    Кто я такой:
    — работаю в Райффайзен банке
    https://xfenix.ru

    View Slide

  3. Денис Аникин
    3
    Кто я такой:
    — работаю в Райффайзен банке
    — team lead в команде Chat
    https://xfenix.ru

    View Slide

  4. Денис Аникин
    4
    Кто я такой:
    — работаю в Райффайзен банке
    — team lead в команде Chat
    — community lead в Python Community
    https://xfenix.ru

    View Slide

  5. Денис Аникин
    5
    Кто я такой:
    — работаю в Райффайзен банке
    — team lead в команде Chat
    — community lead в Python Community
    — fullstack: разрабатываю на back на python и front
    на typescript, занимаюсь devops
    https://xfenix.ru

    View Slide

  6. Цели рассказа
    6
    Важно для тех, кто никогда их
    ещё не писал
    Помочь начать
    писать пайплайны
    Расскажу коротко какие цели преследовал

    View Slide

  7. Цели рассказа
    7
    Важно для тех, кто никогда их
    ещё не писал
    Помочь начать
    писать пайплайны
    Для тех, кто уже пишет давно,
    я принес свой субъективный
    взгляд на вещи
    Натолкнуть на
    мысли
    Расскажу коротко какие цели преследовал

    View Slide

  8. Цели рассказа
    8
    Важно для тех, кто никогда их
    ещё не писал
    Помочь начать
    писать пайплайны
    Для тех, кто уже пишет давно,
    я принес свой субъективный
    взгляд на вещи
    Натолкнуть на
    мысли
    Всем остальным может
    пригодиться мой «суповой
    набор» на гитхабе к этой
    презентации
    Поделиться
    наработками
    Расскажу коротко какие цели преследовал

    View Slide

  9. 9

    View Slide

  10. 10

    View Slide

  11. Термины
    11

    View Slide

  12. Термины
    12
    DevOps: не человек 🤵 🤵‍‍ 🔫, а набор практик
    Нудная часть

    View Slide

  13. 13

    View Slide

  14. Термины
    14
    CI: continuous integration. Это когда все часто отправляют свои
    правки в главную ветку
    Нудная часть

    View Slide

  15. Термины
    15
    CI: continuous integration. Это когда все часто отправляют свои
    правки в главную ветку
    Мы все в основном играем в continuous isolation ❌
    Нудная часть

    View Slide

  16. Термины
    16
    CD:
    — CDL: Continuous DeLivery
    — CDP: Continuous DePloyment
    Это когда у вас (полу)автоматически доставляются релизы
    Нудная часть

    View Slide

  17. Термины
    17
    TBD: ?
    Нудная часть

    View Slide

  18. Термины
    18
    TBD: trunk based development
    Нудная часть

    View Slide

  19. Термины
    19
    TBD: trunk based development
    А теперь моя своеобразная схема! Я рисовал как мог.
    Нудная часть

    View Slide

  20. TBD
    20
    Мой взгляд
    Trunk (master или main)

    View Slide

  21. TBD
    21
    Мой взгляд
    Trunk (master или main)
    Feature/
    Bugfix/
    Hotfix/

    View Slide

  22. TBD
    22
    Мой взгляд
    Trunk (master или main)
    Feature/
    Bugfix/
    Hotfix/
    v1.2.3

    View Slide

  23. TBD
    23
    Мой взгляд
    Trunk (master или main)
    Feature/
    Bugfix/
    Hotfix/
    v1.2.3
    v1.2.4

    View Slide

  24. Термины
    24
    Монорепозиторий
    Нудная часть

    View Slide

  25. Термины
    25
    Мульти репозиторий
    Нудная часть

    View Slide

  26. Из чего состоит
    приемлимый
    пайплайн?
    26

    View Slide

  27. Из чего состоит приемлимый пайплайн?
    27
    — Сборка
    Давайте немного о CI части

    View Slide

  28. Из чего состоит приемлимый пайплайн?
    28
    — Сборка
    — Статический анализ (линтинг)
    Давайте немного о CI части

    View Slide

  29. Из чего состоит приемлимый пайплайн?
    29
    — Сборка
    — Статический анализ (линтинг)
    — Тесты
    Давайте немного о CI части

    View Slide

  30. Из чего состоит приемлимый пайплайн?
    30
    — Сборка
    — Статический анализ (линтинг)
    — Тесты
    Давайте немного о CI части
    А вы уверены в порядке?

    View Slide

  31. А из чего состоит хороший пайплайн?
    31
    — Сборка
    — Статический анализ (линтинг)
    — Тесты
    — Безопасность?
    Давайте немного о CI части

    View Slide

  32. А из чего состоит хороший пайплайн?
    32
    — Сборка
    — Статический анализ (линтинг)
    — Тесты
    — Безопасность?
    — Чистка «соплей»
    Давайте немного о CI части

    View Slide

  33. Кратко об истории
    создания нашего
    пайплайна
    33

    View Slide

  34. 2019 год
    34
    — У нас был bamboo
    Мы начали разработку чат-бота втроем

    View Slide

  35. 35

    View Slide

  36. 36

    View Slide

  37. 2020 год
    37
    — Число репозиториев перешагнуло за 20
    — Появился gitlab

    View Slide

  38. О двойной
    докеризации
    38

    View Slide

  39. 39

    View Slide

  40. О «двойной докеризации»
    40
    Это важно
    Очень часто разработчики делают пайплайн так:
    do-all-things:
    stage: test
    image: python:3.8-slim
    script:
    - apt-get update -y
    - apt-get install -y pgdev kerberos enchant-2
    - pytest .

    View Slide

  41. А что тут не так?
    41
    Сборка неповторяема / не идемпотентна
    do-all-things:
    stage: test
    image: python:3.8-slim
    script:
    - apt-get update -y
    - apt-get install -y pgdev kerberos enchant-2
    - pytest .

    View Slide

  42. А что тут не так?
    42
    Дублируем Dockerfile
    do-all-things:
    stage: test
    image: python:3.8-slim
    script:
    - apt-get update -y
    - apt-get install -y pgdev kerberos enchant-2
    - pytest .

    View Slide

  43. Что делаю я в пайплайне?
    43
    Итак, «двойная докеризация»
    almost-real-pipeline:
    script:
    ...
    - docker build . -t fancy-tag
    - docker run -t fancy-tag:latest bash -c "pytest ."
    ...

    View Slide

  44. В чём помогает «двойная» докеризация?
    44
    Специфика проектов остается
    в этих проектах и не протекает
    в общий пайплайн
    В изоляции

    View Slide

  45. В чём помогает «двойная» докеризация?
    45
    Специфика проектов остается
    в этих проектах и не протекает
    в общий пайплайн
    В изоляции
    Все дубликаты, характерные
    для devops автоматизаций
    можно устранить!
    В устранении
    дупликации

    View Slide

  46. В чём помогает «двойная» докеризация?
    46
    Специфика проектов остается
    в этих проектах и не протекает
    в общий пайплайн
    В изоляции
    Все дубликаты, характерные
    для devops автоматизаций
    можно устранить!
    В устранении
    дупликации
    Ускоряем обновление на
    новые версии с помощью
    низкой связанности
    В ускорении!

    View Slide

  47. Есть проблемы
    47
    — Во-первых, вам нужно коннектиться к докер демону (у gitlab
    хороший гайд) из «второго слоя»
    https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
    «Двойная докеризация» бьет в псину

    View Slide

  48. Есть проблемы
    48
    — Во-первых, вам нужно коннектиться к докер демону (у gitlab
    хороший гайд) из «второго слоя»
    https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
    — Копирование файлов и монтирование — боль
    «Двойная докеризация» бьет в псину

    View Slide

  49. Есть проблемы
    49
    — Во-первых, вам нужно коннектиться к докер демону (у gitlab
    хороший гайд) из «второго слоя»
    https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
    — Копирование файлов и монтирование — боль ✅
    «Двойная докеризация» бьет в псину

    View Slide

  50. Есть проблемы
    50
    — Во-первых, вам нужно коннектиться к докер демону (у gitlab
    хороший гайд) из «второго слоя»
    https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
    — Копирование файлов и монтирование — боль ✅
    — Связываться с сайд контейнерами из services непросто
    «Двойная докеризация» бьет в псину

    View Slide

  51. Наконец уже к
    пайплайну!
    51

    View Slide

  52. Примеры к презентации, код
    52
    https://github.com/xfenix/pycon2022

    View Slide

  53. Ключевые особенности
    53
    — «Двойная докеризация» 🐳 🐳
    Это всё на Gitlab

    View Slide

  54. Ключевые особенности
    54
    — «Двойная докеризация» 🐳 🐳
    — Централизация
    Это всё на Gitlab

    View Slide

  55. Ключевые особенности
    55
    — «Двойная докеризация» 🐳 🐳
    — Централизация
    — Пресеты
    Это всё на Gitlab

    View Slide

  56. Ключевые особенности
    56
    — «Двойная докеризация» 🐳 🐳
    — Централизация
    — Пресеты
    — Использование include, reference, template для избегания
    основной боли пайплайнов: дупликации кода
    Это всё на Gitlab

    View Slide

  57. Ключевые особенности
    57
    — «Двойная докеризация» 🐳 🐳
    — Централизация
    — Пресеты
    — Использование include, reference, template для избегания
    основной боли пайплайнов: дупликации кода
    — Тестируем и отправляем в продакшн один и тот же образ
    Это всё на Gitlab

    View Slide

  58. 58

    View Slide

  59. Лейаут: центральный репозиторий
    59

    View Slide

  60. Лейаут: репозиторий сервиса
    60
    variables:
    DSN_VARIABLE_NAME: "NV_CHB_DB_DSN_SHARED"
    PYLINT_ARGS: "--load-plugins pylint_django base core"
    include:
    - project: "chat/misc/generic-cicd"
    file: "common/group-vars-chat-bot.yml"
    - project: 'chat/misc/generic-cicd'
    file: 'ci/python-postgres.yml'

    View Slide

  61. Вот теперь точно
    пайплайн
    61

    View Slide

  62. 62

    View Slide

  63. Шаг 1
    63
    Проверяем переменные
    .vars-check-job:
    extends: .default-job
    stage: .pre
    before_script:
    - set -u

    View Slide

  64. Шаг 1
    64
    Проверяем переменные
    .vars-check-job:
    extends: .default-job
    stage: .pre
    before_script:
    - set -u
    check-front-necessary-variables:
    extends: .vars-check-job
    script:
    - echo $PROJECT_SLUG $MAIN_USER

    View Slide

  65. Шаг 2
    65
    Собираем образ и пушим в registry. Здесь есть разные важные детали
    .build-docker-job:
    stage: build
    variables:
    STORAGE_DRIVER: vfs
    script:
    - set -x
    ...
    - buildah bud --jobs=0 --format=docker -t $TAG_FOR_CI ./Dockerfile
    - buildah push $DOCKER_TAG_FOR_CI

    View Slide

  66. 66
    Примечание
    set –x
    в каждом пайплане вам будет полезен!

    View Slide

  67. Шаг 2: как генерируются теги
    67
    Деталь 1
    VERSION=$(if [[ $CI_COMMIT_TAG ]]; then
    echo ${CI_COMMIT_TAG/v}; else echo latest; fi)
    NAME=$(if [[ $CI_COMMIT_TAG ]]; then
    echo release; else echo $CI_COMMIT_BRANCH; fi)
    $REGISTRY/$PROJECT/$CI_PROJECT_NAME/$NAME:$VERSION
    # comany-hub.com/service-name/release:1.2.3
    # comany-hub.com/service-name/master:latest

    View Slide

  68. Шаг 2: кладем в образ мета-инфо
    68
    Деталь 2
    echo "{"version": "$DOCKER_IMAGE_VERSION", "service": "$CI_PROJECT_NAME",
    "project": "$PROJECT_SLUG"}" > status.json

    View Slide

  69. Шаг 3: готовим окружение
    69
    Статические проверки (линтинг): подготовка
    script:
    ...
    - >-
    podman run
    -v $TOOLSET_PATH:/$TOOLSET_PATH
    -t $DOCKER_TAG_FOR_CI
    bash -c "if id my-user > /dev/null 2>&1; then runAs='--user my-user'; fi &&
    pip3 install $runAs -U 'pylint==2.12.2' 'mypy==0.942' 'isort==5.10.1'
    'black==22.3.0’ &&
    ...

    View Slide

  70. Шаг 3: pylint
    70
    Статические проверки (линтинг): pylint с поддержкой auto-discovery механизма, fail-under, baseline
    /$TOOLSET_SCRIPTS_PATH/lint.py
    -project_dir=$PROJ_DIR_IN_DOCKER
    -action=lint_pylint
    -pylint_args='$PYLINT_ARGS'
    -pylint_score=${PYLINT_SCORE:-10.0} &&

    View Slide

  71. Шаг 3: mypy
    71
    Статические проверки (линтинг): mypy с поддержкой baseline, «fail under»
    /$TOOLSET_SCRIPTS_PATH/lint.py
    -project_dir=$PROJ_DIR_IN_DOCKER
    -action=lint_mypy
    -mypy_score=${MYPY_SCORE:-0} &&

    View Slide

  72. Шаг 3: остальное
    72
    Банально
    python -m isort **/*.py --check --diff &&
    python -m black **/*.py --check --diff

    View Slide

  73. Шаг 3
    73
    Что можно добавить ещё из полезного
    — Radon (метрики кода)

    View Slide

  74. Шаг 3
    74
    Что можно добавить ещё из полезного
    — Radon (метрики кода)
    — Eradicate (обнаружение комментированого кода)

    View Slide

  75. Шаг 3
    75
    Что можно добавить ещё из полезного
    — Radon (метрики кода)
    — Eradicate (обнаружение комментированого кода)
    — Vulture (обнаружение мертвого кода)

    View Slide

  76. Шаг 3
    76
    Что можно добавить ещё из полезного
    — Radon (метрики кода)
    — Eradicate (обнаружение комментированого кода)
    — Vulture (обнаружение мертвого кода)
    — Prospector (метрики кода, можно заменить pylint в теории)

    View Slide

  77. Шаг 3
    77
    Что можно добавить ещё из полезного
    — Radon (метрики кода)
    — Eradicate (обнаружение комментированого кода)
    — Vulture (обнаружение мертвого кода)
    или
    — Prospector (разные тулы)
    — Pylama — все вместе (разные тулы)

    View Slide

  78. Шаг 3: за рамками python
    78
    — Hadolint
    — Kube-linter

    View Slide

  79. 79

    View Slide

  80. Шаг 4
    80
    Тестирование
    ...
    services:
    - name: postgres:14-alpine
    command: ["postgres", "-c",
    "shared_buffers=256MB", "-c", "max_connections=420"]
    - name: keydb:x86_64_v6.3.0
    script:
    - export BEFORE_TESTS_HOOK="${BEFORE_TESTS_HOOK:-alembic upgrade head}"
    ...
    - export DB_IP_ADDR=$(cat /etc/hosts | grep "runner-" | awk '{print $1}')

    View Slide

  81. Шаг 4
    81
    Тестирование
    - podman run
    ...
    --add-host="$DB_HOST:${TEST_DB_IP_ADDR}"
    --add-host="$SENTINEL_NAME-0:${TEST_DB_IP_ADDR}"
    -e $DSN_NAME=postgresql://$PG_USER:[email protected]$DB_HOST:$DB_PORT/$PG_DB
    bash -c "eval ${BEFORE_TESTS_HOOK:-} && pytest ."
    ...

    View Slide

  82. Шаг 4
    82
    Примечания
    — Мы используем для линтинга, тестов и дальнейшего деплоя
    один и тот же образ

    View Slide

  83. Шаг 4
    83
    Примечания
    — Мы используем для линтинга, тестов и дальнейшего деплоя
    один и тот же образ
    — Лейаут докер образов такой: есть имя master и у него 1 тег
    latest, а так же release и теги — версии. Т.е. выглядит так:
    release:1.2.3

    View Slide

  84. Шаг 5
    84
    Б — безопасность

    View Slide

  85. Шаг 5
    85
    Б — безопасность

    View Slide

  86. Шаг 5
    86
    Б — безопасность

    View Slide

  87. Шаг 5
    87
    Б — безопасность

    View Slide

  88. Шаг 5
    88
    Б — безопасность

    View Slide

  89. Шаг 5
    89
    Б — безопасность

    View Slide

  90. Шаг 6
    90
    Пушим образ
    publish-docker-image:
    script:
    - set -x
    - !reference [.prepare-build-tag, script]
    - echo ${FULL_DOCKER_TAG?-Undefined FULL_DOCKER_TAG var}
    - echo ${DOCKER_TAG_FOR_CI?-Undefined DOCKER_TAG_FOR_CI var}
    - !reference [.podman-login, script]
    - skopeo copy docker://$DOCKER_TAG_FOR_CI docker://$FULL_DOCKER_TAG

    View Slide

  91. А что с CD?
    91

    View Slide

  92. Автоверсионирование
    92
    Под TBD
    — GitVersion
    — Semantic-release
    — …

    View Slide

  93. Автоверсионирование
    93
    Под TBD
    — GitVersion
    — Semantic-release
    — …
    — Напишем свое!

    View Slide

  94. Автоверсионирование
    94
    Кратко
    — Свой скрипт

    View Slide

  95. 95
    РИСУНОК
    ПИТОН КОДА

    View Slide

  96. Автоверсионирование
    96
    Кратко
    — Свой скрипт
    — Ищем нужный маркер ветки

    View Slide

  97. Автоверсионирование
    97
    Кратко
    — Свой скрипт
    — Ищем нужный маркер ветки
    — Бампаем нужную часть
    semver

    View Slide

  98. Автоверсионирование
    98
    Кратко
    — Свой скрипт
    — Ищем нужный маркер ветки
    — Бампаем нужную часть
    semver
    auto-semver:
    stage: deploy
    script:
    - python -m pip install semver GitPython
    - python ./auto-semver.py version
    rules:
    - if: $CI_COMMIT_BRANCH == "master"

    View Slide

  99. И заканчивая
    99
    Надо бы за собой почистить
    — Можно чистить в конце пайплайна (здесь есть проблемы)

    View Slide

  100. И заканчивая
    100
    Надо бы за собой почистить
    — Можно чистить в конце пайплайна (здесь есть проблемы)
    — Можно чистить с помощью scheduled CI раз в сутки

    View Slide

  101. И заканчивая
    101
    Надо бы за собой почистить
    — Можно чистить в конце пайплайна (здесь есть проблемы)
    — Можно чистить с помощью scheduled CI раз в сутки
    — Раз в сутки ходим и чистим release имя, удаляя все кроме N
    (3-5 обычно достаточно) последних версий/тегов

    View Slide

  102. 102

    View Slide

  103. Возвращаемся назад!
    103
    У нас есть проблема с чисткой
    — Пишем свой скрипт

    View Slide

  104. Возвращаемся назад!
    104
    У нас есть проблема с чисткой
    — Пишем свой скрипт
    — В репозиторий добавляем два
    «курсор»-тега: release-prod, release-prev

    View Slide

  105. Возвращаемся назад!
    105
    У нас есть проблема с чисткой
    — Пишем свой скрипт
    — В репозиторий добавляем два
    «курсор»-тега: release-prod, release-prev
    — При каждом деплое мы просто
    удаляем старые и вешаем release-
    prod/release-prev теги «на тот тег»,
    который деплоим

    View Slide

  106. Возвращаемся назад!
    106
    У нас есть проблема с чисткой
    — Пишем свой скрипт
    — В репозиторий добавляем два
    «курсор»-тега: release-prod, release-prev
    — При каждом деплое мы просто
    удаляем старые и вешаем release-
    prod/release-prev теги «на тот тег»,
    который деплоим
    auto-semver:
    stage: deploy
    script:
    - python -m pip install semver GitPython
    - python ./auto-version.py mark
    rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'

    View Slide

  107. Немного про deploy
    107
    — Если у вас docker/docker-compose, то вы делаете pull/restart/up/start

    View Slide

  108. Немного про deploy
    108
    — Если у вас docker/docker-compose, то вы делаете pull/restart/up/start
    — Если у вас helm, всё чуть сложнее (но об этом в кулуарах, смотрите
    на гитхабе и не стесняйтесь писать мне лично)

    View Slide

  109. Немного про deploy
    109
    — Если у вас docker/docker-compose, то вы делаете pull/restart/up/start
    — Если у вас helm, всё чуть сложнее (но об этом в кулуарах, смотрите
    на гитхабе и не стесняйтесь писать мне лично)
    — И здесь есть куча всяких особенностей с формированием окружений
    под разные среды: staging, production, dev

    View Slide

  110. Денис Аникин
    https://xfenix.ru/
    Спасибо!

    View Slide