Задаємо в nodeSelector ім’я ноди, щоб було простіше шукати в Локі.
При старті цього поду Kubernetes його вбиватиме через перевищення лімітів, а journald на WorkerNode записуватиме подію в системний журнал, який збирається promtail:
[simterm]
$ kk -n monitoring get cm logs-promtail -o yaml
...
- job_name: journal
journal:
labels:
job: systemd-journal
max_age: 12h
path: /var/log/journal
relabel_configs:
- source_labels:
- __journal__systemd_unit
target_label: unit
- source_labels:
- __journal__hostname
target_label: hostname
[/simterm]
Запускаємо наш под:
[simterm]
$ kk apply -f test-oom.yaml
pod/oom-test created
[/simterm]
Перевіряємо:
[simterm]
$ kk describe pod oom-test
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 91s default-scheduler Successfully assigned default/oom-test to ip-10-0-0-27.us-west-2.compute.internal
Normal SandboxChanged 79s (x12 over 90s) kubelet Pod sandbox changed, it will be killed and re-created.
Warning FailedCreatePodSandBox 78s (x13 over 90s) kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to start sandbox container for pod "oom-test": Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: container init was OOM-killed (memory limit too low?): unknown
[/simterm]
І перевіряємо логі Loki:
Окей, тепер у нас є oom-killed под для тестів – давайте формувати запит для майбутнього алерту.
Формування запиту в Loki
В логах ми дивилися по запиту {hostname="eks-node-dev_data_services-i-081719890438d467f"} |~ ".*OOM-killed.*" – використовуємо його ж для тестового алерту.
Спочатку перевіримо що нам намалює сама Локі – використовуємо rate()та sum(), див.Log range aggregations:
sum(rate({hostname="eks-node-dev_data_services-i-081719890438d467f"} |~ ".*OOM-killed.*" [5m])) by (hostname)
Гуд!
З цим вже можна працювати – створювати тестовий алерт.
В subPath вказуємо key з ConfigMap, щоб підключити саме як файл.
Налаштування Ruler alerting
Знаходимо Alertmanager URL:
[simterm]
$ kk -n monitoring get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
prometheus-kube-prometheus-alertmanager ClusterIP 172.20.240.159 <none> 9093/TCP 110d
...
Перевіряємо Алерти в Алертменеджері – http://localhost:9093:
Loki та додаткові labels
В алертах хочеться виводити трохи більше інформації, ніж просто повідомлення “Test Loki OOM Killer Alert”, наприклад – відобразити ім’я пода, який був вбитий.
Тут я для тестів створював нові лейбли, які підключалися до логів – sourceі level.
Інший варіант із Promtail – використовуючи static_labels.
Але тут є проблема: оскільки Loki на кожний набір лейбл створює окремий лог-стрім, для якого створюються окремі індекси та блоки даних, то в результаті отримаємо по-перше проблеми з продуктивністю, по-друге – з вартістю, т.к. на кожен індекс і блок даних будуть виконуватися запити читання-запису в shared store, у нашому випадку це AWS S3, де за кожен запит доводиться платити гроші.
Натомість, ми можемо створювати нові лейбли прямо із запиту за допомогою самої Loki.
Візьмемо запис із лога, в якому йдеться про спрацювання OOM Killer:
E1213 16:52:25.879626 3382 pod_workers.go:951] “Error syncing pod, skipping” err=”failed to \”CreatePodSandbox\” for \”oom-test_default(f02523a9-43a7-4370-85dd-1da7554496e6)\” with CreatePodSandboxError: \”Failed to create sandbox for pod \\\”oom-test_default(f02523a9-43a7-4370-85dd-1da7554496e6)\\\”: rpc error: code = Unknown desc = failed to start sandbox container for pod \\\”oom-test\\\”: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: container init was OOM-killed (memory limit too low?): unknown\”” pod=”default/oom-test” podUID=f02523a9-43a7-4370-85dd-1da7554496e6
Тут ми маємо поле pod з ім’ям пода, який був вбитий – pod="default/oom-test".
Останній раз працював з Loki коли вона була ще в Beta, і виглядала вона тоді набагато простіше, ніж зараз.
У новому проекті системи логування немає взагалі, а так як у нас усі люблять Grafana-стек – то вирішили і для логів підняти Loki.
Правда мені думалося, що все буде набагато простіше. Виявилося – ні. Багато змінилося, і довелося знайомитися з нею по суті з нуля.
Що залишилося, як і раніше, – це така собі документація. Якщо опис архітектури та компонентів ще більш-менш нормально описаний, то коли справа доходить до налаштування – то натикаєшся на купу проблем, особливо те, що стосується сторейджа та зберігання в AWS S3 (хоча поки писав цей пост викотили реліз 2.7, і документацію теж оновили – можливо, тепер вона краща). Довелося збирати по шматочках, але в результаті все-таки завелося.
Розглянемо загальну архітектуру та компоненти, потім встановимо в AWS Kubernetes із Helm-чарта.
Архітектура Grafana Loki
Loki створена за мікросервісною архітектурою, при цьому всі мікросервіси зібрані в один бінарник.
Для запуску компонентів використовується опція --target, в якій можна визначити яку частину Loki запустити.
Вхідні дані діляться на стріми – це потік даних, логів, що мають загальний tenant_id (“відправник”), і загальний набір тегів/labels. Докладніше про стріми поговоримо в Storage.
Компоненти Loki
Робота системи ділиться на два основні потоки: Read path– читання (обробка запитів на вибірку даних) та Write path – запис цих даних в сторейдж.
Загальна схема всіх компонентів:
Тут:
distributor (write path): займається обробкою вхідних даних від клієнтів – одержує від них дані, валідує їх, ділить дані на блоки, і відправляє в ingester. Бажано мати LoadBalancer перед дистриб’юторами, щоб вхідні стріми розподілялися по інстансах дистриб’юторів. Є stateless компонентом – не зберігає в собі жодних даних. Також відповідає за рейт-ліміти та препроцесинг тегів.
ingester (write, read path): відповідає за запис даних у довгострокове сховище та за передачу даних для обробки запитів на їх читання клієнтами. Для запобігання втратам даних у разі рестарту інжестера, їх зазвичай запускають у вигляді декількох інстансів (див. replication_factor)
querier (read path): обробляє LogQL запити, завантажуючи для відповіді дані з інжестерів та/або довгострокового сховища – спочатку запитує інжестер, якщо в пам’яті інжестера даних немає – то querier йде в сховище даних
query frontend (read path): опціональний сервіс, що надає доступ до querier API для прискорення операцій читання. При його використанні він зберігає запити, що надходять, а querier звертається до нього, щоб взяти з черги запит на обробку
compactor: зменшення розміру індексів та управління часом зберіганням логів у сховищі даних (retention)
Data flow
Коротко про сам процес обробки даних – запити читання (read), і запис (write).
Loki отримує дані з promtail (або інших агентів, наприклад fluentd)
створює блоки даних (chunks), індекс, завантажує їх у довгострокове сховище
користувач використовує LogQL для вибірки логів у Grafana
ruler перевіряє данні, і при необхідності відправляє алерт у Prometheus Alertmanager
Read Path
При отриманні запиту на вибірку даних:
querier отримує HTTP запит
передає запит до ingesters для пошуку даних в пам’яті
якщо ingesters знаходитт дані у себе – то повертає їх querier
якщо в ingesters даних нема – querier йде в сховище даних, і отримує їх звідти
querier повертає відповідь через теж HTTP-з’єднання
Write Path
При отриманні нових даних:
distributor отримує HTTP/1 запрос на додавання даних в конкретний стрім
distributor передає кожний стрім в ingester
ingester створює новий chunk (“блок даних”, див. Loki Storage) або доповнює існуючий
distributor відповідає ОК на HTTP/1 запит
Режими запуску
Запускати Loki можна в трьох режимах, кожен з яких визначає, як будуть запущені компоненти – у вигляді одного або декількох подів Kubernetes.
Monolithic mode
Дефолтний тип при використанні локальної filesystem для зберігання даних.
Підходить для швидкого запуску та невеликих об’ємів даних, до 100GB на день
Балансування запитів виконується по round robin.
Паралелізація запитів обмежена кількістю інстансів та налаштуванням кожної інстансу.
Основне обмеження – не можна використовувати object store, такі як AWS S3.
Simple scalable deployment mode
Дефолтний тип під час використання object store.
Якщо у вас логів більше кількох сотень гігабайт, але менше кількох терабайт на день, або ви хочете ізолювати читання та запис, то можна задеплоїти Loki в режимі simple scalable deployment:
У такому режимі Loki запускається з двома таргетами – read & write.
Потребує наявності лоад-балансера, який буде роутити запити до інстансів з компонентами Loki.
Microservices mode
І для найскладніших випадків, коли у вас логи йдуть терабайтами на день, має сенс деплоїти кожен сервіс окремо:
ingester
distributor
query-frontend
query-scheduler
querier
index-gateway
ruler
compactor
Дозволяє моніторити та скейлити кожен компонент незалежно.
Loki для зберігання логів використовує два типи даних – chunks та індекси. Не придумав корректного перекладу для chunk, тому нехай буде “блок даних”.
Loki отримує дані від кількох стримів, де кожен стрім – це tenant_id та набір тегів. При отриманні нових записів від стриму вони упаковуються в блоки і відправляються в довгострокове сховище, в ролі якого можуть бути AWS S3, локальна файлова система, або бази даних типу AWS DynamoDB чи Apache Cassandra.
В індексах зберігається інформація про набір тегів кожного стриму і є посилання на пов’язані з цим стримом блоки даних.
Раніше Loki використовувала два окремі сховища – одне під індекси (наприклад, таблиці DynamoDB), і друге – безпосередньо під самі дані (AWS S3).
Десь з версії 2.0 у Loki з’явилася можливість зберігати індекси у вигляді BotlDB файлів та використовувати Single Store – єдине сховище і для блоків даних, і для індексів. Див. Single Store Loki (boltdb-shipper index type).
Ми будемо використовувати boltdb-shipper – він буде формувати індекси локально, а потім пушити їх у shared object store. Там же будуть зберігатися і самі chunks.
Важливий момент, який необхідно враховувати під час роботи з тегами в Loki це те, як формуються індекси та блоки даних: кожен окремий набір тегів формує окремий стрім, а для кожного окремого стріму формуються свої індекси та блоки даних.
Тобто, якщо ви динамічно створюєте теги/лейбли, наприклад client_ip – то у вас буде формуватися окремий набір файлів на кожен клієнтський IP, що призведе до того, що на кожен такий файл будуть виконуватися окремі запити GET/POST/DELETE, що може привести по-перше до зростання вартості сховища (як у випадку з AWS S3, де оплачується кожен виклик), так і до проблем з швидкістю обробки запитів.
Окрім документації у Loki ще й із чартами трохи складнощів, оскільки переносили між репозиторіями, об’єднували, і тепер деякі стали deprecated (хоча посилання на них у документації зустрічаються).
Нижче – не про установку, а просто деякі особливості чартів Loki, з якими довелося повозитися.
Ще один момент, який трохи поламав мозок: окей, ми бачили, що Loki можна запустити з різними Deployment modes – але як це визначити у чарті? Якогось values типу -targetтам немає.
Нижче – трохи копання в чарті, можна пропустити, якщо вам підходить дефолтна установка.
Отже, якщо встановити з дефолтними values, то отримуємо наступні компоненти:
$ kk get pod
NAME READY STATUS RESTARTS AGE
loki-canary-7vrj2 0/1 ContainerCreating 0 12s
loki-gateway-5868b68c68-lwtfj 0/1 ContainerCreating 0 12s
loki-grafana-agent-operator-684b478b77-zmw5t 1/1 Running 0 12s
loki-logs-kwxcx 0/2 ContainerCreating 0 3s
loki-read-0 0/1 ContainerCreating 0 12s
loki-read-1 0/1 Pending 0 12s
loki-read-2 0/1 Pending 0 12s
loki-write-0 0/1 ContainerCreating 0 12s
loki-write-1 0/1 Pending 0 12s
loki-write-2 0/1 Pending 0 12s
[/simterm]
Тобто, по дефолту воно встановлюється в simple-scalable, при цьому в документації самих чартів про це нічого не сказано, як ні слова про те, як задати деплоймент-режим взагалі.
А якщо я хочу Single Binary?
Зносимо:
[simterm]
$ helm uninstall loki
release "loki" uninstalled
[/simterm]
Спробуємо повірити документації, і створюємо свої values:
Тобто, тупо перевизначивши сторейдж – ми змінюємо режим деплойменту?!?
Ах#*$ть – дайте два!
Як воно працює?
Відкриваємо файл templates/_helpers.tpl, в якому є два шаблони – loki.deployment.isScalable і loki.deployment.isSingleBinary, в яких одна і та ж умова, тільки з різними значеннями:
Якщо true – то це isScalable, а якщо false – то isSingleBinary.
Окей, а що за isUsingObjectStorage?
Знаходимо його в тому же хелпері:
...
{{/* Determine if deployment is using object storage */}}
{{- define "loki.isUsingObjectStorage" -}}
{{- or (eq .Values.loki.storage.type "gcs") (eq .Values.loki.storage.type "s3") (eq .Values.loki.storage.type "azure") -}}
{{- end -}}
...
Тобто, якщо ми використовуємо .Values.loki.storage.type із значенням gcs, s3 або azure – то loki.isUsingObjectStorage прийме значення true, і Loki буде встановлено в режимі Simple Scale.
Зовсім не очевидно і не описано в документації до чарту.
Запуск Grafana Loki
А тепер, нарешті, перейдемо до запуску і налаштування Loki.
Для зберігання даних будемо використовувати AWS S3, для роботи з індексами – bottledb-shipper, для налаштування терміну зберігання логів – compactor.
Аутентифікацію Loki реалізуємо через підключення ServiceAccount із AWS IAM Role, але покажу приклад і зі звичайними ACCESS/SECRET keys.
Створення AWS S3 корзини
Почнемо зі створення корзини. Можна через AWS CLI та create-bucket, або через Terraform:
Для корзини буде потрібна політика, яка дозволяє до неї доступ, і роль, яку потім підключимо до Kubernetes Pod.
Повертаючись до проблем документації Loki – на сторінці Grafana Loki Storage є приклад політики для AWS S3, яка… не проходить валідацію в AWS IAM :faceplam:
Взагалі часто виникали асоціації з Miscrosoft Azure – там документації теж вірити не можна від слова зовсім, і все треба перевіряти і збирати по шматочках.
Переходимо до конфігу Loki – тут теж вистачило болю та страждань із документацією та чартом.
Запуск Grafana Loki в Kubernetes
Ну і тепер, коли мені стало ясно і з чартами, і з тим, як же через Helm-чарт Loki задати Deployment Mode, і взагалі який чарт використовувати – спробуємо її запустити.
Готуємо мінімальний конфіг, в якому для початку відключимо весь її внутрішній моніторинг щоб зменшити кількість подів – буде простіше розбиратися з тим, як воно працює, і для початку використовуємо сховище filesystem, щоб зберігати дані та індекси локально в подах:
$ kk -n test-loki-0 get pod
NAME READY STATUS RESTARTS AGE
loki-0 1/1 Running 0 118s
[/simterm]
Окей – є один под, нічого зайвого.
Чарт створює StatefulSet, в якому описується створення цього поду і через який підключаються різні volumes:
[simterm]
$ kk -n test-loki-0 get sts
NAME READY AGE
loki 1/1 3m
[/simterm]
І ConfigMap, в якій зберігається конфіг, доповнений нашим loki-minimal-values.yaml:
[simterm]
$ kk -n test-loki-0 get cm loki -o yaml
apiVersion: v1
data:
config.yaml: |
auth_enabled: false
common:
path_prefix: /var/loki
replication_factor: 1
storage:
filesystem:
chunks_directory: /var/loki/chunks
rules_directory: /var/loki/rules
...
[/simterm]
Grafana Loki S3 config
Дуже багато віддав би, щоб десь знайти повний конфіг для Grafana Loki з AWS S3 як у прикладі нижче, та ще й з авторизацією через ServiceAccount і AWS IAM – витратив багато часу, щоб змусити все це працювати.
Власне, сам конфіг, потім трохи про опції та підводні камені, з якими зіткнувся:
auth_enabled: false – відключаємо авторизацію в самій Loki (в результаті отримаємо tenant_idfake в корзині – це ок, нормально, хоча могли б придумати щось красивіше ніж “фейк”)
storage.bucketNames.chunks – потрібно вказати ім’я корзини для блоків, інакше намагатиметься використовувати локальне сховище; у документації не вказано;
schema_config.configs.store:
boltdb-shipper – вказуємо на використання boltdb-shipper для роботи з індексами, оскільки він вміє в Single Store, тобто і блоки даних і їх індекси будуть в одній корзині
object_store: s3 – вказуємо тип сховища, яке налаштовується в storage_config.aws.s3 (але тут вказуємо саме як schema_config.configs.store.s3, а не schema_config.configs.store.aws.s3)
storage_config – найбільший біль:
aws.s3: вказуємо саме у вигляді s3://<S3_BUCKET_REGION>/<S3_BUCKET_NAME>, інакше при підключенні ServiceAccount Loki починає намагатися ходити для авторизації на https://sts.dummy.amazonaws.com – я так і не знайшов чому, але при використанні ServiceAccount потрібен саме такий формат
boltdb_shipper – вказуємо йому локальний шлях, де він створює індекси – active_index_directory, та shared_store – куди потім їх відправляти; візьме конфіг із тієї ж storage_config.aws.s3
rulerConfig.storage.type: local – для ruler поки вкажемо локальний каталог, з алертами розберемося в інший раз; якщо не вказати – буде постійно писати в лог помилку, що не може отримати доступ до своєї корзини, яка десь прописана у дефолтах, не пам’ятаю вже де саме
write.replicas: 2 – мінімальна кількість подів write, щоб Promatil міг писати дані
$ aws --profile development s3 ls test-loki-0
2022-12-25 11:53:13 251 loki_cluster_seed.json
[/simterm]
І ще за кілька хвилин повинні повитися каталоги fake та index:
[simterm]
$ aws --profile development s3 ls test-loki-0
PRE fake/
PRE index/
2022-12-25 11:53:13 251 loki_cluster_seed.json
[/simterm]
У fake – chunks, в index – індекси.
Окей – начебто завелося.
Дивно, насправді, що цього разу все з першої спроби завелося… Поки сетапив Loki на проекті, де працюю – реально вже подумував взяти ELK та не гаяти час.
Тепер після додавання promatil, який писатиме дані – Loki Write через ingester писатиме блоки даних, а bottledb-shipper почне створювати індекси, і пушити їх у корзину.
Запуск Promtail
Знаходимо Service для Loki Gateway:
[simterm]
$ kk -n test-loki-0 get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
loki-gateway ClusterIP 10.109.225.168 <none> 80/TCP 22m
...
Цього разу трохи детальніше про його сетап і можливості.
Отже, blackbox-exporter – це експортер, який вміє моніторити різноманітні ендпоінти – це можуть бути або якісь URL-и в інтернеті, ваші LoadBalancer-и в Амазоні, або Services в Кубернетесі, такі як MySQL або PostgreSQL бази данних.
Вміє виводити статистику по швидкості відповіді HTTP, коди відповідей, інформацію по SSL-сертіфікатах тощо.
$ kk -n monitoring get pod
NAME READY STATUS RESTARTS AGE
prometheus-blackbox-prometheus-blackbox-exporter-6865d9b44h546j 1/1 Running 0 27s
...
[/simterm]
Blackbox тримає свій конфіг в ConfigMap-і, яка підключається до поду і передає дефолтні параметри. Див. тут>>>.
[simterm]
$ kk -n monitoring get cm prometheus-blackbox-prometheus-blackbox-exporter -o yaml
apiVersion: v1
data:
blackbox.yaml: |
modules:
http_2xx:
http:
follow_redirects: true
preferred_ip_protocol: ip4
valid_http_versions:
- HTTP/1.1
- HTTP/2.0
prober: http
timeout: 5s
[/simterm]
Власне, тут ми і бачимо модулі, точніше поки що один, який використвує prober http, який виконує HTTP-запроси до targets, які ще треба додати.
Blackbox та ServiceMonitor
Для того, щоб додати ендпоінти, котрі ми хочемо моніторити, можна використовувати ServiceMonitor, див. конфіг тут>>>.
Чомусь ніде в нагуглених гайдах цей момент толком не описаний, хоча він дуже зручний: в конфіг Блекбоксу додаємо список таргетів, а Блекбокс створює ServiceMonitor для кожного з них, і Prometheus починає їх моніторити.
Створюємо файл blackbox-exporter-values.yaml, в якому додаємо поки що один ендпоінт – просто перевірити, чи воно взагалі працює:
Якщо не вказано інше, то Блекбокс використвує дефолтні значення із values.yaml чарту, в данному випадку це буде модуль http_2xx, який виконує GET запрос, та перевіряє код відповіді: якщо отримано 200 – то перевірка пройдена, якщо інший – то фейл.
$ kk apply -f testpod-with-svc.yaml
pod/nginx created
service/nginx-service created
[/simterm]
Перевіряємо:
[simterm]
$ kk -n test-ns get all
NAME READY STATUS RESTARTS AGE
pod/nginx 1/1 Running 0 23s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-service ClusterIP 10.106.58.247 <none> 80/TCP 23s
$ kk -n monitoring get servicemonitor
NAME AGE
prometheus-blackbox-prometheus-blackbox-exporter-google.com 12m
prometheus-blackbox-prometheus-blackbox-exporter-nginx-test 5s
[/simterm]
І за хвилину – можемо перевіряти probe_success:
Взагалі, не обов’язково вказувати повний URL у вигляді nginx-service.test-ns.svc.cluster.local – достатньо буде servicename.namespace, тобто nginx-service.test-ns, але повний URL як на мене виглядає більш наочно в лейблах та алертах.
Модулі Blackbox Exporter
Все виглядає чудово, поки ми опитуємо звичайний HTTP-ендпоінт, який завжди віддає код 200.
Що як треба перевірити інші коди?
Створимо власний модуль, використвуючи probes Блекбоксу:
Тут в modules задаємо ім’я нового модуля – http_4xx, який пробер він має викорисовувати – http, і параметри для цього пробера – яким саме запитом перевіряємо, і які коди відповіді будемо вважати правильними.
Далі, в Таргетах для nginx-test-404 явно вказуємо використання модулю http_4xx.
Тестування модулів
Окремо подивимось як саме можемо перевірити – чи буде модуль працювати так, як ми розраховуємо.
Все просто – запускаємо тестовий под, і curl-ом з опцією -I дивимось на відповідь ендпоінта.
Якшо перевіряємо TCP-коннект – то telnet.
Отже, створюємо под з Убунтою, підключаємось до нього – запускаємо всередені bash:
[simterm]
$ kk -n monitoring run pod --rm -i --tty --image ubuntu -- bash
probe_success{target="nginx-test-404"} == 1 – все робить.
TCP Connect і моніторинг баз данних
Ще один модуль, котрий дуже часто використовуємо – TCP, який просто намагається відкрити TCP-сессію на вказанний URL та порт. Підходить для перевірок баз данних та будь-яких інших не-HTTP-ресурсів.
$ kk get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h
mysql ClusterIP 10.99.71.124 <none> 3306/TCP 40s
mysql-headless ClusterIP None <none> 3306/TCP 40s
Запускаємо init, что б підтягнути необхідні модулі самого Тераформу:
[simterm]
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/local...
- Installing hashicorp/local v2.2.3...
- Installed hashicorp/local v2.2.3 (signed by HashiCorp)
...
Terraform has been successfully initialized!
[/simterm]
Потім plan – перевіряємо чи буде воно робити взагалі, та що саме:
[simterm]
$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.file will be created
+ resource "local_file" "file" {
+ content = "file content"
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "file.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.file will be created
+ resource "local_file" "file" {
+ content = "file content"
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "file.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
local_file.file: Creating...
local_file.file: Creation complete after 0s [id=87758871f598e1a3b4679953589ae2f57a0bb43c]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
[/simterm]
І перевіряємо зміст каталогу, в якому запускали:
[simterm]
$ ls -l
total 12
-rwxr-xr-x 1 setevoy setevoy 12 Nov 28 13:14 file.txt
-rw-r--r-- 1 setevoy setevoy 90 Nov 28 13:09 main.tf
-rw-r--r-- 1 setevoy setevoy 854 Nov 28 13:14 terraform.tfstate
Оновлюємо корневий модуль, тобто modules_example/main.tf – видаляємо resource "local_file", і замість нього описуємо два модулі, в яких вказуємо шляхи до каталогів обох модулів:
Запускаеємо init знову, щоб Тераформ створив свою структуру модулей:
[simterm]
$ terraform init
Initializing modules...
- file_1 in modules/file_1
- file_2 in modules/file_2
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/local from the dependency lock file
- Using previously-installed hashicorp/local v2.2.3
Terraform has been successfully initialized!
$ terraform apply -auto-approve
module.file_2.local_file.file_2: Refreshing state... [id=5771aa8b1046de4f4342d492faee20e3c289365d]
module.file_1.local_file.file_1: Refreshing state... [id=8a3552bfa2e46f311e7b718f8eed71b69bb8115f]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.concat_file will be created
+ resource "local_file" "concat_file" {
+ content = <<-EOT
file_1 content from user1
file_2 content from user2
EOT
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "concat_file.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
local_file.concat_file: Creating...
local_file.concat_file: Creation complete after 0s [id=a4e1240fb9cdc96fffc1b0984392f6218422d194]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
[/simterm]
Перевіряємо:
[simterm]
$ cat concat_file.txt
file_1 content from user1
file_2 content from user2
[/simterm]
Передача значень змінних між модулями
І, нарешті, те, з чого почали – як передати значення з одного модулю в інший?
В файлі main.tf першого модулю додаємо змінну з іменем module_1_value, якій задаємо дефолтне значення “module 1 value“:
Теперь в файлі file_1.txt маємо отримати значення із module "file_2", і навпаки.
Запускаємо:
[simterm]
$ terraform apply -auto-approve
module.file_1.local_file.file_1: Refreshing state... [id=d74b0e0b3296ab02e7b4791942d983b175d071b4]
local_file.concat_file: Refreshing state... [id=e0e88293b53693a484db146b15bd2ab3d8f4d250]
module.file_2.local_file.file_2: Refreshing state... [id=12faf027dc96d886c6022b54c878324a21d3d112]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# local_file.concat_file must be replaced
-/+ resource "local_file" "concat_file" {
~ content = <<-EOT # forces replacement
- file_1 content from user1 with value from file_2: variable 2 value
- file_2 content from user2 with value from file_1: variable 1 value
+ file_1 content from user1 with value from file_2: module 2 value
+ file_2 content from user2 with value from file_1: module 1 value
EOT
~ id = "e0e88293b53693a484db146b15bd2ab3d8f4d250" -> (known after apply)
# (3 unchanged attributes hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.
local_file.concat_file: Destroying... [id=e0e88293b53693a484db146b15bd2ab3d8f4d250]
local_file.concat_file: Destruction complete after 0s
local_file.concat_file: Creating...
local_file.concat_file: Creation complete after 0s [id=648854841581b273d26646fff776b33fdf060a19]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
[/simterm]
Перевіряємо результат:
[simterm]
$ cat concat_file.txt
file_1 content from user1 with value from file_2: module 2 value
file_2 content from user2 with value from file_1: module 1 value
Маємо Grafana Loki для логів, до подів якої треба підключити AWS IAM Role з AWS IAM Policy, котра дає доступ до AWS S3 бакету, в якому будуть зберігатися чанки та індекси (про сетап самої Loki з AWS S3 трохи пізніше окремим постом).
IAM ролі для Kubernetes подів працють тим самим чином, як ми це робимо, коли підключаємо IAM-ролі до ЕС2-інстансів – процес всередині пода виконує запит до AWS API, а AWS SDK чи AWS CLI, за допомогою якого робиться запит, виконує запит AssumeRole, якому передається сама IAM Role (див. AWS: ротация ключей IAM пользователей, EC2 IAM Roles и Jenkins).
Щоб перевірити, як воно взагалі буде працювати в Кубері – створимо тестову IAM Role, ServiceAccount з аннотацією цієї ролі, та запустимо под з цим ServiceAccount.
Забігаючи наперед – в самій Локі це все одно працює через якусь альтернативну реальність, тобто навіть коли їй підключаєш вже протестований ServiceAccount – вона валиться з помилками. Треба буде копати.
EKS кластер розгорнутий за допомогою Terraform модуля aws_eks_cluster, і OpenID Connect (OIDC) provider вже має бути налаштований.
Заходимо на сторінку кластера, знаходимо OpenID Connect provider URL:
Або з консолі:
[simterm]
$ aws --profile development --region us-west-2 eks describe-cluster --name dev_data_services --query "cluster.identity.oidc.issuer" --output text
https://oidc.eks.us-west-2.amazonaws.com/id/537***A10
[/simterm]
Копіюємо URL без https://, та перевіряємо в IAM > Identity providers:
Окей, тут все є.
Створення Kubernetes ServiceAccount з AWS IAM role
Для початку, створимо IAM policy, яка дає права на AWS S3 bucket, потім IAM Role з TrustedPolicy, яка дозволяє виконувати AssumeRole, використовуючи OIDC identity provider (IDP) кластеру.
$ aws --profile development iam create-policy --policy-name test-iam-sa-pod-policy --policy-document file://test-iam-sa-pod-policy.json
[/simterm]
Переходимо до ролі.
AWS IAM role та TrustedPolicy
Знаходимо ARN нашого Identity Provier:
URL OIDC identity provider вже знаходили раніше:
[simterm]
$ aws --profile development --region us-west-2 eks describe-cluster --name dev_data_services --query "cluster.identity.oidc.issuer" --output text
https://oidc.eks.us-west-2.amazonaws.com/id/537***A10
[/simterm]
Створення IAM Role з AWS CLI
Створюємо файл з TustedPolicy, в Principal якої вказуємо ARN IDP, а в Condition – URL OIDC кластеру, якому дозволяємо виконувати запит до sts.amazonaws.com:
На цей раз стало мені цікаво – а чи можна якось поекономити заряд батерії ноутбука? Не сказати, що швидко разряжається – на 5-6 годин роботи вистачає, але зайвим не буде.
Знайшов декілька утіліт, про них сьогодні й запишу.
Upower
Перша утілітка – upower:
[simterm]
$ sudo pacman -S upower
[/simterm]
Спочатку можна визвати з опцією --monitor-detail – буде в ріалтаймі виводити інформацію про стан батареї:
[simterm]
$ $ sudo upower --monitor-detail
Monitoring activity from the power daemon. Press Ctrl+C to cancel.
[20:42:20.071] device changed: /org/freedesktop/UPower/devices/battery_BAT0
native-path: BAT0
vendor: SMP
model: LNV-5B10W13895
serial: 2056
power supply: yes
updated: Fri Nov 18 20:42:20 2022 (0 seconds ago)
has history: yes
has statistics: yes
battery
present: yes
rechargeable: yes
state: charging
warning-level: none
energy: 36.7 Wh
energy-empty: 0 Wh
energy-full: 0 Wh
energy-full-design: 0 Wh
energy-rate: 15.538 W
voltage: 12.391 V
charge-cycles: N/A
time to full: 22.7 minutes
percentage: 86%
technology: lithium-polymer
icon-name: 'battery-full-charging-symbolic'
History (charge):
1668796940 86.000 charging
1668796850 85.000 charging
History (rate):
1668796940 15.538 charging
1668796910 15.784 charging
1668796880 16.006 charging
1668796850 16.253 charging
[/simterm]
Відразу бачимо модель батареї ноута – LNV-5B10W13895, та її стан – кількість ватт-годин (36.7 Wh), та скількі споживає зараз (15.538 W), і на якій потужності – 12.391 V.
Можна порахувати в ампер-годинах:
[simterm]
>>> 37.31/12.3
3.03
[/simterm]
Тобто зараз 3033 mAh заряду в батареї.
Заодно побачили сам девайс батареї – /org/freedesktop/UPower/devices/battery_BAT0.
Отримати інфо про неї без моніторингу, а один раз – опція -i з пристроєм – sudo upower -i /org/freedesktop/UPower/devices/battery_BAT0.
ACPI
Друга, аналогічна утіліта – acpi, але ще вміє відображати температуру (і не тільки – див. About ACPI):
[simterm]
$ sudo pacman -S acpi
[/simterm]
Виводимо інформацію щодо батареї:
[simterm]
$ acpi -i
Battery 0: Charging, 91%, 00:21:40 until charged
Battery 0: design capacity 3649 mAh, last full capacity 3431 mAh = 94%
[/simterm]
“design capacity 3649 mAh” – все майже як рахували вище, тільки там брали поточний заряд, який зараз десь 94% від design capacity.
Batstat
Третя корисна річ – batstat. Простенька, але може стати в нагоді.
Качаємо репозіторій, збираємо:
[simterm]
$ git clone https://github.com/Juve45/batstat.git
$ cd batstat
$ make
$ chmod +x batstat
$ sudo mv batstat /usr/local/bin/
[/simterm]
І запускаємо:
Powertop
Powertop вміє не просто виводити інформацю про батарею, але й по-перше – виводити інформацю про процеси, які споживать енергію, по-друге – може виконати налаштування для економії заряду.
[simterm]
$ sudo pacman -S powertop
[/simterm]
Запускаємо під рутом:
[simterm]
$ sudo powertop
[/simterm]
В табі Tunables виводиться інформація по налаштуванням, які можуть оптимізувати споживання енергії.
Вибираємо потрібний пункт, Enter – і опція переключається:
Вміє генерувати html:
[simterm]
$ sudo powertop --html=powerreport.html
[/simterm]
І результат:
Можна додати systemd-сервіс, який буде виконувати автоматичний тюнінг при старті системи.
Bluetooth мені постійно не потрібен, рідко його включаю, теж саме з Притунлом.
tick_sched_timer та Polybar
Ще виявив цікаву річ – tick_sched_timer, шедулєр ядра, з’їдав аж 1.7W:
Погуглив, знайшов цей тред, прибив процес Polybar, який зверху виводить всяку інформацю по системі, використовуючи власні модулі – і tick_sched_timer почав споживати лише 150-300 mW.
Що робити, коли немає світла, вежі мобильного зв’язку відключаються, а подивитися відосиків з нашими котиками хочеться? Правильно – купити собі 3/4G модем з антеною!
До того ж самій антені живлення не треба, а модем можна вставити в звичайний павербанк, якого вистачає надовго, бо модему багато не треба.
Купував комплект 4G Zte Mf79U + Квадрат Mimo – цікавий магазин (не реклама), і продають відразу комплектами, що дуже зручно для таких як я, які не шарять в тому, як його вибирати, особливо коли діло стосується самої антени.
Окрема подяка @Artem за ідею та поміч при налаштуванні)
Сам комплект виглядає так:
Підключення модему
Вставляємо карту micro-SIM в порт карти, я взяв карточку Водафона – він тут наче краще всіх робить, коли вирубає вишки.
Другий – під карту памя’ті:
Пароль та SSID є на девайсі:
Вставляємо в комп/ноут:
Сигнали описані в інструкції, кратко:
зверху – WiFi, знизу – мобільний
мобільний блимає зеленим – підключено до 3G, трафік йде
мобільний блимає блакитним – підключено до 4G, трафік йде
Заходимо на http://192.168.0.1:
Переходимо до налаштувань WiFi:
За бажанням – міняємо SSID та пароль на свої:
Сигнал модему навіть кращий за домашній роутер, який стоїть поруч – setevoy-linksys:
Переходимо до антени.
Підключення антени
Готуємо кабелі.
В комплекті йде один довгий, розрізаємо на дві однакові частини.
На кінцівках надрізаємо пластик, обережно, бо під ним обмотка. Знімаємо верхній шар ізоляції:
Мідну обмотку під ним разом з алюмінюєвий екраном загортаємо донизу, надрізаємо внутрішню ізоляцю, та оголяємо внутрішній мідний стержень:
Накручуємо конектор (всередені різьба) – готово:
З витою парою гемору більше 🙂
Повторюмо для всіх чотирьох, и врешті-решт підключаємо всю систему:
Розміщення антени
Встановлюємо на телефон утілітку Network Cell Info Lite & Wifi – показує найближчі вежі, до яких конектиться телефон.
В мене вишка Водафону як раз навпроти вікон, тож і антену розміщуємо “обличчям” до вежі:
Сила сигналу та тест швидкості
Краще антену взагалі виносити за вікно, бо, сюрпрайз – навіть склопакет впливає на силу сигналу.
Отже, за допомогою Network Cell Info знаходимо напрямок вежі, виставляємо антену в її напрямку, і перевіряємо сигнал, прямо на стартовій сторінці адміністрування самого модему:
-91 decibel-milliwatts (децибел-мілівати) – не вражає, але враховуючи, що я живу за містом, і вишка від мене приблизно в трьох кілометрах – то не так вже й погано.
Швидкість до відключення живлення “на районі”:
І під час вимкнення свіла:
Швидкість не найкраща, але для роботи/месенджерів та навіть Ютубу вистачає.
При цьому мобільний інтернет на телефоні не працює взагалі – Speedtest навіть не стартує, видає помилку.
В самому Києві результати набагато краще – кажуть, що 2Мбс на загрузку видає стабильно.
Або навіть так – Оболонь, з таким же комплектом обладнання:
Хочеться прибрати россійські сайти із результатів пошуку в Гуголі, тож нагуглив декілька варіантів.
Перші два – “ручні”, через додавання параметру ?lr=-lang_ru або ?cr=-countryRU в URL Гугла, тобто виглядатиме він як https://google.com.ua/?lr=-lang_ru:
Але ж це мануальщина…
Нажаль, в Хромі автоматизувати не вийшло – вирізає цю частину при виконанні запиту:
Сьогодні вперше сам платив податки – така приємна і цікава процедура) Ну і справді дуже проста, як розібратися що і кому ми платимо.
Ще треба буде розібратися з поданням декларацій, але на цей раз за мене зробить добра людина, а я вже буду подавати сам (майже) в січні, до того ж там буде подача годової декларації.
Взагалі-то є купа сервісів, які ще більше спрощують життя, наприклад taxer.ua (не реклама), до якого я ще дойду, або та ж Дія. Але спочатку – “Learn Accounting the Hard Way“.
Тобто платимо два податки – Єдиний податок та Єдиний соціальний внесок.
Єдиний податок
Тут я сплачую 5%, бо я не є платником ПДВ.
Сплачувати ЄП можно як щомісяця, так і раз на квартал, головне не пропустити останній день сплати – для ЄП це 50 днів після завершення кварталу.
Для зарплат в валюті, сума податку рахується на день зарахування доллару на долларовий рахунок, але в гривні по курсу НБУ – на сьогодні він все ще фіксований та становить 36.65 грн за один американський доллар. Хоча в Моно все рахуеться автоматом, тож тут все просто.
Платимо ми ГУДКСУ – Головному Управлінню Державної казначейської служби України. Але тут ще скоріш за все ще важливо місце реєстрації… Треба уточнити, доречі, якось пропустив цей момент.
На данний час її рахунки такі – тут чисто для прикладу, бо вони змінюються, начебто раз на рік у січні:
Код ЄДРПОУ отримувача 37955989
Назва отримувача ГОЛОВНЕ УДКСУ У КИЇВСЬКІЙ ОБЛАСТІ
Бюджетний рахунок UA878999980314060699000010782
По коду ЄДРПОУ 37955989 можемо знайти отримувача, наприклад на Опендатаботі – ГОЛОВНЕ УПРАВЛІННЯ ДЕРЖАВНОЇ КАЗНАЧЕЙСЬКОЇ СЛУЖБИ УКРАЇНИ У КИЇВСЬКІЙ ОБЛАСТІ.
Про ЄП наче все – глянемо на другий податок.
Єдиний соціальний внесок
ЄСВ всі ФОП-и платять однаково – це 22% від місячної мінімальної зарплати – вона взагалі на рівні держави впливає на купу показників, тому про неї так часто говорять.
Більш того, треба і самому відстежувати зміни тут, бо, наприклад, у вересні (сентябрь) мінімалка була 6500, отже податок маємо платити 1430 грн, а вже в жовтні (октябрь) стала 6700, тому і подадок вже буде 1474 грн/міс.
По датам оплати – це до 20-ти днів після завершеня кварталу, і реквізити інші, бо тепер платимо Головному Управлінню Державної Податкової Служби:
Код ЄДРПОУ отримувача 44096797
Назва отримувача ГОЛОВНЕ УПРАВЛІННЯ ДПС У КИЇВСЬКІЙ ОБЛАСТІ
Бюджетний рахунок UA938999980000355699204021036
Розібралися? Поїхали до Монобанку.
Monobank – кабінет бухгалтера
Моно зробив прям чудо, і через просту таку собі веб-сторніку можна виконувати всі операціі по податкам. Плюс, можна зробити два додаткових доступи для бухгалтерії, якщо у вас є кому це робити за вас та немає натхнення вести все самому.
Щоб залогінитись – на телефоні відкриваємо Сканер QA-коду, та скануємо код із браузеру:
Підтвержуємо дозвіл на вхід:
І сама юайка – все дуже швидко, зручно і начебто зрозуміло:
Тепер погнали робити платежі на користь держави.
Сплата ЄСВ
Тут у нас 22% від мінімальної зарплати, тож якщо платити за вересень, то це буде 1430 гривень, а за вересень та жовтень – 1430+1474 == 2904 грн.
Відкриваємо Створити платіж – Платіж до бюджету:
Шукаємо по IBAN UA938999980000355699204021036 – система сама підгрузить ЄДРПОУ та назву – ГУ ДПС.
Далі, шукаємо в полі Код виду оплати 101 – це “Сплата податків і зборів/єдиного внеску“:
В Призначенні платежу автоматично сформується строка у вигляді *;<КОД ВИДУ ОПЛАТИ>;<ІНН ПЛАТНИКА>;<ТЕХНОЛОГІЧНИЙ КОД>, та коментар.
<ТЕХНОЛОГІЧНИЙ КОД> підтянеться автоматично з Назви установи – зверніть увагу на цифри 7104000 наприкінці.
Він не завжди використувється при створенні платежиів, наприклад в Укрсіб банку його не вказували.
Якщо дуже цікаво (як стало мені), то можно навіть нагулити що то за такий “технологічний код” ось тут:
37193204002651
71040000
Єдиний соціальний внесок, що сплачується фізичними особами – підприємствами, у т.ч. які обрали спрощену систему оподаткування та осіб, які проводять незалежну професійну діяльність (34,7 %).
Отже, вказуємо суму, підписуємо і відправляємо. Якщо платите за кілька місяців – то можно об’єднати в один платіж, голове щоб податкова отримала вірну (можна більше) кількість податків:
Зверніть увагу, что Технологічний код теж змінився – чудо автоматизації! 😀
Сплата Єдиного Податку
Тут у нас 5% від суми отриманного доходу в гривні.
Вікриваємо Валютний рахунок, і під сумою надходження в долларах бачимо суму в гривні:
Формула проста – (сума надходження в гривні * 5) / 100, тобто якщо отримали 100.000 гривень, то платимо:
[simterm]
$ echo "(100000*5)/100" | bc
5000
[/simterm]
5000 гривень податку.
Шукаємо так само – спочатку по IBAN UA878999980314060699000010782, потім по коду 101:
Перевірка бюджетних надходжень
Переказ має поступити і бути обробленим як звичайно – протягом трьох банківских днів, тож трохи почекаємо.
$ helm -n pritunl-local install pritunl dysnix/pritunl
...
Pritunl default access credentials:
export POD_ID=$(kubectl get pod --namespace pritunl-local -l app=pritunl,release=pritunl -o jsonpath='{.items[0].metadata.name}')
kubectl exec -t -i --namespace pritunl-local $POD_ID pritunl default-password
...
export VPN_IP=$(kubectl get svc --namespace pritunl-local pritunl --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
echo "VPN access IP address: ${VPN_IP}"
[/simterm]
Перевіряємо поди:
[simterm]
$ kubectl -n pritunl-local get pod
NAME READY STATUS RESTARTS AGE
pritunl-54dd47dc4d-672xw 1/1 Running 0 31s
pritunl-mongodb-557b7cd849-d8zmj 1/1 Running 0 31s
Тут у Public Address автоматом буде заданий публічний адресу хоста, на якому запущений сам Прітунл, і потім він буде підставлятися в клієнтські конфіги як адреса хоста VPN.
Так як Pritunl у нас працює в Kubernetes, який працює у VirtualBox, який працює на Linux на звичайному домашньому PC – то нам цей варіант не підходить, але до нього повернемось пізніше. Поки що можна залишити, як є.
DNS Server : до якого ДНС будемо відправляти клієнтів
Port, Protocol : порт і протокол для OpenVPN, який буде запущений “всередині” Притунла і прийматиме підключення від наших користувачів
Virtual Network : мережа, з пулу адрес якої виділятимемо приватні IP для клієнтів
Virtual Network я б виділив 172.16.0.0 – тоді у нас домашня мережа, мережа Кубера та клієнтські IP будуть відрізнятися – зручніше буде дебажит, див. IPv4 Private Address Space and Filtering .
При цьому важливо, щоб порт Сервера тут збігався з портом і протоколом на LoadBalancer – 1194 TCP .
Тобто. запит з робочої машини піде за маршрутом:
192.168.3.0/24 – домашня мережа
потрапить до мережі VirtualBox 192.168.59.1/24 (див. Proxy )
піде на LoadBalancer у мережі Кубера 10.96.0.0/12
а LoadBalancer відроутить запит у Kubernetes Pod, в якому у нас OpenVPN слухає TCP порт 1194
Перевіряємо сам LoadBalancer:
[simterm]
$ kubectl -n pritunl-local get svc pritunl
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
pritunl LoadBalancer 10.104.33.93 <pending> 1194:32350/TCP 22m
[/simterm]
Port 1194 – TCP. Зі статусом Pending розберемося трохи пізніше.
Вказуємо Virtual Network, порт та протокол для Server:
Далі, підключаємо Організацію з усіма її користувачами:
Стартуємо сервер:
Перевіряємо процес та порт у Kubernetes Pod – бачимо наш OpenVNP Сервер на порту 1194:
$ minikube tunnel
[sudo] password for setevoy:
Status:
machine: minikube
pid: 1467286
route: 10.96.0.0/12 -> 192.168.59.108
minikube: Running
services: [pritunl]
errors:
minikube: no errors
router: no errors
loadbalancer emulator: no errors
...
[/simterm]
Перевіряємо Loadbalancer:
[simterm]
$ kubectl -n pritunl-local get svc pritunl
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
pritunl LoadBalancer 10.104.33.93 10.104.33.93 1194:32350/TCP 139m
[/simterm]
З’явився EXTERNAL-IP– перевіряємо підключення:
[simterm]
$ telnet 10.104.33.93 1194
Trying 10.104.33.93...
Connected to 10.104.33.93.
Escape character is '^]'.
[/simterm]
Повертаємося до основних Settings, вказуємо Public Address == LoadBalancer IP:
OpenVPN – підключення до серверу
Переходимо в Users, клікаємо Download profile:
Розпаковуємо архів:
[simterm]
$ tar xfp local-user.tar
[/simterm]
І підключаємося за допомогою звичайного OpenVPN клієнта:
[simterm]
$ sudo openvpn --config local-org_local-user_local-server.ovpn
[sudo] password for setevoy:
...
2022-10-04 15:58:32 Attempting to establish TCP connection with [AF_INET]10.104.33.93:1194 [nonblock]
2022-10-04 15:58:32 TCP connection established with [AF_INET]10.104.33.93:1194
...
2022-10-04 15:58:33 net_addr_v4_add: 172.16.0.2/24 dev tun0
2022-10-04 15:58:33 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
2022-10-04 15:58:33 Initialization Sequence Completed