Как мы построили отказоустойчивую open-source-инфраструктуру для управления пользовательскими Linux-устройствами

e7c85224b561b201dd2401ebe16d32d2.png

Всем привет! Меня зовут Владислав, я руководитель направления развития пользовательских Linux-систем в Т-Банке. Мы работаем над проектом Linux Desktop. 

Проект зародился во времена блокировки иностранного софта. Нам нужен был опенсорсный продукт, который никуда не исчезнет и на закроется. Но прежде чем развивать Linux Desktop, в компании нужно было выбрать систему управления конфигурациями, которая сможет выдержать больше 15 000 хостов. А еще построить инфраструктуру, которая будет отказоустойчивой и не рассыпаться, если один из ЦОДов упадет.

Расскажу, как мы создали инфраструктуру, которая контролирует системы безопасности, магазин приложений, конфигурации ядра и многое другое. А еще такая инфраструктура — запасной аэродром, если вдруг придется отказаться от западного вендора.

Требования к новой системе

Основная идея Linux Desktop в том, чтобы создать свой образ Linux на основе open-source-продуктов, который будет user-friendly для разработчиков и операторов колл-центра и при этом будет соответствовать строгим требованиям безопасности банка.

Мы размышляли, как управлять всем этим парком. Первое, что пришло в голову, — стандартные решения, такие как Ansible, Puppet, SaltStack, Chef. Вариантов много, и каждый из них по своему хорош, но сначала нужно определиться с нашими требованиями.

Мы старались сформулировать требования по аналогии с другими большими устоявшимися MDM-системами: SCCM, Jamf, Intune. Нам нужны:

  1. Декларированный подход к управлению конфигурациями. Хотим видеть конечное решение конфигураций нашей системы, а не просто последовательное выполнение команд без финального результата.

  2. Масштабируемость и отказоустойчивость — эти параметры обязательны.

  3. Возможность сбора различных фактов о системе, потому что мы хотим вести статистику по всему парку, собирать информацию об установленных программах и другую интересную нам информацию.

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

  5. Большое сообщество, которое следит за обновлениями и безопасностью.
    Это плюс для любой системы: быстрый выпуск фиксов багов, много разных модулей для удобства управления.

  6. И нужно выбирать то, что нравится, потому что потом самим же с этим работать.

Выбор пал на 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 memory

puppet-server-01 CPU

puppet-server-01 CPU

puppet-master.domain.ru: RAM 8 ГБ VCPUs 4

puppet-master memory

puppet-master memory

puppet-master CPU

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, логинимся и получаем готовый сервис для работы.

07c61e2a271adcf7e2fa4e11f4639de6.png

А в итоге

Мы получили отказоустойчивую инфраструктуру, готовую выдержать до 3000 агентов с возможностью масштабирования. Это могут быть как серверы, так и мобильные рабочие станции, контроль распространения манифестов через git, удобное управление парком через Foreman, полный функционал puppet с управлением сертификатами, гибким написанием манифестов и множеством модулей от комьюнити.

P.S.

Кстати, puppet может управлять не только Linux-, но и Windows-системами)

© Habrahabr.ru