Всем привет! Меня зовут Владислав, я руководитель направления развития пользовательских Linux-систем в Т-Банке. Мы работаем над проектом Linux Desktop.
Проект зародился во времена блокировки иностранного софта. Нам нужен был опенсорсный продукт, который никуда не исчезнет и на закроется. Но прежде чем развивать Linux Desktop, в компании нужно было выбрать систему управления конфигурациями, которая сможет выдержать больше 15 000 хостов. А еще построить инфраструктуру, которая будет отказоустойчивой и не рассыпаться, если один из ЦОДов упадет.
Расскажу, как мы создали инфраструктуру, которая контролирует системы безопасности, магазин приложений, конфигурации ядра и многое другое. А еще такая инфраструктура — запасной аэродром, если вдруг придется отказаться от западного вендора.
Требования к новой системе
Основная идея Linux Desktop в том, чтобы создать свой образ Linux на основе open-source-продуктов, который будет user-friendly для разработчиков и операторов колл-центра и при этом будет соответствовать строгим требованиям безопасности банка.
Мы размышляли, как управлять всем этим парком. Первое, что пришло в голову, — стандартные решения, такие как Ansible, Puppet, SaltStack, Chef. Вариантов много, и каждый из них по своему хорош, но сначала нужно определиться с нашими требованиями.
Мы старались сформулировать требования по аналогии с другими большими устоявшимися MDM-системами: SCCM, Jamf, Intune. Нам нужны:
Декларированный подход к управлению конфигурациями. Хотим видеть конечное решение конфигураций нашей системы, а не просто последовательное выполнение команд без финального результата.
Масштабируемость и отказоустойчивость — эти параметры обязательны.
Возможность сбора различных фактов о системе, потому что мы хотим вести статистику по всему парку, собирать информацию об установленных программах и другую интересную нам информацию.
Возможность задавать конкретные политики безопасности и обеспечивать их выполнение на всех управляемых нами устройствах, что критически важно в большой и сложной инфраструктуре.
Большое сообщество, которое следит за обновлениями и безопасностью.
Это плюс для любой системы: быстрый выпуск фиксов багов, много разных модулей для удобства управления.И нужно выбирать то, что нравится, потому что потом самим же с этим работать.
Выбор пал на Puppet, потому что он использует pull-модель и у него свой центр сертификации. Масштабируемость инфраструктуры может быть как горизонтальная, так и вертикальная, что очень удобно. А если рядом установить Foreman, получится полноценная система управления с графическим интерфейсом, и все это в последствии можно кастомизировать под себя.
Дальше нужно было подсчитать, сколько потребуется серверов, построить схему и настроить отказоустойчивость. Расчеты мы проводили просто: подняли сервер, настроили на нем Puppet-сервер, загрузили туда пару активных машин и начали снимать метрики с сервера.
Логичный вопрос: зачем выполнять такой расчет, если Puppet уже все посчитал сам? Доверяй, но проверяй. Тем более что не совсем понятно, как он поведет себя в нашей инфраструктуре.
Вот что получилось в наших расчетах.
Конфигурации серверов
hostname |
cpu |
ram |
Назначение сервера |
puppet-master.domain.ru |
4 |
8 Gb |
Puppet master |
puppet-server-01.domain.ru |
4 |
8 Gb |
Puppet server |
Тестирование нагрузки. Во время тестирования онлайн было 50—80 хостов, и всю нагрузку мы распределили на два сервера.
puppet-server-01.domain.ru: RAM 8 ГБ VCPUs 4
puppet-master.domain.ru: RAM 8 ГБ VCPUs 4
Возьмем среднюю нагрузку на сервер в 75 хостов.
Посчитаем CPU
avg CPU utilization
puppet-master.domain.ru(1,86) + puppet-server-01.domain.ru(0,92) / 2 = 1,39%
Получается, что средняя нагрузка на процессор составляет 1,39% от 4 VCPUs при онлайне в 37 хостов.
Считаем половину от 75, потому что происходит балансировка между нодами.
На 1000 хостов потребление будет примерно 1,5 VCPUs (все расчеты производятся с учетом уже написанных манифестов, модулей и фактов).
Из официальной документации видно, что минимальные требования — от 2 до 4 VCPUs на 1000 хостов.
Берем среднее значение 3 VCPUs на 1000 хостов.
Потом считаем memory
avg Memory utilization
puppet-master.domain.ru(56,21) + puppet-server-01.domain.ru(38,99) / 2 = 47,6%
Получается, что средняя нагрузка на оперативную память составляет 47,6% от 8 Gb при онлайне в 37 хостов.
Считаем половину от 75, потому что происходит балансировка между нодами.
С учетом того, что JAVA воркеры настроены стандартно, а пополнение хостов происходило постепенно, можно считать расчеты из официальной документации за основу.
На 1000 хостов потребление будет примерно 4—6 Gb. Все расчеты провели с учетом уже написанных манифестов, модулей и фактов.
Получаем, что конфигурация инфраструктуры выглядит так:
hostname |
cpu |
ram |
Назначение сервера |
puppet-server-01.domain.ru |
4 |
6 Gb |
Puppet server |
foreman.domain.ru |
4 |
6 Gb |
Foreman |
puppet-master.domain.ru |
4 |
6 Gb |
Puppet master |
puppet-proxy-01.domain.ru |
4 |
4 Gb |
Proxy |
puppet-ca.domain.ru |
4 |
6 Gb |
Puppet CA |
puppet-server-02.domain.ru |
4 |
6 Gb |
Puppet server |
puppet-proxy-02.domain.ru |
4 |
4 Gb |
Proxy |
Из расчетов видно, что нашей инфраструктуры хватит на 3000 хостов, и мы вынесли отдельно центр сертификации, чтобы не нагружать серверы. Правило хорошего тона — держать центр сертификации отдельно. Proxy-серверы мы настроили так, чтобы они распределяли запросы на подпись сертификатов и запросы на получение конфигураций.
После нехитрых манипуляций получаем вот такую схему:
Схема инфраструктуры
Ну а теперь, как говорится, следите за руками!
Агент обращается к кластеру Nginx, который выполняет балансировку на уровне L4 и просто перенаправляет запросы на первый или второй прокси-сервер. Прокси располагаются в разных ЦОДах для повышения отказоустойчивости, и мы настраиваем их на распределение запросов в CA и к Puppet-серверу.
CA мы вынесли и создали три независимых друг от друга сервера. При падении любого сервера агенты все равно будут обращаться к другим серверам и получать конфигурацию. Ну а саму конфигурацию мы, как положено, распространяем с помощью Git.
Foreman нужен для удобного управления системами, а для повышения отказоустойчивости мы еще развернули кластер баз данных.
Как все выглядело на практике
Расскажу, как сделать все так красиво. У нас уже был git, мы настроили ci/cd так, чтобы ранер собирал наши манифесты с git и пушил их на все puppet-серверы. При этом мы использовали ansible-контейнер.
Развернули все на Ubuntu 20.04 и настроили /etc/hosts на серверах.
Начали с puppet-серверов, центра сертификации и foreman — установили puppet на сервера:
puppet-master.domain.ru
puppet-server-01.domain.ru
puppet-server-02.domain.ru
puppet-ca.domain.ru
foreman.domain.ru
sudo apt-get -y install ca-certificates
cd /tmp && wget https://apt.puppet.com/puppet7-release-focal.deb
sudo apt-get install /tmp/puppet7-release-focal.deb
sudo apt-get update
sudo apt-get install puppetserver
Установка Puppet CA
1. Настраиваем puppet.conf. Редактируем /etc/puppetlabs/puppet/puppet.conf, указываем dns мастера и центра сертификации:
[main]
ca_server = puppet-ca.domain.ru
certname = puppet-ca.domain.ru
server = puppet-master.domain.ru
2. Включаем центр сертификации. Редактируем /etc/puppetlabs/puppetserver/services.d/ca.cfg:
# To enable the CA service, leave the following line uncommented
puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
#puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
3. Разрешаем подписывать сертификаты master-server:
cp /etc/puppetlabs/puppetserver/conf.d/ca.conf /etc/puppetlabs/puppetserver/conf.d/ca.conf.backup
Редактируем /etc/puppetlabs/puppetserver/conf.d/ca.conf. Такая конфигурация нужна для подписи сертификатов с альтернативными dns-именами. После подписания нод ее нужно вернуть в исходное состояние:
certificate-authority: {
# allow CA to sign certificate requests that have subject alternative names.
allow-subject-alt-names: true
# allow CA to sign certificate requests that have authorization extensions.
allow-authorization-extensions: true
# enable the separate CRL for Puppet infrastructure nodes
# enable-infra-crl: false
}
4. Запускаем puppetserver:
sudo systemctl start puppetserver.service
Настройка puppet-master nod
1. Выключаем центр сертификации. Чтобы сервер не участвовал в управлении сертификатами, его нужно отключить.
Редактируем /etc/puppetlabs/puppetserver/services.d/ca.cfg:
# To enable the CA service, leave the following line uncommented
#puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
2. Настройка puppet.conf. Редактируем /etc/puppetlabs/puppet/puppet.conf. Тут нужно чуть-чуть пояснить: чтобы клиент ходил на балансировщик, мы добавили dns балансировщика тоже в dns_alt_names, просто если puppet agent не увидит dns балансировщика в сертификате, то он не пойдет к нему.
[main]
dns_alt_names = puppet-master.domain.ru,puppet.domain.ru
ca_server = puppet-ca.domain.ru
certname = puppet-master.domain.ru
server = puppet-master.domain.ru
3. Настройка puppet-nod. Привожу настройку сразу двух нод, потому что они идентичны.
Выключаем центр сертификации. Сервер нужно отключить, чтобы он не участвовал в управлении сертификатами.
Редактируем /etc/puppetlabs/puppetserver/services.d/ca.cfg:
# To enable the CA service, leave the following line uncommented
#puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
Настройка puppet.conf. Редактируем /etc/puppetlabs/puppet/puppet.conf. То же самое прописываем на второй ноде, только:
[main]
dns_alt_names = puppet-master.domain.ru,puppet.domain.ru
ca_server = puppet-ca.domain.ru
certname = puppet-server-01.domain.ru
server = puppet-master.domain.ru
4. Получение сертификатов. Запускаем на нодах puppet-master.domain.ru puppet-server-01.domain.ru puppet-server-02.domain.ru
sudo /opt/puppetlabs/bin/puppet agent -t --waitforcert 10
На ноде СА подписываем все сертификаты puppet-ca.domain.ru
#Проверяем, что сертификаты
/opt/puppetlabs/bin/puppetserver ca sign --all
#Подписываем все сертификаты
/opt/puppetlabs/bin/puppetserver ca list --all
Теперь нужно отключить подпись сертификатов с dns_alt_names на puppet-ca.domain.ru
sudo systemctl restart puppetserver.service
Настройка Proxy Nods
Устанавливаем nginx:
sudo apt install nginx
sudo nano /etc/nginx/nginx.conf
worker_rlimit_nofile 50000;
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 10000;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols SSLv2 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
log_format pptsrv '[$time_local]-- ip_client:$remote_addr -> ip_server:$upstream_addr status:$status bytes_client:$bytes_sent';
log_format pptca '[$time_local]-- ip_client:$remote_addr -> ip_server:$upstream_addr status:$status bytes_client:$bytes_sent';
access_log /var/log/nginx/pptsrv-access.log pptsrv;
access_log /var/log/nginx/pptsrv-access.log pptca;
upstream pptsrv {
zone pptsrv 4M;
least_conn;
server ip-server:8140; #puppet-master.domain.ru
server ip-server:8140 weight=5; #puppet-server-01.domain.ru
server ip-server:8140 weight=5; #puppet-server-02.domain.ru
}
upstream pptca {
server ip-server:8140; #puppet-ca.domain.ru
}
server {
listen 8140 ssl proxy_protocol;
server_name puppet-proxy-01.domain.ru
ssl_session_timeout 5m;
ssl_certificate /etc/puppetlabs/puppet/ssl/certs/puppet-proxy-01.domain.ru.pem;
ssl_certificate_key /etc/puppetlabs/puppet/ssl/private_keys/puppet-proxy-01.domain.ru.pem;
ssl_client_certificate /etc/puppetlabs/puppet/ssl/certs/ca.pem;
ssl_verify_client optional;
ssl_crl /etc/puppetlabs/puppet/ssl/crl.pem;
location /puppet/ {
proxy_pass https://pptsrv;
proxy_redirect off;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_name puppet.domain.ru; #Тут указываем имя балансировщика. Это нужно для того, чтобы агент понимал, как ему ходить
proxy_ssl_crl /etc/puppetlabs/puppet/ssl/crl.pem;
proxy_ssl_trusted_certificate /etc/puppetlabs/puppet/ssl/certs/ca.pem;
proxy_ssl_certificate /etc/puppetlabs/puppet/ssl/certs/puppet-proxy-01.domain.ru.pem;
proxy_ssl_certificate_key /etc/puppetlabs/puppet/ssl/private_keys/puppet-proxy-01.domain.ru.pem;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /puppet-ca/ {
proxy_pass https://pptca;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Subject $ssl_client_s_dn;
proxy_set_header X-SSL-Issuer $ssl_client_i_dn;
proxy_read_timeout 65;
}
}
}
Мы добавили метод балансировки и вес для серверов. Чтобы проверить конфиг, можно использовать команду nginx -t, а чтобы применить конфиг без перезагрузки службы и сервера, используем команду nginx -s reload.
Настройка Foreman
Перед настройкой Foreman советую настроить базу данных, но об этом тут рассказывать не буду. Идем дальше по настройке.
Устанавливаем репозитории и установщик:
sudo wget https://deb.theforeman.org/foreman.asc -O /etc/apt/trusted.gpg.d/foreman.asc
echo "deb http://deb.theforeman.org/ focal 3.10" | sudo tee /etc/apt/sources.list.d/foreman.list
echo "deb http://deb.theforeman.org/ plugins 3.10" | sudo tee -a /etc/apt/sources.list.d/foreman.list
sudo apt-get update && sudo apt-get -y install foreman-installer puppet-agent
Настраиваем Noda Foreman. Устанавливаем puppet-agent.
Редактируем конфигурацию /etc/puppetlabs/puppet/puppet.conf:
[main]
ca_server = puppet-ca.domain.ru
certname = foreman.domain.ru
server = puppet-master.domain.ru
Установка Foreman отдельно от puppet servers:
foreman-installer \
--puppet-server=false \
--foreman-proxy-puppet=false \
--foreman-proxy-puppetca=false \
--foreman-db-manage=false \
--foreman-db-host=name host DB \
--foreman-db-database=name DB \
--foreman-db-username=name user DB \
--foreman-db-password=pass user DB
Для настройки базы данных:
foreman-rake db:migrate
foreman-rake db:seed
foreman-rake apipie:cache:index
Noda Puppet-server. После установки Foreman nod нужно запомнить для подключения puppet-server:
-foreman-proxy-oauth-consumer-key
-foreman-proxy-oauth-consumer-secret
Команда выполняется на foreman nod, чтобы получись oauth для получения foreman-proxy-oauth-consumer-key и foreman-proxy-oauth-consumer-secret:
sudo cat /etc/foreman-installer/scenarios.d/foreman-answers.yaml | grep oauth_consumer
Дальше запускаем установку на Puppet-server:
foreman-installer \
--no-enable-foreman \
--no-enable-foreman-plugin-puppet \
--no-enable-foreman-cli \
--no-enable-foreman-cli-puppet \
--enable-puppet \
--puppet-server-ca=false \
--puppet-server-foreman-url=https://foreman.domain.ru \
--enable-foreman-proxy \
--foreman-proxy-puppetca=false \
--foreman-proxy-foreman-base-url=https://foreman.domain.ru \
--foreman-proxy-trusted-hosts=foreman.domain.ru \
--foreman-proxy-oauth-consumer-key=***********************\
--foreman-proxy-oauth-consumer-secret=********************
Обратите внимание, что эта нода ставится без СА
По такой схеме настраивается master и slave server.
6.4 Noda Puppet-CA-server.
Устанавливаем прокси с СА:
foreman-installer \
--no-enable-foreman \
--no-enable-foreman-plugin-puppet \
--no-enable-foreman-cli \
--no-enable-foreman-cli-puppet \
--enable-puppet \
--puppet-server-ca=true \
--puppet-server-foreman-url=https://foreman.domain.ru \
--enable-foreman-proxy \
--foreman-proxy-puppetca=true \
--foreman-proxy-foreman-base-url=https://foreman.domain.ru \
--foreman-proxy-trusted-hosts=foreman.domain.ru \
--foreman-proxy-oauth-consumer-key=**************************\
--foreman-proxy-oauth-consumer-secret=**************************
После установки получите логин и пароль для входа, переходим на https://foreman.domain.ru, логинимся и получаем готовый сервис для работы.
А в итоге
Мы получили отказоустойчивую инфраструктуру, готовую выдержать до 3000 агентов с возможностью масштабирования. Это могут быть как серверы, так и мобильные рабочие станции, контроль распространения манифестов через git, удобное управление парком через Foreman, полный функционал puppet с управлением сертификатами, гибким написанием манифестов и множеством модулей от комьюнити.
P.S.
Кстати, puppet может управлять не только Linux, но и Windows-системами )
Комментарии (10)
il_l
10.12.2024 22:51Спасибо за статью, тема мне довольно близка, поэтому появился ряд вопросов.
Расскажите, пожалуйста, чем отличается puppet-master от puppet-server в вашей конфигурации, если CA вынесен на отдельную машину? Как хосты попадают в Foreman? По результатам отчёта от Puppet, создаются там в ручную/скриптами или используете деплой хостов через Foreman? Как вы обеспечиваете однообразие и актуальность puppet-классов на разных puppet серверах?
MorozovVladislav Автор
10.12.2024 22:51Чем отличается puppet-master от puppet-server в вашей конфигурации?
По структуре не чем не отличается, тут идет просто соединение всех puppet серверов через один мастер, чтобы server роль тоже ходила в мастер и синкалась.
Как хосты попадают в Foreman?
При заливки хоста через cloud-init выполняется коннект к puppet, а на каждой puppet ноде устанавливается foreman-proxy ( как я показал в статье ) и каждая прокси собирает данные о хостах и передает все на Foreman, а на там уже происходить управления хостами.
Как вы обеспечиваете однообразие и актуальность puppet-классов на разных puppet серверах?
Для этого и нужен git. На нем хранится манифесты и модули, которые одновременно при деплое разливаются на все сервера.
il_l
10.12.2024 22:51Спасибо. Если это не секрет фирмы и не под NDA, то как реализована доставка кода в puppet-server из git? При коммите в git, puppet-server сам узнаёт об этом и обновляет модули, или вы нажимаете deploy в ci-пайплайне и код отправляется на puppet-server?
У себя уже довольно давно изобретаем велосипеды на эту тему, пока всё сводится к тому, что пулим скриптом код из гита на каждом puppet-server и надеемся, что код доехал и доехал актуальный. Иногда возникают коллизии, но на этот случай прикрутили другой скрипт, который сверяет хеши коммита в модуле на puppet-server и в git.MorozovVladislav Автор
10.12.2024 22:51как реализована доставка кода в puppet-server из git
Схема не сложная, мы реализовали так:
- раннер подняли с докером
- используем ансибл докер который собирает папку code(/etc/puppetlabs/code) которая лежит в git и пушит ее на все сервера puppet рекурсивно (чтобы папка code на git была идентична папке на сервере)
Oldju
10.12.2024 22:51Мне для общего развития. У вас в рейдах вы видите состояние каждого отдельного диска какими средствами?
MorozovVladislav Автор
10.12.2024 22:51Если вы про сервера puppet, то это виртуалки и рейдов там нет, если вы про агентов, то это в основном хосты без рейдов, потому что linux desktop, но вопрос интересный)
А вообще у foreman есть факты которые он собирает с хоста, то есть что видит хост то видит foreman, из этой логики скорее всего он не увидит рейд железный, но возможно увидит рейд программный.Oldju
10.12.2024 22:51Я просто недавно отвечал в теме про сервера Линукс и выяснил этот момент. Я знаю вин и аппаратные средства НР. На уровне пятилетней давности. И для меня было открытые, что в коммерции на Линукс этот вопрос не решён в одном конкретном случае. Поэтому решил поспрашивать. У вас видимо тоже такой возможности нет. На массмаркетовом железе. Спасибо.
A1EF
Интересно услышать, чем не подошел SaltStack?
MorozovVladislav Автор
В интернете есть много споров на эту тему и у каждого свое мнение, от себя могу добавить, что тут вопрос вкуса и удобства, ну и puppet вроде как имеет больше комьюнити.
Примерно одинаковые инструменты Chef (ruby) - Ansible (python), Puppet (ruby) - Salt (python)
navion
Интересны критерии отбора - у вас есть программисты на ruby и поэтому выбрали Puppet?