Bash Days | Linux | DevOps
23.3K subscribers
151 photos
25 videos
663 links
Авторский канал от действующего девопса

Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.

Автор: Роман Шубин
Реклама: @maxgrue

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
Proxmox post install in 2025 (часть 1)

Давай разберемся, что нужно сделать после установки proxmox.

Как вариант сразу накатить эту штуку Proxmox VE Post Install, но процесс не прозрачный.


Наш путь — это всё сделать ручками, чтобы было понимание и контроль над процессом.

Для начала пиздуем сюда:

/etc/apt/sources.list


И добавляем дополнительные репозитории:

deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
deb http://security.debian.org/debian-security bookworm-security main contrib


Затем отключаем интерпрайзный репозиторий в файле:

/etc/apt/sources.list.d/pve-enterprise.list


В нем комментируем одну строчку:

# deb https://enterprise.proxmox.com/debian/pve bookworm pve-enterprise


Следом отключаем платный ceph, идем в файл:

/etc/apt/sources.list.d/ceph.list


Комментируем и добавляем халявную репу:

# deb https://enterprise.proxmox.com/debian/ceph-quincy bookworm enterprise
deb http://download.proxmox.com/debian/ceph-quincy bookworm no-subscription


После этого выполняем:

apt update && apt upgrade
reboot


Всё раздуплится и обновится из бесплатных репозиториев. После этого перезагружаем сервер, потому что прилетело достаточно много важного и серьезного.

Дальше выполняем в терминале proxmox:

sed -Ezi.bak "s/(Ext.Msg.show\(\{\s+title: gettext\('No valid sub)/void\(\{ \/\/\1/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js


Эта команда отключит No valid subsciption, сообщение о том, что у нас отсутствует подписка на интерпрайз. Довольно нозящая и заёбистая тема.

Чтобы изменения вступили в силу, скинь кэш в браузере. Всё, больше это информационное окошко тебя не побеспокоит.


На этом можно и закончить, но есть еще момент с прокидываем физических устройств в виртуальные машины.

Этот момент рассмотрим во второй части (завтра в 14:13 МСК залетит, пост в отложке), там похитрее всё.

🛠 #proxmox #devops

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
97
Proxmox post install in 2025 (часть 2)

Такс, следующим этапом после установки proxmox необходимо настроить проброс реальных устройств в виртуальные машины.

Проброс устройств подразумевает — устройства которые подключены к материнской плате на хостовой машине. Например, видюха, модемы и т.п. То есть виртуальные машины смогут их использовать.


Если устройство проброшено в виртуальную машину, то ни хостовая машина ни другие виртуальные машины использовать это устройство не смогут. Короче устройство будет изолировано.

У меня таким боком прокинут рутокен в виндовую виртуалку, чтобы в ЭДО доки подписывать с любого устройства и из любой жопы мира.


Открываем на редактирование файл: /etc/default/grub

И правим GRUB_CMDLINE_LINUX_DEFAULT, должно получиться так:

Если у тебя Intel:

GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on pt=on"


Если у тебя AMD

GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on pt=on"


iommu=on — включает IOMMU (Input-Output Memory Management Unit) для процессоров. Этот механизм позволяет изолировать устройства ввода-вывода, повышая безопасность и управление памятью.


pt=on — включает поддержку PCI passthrough для виртуализации, позволяя передавать устройства (например, видеокарты или другие PCI-устройства) непосредственно в виртуальные машины.


Сохраняем изменения и обновляем загрузчик:

update-grub
reboot


По-хорошему proxmox нужно отправить в перезагрузку, но это не точно. В любом случае для чистоты эксперимента я все же ребучу.

Загружаем модули:

Открываем на редактирование: /etc/modules

И прописываем:

vfio
vfio_iommu_type1
vfio_pci


С версии ядра 6.2 модуль vfio_virqfd больше нахуй не нужен.

Сохраняем и запускаем:

update-initramfs -u -k all
reboot


Здесь обязательно нужен ребут, без ребута нихуя НЕ заработает!

Проверяем:

dmesg | grep -e DMAR -e IOMMU or dmesg | grep -e DMAR -e IOMMU


И ищем строчку: Interrupt remapping enable

Если нашел, то поздравляю, у тебя всё получилось! Если же нет, то еще раз внимательно выполни все шаги и не забудь про ребут.

На закуску

Чтобы изолировать GPU карточку от хостовой системы, нужно еще с бубном побегать.

lspci -nn


Узнаём ID карточки, а дальше подставляем этот ID в эту команду:

echo "options vfio-pci ids=10de:____,10de:____ disable_vga=1" > /etc/modprobe.d/vfio.conf


И пиздярим блеклисты:

echo "blacklist radeon" >> /etc/modprobe.d/blacklist.conf 
echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf
echo "blacklist nvidia" >> /etc/modprobe.d/blacklist.conf
echo "blacklist nvidiafb" >> /etc/modprobe.d/blacklist.conf
echo "blacklist nvidia_drm" >> /etc/modprobe.d/blacklist.conf
echo "blacklist i915" >> /etc/modprobe.d/blacklist.conf


Ну и само собой отправляем proxmox в ребут.

Проброс HDD

ls -n /dev/disk/by-id/


Узнаём ID диска и выполняем команду:

/sbin/qm set [VM-ID] -virtio2 /dev/disk/by-id/[DISK-ID]


Вот и вся наука. Всё лично протестировано и работает как часы, включая проброс видюхи.

Если есть чего добавить, велком в комменты.

🛠 #proxmox #devops

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
49
Бомбардиро крокодило!

Я выспался, можно продолжать радовать вас всякой «нужной» хуйней.

Давно про nip.io ничего не слышал, но всё чаще встречаю его у ребят с Linux Factory.

Суть этого сервиса — автоматически разрешать поддомены в IP адреса без необходимости поднимать свой DNS сервер.

Сейчас как мы делаем, локально редактируем файл /etc/hosts, прописываем в него какие-то свои локальные айпишники на вымышленные домены.

Например:

192.168.0.10 nginx.local
192.168.0.11 grafana.local


И если перейти по адресу nginx.local попадаешь на виртуалку с айпишником 192.168.0.10. Это всё очевидно.

Но опять же как оказалось про манипуляции с hosts файлом многие не знали.


А ведь это база. С помощью которой ты к примеру поднимаешь новый продакшен. Но что бы протестировать его, тебе нужно подкинуть на новый продакшен домен со старого продакшена. А тебе такое сделать не дадут.

Вот и прописываешь себе в hosts этот домен, указываешь айпишник нового прода, идешь тестишь, если все ок, то меняешь A запись.

Вернемся к nip.io, суть там такая же, прописываешь айпишники и нужные домены и по итогу получаешь такое:

nginx.192.168.0.10.nip.io → 192.168.0.10
app.192.168.0.11.nip.io → 192.168.0.11
test.192.168.0.12.nip.io → 192.168.0.12


Теперь открыв nginx.192.168.0.10.nip.io ты попадешь на 192.168.0.10 без редактирования своего hosts файла.

Давай потыкаем

У меня на IP 192.168.10.6 в локалке висит малина со всякими открытыми портами.

Пингуем:

ping 192.168.10.6.nip.io


В ответ я получаю такую картинку:

Server: 8.8.8.8
Address: 8.8.8.8#53

Non-authoritative answer:
Name: 192.168.10.6.nip.io
Address: 192.168.10.6


Отлично, пинг прошел, вернулся локальный айпишник. То есть мы пинганули какой-то глобальный домен, а он привел нас на локальный сервис.

Теперь я направляюсь в браузер и открываю url:

http://192.168.10.6


Мне открывается стартовая страница nginx. Тут всё понятно и логично. А теперь давай сделаем так.

http://nginx.192.168.10.6.nip.io


Хуяк и видим предупреждение:

Для сайта 192.168.10.6.nip.io не поддерживается защищенное подключение


Ну клёвое же. Запрос прошел через глобальный домен и перенаправил запрос в мою локальную сеть. Без необходимости что-то прописывать в hosts файле.

Просто жмем «продолжить» и попадаем снова на стартовую страницу nginx.

Еще вариант с портами:

http://sync.192.168.10.6.nip.io:8384


На порту 8384 у меня висит syncthing и всё отлично открывается. То есть можно пробрасывать запросы через домен прям на порты или в докер контейнеры.

А можно прям кучу поддоменов плодить:

http://a.b.c.d.192.168.10.6.nip.io/


И всё будет работать.

А еще можно на такие домены получить SSL сертификаты, но у тебя должен быть белый айпишник.

sudo certbot certonly --standalone -d nginx.204.0.115.50.nip.io


Хуяк и у тебя готовый SSL для домена nginx.204.0.115.50.nip.io. Ну а если не хочется возиться с certbot можно воспользоваться алтернативами, например acme.sh или caddy.

Минусы nip.io:

1. Не поддерживает wildcards, но в большинстве случаев они избыточны. Инструмент заточен больше на удобное тестирование при разработке.

2. SSL работает только с публичными IP. Для 127.0.0.1.nip.io или 192.168.x.x.nip.io Let's Encrypt не выдаст сертификат.

У nip.io есть альтернативы: xip.io или sslip.io

В общем я показал, а тебе уже самому решать где это применить или не применить.


Хорошо тебе провести эти выходные. Ну а я пошел фиксить баги.
Увидимся!

🛠 #devops #networks

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
62
У Hashicorp Vault есть прикольная фишка со встроенным ssh враппером и подписными ключами.

То есть ты не напрямую подключаешься к серверу:

ssh root@server


А подключаешься к серверу через сервер с Vault, получается что-то вроде бастиона. Vault подписывает ключи и запускает тебя в нужное место.

Настраивается эта штука так:

На сервере с Vault выполняем:

vault secrets enable -path=ssh ssh
vault write ssh/config/ca generate_signing_key=true


Создаём под это дело отдельную роль:

vault write ssh/roles/devops -<<"EOH"
{
"allow_user_certificates": true,
"allowed_users": "*",
"allowed_extensions": "permit-pty,permit-port-forwarding,permit-agent-forwarding,permit-user-rc,permit-X11-forwarding",
"default_extensions": [
{
"permit-pty": ""
}
],
"key_type": "ca",
"default_user": "root",
"ttl": "30m0s"
}
EOH


На сервере к которому хотим подключаться, делаем:

curl -o /etc/ssh/trusted-user-ca-keys.pem https://vault.bashdays.ru:8200/v1/ssh/public_key


Добавляем в конфиг /etc/ssh/sshd_config строчку:

TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem


И перезапускаем: systemctl restart sshd

Переходим на свою локальную машину, с которой будем подключаться:

export VAULT_TOKEN="token"

vault write -address='https://vault.bashdays.ru:8200' ssh/sign/devops public_key=@$HOME/.ssh/id_rsa.pub


Этой командой подписываем свой публичный ключ через vault сервер. В ответ получаем кучу всякой хуйни типа: serial_number и signed_key.

Сохраняем на локальную машину подписанный ключ:

export VAULT_TOKEN="token"

vault write -address='https://vault.bashdays.ru:8200' --field=signed_key ssh/sign/devops public_key=@$HOME/.ssh/id_rsa.pub > ~/.ssh/id_rsa_signed_key.pub


Смотрим параметры получившегося ключа:

ssh-keygen -Lf /Users/user/.ssh/id_rsa_signed_key.pub`


Опять же получишь портянку информации, можно посмотреть валидность и время жизни этого ключа.

Теперь подключаемся:

ssh -i ~/.ssh/id_rsa_signed_key.pub -i ~/.ssh/id_rsa root@server


Ну и вишенка
, подключаемся через враппер:

vault ssh -address='https://vault.bashdays.ru:8200' -private-key-path=~/.ssh/id_rsa -public-key-path=~/.ssh/id_rsa.pub -mode=ca -role=devops -user-known-hosts-file=/dev/null root@server


В этом случае подписывать ключ не обязательно.

Доступ к серверам контролирует Vault. Можно быстро накручивать необходимые права, забирать и отзывать ключи. Ну и там много чего еще можно сделать.

Выглядит крайне неудобно и не интуитивно. Но порой такую штуку можешь встретить в компаниях. Хотя я против такой хуйни, какая-то блядь избыточная безопасность.


Что-то вроде центра сертификации, писал как-то отдельный пост на эту тему.

Нашел еще видос у пиндоса на эту тему, можешь визуально глянуть и проникнутся.

Такие дела!

🛠 #linux #devops

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
156
Как оказалось многие испытывают неистовые жопные боли при настройке angie со встроенной поддержкой SSL сертификатов от Lets Encrypt.

Сегодня покажу как избавиться от боли и на лету получать SSL для angie. Без всяких там certbot и acme.sh. Довольно удобно реализовано.

Для чистоты эксперимента я взял чистый, прерываемый сервер, без nginx’ов и т.п. на базе Ubuntu 24.04.


angie это аналог nginx, но на стероидах, у меня он крутится на уроках для LinuxFactory и в блоге. Подкупил он меня нативной поддержкой LUA и кучей модулей включая авто-получение SSL сертов.

Устанавливаем angie:

sudo curl -o /etc/apt/trusted.gpg.d/angie-signing.gpg https://angie.software/keys/angie-signing.gpg

echo "deb https://download.angie.software/angie/$(. /etc/os-release && echo "$ID/$VERSION_ID $VERSION_CODENAME") main" | sudo tee /etc/apt/sources.list.d/angie.list > /dev/null

sudo apt-get update
sudo apt-get install -y angie


Начиная с Angie 1.3.0, модуль ACME (http_acme_module) включён по умолчанию в основной пакет angie.

Открываем айпишник сервера в браузере и убеждаемся что все работает. Если всё заебись, получишь стартовую страницу angie. Дефолтная страница практически один в один повторяет дефолтную страницу от nginx.

Дальше у меня есть домен two.su который живет в Cloudfalre. В настройках DNS я прописываю A запись и указываю айпишник нового сервера. Весь трафик идет напрямую, без фильтрации самого Cloudfalre.


Конфигуряем angie на домен two.su.

Создаем файл two.su.conf в /etc/angie/httpd.d/ с таким содержимым.

    server {
listen 443 ssl;
server_name two.su www.two.su;

acme letsencrypt;
ssl_certificate $cert_letsencrypt;
ssl_certificate_key $cert_key_letsencrypt;

location / {
root /var/www/html;
}
}


А в файле /etc/angie/angie.conf в секцию http добавляем:

    acme_client letsencrypt https://acme-v02.api.letsencrypt.org/directory;
resolver 127.0.0.53;

map $acme_cert_letsencrypt $cert_letsencrypt {
'' /etc/angie/ssl-self-signed/cert.pem;
default $acme_cert_letsencrypt;
map $acme_cert_letsencrypt $cert_key_letsencrypt {
'' /etc/angie/ssl-self-signed/key.pem;
default $acme_cert_key_letsencrypt;
}


Если angie у тебя работает в докере, то в resolver пропиши: 127.0.0.11.

Проверяем: angie -t и в ответ получаем:

angie: the configuration file /etc/angie/angie.conf syntax is ok
angie: configuration file /etc/angie/angie.conf test is successful


Полученный сертификат и соответствующий ключ будут доступны в конфигурации через переменные $acme_cert_<имя> и $acme_cert_key_<имя>.

Перезапускаем: systemctl reload angie

Если получил ошибку, закинь сертификаты заглушки в /etc/angie

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -subj "/C=RU/O=Bashdays/CN=LinuxFactory"


У меня никаких ошибок не возникло.

В папке: /var/lib/angie/acme/letsencrypt появились ключи и серты.

Захожу в браузере на two.su и вижу зеленый замочек. Что и требовалось доказать. SSL сертификат был автоматически получен и в дальнейшем будет автоматически продляться. Без всяких кронов, клиентов, API ключей и хуйни.

Единый сертификат будет получен для всех доменных имён, которые были перечислены в директиве server_name. А директива acme указывает, какой ACME-клиент должен использоваться.


Меня вся эта кухня полностью устраивает. Понятно дело есть Nginx Proxy Manager, но я привык работать без гуёвых штук и мордочек.

Когда что-то настраиваешь руками, а не мышкой, то имеешь представление как работает технология изнутри. А если ты это знаешь, то легко сможешь отдебажить баги и найти первопричину.

По крайней мере меня так учили и я с этим полностью согласен.

🛠 #linux #devops #angie #ssl

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
372
Распространенный случай в пайплайнах, как ребята пишут в LF:

deploy:
stage: deploy
script:
- ssh ${USER}@${HOST} "docker pull"
- ssh ${USER}@${HOST} "docker down"
- ssh ${USER}@${HOST} "docker up -d"
- ssh ${USER}@${HOST} "....."


🔥 Как и обещал. С сегодняшнего дня в Linux Factory действуют летние скидки. Кто ждал, велком.


То есть на каждую команду, создается отдельная SSH сессий. В большинстве случаев у тебя всё будет работать, но порой можно наступить на грабли.

На сервере может быть установлены лимиты в ssh_configMaxSessions, а плюсом еще работает Fail2ban или нечто подобное.

И по итогу пайплайн будет вечно делать хуйню, внезапно падать и т.п.

Что делать?

Передать команды в рамках одной сессии.

Например, так:

script:
- |
ssh ${USER}@${HOST} << EOF
docker pull ...
docker down ...
docker up -d ...
...
EOF


В YAML, конструкция | перед блоком многострочного текста указывает, как обрабатывать переносы строк. | означает: сохраняй все переносы строк, как они написаны.


Либо пропихать так:

script:
- ssh ${USER}@${HOST} "docker pull ...; docker down ...; ..."


Но предпочтительнее первый вариант, он консистентный и более читаемый.

Учись сразу делать нормально и учитывать такие моменты.

Ну и с праздником тебя и твоих ребятишек!

🛠 #devops #linuxfactory #cicd

@bashdays / @linuxfactory / @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
782
Довольно частый вопрос с LFКак жить с gitlab раннерами?

Да просто там всё. Если у тебя основная инфра в кубе, то в кубе и крутим.

Ну а если ламповые сервера, без всей этой кубовой хуйни, то вариантов не много.

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

Здесь важно раздуплить себе какой-нибудь гарбаж коллектор, либо башник в крону накидать.

Часто раннеры пихают прям на продакшен сервера, где крутятся все бизнес процессы. В этом случае продакшен превращается в помойку, нагрузка постоянно скачет при сборках/деплое и т.п.

Это хуёвая практика, на продакшене такого быть не должно. Лишь поэтому мы и поднимаем отдельный сервер под раннеры.


Подняли сервер, нарегали нужных раннеров. В идеале это процесс автоматизировать, ансибл-хуянсибл ну или чё там у тебя есть.

Раннеры можно зарегать на один токен:

Сервер 1

gitlab-runner register \
--url https://gitlab.com/ \
--registration-token ABCDEFG123456 \
--executor docker \
--description "runner-1"


Сервер 2

gitlab-runner register \
--url https://gitlab.com/ \
--registration-token ABCDEFG123456 \
--executor docker \
--description "runner-2"


В морде гитлаба появятся 2 отдельных раннера, а далее гитлаб будет балансировать между ними. Также можешь указать одинаковые теги. Тут у нас все сводится в сторону масштабируемости.

Раннер 1

--description "runner-fast"
--tag-list "docker"


Раннер 2

--description "runner-slow"
--tag-list "docker"


В .gitlab-ci.yml у нас так:

build:
script: echo "Building"
tags:
- docker


Задание пойдет на тот раннер, который раньше освободится. Если оба свободны — гитлаб рандомно сделает выбор, приоритетности нет.

Если важна приоритетность, можно поиграть в юните с параметром GITLAB_RUNNER_PRIORITY.

Короче если хочешь распределять нагрузку по раннерам с одинаковыми тегами, и раннера работают на разных машинах, просто регистрируй всех с одинаковыми тегами — гитлаб сам уже всё разрулит.


А как деплоить?

Тоже всё просто, ssh/rsync в помощь, раннер подключается к нужному серверу, например по ssh и выполняет все необходимые команды. Затаскивает новый докер образ, стопорит старый, запускает новый.

Как-то так:

deploy:
stage: deploy
script:
- |
ssh $SSH_USER@$SSH_HOST << EOF
docker pull $IMAGE_NAME
docker stop my_app || true
docker rm my_app || true
docker run -d -p 5000:5000 --name my_app $IMAGE_NAME
EOF


Подключаемся под юзером из переменной $SSH_USER к хосту из переменной $SSH_HOST. Ну и запускаем команды.

Важно!

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

Основное рассказал, если что-то еще вспомню, накидаю. Если у тебя есть мысли, пиши в комментарии, будем обсуждать.

🛠 #devops #linuxfactory #cicd

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
455
GIT для девопс-инженера

Многих git пугает разнообразием команд, но 99% его функционала тебе никогда не пригодится. Это как MS Word или Excel, где ты используешь ну от силы 0000.1% всего задуманного в нём.

Да, разработчикам посложнее, надо разруливать мердж-конфликты, делать какие-то ебаные черепики и ребейзы. Но для программистов на yml, достаточно основной базы.

Стянул-закомитил-запушил. Не пушится? Похуй — push force!


Теорию по git опустим, ты и сам знаешь для чего оно всё нужно — для удобства.

Я к примеру в гите храню базу obsidian и совсем недавно это спасло все мои заметки. Я установил плагин для синка с seafile и оно мне нахуй всё стерло. У меня сука глаз выпал, овер 3к заметок проебало за секунду.

Но в гите у меня все это осталось, склонировал, восстановил. Бекап который однажды пригодился. Удобно.


К сути. Как я сказал выше, тебе достаточно базы:

git pull
git commit -m "ебал я вашу буравую, несите книжку трудовую"
git push


К этому можно еще добавить clone, checkout и init, но этим ты будешь пользоваться очень редко.

Как все работает в реальности

У тебя есть корпоративная учетка в гитлабе/гитхабе, там уже сто пудово созданы репы. Копируешь мышкой строку для клона, вставляешь себе в терминале.

git clone git@git.bashdays.ru:shubin/obsidian.git


ВСЁ! Репа у тебя на руках.

Создаешь новую ветку от мастера:

git checkout -b 010825


И уже в нее говнокодишь. Когда говна достаточно, комитишь, делаешь пулл-реквест (мердж-реквест). После того как тимлид заапрувит твой реквест, правки вольются в мастер. А ветку 010825 можно закрывать.

Пункт с реквестами опять-же маловероятен, если компания не большая и ты там царь и бог девопса. Сразу хуяришь в мастер и никого не слушаешь. Отпадает необходимость создавать ветки и т.п. Ведь ты же всегда все делаешь идеально.

Я порой прям в гитлабе правлю через внутренний редактор, ебал я локально себе что-то клонировать. Быстрее через морду зайти и пайп подправить.

На этом можно и заканчивать, все элементарно. Не бойся гита, это хуйня не страшная. Если что-то пошло по пизде, удали, склонируй мастер и начни с чистого листа.

А как настроить ключи и т.п. я писал много постов, поищи по тегу #linuxfactory

А тут разбирали почему порой главная ветка называется main а не master.

🛠 #git #devops

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
68
This media is not supported in your browser
VIEW IN TELEGRAM
Про яйца и балансировщик нагрузки

Когда нужно распределить запросы между серверами, возникает проблема — а как блядь понять, какой сервер сейчас менее нагружен?

Постоянно опрашивать сервера, это хуйня, информация будет не актуальной, все это медленно, пока мы собирали данные, ситуация уже поменялась.

На этом фоне возникает эффект «стада», когда много запросов дружно бегут туда, где вроде бы свободно и по итогу перегружают сервер еще больше.

Это можно решить проще — например перенаправлять запросы случайно.

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

Какой-то замкнутый круг, печаль и говнище.

А чё делать?

Представь: я в рандоме выбираю два сервера, смотрю какой из них менее загружен и отправляю туда запросы. Все!

Вроде мелочь, но работает этот подход прям заебись! И что интересно у этого подхода есть официальная формулировка — Power of Two Choices или «Сила двух выборов».

Почему это заебись?

На просторах есть задачка «яйца и корзины», в оригинале там конечно не «яйца», а шары.

Короче. Мы случайно кидаем яйца в корзины. Если класть каждое яйцо в случайную корзину, то в итоге некоторые корзины будут переполнены.

Но если каждый раз выбирать две корзины и класть яйцо туда, где их меньше — ситуация резко улучшается.

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

Все просто! Тоже самое и с серверами:

- Если мы выбираем сервер наугад, нагрузка на сервер может стать критической.
- Если выбираем между двумя, вероятность перегрузить сервер падает квадратично.

Давай на котиках:

Представь, что у тебя есть куча серверов, и на четверти из них пришло по 4 запроса.

- Если я выбираю сервер случайно, есть 25% шанс попасть именно на сервер, которому уже пезда.

- Если я выбираю два сервера и беру менее загруженный — вероятность того, что оба окажутся с нагрузкой 4, уже всего 6,25% (1/16).

Получается, что вероятность перегрузки падает очень быстро. А дальше — ещё быстрее: чтобы нагрузка выросла до 5, 6 и так далее, нужно каждый раз «удачно» попасть в редкие перегруженные сервера, и шанс становится ничтожным.

Видишь, математика нужна не только бекендерам, но и в девопсе можно применить.

Что получаем на практике:

На практике балансировка «выбором из двух» делает распределение нагрузки по серверам почти равномерной. При этом мы не тратим кучу ресурсов на мониторинг и не пытаемся каждый раз точно узнать состояние всех серверов.

Такой подход - простой и элегантный. Как говорится — без хуйни!

Формулы и расчеты визуально можешь глянуть тут. Там как раз разбирается способ с яйцами, но уже с углубленной математикой и формулами.

Этот же принцип используют, например, в cuckoo hashing (структура данных для хранения ключей).

Cuckoo hashing (кукушкино хеширование) — алгоритм разрешения коллизий значений хеш-функций в таблице с постоянным временем выборки в худшем случае.


Как реализовать в nginx

upstream backend {
random two; # Power of 2 Choices
server app1.bashdays.ru;
server app2.bashdays.ru;
server app3.bashdays.ru;
}


Вместо случайного выбора nginx берёт два случайных сервера и кидает запрос на тот, где меньше соединений.

Этот же принцип используют, например, в cuckoo hashing (структура данных для хранения ключей).


Мотай на ус, глядишь сгодится в хозяйстве.

🛠 #devops #nginx

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
786
Фиксим кривой Exit Code в docker

Во время работы с docker контейнером наткнулся на неочевидный код выхода (exit code).

Про exit codes (коды выхода) писал тут


Суть проблемы: Когда программа внутри контейнера падает с abort(), Docker возвращает неправильный код выхода. Вместо ожидаемого 134 (128 + SIGABRT), контейнер отдаёт 139 (128 + SIGSEGV).

То есть контейнер маскирует реальную причину падения приложения. Соответственно дальнейший дебаг не имеет смысла, потому что код выхода не соответствует действительности.

Давай проверим:

#include <cstdlib>

int main() {
std::abort();
return 0;
}


Пишем Dockerfile:

FROM ubuntu:22.04

RUN apt-get update \
&& apt-get -y install \
build-essential \
&& rm -rf /var/lib/apt/lists/*

COPY ./ /src/
WORKDIR /src/
RUN g++ main.cpp -o app

WORKDIR /
CMD ["/src/app"]


Собираем и запускаем:

docker build -f ./Dockerfile -t sigabort_test:latest .
docker run --name test sigabort_test:latest ; echo $?


А на выходе у нас код: 139.

В примере выше код выхода — 139 = 128 + 11, где 11 соответствует SIGSEGV (ошибка сегментации), а не 134 = 128 + 6, что был бы SIGABRT (аварийное завершение).

Чтобы это пофиксить, нужно захерачить хак:

CMD ["bash", "-c", "/src/app ; exit $(echo $?)"]


docker run --name test sigabort_test:latest ; echo $?
bash: line 1: 6 Aborted /src/app
134


После этого контейнер будет возвращать корректный код 134.

Вариант рабочий, но костыльный. Правильнее использовать ключ --init.

Если запустить контейнер с флагом --init, используя исходную команду CMD ["/src/app"], мы получим ожидаемый 134 код. Что нам и нужно.

docker run --init --name test sigabort_test:latest ; echo $?

134


Почему init все починил?

Давай копнём глубже. В Linux процесс с PID 1 (init) имеет нестандартную семантику сигналов:

- Если у PID 1 для сигнала стоит действие «по умолчанию» (никакого обработчика), то сигналы с действием terminate игнорируются ядром. Это сделано, чтобы случайным SIGTERM/SIGINT нельзя было «уронить» init.

- PID 1 должен забирать зомби-процессы (делать wait() за умершими детьми). Если этого не делать, накопятся зомби.

- PID 1 обычно пробрасывает сигналы дальше — тому «настоящему» приложению, которое оно запускает.

Когда мы запускаем контейнер без --init, приложение становится PID 1.

Большинство обычных приложений (на C/C++/Go/Node/Java и т.д.) не написаны как «инит-системы», они не настраивают обработку всех сигналов, не занимаются «реапингом» детей и не пробрасывают сигналы. В результате вылазиют баги.


Наш сценарий с abort() (который поднимает SIGABRT) упирается именно в правила для PID 1. abort() внутри процесса поднимает SIGABRT.

Для обычного процесса с PID ≠ 1 это приводит к завершению с кодом 128 + 6 = 134. Но если процесс — PID 1, ядро игнорирует «терминирующие» сигналы при действии по умолчанию. В результате стандартные ожидания вокруг SIGABRT ломаются.

Ну а дальше вступают в силу детали реализации рантайма/сишной библиотеки, как именно контейнерный рантайм считывает статус.

На практике это может приводить к тому, что ты видишь 139 (SIGSEGV) вместо ожидаемого 134 (SIGABRT).

И тут проблема не в docker, а в том, что приложение неожиданно оказалось в роли init-процесса и попало под его особые правила.

Вот и вся наука. Изучай.

🛠 #docker #devops #linux #debug

@bashdays @linuxfactory @blog
Please open Telegram to view this post
VIEW IN TELEGRAM
566
Как прокачать Minecraft сервер с помощью Angie

Накидал пост о том, как мы мигрировали minecraft сервер на выделенный сервер и защитились от ботов с помощью Angie, просто и эффективно.

Сюда по классике не влезло, всё в блоге 👇

Читать: https://two.su/bieb4

🛠 #angie #devops #security

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
548