🏠: Viacheslav

Влияние одного параметра на общую производительность системы

Несколько месяцев назад я настроил на работе прокси-сервер squid, он успешно прошёл стадию тестирования и с декабря прошлого года переведён в боевой режим.

Статистика за февраль

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

Нагрузка на 6 виртуальных процессоров вчера

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

Программа top показала, что сам squid весьма скромен в потреблении ресурсов, а процессор загружен процессами аутентификации kerberos negotiate_kerberos_auth. Оказалось, что в kerberos есть replay cache, который пытается бороться с потенциальной подменой ключей шифрования, но, во-первых, он несовершенен, о чём прямо упомянуто в описании, а во-вторых, сильно тормозит более-менее нагруженную систему.

# man negotiate_kerberos_auth

Kerberos can keep a replay cache to detect the reuse of Kerberos
tickets (usually only possible in a 5 minute window). If squid is under
high load with Negotiate (Kerberos) proxy authentication requests the
replay cache checks can create high CPU load. If the environment does
not require high security the replay cache check can be disabled for
MIT based Kerberos implementations by adding the below to the startup
script or use the -t none option.

Добавляем параметр -t none к команде аутентификации:

auth_param negotiate program /usr/lib/squid/negotiate_kerberos_auth -s HTTP/proxy.domain.ru@DOMAIN.RU -t none

Результат очень радует:

Нагрузка на 4 виртуальных процессора сегодня

Хотел сразу оставить два vCPU, но перестраховался, оставил пока четыре, вечерком сделаю.

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

Новый неттоп

Когда-то давно, в прошлой жизни — 26 августа 2013 года — я купил прекрасный неттоп Pegatron Saishiat2+ за скромную цену в 5308 рублей, который проработал у меня без каких-либо проблем до вчерашнего дня. Он не сломался, нет — он всё так же отлично работает, просто мне уже нужен аппарат побыстрее, соответствующий сегодняшним требованиям; тем более, что старый компьютер уже на момент его покупки звёзд с неба не хватал, что уж говорить по прошествии стольких лет.

Критерии выбора:

  1. ПК должен быть компактным, времена громоздких жестяных ящиков давно ушли, поэтому форм-фактором также будет неттоп.
  2. Аппаратная поддержка процессором кодирования в x264 (AVC) и x265 (HEVC).
  3. Поддержка >= 32 ГБ оперативной памяти (виртуализация, контейнеры, вот это всё).
  4. Встроенный wi-fi c поддержкой 802.11ac.
  5. Нормальная работа Майнкрафта без тормозов на стандартных настройках графики.
  6. Цена — не космическая.

Выбор пал на линейку Intel NUC, где из современных вариантов есть 8-е и 10-е поколение процессоров. Десятое поколение дороже примерно на четверть и поддерживает аж до 64 ГБ памяти, но по производительности уступает восьмому из-за медленного графического ядра. Между вариантами на процессорах i3 и i5 я выбрал последний (i5-8259U), потому что разница в цене между ними — пара тысяч, а i5 ощутимо мощнее. Изначально я хотел рассмотреть что-то сопоставимое на процессоре AMD, но ничего похожего на сегодняшний день на рынке, к сожалению, не существует.

В восьмом поколении есть две модификации нужной мне модели — NUC8I5BEK и NUC8I5BEH, первый тоньше и в него можно вставить «жёсткий диск» только формата M.2; второй толще, потому что у него, помимо того же M.2, есть возможность поставить стандартный SATA-диск 2,5″.

Слева направо: старый неттоп, планки памяти, новый неттоп

Так как у меня уже есть SATA-диск Samsung SSD 850 EVO 250 GB, который стоит в старом неттопе, я выбрал второй вариант как более универсальный. Получается дешевле, так как не нужно прямо сейчас тратиться на диск M.2, да и переехать проще — просто переставить уже имеющийся диск из одной коробочки в другую. Памяти я взял 2 планки по 16 ГБ, с уменьшенной CAS Latency (CL15, стандартное значение — 17).

IMG_20210120_193005.jpg
IMG_20210120_193527.jpg

Всё-таки, к вещам привязываешься. Разбирая свой старый компьютер и вытаскивая из него диск, мне было жалко его, и я думал об этих прошедших 7 годах с какой-то ностальгией. Тем не менее, и монитор-долгожитель Benq G900, который мне подарили на новый 2008 год, и стол, и даже клавиатура с мышкой всё те же — вид моего рабочего места мало чем отличается от прежнего. У монитора недавно провалилась кнопка включения — отломились пластмассовые маленькие капельки, которыми кнопка была приварена одним концом к тыльной стороне передней панели, но я зафиксировал её с помощью смеси суперклея и соды — теперь монитор выглядит как новый, и менять я его на сегодняшний день не планирую.

IMG_20210120_194307.jpg
IMG_20210120_194536.jpg

Переезд прошёл безо всяких трудностей, Windows 10 завелась как ни в чём не бывало, нужно было только поставить несколько пакетов с драйверами да обновить пару прошивок; машинка работает отлично и шустро, надеюсь, на следующие 7 лет хватит. Впоследствии можно будет перейти на диск M.2 как более скоростной и прогрессивный вариант, но это совершенно не к спеху. Старый неттоп я буду продавать вместе с установленной в него планкой памяти на 8 ГБ, тем более, что у меня сохранился полный комплект — и коробка, и документация, и совершенно новая нога для установки на стол, и даже компакт-диск (Upd: 27 января ушёл за 2400).

Общий вид рабочего места

Заменил кран раковины

«…спорол галуны ливрейные, изул штиблеты от ног своих
и с внутренним сдержанным удовольствием возвратился
к серому сюртуку и тихим холстинным панталонам.»
А. Сухово-Кобылин — Дело

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

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

Снятый старый кран

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

Новый кран

Ну, с почином.

Ковёр в прихожей

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

Формулируем задачу: найти покрытие, которое легко чистить, оно не пропускает воду, покрывает всю поверхность коридора, недорогое. Оказалось, что существует материал под названием этиленвинилацетат (ЭВА), из которого делают коврики в автомобили и ещё много чего, а в частности, можно купить из него листы разных размеров и цветов. Он отвечает поставленной задаче и имеет пористую структуру.

Рассчитав примерно свои потребности, я купил лист 3,6 м2 (1,4 на 2,55 м).

Разметив и порезав его на куски, получил следующий результат:

Материал напоминает твёрдый поролон и довольно легко режется обычным канцелярским ножом. В моём случае ситуация осложнялась тем, что стены у меня очень кривые и просто так под 90° резать не вышло, из-за этого примыкания неидеальны.

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

Бюджет вместе с клеящей лентой — примерно 3000 рублей. Мне пока нравится — выглядит, во всяком случае, не так колхозно, как раньше. Теперь посмотрим, как оно в эксплуатации.

Путь к улучшению путём осмысления целого или автонастройка прокси-сервера на клиентских машинах

Хорошо спроектированная система обладает следующими признаками:

  • Обладает максимальным охватом всех возможных применений в рамках её предназначения
  • Управляется централизованно и имеет автоматические проверки и процедуры для минимизации влияния т. н. человеческого фактора
  • Имеет лаконичное и хорошо читаемое описание
  • Не требует большого количества исключений и их ручной настройки
  • Рационально использует ресурсы

Может быть, что-то ещё, но направление мысли, думаю, понятно. Применительно, предположим, к настройке подключения к интернету пользователей в организации — если централизованно вроде бы что-то и настроено, но при этом постоянно нужно что-то делать на пользовательских машинах, прописывать маршруты, ставить-снимать галки в свойствах браузера, делать резервирования в DHCP, разрешать прямой выход в интернет в обход прокси-сервера, просить сетевиков написать на шлюзе очередное правило для одной машины, при этом все эти 100500 изменений нигде не записаны и делаются бессистемно от случая к случаю; если в организации существуют дополнительные шлюзы, где всё открыто, потому что «надо работать и некогда разбираться» — это всё наводит на мысли, что система спроектирована как-то неправильно.

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

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

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

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

Выяснилось, что за автонастройку клиентов отвечает файл wpad.dat следующего содержания:

function FindProxyForURL(url, host)
{
    $NewProxy = "PROXY proxy.domain.ru:3128";

    if (isPlainHostName(host)) {return "DIRECT";}   
    if (isInNet(host, "10.1.0.0", "255.255.0.0")) {return "DIRECT";}
    if (dnsDomainIs(host,"localhost")) {return "DIRECT";}
    if (dnsDomainIs(host,"127.0.0.1")) {return "DIRECT";}

    if (dnsDomainIs(host,"domain.ru")) {return "DIRECT";}
    if (dnsDomainIs(host,"support.domain.ru")) {return "DIRECT";}
    if (dnsDomainIs(host,"test-sps.domain.ru")) {return "DIRECT";}
    if (dnsDomainIs(host,"portal.domain.ru")) {return "DIRECT";}
    if (dnsDomainIs(host,"portal.domain.ru")) {return "DIRECT";}

    if (shExpMatch(url, "http:*")
    ||  shExpMatch(url, "https:*")
    ||  shExpMatch(url, "ftp:*")
    ||  shExpMatch(url, "gopher:*"))
    return $NewProxy;

    return $NewProxy;
     }

Не принимая в расчёт общую неструктурированность файла, дубль записи и явную копипасту раздела с url (кто вообще сегодня знает про gopher?), возникает вопрос — а почему просто не пустить напрямую весь локальный .domain.ru? «А потому что есть ресурсы в domain.ru, опубликованные на внешних IP-адресах», — ответили мне. Дело в том, что и внешний, и внутренний домен названы одинаково. Что же делать? Полез в интернет и через некоторое время обнаружил отличное описание файла конфигурации с примером, как раз подходящим в моей ситуации:

function FindProxyForURL(url, host) {
  if (
    (isPlainHostName(host) || dnsDomainIs(host, ".mozilla.org")) &&
    !localHostOrDomainIs(host, "www.mozilla.org") &&
    !localHostOrDoaminIs(host, "merchant.mozilla.org")
  ) {
    return "DIRECT";
  } else {
    return "PROXY w3proxy.mozilla.org:8080; DIRECT";
  }
}

Красивая логика — на одну ступенечку сложнее, и уже такая мощь! Пускаем напрямую имена без указания домена или весь домен, но не конкретные имена в этом же домене. Больше того, можно указывать цепочку прокси-серверов — в данном примере, если не будет доступен w3proxy.mozilla.org, то идти напрямую.

Так, логика ясна. И сразу напрашивается решение эти опубликованные вовне ресурсы локального домена собирать из DNS автоматически — они ведь могут добавляться туда — и на этой основе формировать файл автонастройки. Тут уже приходит на помощь любимый Powershell:

# Records in domain.ru zone
$domainRecs = Get-DnsServerResourceRecord -ZoneName "domain.ru" -computerName "srv-dc3" |select hostname,recordtype,@{n='IPAddr';e={$_.RecordData.ipv4address.IPAddressToString}},@{n='Alias';e={$_.RecordData.hostnamealias -replace "\.domain\.ru\."}}
# External A records
$domainARecsExt = $domainRecs |? {$_.IPAddr -match "^186" -and $_.recordtype -eq "A"}
# CNAMEs which correspond external A records
$domainCnameRecsExt = foreach ($aRec in $domainARecsExt.hostname) {
$domainRecs |? {$_.recordtype -eq "CNAME" -and $_.alias -eq "$aRec"}
}
# all external names list
$domainAllExtRecs = ($domainARecsExt + $domainCnameRecsExt).hostname -replace "$",".domain.ru" |sort

# PAC file
$pac1 = "function FindProxyForURL(url, host) {
  if (
    (isPlainHostName(host) ||
    dnsDomainIs(host, `".domain.ru`") ||
    isInNet(host, `"10.0.0.0`", `"255.0.0.0`") ||    
    isInNet(host, `"127.0.0.0`", `"255.0.0.0`") ||
    localHostOrDomainIs(host, `"localhost`")) `&`&
"

# form the list for PAC
$total = $domainAllExtRecs.count
$C = 1

$pac2 = $domainAllExtRecs |% {
    if ($c -lt $total) {
    "    !localHostOrDomainIs(host, `"$_`") `&`&"
    }
    else {
    "    !localHostOrDomainIs(host, `"$_`")"
    }
$c++
}

$pac3 = "
  ) {
    return `"DIRECT`";
  } else {
    return `"PROXY proxy.domain.ru:3128`";
  }
}
"
# Combine all pieces and dump
$pac1 + ($pac2 -join "`n") + $pac3 |Out-File ~\Documents\proxy.pac -Encoding default

По желанию можно прикрутить сверку и оповещение на почту, если что-то убрали или добавили в DNS (в примере адреса начинаются на 186). Вот итоговый файл:

function FindProxyForURL(url, host) {
  if (
    (isPlainHostName(host) ||
    dnsDomainIs(host, ".domain.ru") ||
    isInNet(host, "10.0.0.0", "255.0.0.0") ||
    isInNet(host, "127.0.0.0", "255.0.0.0") ||
    localHostOrDomainIs(host, "localhost")) &&
    !localHostOrDomainIs(host, "8mar.domain.ru") &&
    !localHostOrDomainIs(host, "wolf.domain.ru") &&
    !localHostOrDomainIs(host, "wolf1.domain.ru") &&
    !localHostOrDomainIs(host, "wolf3.domain.ru") &&
    !localHostOrDomainIs(host, "wolfapi.domain.ru") &&
    !localHostOrDomainIs(host, "docs.domain.ru") &&
    !localHostOrDomainIs(host, "old.domain.ru") &&
    !localHostOrDomainIs(host, "xonix.domain.ru") &&
    !localHostOrDomainIs(host, "services.domain.ru") &&
    !localHostOrDomainIs(host, "test.xonix.domain.ru") &&
    !localHostOrDomainIs(host, "test.services.domain.ru") &&
    !localHostOrDomainIs(host, "web.domain.ru") &&
    !localHostOrDomainIs(host, "www.8mar.domain.ru") &&
    !localHostOrDomainIs(host, "www.docs.domain.ru") &&
    !localHostOrDomainIs(host, "www.domain.ru") &&
    !localHostOrDomainIs(host, "www.xonix.domain.ru") &&
    !localHostOrDomainIs(host, "www.services.domain.ru")
  ) {
    return "DIRECT";
  } else {
    return "PROXY proxy.domain.ru:3128";
  }
}

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

А теперь уже можно заняться другими интересными вещами — отрезать назойливую телеметрию, засоряющую access.log, настраивать кэширование для того, чтобы каждый компьютер не лез в интернет за обновлением Google Chrome, а получал его с прокси-сервера, и так далее.