DevOps-им по-взрослому
image_2023-10-27_23-10-02.png
Бекапы в Yandex.Cloud или как перестать беспокоиться о том, что случайно нажмешь "не туда"?
Само собой, облако предоставляет резервное копирование по умолчанию для DBaaS (DataBase as Service): Managed PostgreSQL/MySQL/ClickHouse/etc. Но что насчёт IaaS (Infrastructure as Service)?
Облако способно удовлетворить почти все потребности бизнеса с помощью PaaS, для которых достаточно нажатия одной кнопки для стабильной и безопасной работы. Но иногда, в связи с отсутствием нужного сервиса, приходиться разворачивать его на виртуальных машинах.
Что делать в таком случае? Можно настроить автоматическое резервное копирование на уровне операционной системы.
Погодите, но может есть другой способ? Да. Снимки дисков! Снимок диска - копия файловой системы. Есть также расписания снимков дисков, позволяющие автоматический создавать снимки дисков в определенный момент времени. Снимки могут покрыть ваши требования по сохранности данных, но и этого может оказаться недостаточно.
Yandex Cloud Backup - сервис, существующий уже долгое время, но недавно (в сентябре) вышедший в общий доступ. Он позволяет также как и с DBaaS сервисами настроить резервное копирование для виртуальных машин. Этот подход дает больше гибкости. Создание снимков означает, что каждый раз вы сохраняете ВСЮ файловую систему. Yandex Cloud Backup в свою очередь предоставляет два типа копии - полная (все данные для восстановления ВМ) и инкрементальная (только те данные, которые отличаются от предыдущей копии). Данные типы копии можно совмещать - инкрементальные создавать каждый день, а полные оставить на конец недели.
А что по ценам? Мне резервное копирование нужно для ВМ, на котором развернут корпоративный репозиторий артефактов. Допустим, что диск хранит 20гб данных. Каждые 03:00 создается снимок, максимальное количество снимков - 3, далее с каждым новым снимком самый старый удаляется и так далее.
Посмотрим на прайсинг. Получается, что за месяц мы храним
Допустим мы храним данные в том же объеме. Каждый день создаем инкрементальный тип копии и каждые две недели полный. Не будем учитывать цены на инкрементальные - они копеечные (по 1-2мб). В месяц будет создано две полные копии - 40гб суммарно. Помимо платы за гиги, придется также выложить денюжку за каждое подключение ВМ к Cloud Backup - 238₽. Цена за ГБ в месяц стоит 4.2₽. Таким образом, в месяц бэкапы через Yandex Cloud Backup обойдутся в 403₽.
Где выгода от использования Yandex Cloud Backup??? В данном случае вы её не увидите. Но она обязательно появится, если вы начнете использовать копии для хранения более важных и объемных данных. Вам не придется каждый раз создавать и платить за полные копии, что и является самым главным плюсом сервиса.
Само собой, облако предоставляет резервное копирование по умолчанию для DBaaS (DataBase as Service): Managed PostgreSQL/MySQL/ClickHouse/etc. Но что насчёт IaaS (Infrastructure as Service)?
Облако способно удовлетворить почти все потребности бизнеса с помощью PaaS, для которых достаточно нажатия одной кнопки для стабильной и безопасной работы. Но иногда, в связи с отсутствием нужного сервиса, приходиться разворачивать его на виртуальных машинах.
Что делать в таком случае? Можно настроить автоматическое резервное копирование на уровне операционной системы.
Погодите, но может есть другой способ? Да. Снимки дисков! Снимок диска - копия файловой системы. Есть также расписания снимков дисков, позволяющие автоматический создавать снимки дисков в определенный момент времени. Снимки могут покрыть ваши требования по сохранности данных, но и этого может оказаться недостаточно.
Yandex Cloud Backup - сервис, существующий уже долгое время, но недавно (в сентябре) вышедший в общий доступ. Он позволяет также как и с DBaaS сервисами настроить резервное копирование для виртуальных машин. Этот подход дает больше гибкости. Создание снимков означает, что каждый раз вы сохраняете ВСЮ файловую систему. Yandex Cloud Backup в свою очередь предоставляет два типа копии - полная (все данные для восстановления ВМ) и инкрементальная (только те данные, которые отличаются от предыдущей копии). Данные типы копии можно совмещать - инкрементальные создавать каждый день, а полные оставить на конец недели.
А что по ценам? Мне резервное копирование нужно для ВМ, на котором развернут корпоративный репозиторий артефактов. Допустим, что диск хранит 20гб данных. Каждые 03:00 создается снимок, максимальное количество снимков - 3, далее с каждым новым снимком самый старый удаляется и так далее.
Посмотрим на прайсинг. Получается, что за месяц мы храним
<занятая_память>*<кол_во_снимков>
, то есть платим за 60 гигабайт. По прайсингу хранение 1гб на SSD в месяц стоит 3.1₽. Получается, что ежемесячно за снимки нужно отдавать по 180₽Допустим мы храним данные в том же объеме. Каждый день создаем инкрементальный тип копии и каждые две недели полный. Не будем учитывать цены на инкрементальные - они копеечные (по 1-2мб). В месяц будет создано две полные копии - 40гб суммарно. Помимо платы за гиги, придется также выложить денюжку за каждое подключение ВМ к Cloud Backup - 238₽. Цена за ГБ в месяц стоит 4.2₽. Таким образом, в месяц бэкапы через Yandex Cloud Backup обойдутся в 403₽.
Где выгода от использования Yandex Cloud Backup??? В данном случае вы её не увидите. Но она обязательно появится, если вы начнете использовать копии для хранения более важных и объемных данных. Вам не придется каждый раз создавать и платить за полные копии, что и является самым главным плюсом сервиса.
DevOps-им по-взрослому
image_2023-11-03_20-23-51.png
Хабр
Вас сдаст Гитхаб: деанонимизация пользователей SSH-серверов
Недавно в своих ежедневных чтениях я наткнулся на явление, о котором думал уже много лет: феномен утечки информации людей, использующих SSH. Этот тип утечки информации не является новым явлением. Я...
А вы знали, что GitHub и GitLab хранят ваши публичные SSH-ключи в открытом доступе?
Сюрприз! Больше подробностей в статье: https://habr.com/ru/articles/771688/
Как было подмечено в одном из комментариев, бояться за утечку публичных ключей не стоит (на то они и "публичные"), особенно если вы используете для каждого сервера/сервиса свою пару ключей. Но увидеть список своих ключей в публичном доступе было для меня, мягко сказать, небольшим шоком)))
Сюрприз! Больше подробностей в статье: https://habr.com/ru/articles/771688/
Как было подмечено в одном из комментариев, бояться за утечку публичных ключей не стоит (на то они и "публичные"), особенно если вы используете для каждого сервера/сервиса свою пару ключей. Но увидеть список своих ключей в публичном доступе было для меня, мягко сказать, небольшим шоком)))
👎2👍1
Актуальные проблемы требуют актуальных решений 👌
На фоне новостей о том, что мобильные (и не только) интернет-провайдеры блокировали протоколы Wireguard/OpenVPN (даже для трафика внутри РФ), решил внести IP-адреса корпоративных VPN серверов в "белый список" РКН.
На фоне новостей о том, что мобильные (и не только) интернет-провайдеры блокировали протоколы Wireguard/OpenVPN (даже для трафика внутри РФ), решил внести IP-адреса корпоративных VPN серверов в "белый список" РКН.
👍5🥱1
Увеличиваем скорость CI/CD пайплайна в 3.5 раза 2 раза, но с одним нюансом...
Дали задание развернуть новый Front-End сервис, который содержит зависимостей на 1.1Гб и собирается в CI/CD пайплайне 26-28 минут. Сначала я задумывался над апгрейдом ресурсов для сервера, где развёрнут GitLab-runner, но затем коллега посоветовал посмотреть в сторону использования кеширования для node_modules.
Основная проблема, по-моему изначальному мнению, была в том, что мы используем Docker executor. Это означает, что сборка образа нашего приложения происходит в контейнере с ubuntu:22.04 при помощи kaniko (DockerInDocker асуждаем). Но, как оказалось, кэширование работает отлично и для такого сценария.
После прочтения документаций, хранение кэша в файловой системе раннера казалось сомнительной идеей. Куда больше понравилось использование S3 хранилища.
Настроить его очень просто. Нужно просто создать бакет в хранилище, которое совместимо с S3 API (я использую Yandex Object Storage), а затем указать в config.toml следующие значения:
Затем путём "эксперимента" я выяснил куда gitlab ci/cd сохраняет кэш: в бакете создается папка
cache:path - файл из кэша (то есть из архива)
cache:policy - если указать pull будет только скачивать кэш, без его загрузки обратно в S3
Что касается Dockerfile, я убрал из
Но в чём же нюанс?
Как оказалось, запуская npm install при сборке образа, docker игнорирует закэшированный node_modules и ставит все пакеты заново 😕
Поэтому я удалил
Что имеем в итоге?
Скорость выполнения CI/CD до оптимизаций: 26-28min
Скорость выполнения CI/CD после оптимизаций: 7-8min
И костыль в виде ручного добавления зависимостей в случае обновления package.json
Дали задание развернуть новый Front-End сервис, который содержит зависимостей на 1.1Гб и собирается в CI/CD пайплайне 26-28 минут. Сначала я задумывался над апгрейдом ресурсов для сервера, где развёрнут GitLab-runner, но затем коллега посоветовал посмотреть в сторону использования кеширования для node_modules.
Основная проблема, по-моему изначальному мнению, была в том, что мы используем Docker executor. Это означает, что сборка образа нашего приложения происходит в контейнере с ubuntu:22.04 при помощи kaniko (
После прочтения документаций, хранение кэша в файловой системе раннера казалось сомнительной идеей. Куда больше понравилось использование S3 хранилища.
Настроить его очень просто. Нужно просто создать бакет в хранилище, которое совместимо с S3 API (я использую Yandex Object Storage), а затем указать в config.toml следующие значения:
[[runners]]
name = "gitlab-runner"
<some_info_about_your_runner>
executor = "docker"
[runners.cache]
Type = "s3"
[runners.cache.s3]
ServerAddress = "storage.yandexcloud.net"
AccessKey = "<your_access_key>"
SecretKey = "<your_secret_key>"
BucketName = "<your_bucket_name>"
Insecure = false
Затем путём "эксперимента" я выяснил куда gitlab ci/cd сохраняет кэш: в бакете создается папка
/runner/ <runner_id>/project/<project_id>/<cache_key>-protected
<cache_key>-protected
оказался zip архивом, в котором хранятся уже все закэшированные папки/файлы. Поэтому я решил создать точно такой же архив, но поместить туда node_modules. И это не сработало.... Как оказалось, на этапе создания zip архива происходили утечки в некоторых файлах, которые не позволяли запустить билд. Поэтому node_modules я сначала поместил в архив tar.gz, затем сам архив упаковал в zip и загрузил в бакет. И это сработало! Теперь чтобы использовать этот самый кэш из джобы GitLab CI/CD нужно добавить блок cache
:cache:
key: "my_client"
paths:
- my-client.tar.gz
policy: pull
script:
- tar -xzvf ./my-client.tar.gz
- mv ./node_modules ./autovill
cache:path - файл из кэша (то есть из архива)
cache:policy - если указать pull будет только скачивать кэш, без его загрузки обратно в S3
Что касается Dockerfile, я убрал из
.dockerignore
строчку с node_modules, чтобы иметь возможность копировать в Docker образ node_modules из распакованного архива. Но в чём же нюанс?
Как оказалось, запуская npm install при сборке образа, docker игнорирует закэшированный node_modules и ставит все пакеты заново 😕
Поэтому я удалил
RUN npm install
из Dockerfile. Всё работает, но минус в том, что если кто-то из разработчиков решит добавить новую библиотеку, то придётся вручную создавать архив с новым node_modules и вносить его в Object Storage. Что имеем в итоге?
Скорость выполнения CI/CD до оптимизаций: 26-28min
Скорость выполнения CI/CD после оптимизаций: 7-8min
И костыль в виде ручного добавления зависимостей в случае обновления package.json
👍1
DevOps-им по-взрослому
image_2023-11-18_00-25-01.png
❌ Кликбейтный заголовок: на самом деле скорость выполнения CI/CD увеличилась в 1.5 раза (с 15 минут до 10).... Но при этом остался нюанс, с которым работать дальше было неприемлемо.
✔️ Теперь не обманываю! Честное слово, сборка теперь занимает 2-3 минуты! (пруф на фото)
🤖 Для начала я всё же выяснил причину, почему при сборке Docker-образа npm не принимает node_modules, который передаётся из S3. Как мне подсказал ChatGPT, дело в том, что node_modules был создан на другом устройстве, поэтому npm переустанавливает все пакеты, чтобы точно ничего не сломать. Поэтому идея продолжать хранить node_modules в S3 отпала сразу.
🏗 До внедрения S3, в одном чате мне посоветовали запихнуть node_modules в Docker-образ, а затем на основе этого образа производить установку новых зависимостей (если они есть) и сборку приложения. Сначала я отклонил этот вариант, так как не представлял себе это даже возможным. Но как оказалось зря.
😏 Это же всё собирается в одном образе! Получается, что теоретически проблема с "непринятием" node_modules должна исчезнуть.... Да! Я собрал образ с одной командой
🤔 Всё вышесказанное работает локально идеально. Но что насчёт CI/CD? Дело в том, что для сборки всех приложении изначально используется Docker-executor. Поэтому Docker-образы собираются в Docker контейнерах. И для безопасной сборки (dind, асуждаем x2) используется kaniko.
Проблема возникла следующая: так как каждый раз сборка приложения запускается в чистом окружений (то есть в контейнере), то kaniko каждый раз должен пуллить образ с установленными модулями, чтобы собрать образ приложения. Так не пойдет!
👀 Для начала я решил архивировать образ и закинуть его на сервер с gitlab-runner. А что дальше?)) Тут я начал копать в сторону того, чтобы каким-то образом указать kaniko использовать tar архив с образом в качестве кэша. Но из этого ничего не вышло.
😢 "К сожалению, мы не сможем реализовать нормальное кэширование установленных модулей....". Так стоп, надо чуть подумать. Но.... Если просто не собирать приложение в Docker контейнере через kaniko, а выполнять команды прямо на ВМ и при этом использовать docker build? Точно, ведь так мы дополнительно решим вопрос с тем как подтягивать образ с модулями для последующего использования в сборке, ведь образ будет браться из локального реестра.
🧐 Но насколько это безопасно - производить запуск пайплайнов в Shell-executor? Помимо этого, я задался вопросом можно ли поставить два executor-а на одну ВМ. Ответ: можно. Конфигурация для двух раннеров будет храниться в одном файле
🤯 Интересный момент - в рамках одного Pipeline разные Job-ы можно запускать на разных gitlab-runner-ах (то есть executor-ах), при этом артефакты будут работать. Таким образом, единственное что я поменял - Job-ы для стадий build и заменил
😎 Всё работает! Сборка на Shell-executor занимает 2-3 минуты. Но всё ли прошло гладко? К сожалению, нет. После добавления раннера с Shell-executor-ом появилась неприятная проблема: скорость сборки на Docker-executor уменьшилась в два раза, а местами даже в три.
После казалось бы завершенной работы такой "подарок"....Обидно :(
После недолгого поиска в интернете, пришёл к абсолютно ничему. Решил удалить все раннеры и добавить по новой.
Это сработало! Всё вернулось на свои места, при этом сборка в Shell-executor до сих пор занимает 2-3 минуты. ПРЕЛЕСТЬ!
Из того, что явно не ожидал - скорость сборки в Docker-executor после этих манёвров увеличилась на полторы, а местами даже на три минуты!
✔️ Теперь не обманываю! Честное слово, сборка теперь занимает 2-3 минуты! (пруф на фото)
🤖 Для начала я всё же выяснил причину, почему при сборке Docker-образа npm не принимает node_modules, который передаётся из S3. Как мне подсказал ChatGPT, дело в том, что node_modules был создан на другом устройстве, поэтому npm переустанавливает все пакеты, чтобы точно ничего не сломать. Поэтому идея продолжать хранить node_modules в S3 отпала сразу.
🏗 До внедрения S3, в одном чате мне посоветовали запихнуть node_modules в Docker-образ, а затем на основе этого образа производить установку новых зависимостей (если они есть) и сборку приложения. Сначала я отклонил этот вариант, так как не представлял себе это даже возможным. Но как оказалось зря.
😏 Это же всё собирается в одном образе! Получается, что теоретически проблема с "непринятием" node_modules должна исчезнуть.... Да! Я собрал образ с одной командой
npm install
, а затем указал в Dockerfile приложения вместо образа node использовать образ с установленными зависимостями и всё сработало! При этом, при добавлений новой зависимости, она подтягивается на стадий сборки приложения! 🤔 Всё вышесказанное работает локально идеально. Но что насчёт CI/CD? Дело в том, что для сборки всех приложении изначально используется Docker-executor. Поэтому Docker-образы собираются в Docker контейнерах. И для безопасной сборки (dind, асуждаем x2) используется kaniko.
Проблема возникла следующая: так как каждый раз сборка приложения запускается в чистом окружений (то есть в контейнере), то kaniko каждый раз должен пуллить образ с установленными модулями, чтобы собрать образ приложения. Так не пойдет!
👀 Для начала я решил архивировать образ и закинуть его на сервер с gitlab-runner. А что дальше?)) Тут я начал копать в сторону того, чтобы каким-то образом указать kaniko использовать tar архив с образом в качестве кэша. Но из этого ничего не вышло.
😢 "К сожалению, мы не сможем реализовать нормальное кэширование установленных модулей....". Так стоп, надо чуть подумать. Но.... Если просто не собирать приложение в Docker контейнере через kaniko, а выполнять команды прямо на ВМ и при этом использовать docker build? Точно, ведь так мы дополнительно решим вопрос с тем как подтягивать образ с модулями для последующего использования в сборке, ведь образ будет браться из локального реестра.
🧐 Но насколько это безопасно - производить запуск пайплайнов в Shell-executor? Помимо этого, я задался вопросом можно ли поставить два executor-а на одну ВМ. Ответ: можно. Конфигурация для двух раннеров будет храниться в одном файле
config.toml
.🤯 Интересный момент - в рамках одного Pipeline разные Job-ы можно запускать на разных gitlab-runner-ах (то есть executor-ах), при этом артефакты будут работать. Таким образом, единственное что я поменял - Job-ы для стадий build и заменил
kaniko
командами docker build
и docker push
😎 Всё работает! Сборка на Shell-executor занимает 2-3 минуты. Но всё ли прошло гладко? К сожалению, нет. После добавления раннера с Shell-executor-ом появилась неприятная проблема: скорость сборки на Docker-executor уменьшилась в два раза, а местами даже в три.
После казалось бы завершенной работы такой "подарок"....Обидно :(
После недолгого поиска в интернете, пришёл к абсолютно ничему. Решил удалить все раннеры и добавить по новой.
Это сработало! Всё вернулось на свои места, при этом сборка в Shell-executor до сих пор занимает 2-3 минуты. ПРЕЛЕСТЬ!
Из того, что явно не ожидал - скорость сборки в Docker-executor после этих манёвров увеличилась на полторы, а местами даже на три минуты!
👍1
Хотите нормально хранить файлы на сервере для своих приложений? Встречайте, MinIO - S3 на своём железе!
Не раз лично встречался с проблемой хранения файлов (media) для своих Pet-проектов. Обычно нужно было копать документацию, возиться с путями, узнавать как подтягивать/загружать эти файлы. При этом файлы хранятся в той же папке, где и код. Gitignore позволит ограничить их попадание в Git-репозиторий, но согласитесь, что выглядит это ужасно. Что мне понравилось в S3, так это легкая настройка для любого популярного ЯП и простое API.
Сейчас я не занимаюсь разработкой. Но занимаюсь эксплуатацией инфраструктуры в облаке. Поэтому для хранения файлов мы остановились на Yandex Object Storage - S3 хранилище данных от Yandex.Cloud, файлы в котором копируются, а затем реплицируются по трём датацентрам, что делает хранение ваших файлов физически надёжным, а соответствие местному законодательству юридически!
Но что делать тем, кто хочет развернуть S3 совместимое хранилище данных на своём железе? К примеру, для локального запуска всех сервисов, для своих Pet-проектов или же у вас огромный страх перед облаками и вы им не доверяете... (ты знаешь о ком речь 😑 )
Мне понадобилось для локального запуска. Дело в том, что на данный момент я занимаюсь написанием docker-compose.yml файла, который одной командой должен разворачивать всё локальное окружение. И тут возник вопрос, связанный с S3. И так, поднимем эту махину!
Linux
Я разворачиваю MinIO в Docker, поэтому настройку в Linux описывать не буду. Просто оставлю команды, чтобы вы поняли как легко его запустить! Настройка бакета и доступов происходит в GUI, поэтому разобраться с этим можно без проблем.
docker-compose
Тут я использую два контейнера. Первый поднимает MinIO с заданными ACCESS_KEY и SECRET_KEY, второй подключается к хранилищу и создаёт бакет.
Теперь всё готово к работе! Креды для подключения:
host: http://localhost:9000 (from machine) / http://minio:9000 (from other containers)
access_key: access_key
secret_key: secret_key
Не раз лично встречался с проблемой хранения файлов (media) для своих Pet-проектов. Обычно нужно было копать документацию, возиться с путями, узнавать как подтягивать/загружать эти файлы. При этом файлы хранятся в той же папке, где и код. Gitignore позволит ограничить их попадание в Git-репозиторий, но согласитесь, что выглядит это ужасно. Что мне понравилось в S3, так это легкая настройка для любого популярного ЯП и простое API.
Сейчас я не занимаюсь разработкой. Но занимаюсь эксплуатацией инфраструктуры в облаке. Поэтому для хранения файлов мы остановились на Yandex Object Storage - S3 хранилище данных от Yandex.Cloud, файлы в котором копируются, а затем реплицируются по трём датацентрам, что делает хранение ваших файлов физически надёжным, а соответствие местному законодательству юридически!
Но что делать тем, кто хочет развернуть S3 совместимое хранилище данных на своём железе? К примеру, для локального запуска всех сервисов, для своих Pet-проектов или же у вас огромный страх перед облаками и вы им не доверяете... (
Мне понадобилось для локального запуска. Дело в том, что на данный момент я занимаюсь написанием docker-compose.yml файла, который одной командой должен разворачивать всё локальное окружение. И тут возник вопрос, связанный с S3. И так, поднимем эту махину!
Linux
Я разворачиваю MinIO в Docker, поэтому настройку в Linux описывать не буду. Просто оставлю команды, чтобы вы поняли как легко его запустить! Настройка бакета и доступов происходит в GUI, поэтому разобраться с этим можно без проблем.
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x ./minio
mkdir ./data
./minio server ./data
docker-compose
services:
minio:
image: minio/minio:RELEASE.2023-11-20T22-40-07Z
container_name: minio
environment:
MINIO_ROOT_USER: access_key
MINIO_ROOT_PASSWORD: secret_key
volumes:
- minio_data:/data
ports:
- "9000:9000"
command: "server /data"
create-buckets:
image: minio/mc
container_name: minio-create-buckets
depends_on:
- minio
entrypoint: >
/bin/sh -c "
/usr/bin/mc config host add myminio http://minio:9000 access_key secret_key;
/usr/bin/mc mb myminio/default-bucket;
exit 0;
"
Тут я использую два контейнера. Первый поднимает MinIO с заданными ACCESS_KEY и SECRET_KEY, второй подключается к хранилищу и создаёт бакет.
Теперь всё готово к работе! Креды для подключения:
host: http://localhost:9000 (from machine) / http://minio:9000 (from other containers)
access_key: access_key
secret_key: secret_key
⚡1👎1
🛠 Liveness и Readiness пробы: docker compose
👨🔬 Вступление: проблема
Предположим, что вам необходимо запустить два контейнера через docker compose. Но один из контейнеров требует доступ ко второму.
В моём случае есть два контейнера: keycloak и backend. backend при запуске обращается к keycloak. Но возникает следующая проблема: docker compose стартует два контейнера одновременно, но backend поднимается быстрее. Получаем ошибку:
🤔 depends_on: разве не решение?
Можно подумать, что следующий кусок YAML-а должен избавить нас от проблем:
❌ Нет. Дело в том, что по-умолчанию условием для depends_on указано "service_started". А запуск контейнера не означает, что сервис внутри этого самого контейнера запущен. Поэтому мы и получаем такую же ошибку.
🤒 healthcheck: выход!
Относительно недавно услышал про так называемые "пробы" в Kubernetes. Они позволяют отслеживать состояние запущенных в контейнере сервисов и в случае чего реагировать на их остановку. Про пробы в K8s поговорим позже. Сегодня заденем healthcheck в docker compose.
healthcheck в docker compose позволяет определить всё ли в порядке с нашим контейнером. Я уже добавил healthcheck для контейнера backend:
🤓 В healthcheck.test указывается команда, которая должна повторяться каждые healthcheck.interval секунд. Если команда выполняется успешно (code: 0), тогда контейнер переходит из статуса started в статус healthy. Если команда выполняется с ошибкой (code: >0) или время выполнения команды занимает более healthcheck.timout, то делается повторная попытка через healthcheck.interval секунд. Если проблемы повторяются таким образом healthcheck.retries раз, то контейнер переходит в статус unhealthy.
🤨 depends_on: он нам всё таки нужен
Как я писал выше, условием по-умолчанию для depends_on является статус "service_started" у контейнера. Мы должны его поменять на статус "service_healthy":
Попробуем запустить наши контейнеры и посмотреть что будет происходить:
1) запускаем docker-compose up, смотрим логи: запустился только keycloak, backend ждёт. "docker compose ps" сообщает о том, что keycloak находится в статусе "started":
2) Ждём 15 секунд, смотрим по логам запустился ли office-api. Пока нет, так как keycloak ещё не перешёл в статус healthy
3) Ждём еще 15 секунд, keycloak до сих пор не перешёл в нужный нам статус
4) На 39 секунде keycloak запустился. Но контейнер не перешёл в статус healthy. Ждём до 45 секунды (время итерации) и видим, что keycloak теперь healthy!
5) backend стартует, ждём, всё ок!
Вывод
Таким образом, при помощи healthcheck вы можете выстроить зависимости одних приложений от других. Это бывает необходимо, если, например, один сервис должен запуститься раньше, чем другой.
👨🔬 Вступление: проблема
Предположим, что вам необходимо запустить два контейнера через docker compose. Но один из контейнеров требует доступ ко второму.
В моём случае есть два контейнера: keycloak и backend. backend при запуске обращается к keycloak. Но возникает следующая проблема: docker compose стартует два контейнера одновременно, но backend поднимается быстрее. Получаем ошибку:
I/O error on GET request for "http://keycloak:8080/realms/master/.well-known/openid-configuration": Connection refused
🤔 depends_on: разве не решение?
Можно подумать, что следующий кусок YAML-а должен избавить нас от проблем:
backend:
<some_other_yaml_content>
depends_on:
- keycloak
❌ Нет. Дело в том, что по-умолчанию условием для depends_on указано "service_started". А запуск контейнера не означает, что сервис внутри этого самого контейнера запущен. Поэтому мы и получаем такую же ошибку.
🤒 healthcheck: выход!
Относительно недавно услышал про так называемые "пробы" в Kubernetes. Они позволяют отслеживать состояние запущенных в контейнере сервисов и в случае чего реагировать на их остановку. Про пробы в K8s поговорим позже. Сегодня заденем healthcheck в docker compose.
healthcheck в docker compose позволяет определить всё ли в порядке с нашим контейнером. Я уже добавил healthcheck для контейнера backend:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
interval: 15s
timeout: 3s
retries: 3
🤓 В healthcheck.test указывается команда, которая должна повторяться каждые healthcheck.interval секунд. Если команда выполняется успешно (code: 0), тогда контейнер переходит из статуса started в статус healthy. Если команда выполняется с ошибкой (code: >0) или время выполнения команды занимает более healthcheck.timout, то делается повторная попытка через healthcheck.interval секунд. Если проблемы повторяются таким образом healthcheck.retries раз, то контейнер переходит в статус unhealthy.
🤨 depends_on: он нам всё таки нужен
Как я писал выше, условием по-умолчанию для depends_on является статус "service_started" у контейнера. Мы должны его поменять на статус "service_healthy":
backend:
<some_other_yaml_content>
depends_on:
keycloak:
condition: service_healthy
Попробуем запустить наши контейнеры и посмотреть что будет происходить:
1) запускаем docker-compose up, смотрим логи: запустился только keycloak, backend ждёт. "docker compose ps" сообщает о том, что keycloak находится в статусе "started":
Up 22 seconds (health: starting)
2) Ждём 15 секунд, смотрим по логам запустился ли office-api. Пока нет, так как keycloak ещё не перешёл в статус healthy
3) Ждём еще 15 секунд, keycloak до сих пор не перешёл в нужный нам статус
4) На 39 секунде keycloak запустился. Но контейнер не перешёл в статус healthy. Ждём до 45 секунды (время итерации) и видим, что keycloak теперь healthy!
Up 46 seconds (healthy)
5) backend стартует, ждём, всё ок!
Вывод
Таким образом, при помощи healthcheck вы можете выстроить зависимости одних приложений от других. Это бывает необходимо, если, например, один сервис должен запуститься раньше, чем другой.
🛠 Liveness и Readiness пробы: Kubernetes
🤡 Шо, опять? Нит. Теперь поговорим про пробы в Kubernetes. Проблема в K8s отличается от проблемы с docker compose (в прошлом посту)
👨🔬 Проблема
После того, как разработчики заливают код в GitLab, собирается образ, а затем в Kubernetes запускается новый контейнер. Дело в том, что новый контейнер сразу же заменяет старый, при том, что сам сервис ещё находится в стадий запуска. В этот момент приложение по сути становится недоступным, что влечёт за собой ошибки других сервисов, пинающих этот самый запускаемый контейнер, а также негодование фронтендеров , тестировщиков и в будущем клиентов....
🙉 Решение: Readiness Probes
Про то, как работают пробы я уже рассказывал в прошлом посту. Принцип их работы такой же, как в Kubernetes. Но тут есть отличие: нет healthcheck, а есть возможность настроить readinessProbe и/или livenessProbe. Разница в том, что readinessProbe проверяет, что контейнер готов принимать трафик (если нет, то ждать пока проба выполнится), тогда как livenessProbe, что сервис запущен (если нет, то рестартануть контейнер). В моём случае, нужна проба readiness.
🌿 Открываем эндпоинт /health для Spring Boot
Для начала нужно определить какой какой эндпоинт "дёргать". Изучив документацию спринга, без проблем можно найти ответ. Нужно всего-лишь установить зависимость:
После запуска приложения, конечная точка автоматически станет доступна:
🧑🏻💻 Настраиваем пробу
Сразу приступим к коду:
🤓
🤩 Смотрим это работает на практике
На данный момент у нас запущен контейнер. Сделаем git push, подождём завершения пайплайна и посмотрим что будет дальше.
Пайп завершился. Смотрим на список подов:
Как видим, старый под все ещё запущен и ожидает, пока новый контейнер не встанет. Выполним describe:
Как видим, за 81 секунду проба прошла неудачно 3 раза (на 60, 70 и 80 секундах). Поэтому ждём пока бэк внутри контейнера стартанет. А пока можем убедиться, что приложение в рабочем состоянии:
Подождём немного и снова получим список всех подов:
Как мы видим, теперь у нас один под! А старого просто убили... Press F 🥺
🔥 Итоги
Некоторые контейнеры старуют очень долго, поэтому потребность в readinessProbe будет всегда. Важно сделать так, чтобы замену нового контейнера на старый никто не заметил, поэтому не откладывайте внедрение проб в ваши деплойменты, сделайте жизнь окружающих легче! 🤝
🤡 Шо, опять? Нит. Теперь поговорим про пробы в Kubernetes. Проблема в K8s отличается от проблемы с docker compose (в прошлом посту)
👨🔬 Проблема
После того, как разработчики заливают код в GitLab, собирается образ, а затем в Kubernetes запускается новый контейнер. Дело в том, что новый контейнер сразу же заменяет старый, при том, что сам сервис ещё находится в стадий запуска. В этот момент приложение по сути становится недоступным, что влечёт за собой ошибки других сервисов, пинающих этот самый запускаемый контейнер, а также негодование фронтендеров , тестировщиков и в будущем клиентов....
🙉 Решение: Readiness Probes
Про то, как работают пробы я уже рассказывал в прошлом посту. Принцип их работы такой же, как в Kubernetes. Но тут есть отличие: нет healthcheck, а есть возможность настроить readinessProbe и/или livenessProbe. Разница в том, что readinessProbe проверяет, что контейнер готов принимать трафик (если нет, то ждать пока проба выполнится), тогда как livenessProbe, что сервис запущен (если нет, то рестартануть контейнер). В моём случае, нужна проба readiness.
🌿 Открываем эндпоинт /health для Spring Boot
Для начала нужно определить какой какой эндпоинт "дёргать". Изучив документацию спринга, без проблем можно найти ответ. Нужно всего-лишь установить зависимость:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
После запуска приложения, конечная точка автоматически станет доступна:
$ curl http://localhost:8000/actuator/health
{"status":"UP","groups":["liveness","readiness"]}
🧑🏻💻 Настраиваем пробу
Сразу приступим к коду:
spec:
containers:
- name: my_app
<some_other_yaml_content>
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health
port: http
initialDelaySeconds: 50
periodSeconds: 10
🤓
httpGet.path
указывает путь для отправки запроса, httpGet.port
- название порта или число. initialDelaySeconds
отвечает за количество секунд, после которых нужно осуществить первую пробу. periodSeconds
отвечает за то, как часто осуществлять пробу. 🤩 Смотрим это работает на практике
На данный момент у нас запущен контейнер. Сделаем git push, подождём завершения пайплайна и посмотрим что будет дальше.
Пайп завершился. Смотрим на список подов:
$ kubectl get pods
my_app-5765888b6f-w7t6f 0/1 Running 0 66s
my_app-6f578d599d-22vsk 1/1 Running 0 4d22h
Как видим, старый под все ещё запущен и ожидает, пока новый контейнер не встанет. Выполним describe:
$ kubectl describe pods/my_app-5765888b6f-w7t6f
Normal Created 81s kubelet Created container my_app-app
Normal Started 81s kubelet Started container my_app-app
Warning Unhealthy 6s (x3 over 26s) kubelet Readiness probe failed: Get "http://10.112.129.116:8080/actuator/health": dial tcp 10.112.129.116:8080: connect: connection refused
Как видим, за 81 секунду проба прошла неудачно 3 раза (на 60, 70 и 80 секундах). Поэтому ждём пока бэк внутри контейнера стартанет. А пока можем убедиться, что приложение в рабочем состоянии:
$ curl https://my_app.ru/actuator/health
{"status":"UP","groups":["liveness","readiness"]}
Подождём немного и снова получим список всех подов:
$ kubectl get pods
my_app-5765888b6f-w7t6f 1/1 Running 0 13m
Как мы видим, теперь у нас один под! А старого просто убили... Press F 🥺
🔥 Итоги
Некоторые контейнеры старуют очень долго, поэтому потребность в readinessProbe будет всегда. Важно сделать так, чтобы замену нового контейнера на старый никто не заметил, поэтому не откладывайте внедрение проб в ваши деплойменты, сделайте жизнь окружающих легче! 🤝
👍4
DevOps-им по-взрослому
Photo
В начале года люди обычно подводят итоги уходящего года, а также строят цели на следующий...
Ну что ж, я решил поделиться откровением... В этом году я вёл себя плохо: ронял сервера, забивал на сертификаты, с самого начала использовал неправильные решения. Поэтому подарок от Деда Мороза не получил :(
Зато получил опыт! Как бы это банально не звучало, совершать ошибки нормально! Ненормально их скрывать или не признавать. Поэтому я и решил вскрыть чёрный ящик....
1) Не лезь, иначе убьёт: DNS-записи, почта и Ru-Center
Я впервые столкнулся с Ru-Center и первое (и последнее тоже) впечатление было ужасным. В отличие от reg.ru, с которым я работал ранее, тут было недостаточно купить домен, чтобы просто добавить DNS-запись для поддомена. Во-первых, вам нужно приобрести отдельную услугу "DNS-хостинг". Во-вторых, вам нужно выбрать правильные DNS-зоны, которые, к тому же, для нашего домена были настроены неверно. На это всё суммарно была затрачена неделя!
Случилось страшное....Я уронил почту на домене! 😱 Как это случилось?
Я достаточно долго боролся с тем, что не мог выпустить TLS-сертификат для поддомена (как оказалось, дело было в DNS-зонах). Я перепробовал почти все способы, поэтому подумал в сторону DNS-записей.... И просто удалил MX и CNAME записи, необходимых для работы почты....
Моя реакция была мгновенной: почта упала сразу же, а я стал в панике искать как вернуть те самые записи. 🤕 Почти час прошуршав весь интернет (по nic.ru достаточно мало источников), я всё же обнаружил в настройках DNS-записей вкладку "Шаблоны", где и смог найти заветную кнопку "Записи для почты".
Но она заработала не сразу.... Дело в том, что обновление записей может занять от 10 минут до 24 часов! Таким образом, почта встала только спустя 4 часа.
Что можно из этого вынести? Как и из всех косяков, всегда думать про последствия. А также не забывать про бекапы, (можно импортировать DNS-записи в текстовом формате) которые я забыл сделать(
2) Используйте terraform с умом и внимательностью...
Я много писал про Terraform. Это очень полезная штука, позволяющая управлять инфраструктурой-как-код. Важно понимать, что Terraform также является весьма опасным инструментом в руках неопытного девопса. Про какого девопса идёт речь вы уже поняли) 😅
Когда я только перевёл всю существующую инфраструктуру в код, я занялся некоторым редактированием конфигов. Я понимал опасность команды
В один прекрасный летний день я выполнил
Последствия были не такими серьезными, но всё же были. Один разработчик обратился ко мне с тем, что отвалился пайплайн. Я быстро ему объяснил ситуацию и он с пониманием набрался терпением, чтобы я всё поднял. (респект коллеге 🤝)
Как оказалось, поднять ВМ и поставить туда gitlab-runner оказалось недостаточным, потому что гитлаб не мог его увидеть. Дошло до того, что пришлось пересоздавать ВМ. Таким образом, на все манипуляции было затрачено ~полтора часа. 🙁 Эти полтора часа никто не мог залить свой код на дев окружение!
Что из этого следует? А то, что нужно внимательно следить за каждым своим действием, особенно если есть риск затронуть что-нибудь важное. С тех пор, я дважды пересматриваю вывод
Ну что ж, я решил поделиться откровением... В этом году я вёл себя плохо: ронял сервера, забивал на сертификаты, с самого начала использовал неправильные решения. Поэтому подарок от Деда Мороза не получил :(
Зато получил опыт! Как бы это банально не звучало, совершать ошибки нормально! Ненормально их скрывать или не признавать. Поэтому я и решил вскрыть чёрный ящик....
1) Не лезь, иначе убьёт: DNS-записи, почта и Ru-Center
Я впервые столкнулся с Ru-Center и первое (и последнее тоже) впечатление было ужасным. В отличие от reg.ru, с которым я работал ранее, тут было недостаточно купить домен, чтобы просто добавить DNS-запись для поддомена. Во-первых, вам нужно приобрести отдельную услугу "DNS-хостинг". Во-вторых, вам нужно выбрать правильные DNS-зоны, которые, к тому же, для нашего домена были настроены неверно. На это всё суммарно была затрачена неделя!
Случилось страшное....Я уронил почту на домене! 😱 Как это случилось?
Я достаточно долго боролся с тем, что не мог выпустить TLS-сертификат для поддомена (как оказалось, дело было в DNS-зонах). Я перепробовал почти все способы, поэтому подумал в сторону DNS-записей.... И просто удалил MX и CNAME записи, необходимых для работы почты....
Моя реакция была мгновенной: почта упала сразу же, а я стал в панике искать как вернуть те самые записи. 🤕 Почти час прошуршав весь интернет (по nic.ru достаточно мало источников), я всё же обнаружил в настройках DNS-записей вкладку "Шаблоны", где и смог найти заветную кнопку "Записи для почты".
Но она заработала не сразу.... Дело в том, что обновление записей может занять от 10 минут до 24 часов! Таким образом, почта встала только спустя 4 часа.
Что можно из этого вынести? Как и из всех косяков, всегда думать про последствия. А также не забывать про бекапы, (можно импортировать DNS-записи в текстовом формате) которые я забыл сделать(
2) Используйте terraform с умом и внимательностью...
Я много писал про Terraform. Это очень полезная штука, позволяющая управлять инфраструктурой-как-код. Важно понимать, что Terraform также является весьма опасным инструментом в руках неопытного девопса. Про какого девопса идёт речь вы уже поняли) 😅
Когда я только перевёл всю существующую инфраструктуру в код, я занялся некоторым редактированием конфигов. Я понимал опасность команды
terraform apply
, что нужно прежде выполнять terraform plan
и внимательно читать вывод команд утилиты. Но скорее всего понимал не до конца....В один прекрасный летний день я выполнил
terraform apply
, при этом лишь частично взглянув на изменения. Как оказалось, я удалил совсем не то, что нужно, а именно ВМ с работающим gitlab-runner в разгар рабочего дня... 🥴Последствия были не такими серьезными, но всё же были. Один разработчик обратился ко мне с тем, что отвалился пайплайн. Я быстро ему объяснил ситуацию и он с пониманием набрался терпением, чтобы я всё поднял. (респект коллеге 🤝)
Как оказалось, поднять ВМ и поставить туда gitlab-runner оказалось недостаточным, потому что гитлаб не мог его увидеть. Дошло до того, что пришлось пересоздавать ВМ. Таким образом, на все манипуляции было затрачено ~полтора часа. 🙁 Эти полтора часа никто не мог залить свой код на дев окружение!
Что из этого следует? А то, что нужно внимательно следить за каждым своим действием, особенно если есть риск затронуть что-нибудь важное. С тех пор, я дважды пересматриваю вывод
terraform plan
, а затем ещё трижды от terraform apply
прежде чем ввести yes. Потому что на месте этой ВМ могла оказаться вся инфраструктура!❤🔥1👎1
DevOps-им по-взрослому
В начале года люди обычно подводят итоги уходящего года, а также строят цели на следующий... Ну что ж, я решил поделиться откровением... В этом году я вёл себя плохо: ронял сервера, забивал на сертификаты, с самого начала использовал неправильные решения.…
...
3) Эта штука точно не нужна! Так стоп... Кластер Kubernetes перестал работать 😓
Эта штука называется Yandex Key Management Service (KMS). Наверное это единственный сервис, который я использую, при этом не понимая как он работает или работает вообще. 🤡
Последний фактор сыграл со мной злую шутку... Так как я хотел сделать всё "по бэст практисам" и "секьюрно", то перед созданием кластера Kubernetes я сначала создал ключ KMS, а затем указал его при создании кластера кубера. 🙂
Спустя некоторое время, проводя "инвентаризацию" инфраструктуры и наткнувшись на этот ключ, решил, что он только глаза мозолит и решил данную проблему удалением. 😶🌫️
Что же случилось? Кластер Kubernetes не мог продолжать работу, так как ключ был утерян, а восстановить его было невозможным. Пришлось сносить... 👷
А удаление Kubernetes означает, что все сервисы сразу станут недоступны, данные будут утеряны и придется настраивать всё с нуля. К счастью, никаких важных данных утеряно не было, а поднять все поды оказалось весьма простой задачей. Дополнительным фактором, который немного успокоил меня, было то, что это происходило вечером, поэтому за ночь смог развернуть кластер с нуля. Надеюсь, что никто не был блокнут и пострадал только мой режим сна. 🥹
Урок из этого был вынесен: во-первых, прежде чем что-то удалять, убедитесь, что это никак не затронет работу других сервисов. Во-вторых, читайте документацию! 📃
——
Это был список самых-самых ошибок в прошлом году. Конечно, это не весь список, были и остальные, но их последствия были не такими, как в вышеописанных "косяках".
3) Эта штука точно не нужна! Так стоп... Кластер Kubernetes перестал работать 😓
Эта штука называется Yandex Key Management Service (KMS). Наверное это единственный сервис, который я использую, при этом не понимая как он работает или работает вообще. 🤡
Последний фактор сыграл со мной злую шутку... Так как я хотел сделать всё "по бэст практисам" и "секьюрно", то перед созданием кластера Kubernetes я сначала создал ключ KMS, а затем указал его при создании кластера кубера. 🙂
Спустя некоторое время, проводя "инвентаризацию" инфраструктуры и наткнувшись на этот ключ, решил, что он только глаза мозолит и решил данную проблему удалением. 😶🌫️
Что же случилось? Кластер Kubernetes не мог продолжать работу, так как ключ был утерян, а восстановить его было невозможным. Пришлось сносить... 👷
А удаление Kubernetes означает, что все сервисы сразу станут недоступны, данные будут утеряны и придется настраивать всё с нуля. К счастью, никаких важных данных утеряно не было, а поднять все поды оказалось весьма простой задачей. Дополнительным фактором, который немного успокоил меня, было то, что это происходило вечером, поэтому за ночь смог развернуть кластер с нуля. Надеюсь, что никто не был блокнут и пострадал только мой режим сна. 🥹
Урок из этого был вынесен: во-первых, прежде чем что-то удалять, убедитесь, что это никак не затронет работу других сервисов. Во-вторых, читайте документацию! 📃
——
Это был список самых-самых ошибок в прошлом году. Конечно, это не весь список, были и остальные, но их последствия были не такими, как в вышеописанных "косяках".
❤🔥1👎1
Telegraph
Выбросьте принты, используйте Debugger!
Вы решаете сложную задачу на алгоритмы, пишите аналог VK, сидите на олимпиаде и ничего не понимаете (жиза) - в любом случае вам, скорее всего, нужен Debugger. Зачем? Стоит смириться с тем, что баги в коде будут возникать всегда. При этом важно их быстро и…
До сих пор используете кипячение принты? Начните использовать тайд дебаггер и ваша жизнь станет намного легче!
Читать: тык
Читать: тык
❤3👎1🔥1