Как мы построили отказоустойчивую open-source-инфраструктуру для управления пользовательскими Linux-устройствами
Всем привет! Меня зовут Владислав, я руководитель направления развития пользовательских 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-server-01 memory
puppet-server-01 CPU
puppet-master.domain.ru: RAM 8 ГБ VCPUs 4
puppet-master memory
puppet-master CPU
Возьмем среднюю нагрузку на сервер в 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 на сервера:
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.con. То же самое прописываем на второй ноде, только:
[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. После установки Forman 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-системами)