Загоняем маки спать

Попал в стандартную ситуацию: закрываешь вечером ноутбук, а утром он отказывается включаться и требует зарядку. Маки нынче пошли не те … На самом деле в самоизоляции у меня куча терминалов, всяких ремотных десктопов и прочих штук, про которые мак знает и помогает им оставаться на связи.

Решение очень простое:

  1. Спрашиваем у мака, сколько раз и когда он просыпается: pmset -g log|grep due
  2. Увидев, что он просыпается практически каждую минуту, запрещаем ему поддерживать соединения: sudo pmset -b tcpkeepalive 0

Плюс: мак теперь переживает ночь, теряя 1-2% заряда батареи

Минус: теперь после открытия все соединения разорваны и надо с минуту, пока все вернется в привычное русло.

Android init undocumented

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

Казалось бы, ну в чем проблема? Там внутри линукс, он даже вроде задокументирован. Гуглим, находим https://android.googlesource.com/platform/system/core/+/master/init/README.md

Добавлям все необходимое … и не работает.

Пропускаю кучу мучений с офигенной (в больших кавычках) системой логирования и отладки.

Решение: rc-файлы должны иметь права 644 и никак иначе. Другие права? Не будем с ними работать! Секурсная сесурити, понимаешь. И пофиг, что если я получил доступ до /system, то уж такая-то мелочь меня не остановит “от кражи информации”

Возвращаем четкость маку

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

Если кратко, то в Apple победили менеджеры, которым надо “красиво”, а не “правильно”. В этом оплоте графики и накосячили! Хорошо, что вернуть назад все легко.

Settings-General-Снять галочку с Use font smoothing when available

и в Displays перещелкнуть радиокнопку Resolution на Scaled и выбрать пункт левее от Default. В общем, как на скриншотах выше.

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

Обновления. Боль и лечение.

Как я избавился от обновлений в субботу ночью

“Обновление необходимо произвести в ночь с субботы на воскресение, в период минимальной нагрузки” – эта фраза знакома любому ИТшнику в любой стране. И точно так же им нелюбима. Что делать, что бы начать спать по ночам?

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

Для начала хорошее: действительно, в большинстве случаев ночь с субботы на воскресенье является периодом, когда система испытывает самую минимальную нагрузку и даже полная остановка затронет минимальное число пользователей. И … в принципе это все, что можно найти хорошего.

Что плохого?

  • Для начала, работа ночью не является самым желаемым временем работы у ИТ подразделения, что бы не говорили об этом мифы, предания и сказания. Мы такие же люди, мы точно так же любим спать.
  • Как ни странно, но повторюсь: ночью большинство людей спят. И если что-то пойдет не так, решение проблемы может застопориться до утра. Просто потому что дежурная смена не может сделать что-то этакое, что потребовалось. Как бы вы не стояли на голове, но часто в 4 утра нельзя сделать то, что после 9 утра делается за 10 минут. В результате время простоя растет, SLA нарушается, мотивация снижается.
  • И наконец, просто стоимость обновления. Ночные работы в воскресенье гораздо дороже тех же самых, но во вторник утром.

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

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

Во-первых, начать в конце-концов применять техники, позволяющие обновляться без остановки основного сервиса. Их много, они описаны очень хорошо и я не буду тут повторяться. Но начальные слова для гугления например CI/CD и canary deployment.

Главная задача: научиться обновляться быстро и однообразно. Поверьте, это больно только в самом начале. При первой попытке обновления “вне графика” вас ждет весь привычный вам набор проблем при обновлении: что-то забыли, где-то не та версия, какое-то место требует ручной работы и так далее. Но стоит руководству (хотя бы на уровне тимлидов) “упереться” и повторять, как каждое новое обновление будет происходить все быстрее и проще. Люди ленивые: одни напишут скрипты, другие немного изменят процесс или вообще добавят новый.

Мой опыт показывает, что переход от “ой, для обновления нам надо Н часов и работа М людей” до “новая версия появляется где надо через 10-15 минут и сама” необходимо примерно полгода. Это без надрывов и резких движений. И да, эти практики применимы к любым системам.

Вторая по важности задача это донести до всех в компании график обновления продакшн систем. Зачем? “Все мы люди, все мы человеки”. И даже если у вас от коммита в репозиторий до выкатки на прод проходит пять минут, то любой очень активный разработчик способен обеспечить увлекательное времяпрепровождение в самый не подходящий момент. Оно нам надо?

На мой взгляд, один из самых правильных графиков звучит примерно так:

  • Никаких обновлений продакшн систем с обеда пятницы до утра понедельника
  • Большие релизы планируются на утро вторника
  • Большие релизы перед праздниками недопустимы

Откуда пошли эти правила? Конечно, из опыта.

Почему никаких релизов перед праздниками и выходными? Потому что в 99% случаев такие даты – это просто кто-то из менеджеров где-то на каком-то совещании сказал что-то типа “ну, 1го числа систему переводим на новые рельсы”. А другой менеджер не имеет достаточно смелости сказать “Нафига обновлять систему 1 го мая? Там длинные праздники”. 1 процент я оставил, потому что где-то наверное “круглые” и “ровные” даты оправданы. Но лично я ни разу не встречал такого.

Почему большие обновления лучше всего планировать на утро вторника?

  • Все вопросы и проблемы, которые были в выходные, обычно уже решены в понедельник, либо понятны их последствия. И если что, легко отменить обновление вообще.
  • Люди вошли в рабочий ритм и все известные мне организации считают вторник рабочим днем.
  • Если что-то пойдет не так, у нас есть куча рабочих дней с очень малой вероятностью прерывания по личным делам.
  • И наконец, “опоздуны” и “потеряшки” могут протратить свои выходные из-за своей лени.

Ну а про вторую половину пятницы и говорить нечего: в это время вылавливаются большинство свежих проблем и происходит так называемая “стабилизация”. Уходить на выходные всегда спокойней, если ты знаешь, что до этого новая версия проработала под твоим присмотром несколько часов.

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

Но если выдержать первоначальный наплыв, то буквально через 3-4 недели все заметят сильные позитивные изменения. Предидущие три компании показали: резко снижается напряженность в коллективе и число подъемов ночью на починку сломанного. Остальное думаю у всех свое будет.

И самое приятное: вас потом поблагодарят за возвращенные выходные и спокойный сон ночью.

PS Photo by Andrew Neel on Unsplash

CentOS 7 и MariaDB 10

Внезапно потребовалось обновить на старой центоси mariadb до свежих версий. Оказалось довольно просто:

Удаляем старое, добавляет новое, обновляем.

# cat > /etc/yum.repos.d/MariaDB10.repo
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.3/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1
# systemctl stop mariadb
# yum remove mariadb-server mariadb mariadb-libs
# yum clean all
# yum -y install MariaDB-server MariaDB-client
# systemctl enable mysql
# systemctl start mysql
# mysql_upgrade -p
......
cj64070_base.b_xml_tree                            OK
information_schema
performance_schema
Phase 7/7: Running 'FLUSH PRIVILEGES'
OK

И всё.

PS 10.4 почему-то встает криво, лень разбираться

FreeIPA и Ubuntu 18.04

Попробовал эту связку. Кратко: Ubuntu использовать можно только как клиент. Как сервер – огребете дикую тучу проблем. Просто из-за того, что никто так не делает. А репликация вообще не работает, потому что сломана в потрохах системы.

Но на всякий случай, если очень охота поднять сервер и клиента на убунту

И на сервере и на клиенте

apt-get install rng-tools

cat >> /etc/default/rng-tools
HRNGDEVICE=/dev/urandom

systemctl enable rng-tools
systemctl start rng-tools

на сервере

chmod o+r /var/lib/krb5kdc
chmod o+x /var/lib/krb5kdc
touch /etc/krb5kdc/kadm5.acl

apt install libjs-scriptaculous

cd /usr/share/fonts/truetype
ln -s font-awesome /usr/share/fonts/truetype/fontawesome

и убрать из

# cat /etc/apache2/conf-enabled/ipa.conf|grep DEF
AddOutputFilterByType DEFLATE text/html text/plain text/xml 

Повторюсь, репликацию я так и не смог настроить. Где-то в районе wsgi-dbus они друг друга не любят

Игродельство

Чего-то зачесалось у меня в старые игрушки поиграть. Но быстрый поиск показал, что фиг вам там. Либо надо ставить эмуляторы и искать образы, либо идти и платить непонятно за что с каким качеством (у меня до сих пор в голове свежи воспоминания, как угробили pac-man, mario и boulder dash).

Ну если оно не получается, то почему бы не взять и не написать самому? В тех играх не ни графики, ни звука, ни работ на 100500 мульонов денег. А интерес-то тот же …

В общем, отряхнул я руки и за пол-дня написал первую игру: Пинг-Понг.

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

Брать тут https://github.com/kiltum/games/tree/master/pingpong

Пароли – наше все!

Давным давно, аж 5 лет назад, я уже задавался темой хранения паролей. Тогда я выбрал платный 1Password и до недавнего времени был полностью доволен.

За 5 лет изменилось довольно многое и к примеру тот 1Password уже не купить: нынче всё по подписке, за каждый чих требуют денег. Да и требования к хранению паролей лично у меня изменились. Немного, но значительно.

Во-первых, я больше не хочу верить. Все маркетинговые слова про “улучшенную защиту”, “aes-256”, “PBKDF2” и “SHA256” остаются словами до тех пор, пока не будет доказательств. Нет, в реальности это может быть и так, но проверить нечем.

Во-вторых, я не хочу больше отдавать контроль над моими паролями кому-то еще. Со старой версией 1Password все хорошо: она позволяет хранить пароли в iCloud, Dropbox или любом каталоге по выбору. Нынешняя, как и остальные, предлагают хранить пароли непонятно где и не понятно как защищенными. Специально сейчас прошелся по сайтам 1Password, Dashline, Lastpass, Roboform и прочим, все тот же набор маркетингового буллшита и никаких доказательств. А возможность доступа к паролям из браузера вообще считают за достоинство …

И наконец, я хочу халявы. Не то, что бы денег жалко, но садиться на иглу “платите нам $10 в месяц” не охота

По своей старой привычке “а оно надо?” я посмотрел профиль использования своих данных. Чаще всего мне надо использовать различные ключи к облачным сервисам. Вот эти вот все GCP_PROJECT, AWS_ACCESS_KEY_ID, pem файлы для ssh и так далее. А вот пароли как пароли у меня используются довольно редко. Ну раз, ну два в день. Лицензии и прочие подобные ключи я вообще использую раз в месяц, когда переустанавливаю все в рамках DRP.

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

Довольно специфичные условия, не так ли? Но нет предела совершенству!

Первым делом я пошел смотреть на KeePass. За прошедшие 5 лет он никуда не делся, все такой же монстр с кучей свистелок и перделок. И все так же хочет кучу всего, потому что и может практически все. Можно, не если честно, не вставляет.

Затем поиск вывел меня на довольно новый менеджер паролей Bitwarden. В нем все, как я люблю: у него открытый код и абсолютно прозрачная модель монетизации. За $10 в год можно получить абсолютно все, что необходимо от менеджера паролей в настоящее время. Более того, можно поставить свой собственный сервер и все клиенты будут синхронизироваться с ним, а не с сервером компании.

Попутно обнаружилась совершенно шикарная вещь: у битвардена есть консольный клиент. Выглядит это так (пароли все равно поменял)

[kiltum@mbook ~]$ bw unlock
? Master password: [hidden]
Your vault is now unlocked!

To unlock your vault, set your session key to the `BW_SESSION` environment variable. ex:
$ export BW_SESSION="WgOBidzNy5wybOFgJ8t9MB3gHN2ZQww1ux0ovrbmaA0N21xRS0rrwlBofi8sp6bKe1+HmYaybCgIQnZxGqzkWB=="
> $env:BW_SESSION="WgOBidzNy5wybOFgJ8t9MB3gHN2ZQww1ux0ovrbmaA0N21xRS0rrwlBofi8sp6bKe1+HmYaybCgIQnZxGqzkWB=="

You can also pass the session key to any command with the `--session` option. ex:
$ bw list items --session WgOBidzNy5wybOFgJ8t9MB3gHN2ZQww1ux0ovrbmaA0N21xRS0rrwlBofi8sp6bKe1+HmYaybCgIQnZxGqzkWB==

[kiltum@mbook ~]$ bw list items --session WgOBidzNy5wybOFgJ8t9MB3gHN2ZQww1ux0ovrbmaA0N21xRS0rrwlBofi8sp6bKe1+HmYaybCgIQnZxGqzkWB==
[{"object":"item","id":"de683640-15ca-4dc0-98ec-a95400951086","organizationId":null,"folderId":null,"type":1,"name":"kiltum.livejournal.com","notes":null,"favorite":false,"login":{"uris":[{"match":null,"uri":"https://kiltum.livejournal.com/"}],"username":"kiltum","password":"passwordwasdeleted","totp":null,"passwordRevisionDate":null},"revisionDate":"2018-09-07T09:02:43.6466667Z"},{"object":"item","id":"161cff1e-eeaf-4091-8c0f-a954009ee2ab","organizationId":null,"folderId":null,"type":1,"name":"www.facebook.com","notes":null,"favorite":false,"login":{"uris":[{"match":null,"uri":"https://www.facebook.com/"}],"username":"multik@multik.org","password":"herewaspassword","totp":null,"passwordRevisionDate":null},"revisionDate":"2018-09-07T09:38:29.0466667Z"}]

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

Но после небольшой дискуссии в #unix.ru мне было предложено посмотреть на Pass. Поначалу я попробовал и весь расплевался. Этот менеджер паролей … ну в общем, он совершенно перпендикулярен всем остальным. В нем все не так и не туда. И вообще он какой-то кривой. Да, кривой и точка!

Но так как больше других менеджеров не находилось, решил оставить и попользоваться всеми тремя менеджерами (1password, bitwarden и pass) одновременно. Создавать новые пароли, ключи и токены, перемещаться между машинами и вообще пытаться вести обычную жизнь DevOps/SRE/SysAdmin/CTO/чебурашки.

Практически сразу выяснилось, что pass это просто развесистый bash скрипт, который в своей работе использует gpg и git. В теории это означает, что pass будет работать на всех платформах где есть эти программы. В реальности я его попробовал только под OS X и Linux

Что в итоге я получил?

Во-первых, мне больше не надо верить. Для шифрования используется gpg из дистрибутива с моим личным ключем. Каждая запись – это зашифрованный gpg файл. При желании можно взять и достать вручную.

$ gpg aws_prod.gpg 
gpg: WARNING: no command supplied.  Trying to guess what you mean ...
gpg: encrypted with 4096-bit RSA key, ID B2698444DC05C50F, created 2017-09-11
      "Viacheslav Kaloshin "

Во-вторых, использование git на своем сервере снимает все вопросы про “кто еще получает доступ до моих данных”. Как и полную историю всех изменений с самого начала. Никаких “30 последних изменений”. Хранится всё.

$ git log|head -5
commit 8ab2f5fae8bf9a8b48dc97df908343c25a1745c1
Author: Viacheslav Kaloshin 
Date:   Mon Sep 24 15:41:37 2018 +0300

    Rename cs/ed_aws_qa to cs/ed/aws_qa.

И наконец, эта опенсорс, халява и возможность поправить и сделать по-своему.

Описывать, как устанавливать pass смысла нет: это и так прекрасно расписано на официальном сайте. Более того, на новых машинах можно просто склонировать репозиторий в ~/.password-store и на этом вся установка будет завершена.

И наконец, то, что мне больше всего понравилось.

Безопасное переключение между окружениями. В данном случае AWS, но этот же механизм работает с GCE, Azure и любым другим софтом, хранящим ключи в переменных окружения.

$ pass show cs/ed/aws_qa
AWS_ACCESS_KEY_ID=BKIAIELZCTWSJ7FECP4Z
...
$ pass show cs/ed/aws_prod 
AWS_ACCESS_KEY_ID=BKIAIN56RKIABTBHJXTW
...
$ export  `pass show cs/ed/aws_qa`
$ set|grep AWS_ACC
AWS_ACCESS_KEY_ID=BKIAIELZCTWSJ7FECP4Z
$ export  `pass show cs/ed/aws_qa`
$ set|grep AWS_ACC
AWS_ACCESS_KEY_ID=BKIAIN56RKIABTBHJXTW

Замена конструкции ssh -i key.pem user@hostname

$ pass show cs/qapem
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAw4AMstljzZRxvqNIO/ZsXnkCMm8O+FXuuTGqzII2ysH5fz8Q3pLZmKVXfz+7
....
eNK7UAHXBLGciXfFjlYlvZaLci93wtY4reWCgmsCmNx98WBMZUmF0R1VCjU/DYleIpMtNBY=
-----END RSA PRIVATE KEY-----
$ pass show cs/qapem| ssh-add -
Identity added: (stdin) ((stdin))
$ ssh user@hostname

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

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

Свой gitlab на centos 7

Получив очередной счет от gitlab и github одновременно, я как-то задумался: а нафига я им плачу, когда я все это могу поднять на своем сервере? “Все это” – это кучку приватных git и простую ci/cd систему. Поставил и решил написать напоминалку, что бы в следующий раз не гуглить.

Процесс установки не вильно отличается от описанного на сайте, но есть несколько НО:

Во-первых, в centos7 nginx идет без поддержки passenger. Поэтому обновляем на версию из “пассажирской” репы.

curl --fail -sSLo /etc/yum.repos.d/passenger.repo https://oss-binaries.phusionpassenger.com/yum/definitions/el-passenger.repo
yum-config-manager --enable cr
yum install -y passenger
rpm -e nginx-mod-http-perl --nodeps
yum update nginx

Во-вторых, нигде в мануале не указано, что gitlab требует nodejs для работы

yum install nodejs

И наконец, нигде не указано, что gitlab не работает без unicorn. Во всех мануалах написано, что если у вас внешний nginx, отрубите встроенный и unicorn. Так вот, этого делать нельзя, иначе получите неработающий gitlab-workhouse

Из других неочевидных тюнингов стал вынос порта unicorn с 8080 и изменение размера буферов у постгреса. Иначе на моем загруженном сервере он отказывался запускаться.

unicorn['port'] = 8088
postgresql['shared_buffers'] = "100MB"

В остальном единственной засадой было изменение прав на сокеты, но это только из-за моей конфигурации, где куча всяких пользователей лезут в в один каталог. Так как сервер “домашний”, проще стало дать всем права на запись.

Больше никаких отступлений от официального руководства.

gpg sign пароль не в консоли

Достаточно долгое время все мои коммиты во все репозитории подписываются моим PGP ключем. Но все время доставало то, что при коммите из всяких “гламурных” программ типа PyCharm gpg отказывался подписывать коммит, заставляя открывать консоль и вводить gpg commit там. Сегодня мне это надоело и я нашел вот такое вот простое решение:

brew install pinentry-mac
echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
killall gpg-agent

Если вы под линуксом, то замените brew на yum/apt/чтоеще

Ресайз PV в k8s

Исходная ситуация: есть сервис, который использует PV, который необходимо увеличить. Примем, что другого (кроме стандартных утилит кубера) доступа к дискам нет от слова совсем.

В моем случае я увеличивал размер /data для prometheus.

Для начала запускаю копирование данных на локальную машину.

kubectl cp --container prometheus-server  prometheus-server-6966b574d7-5svfw:/data data/

Создаю новый диск и под к нему

$ cat pv.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  annotations:
    volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/gce-pd
  name: prometheus-server-new
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
$ cat pod.yaml 
kind: Pod
apiVersion: v1
metadata:
  name: prom-transfer-pod
spec:
  volumes:
    - name: storage
      persistentVolumeClaim:
       claimName: prometheus-server-new
  containers:
    - name: container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/data"
          name: storage
kubectl apply -f pv.yaml
kubectl apply -f pod.yaml

Теперь для ускорения заливки данных назад архивирую скачанное и заливаю назад

cd data
tar zcvf data.tar .
kubectl cp data.tar prom-transfer-pod:/data/

Заходим на под и распаковываем.

Теперь начинается самое интересное. Торможу сервис, не убивая его. Это необходимо, что бы pv перестал меняться.

kubectl scale deployment  prometheus-server  --replicas=0

Редактирую PV

kubectl edit pv pvc-6ef4e067-6012-11e8-a42f-42010a840193

Где меняю persistentVolumeReclaimPolicy на Retain. Это необходимо, что бы PV не удалился при удалении/изменении PVC.

Делаю полностью аналогичное для нового PV и удаляю новый же PVC. Теперь у меня есть старый и новый PV. Опять редактирую новый PV, на этот раз убивая секцию claimRef. Это “отцепит” новый PV от несуществующего уже PVC и разрешит его монтировать куда угодно.

Теперь можно заменить volumeName в старом PVC. Ну или тупо грохнуть и создать новый по образцу

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: prometheus-server
  namespace: default
spec:
  accessModes:
  - ReadWriteOnce
  resources:
     requests:
       storage: 50Gi
  volumeName: pvc-bd24792e-785c-11e8-a42f-42010a840193

Все, после того, как PV сменит статус на Bound, можно запустить сервер назад

kubectl scale deployment  prometheus-server  --replicas=1

После проверки работоспособности старый PV можно удалить.

Что делать, если …

… что-то упало (как телеграм) или недоступно?

Во-первых, надо заранее подготовиться и заиметь сервера где-то там далеко.
Во-вторых, надо сказать секретную команду

ssh -D 8123 -f -C -q -N user@server

Она поднимет socks5 прокси на localhost:8123

И наконец, указать в вашем любимом браузере, телеграмчике и остальном sock5 прокси.

Проверить работоспособность прокси можно командой

httping -x localhost:8123 -5 -g http://google.com
PING google.com:80 (/):
connected to google.com:80 (325 bytes), seq=0 time=636.58 ms 
connected to google.com:80 (325 bytes), seq=1 time=506.72 ms 
connected to google.com:80 (325 bytes), seq=2 time=657.07 ms 

Ученье свет, а неученых тьма

Внезапно и совершенно неожиданно для себя обнаружил, что на udemy курсы значительно внятней и понятней, чем на coursera. Мой любимый тест “на кубернетес”, который coursera вместе с Хайтауэром провалила тотально и полностью (там большая часть курса – тотальный бред. С тех пор я уверен, что в Kubernetes Up & Running Хайтауэр только ради политкорректности), тут не вызвал никаких проблем.

В общем, я на udemy набрал курсов, теперь во всю обучаюсь.

Картинка просто для привлечения внимания и как свидетельство, что я там слушаю.

Новый датацентр. Сертификаты в почту

Для своих нужд я обычно ставлю Zimbra Open Source Edition. Простой, дуракоустойчивый почтовый сервер со всякими необходимыми плюшками. Но есть в нем одна маленькая проблема: по умолчанию он генерит самоподписанные сертификаты, на которые ругаются всякие почтовые клиенты. Значит, надо подсунуть сертификаты от letsencrypt

Сделаем на сервере каталог для сертификатов

mkdir -p /opt/zimbra/ssl/letsencrypt/
chown zimbra:zimbra /opt/zimbra/ssl/letsencrypt/

Опять же, маленький скриптик, который пускается руками после обновления сертификатов и проверки готовности всего ко всему. Он же zimbra_check

cat /opt/www/chain.pem >> /etc/letsencrypt/live/mail.ka12.co/fullchain.pem 
scp  /etc/letsencrypt/live/mail.ka12.co/* root@mail:/opt/zimbra/ssl/letsencrypt/
ssh root@mail "chown zimbra:zimbra /opt/zimbra/ssl/letsencrypt/*"
ssh root@mail "runuser -u zimbra /opt/zimbra/bin/zmcertmgr verifycrt comm /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem"

Логика понятна из текста: добавляем CA ключ (отсюда) в цепочку, копируем все это в потроха зимбры и проверяем, согласится ли она это съесть. Обычно ответ “да”, но иногда она взбрыкивает и надо смотреть, чего же ей не нравится. Если ошибок нет, то можно запустить zimbra_deploy

#!/bin/bash
ssh root@mail "runuser -l zimbra -c 'zmcontrol stop'"
ssh root@mail "cp -a /opt/zimbra/ssl/zimbra /opt/zimbra/ssl/zimbra.`date \"+%Y%m%d\"`"
ssh root@mail "runuser -u zimbra cp /opt/zimbra/ssl/letsencrypt/privkey.pem /opt/zimbra/ssl/zimbra/commercial/commercial.key"
ssh root@mail "runuser -u zimbra /opt/zimbra/bin/zmcertmgr deploycrt comm /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem"
ssh root@mail "runuser -l zimbra -c '/opt/zimbra/bin/zmcertmgr deploycrt comm /opt/zimbra/ssl/letsencrypt/cert.pem /opt/zimbra/ssl/letsencrypt/fullchain.pem'"
ssh root@mail "runuser -l zimbra -c 'zmcontrol start'"

Он тормозит нафиг всю зимбру, бекапит и деплоит сертификаты и снова запускает все назад.

Новый датацентр. Web

Что самое главное во всем этом? Правильно, наши интернетики с кисками и прочим. А значит, надо обеспечить заход снаружи внутрь по https

На данный момент я знаю всего 3 способа: с помощью nginx, apache и traefik. Apache как-то старо, traefik наоборот, слишком новомодно.

Предупреждаю сразу: использовать в докере nginx c nginx-gen и companion, как описано вот тут https://github.com/gilyes/docker-nginx-letsencrypt-sample – нельзя. Проблема простая: генератор тупо мешает ip бэкэндов как ему понравится.

Краткий план: ставим виртуалку

virt-builder centos-7.4 --arch amd64 -o nginx.ka12.co.img --size 20G --format qcow2 --hostname nginx.ka12.co --ssh-inject root:file:/root/multik.sshkey --root-password file:root_pass --copy-in ifcfg-eth0:/etc/sysconfig/network-scripts/ --copy-in resolv.conf:/etc
virt-install --name nginx.ka12.co --network bridge=virbr100 --memory 2048 --disk path=nginx.ka12.co.img --import --os-variant centos7.0 --graphics vnc,listen=172.16.100.1 --noautoconsole

бутстрапим ее

ansible-playbook -i inventory -l nginx* centos-bootstrap.yml

Добавляем в нее нужное

yum -y install freeipa-client && ipa-client-install --mkhomedir
yum install certbot python2-certbot-nginx nginx

И запускаем nginx

openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
systemctl start nginx

Берем из git (https://github.com/kiltum/newdc/tree/master/nginx) и кладем в /opt/www

Там два темплейта – первый для просто “сделай так, что бы nginx узнал о сайте”, а второй – уже готовый полноценный конфиг.

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

Запускаем ./new_site mail.ka12.co и вот результат:

После меняем конфиг сайта как нам надо и вуаля! Теперь по приходу емайла от letsencrypt заходим и обновляем все сразу.

Новый датацентр. Докер

Следующим большим шагом в постройке “датацентра” у меня будет разворачивание докера. Без него нынче никуда, да и удобный он, зараза.

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

Но три виртуалки – это уже не одна. Требуется автоматизация. Вариантов много – от правки кикстарт файлов до клонирования уже существующих машин. Но тут я решил пойти другим путем, более простым в случае использования KVM

Добываю/делаю правильный ifcfg фаил

cat ifcfg-eth0
TYPE="Ethernet"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="no"
NAME="eth0"
DEVICE="eth0"
ONBOOT="yes"
IPADDR="172.16.100.11"
PREFIX="24"
GATEWAY="172.16.100.1"
NM_CONTROLLED="no"

Добавляю пароль рута в файл root_pass и кладу рядом свой публичный ключ. Рядом же resolv.conf. Затем создаю образ диска

virt-builder centos-7.4 --arch amd64 -o docker1.ka12.co.img --size 80G --format qcow2 --hostname docker1.ka12.co --ssh-inject root:file:/root/multik.sshkey --root-password file:root_pass --copy-in ifcfg-eth0:/etc/sysconfig/network-scripts/ --copy-in resolv.conf:/etc

И запускаю машину с ним.

virt-install --name docker1.ka12.co --network bridge=virbr100 --memory 8096 --disk path=docker1.ka12.co.img --import --os-variant centos7.0 --graphics vnc,listen=172.16.100.1 --noautoconsole

Минута и виртуалка готова. Меняем ip адрес в конфиге+хостнейм и повторяем так еще два раза. Все, ноды для кластера готовы.

Добавляем их в инвентори и бутсрапим до приемлемого состояния.

ansible-playbook -i inventory -l docker* centos-bootstrap.yml

Делать еще один плейбук лень, поэтому просто прохожу по хостам с командой

yum -y install freeipa-client && ipa-client-install --mkhomedir

ansible-playbook -i inventory docker-install.yml

И на любой машине проверяю, что докер докерит

docker run hello-world

Так как у нас инфраструктура пока из одного хоста, заморачиваться распределенным хранилищем нет смысла от слова совсем. Поэтому просто раскидаю по хостам nfs

На хосте:

mkdir -p /opt/nfs
chmod 777 /opt/nfs
cat /etc/exports
/opt/nfs 172.16.0.0/16(rw,sync,no_root_squash,no_all_squash)
systemctl restart nfs-server

Проверяю, что увидит докер

# showmount -e 172.16.100.1
Export list for 172.16.100.1:
/opt/nfs 172.16.0.0/16

Создаю супер-каталог для volume

mkdir -p /opt/nfs/data
chmod 777 /opt/nfs/data

Теперь на докер-хосте создаю volume


docker volume create --driver local --opt type=nfs --opt o=addr=172.16.100.1,rw --opt device=:/opt/nfs/data --name data

И проверяю

[root@docker1 ~]# docker volume ls
DRIVER VOLUME NAME
local data
[root@docker1 ~]# docker volume inspect data
[
{
"CreatedAt": "2018-01-27T08:46:56-05:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/data/_data",
"Name": "data",
"Options": {
"device": ":/opt/nfs/data",
"o": "addr=172.16.100.1,rw",
"type": "nfs"
},
"Scope": "local"
}
]

Теперь проверяю, как с этим будут работать контейнеры

docker container run -ti -v data:/data alpine sh

Там в /data можно посоздавать файлики и вообще поиграться “как будто в реальной жизни”.

Новый датацентр. Сервер.

Долго ли, коротко, но дошел я до того, что мне стало уж очень дорого пользоваться услугами Amazon. Оно удобно, но дорого. Значит, мне опять дорога назад, к выделенному серверу.

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

1. Централизованное управление аккаунтами. Для всяких ssh, git и прочим
2. KVM и докер.
3. Чистый, не засранный хост. Не хочу видеть по интерфейсу и 10 правил на каждый контейнер.
4. Разное.

Итак, стадия номер 1 или “подготовка”. Все использованное мной можно найти тут: https://github.com/kiltum/newdc Ну и я подозреваю, что вы уже имеете опыт администрирования linux, поэтому буду только намечать путь.

Арендую новый сервер. Hetzner, leaseweb – их много. И ставлю туда пустую и голую CentOS 7. Никаких панелей, рюшечек и прочего. Из моих уже личных требований – поставить все на raid1.

Бутстраплю ее с помощью ansible и роли centos-bootstrap.yml. Там снос selinux, firewalld, установка ntp и прочих пакетиков и обновление системы. Самый необходимый минимум.

Ставлю KVM. Эта операция одноразовая, поэтому можно и руками.

yum install qemu-kvm libvirt libvirt-python libguestfs-tools virt-install

Сношу дефаултную сеть от KVM. Это автоматически избавляет меня от кучки правил в iptables.

virsh net-autostart --disable default
virsh net-destroy default

Создаю тупой интерфейс, куда буду цеплять с помощью бриджа виртуалки

modprobe dummy numdummies=1
echo "dummy" > /etc/modules-load.d/dummy.conf
echo "options dummy numdummies=1" >> /etc/modprobe.d/dummy.conf

Генерирую для него мак-адресс (в принципе можно от балды, но лучше что бы с первыми октетами от KVM – так симпатичней)

hexdump -vn3 -e '/3 "52:54:00"' -e '/1 ":%02x"' -e '"\n"' /dev/urandom

И делаю конфиг-фаил для этого интерфейса

cat > /etc/sysconfig/network-scripts/ifcfg-dummy0
DEVICE=dummy0
MACADDR=52:54:00:1b:e1:80
NM_CONTROLLED=no
ONBOOT=yes
TYPE=Ethernet
IPV6INIT=no
BRIDGE=virbr100

и описываю бридж

cat > /etc/sysconfig/network-scripts/ifcfg-virbr100
DEVICE=virbr100
NAME=virbr100
NM_CONTROLLED=no
ONBOOT=yes
TYPE=Bridge
DELAY=2
STP=on
IPADDR=172.16.100.1
NETMASK=255.255.255.0
IPV6INIT=no

Поднимаю созданное

ifup virbr100

В результате я получил следующее

2: dummy0: mtu 1500 qdisc noqueue master virbr100 state UNKNOWN qlen 1000
link/ether 52:54:00:1b:e1:80 brd ff:ff:ff:ff:ff:ff
inet6 fe80::5054:ff:fe1b:e180/64 scope link
valid_lft forever preferred_lft forever
...
5: virbr100: mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 52:54:00:1b:e1:80 brd ff:ff:ff:ff:ff:ff
inet 172.16.100.1/24 brd 172.16.100.255 scope global virbr100
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe1b:e180/64 scope link
valid_lft forever preferred_lft forever

Разрешаю форвардинг пакетиков между интерфейсами

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.forwarding=1" >> /etc/sysctl.conf
sysctl -p

Для полноценной работы виртуалок я добавляю в стандартный набор правил еще одно, которое обеспечивает виртуалкам выход в интернет. Вместо иксиков подставьте свой внешний ipшник

-A POSTROUTING -o eth0 -j SNAT --to-source x.x.x.x

И в принципе все, я готов создать первую виртуалку.

virt-install --name head.ka12.ko --network bridge=virbr100 --memory 1024 --disk path=head.ka12.co.img,size=30 --cdrom ../iso/CentOS-7-x86_64-Minimal-1708.iso --graphics vnc,listen=172.16.100.1 --noautoconsole

И получаю к ней доступ по VNC

virsh vncdisplay head.ka12.co

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

ssh -L 5901:172.16.100.1:5900 kiltum@ka12.co

Дальше открываю VNC клиент и по адресу localhost:5901 получаю консоль виртуалки. Дальше как обычно “Далее-далее-подождать и перезагрузить”. Опять же можно было заморочиться и использовать автостарты, но мне лень такое делать на редких и одноразовых операциях.

У этой новой виртуалки будет совершенно ожидаемый адрес 172.16.100.2. После “ребута” инсталлятора снова запускаем и ставим в автозапуск

virsh start head.ka12.co
virsh autostart head.ka12.co

Первым делом надо решить вопрос с доступом в мою “инфраструктуру”. Тут пока ничего лучше не придумали, как openvpn. Запихиваю в /etc/resolv.conf временный адрес DNS сервера и начинаю

yum -y install epel-release mc
yum -y install openvpn easy-rsa

Меняю конфиги openvpn

cat server.conf
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
server 172.16.101.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 172.16.0.0 255.255.0.0"
;push "redirect-gateway def1 bypass-dhcp"
;push "dhcp-option DNS 172.16.100.2"
;push "dhcp-option DOMAIN ka12.co"
duplicate-cn
keepalive 10 120
comp-lzo
user nobody
group nobody
persist-key
persist-tun
verb 3

Обратите внимание, специально закомментировал некоторые строки, которые понадобятся в будущем. Теперь генерирую ключи

mkdir -p /etc/openvpn/easy-rsa/keys
cp -rf /usr/share/easy-rsa/2.0/* /etc/openvpn/easy-rsa
mcedit /etc/openvpn/easy-rsa/vars
cp /etc/openvpn/easy-rsa/openssl-1.0.0.cnf /etc/openvpn/easy-rsa/openssl.cnf
cd /etc/openvpn/easy-rsa
source ./vars
./clean-all
./build-ca
./build-key-server server
./build-dh
cd /etc/openvpn/easy-rsa/keys
cp dh2048.pem ca.crt server.crt server.key /etc/openvpn
cd /etc/openvpn/easy-rsa
./build-key client

Разрешаю форвардинг

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.forwarding=1" >> /etc/sysctl.conf
sysctl -p

и запускаю openvpn

systemctl enable openvpn@server.service
systemctl start openvpn@server.service

так как там все по умолчанию, очищаю правила фаерволла на виртуалке

iptables -F

Все, теперь на хосте надо открыть порт 1194/udp и пробросить его на виртуалку

-A PREROUTING -d x.x.x.x/32 -i eth0 -p udp --dport 1194 -j DNAT --to-destination 172.16.100.2
-A INPUT -p udp -m state --state NEW -m udp --dport 1194 -j ACCEPT

Добавляю роутинг до подсети vpn

ip route add 172.16.101.0/24 via 172.16.100.2

cat > /etc/sysconfig/network-scripts/route-virbr100
172.16.101.0/24 via 172.16.100.2 dev virbr100

Делаю темплейт клиентского конфига

cat client.template
client
dev tun
proto udp
remote vpn.ka12.co 1194
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
verb 3
key-direction 1
remote-cert-tls server
mssfix
<ca>
ca.crt
</ca>
<cert>
client.crt
</cert>
<key>
client.key
</key>
<tls-auth>
ta.key
</tls-auth>

И заполняю его (в дальнейшем я сделал маленький скрипт gen_config.sh)

sed -e '/ca.crt/rca.crt' -e '/client.crt/reasy-rsa/keys/client.crt' -e '/client.key/reasy-rsa/keys/client.key' -e '/ta.key/rta.key' -e '/crt/d' -e '/\.key/d' client.template | sed -e 's/#.*$//' -e '/^$/d' > cl.ovpn

Цепляюсь и проверяю доступность хоста через vpn

traceroute 172.16.100.1
traceroute to 172.16.100.1 (172.16.100.1), 64 hops max, 52 byte packets
1 172.16.101.1 (172.16.101.1) 53.941 ms 48.797 ms 47.938 ms
2 172.16.100.1 (172.16.100.1) 49.186 ms !Z 48.813 ms !Z 49.865 ms !Z

Пробрасываю ключ, бутстраплю виртуалку и ставлю в ней FreeIPA
ssh-copy-id root@172.16.100.2

ansible-playbook -i inventory -l head.ka12.co centos-bootstrap.yml

yum -y install ipa-server bind bind-dyndb-ldap ipa-server-dns

ipa-server-install --setup-dns --mkhomedir --allow-zone-overlap

Меняю шелл по умолчанию

kinit admin
ipa config-mod --defaultshell=/bin/bash

Внезапно оказалось, что FreeIPA с 1 гигом памяти стартует очень тяжко. Добавляю памяти

virsh setmaxmem head.ka12.co 4G --config
virsh setmem head.ka12.co 2G --config

И включаю VPN на полную

push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 172.16.100.2"
push "dhcp-option DOMAIN ka12.co"

Добавляю pam сервис

cd /etc/pam.d/
ln -s system-auth openvpn

Добавляю группу openvpn и sshd и заношу в нее меня. В принципе все тоже самое можно сделать через веб-интерфес FreeIPA.

ipa hbacsvc-add openvpn
ipa hbacrule-add allow_openvpn
ipa hbacrule-add-service allow_openvpn --hbacsvcs=openvpn
ipa hbacrule-add-user allow_openvpn --user=kiltum
ipa hbacrule-add-service allow_sshd --hbacsvcs=sshd
ipa hbacrule-add-user allow_sshd --user=kiltum
ipa hbacrule-add-host allow_sshd --hosts=head.ka12.co
ipa hbactest --user=kiltum --host=head.ka12.co --service=sshd

Теперь остался последний шаг. Добавляю в конфиг сервера

plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn

а в конфиг клиента

auth-user-pass

И всё.

В случае проблем проверить работу авторизации можно и прямо на стороне сервера:

pamtester openvpn kiltum authenticate

Что же получил на данном этапе?

1. Хост, на котором крутится только KVM и больше ничего. А значит: ошибиться негде, ломать тоже особо нечего.
2. OpenVPN, доступ до которого защищен сразу двумя методами: клиентским сертификатом и логином-паролем.
3. Внутренний DNS, который снаружи никак не виден.
4. Управление пользователями/группами/сервисами. Располагается исключительно во внутренней сети.
5. Даже если опустить везде фаирволлы, то ничего нового не будет доступно снаружи.

Postgres PITR to one file

У штуки под названием postgres есть очень хороший способ бекапа. Называется он PITR. Стандартный процесс бэкапа выглядит так:

touch /var/lib/pgsql/backup_in_progress
psql -c "select pg_start_backup('hot_backup');"
tar -cf /var/lib/pgsql/backup.tar /var/lib/pgsql/data/
psql -c "select pg_stop_backup();"
rm /var/lib/pgsql/backup_in_progress
tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/

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

Первым предположением будет добавить ключик z или j – пусть сразу пакует. И тут сразу же возникает проблема: нельзя добавить файликов в уже запакованный tar. Надо распаковывать, добавлять и снова запаковывать. Какие есть пути решения?

1. Так и таскать два .tar.gz файла. Одиним меньше, другим больше …
2. Забить и переложить проблему на админов сторов. Пусть дают больше места и скорости.
3. Сделать скрипт, который где-то там, далеко, будет перепаковывать файлы. Заодно и целостность бекапа проверит.

Но я решил пойти другим путем. Он чуть-чуть посложнее, но зато результатом становится один сжатый файл. Вот упрощенный псевдокод:

mkfifo backup_fifo
sleep 98765 > backup_fifo
stdbuf -i0 -o0 -e0 cat backup_fifo | cpio -o -H tar | pigz -q > /path/to/backup.gz
psql -c "select pg_start_backup('hot_backup');"
stdbuf -i0 -o0 -e0 find postgres/data -type f > backup_fifo
psql -c "select pg_stop_backup();"
stdbuf -i0 -o0 -e0 find postgres/archive -type f > backup_fifo
kill sleep

Расскажу последовательно:

mkfifo backup_fifo. Создаем fifo фаил. Он будет у нас очередью для имен файлов, подлежащих архивированию.

sleep 98765 > backup_fifo. Открываем fifo и держим его открытым. Думаю, что 68 суток должно хватить для любого бекапа.

cat backup_fifo | cpio -o -H tar | pigz -q Запускаем процесс “таренья” всего, чьи имена прилетят в fifo. Так как tar не умеет читать имена файлов с stdin, использовал cpio в режиме tar. Ну и pigz – это параллельный gzip.

А дальше полностью повторяем стандартный процесс бекапа postgres, без каких-либо отступлений от генеральной линии. В конце прибиваем sleep и fifo закрывается, закрывая за собой все остальное.

В чем тонкости?

1. Использование sleep в качестве держалки для fifo. Я больше не смог вспомнить ни одной утилиты, которые ничего никуда не пишут, но открывают stdout & stdin.
2. Использование stdbuf. Если её не использовать, то из-за буферизации будет невозможно понять, какой и когда закончился этап. В результате легко получается, что tar забирает не то, что нужно.

Для предотвращения гонок в пункте 2 я пробовал вставлять в бекап файлы-маркеры и потом отслеживать время доступа к ним, но решение получилось … не элегантным и потребовало третий поток для исполнения.

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

ЗЫ Картинку честно стащил из интернета.