суббота, 17 декабря 2022 г.

Релиз jtv2xmltv 0.2.2

Выпустил обновление jtv2xmltv 0.2.2 в котором исправил ошибку с неверным разбором названий программ в случае если в NDX файле есть несколько ссылок на одну и ту же запись в PDT файле. Конвертация проходила без ошибок, но часть названий программ не соответствовали расписанию.

Изначально этот конвертер писался для работы с провайдером IPTV, который предоставлял EPG только в формате JTV. Позже стали выкладывать и XMLTV и необходимость в конвертере отпала.

Из других изменений:

  • добавил список изменений
  • поправил тесты
  • добавил CI пайплайн

Пакет для Debian/Ubuntu - jtv2xmltv_0.2.2-1_all.deb 

среда, 30 ноября 2022 г.

GCloud prompt

Много работаю с GCP и зачастую приходится иметь дело с несколькими проектами одновременно и переключаться между ними в течении дня. Случаются досадные ситуации когда команда была выполнена не в том проекте или не в том GKE кластере. Последнее особенно коварно, т.к. в корпоративной среде обычно один и тот же пользователь имеет доступ в разные кластера в разных проектах и переключение профиля gcloud не влияет на kubectl.

пятница, 25 ноября 2022 г.

Bitnami Sealed Secrets

Чтобы управлять Kubernetes секретами в духе GitOps нужно их шифровать перед фиксацией в Git и расшифровывать на стороне Kubernetes. Для CI я раньше использовал Mozilla SOPS, а для Kubernetes решил попробовать Bitnami Sealed Secrets.

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

понедельник, 21 ноября 2022 г.

Частичное зеркалирование данных по SFTP

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

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

Для решения этой задачи подошла утилитка lftp, которая поддерживает много протоколов включая SFTP. Список шаблонов для синхронизации записали в файлик, который служит аргументом для --include-glob-from. Закачка самих файлов идет в несколько потоков (--parallel) с удалением тех файлов, которых больше нет на источнике (--delete).

#!/bin/bash

LFTP_PROTOCOL=sftp
LFTP_HOST=sftp.example.com
LFTP_PORT=22
LFTP_USERNAME=sftpuser
SFTP_PASSWORD=sftppass
REMOTE_DIR=/remote/path
LOCAL_DIR=/local/path
INCLUDE_LIST=$(dirname $(readlink -f $0))/sftp-mirror.include

lftp -u ${LFTP_USERNAME},${LFTP_PASSWORD} ${LFTP_PROTOCOL}://${LFTP_HOST}:${LFTP_PORT} <<_EOF_
cd ${REMOTE_DIR}
lcd ${LOCAL_DIR}
mirror --recursion=always --no-empty-dirs --delete --parallel=8 --include-glob-from=${INCLUDE_LIST}
close
_EOF_

Файл sftp-mirror.include с шаблонами имен по одному на строку, которые нужно зеркалировать

*Element=ABA090*.xml.gz
*Element=BAC105*.xml.gz
*Element=DOM288*.xml.gz

Расчет разницы и выкачивание новых файлов происходит очень быстро.

пятница, 18 ноября 2022 г.

Nginx запрашивает / вместо /.well-known/openid-configuration

Настраиваю workload identity federation между on-prem GitLab и GCP согласно документации. Сделал настройки в GCP, взял готовый пример пайплайна, но джоба падает с ошибкой

$ gcloud auth print-access-token
ERROR: (gcloud.auth.print-access-token) ("Error code invalid_grant: Parsing error for OIDC discovery document: [Line 0, column 0: Unexpected end of stream : expected '{']", '{"error":"invalid_grant","error_description":"Parsing error for OIDC discovery document: [Line 0, column 0: Unexpected end of stream : expected \'{\']"}')

Смотрю что выдаёт https://GITLAB/.well-known/openid-configuration и вижу 302 редирект на /users/sign_in - это неожиданное поведение. На этот запрос должно возвращать JSON вида

$ curl -s https://gitlab.com/.well-known/openid-configuration
{"issuer":"https://gitlab.com","authorization_endpoint":"https://gitlab.com/oauth/authorize","token_endpoint":"https://gitlab.com/oauth/token","revocation_endpoint":"https://gitlab.com/oauth/revoke","introspection_endpoint":"https://gitlab.com/oauth/introspect","userinfo_endpoint":"https://gitlab.com/oauth/userinfo","jwks_uri":"https://gitlab.com/oauth/discovery/keys","scopes_supported":["api","read_api","read_user","read_repository","write_repository","read_registry","write_registry","sudo","openid","profile","email"],"response_types_supported":["code"],"response_modes_supported":["query","fragment"],"grant_types_supported":["authorization_code","password","client_credentials","refresh_token"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"claim_types_supported":["normal"],"claims_supported":["iss","sub","aud","exp","iat","sub_legacy","name","nickname","email","email_verified","website","profile","picture","groups","groups_direct","https://gitlab.org/claims/groups/owner","https://gitlab.org/claims/groups/maintainer","https://gitlab.org/claims/groups/developer"]}

Нужно разобраться откуда у меня берется этот редирект.

воскресенье, 13 ноября 2022 г.

Spinnaker - день второй

Второй день изучения Spinnaker (почитать про первый день можно тут). На сегодня в планах построить минимальную инфраструктуру вокруг Spinnaker и задеплоить приложение в отдельный GKE кластер.

Spinnaker у меня крутится в VirtualBox, поэтому нужно создать ключ для IAM сервис аккаунта, который будет использоваться для доступа в GCS, GCR и Pub/Sub. Для подключения к GKE я сгенерировал отдельный kubeconfig.

При составлении пайплайна для Spinnaker я пользовался репозитарием spinnaker-for-gcp. Сам туториал у меня не завелся из-за недоступности управления IAM в Cloud Playground.

среда, 9 ноября 2022 г.

Binary grep

В базе материализовалась запись, которая приводит к ошибкам вида org.postgresql.util.PSQLException: ERROR: invalid byte sequence for encoding "UTF8": 0xed 0x70 0x69. Возникает такое когда кто-то сделал изменение в таблице, но при этом соединение с базой не было настроено на UTF-8.

По прошлому опыту, данные вставили в кодировке latin1 и там будут символы вроде "í", "é", "á" или им подобные. Можно записать байтики в файлик и использовать его как паттерн для grep, но захотелось возможности грепать сразу в виде байтов.

понедельник, 7 ноября 2022 г.

Spinnaker - день первый

Первый раз я узнал про Spinnaker от коллеги года четыре назад. Тогда я немного покликал по интерфейсу, посмотрел архитектурную диаграмму и успешно забыл про него. Позже он еще несколько раз всплывал в "байках" с полей от коллег, но пересечься более не довелось.

Недели две назад Spinnaker снова появился на радаре в виде презентации от заказчика и вопросом стоит ли его использовать. Нутро подсказывало что не стоит с ним связываться, но четких аргументов я сформулировать тогда не смог т.к. практического опыта не было. Ради обоснованных аргументов я и решился на близкое знакомство со Spinnaker в рамках прошедших выходных.

воскресенье, 30 октября 2022 г.

Подключение к Kafka в изолированном окружении

Есть изолированное от внешнего мира окружение в GCP с кластером Kafka, который живет в GKE. Kafka используется как внутри GKE кластера, так и виртуальными машинами, которым Kafka выставлена через внутренний балансировщик. Часть пользователей не приемлют использование консольных команд чтобы подключиться к Kafka и хотят пользоваться привычными им инструментами вроде Offset Explorer и ему подобными.

Итого у нас есть три брокера Kafka, которые доступны внутри кластера как kafka-0.kafka-headless.default.svc.cluster.local:9092, kafka-1.kafka-headless.default.svc.cluster.local:9092 и kafka-2.kafka-headless.default.svc.cluster.local:9092. Для виртуальных машин создано три балансировщика с адресами 172.16.0.16:19092, 172.16.0.17:19092 и 172.16.0.18:19092.

Если просто пробросить порты на локальную машину, то ничего работать не будет, т.к. после подключения к Kafka брокеру клиент получит метаданные в которых будет фигурировать Kafka advertised listeners и клиент будет пытаться подключиться к ним вместо работы с проброшенными портами.

среда, 5 октября 2022 г.

Лимиты памяти в LXC для CGroupV2

С неделю назад мигрировал ряд сервисов на базе контейнеров LXC в облако. Чтобы не переплачивать за простаивающие ресурсы в облаке взял в два раза меньше памяти и процессорных ядер. Поскольку нагрузка на сервисы минимальная, а памяти на исходном сервере было с запасом, то лимиты по памяти и ядрам не были настроены, но в условиях потенциальной нехватки ресурсов нужно решить проблему "шумного соседа" (когда один сервис может исчерпать ресурсы всего сервера и привести к нестабильной работе или отказу в обслуживании у остальных сервисов).

Для начала настроил сколько RAM и SWAP может использовать каждый из контейнеров.

воскресенье, 25 сентября 2022 г.

Первые впечатления от Backup for GKE

В рамках ликбеза потрогал немного Backup for GKE чтобы понять чем он отличается от привычного Velero. На данный момент времени он в preview и часть выводов может стать неактуальными в будущем.

Первое что бросается в глаза это его ценообразование - оно зависит не только от объема данных, которые попадают в бэкап, но и от количества подов, которые бэкапятся. Т.е. если у вас условно в среднем 30 подов в месяц, то вам нужно заплатить около 30$ за backup management, плюс стоимость хранения данных. В этом плане Velero выигрывает, т.к. там в основном стоимость только за хранение данных.

Далее его региональная доступность - сейчас он поддерживается только в некоторых регионах, но при этом создать бэкапить дает кластера и в неподдерживаемых регионах. Но вот восстановить бэкап в тот же кластер уже не получится - обязательно проверяйте список поддерживаемых регионов через gcloud beta container backup-restore locations list.

четверг, 22 сентября 2022 г.

Сравнение gcloud storage и gsutil

Недавно команда gcloud storage вышла в GA и я решил проверить насколько ее производительность отличается от gsutil. Для тестов нашелся GCS бакет с примерно 21k мелких объектов (средний размер 24kB), которые сжаты gzip.

Запускать тесты буду в e2-standard-2 VM (2 vCPU / 8 GB). Текущая версия Google SDK:

$ gcloud version
Google Cloud SDK 403.0.0
alpha 2022.09.20
beta 2022.09.20
bq 2.0.77
bundled-python3-unix 3.9.12
core 2022.09.20
gcloud-crc32c 1.0.0
gsutil 5.13

Чтобы не зависеть от скорости работы блочного устройства тесты буду выполнять на ramfs.

$ sudo mkdir /mnt/ramdisk
$ sudo mount -t ramfs none /mnt/ramdisk
$ sudo chmod 777 /mnt/ramdisk
$ cd /mnt/ramdisk
$ mkdir test1 test2

Копирование данных через gcloud storage cp

$ cd /mnt/ramdisk/test1/
$ time gcloud storage cp -r gs://BUCKET/SAMPLE/DATA/* .

[skipped output]

  Completed files 21000/21000 | 448.9MiB/448.9MiB | 1.5MiB/s

Average throughput: 1.5MiB/s

real    4m54.065s
user    9m4.797s
sys     0m30.854s

Копирование данных через gsutil cp (используется многопоточность)

$ cd /mnt/ramdisk/test2/
$ time gsutil -m cp -r gs://BUCKET/SAMPLE/DATA/* .

[skipped output]

Operation completed over 21.0k objects/448.9 MiB.

real    5m58.861s
user    10m8.143s
sys     1m18.350s

В моем тесте копирование мелких объектов c использованием gcloud storage быстрее gsutil примерно на 18%. Пока что gcloud storage не заменит полностью gsutil, но для простых операций стоит использовать её.

четверг, 25 августа 2022 г.

Раздельный SSID для Huawei LG8245X6-50

С некоторых пор домашний интернет обеспечивает роутер Huawei LG8245X6-50 с поддержкой Wifi 6 (802.11ax). В комнатах, которые прилегают к шкафу с роутером, интернет работает стабильно, но в дальней комнате из-за обилия стен сигнал был в районе от -75dBm до -85dBm. В таких условиях ни о какой надёжной работе сети говорить не приходится (считается что приемлемый уровень сигнала должен в районе -65dBm).

Из коробки роутер идет с одинаковым SSID для 2.4GHz и 5GHz что с одной стороны должно упрощать жизнь, если 5Ghz не "добивает", но 2.4GHz работает нормально (5GHz хуже "пробивает" стены, особенно если они из железобетона). Но в моём случае в зоне слабого сигнала клиенты постоянно "скачут" с 2.4GHz на 5GHz и обратно.

Чтобы этого не происходило нужно присвоить разные SSID для разных частот. И тут кроется проблема с Huawei LG8245X6-50 - админка не дает переименовать SSID для сети 5Ghz. Поле имени копирует настройки с 2.4GHz и не даёт сменить отдельно. Чтобы обойти это ограничение достаточно завести по дополнительному SSID для 2.4GHz и 5GHz с разными именами, а после деактивировать изначальный SSID.

пятница, 1 июля 2022 г.

Обновления репозитария - июль 2022

Добавил в свой репозитарий поддержку Ubuntu Jammy (22.04) и собрал пакеты Midnight Commander 4.8.28 для архитектур amd64, armhf и arm64.

Удалил Debian Stretch (LTS поддержка закончилась вчера) и Ubuntu Hirsute (поддержка закончилась еще в январе, но руки не доходили заняться).

четверг, 23 июня 2022 г.

Настройка Google Cloud Ops Agent для Nextcloud

В процессе настройки отправки логов Nextcloud в Google Cloud Logging довелось поработать с Google Cloud Ops Agent. Это относительно новый агент, который отвечает и за логи и за метрики.

Nextcloud пишет структурированные логи в JSON формате так что возиться с регулярками не придется. Одна запись выглядит примерно так:

{
  "reqId": "fA6aIAL7fObKyDA2YeDq",
  "level": 2,
  "time": "2022-06-21T06:55:18+00:00",
  "remoteAddr": "",
  "user": "--",
  "app": "news",
  "method": "",
  "url": "--",
  "message": "https://[skipped]/data/rss read error : Server error: `GET https://[skipped]/data/rss` resulted in a `500 Internal Server Error` response:\n<html>\r\n<head><title>500 Internal Server Error</title></head>\r\n<body bgcolor=\"white\">\r\n<center><h1>500 Internal Server E (truncated...)\n",
  "userAgent": "--",
  "version": "23.0.5.1"
}

Для начала нужно вытащить timestamp и severity. Timestamp буду брать из поля time, а severity из поля level, но для level потребуется преобразование из значения Nextcloud в значение Google LogEntry:

среда, 22 июня 2022 г.

Настройка Nextcloud для работы с Redis

В процессе настройки Nextcloud для работы с Redis на нескольких серверах столкнулся с тем, что в phpredis есть нюансы с поддержкой TLS для PHP сессий.

В phpredis есть два session handler'а:

  • session.save_handler = redis
  • session.save_handler = rediscluster
В phpredis 5.3.2 в реализацию PHP сессий для rediscluster добавили поддержку TLS, но в обычном redis ее по-прежнему нету.

Пришлось отключить TLS в сервере Redis чтобы не городить отдельное решение для PHP сессий. На всякий случай оставлю тут настройки:

config/config.php

'redis' =>
  array (
    'host' => 'redis.example.com',
    'port' => '6379',
    'password' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
  ),
  'memcache.local' => '\\OC\\Memcache\\Redis',
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'filelocking.enabled' => 'true',

php.ini

redis.session.locking_enabled=1
redis.session.lock_retries=-1
redis.session.lock_wait_time=10000
session.save_handler = redis
session.save_path = "tcp://redis.example.com:6379?auth=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Документацию по настройке Redis для Nextcloud можно найти тут.

вторник, 7 июня 2022 г.

Terraform: could not connect to registry.terraform.io

С некоторых пор из-за санкций в Беларуси есть проблемы с доступом к зарубежным ресурсам и в частности с выкачиванием провайдеров Terraform:

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/google from the dependency lock file
╷
│ Error: Failed to query available provider packages
│
│ Could not retrieve the list of available versions for provider hashicorp/google: could not connect to registry.terraform.io: Failed to request discovery
│ document: 403 Forbidden
╵

Заход в документацию из браузера также блокируется

Самым простым решением будет использование VPN из другого региона, но если такой вариант по каким-либо причинам не подходит, но есть SSH доступ к внешнему хосту в "правильном" регионе, то выкрутиться можно так:

вторник, 29 марта 2022 г.

Midnight Commander 4.8.28

Собрал пакеты Midnight Commander 4.8.28 для Debian/Ubuntu с архитектурами i386, amd64, armel, armhf и arm64. Пакеты для i386 и armel есть только в тех дистрибутивах, где они поддерживаются официально (в свежих Ubuntu их нету).

Инструкция по подключению репозитария. Больше информации о бинарных сборках Midnight Commander можно найти на странице https://midnight-commander.org/wiki/Binaries.

воскресенье, 6 февраля 2022 г.

Тестирование лабораторного блока питания

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

Сперва проверил точность отображения напряжения и тока - показания сравнивал с мультиметром Zoyi ZT102. Для напряжения результаты получились не очень, т.к. индикация начинается где-то от 350мВ (до 350мВ вольтметр блока питания показывает 0.00) и вначале показания занижаются где-то до 6В, а затем завышаются. С измерением тока картина лучше - показания завышаются от 5мА до 20мА в зависимости от тока.

понедельник, 10 января 2022 г.

Лабораторный блок питания

Завершил сборку нового лабораборного блока питания на 30В/3А. В отличии от моего предыдущего лабораторника, который работает в импульсном режиме и способен выдавать до 23В и 5А, новый работает в линейном режиме, расчитан на большее напряжение, не дает помех в нагрузку и хорошо ограничивает ток.