Русский
Русский
English
Статистика
Реклама

Lessons learned

Перевод 3 года с Kubernetes в production вот что мы поняли

21.09.2020 12:22:00 | Автор: admin
Прим. перев.: в очередной статье из категории lessons learned DevOps-инженер австралийской компании делится главными выводами по итогам продолжительного использования Kubernetes в production для нагруженных сервисов. Автор затрагивает вопросы Java, CI/CD, сетей, а также сложности K8s в целом.

Свой первый кластер Kubernetes мы начали создавать в 2017 году (с версии K8s 1.9.4). У нас было два кластера. Один работал на bare metal, на виртуальных машинах RHEL, другой в облаке AWS EC2.

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

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

Вот ключевые уроки, которые мы вынесли из опыта использования Kubernetes в production на протяжении трех лет.

1. Занимательная история с Java-приложениями


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

В 20172018 годах некоторые наши приложения работали на Java восьмой версии. Частенько они отказывались функционировать в контейнерных средах вроде Docker и падали из-за проблем с heap-памятью и неадекватной работы сборщиков мусора. Как оказалось, эти проблемы были вызваны неспособностью JVM работать с механизмами контейнеризации Linux (cgroups и namespaces).

С тех пор Oracle приложила значительные усилия, чтобы повысить совместимость Java с миром контейнеров. Уже в 8-ой версии Java появились экспериментальные флаги JVM для решения этих проблем: XX:+UnlockExperimentalVMOptions и XX:+UseCGroupMemoryLimitForHeap.

Но, несмотря на все улучшения, никто не будет спорить, что у Java по-прежнему плохая репутация из-за чрезмерного потребления памяти и медленного запуска по сравнению с Python или Go. В первую очередь это связано со спецификой управления памятью в JVM и ClassLoader'ом.

Сегодня, если нам приходится работать с Java, мы по крайней мере стараемся использовать версию 11 или выше. И наши лимиты на память в Kubernetes на 1 Гб выше, чем ограничение на максимальный объем heap-памяти в JVM (-Xmx) (на всякий случай). То есть, если JVM использует 8 Гб под heap-память, лимит в Kubernetes на память для приложения будет установлен на 9 Гб. Благодаря этим мерам и улучшениям жизнь стала чуточку легче.

2. Обновления, связанные с жизненным циклом Kubernetes


Управление жизненным циклом Kubernetes (обновления, дополнения) вещь громоздкая и непростая, особенно если кластер базируется на bare metal или виртуальных машинах. Оказалось, что для перехода на новую версию гораздо проще поднять новый кластер и потом перенести в него рабочие нагрузки. Обновление существующих узлов попросту нецелесообразно, поскольку связано со значительными усилиями и тщательным планированием.

Дело в том, что в Kubernetes слишком много движущихся частей, которые необходимо учитывать при проведении обновлений. Для того, чтобы кластер мог работать, приходится собирать все эти компоненты вместе начиная с Docker и заканчивая CNI-плагинами вроде Calico или Flannel. Такие проекты, как Kubespray, KubeOne, kops и kube-aws, несколько упрощают процесс, однако все они не лишены недостатков.

Свои кластеры мы разворачивали в виртуальных машинах RHEL с помощью Kubespray. Он отлично себя зарекомендовал. В Kubespray были сценарии для создания, добавления или удаления узлов, обновления версии и почти все, что необходимо для работы с Kubernetes в production. При этом сценарий обновления сопровождался предостережением о том, что нельзя пропускать даже второстепенные (minor) версии. Другими словами, чтобы добраться до нужной версии, пользователю приходилось устанавливать все промежуточные.

Главный вывод здесь в том, что если вы планируете использовать или уже используете Kubernetes, продумайте свои шаги, связанные с жизненным циклом K8s и то, как он вписывается в ваше решение. Создать и запустить кластер часто оказывается проще, чем поддерживать его в актуальном состоянии.

3. Сборка и деплой


Будьте готовы к тому, что придется пересмотреть пайплайны сборки и деплоя. При переходе на Kubernetes у нас прошла радикальная трансформация этих процессов. Мы не только реструктурировали пайплайны Jenkins, но с помощью инструментов, таких как Helm, разработали новые стратегии сборки и работы с Git'ом, тегирования Docker-образов и версионирования Helm-чартов.

Вам понадобится единая стратегия для поддержки кода, файлов с deploymentами Kubernetes, Dockerfiles, образов Docker'а, Helm-чартов, а также способ связать все это вместе.

После нескольких итераций мы остановились на следующей схеме:

  • Код приложения и его Helm-чарты находятся в разных репозиториях. Это позволяет нам версионировать их независимо друг от друга (семантическое версионирование).
  • Затем мы сохраняем карту с данными о том, какая версия чарта к какой версии приложения привязана, и используем ее для отслеживания релиза. Так, например, app-1.2.0 развертывается с charts-1.1.0. Если меняется только файл с параметрами (values) для Helm, то меняется только patch-составляющая версии (например, с 1.1.0 на 1.1.1). Все требования к версиям описываются в примечаниях к релизу (RELEASE.txt) в каждом репозитории.
  • К системным приложениям, таким как Apache Kafka или Redis (чей код мы не собирали и не модифицировали), у нас иной подход. Нам не были нужны два репозитория, поскольку Docker-тег был просто частью версионирования Helm-чартов. Меняя Docker-тег для обновления, мы просто увеличиваем основную версию в теге чарта.

(Прим. перев.: сложно пройти мимо такой трансформации и не указать на нашу Open Source-утилиту для сборки и доставки приложений в Kubernetes werf как один из способов упростить решение тех проблем, с которыми столкнулись авторы статьи.)

4. Тесты Liveliness и Readiness (обоюдоострый меч)


Проверки работоспособности (liveliness) и готовности (readiness) Kubernetes отлично подходят для автономной борьбы с системными проблемами. Они могут перезапускать контейнеры при сбоях и перенаправлять трафик с нездоровых экземпляров. Но в некоторых условиях эти проверки могут превратиться в обоюдоострый меч и повлиять на запуск и восстановление приложения (это особенно актуально для stateful-приложений, таких как платформы обмена сообщениями или базы данных).

Наш Kafka стал их жертвой. У нас был stateful set из 3 Broker'ов и 3 Zookeeper'ов с replicationFactor = 3 и minInSyncReplica = 2. Проблема возникала при перезапуске Kafka после случайных сбоев или падений. Во время старта Kafka запускал дополнительные скрипты для исправления поврежденных индексов, что занимало от 10 до 30 минут в зависимости от серьезности проблемы. Такая задержка приводила к тому, что liveliness-тесты постоянно завершались неудачей, из-за чего Kubernetes убивал и перезапускал Kafka. В результате Kafka не мог не только исправить индексы, но даже стартовать.

Единственным решением на тот момент виделась настройка параметра initialDelaySeconds в настройках liveliness-тестов, чтобы проверки проводились только после запуска контейнера. Главная сложность, конечно, в том, чтобы решить, какую именно задержку установить. Отдельные запуски после сбоя могут занимать до часа времени, и это необходимо учитывать. С другой стороны, чем больше initialDelaySeconds, тем медленнее Kubernetes будет реагировать на сбои во время запуска контейнеров.

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

Обновление: в свежих версиях Kubernetes появился третий тип тестов под названием startup probe. Он доступен как альфа-версия, начиная с релиза 1.16, и как бета-версия с 1.18.

Startup probe позволяет решить вышеописанную проблему, отключая readiness и liveliness-проверки до тех пор, пока контейнер не запустится, тем самым позволяя приложению нормально стартовать.

5. Работа с внешними IP


Как оказалось, использование статических внешних IP для доступа к сервисам оказывает серьезное давление на механизм отслеживания соединений системного ядра. Если его не продумать тщательно, он может сломаться.

В своем кластере мы используем Calico как CNI и BGP в качестве протокола маршрутизации, а также для взаимодействия с пограничными маршрутизаторами. В Kube-proxy задействован режим iptables. Доступ к нашему очень загруженному сервису в Kubernetes (ежедневно он обрабатывает миллионы подключений) открываем через внешний IP. Из-за SNAT и маскировки, проистекающих от программно-определяемых сетей, Kubernetes нуждается в механизме отслеживания всех этих логических потоков. Для этого K8s задействует такие инструменты ядра, как сonntrack и netfilter. С их помощью он управляет внешними подключениями к статическому IP, который затем преобразуется во внутренний IP сервиса и, наконец, в IP-адрес pod'а. И все это делается с помощью таблицы conntrack и iptables.

Однако возможности таблицы conntrack небезграничны. При достижении лимита кластер Kubernetes (точнее, ядро ОС в его основе) больше не сможет принимать новые соединения. В RHEL этот предел можно проверить следующим образом:

$  sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count = 167012net.netfilter.nf_conntrack_max = 262144

Один из способов обойти это ограничение объединить несколько узлов с пограничными маршрутизаторами, чтобы входящие соединения на статический IP распределялись по всему кластеру. В случае, если у вас в кластере большой парк машин, такой подход позволяет значительно увеличить размер таблицы conntrack для обработки очень большого числа входящих соединений.

Это полностью сбило нас с толку, когда мы только начинали в 2017 году. Однако сравнительно недавно (в апреле 2019-го) проект Calico опубликовал подробное исследование под метким названием Why conntrack is no longer your friend (есть такой её перевод на русский язык прим. перев.).

Действительно ли вам нужен Kubernetes?


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

С другой стороны, работа в облаке и возможность использовать Kubernetes как услугу избавит вас от большинства забот, связанных с обслуживанием платформы (вроде расширения CIDR внутренней сети и обновления Kubernetes).

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

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

Помните, что технология исключительно ради технологии бессмысленна.

P.S. от переводчика


Читайте также в нашем блоге:

Подробнее..

Перевод Наши выводы за год миграции GitLab.com на Kubernetes

23.09.2020 10:08:04 | Автор: admin
Прим. перев.: адаптацию Kubernetes в GitLab считают одним из двух главных факторов, способствующих росту компании. Тем не менее, до недавнего времени инфраструктура онлайн-сервиса GitLab.com была построена на виртуальных машинах, и только около года назад началась её миграция в K8s, которая до сих пор не завершена. Рады представить перевод недавней статьи SRE-инженера GitLab о том, как это происходит и какие выводы делают инженеры, участвующие в проекте.



Вот уже около года наше инфраструктурное подразделение занимается миграцией всех сервисов, работающих на GitLab.com, в Kubernetes. За это время мы столкнулись с проблемами, связанными не только к перемещением сервисов в Kubernetes, но и с управлением гибридным deployment'ом во время перехода. О ценных уроках, полученных нами, и пойдет речь в этой статье.

С самого начала GitLab.com его серверы работали в облаке на виртуальных машинах. Этими виртуальными машинами управляет Chef, а их установка происходит с помощью нашего официального Linux-пакета. Стратегия развертывания на случай, если нужно обновить приложение, состоит в простом обновлении парка серверов скоординированным последовательным образом с помощью CI-пайплайна. Этот метод пусть медленный и чуточку скучный гарантирует, что GitLab.com применяет те же способы установки и конфигурирования, что и пользователи автономных (self-managed) инсталляций GitLab, применяющие для этого наши Linux-пакеты.

Мы используем такой метод, поскольку исключительно важно ощутить на себе все печали и радости, которые испытывают рядовые представители сообщества, когда устанавливают и настраивают свои копии GitLab. Этот подход хорошо работал в течение некоторого времени, однако когда число проектов на GitLab перевалило за 10 млн, мы поняли, что он больше не удовлетворяет нашим потребностям в масштабировании и развертывании.

Первые шаги к Kubernetes и cloud-native GitLab


В 2017-м был создан проект GitLab Charts для подготовки GitLab к развертыванию в облаке, а также для того, чтобы дать пользователям возможность устанавливать GitLab в кластеры Kubernetes. Тогда мы знали, что перенос GitLab в Kubernetes увеличит возможности масштабирования SaaS-платформы, упростит развертывания и повысит эффективность использования вычислительных ресурсов. В то же время многие функции нашего приложения зависели от примонтированных NFS-разделов, что замедляло переход с виртуальных машин.

Стремление к cloud native и Kubernetes позволило нашим инженерам планировать постепенный переход, в ходе которого мы отказались от некоторых зависимостей приложения от сетевых хранилищ, попутно продолжая разрабатывать новые функции. С тех пор, как мы начали планировать миграцию летом 2019-го, многие из этих ограничений были устранены, и процесс перевода GitLab.com на Kubernetes теперь идет полным ходом!

Особенности работы GitLab.com в Kubernetes


Для GitLab.com мы используем единый региональный кластер GKE, обрабатывающий весь трафик приложения. Чтобы минимизировать сложность (без того мудреной) миграции, мы концентрируемся на сервисах, которые не зависят от локального хранилища или NFS. GitLab.com использует преимущественно монолитную кодовую базу на Rails, и мы направляем трафик в зависимости от характеристик рабочей нагрузки на различные endpoint'ы, изолированные в свои собственные пулы узлов.

В случае фронтенда эти типы делятся на запросы к web, API, Git SSH/HTTPS и Registry. В случае бэкенда мы разбиваем job'ы в очереди по различным характеристикам в зависимости от предопределенных границ ресурсов, которые позволяют нам устанавливать целевые показатели уровня обслуживания (Service-Level Objectives, SLOs) для различных нагрузок.

Все эти сервисы GitLab.com настроены с помощью немодифицированного Helm-чарта GitLab. Конфигурация проводится в субчартах, которые могут быть выборочно включены по мере того, как мы постепенно переносим сервисы в кластер. Даже с учетом того, что было решено не включать в миграцию некоторые из наших stateful-сервисов, такие как Redis, Postgres, GitLab Pages и Gitaly, использование Kubernetes позволяет радикально сократить число VM, которыми управляет Chef в настоящее время.

Прозрачность и управление конфигурацией Kubernetes


Все настройки управляются самим GitLab'ом. Для этого используются три конфигурационных проекта на основе Terraform и Helm. Мы стараемся везде по возможности использовать сам GitLab для запуска GitLab'а, но для эксплуатационных задач у нас функционирует отдельная инсталляция GitLab. Она нужна для того, чтобы не зависеть от доступности GitLab.com при проведении развертываний и обновлений GitLab.com.

Хотя наши пайплайны для кластера Kubernetes работают на отдельной инсталляции GitLab, есть у репозиториев кода есть зеркала, публично доступные по следующим адресам:

  • k8s-workloads/gitlab-com конфигурационная обвязка GitLab.com для Helm-чарта GitLab;
  • k8s-workloads/gitlab-helmfiles содержит конфигурации для сервисов, которые не связаны с приложением GitLab непосредственно. В их число входят конфигурации для ведения логов и мониторинга кластера, а также для интегрированных инструментов вроде PlantUML;
  • Gitlab-com-infrastructure конфигурация Terraform для Kubernetes и старой (legacy) VM-инфраструктуры. Здесь настраиваются все ресурсы, необходимые для запуска кластера, включая сам кластер, пулы узлов, учетные записи служб, резервирование IP-адресов.


При внесении изменений показывается общедоступное краткое резюме со ссылкой на подробный diff, который SRE анализирует перед внесением изменений в кластер.

Для SRE ссылка ведет на подробный diff в инсталляции GitLab, которая используется для эксплуатации и доступ к которой ограничен. Это позволяет сотрудникам и сообществу без доступа к эксплуатационному проекту (он открыт только для SRE) просматривать предлагаемые изменения в конфигурации. Сочетая общедоступный экземпляр GitLab'а для кода с закрытым экземпляром для CI-пайплайнов, мы сохраняем единый рабочий процесс, в то же время гарантируя независимость от GitLab.com при обновлениях конфигурации.

Что мы выяснили за время миграции


В процессе переезда был накоплен опыт, который мы применяем к новым миграциям и deploymentам в Kubernetes.

1. Рост расходов из-за трафика между зонами доступности



Посуточная egress-статистика по парку Git-хранилищ на GitLab.com

Google делит свою сеть на регионы. Те, в свою очередь, разбиваются на зоны доступности (AZ). Git-хостинг связан с большими объемами данных, поэтому нам важно контролировать сетевой egress. В случае внутреннего трафика egress бесплатен только в том случае, если он остается в границах одной зоны доступности. На момент написания этой статьи мы отдаем примерно 100 Тб данных в обычный рабочий день (и это только для Git-репозиториев). Сервисы, которые в нашей старой топологии, основанной на VM, находились на одних и тех же виртуальных машинах, теперь работают в разных pod'ах Kubernetes. Это означает, что некоторая часть трафика, которая раньше была локальной для VM, может потенциально выходить за пределы зон доступности.

Региональные кластеры GKE позволяют охватывать несколько зон доступности для резервирования. Мы рассматриваем возможность разделить региональный кластер GKE на однозонные кластеры для сервисов, которые генерируют большие объемы трафика. Это позволит сократить расходы на egress при сохранении резервирования на уровне кластера.

2. Limit'ы, request'ы ресурсов и масштабирование



Число реплик, обрабатывающих production-трафик на registry.gitlab.com. Трафик достигает своего пика в ~15:00 UTC.

Наша история с миграцией началась в августе 2019-го, когда мы перенесли первый сервис реестр контейнеров GitLab (GitLab Container Registry) в Kubernetes. Этот критически важный сервис с высоким трафиком хорошо подошел для первой миграции, поскольку представляет собой stateless-приложение с малым числом внешних зависимостей. Первой проблемой, с которой мы столкнулись, стало большое число вытесненных pod'ов из-за нехватки памяти на узлах. Из-за этого нам пришлось менять request'ы и limit'ы.

Было обнаружено, что в случае приложения, у которого потребление памяти растет со временем, низкие значения для request'ов (резервирующих память для каждого pod'а) вкупе с щедрым жестким limit'ом на использование приводили к насыщению (saturation) узлов и высокому уровню вытеснений. Чтобы справиться с этой проблемой, было решено увеличить request'ы и снизить limit'ы. Это сняло давление с узлов и обеспечило жизненный цикл pod'ов, который не оказывал слишком высокого давления на узел. Теперь мы начинаем миграции с щедрыми (и почти одинаковыми) значениями request'ов и limit'ов, корректируя их по необходимости.

3. Метрики и логи



Инфраструктурное подразделение фокусируется на задержках, проценте ошибок и сатурации с установленными целями по уровню обслуживания (SLO), привязанными к общей доступности нашей системы.

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

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

Параллельное обслуживание одних и тех же запросов на старой VM-инфраструктуре и новой, основанной на Kubernetes, представляло собой уникальную задачу. В отличие от миграции типа lift-and-shift (быстрый перенос приложений как есть в новую инфраструктуру; подробнее можно прочитать, например, здесь прим. перев.), параллельная работа на старых VM и Kubernetes требует, чтобы инструменты для мониторинга были совместимы с обеими средами и умели объединять метрики в один вид. Важно, что мы используем одни и те же dashboardы и запросы к логам, чтобы добиться согласованной наблюдаемости во время переходного периода.

4. Переключение трафика на новый кластер


Для GitLab.com часть серверов выделяется под канареечную (canary) стадию. Канареечный парк обслуживает наши внутренние проекты, а также может включаться пользователями. Но в первую очередь он предназначен для проверки изменений, вносимых в инфраструктуру и приложение. Первый перенесенный сервис начал с приема ограниченного объема внутреннего трафика, и мы продолжаем использовать этот метод, чтобы убедиться в соблюдении SLO перед тем, как направить весь трафик в кластер.

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

5. Резервные мощности pod'ов и их использование


Почти сразу была выявлена следующая проблема: pod'ы для сервиса Registry стартовали быстро, однако запуск pod'ов для Sidekiq занимал до двух минут. Продолжительный запуск pod'ов для Sidekiq стал проблемой, когда мы приступили к миграции в Kubernetes рабочих нагрузок для worker'ов, которым нужно быстро обрабатывать job'ы и быстро масштабироваться.

В данном случае урок заключался в том, что, хотя Horizontal Pod Autoscaler (HPA) в Kubernetes хорошо справляется с ростом трафика, важно принимать во внимание характеристики рабочих нагрузок и выделять резервные мощности pod'ов (особенно в условиях неравномерного распределения спроса). В нашем случае наблюдался внезапный всплеск job'ов, влекущий за собой стремительное масштабирование, что приводило к насыщению ресурсов CPU до того, как мы успевали масштабировать пул узлов.

Всегда есть соблазн как можно больше выжать из кластера, однако мы, изначально столкнувшись с проблемами с производительностью, теперь начинаем с щедрого pod budget'а и уменьшаем его впоследствии, пристально следя за SLO. Запуск pod'ов для сервиса Sidekiq значительно ускорился и теперь в среднем занимает около 40 секунд. От сокращения времени запуска pod'ов выиграл как GitLab.com, так и наши пользователи инсталляций self-managed, работающие с официальным Helm-чартом GitLab.

Заключение


После переноса каждого сервиса мы радовались преимуществам использования Kubernetes в production: более быстрому и безопасному деплою приложения, масштабированию и более эффективному распределению ресурсов. Причем плюсы миграции выходят за рамки сервиса GitLab.com. От каждого улучшения официального Helm-чарта выигрывают и его пользователи.

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


P.S. от переводчика


Читайте также в нашем блоге:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru