Как стать автором
Обновить

Лучшие практики для деплоя высокодоступных приложений в Kubernetes. Часть 1

Время на прочтение 13 мин
Количество просмотров 26K
Всего голосов 68: ↑67 и ↓1 +66
Комментарии 19

Комментарии 19

Спасибо, статья в целом полезна.


Есть сомнения по поводу проверки внешних зависимостей в initContainers, а именно — как сделано в вашем примере — с использованием образов бд. Идея скачать и запустить целый постгрес, чтобы проверить соединение с постгрес через его cli клиент, выглядит мягко говоря избыточно. Мы у себя обычно делаем это внутри самого приложения.

Действительно, можно реализовать проверку внутри приложения. Инит-контейнеры не использовать. Стартап-проба только в таком случае по-хорошему не должна проверять внешние зависимости, иначе проба начнет падать и Pod может войти в CrashLoopBackOff, что приведет к лишнему простою.

При этом само приложение не должно падать, когда внешняя зависимость становится недоступной, иначе опять же Pod может войти в CrashLoopBackOff. Вместо этого надо ретраить внешнюю зависимость, пока она не поднимется.

Если всё это получается реализовать, то соглашусь, такой подход предпочтительнее инит-контейнеров. Это у меня профдеформация видимо уже малехонькая, в нашем случае «ребят, ваше приложение надо переписать» нечасто работает :)

Идея скачать и запустить целый постгрес

Там образ весит всего 50Мб, на практике это ничего особо не аффектит. Сам postgresql-сервер в данном случае конечно не запускается. Если всё же образ беспокоит, то можно либо самому собрать минимальный образ только с pg_isready, либо засунуть pg_isready в контейнер с приложением и поэкспериментировать с чем-то вроде

readinessProbe:
  exec:
    command:
    - sh
    - -ec
    - |
      pg_isready ....
      redis-cli ping ....


И всё же, ваш вариант мне видится самым предпочтительным. Остается только вопрос, стоит ли в вашем варианте вынести проверку внешних зависимостей на какой-нибудь /readiness, где проверять этот эндпоинт в readinessProbe.

Спасибо, все по делу. Задумался о пробах и их корректности в очередной раз.

То чувство, когда cicd проекта выполнен почти по статье))


  • в хорошие стандартные практики — явное проставление конкретной версии собираемого образа, не использовать latest

И pull always ))) вместе с зависимыми проверками в readness probe это просто катастрофа

у pull always (точнее его неиспользования) есть очень интересный сайд эффект.
Сам удивлен был. Кратко — если PullAlways не стоит, то пользователь кластера может инстанцировать под на произвольной ноде из любого образа, который на ней лежит. Проверки на то, что пользователь знает секрет, с помощью которого образ можно скачать из регистри, не производится. Вот так вот.
Так что помимо здравого смысла PullAlways надо еще ставить по соображениям ИБ.
Ссылка на CIS стандарт для кубернетеса

Так надо не latest качать, а что-то более точное. Вроде бы этого хватает, чтобы скачать обновленные версии. У нас сборки тегируются хешем коммита в git. Вы сталкивались с проблемами (кроме проблем с secrets) даже в таком случае?
Так надо не latest качать, а что-то более точное.

только это "точное" должно быть каждый раз новое.


У нас сборки тегируются хешем коммита в git.

это вопрос к CI/CD, но вообще — вы не фиксируете зависимости на уровне пакетов внутри Dockerfile. Следовательно, две последовательные сборки образа из одного sha могут быть разные, причем существенно. Следовательно, возникает большое желание либо идти в сторону reproducible build, но соответствующая инфра для докера пока отсутствует, либо в сторону идеи, что если у нас уже есть собранный образ, то мы не имеем права его менять == иммутабельность тегов. Если нужна новая сборка — будьте добры сделайте новый коммит -> новый Sha -> новая пачка образов.


Так что, в целом, да — у вас верный подход.

Immutable образы в этом контексте выглядят самой подходящее практикой имхо. И решается не сложно, как уже упоминали, версионировать образы по sha коммита, плюс запрет на перезапись артифактов в хранилке. По крайней мере для дев окружений, в продакшн с семвер версинированием как-то удобнее )

Так можно же не sha git commit, а sha контейнера деплоить:
$ docker inspect alpine:latest -f "{{.RepoDigests}}"
$ kubectl set image deployment/test alpine=alpine@sha256:08d6ca16c60fe7490c03d10dc339d9fd8ea67c6466dea8d558526b1330a85930 --record


А если лень потом искать sha коммита в пайплане, можно commit sha в метаданные лейбла при docker build засовывать:
$ docker build -t ${DOCKER_IMG}:${MOVING_TAG} --build-arg=COMMIT=$(git rev-parse --short HEAD) .
соответсвенно в Dockerfile должно быть
LABEL commit=${COMMIT}

Так можно же не sha git commit, а sha контейнера деплоить:

докопаюсь — не "контейнера", а "образа"
И все равно это плохая практика… потому что иммутабельность образов ломается.


$ docker build -t ${DOCKER_IMG}:${MOVING_TAG} --build-arg=COMMIT=$(git rev-parse --short HEAD).
соответсвенно в Dockerfile должно быть
LABEL commit=${COMMIT}

а это — одобрям-с, запекать служебную инфу в LABELs и ENV'ы — хорошая практика.

И, с учётом лимита на Docker Hub — задуматься о складывание образов в свой приватный registry.
Мы просто купили платный docker hub на год на одного юзера и вопрос исчез
в хорошие стандартные практики — явное проставление конкретной версии собираемого образа, не использовать latest

и использование иммутабельных тегов по возможности (но там тоже большие нюансы).


n_bogdanov


И, с учётом лимита на Docker Hub — задуматься о складывание образов в свой приватный registry.

это нужно ВНЕ ЗАВИСИМОСТИ от лимитов Docker Hub. Это вопрос безопасности, т.к. в докерхабе образ подменят или удалят — и все, твой проект встанет

я чего подумал еще — здесь речь идет о практиках деплоя "высокодоступных" приложений, но по ходе нам еще нужно добавить несколько grains of salt (щепоток соли):


  1. безопасность — потому что все в целом на нее забивают и хотя бы минимальная ее имплементация может быть конкурентным преимуществом и shift left позволяет избежать больших трат, когда выясняется, что чтобы "сделать безопасно" приходится перелопатить пол-проекта. Заодно безопасность часто имеет отношение к доступности и целостности систем (но иногда вступает с доступностью и быстродействием в противоречие).
  2. не хватает отказоустойчивости связанных компонентов — тех же БД, хранилищ и прочего. И паттернов реализации этого на уровне микросервиса — retry, circuit breaker etc. Но это больше архитектурные паттерны, чем особенности кубернетеса самого, хотя они могут быть реализованы на уровне service mesh...
Всё это интересно и мы подумаем, что из этого мы можем захватить, хотя возможно уже в рамках других статей. Тут хотелось ограничить охват того, что описываем, а то как видите, даже несмотря на то, что опустили шаблонизацию и опустили какие-то слишком уж очевидные (на мой взгляд) вещи, вроде пина тегов, а итак вышло довольно объёмно. Ну и вторая часть статьи сейчас в работе.
Эх, эти бы заметки да два года назад, когда все эти грабли были собраны на собственном опыте. Но для тех кто только вступает на эту стезю — будет очень полезно.
Тут как в анекдоте: всё знаем, всё умеем, но уже поздно.
Очень хорошая статья, по делу, спасибо
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.