-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Перевод] DevOps с Kubernetes и VSTS. Часть 2: Облачная история

Вторник, 19 Сентября 2017 г. 17:58 + в цитатник
stasus сегодня в 17:58 Разработка

DevOps с Kubernetes и VSTS. Часть 2: Облачная история

  • Перевод
  • Tutorial
Продолжение истории про Kubernetes, контейнеры и организацию CI/CD пайплайна. Наконец-то появляется облако Azure и Visual Studio Team Services. Интересно, что CI/CD пайплайн VSTS использует для работы с k8s кластером kubectl, поэтому развёртывать приложение можно не только в Azure Container Services, но и в любой другой инстраляции Kubernetes.



Читайте перевод второй части статьи DevOps с Kubernetes и VSTS.

Цикл статей «DevOps с Kubernetes и VSTS»


1. Локальная история
2. Облачная история

В первой части я продемонстрировал подход к разработке многоконтейнерных приложений с использованием Kubernetes (k8s), а точнее minikube, полноценной среды k8s, которая запускает один узел на виртуальной машине на вашем ноутбуке. В предыдущей статье я клонировал этот репозиторий (убедитесь, что развернута ветка docker) с двумя контейнерами: DotNet Core API и frontend SPA (Aurelia) (в виде статических файлов в приложении DotNet Core app). Я показал, как создавать контейнеры локально и запускать их в minikube, а также использовать возможности ConfigMaps для работы с конфигурациями.

В этой статье я расскажу вам, как перенести локальную разработку в CI/CD и создать конвейер для автоматизированного формирования сборки/выпуска с использованием VSTS. Мы создадим реестр контейнеров и службы контейнеров в Azure, используя k8s в качестве механизма оркестрации.

CI/CD пайплайн для Kubernetes в VSTS


Настоятельно рекомендую вам изучить прекрасный начальный курс Найджела Поултона (Nigel Poulton) под названием Getting Started with Kubernetes на PluralSight (прим. переводчика — на английском языке, нужна платная подписка), а также статью Атула Малавия (Atul Malaviya) из Microsoft. Курс Найджела стал превосходным погружением в Kubernetes для новичков, а статья Атула помогла понять принципы взаимодействия VSTS и k8s, но ни курс, ни статья не охватили конвейер полностью. Из них я так и не понял, как в конвейере CI/CD реализовано обновление образов. Что ж, пришлось самому провести ряд экспериментов и подготовить эту статью!

Развертывание среды k8s с помощью служб контейнеров Azure


k8s можно запускать локально, в AWS или Google Cloud или Azure. Мы будем использовать службу контейнеров Azure (Azure Container Service). Однако, конвейер CI/CD, который я демонстрирую в этой статье, не зависит от конкретного облачного хостинга, он подходит для любого кластера k8s. Мы также создадим приватный реестр контейнеров в Azure, но вы, опять же, можете использовать любой реестр контейнеров по своему выбору.

Создать кластер k8s можно и на портале Azure. Однако Azure CLI позволяет сделать это быстрее, и вы сохраните ключи, которые понадобятся для подключения, поэтому я решил использовать этот механизм. Я также буду использовать Bash для Windows с kubectl, но подойдет любая платформа с kubectl и Azure CLI.

Вот команды:

# set some variables
export RG="cd-k8s"
export clusterName="cdk8s"
export location="westus"
# create a folder for the cluster ssh-keys
mkdir cdk8s
 
# login and create a resource group
az login
az group create --location $location --name $RG
 
# create an ACS k8s cluster
az acs create --orchestrator-type=kubernetes --resource-group $RG --name=$ClusterName --dns-prefix=$ClusterName --generate-ssh-keys --ssh-key-value ~/cdk8s/id_rsa.pub --location $location --agent-vm-size Standard_DS1_v2 --agent-count 2
 
# create an Azure Container Registry
az acr create --resource-group $RG --name $ClusterName --location $location --sku Basic --admin-enabled
 
# configure kubectl
az acs kubernetes get-credentials --name $ClusterName --resource-group $RG --file ~/cdk8s/kubeconfig --ssh-key-file ~/cdk8s/id_rsa
export KUBECONFIG="~/cdk8s/kubeconfig"
 
# test connection
kubectl get nodes
NAME                    STATUS                     AGE       VERSION
k8s-agent-96607ff6-0    Ready                      17m       v1.6.6
k8s-agent-96607ff6-1    Ready                      17m       v1.6.6
k8s-master-96607ff6-0   Ready,SchedulingDisabled   17m       v1.6.6

Примечания:

  • Строки 2–4: создаем переменные.
  • Строка 6: создаем каталог для ssh-ключей и файла конфигурации kubeconfig.
  • Строка 9: входим в систему в Azure (запрос на открытие в браузере страницы с меню входа; если у вас нет подписки Azure, создайте бесплатную прямо сейчас!).
  • Строка 10: создаем группу для размещения всех ресурсов, которые мы собираемся создать.
  • Строка 13: развертываем кластер k8s с использованием только что созданной группы ресурсов и имени, которое мы передаем; генерируем ssh-ключи и помещаем в указанный каталог; нам нужны два агента (узла) с указанным размером виртуальной машины.
  • Строка 16: создаем реестр контейнеров Azure в той же группе ресурсов с доступом от имени администратора.
  • Строка 19: получаем учетные данные для подключения к кластеру с помощью kubectl; используем полученный ssh-ключ и сохраняем учетные данные в указанном файле kubeconfig.
  • Строка 20: просим kubectl использовать эту конфигурацию вместо конфигурации по умолчанию (которая может иметь другие кластеры k8s или конфигурацию minikube).
  • Строка 23: проверяем возможность подключения к кластеру.
  • Строки 24–27: мы успешно подключаемся!

Если вы запустите браузер, перейдете на портал Azure и откроете свою группу ресурсов, то увидите, сколько всего было создано этими простыми командами:



Не беспокойтесь, самостоятельно управлять ресурсами вам не придется. Azure и кластер k8s берут это на себя!

Пространства имен


Прежде чем мы создадим сборку и выпуск для наших контейнерных приложений, давайте рассмотрим модель продвижения. Обычно схема примерно такая: разработка -> пользовательские приемочные испытания -> производственная среда (Dev -> UAT -> Prod). В случае c k8s, minikube представляет собой локальную среду разработки, и это здорово. Это полноценный кластер k8s на вашем ноутбуке, поэтому вы можете запускать свой код локально, в том числе с помощью таких конструктов k8s, как configMaps. А как быть с UAT и Prod? Вариант — развернуть отдельные кластеры, но такой подход может оказаться дорогостоящим. Также вы можете обеспечить совместное использование ресурсов кластера с помощью пространств имен.

Пространства имен в k8s выступают в качестве рубежей безопасности, но они также могут стать границами изоляции. Я могу развернуть новые версии моего приложения в пространстве имен dev, которое будет использовать ресурсы пространства имен prod, но при этом останется полностью невидимым (собственные IP-адреса и т. д.). Разумеется, не следует проводить нагрузочное тестирование в рамках такой конфигурации, поскольку мы будем потреблять значительные ресурсы, предназначенные для приложений в производственной среде. Эта концепция напоминает слоты развертывания в службах приложений Azure, которые используются для незаметного тестирования приложений перед их передачей в производственную среду.

Если вы создаете кластер k8s, то помимо пространств имен kube-system и kube-public (с подами k8s) получаете пространство имен по умолчанию. При отсутствии четких указаний с вашей стороны любые службы, развертывания или поды, которые вы создадите, попадают в это пространство имен. Но мы создадим два дополнительных пространства имен: dev и prod. Вот наш yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: dev
---
apiVersion: v1
kind: Namespace
metadata:
  name: prod

Этот файл содержит определения для обоих пространств имен. Выполните команду Apply, чтобы создать пространства имен. Завершив все процедуры, вы можете вывести список всех пространств имен в кластере:

kubectl apply -f namespaces.yml
namespace "dev" created
namespace "prod" created
 
kubectl get namespaces
NAME          STATUS    AGE
default       Active    27m
dev           Active    20s
kube-public   Active    27m
kube-system   Active    27m
prod          Active    20s

Настройка секрета для реестра контейнеров


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

az acr credential show --name $ClusterName --output table
USERNAME    PASSWORD                          PASSWORD2
----------  --------------------------------  --------------------------------
cdk8s       some-long-key-1                   some-long-key-2

kubectl create secret docker-registry regsecret --docker-server=$ClusterName.azurecr.io --docker-username=$ClusterName --docker-password= --docker-email=admin@azurecr.io
secret "regsecret" created
 

Первая команда использует az, чтобы получить ключи для пользователя с правами администратора (имя пользователя с правами администратора совпадает с именем реестра контейнеров, поэтому я создал cdk8s.azurecr.io, и имя пользователя-администратора будет cdk8s). Передайте один из ключей (неважно, какой) в качестве пароля. Адрес электронной почты не используется, поэтому можете указать какой угодно. Теперь у нас есть секрет реестра с именем regsecret, на который мы можем ссылаться, выполняя развертывание в кластере k8s. K8s будет использовать этот секрет для проверки подлинности в реестре.

Настройка конечных точек VSTS


Мы настроили кластер k8s и реестр контейнеров. Теперь добавим эти конечные точки в VSTS, чтобы мы могли передавать контейнеры в реестр при создании сборки и выполнять команды в отношении кластера k8s во время подготовки выпуска. Конечные точки позволяют нам абстрагировать проверку подлинности, чтобы не хранить учетные данные непосредственно в определениях наших выпусков. Вы также можете создать роли, чтобы ограничить доступ на просмотр и использование конечных точек.

Запустите VSTS и откройте командный проект (или просто создайте новый). Перейдите в командный проект и нажмите значок шестеренки, чтобы открыть узел настроек для этого командного проекта. Щелкните Services. Щелкните + New Services и создайте новую конечную точку Docker Registry. Введите те же учетные данные, которые вы использовали для создания секрета реестра в k8s с помощью kubectl:



Теперь создайте конечную точку k8s. Введите URL: https://$ClusterName.$location.cloudapp.azure.com (clustername и location — переменные, которые мы использовали при создании кластера). Необходимо скопировать в текстовое поле для учетных данных все содержимое файла ~/cdk8s/kubeconfig (вы могли назвать его по-другому), который был создан после выполнения команды az acs kubernetes get-credential:



Теперь у нас есть две конечные точки, которые мы можем использовать в определениях сборки и выпуска:



Сборка


Теперь мы можем создать сборку, которая будет компилировать/тестировать наш код, создавать образы docker и помещать их в реестр контейнеров, соответствующим образом помечая их. Щелкните Build & Release, а затем — Builds, чтобы открыть узел сборки. Создайте новое определение сборки. Выберите шаблон ASP.NET Core и нажмите Apply. Необходимо выполнить следующие настройки:

  • Tasks -> Process: введите имя, например k8s-demo-CI, и выберите очередь Hosted Linux Preview.
  • Опционально: измените формат номера сборки на 1.0.0$(rev:.r), чтобы ваши сборки имели формат 1.0.0.x.
  • Tasks -> Get Sources: выберите репозиторий Github с проверкой подлинности OAuth или PAT. Выберите AzureAureliaDemo, а затем docker в качестве ветки по умолчанию. Возможно, вам придется создать «вилку» для репозитория (или просто импортировать его в VSTS), если вы выполняете действия вместе со мной.
  • Tasks -> DotNet Restore: не вносите никаких изменений.
  • Tasks -> DotNet Build: добавьте --version-suffix $(Build.BuildNumber) в аргументы сборки, чтобы обеспечить соответствие версии и номера сборки.
  • Tasks -> DotNet Test: отключите эту задачу, поскольку в нашем решении не применяются тесты DotNet (конечно, если у вас будут тесты, задачу можно снова включить).
  • Tasks -> добавьте задачу npm. В качестве рабочего каталога выберите frontend и убедитесь, что используется команда install.
  • Tasks -> добавьте задачу Command line. В качестве инструмента (tool) выберите node, укажите аргументы: node_modules/aurelia-cli/bin/aurelia-cli.js test и рабочий каталог: frontend. Так вы запустите тесты Aurelia.
  • Tasks -> добавьте задачу Publish test results. В поле Test Results files укажите test*.xml, а в поле Search Folder введите $(Build.SourcesDirectory)/frontend/testresults. Так вы опубликуете результаты тестов Aurelia.
  • Tasks -> добавьте задачу Publish code coverage. В поле Coverage Tool введите Cobertura, в поле Summary File — $(Build.SourcesDirectory)/frontend/reports/coverage/cobertura.xml, в поле Report Directory — $(Build.SourcesDirectory)/frontend/reports/coverage/html. Так вы опубликуете данные о покрытии тестов Aurelia.
  • Tasks -> добавьте задачу Command line. В качестве инструмента (tool) выберите node, укажите аргументы: node_modules/aurelia-cli/bin/aurelia-cli.js test и рабочий каталог: frontend. Так вы скомпилируете, обработаете и упакуете приложение Aurelia SPA.
  • Tasks -> DotNet Publish. В качестве аргументов введите -c $(BuildConfiguration) -o publish и снимите флажок Zip Published Projects.
  • Tasks -> добавьте задачу Docker Compose. В поле Container Registry Type укажите Azure Container Registry, в качестве подписки и реестра контейнеров Azure укажите реестр, для которого мы создали конечную точку ранее. В поле Additional Docker Compose Files укажите docker-compose.vsts.yml, в поле Action — Build service images, в поле Additional Image Tags — $(Build.BuildNumber), чтобы номер сборки использовался в качестве тега для образов.
  • Создайте клон задачи Docker Compose. В качестве имени укажите Push service images и выберите действие Push service images. Установите флажок Include Latest Tag.
  • Tasks -> Publish Artifact. В полях Path to Publish и Artifact Name выберите k8s. Так вы опубликуете файлы k8s yaml, чтобы включить их в выпуск.

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



Теперь можно нажать Save and Queue, чтобы сохранить сборку и поставить ее в очередь. По завершении процесса создания сборки вы увидите сводную информацию о тестировании/покрытии.



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



Выпуск


Теперь мы можем настроить выпуск, который будет создавать/обновлять нужные службы. Для этого следует обеспечить управление конфигурацией. Можно было просто включить конфигурацию в код, но в таком случае конфиденциальные данные (например, пароли) попали бы в инструмент управления версиями. Я предпочитаю «токенизировать» любую конфигурацию, чтобы решение для управления выпусками размещало конфиденциальные данные за пределами зоны, контролируемой инструментом управления версиями. Решение VSTS Release Management позволяет создавать секреты для отдельных сред или выпусков, также вы можете создавать их в группах переменных с поддержкой повторного использования. Кроме того, теперь поддерживается бесшовная интеграция с Azure Key Vault.

Чтобы вместо токенов использовать значения, специфичные для среды, нам нужна задача для замены токена. К счастью, у меня есть кросс-платформенная задача ReplaceTokens из модуля расширения Colin's ALM Corner Build & Release Tasks, который я скачал из магазина VSTS Marketplace. Щелкните ссылку, чтобы перейти на нужную страницу, затем нажмите Install, чтобы установить расширение для своей учетной записи.

На странице сводной информации о сборке прокрутите бегунок справа до раздела Deployments и щелкните ссылку Create release. Также можете щелкнуть Releases и создать новое определение оттуда. Начните с пустого шаблона (Empty), выберите свой командный проект и сборку, которую вы только что создали, в качестве исходной сборки. Поставьте флажок Continuous Deployment, чтобы выпуск создавался автоматически для каждой правильной сборки.

В качестве имени для определения укажите k8s или что-то описательное. На вкладке General измените формат номера выпуска на $(Build.BuildNumber)-$(rev:r), чтобы по имени выпуска всегда можно было без труда определить номер сборки. Вернитесь в раздел Environments и вместо Environment 1 введите dev. Щелкните ссылку Run on Agent и убедитесь, что в поле Deployment queue выбрано Hosted Linux Preview.

Добавьте следующие задачи:

  • Replace Tokens.
    • Source Path: в проводнике откройте каталог k8s.
    • Target File Pattern: *-release.yml. Таким образом, токен будет заменен в любом файле yml с именем, которое оканчивается на -release. Таких файлов три: файлы службы/развертывания для сервера и клиента, а также файл конфигурации клиента. Эта задача находит токены в файле (с префиксом и постфиксом __) и ищет переменные с таким же именем. Каждая переменная заменяется своим значением. Через некоторое время мы создадим переменные.
  • Kubernetes Task 1 (применить конфигурацию для клиента).
    • Настройте подключение k8s к созданной ранее конечной точке. Также необходимо настроить подключение к реестру контейнеров Azure. Это касается всех задач Kubernetes. В поле Command выберите apply, поставьте флажок Use Configuration Files и укажите файл k8s/app-demo-frontend-config-release.yml с помощью средства выбора файлов. Укажите --namespace $(namespace) в текстовом поле для аргументов.
  • Kubernetes Task 2 (применить определение службы/развертывания на стороне сервера).
    • Задайте одинаковые параметры подключения для службы k8s и реестра контейнеров Azure. В этот раз в поле Secret Name укажите regsecret (это имя секрета, который мы создали при настройке кластера k8s, а также имя, на которое мы ссылаемся в параметре imagePullSecret в определениях развертывания). Поставьте флажок Force update secret. Это обеспечит совпадение значений секрета k8s и ключа из Azure. Этот параметр можно было пропустить, поскольку ключ мы создали вручную.
    • В поле Command выберите apply, поставьте флажок Use Configuration Files и укажите файл k8s/app-demo-backend-release.yml с помощью средства выбора файлов. Укажите --namespace $(namespace) в текстовом поле для аргументов.

  • Kubernetes Task 3 (применить определение службы/развертывания на стороне клиента).
    • Настройки аналогичны предыдущей задаче, только выбираем файл k8s/app-demo-frontend-release.yml.
  • Kubernetes Task 4 (обновить образ на стороне сервера).
    • Задайте одинаковые параметры подключения для службы k8s и реестра контейнеров Azure. Секрет здесь не требуется. В поле Command выберите set, а в поле Arguments укажите image deployment/demo-backend-deployment backend=$(ContainerRegistry)/api:$(Build.BuildNumber) --record --namespace=$(namespace).
    • Так вы обновите версию (тег) используемого образа контейнера. K8s выполнит последовательное обновление, запустив новые контейнеры и отключив старые, и служба все это время будет работоспособна.

  • Kubernetes Task 5 (обновить образ на стороне клиента).
    • Параметры аналогичны предыдущей задаче, только в поле Arguments необходимо указать image deployment/demo-frontend-deployment frontend=$(ContainerRegistry)/frontend:$(Build.BuildNumber) --record --namespace=$(namespace)
  • Нажмите кнопку «…» на карточке dev и щелкните Configure Variables для настройки переменных. Задайте следующие значения:
    • BackendServicePort: 30081
    • FrontendServicePort: 30080
    • ContainerRegistry: <ваше реестр контейнеров>.azurecr.io
    • namespace: $(Release.EnvironmentName)
    • AspNetCoreEnvironment: development
    • baseUri: http://$(BackendServiceIP)/api
    • BackendServiceIP: 10.0.0.1


Так задаются специфичные для среды значения для всех переменных в файлах yml. Задача Replace Tokens запишет для нас нужные значения в файлы. Давайте быстро посмотрим на один из токенизированных файлов (токенизированные строки выделены):

apiVersion: v1
kind: Service
metadata:
  name: demo-frontend-service
  labels:
    app: demo
spec:
  selector:
    app: demo
    tier: frontend
  ports:
    - protocol: TCP
      port: 80
      nodePort: __FrontendServicePort__
  type: LoadBalancer
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: demo-frontend-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: demo
        tier: frontend
    spec:
      containers:
        - name: frontend
          image: __ContainerRegistry__/frontend
          ports:
          - containerPort: 80
          env:
          - name: "ASPNETCORE_ENVIRONMENT"
            value: "__AspNetCoreEnvironment__"
          volumeMounts:
            - name: config-volume
              mountPath: /app/wwwroot/config/
          imagePullPolicy: Always
      volumes:
        - name: config-volume
          configMap:
            name: demo-app-frontend-config
      imagePullSecrets:
        - name: regsecret

Комментарий относительно значения для BackendServiceIP: мы используем 10.0.0.1 в качестве замещающего текста, так как Azure присвоит IP-адрес этой службе, когда k8s запустит службу на стороне сервера (вы увидите общедоступный IP-адрес в группе ресурсов на портале Azure). Нам нужно будет выполнить это один раз, чтобы создать службы, а затем обновить, чтобы получить реальный IP-адрес и обеспечить работоспособность службы на стороне клиента. Мы также используем $(Release.EnvironmentName) в качестве значения для namespace, поэтому для dev (а затем и для prod) пространства имен должны совпадать с теми, что создали мы (включая регистр символов).

Если служба/развертывание и конфигурация не изменяются, то первые три задачи k8s по существу не работают. Какой-то результат даст только команда set. Но это как раз здорово, поскольку файлы службы/развертывания и конфигурации можно применять идемпотентно! Они меняются, когда это необходимо, и не вносят никаких возмущений во всех остальных ситуациях — идеальный подход для повторяющихся выпусков!

Сохраните определение. Щелкните + Release, чтобы создать новый выпуск. Щелкните по номеру выпуска (это будет что-то вроде 1.0.0.1-1), чтобы открыть его. Щелкните logs, чтобы просмотреть журналы.



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

az acs kubernetes browse -n $ClusterName -g $RG --ssh-key-file ~/cdk8s/id_rsa
 
Proxy running on 127.0.0.1:8001/ui
Press CTRL+C to close the tunnel...
Starting to serve on 127.0.0.1:8001

Последний аргумент — это путь к файлу SSH-ключа, который генерируется при создании кластера (укажите актуальный путь). Теперь можно открыть в браузере страницу http://localhost:8001/ui. В раскрывающемся меню namespace выберите dev и щелкните Deployments. Вы должны увидеть два успешных развертывания с двумя работоспособными подами в каждом. Вы также можете увидеть образы, которые запускаются в развертываниях. Обратите внимание на номер сборки, указанный в качестве тега!



Чтобы увидеть службы, щелкните Services.



Теперь у нас есть IP-адрес службы на стороне сервера, поэтому мы можем обновить переменную в выпуске. Затем мы можем поставить в очередь новую версию, и на этот раз в конфигурации клиента будет указан правильный IP-адрес для службы на стороне сервера (в нашем случае это 23.99.58.48). Мы можем ввести IP-адрес нашей клиентской службы в браузере и убедиться, что теперь все работает!



Создание производственной среды


Теперь, когда мы убедились в работоспособности среды dev, мы можем вернуться к выпуску, клонировать среду dev и назвать копию prod. Укажите одобрение постфактум для dev (или предварительное одобрение для prod), чтобы между двумя средами была контрольная точка.



Затем мы можем просто изменить порты для узлов, а также значения переменных AspNetCoreEnvironment и BackendServiceIP, и готово! Разумеется, нам нужно сначала выполнить развертывание в пространстве имен prod, прежде чем мы увидим IP-адрес, назначенный k8s/Azure для сервера prod. Затем необходимо повторно запустить процедуру создания выпуска, чтобы обновить конфигурацию.



Мы могли бы удалить параметр nodePort из определений и позволить платформе k8s самой выбрать порт узла, но если порт указан явным образом, то мы будем точно знать, какой порт служба будет использовать в кластере (не для доступа извне).

Мне не нравится указывать --namespace для каждой команды, настолько не нравится, что я создал запрос Pull Request в репозитории vsts-tasks на Github, чтобы дать доступ к пространству имен в качестве дополнительного элемента пользовательского интерфейса!

Проход по всему CI/CD конвееру


Теперь, когда наши среды dev и prod в конвейере CI/CD настроены, мы можем внести изменения в код. Я поменяю текст под версией на «K8s demo» и применю изменения. Так мы инициируем сборку, создание более нового образа контейнера и выполнение тестов, что, в свою очередь, запустит выпуск в среде dev. Теперь я вижу изменение в среде dev (версия 1.0.0.3 или более новая, то есть больше, чем 1.0.0.1), в то время как версия prod все еще равна 1.0.0.1.



Одобрите dev в решении для управления выпусками, и процесс для prod запустится, через несколько секунд версия prod также будет равна 1.0.0.3.

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

Заключение


k8s имеет большой потенциал как надежный механизм оркестрации контейнеров. Технология yml позволяет реализовать концепцию «инфраструктура как код» и отлично подходит для управления версиями. Механизм развертывания сводит к минимуму или вовсе устраняет время простоя при развертывании, а возможность использования configMaps и секретов обеспечивает безопасность всего процесса. Интерфейс командной строки Azure CLI позволяет создать кластер k8s и реестр контейнеров Azure при помощи пары простых команд. Интеграция с VSTS с помощью задач k8s упрощает настройку конвейера CI/CD, вместе они формируют оптимальный рабочий процесс разработки. А с учетом решения minikube, о котором я писал в первой части этой серии статей и который предоставляет вам полноценный кластер k8s для локальной разработки на вашем ноутбуке, вы получаете отличный рабочий процесс разработки, непрерывной интеграции и непрерывного развертывания (Dev/CI/CD).

Разумеется, конвейер CI/CD не проводит нагрузочное тестирование реальных приложений в производственной среде! Я хотел бы узнать о вашем опыте применения k8s в производственных средах. Пишите в комментариях, если у вас есть опыт запуска приложений в кластере k8s в производственной среде!

Удачи вам с k8sing!



P.S. Благодарим Константина Кичинского (Quantum Quintum) за иллюстрацию к этой статье.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337708/


Метки:  

ASO в Playstore: добавим немного юмора в работу, или как поэзия может помочь в росте органики на 304% за 30 дней

Вторник, 19 Сентября 2017 г. 17:08 + в цитатник
В рамках плана экспериментов решили добавить толику креатива в семантическое ядро. Другими словами, запилить А/В тест ”на грани приличий” или ”in search for The Line”, если выражаться терминами Gray Hat ASO. Результаты впечатлили, но об этом ниже.
Читать дальше ->

https://habrahabr.ru/post/338266/


Метки:  

[Из песочницы] Пишем для UEFI BIOS в Visual Studio. Часть 1 — разворачивание среды разработки, компиляция и запуск на отладку

Вторник, 19 Сентября 2017 г. 17:00 + в цитатник
DarkTiger сегодня в 17:00 Разработка

Пишем для UEFI BIOS в Visual Studio. Часть 1 — разворачивание среды разработки, компиляция и запуск на отладку

Введение


В этой статье будет описано, как быстро начать программировать для UEFI во фреймворке edk2 в среде Visual Studio, не тратя массу времени на настройку среды обычным способом, по оригинальным мануалам. Достаточно дать команду git clone ... в корневом каталоге диска, и это на самом деле все, среда будет полностью установлена и готова к работе. Требуются 64-разрядная Windows 7 и выше c Visual Studio 2008-2016. Эти два условия не обязательны, но тогда придется немного потрудиться над собиранием системы edk2-Visual Studio в единое целое, краткая памятка будет приведена.

Цель статьи — провести начинающего за руку по первому UEFI проекту, оставаясь в привычной ему среде. Для более опытных людей, надеюсь, будет интересным поработать в VS вместо привычной командной строки, или разобрать подход и перенести его в любимый Eclipse.

Начнем с простых вещей, вывода строки на консоль и русификации (довольно востребованная вещь, причем простая в реализации), потом будет работа с формами в HII (то, что называлось в обиходе страницами BIOS Setup), потом графика, потом Boot Manager, а потом видно будет (с).


Желающие — прошу пожаловать под кат.

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

Вначале хорошее


1) Аппаратура не понадобится от слова совсем. Никаких Evaluation Boards, никаких Intel BlueBox JTAG за $3000. Все будет отлаживаться в 32-битной виртуальной машине OVMF – портированной Интелом виртуалке qemu для отладки UEFI Firmware. Для перекомпиляции под реальную платформу достаточно потом — после отладки — переставить пару ключей в настройках компиляции, и на этом все.

2) Работать будем со всеми возможностями Visual Studio, т.е. доступны breakpoints, watch, step execution и остальное. Перекомпиляция и запуск несложного модуля занимает 8-10 секунд.

3) Файловая система виртуалки доступна на Windows-машине для записи-чтения. Очень пригодится, если надо будет редактировать скрипты UEFI Shell, после запуска посмотреть скриншоты и проанализировать логи средствами Windows.

4) Никакого ассемблера, только С/С++.

Теперь о том, что мы делать не будем


1) Мы будем работать в DXE (где уже есть UEFI Shell) и поздних фазах. В более ранние фазы не полезем, поскольку нас туда никто не пустит, по крайней мере – для Intel-процессоров. Позже будет объяснение, почему. Если хотите сделать полный цикл, от включения до загрузки ОС, причем быстро и не забивая голову кучей ненужной и неинтересной вам в данный момент информацией, а ручное конфигурирование системы вам совершенно не требуется – дальше не читайте, а наберите в Гугле «coreboot».

2) «Графического» UEFI с мышкой и кнопками, как у Dell, MSI и прочих, здесь не будет. Это платные среды, для использования в крупных компаниях. Есть, разумеется, энтузиасты, которые сами создают их своими руками, не ответив предварительно на вопрос «Зачем?», но обычно их энтузиазм заканчивается на второй форме с кнопками.

3) Мы будем работать с компилятором Visual Studio. Желающие могут настроить gcc в cygwin, или icc, но в данный момент не стоит задача получить оптимальный быстрый код, а стоит задача быстро пройти путь к началу полноценной работы.
Все, предварительные танцы закончены, кто надо – воодушевлен, кто надо – напуган.

Переходим к делу


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

Итак, у кого на машине есть git в командной строке, выполняют команду в cmd окне (или в Far Commander, не суть) из корневого каталога:
git clone https://github.com/ProgrammingInUEFI/FW
а те, у кого нет, идут по ссылке на github, скачивают zip-файл и раскрывают его в каталог с:/FW.

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



Конфигурирование среды для версии, отличной от VS2010


Открываем файл c:\FW\edk2\Conf\target.txt и в строчке
TOOL_CHAIN_TAG = VS2010x86
Заменяем VS2010x86 на тэг установленной у вас версии Visual Studio. Для Visual Studio 2010 тэг останется как есть, для других версий VS – смотрите картинку выше, или список в начале файла c:\FW\edk2\Conf\tools_def.txt
Собственно, среда разработки edk2 развернута полностью и в ней можно работать из командной строки. Некоторые так и работают всю жизнь («угорать по хардкору, поддерживать дух старой школы и всё такое» — (с) CodeRush в своей ставшей классической статье). Но мы все же пойдем дальше, пересаживать человека из MSVS обратно в командную строку — негуманно, особенно в 2017.

Настраиваем проект в Visual Studio


Открываем Visual Studio, в нем открываем Solution NT32.sln из каталога C:\FW\VS\NT32. В целях уменьшения времени входа в тему, в solution уже создан одноименный проект NT32, в котором уже сделаны описанные ниже настройки. Это если не получится создать самим – чтобы иметь гарантированно рабочие настройки проекта. Такой подход сильно сократит время поиска источника проблем, в случае их появления. Тем не менее, лучше пройти описанный ниже путь самим, и понять смысл настроек – это облегчит настройку следующих проектов.

Небольшой совет для тех, кто загорелся всерьез
Полезно будет сразу в Tools->Options настроить рабочий каталог на c:\FW\VS, но если в VS ведется другой, рабочий проект, то так делать не надо:



Итак, по шагам:

Создание проекта


Создаем в Solution NT32 новый проект для Visual C++ (правой клавишей на Solution NT32, Add->New Project, выбираем опцию Makefile Project), и назовем его MyFirstUEFIProject (или как угодно еще). Жмем Finish.



Выбираем в Solution проект NT32, выбираем из контекстного меню Project->Properties и производим настройки проекта.

Настройка NMake опций


Выбираем в окне слева строку Configurarion Properties ->NMake, в окне справа — строку Build Command Line



Жмем Edit… и в открывшемся текстовом окне вводим:

set NASM_PREFIX=C:\FW\NASM\
call c:\FW\edk2\edksetup.bat --nt32
build

Сейчас стоит немного объяснить, что мы делаем. По сути, мы пишем в этом окне обычный пакетный bat-файл вместо makefile.

В первой строке устанавливается переменная окружения ассемблера NASM_PREFIX в том виде, как ее понимает edk2, то есть путь, по которому лежит файл nasm.exe. На ассемблере мы сами писать не будем, но нашей системе сборки ассемблер нужен обязательно.

Во второй строке вызывается скрипт настройки среды edk2 и настраиваются переменные окружения для данного сеанса компиляции и запуска (вне VS эти переменные не отражаются). Ключ –nt32 указывает системе сборки, что компилировать исходники надо для пакета (package) Nt32Pkg, расположенного в C:\FW\edk2\Nt32Pkg. Этих пакетов там много, мы их рассмотрим, но не сейчас.

В третьей строке мы даем команду на компиляцию в только что настроенной среде (build.exe лежит в C:\FW\edk2\BaseTools\Bin\Win32, этот путь прописывается в предыдущей строчке, в edksetup.bat)

Итак, вот что у нас должно появиться в итоге в текстовом окне Build Command Line:



Затем вводим в следующей строке Rebuild Command Line в открывшееся по Edit … окно следующий текст

set NASM_PREFIX=C:\FW\NASM\
call c:\FW\edk2\edksetup.bat --nt32 
build clean
build

Команда build clean означает то самое, что вы предполагаете. Она делает полную перестройку проекта с перекомпиляцией всех модулей.

Что мы вводим в окне из Clean Command Line, наверное, все уже догадались:

set NASM_PREFIX=C:\FW\NASM\
call  c:\FW\edk2\edksetup.bat --nt32 
build clean

Честно говоря, особо эта опция не нужна, в 99% случаев хватит Rebuild, но пускай будет – например, очистить среду для ее переноса в другое место или заливки на github.
В итоге, у нас должно получиться вот такое окно:



Все с настройкой NMake.

Настройка опции Debugging


Итак, открываем строчку Debugging и вводим:
В строчке Command:

C:\FW\edk2\Build\NT32IA32\DEBUG_VS2010x86\IA32\SecMain.exe

В строчке “Working Directory”:

C:\FW\edk2\Build\NT32IA32\DEBUG_VS2010x86\IA32\

Пара комментариев:
SecMain.exe – объяснять сейчас, что это такое – долго, если очень кратко и упрощенно – то это аналог bootloader-a, который запускает все остальное.
Рабочий каталог – сюда будут помещаться все успешно созданные модули, и доступны они будут все сразу из командной строки.

Итак, вот что мы должны получить после настроек в этом окне:



На этом все с настройками проекта.

Построение проекта


Вызываем Build Solution, смотрим на экран примерно минуту, в течение которой есть наибольший риск быть обруганным компилятором, и идем пить кофе – создаваться это все будет 10-15 мин, в зависимости от ресурсов вашего компьютера. Ничто нудное не вечно, и в конце концов мы получаем сообщение:
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Если же вместо этого получено что-то иное, смотрите, правильно ли вы прошли все шаги. Наиболее тяжелый случай – получить:
LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt
это баг среды VS2010 и означает, что VS2010 установлен без SP1. Поставьте SP1, или ищите способы затыкания этой ошибки в инете.

Если же получили ошибку и из сообщений компилятора не понятно, что это такое – переставьте дефолтный проект на NT32 и запустите его на компиляцию с отладкой. Если и там ошибка – проверьте еще раз соответствие TOOL_CHAIN_TAG предопределенным значениям, описанным в tools_def.txt. Больше ничего там упираться не может, разве что сам Visual Studio установлен, хм, не вполне стандартно, или использует сторонний компилятор.

Работа в UEFI Shell


Итак, все скомпилялось хорошо, и вы читаете эти строки. Теперь жмем на любимую F5 и после примерно минуты работы с диском (чуть позже сократим и это время) получаем вот такую требуемую картинку:



Собственно, это и есть UEFI Shell. Как в нем работать – написана куча руководств, посмотрите в Гугле, а мы пока сделаем в нем несколько вещей.

1. Смотрим, что мы там накомпиляли за эти 10 минут. Вводим fs0 (UEFI Shell нечувствителен к регистру) и затем ls –b, где опция –b означает ожидание нажатия Enter для прокрутки страницы, ибо список там большой, не на один экран.
Теперь стало понятно, что означал параметр “Working Directory” в настройке опций проекта Visual Studio — C:\FW\edk2\Build\NT32IA32\DEBUG_VS2010x86\IA32\. Там этот же самый список файлов, и лучше его смотреть (и редактировать скрипты) через развитую оболочку Far Commander (или Total Commander), чем с командной строки в UEFI Shell.

2. В UEFI Shell и набираем “hel”, жмем Tab и видим на экране Helloworld.efi. Не то, чтобы мы совсем не догадывались, что будет, если нажать после этого Enter, но проверить-то надо! Жмем и получаем троекратное UEFI Hello World!. Число повторений – это конфигурируемый в настройках среды (а не в исходниках) внешний параметр и мы будем эту конфигурацию потом разбирать.

3. Набираем exit и попадаем в наше любимое и знакомое окно:


Ну вот, можно любоваться на плоды своих трудов. После этого стрелками гоним фокус на Reset и виртуалка закрывается, возвращая нас в знакомое окно MSVC.

Выводим свою строку


Создание полностью своего приложения потребует достаточно много настроек, которые лучше будет рассмотреть в следующей статье — иначе получится большой объем, а длинные простыни никто не читает. Однако же в заголовке этой статьи написано «Программирование», а мы пока занимались только настройкой. Чтобы сдержать обещание, давайте в этой статье сделаем очень простую модификацию приложения HelloWorld, используя его имеющийся набор файлов, а в следующей статье создадим свой, при помощи Интеловской утилиты UEFI Driver Wizard, поскольку прогонять начинающих по полному циклу создания набора файлов для UEFI драйвера (или приложения) — это нечеловеколюбиво, дико рутинно и несет риск потери 90% аудитории. Если человека зацепит — он сам к этому придет со временем, а если хочется просто поиграться — нет смысла тратить на это кучу времени, благо многое давно уже делается автоматически через UEFI Driver Wizard, причем по фэн-шуй, чего от новичка ждать наивно.

Итак, открываем в Visual Studio файл
C:\FW\edk2\MdeModulePkg\Application\HelloWorld\HelloWorld.c

И добавим свою простую строчку, сразу после объявления переменных в единственной функции:

Print (L"I did it in UEFI!\r\n");

Разумеется, текст можно заменить на что угодно, только английскими буквами — русский шрифт edk2 еще не понимает, мы добавим его в следующих статьях. Можете поставить на эту строку обычный breakpoint и посмотреть, как себя поведет Visual Studio.
Жмем F5, после компиляции и устранения ошибок вводим «fs0», HelloWorld и получим такой вывод:



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

Что хотелось бы узнать сейчас — как выделение жирным шрифтом влияет на восприятие? Новый материал так воспринимается легче, на мой взгляд — но люди разные.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/338264/


Метки:  

Superjob IT-meetup. Системный бизнес-анализ

Вторник, 19 Сентября 2017 г. 16:54 + в цитатник
matvey_travkin сегодня в 16:54 Разработка

Superjob IT-meetup. Системный бизнес-анализ

    Superjob приглашает на meetup «Системный бизнес-анализ». Встречаемся 12 октября в нашем офисе на Малой Дмитровке.

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

    image

    Что нужно, чтобы запустить новый ИТ-проект или итерацию развития системы?

    • Спонсору — прогноз, сколько стоит система, как и когда окупятся инвестиции.
    • Технической команде — требования для планирования и выполнения работ.
    • Маркетологам — основные фишки для «упаковки» продукта.
    • Конечным потребителям — понятные объяснения, что они получат.


    Все это нужно

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


    Тайминг и спикеры:

    19.00—19.40 — Сергей Нужненко, ведущий аналитик Superjob

    Тема: «Шаблоны сбора, анализа и коммуникации требований в предпроекте»

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

    19.40—20.20 — Денис Бесков, сооснователь и руководитель Школы системного анализа

    Тема: «Типичные ошибки предпроектной стадии и как с ними бороться»

    Расскажет о типичных ошибках предпроектной стадии и смене лексики в индустрии от требований к ценности. Откуда это берётся, зачем это надо, почему теперь мы на стадии предпроекта ищем ценность, а не разрабатываем требования.

    20.20—20.40 — Елена Рожкова, CEO «АЭРОС Систем», в прошлом — бизнес и системный аналитик компаний IBS, Астерос Консалтинг, ТопС Бизнес Интегратор.

    Тема: «Бизнес-аналитика на ранних стадиях проекта»

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

    20.40—21.00 — Спикер уточняется.

    Вход бесплатный, количество мест ограниченно. Зарегистрироваться здесь.

    В день мероприятия возьмите с собой паспорт или водительское удостоверение для прохода в наш бизнес-центр.

    Отчеты с предыдущих митапов можно найти в официальной группе Superjob IT-meetup в Facebook.
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338262/


    Платформа ServiceNow: тематическая подборка материалов для начинающих

    Вторник, 19 Сентября 2017 г. 16:29 + в цитатник
    it-guild сегодня в 16:29 Управление

    Платформа ServiceNow: тематическая подборка материалов для начинающих

      Продолжаем цикл наших дайджестов о том, что такое платформа ServiceNow и «с чем ее едят». Говорим о специфике, функциональных возможностях, новинках и лайфхаках. А еще: об эффективной организации бизнес-процессов и значимости человеческих ресурсов.

      / Flickr / Dean Hochman / CC

      Самые популярные вопросы при внедрении ServiceNow. Краткий ликбез по теме, с которого мы рекомендуем начать «погружение». Здесь мы рассказываем, из чего складывается стоимость платформы, почему использована модель SaaS, как и чем обеспечивается безопасность и автоматизация. Что еще есть в этом FAQ: немного о персонализации и создании отчетов.

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




      Аналитические обзоры


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

      «На полпути»: Пятерка главных новостей компании ServiceNow за 2017 год. Из этой статьи вы узнаете подробности о таких событиях как: новый релиз Jakarta, покупка стартапа DxContinuum, сотрудничество с IBM и MapAnything, проекте Qlue и системах ИИ в ServiceNow.

      ServiceNow Jakarta: обзор новых возможностей. Знакомимся с июльским релизом платформы на практике. В формате, который немного напоминает tutorial, мы прошлись по новому функционалу и рассказали о том, на какие моменты стоит обратить внимание.

      ServiceNow Jakarta: для бизнеса. В этом аналитическом обзоре мы сделали акцент на управленческую составляющую. Как обычно — в формате ознакомительного tutorial'а.

      ServiceNow Communities: новый сервис для повышения лояльности клиентов. Учимся управлять форумами и топиками с помощью Communities. Здесь есть все, что нужно для работы: от модерации до индивидуальных профилей клиентов.

      Практические материалы


      Далее мы приведем видеогайд от Ивана Жигалова, директора компании ИТ Гильдия. Он расскажет об управлении проектами на платформе ServiceNow. Речь пойдет о распределении ролей, карточке и консоли проекта, планировании спринта, отчетах и особенностях управления ресурсами.




      Как создать и начать использовать каталог услуг (Service Catalog). Статья затрагивает проблемы ограниченного и неструктурированного доступа к ИТ-услугам компании. Чтобы помочь вам навсегда забыть о путанице, мы рассматриваем шаблоны описания услуг, поддержку каталога услуг в актуальном состоянии и работу с мнением сотрудников вашей компании.

      Как внедрить процессы управления конфигурацией. Думаем о том, как не растрачивать человеческие ресурсы и деньги впустую, а направлять их в том числе и на оптимизацию бизнес-процессов. Это — один из наших первых материалов на Хабре. Здесь мы разбираемся с оценкой статуса инфраструктуры компании, азами успешного контроля и мониторинга изменений при внедрении процессов, отслеживанием жизненного цикла и основными вопросами для аудита.

      Financial Management: Как оценить затраты на предоставляемые услуги. Затраты на обеспечение ИТ-систем в любой организации занимают отдельные статьи бюджета, но путаница в их структуре препятствует четкому пониманию их воздействия на достижение целей компании. Разберемся в механизмах управления финансами, планировании бюджета и рассмотрим два варианта расчета затрат. Все это на примере набора инструментов ServiceNow.

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





      Немного о человеческих ресурсах и опыте успешных компаний


      Простой способ сделать самообслуживание популярнее. Как сделать так, чтобы как можно больше ваших сотрудников пользовались порталом самообслуживания для решения своих проблем? Почему они любят прикидываться «блондинками», как провести для них грамотный ликбез или «эффект ИКЕА в действии».

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

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

      «Вредные советы»: Как потерять клиента или 7 лучших прощальных фраз. Рассмотрим работу отдела технической поддержки и способы замены клишированных фраз при общении с клиентом. Разберемся, как вести себя в непростых ситуациях и как убедить клиента в том, что «решение его проблемы возможно, и мы непременно его найдем», даже если решения у вас пока нет.

      Что еще почитать по теме


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

      Знакомство с ServiceNow и управлением ИТ-инфраструктурой: Дайджест #2. Наш второй дайджест продолжает логику первого — мы стараемся дать практические материалы и не забывать о теории. Здесь вы найдете статьи об управлении инфраструктурой и разработкой, вместе с этим — разберетесь со стандартами и мифами, которые окружают тему управления услугами.

      «Горшочек, вари»: 50 инструментов для управления разработкой. Мы постарались собрать максимум полезных материалов, чтобы немного разгрузить разработчиков от повседневной рутины. Здесь мы сделали ставку на системы автоматизации, инструменты для управления проектами, прототипирования и тестирования.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/338236/


      Метки:  

      Рекомендации по нейтрализации угроз, связанных с уязвимостью CVE-2017-8759

      Вторник, 19 Сентября 2017 г. 16:29 + в цитатник
      infosecurity_iz сегодня в 16:29 Администрирование

      Рекомендации по нейтрализации угроз, связанных с уязвимостью CVE-2017-8759

        Компания «Информзащита» рекомендует всем организациям принять необходимые меры для защиты своих ресурсов и обеспечения информационной безопасности.

        Общее описание угрозы


        12 сентября 2017 года была опубликована информация об уязвимости CVE-2017-8759, которая связана с удаленным выполнением кода с использованием документов Microsoft Office.
        Уязвимость была обнаружена компанией FireEye вследствие фиксации ряда атак с использованием ранее неизвестного эксплойта.

        Уязвимости CVE-2017-8759 подвержены операционные системы семейства Microsoft Windows. Эксплуатация данной уязвимости с внедрением стороннего кода производится с помощью уязвимого компонента Microsoft.NET Framework – SOAP WSDL Parser, позволяет получить пользовательские права на уязвимой системе.

        Данная уязвимость имеет высокую степень критичности по ряду причин:

        1) Эксплуатация уязвимости не требует использования и включения макросов в офисных приложениях Microsoft Office;

        2) Для эксплуатации уязвимости пользователю достаточно открыть вредоносный файл;

        3) Код эксполойта для данной уязвимости опубликован в открытом доступе в сети Интернет и это означает, что данной уязвимостью может воспользоваться любой пользователь сети.

        Наименование угрозы: CVE-2017-8759 «.NET Framework Remote Code Execution Vulnerability».

        Возможные векторы атаки: рассылка целевого фишинга с вложением документа Microsoft Office (doc, rtf и др.).

        Уязвимые системы: операционные системы семейства Microsoft Windows от Windows 7 до последних версий, компонент Microsoft .NET Framework от версии 2.0 до 4.72. Полный перечень уязвимых ОС и компонентов представлен по ссылке.

        Масштаб распространения угрозы: на данный момент зафиксирована одна фишинговая кампания, использующая эксплойт для уязвимости CVE-2017-8759 для установки троянской программы FINSPY (FinFisher).

        Превентивные меры


        Для снижения рисков, связанных с уязвимостью CVE-2017-8759, рекомендуется:

        1. Установить обновление безопасности, соответствующее версии операционной системы и версии Microsoft .NET Framework. Полный перечень уязвимых ОС и компонентов представлен по ссылке;

        2. Обновить базы сигнатур установленного антивирусного ПО;

        3. Обновить базы сигнатуры IPS, защищенных почтовых и веб-шлюзов (ESG, WSG) и других сигнатурных СЗИ.

        Примечание: по ссылке представлены идентификаторы полных обновлений, в то время как для систем до Windows 8.1 и Windows Server 2012 R2 существует возможность установить только обновления безопасности – столбец Security Only Release в оригинальной таблице Microsoft.

        Также необходимо обновить базы сигнатур установленного антивирусного ПО в целях предотвращения заражения FINSPY.

        Описание уязвимости


        Уязвимость удаленного выполнения кода, зарегистрированная с идентификатором CVE-2017-8759, обнаружена экспертами компании FireEye в модуле WSDL Parser (Web Services Description Language) в Microsoft.NET Framework. Метод модуля IsValidUrl не осуществляет корректную проверку переданных значений на предмет наличия в них последовательности CRLF (перенос строки и каретки). Таким образом атакующий, используя данную последовательность имеет возможность внедрить вредоносный код в процесс выполнения модуля.


        Рисунок 1. Пример использования уязвимости

        Индикаторы компрометации


        Известные на текущий момент хэш-суммы (MD5) вредоносных файлов:

        — вредоносный файл «Проект.doc»: fe5c4d6bb78e170abf5cf3741868ea4c;

        — FINSPY: a7b990d5f57b244dd17e9a937a41e7f5.

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

        Автор: Техническая дирекция компании «Информзащита»
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/338256/


        Метки:  

        [Из песочницы] learnopengl. Урок 2.6 — Несколько источников освещения

        Вторник, 19 Сентября 2017 г. 15:41 + в цитатник
        dima19972525 сегодня в 15:41 Разработка

        learnopengl. Урок 2.6 — Несколько источников освещения

        OGL3

        Несколько источников освещения


        В предыдущих уроках мы выучили довольно много об освещении в OpenGL. Мы познакомились с моделью освещения по Фонгу, разобрались как работать с материалами, текстурными картами и различными типами источника света. В этом уроке мы собираемся объединить все наши знания, чтобы создать полностью освещенную сцену с 6 активными источниками света. Мы собираемся симулировать солнце как направленный источник освещения, добавим 4 точки света, разбросанные по всей сцене, и конечно мы добавим фонарик.

        В предыдущих сериях

        Часть 1. Начало

        1. OpenGL
        2. Создание окна
        3. Hello Window
        4. Hello Triangle
        5. Shaders
        6. Текстуры
        7. Трансформации
        8. Системы координат
        9. Камера

        Часть 2. Базовое освещение

        1. Цвета
        2. Основы освещения
        3. Материалы
        4. Текстурные карты
        5. Источники света

        Чтобы использовать более чем 1 источник света в сцене, нам следует разбить расчеты типов освещения на функции. Если мы добавим расчеты всех освещений в одну функцию main, то наш код быстро испортится.

        Функции в GLSL такие-же как и функции в языке C. У нас есть имя функции, возвращаемый тип и также мы можем объявить её прототип сверху, а описать её снизу. Мы создадим разные функции для каждого типа освещения: направленного источника освещения, точечного источника и прожектора.

        При использовании нескольких источников освещение в сцене, подход обычно такой: у нас есть 1 вектор цвета, который представляет выходной цвет фрагмента. Результат вычисления каждого источника света добавляется к этому вектору. То есть мы будем вычислять каждый цвет фрагмента, относительно каждого источника света в сцене, и комбинировать его с выходным цветом. Сама функция main может выглядеть примерно так:

        out vec4 FragColor;

        void main()
        {
        // устанавливаем значение нашего выходного цвета
        vec3 output = vec3(0.0);
        // добавляем значение, полученное из направленного источника освещения
        output += someFunctionToCalculateDirectionalLight();
        // делаем тоже самое и с точечным источником
        for(int i = 0; i < nr_of_point_lights; i++)
        output += someFunctionToCalculatePointLight();
        // и добавляем остальные значения так же
        output += someFunctionToCalculateSpotLight();

        FragColor = vec4(output, 1.0);
        }


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

        Направленный источник освещения


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

        Первое, что нам нужно, это решить, какой минимальный набор переменных нам нужен для вычисления направленного источника света. Мы будем хранить переменные в структуре DirLight и объявим её объект как uniform. Эти переменные должны быть вам хорошо знакомы с
        предыдущего урока:

        struct DirLight {
        vec3 direction;

        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
        };
        uniform DirLight dirLight;


        Мы можем передать наш объект dirLight в функцию со следующим прототипом:

        vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);

        Также как в C и C++, если мы хотим вызвать функцию (в нашем случае внутри функции main) функция должна быть объявлена где-нибудь до того момента, где мы её вызываем. В нашем случае, мы объявим прототип над функцией main, а опишем её где нибудь ниже.
        Вы можете видеть, что функция требует DirLight структуру и 2 вектора. Если вы успешно завершили предыдущие уроки, тогда код этой функции не должен вызывать для вас вопросов:

        vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
        {
        vec3 lightDir = normalize(-light.direction);
        // диффузное освещение
        float diff = max(dot(normal, lightDir), 0.0);
        // освещение зеркальных бликов
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        // комбинируем результаты
        vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
        vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
        vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
        return (ambient + diffuse + specular);
        }


        Пользуясь примером кода с предыдущего урока и используя вектора, которые принимает функция в качестве аргументов, вычисляем результат каждого компонента (ambient, diffuse и specular). Затем суммируем наши компоненты и получаем конечный цвет фрагмента.

        Точечные источники освещения


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

        struct PointLight {
        vec3 position;

        float constant;
        float linear;
        float quadratic;

        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
        };
        #define NR_POINT_LIGHTS 4
        uniform PointLight pointLights[NR_POINT_LIGHTS];


        Как вы можете видеть мы использовали препроцессор GLSL, что-бы объявить число точечных источников NR_POINT_LIGHTS равное 4. Мы используем эту константу NR_POINT_LIGHTS, чтобы создать объект массив структуры PointLight. Массивы в GLSL такие же как и массивы в C и
        могут быть созданы с использованием двух квадратных скобок. Прямо сейчас у нас есть 4 объекта pointLights[NR_POINT_LIGHTS] структуры PointLigh.
        Мы могли также создать одну большую структуру, которая включала бы все нужные переменные для всех разных типов освещения, и использовали бы её для каждой функции, игнорируя переменные в которых бы мы не нуждались. Хотя, я лично нахожу нынешний подход более лучшим, т.к. не всем типам освещения будут нужны все переменные.
        Прототип функции точечного источника:

        vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);

        Функция принимает все данные, в которых она нуждается и возвращает vec3 с вычисленным цветом фрагмента. Снова, несколько манипуляций копировать-вставить из предыдущего урока, приводит к следующему результату:

        vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
        {
        vec3 lightDir = normalize(light.position - fragPos);
        // диффузное освещение
        float diff = max(dot(normal, lightDir), 0.0);
        // освещение зеркальных бликов
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
        // затухание
        float distance = length(light.position - fragPos);
        float attenuation = 1.0 / (light.constant + light.linear * distance +
        light.quadratic * (distance * distance));
        // комбинируем результаты
        vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
        vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
        vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
        ambient *= attenuation;
        diffuse *= attenuation;
        specular *= attenuation;
        return (ambient + diffuse + specular);
        }


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

        Объединяем всё вместе


        Теперь, когда мы написали наши функции для направленного и точечного источника освещения, мы можем вызвать их в функции main.

        void main()
        {
        // свойства
        vec3 norm = normalize(Normal);
        vec3 viewDir = normalize(viewPos - FragPos);

        // фаза 1: Направленный источник освещения
        vec3 result = CalcDirLight(dirLight, norm, viewDir);
        // фаза 2: Точечные источники
        for(int i = 0; i < NR_POINT_LIGHTS; i++)
        result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
        // фаза 3: фонарик
        //result += CalcSpotLight(spotLight, norm, FragPos, viewDir);

        FragColor = vec4(result, 1.0);
        }


        Каждый тип освещения добавляет свой вклад в выходной вектор цвета, до тех пор, пока не будут обработаны все источники света. Выходной вектор цвета содержит все вычисления источников света в сцене. Если вы хотите, вы можете также добавить сами функцию прожектора, опираясь на код с предыдущего урока. Мы оставим функцию CalcSpotLight в качестве упражнения для читателей.

        Установка значений uniform переменным не должна быть для вас незнакомой, но вы можете быть удивлены как мы можем установить uniform переменные для объекта структуры точечного освещения.

        Удачно для нас, что это не слишком сложно. Чтобы задать значение конкретному объекту uniform массива, нужно лишь обратиться к этому объекту, как к обычному массиву (через индекс).

        lightingShader.setFloat("pointLights[0].constant", 1.0f);

        Здесь мы обращаемся к 1 элементу нашего массива pointLights и устанавливаем значение 1.0f переменной constant. К сожалению это значит, что мы должны таким-же способом установить все переменные, всем элементам массива, что в итоге приведет к 28 строка кода. Вы можете попытаться, написать боле удобный код для этой задачи.

        Не забываем, что мы также нуждаемся в position векторе, для каждого точечного источника освещения. Мы определим массив glm::vec3, который будет хранить эти позиции:

        glm::vec3 pointLightPositions[] = {
        glm::vec3( 0.7f, 0.2f, 2.0f),
        glm::vec3( 2.3f, -3.3f, -4.0f),
        glm::vec3(-4.0f, 2.0f, -12.0f),
        glm::vec3( 0.0f, 0.0f, -3.0f)
        };


        Далее, мы просто проиндексируемся по этому массиву и установим значения position для каждого объекта массива pointLight. Также, нам нужно нарисовать 4 световых куба, вместо 1. Простой способ сделать это, передавать разные значения матрицы модели, используя наш только что созданный массив pointLightPositions.

        Если вы будите использовать фонарик, то наша сцена будет выглядеть примерно так:

        image

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

        Вы можете найти полный код здесь.

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

        image

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

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

        Задания


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

        -> Оригинал статьи
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/338254/


        Метки:  

        It's a (focus) Trap

        Вторник, 19 Сентября 2017 г. 15:33 + в цитатник

        Метки:  

        Sberbank In-Memory Computing Day

        Вторник, 19 Сентября 2017 г. 15:14 + в цитатник
        8 октября в Москве Сбербанк проведет мероприятие Sberbank InMemory Computing Day. Это уникальный форум для популяризации IMDG технологий (In-Memory Data Grid), в котором примут участие лидеры компаний мирового уровня: IBM, Boeing, Intel, SAP, Mail.Ru Group, EPAM и другие. В программе запланированы выступления спикеров ИТ-индустрии и мастер классы по четырем направлениям: IMDG, DevOps, Artificial Intelligence (AI), Internet of Things (IoT). Также компании-партнеры банка представят на выставочной площадке форума инновационные решения по разработке в области IoT и AI. читать далее

        https://habrahabr.ru/post/338234/


        Метки:  

        Трансляция конференции «HPE Genesis: инфраструктура вычислений будущего»

        Вторник, 19 Сентября 2017 г. 15:06 + в цитатник
        tonyafilonenko сегодня в 15:06 Администрирование

        Трансляция конференции «HPE Genesis: инфраструктура вычислений будущего»

          Что делает новое поколение серверов HPE ProLiant Gen10 уникальным? Какие новые серверные опции повышают производительность и безопасность? Что нового в HPE BladeSystem c-Class и Synergy? Зачем суперкомпьютер HPE Spaceborne отправился на МКС? Как проект The Machine совершает революцию в вычислениях?

          Ответы на эти вопросы вы узнаете на трансляции конференции «HPE Genesis: инфраструктура вычислений будущего», которую компания Hewlett Packard Enterprise проводит в партнерстве с компанией Intel завтра, 20 сентября. Расписание трансляции ниже. Подключайтесь — будет интересно!







          Расписание трансляции (по МСК):

          10:00 — 10:05 Открытие мероприятия, приветственное слово

          10:05 — 10:25 Стратегия Hewlett Packard Enterprise

          10:25 — 10:45 Новые возможности с масштабируемыми процессорами Intel Xeon

          10:45 — 11:15 HPE ProLiant Gen10 — самые производительные и безопасные серверы

          11:15 — 11:45 Серверы нового поколения для HPE BladeSystem c-Class и Synergy

          11:45 — 12:00 — Перерыв

          12:00 — 12:20 Серверные опции для повышения производительности, надежности и эффективности

          12:20 — 12:40 Управление серверной инфраструктурой c iLO5 и OneView

          12:40 — 13:00 HPE SimpliVity 380 — наиболее полная гиперконвергентная платформа на рынке

          13:00 — 13:20 Системы хранения данных HPE

          13:20 — 13:40 Практика применения высокопроизводительных вычислений HPE: искусственный интеллект и и вычисления на МКС.

          13:40 — 14:00 The Machine — вычисления, ориентированные на память

          Ждем вас на трансляции! Материалы презентаций будут доступны в нашем канале Telegram: t.me/hpedigitize.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338218/


          [Перевод] Создаём GTK-видеоплеер с использованием Haskell

          Вторник, 19 Сентября 2017 г. 15:04 + в цитатник
          AloneCoder сегодня в 15:04 Разработка

          Создаём GTK-видеоплеер с использованием Haskell

          • Перевод


          Когда мы в последний раз остановились на Movie Monad, мы создали десктопный видео-плеер, использующий все веб-технологии (HTML, CSS, JavaScript и Electron). Фокус был в том, что весь исходный код проекта был написан на Haskell.


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


          Мы могли бы продолжить развивать наш подход с вебом, настроив бэкенд на стриминг видеофайла в HTML5-сервер, запустив параллельно сервер и Electron-приложение. Вместо этого мы откажемся от веб-технологий и обратимся к GTK+, Gstreamer и системе управления окнами X11.


          image


          Если вы используете другую систему управления окнами, например, Wayland, Quartz или WinAPI, то этот подход может быть адаптирован для работы с вашим GDK-бэкендом. Адаптация заключается во встраивании выходного видеосигнала GStreamer playbin в окно Movie Monad.


          GDK — важный аспект портируемости GTK+. Поскольку Glib уже предоставляет низкоуровневую кроссплатформенную функциональность, то чтобы заставить GTK+ работать на других платформах вам нужно только портировать GDK на базовый графический уровень операционной системы. То есть именно GDK-порты на Windows API и Quartz позволяют приложениям GTK+ исполняться на Windows и macOS (источник).


          Для кого эта статья


          • Для программистов на Haskell, которые хотят реализовать пользовательский интерфейс на GTK+.
          • Для программистов, интересующихся функциональным программированием.
          • Для создателей GUI.
          • Для тех, кто ищет альтернативы GitHub Electron.
          • Для фанатов видео-плееров.

          Что мы рассмотрим


          • Stack.
          • Привязки (bindings) haskell-gi
          • Директорию различных данных и файлы с ними.
          • Glade.
          • GTK+.
          • GStreamer.
          • Как создать Movie Monad.

          Настройка проекта


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


          Платформа Haskell


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


          Stack


          Если у вас ещё нет Stack, то обязательно установите его, прежде чем приступать к разработке. Но если вы уже пользовались платформой Haskell, то Stack у вас уже есть.


          ExifTool


          Прежде чем проигрывать видео в Movie Monad, нам нужно собрать кое-какую информацию о выбранном пользователем файле. Для этого воспользуемся ExifTool. Если вы работаете под Linux, то велик шанс, что у вас уже есть этот инструмент (which exiftool). ExifTool доступен для Windows, Mac и Linux.


          Файлы проекта


          Есть три способа получения файлов проекта.


          wget https://github.com/lettier/movie-monad/archive/master.zip
          unzip master.zip
          mv movie-monad-master movie-monad
          cd movie-monad/

          Можете скачать ZIP-архив и извлечь их.


          git clone git@github.com:lettier/movie-monad.git
          cd movie-monad/

          Можете сделать Git-клон с помощью SSH.


          git clone https://github.com/lettier/movie-monad.git
          cd movie-monad/

          Можете склонировать git через HTTPS.


          haskell-gi


          haskell-gi умеет генерировать Haskell-привязки (bindings) к библиотекам, использующим связующее ПО для самодиагностики (introspection middleware) GObject. На момент написания статьи все необходимые привязки доступны на Hackage.


          Зависимости


          Теперь устанавливаем зависимости проекта.


          cd movie-monad/
          stack install --dependencies-only

          Код


          Теперь настраиваем внедрение Movie Monad. Вы можете удалить исходные файлы и создать их заново, или следовать указаниям.


          Paths_movie_monad.hs


          Paths_movie_monad.hs используется для поиска файла Glade XML GUI во время runtime. Поскольку мы занимаемся разработкой, то будем использовать фиктивный модуль (dummy module) (movie-monad/src/dev/Paths_movie_monad.hs) для поиска файла movie-monad/src/data/gui.glade. После сборки/установки проекта реальный модуль Paths_movie_monad будет сгенерирован автоматически. Он предоставит нам функцию getDataFileName. Она присваивает своим выходным данным префикс в виде абсолютного пути, куда скопированы или установлены data-dir (movie-monad/src/) data-files.


          {-# LANGUAGE OverloadedStrings #-}
          
          module Paths_movie_monad where
          
          dataDir :: String
          dataDir = "./src/"
          
          getDataFileName :: FilePath -> IO FilePath
          getDataFileName a = do
            putStrLn "You are using a fake Paths_movie_monad."
            return (dataDir ++ "/" ++ a)

          Фиктивный модуль Paths_movie_monad.


          {-# LANGUAGE CPP #-}
          {-# OPTIONS_GHC -fno-warn-missing-import-lists #-}
          {-# OPTIONS_GHC -fno-warn-implicit-prelude #-}
          module Paths_movie_monad (
              version,
              getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir,
              getDataFileName, getSysconfDir
            ) where
          
          import qualified Control.Exception as Exception
          import Data.Version (Version(..))
          import System.Environment (getEnv)
          import Prelude
          
          #if defined(VERSION_base)
          
          #if MIN_VERSION_base(4,0,0)
          catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
          #else
          catchIO :: IO a -> (Exception.Exception -> IO a) -> IO a
          #endif
          
          #else
          catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
          #endif
          catchIO = Exception.catch
          
          version :: Version
          version = Version [0,0,0,0] []
          bindir, libdir, dynlibdir, datadir, libexecdir, sysconfdir :: FilePath
          
          bindir     = "/home//.stack-work/install/x86_64-linux-nopie/lts-9.1/8.0.2/bin"
          libdir     = "/home//.stack-work/install/x86_64-linux-nopie/lts-9.1/8.0.2/lib/x86_64-linux-ghc-8.0.2/movie-monad-0.0.0.0"
          dynlibdir  = "/home//.stack-work/install/x86_64-linux-nopie/lts-9.1/8.0.2/lib/x86_64-linux-ghc-8.0.2"
          datadir    = "/home//.stack-work/install/x86_64-linux-nopie/lts-9.1/8.0.2/share/x86_64-linux-ghc-8.0.2/movie-monad-0.0.0.0"
          libexecdir = "/home//.stack-work/install/x86_64-linux-nopie/lts-9.1/8.0.2/libexec"
          sysconfdir = "/home//.stack-work/install/x86_64-linux-nopie/lts-9.1/8.0.2/etc"
          
          getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath
          getBinDir = catchIO (getEnv "movie_monad_bindir") (\_ -> return bindir)
          getLibDir = catchIO (getEnv "movie_monad_libdir") (\_ -> return libdir)
          getDynLibDir = catchIO (getEnv "movie_monad_dynlibdir") (\_ -> return dynlibdir)
          getDataDir = catchIO (getEnv "movie_monad_datadir") (\_ -> return datadir)
          getLibexecDir = catchIO (getEnv "movie_monad_libexecdir") (\_ -> return libexecdir)
          getSysconfDir = catchIO (getEnv "movie_monad_sysconfdir") (\_ -> return sysconfdir)
          
          getDataFileName :: FilePath -> IO FilePath
          getDataFileName name = do
            dir <- getDataDir
            return (dir ++ "/" ++ name)

          Автоматически сгенерированный модуль Paths_movie_monad.


          Main.hs


          Main.hs — это входная точка для Movie Monad. В этом файле мы настраиваем наше окно с разными виджетами, подключаем GStreamer, а когда пользователь выходит, мы сносим окно.


          Прагмы (Pragmas)


          Нам нужно сказать компилятору (GHC), что нам нужны перегруженные (overloaded) строковые и лексически входящие в область видимости (lexically scoped) переменные типов.


          OverloadedStrings позволяет нам использовать строковые литералы ("Literal") там, где требуются String/[Char] или Text. ScopedTypeVariables позволяет нам использовать сигнатуру типа в паттерне параметра лямбда-функции, передаваемую для перехвата при вызове ExifTool.


          {-# LANGUAGE OverloadedStrings #-}
          {-# LANGUAGE ScopedTypeVariables #-}

          Импорты


          module Main where
          
          import Prelude
          import Foreign.C.Types
          import System.Process
          import System.Exit
          import Control.Monad
          import Control.Exception
          import Text.Read
          import Data.IORef
          import Data.Maybe
          import Data.Int
          import Data.Text
          import Data.GI.Base
          import Data.GI.Base.Signals
          import Data.GI.Base.Properties
          import GI.GLib
          import GI.GObject
          import qualified GI.Gtk
          import GI.Gst
          import GI.GstVideo
          import GI.Gdk
          import GI.GdkX11
          import Paths_movie_monad

          Поскольку мы работает с привязками Си, нам понадобится работать с типами, уже существующими в этом языке. Немалую часть импортов составляют привязки, генерируемые haskell-gi.


          IsVideoOverlay


          GStreamer-видеопривязки (gi-gstvideo) содержат класс типа (интерфейс) IsVideoOverlay. GStreamer-привязки (gi-gst) содержат тип элемента. Чтобы использовать элемент playbin с функцией GI.GstVideo.videoOverlaySetWindowHandle, нам нужно объявить GI.Gst.Element — экземпляр типа (type instance) IsVideoOverlay. А на стороне Cи playbin реализует интерфейс VideoOverlay.


          newtype GstElement = GstElement GI.Gst.Element
          instance GI.GstVideo.IsVideoOverlay GstElement

          Обратите внимание, что мы обёртываем GI.Gst.Element в новый тип (newtype), чтобы избежать появления потерянного (orphaned) экземпляра, поскольку мы объявляем экземпляр вне привязок haskell-gi.


          main


          Main — наша самая большая функция. В ней мы инициализируем все GUI-виджеты и определяем коллбэк-процедуры на основе определённых событий.


          main :: IO ()
          main = do

          GI-инициализация


            _ <- GI.Gst.init Nothing
            _ <- GI.Gtk.init Nothing

          Здесь мы инициализировали GStreamer и GTK+.


          Сборка GUI-виджетов


            gladeFile <- getDataFileName "data/gui.glade"
            builder <- GI.Gtk.builderNewFromFile (pack gladeFile)
          
            window <- builderGetObject GI.Gtk.Window builder "window"
            fileChooserButton <- builderGetObject GI.Gtk.FileChooserButton builder "file-chooser-button"
            drawingArea <- builderGetObject GI.Gtk.Widget builder "drawing-area"
            seekScale <- builderGetObject GI.Gtk.Scale builder "seek-scale"
            onOffSwitch <- builderGetObject GI.Gtk.Switch builder "on-off-switch"
            volumeButton <- builderGetObject GI.Gtk.VolumeButton builder "volume-button"
            desiredVideoWidthComboBox <- builderGetObject GI.Gtk.ComboBoxText builder "desired-video-width-combo-box"
            fullscreenButton <- builderGetObject GI.Gtk.Button builder "fullscreen-button"
            errorMessageDialog <- builderGetObject GI.Gtk.MessageDialog builder "error-message-dialog"
            aboutButton <- builderGetObject GI.Gtk.Button builder "about-button"
            aboutDialog <- builderGetObject GI.Gtk.AboutDialog builder "about-dialog"

          Как уже было сказано, мы получаем абсолютный путь к XML-файлу data/gui.glade, который описывает все наши GUI-виджеты. Дальше создаём из этого файла конструктор и получаем свои виджеты. Если бы мы не использовали Glade, то их пришлось бы создавать вручную, что довольно утомительно.


          Playbin


            playbin <- fromJust <$> GI.Gst.elementFactoryMake "playbin" (Just "MultimediaPlayer")

          Здесь мы создаём GStreamer-конвейер playbin. Он предназначен для решения самых разных нужд и экономит нам время на создании собственного конвейера. Назовём этот элемент MultimediaPlayer.


          Встраиванние выходных данных GStreamer


          Чтобы GTK+ и GStreamer заработали вместе, нам нужно сказать GStreamer, куда именно нужно выводить видео. Если этого не сделать, то GStreamer создаст собственное окно, поскольку мы используем playbin.


            _ <- GI.Gtk.onWidgetRealize drawingArea $ onDrawingAreaRealize drawingArea playbin fullscreenButton
          
          -- ...
          
          onDrawingAreaRealize ::
            GI.Gtk.Widget ->
            GI.Gst.Element ->
            GI.Gtk.Button ->
            GI.Gtk.WidgetRealizeCallback
          onDrawingAreaRealize drawingArea playbin fullscreenButton = do
            gdkWindow <- fromJust <$> GI.Gtk.widgetGetWindow drawingArea
            x11Window <- GI.Gtk.unsafeCastTo GI.GdkX11.X11Window gdkWindow
          
            xid <- GI.GdkX11.x11WindowGetXid x11Window
            let xid' = fromIntegral xid :: CUIntPtr
          
            GI.GstVideo.videoOverlaySetWindowHandle (GstElement playbin) xid'
          
            GI.Gtk.widgetHide fullscreenButton

          Здесь вы видите настройку коллбэка по мере готовности виджета drawingArea. Именно в этом виджете GStreamer должен показывать видео. Мы получаем родительское GDK-окно для виджета области отрисовки. Затем получаем обработчик окна, или XID системы X11 нашего окна GTK+. Строка CUIntPtr преобразует ID из CULong в CUIntPtr, необходимый для videoOverlaySetWindowHandle. Получив правильный тип, мы уведомляем GStreamer, что с помощью обработчика xid' он может отрисовывать в нашем окне выходные данные playbin.


          Из-за бага в Glade мы программно скрываем полноэкранный виджет, поскольку если в Glade снять галочку visible box, то виджет всё-равно не будет спрятан.


          Обратите внимание, что здесь нужно адаптировать Movie Monad для работы с оконной системой, если вы используете не Х-систему, а какую-то другую.


          Выбор файла


            _ <- GI.Gtk.onFileChooserButtonFileSet fileChooserButton $
              onFileChooserButtonFileSet
                playbin
                fileChooserButton
                volumeButton
                isWindowFullScreenRef
                desiredVideoWidthComboBox
                onOffSwitch
                fullscreenButton
                drawingArea
                window
                errorMessageDialog
          
          -- ...
          
          onFileChooserButtonFileSet ::
            GI.Gst.Element ->
            GI.Gtk.FileChooserButton ->
            GI.Gtk.VolumeButton ->
            IORef Bool ->
            GI.Gtk.ComboBoxText ->
            GI.Gtk.Switch ->
            GI.Gtk.Button ->
            GI.Gtk.Widget ->
            GI.Gtk.Window ->
            GI.Gtk.MessageDialog ->
            GI.Gtk.FileChooserButtonFileSetCallback
          onFileChooserButtonFileSet
            playbin
            fileChooserButton
            volumeButton
            isWindowFullScreenRef
            desiredVideoWidthComboBox
            onOffSwitch
            fullscreenButton
            drawingArea
            window
            errorMessageDialog
            = do
            _ <- GI.Gst.elementSetState playbin GI.Gst.StateNull
          
            filename <- fromJust <$> GI.Gtk.fileChooserGetFilename fileChooserButton
          
            setPlaybinUriAndVolume playbin filename volumeButton
          
            isWindowFullScreen <- readIORef isWindowFullScreenRef
          
            desiredVideoWidth <- getDesiredVideoWidth desiredVideoWidthComboBox
            maybeWindowSize <- getWindowSize desiredVideoWidth filename
          
            case maybeWindowSize of
              Nothing -> do
                _ <- GI.Gst.elementSetState playbin GI.Gst.StatePaused
                GI.Gtk.windowUnfullscreen window
                GI.Gtk.switchSetActive onOffSwitch False
                GI.Gtk.widgetHide fullscreenButton
                GI.Gtk.widgetShow desiredVideoWidthComboBox
                resetWindowSize desiredVideoWidth fileChooserButton drawingArea window
                _ <- GI.Gtk.onDialogResponse errorMessageDialog (\ _ -> GI.Gtk.widgetHide errorMessageDialog)
                void $ GI.Gtk.dialogRun errorMessageDialog
              Just (width, height) -> do
                _ <- GI.Gst.elementSetState playbin GI.Gst.StatePlaying
                GI.Gtk.switchSetActive onOffSwitch True
                GI.Gtk.widgetShow fullscreenButton
                unless isWindowFullScreen $ setWindowSize width height fileChooserButton drawingArea window

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


          • Получаем имя файла из виджета выбора файла.
          • Говорим playbin, какой файл он должен воспроизвести.
          • Делаем уровень громкомсти в виджете таким же, как в playbin.
          • На основе желаемой ширины изображения и размера видео определяем подходящие ширину и высоту окна.
          • Если размеры окна успешно получены:
            • Начинаем воспроизведение файла.
            • Переводим кнопку пауза/воспроизведение в состояние ”on”.
            • Показываем полноэкранный виджет.
            • Если видео не в полноэкранном режиме:
            • Меняем размер окна, чтобы оно совпало с относительным размером видео.
          • Если не удалось получить размеры окна:
            • Ставим playbin на паузу.
            • Переводим переключатель в положение ”off”.
            • Если это возможно, выводим окно из полноэкранного режима.
            • Сбрасываем размер окна.
            • Выводим маленькое диалоговое сообщение об ошибке.

          Пауза и воспроизведение


            _ <- GI.Gtk.onSwitchStateSet onOffSwitch (onSwitchStateSet playbin)
          
          -- ...
          
          onSwitchStateSet ::
            GI.Gst.Element ->
            Bool ->
            IO Bool
          onSwitchStateSet playbin switchOn = do
            if switchOn
              then void $ GI.Gst.elementSetState playbin GI.Gst.StatePlaying
              else void $ GI.Gst.elementSetState playbin GI.Gst.StatePaused
            return switchOn

          Всё просто. Если переключатель в положении ”on”, то задаём элементу playbin состояние воспроизведения. В противном случае задаём ему состояние паузы.


          Настройка громкости


            _ <- GI.Gtk.onScaleButtonValueChanged volumeButton (onScaleButtonValueChanged playbin)
          
          -- ...
          
          onScaleButtonValueChanged ::
            GI.Gst.Element ->
            Double ->
            IO ()
          onScaleButtonValueChanged playbin volume =
              void $ Data.GI.Base.Properties.setObjectPropertyDouble playbin "volume" volume

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


          Перемещение по видео


            seekScaleHandlerId <- GI.Gtk.onRangeValueChanged seekScale (onRangeValueChanged playbin seekScale)
          
          -- ...
          
          onRangeValueChanged ::
            GI.Gst.Element ->
            GI.Gtk.Scale ->
            IO ()
          onRangeValueChanged playbin seekScale = do
            (couldQueryDuration, duration) <- GI.Gst.elementQueryDuration playbin GI.Gst.FormatTime
          
            when couldQueryDuration $ do
              percentage' <- GI.Gtk.rangeGetValue seekScale
              let percentage = percentage' / 100.0
              let position = fromIntegral (round ((fromIntegral duration :: Double) * percentage) :: Int) :: Int64
              void $ GI.Gst.elementSeekSimple playbin GI.Gst.FormatTime [ GI.Gst.SeekFlagsFlush ] position

          В Movie Monad есть шкала воспроизведения, в которой вы можете перемещать ползунок вперёд/назад, тем самым переходя по видеофреймам.


          Шкала от 0 до 100% представляет общую длительность видео-файла. Если переместить ползунок, например, на 50, то мы перейдём к временной отметке, находящийся посередине между началом и окончанием. Можно было бы настроить шкалу от нуля до значения длительности видео, но описанный метод более универсален.


          Обратите внимание, что для этого коллбэка мы используем сигнальный ID (seekScaleHandlerId), поскольку он понадобится нам позднее.


          Обновление шкалы воспроизведения


            _ <- GI.GLib.timeoutAddSeconds GI.GLib.PRIORITY_DEFAULT 1 (updateSeekScale playbin seekScale seekScaleHandlerId)
          
          -- ...
          
          updateSeekScale ::
            GI.Gst.Element ->
            GI.Gtk.Scale ->
            Data.GI.Base.Signals.SignalHandlerId ->
            IO Bool
          updateSeekScale playbin seekScale seekScaleHandlerId = do
            (couldQueryDuration, duration) <- GI.Gst.elementQueryDuration playbin GI.Gst.FormatTime
            (couldQueryPosition, position) <- GI.Gst.elementQueryPosition playbin GI.Gst.FormatTime
          
            let percentage =
                  if couldQueryDuration && couldQueryPosition && duration > 0
                    then 100.0 * (fromIntegral position / fromIntegral duration :: Double)
                    else 0.0
          
            GI.GObject.signalHandlerBlock seekScale seekScaleHandlerId
            GI.Gtk.rangeSetValue seekScale percentage
            GI.GObject.signalHandlerUnblock seekScale seekScaleHandlerId
          
            return True

          Чтобы синхронизировать шкалу и сам процесс воспроизведения видео, нужно передавать сообщения между GTK+ и GStreamer. Каждую секунду мы будем запрашивать текущую позицию воспроизведения и в соответствии с ней обновлять шкалу. Так мы показываем пользователю, какая часть файла уже показана, а ползунок всегда будет соответствовать реальной позиции воспроизведения.


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


          Изменение размеров видео


            _ <- GI.Gtk.onComboBoxChanged desiredVideoWidthComboBox $
                onComboBoxChanged fileChooserButton desiredVideoWidthComboBox drawingArea window
          
          -- ...
          
          onComboBoxChanged ::
            GI.Gtk.FileChooserButton ->
            GI.Gtk.ComboBoxText ->
            GI.Gtk.Widget ->
            GI.Gtk.Window ->
            IO ()
          onComboBoxChanged
            fileChooserButton
            desiredVideoWidthComboBox
            drawingArea
            window
            = do
            filename' <- GI.Gtk.fileChooserGetFilename fileChooserButton
            let filename = fromMaybe "" filename'
          
            desiredVideoWidth <- getDesiredVideoWidth desiredVideoWidthComboBox
            maybeWindowSize <- getWindowSize desiredVideoWidth filename
          
            case maybeWindowSize of
              Nothing -> resetWindowSize desiredVideoWidth fileChooserButton drawingArea window
              Just (width, height) -> setWindowSize width height fileChooserButton drawingArea window

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


          Полноэкранный режим


            _ <- GI.Gtk.onWidgetButtonReleaseEvent fullscreenButton
                (onFullscreenButtonRelease isWindowFullScreenRef desiredVideoWidthComboBox fileChooserButton window)
          
          -- ...
          
          onFullscreenButtonRelease ::
            IORef Bool ->
            GI.Gtk.ComboBoxText ->
            GI.Gtk.FileChooserButton ->
            GI.Gtk.Window ->
            GI.Gdk.EventButton ->
            IO Bool
          onFullscreenButtonRelease
            isWindowFullScreenRef
            desiredVideoWidthComboBox
            fileChooserButton
            window
            _
            = do
            isWindowFullScreen <- readIORef isWindowFullScreenRef
            if isWindowFullScreen
              then do
                GI.Gtk.widgetShow desiredVideoWidthComboBox
                GI.Gtk.widgetShow fileChooserButton
                void $ GI.Gtk.windowUnfullscreen window
              else do
                GI.Gtk.widgetHide desiredVideoWidthComboBox
                GI.Gtk.widgetHide fileChooserButton
                void $ GI.Gtk.windowFullscreen window
            return True

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


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


            _ <- GI.Gtk.onWidgetWindowStateEvent window (onWidgetWindowStateEvent isWindowFullScreenRef)
          
          -- ...
          
          onWidgetWindowStateEvent ::
            IORef Bool ->
            GI.Gdk.EventWindowState ->
            IO Bool
          onWidgetWindowStateEvent isWindowFullScreenRef eventWindowState = do
            windowStates <- GI.Gdk.getEventWindowStateNewWindowState eventWindowState
            let isWindowFullScreen = Prelude.foldl (\ acc x -> acc || GI.Gdk.WindowStateFullscreen == x) False windowStates
            writeIORef isWindowFullScreenRef isWindowFullScreen
            return True

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


          Благодаря использованию в главном потоке выполнения единственного пишущего и кучи сигнальных коллбэков, мы избегаем возможных ловушек общего изменяемого состояния. Если бы нас заботила безопасность потока выполнения, то вместо этого мы могли бы использовать MVar, TVar или atomicModifyIORef.


          О программе


            _ <- GI.Gtk.onWidgetButtonReleaseEvent aboutButton (onAboutButtonRelease aboutDialog)
          
          -- ...
          
          onAboutButtonRelease ::
            GI.Gtk.AboutDialog ->
            GI.Gdk.EventButton ->
            IO Bool
          onAboutButtonRelease aboutDialog _ = do
            _ <- GI.Gtk.onDialogResponse aboutDialog (\ _ -> GI.Gtk.widgetHide aboutDialog)
            _ <- GI.Gtk.dialogRun aboutDialog
            return True

          Последний рассматриваемый виджет — диалоговое окно «О программе». Здесь мы связываем диалоговое окно с кнопкой «О программе», отображающейся в основном окне.


          Закрытие окна


            _ <- GI.Gtk.onWidgetDestroy window (onWindowDestroy playbin)
          
          -- ...
          
          onWindowDestroy ::
            GI.Gst.Element ->
            IO ()
          onWindowDestroy playbin = do
            _ <- GI.Gst.elementSetState playbin GI.Gst.StateNull
            _ <- GI.Gst.objectUnref playbin
            GI.Gtk.mainQuit

          Когда пользователь закрывает окно, мы уничтожаем конвейер playbin и выходим из основного цикла GTK.


          Запуск


            GI.Gtk.widgetShowAll window
            GI.Gtk.main

          Наконец, мы показываем или отрисовываем главное окно и запускаем основной цикл GTK+. Он блокируется до вызова mainQuit.


          Полный файл Main.hs


          Ниже приведён файл movie-monad/src/Main.hs. Не показаны разные вспомогательные функции, относящиеся к main.


          {-
            Movie Monad
            (C) 2017 David lettier
            lettier.com
          -}
          
          {-# LANGUAGE OverloadedStrings #-}
          {-# LANGUAGE ScopedTypeVariables #-}
          
          module Main where
          
          import Prelude
          import Foreign.C.Types
          import System.Process
          import System.Exit
          import Control.Monad
          import Control.Exception
          import Text.Read
          import Data.IORef
          import Data.Maybe
          import Data.Int
          import Data.Text
          import Data.GI.Base
          import Data.GI.Base.Signals
          import Data.GI.Base.Properties
          import GI.GLib
          import GI.GObject
          import qualified GI.Gtk
          import GI.Gst
          import GI.GstVideo
          import GI.Gdk
          import GI.GdkX11
          import Paths_movie_monad
          
          -- Declare Element a type instance of IsVideoOverlay via a newtype wrapper
          -- Our GStreamer element is playbin
          -- Playbin implements the GStreamer VideoOverlay interface
          newtype GstElement = GstElement GI.Gst.Element
          instance GI.GstVideo.IsVideoOverlay GstElement
          
          main :: IO ()
          main = do
            _ <- GI.Gst.init Nothing
            _ <- GI.Gtk.init Nothing
          
            gladeFile <- getDataFileName "data/gui.glade"
            builder <- GI.Gtk.builderNewFromFile (pack gladeFile)
          
            window <- builderGetObject GI.Gtk.Window builder "window"
            fileChooserButton <- builderGetObject GI.Gtk.FileChooserButton builder "file-chooser-button"
            drawingArea <- builderGetObject GI.Gtk.Widget builder "drawing-area"
            seekScale <- builderGetObject GI.Gtk.Scale builder "seek-scale"
            onOffSwitch <- builderGetObject GI.Gtk.Switch builder "on-off-switch"
            volumeButton <- builderGetObject GI.Gtk.VolumeButton builder "volume-button"
            desiredVideoWidthComboBox <- builderGetObject GI.Gtk.ComboBoxText builder "desired-video-width-combo-box"
            fullscreenButton <- builderGetObject GI.Gtk.Button builder "fullscreen-button"
            errorMessageDialog <- builderGetObject GI.Gtk.MessageDialog builder "error-message-dialog"
            aboutButton <- builderGetObject GI.Gtk.Button builder "about-button"
            aboutDialog <- builderGetObject GI.Gtk.AboutDialog builder "about-dialog"
          
            playbin <- fromJust <$> GI.Gst.elementFactoryMake "playbin" (Just "MultimediaPlayer")
          
            isWindowFullScreenRef <- newIORef False
          
            _ <- GI.Gtk.onWidgetRealize drawingArea $ onDrawingAreaRealize drawingArea playbin fullscreenButton
          
            _ <- GI.Gtk.onFileChooserButtonFileSet fileChooserButton $
              onFileChooserButtonFileSet
                playbin
                fileChooserButton
                volumeButton
                isWindowFullScreenRef
                desiredVideoWidthComboBox
                onOffSwitch
                fullscreenButton
                drawingArea
                window
                errorMessageDialog
          
            _ <- GI.Gtk.onSwitchStateSet onOffSwitch (onSwitchStateSet playbin)
          
            _ <- GI.Gtk.onScaleButtonValueChanged volumeButton (onScaleButtonValueChanged playbin)
          
            seekScaleHandlerId <- GI.Gtk.onRangeValueChanged seekScale (onRangeValueChanged playbin seekScale)
          
            _ <- GI.GLib.timeoutAddSeconds GI.GLib.PRIORITY_DEFAULT 1 (updateSeekScale playbin seekScale seekScaleHandlerId)
          
            _ <- GI.Gtk.onComboBoxChanged desiredVideoWidthComboBox $
                onComboBoxChanged fileChooserButton desiredVideoWidthComboBox drawingArea window
          
            _ <- GI.Gtk.onWidgetButtonReleaseEvent fullscreenButton
                (onFullscreenButtonRelease isWindowFullScreenRef desiredVideoWidthComboBox fileChooserButton window)
          
            _ <- GI.Gtk.onWidgetWindowStateEvent window (onWidgetWindowStateEvent isWindowFullScreenRef)
          
            _ <- GI.Gtk.onWidgetButtonReleaseEvent aboutButton (onAboutButtonRelease aboutDialog)
          
            _ <- GI.Gtk.onWidgetDestroy window (onWindowDestroy playbin)
          
            GI.Gtk.widgetShowAll window
            GI.Gtk.main
          
          builderGetObject ::
            (GI.GObject.GObject b, GI.Gtk.IsBuilder a) =>
            (Data.GI.Base.ManagedPtr b -> b) ->
            a ->
            Prelude.String ->
            IO b
          builderGetObject objectTypeClass builder objectId =
            fromJust <$> GI.Gtk.builderGetObject builder (pack objectId) >>=
              GI.Gtk.unsafeCastTo objectTypeClass
          
          onDrawingAreaRealize ::
            GI.Gtk.Widget ->
            GI.Gst.Element ->
            GI.Gtk.Button ->
            GI.Gtk.WidgetRealizeCallback
          onDrawingAreaRealize drawingArea playbin fullscreenButton = do
            gdkWindow <- fromJust <$> GI.Gtk.widgetGetWindow drawingArea
            x11Window <- GI.Gtk.unsafeCastTo GI.GdkX11.X11Window gdkWindow
          
            xid <- GI.GdkX11.x11WindowGetXid x11Window
            let xid' = fromIntegral xid :: CUIntPtr
          
            GI.GstVideo.videoOverlaySetWindowHandle (GstElement playbin) xid'
          
            GI.Gtk.widgetHide fullscreenButton
          
          onFileChooserButtonFileSet ::
            GI.Gst.Element ->
            GI.Gtk.FileChooserButton ->
            GI.Gtk.VolumeButton ->
            IORef Bool ->
            GI.Gtk.ComboBoxText ->
            GI.Gtk.Switch ->
            GI.Gtk.Button ->
            GI.Gtk.Widget ->
            GI.Gtk.Window ->
            GI.Gtk.MessageDialog ->
            GI.Gtk.FileChooserButtonFileSetCallback
          onFileChooserButtonFileSet
            playbin
            fileChooserButton
            volumeButton
            isWindowFullScreenRef
            desiredVideoWidthComboBox
            onOffSwitch
            fullscreenButton
            drawingArea
            window
            errorMessageDialog
            = do
            _ <- GI.Gst.elementSetState playbin GI.Gst.StateNull
          
            filename <- fromJust <$> GI.Gtk.fileChooserGetFilename fileChooserButton
          
            setPlaybinUriAndVolume playbin filename volumeButton
          
            isWindowFullScreen <- readIORef isWindowFullScreenRef
          
            desiredVideoWidth <- getDesiredVideoWidth desiredVideoWidthComboBox
            maybeWindowSize <- getWindowSize desiredVideoWidth filename
          
            case maybeWindowSize of
              Nothing -> do
                _ <- GI.Gst.elementSetState playbin GI.Gst.StatePaused
                GI.Gtk.windowUnfullscreen window
                GI.Gtk.switchSetActive onOffSwitch False
                GI.Gtk.widgetHide fullscreenButton
                GI.Gtk.widgetShow desiredVideoWidthComboBox
                resetWindowSize desiredVideoWidth fileChooserButton drawingArea window
                _ <- GI.Gtk.onDialogResponse errorMessageDialog (\ _ -> GI.Gtk.widgetHide errorMessageDialog)
                void $ GI.Gtk.dialogRun errorMessageDialog
              Just (width, height) -> do
                _ <- GI.Gst.elementSetState playbin GI.Gst.StatePlaying
                GI.Gtk.switchSetActive onOffSwitch True
                GI.Gtk.widgetShow fullscreenButton
                unless isWindowFullScreen $ setWindowSize width height fileChooserButton drawingArea window
          
          onSwitchStateSet ::
            GI.Gst.Element ->
            Bool ->
            IO Bool
          onSwitchStateSet playbin switchOn = do
            if switchOn
              then void $ GI.Gst.elementSetState playbin GI.Gst.StatePlaying
              else void $ GI.Gst.elementSetState playbin GI.Gst.StatePaused
            return switchOn
          
          onScaleButtonValueChanged ::
            GI.Gst.Element ->
            Double ->
            IO ()
          onScaleButtonValueChanged playbin volume =
              void $ Data.GI.Base.Properties.setObjectPropertyDouble playbin "volume" volume
          
          onRangeValueChanged ::
            GI.Gst.Element ->
            GI.Gtk.Scale ->
            IO ()
          onRangeValueChanged playbin seekScale = do
            (couldQueryDuration, duration) <- GI.Gst.elementQueryDuration playbin GI.Gst.FormatTime
          
            when couldQueryDuration $ do
              percentage' <- GI.Gtk.rangeGetValue seekScale
              let percentage = percentage' / 100.0
              let position = fromIntegral (round ((fromIntegral duration :: Double) * percentage) :: Int) :: Int64
              void $ GI.Gst.elementSeekSimple playbin GI.Gst.FormatTime [ GI.Gst.SeekFlagsFlush ] position
          
          updateSeekScale ::
            GI.Gst.Element ->
            GI.Gtk.Scale ->
            Data.GI.Base.Signals.SignalHandlerId ->
            IO Bool
          updateSeekScale playbin seekScale seekScaleHandlerId = do
            (couldQueryDuration, duration) <- GI.Gst.elementQueryDuration playbin GI.Gst.FormatTime
            (couldQueryPosition, position) <- GI.Gst.elementQueryPosition playbin GI.Gst.FormatTime
          
            let percentage =
                  if couldQueryDuration && couldQueryPosition && duration > 0
                    then 100.0 * (fromIntegral position / fromIntegral duration :: Double)
                    else 0.0
          
            GI.GObject.signalHandlerBlock seekScale seekScaleHandlerId
            GI.Gtk.rangeSetValue seekScale percentage
            GI.GObject.signalHandlerUnblock seekScale seekScaleHandlerId
          
            return True
          
          onComboBoxChanged ::
            GI.Gtk.FileChooserButton ->
            GI.Gtk.ComboBoxText ->
            GI.Gtk.Widget ->
            GI.Gtk.Window ->
            IO ()
          onComboBoxChanged
            fileChooserButton
            desiredVideoWidthComboBox
            drawingArea
            window
            = do
            filename' <- GI.Gtk.fileChooserGetFilename fileChooserButton
            let filename = fromMaybe "" filename'
          
            desiredVideoWidth <- getDesiredVideoWidth desiredVideoWidthComboBox
            maybeWindowSize <- getWindowSize desiredVideoWidth filename
          
            case maybeWindowSize of
              Nothing -> resetWindowSize desiredVideoWidth fileChooserButton drawingArea window
              Just (width, height) -> setWindowSize width height fileChooserButton drawingArea window
          
          onFullscreenButtonRelease ::
            IORef Bool ->
            GI.Gtk.ComboBoxText ->
            GI.Gtk.FileChooserButton ->
            GI.Gtk.Window ->
            GI.Gdk.EventButton ->
            IO Bool
          onFullscreenButtonRelease
            isWindowFullScreenRef
            desiredVideoWidthComboBox
            fileChooserButton
            window
            _
            = do
            isWindowFullScreen <- readIORef isWindowFullScreenRef
            if isWindowFullScreen
              then do
                GI.Gtk.widgetShow desiredVideoWidthComboBox
                GI.Gtk.widgetShow fileChooserButton
                void $ GI.Gtk.windowUnfullscreen window
              else do
                GI.Gtk.widgetHide desiredVideoWidthComboBox
                GI.Gtk.widgetHide fileChooserButton
                void $ GI.Gtk.windowFullscreen window
            return True
          
          onWidgetWindowStateEvent ::
            IORef Bool ->
            GI.Gdk.EventWindowState ->
            IO Bool
          onWidgetWindowStateEvent isWindowFullScreenRef eventWindowState = do
            windowStates <- GI.Gdk.getEventWindowStateNewWindowState eventWindowState
            let isWindowFullScreen = Prelude.foldl (\ acc x -> acc || GI.Gdk.WindowStateFullscreen == x) False windowStates
            writeIORef isWindowFullScreenRef isWindowFullScreen
            return True
          
          onAboutButtonRelease ::
            GI.Gtk.AboutDialog ->
            GI.Gdk.EventButton ->
            IO Bool
          onAboutButtonRelease aboutDialog _ = do
            _ <- GI.Gtk.onDialogResponse aboutDialog (\ _ -> GI.Gtk.widgetHide aboutDialog)
            _ <- GI.Gtk.dialogRun aboutDialog
            return True
          
          onWindowDestroy ::
            GI.Gst.Element ->
            IO ()
          onWindowDestroy playbin = do
            _ <- GI.Gst.elementSetState playbin GI.Gst.StateNull
            _ <- GI.Gst.objectUnref playbin
            GI.Gtk.mainQuit
          
          setPlaybinUriAndVolume ::
            GI.Gst.Element ->
            Prelude.String ->
            GI.Gtk.VolumeButton ->
            IO ()
          setPlaybinUriAndVolume playbin filename volumeButton = do
            let uri = "file://" ++ filename
            volume <- GI.Gtk.scaleButtonGetValue volumeButton
            Data.GI.Base.Properties.setObjectPropertyDouble playbin "volume" volume
            Data.GI.Base.Properties.setObjectPropertyString playbin "uri" (Just $ pack uri)
          
          getVideoInfo :: Prelude.String -> Prelude.String -> IO (Maybe Prelude.String)
          getVideoInfo flag filename = do
            (code, out, _) <- catch (
                readProcessWithExitCode
                  "exiftool"
                  [flag, "-s", "-S", filename]
                  ""
              ) (\ (_ :: Control.Exception.IOException) -> return (ExitFailure 1, "", ""))
            if code == System.Exit.ExitSuccess
              then return (Just out)
              else return Nothing
          
          isVideo :: Prelude.String -> IO Bool
          isVideo filename = do
            maybeOut <- getVideoInfo "-MIMEType" filename
            case maybeOut of
              Nothing -> return False
              Just out -> return ("video" `isInfixOf` pack out)
          
          getWindowSize :: Int -> Prelude.String -> IO (Maybe (Int32, Int32))
          getWindowSize desiredVideoWidth filename =
            isVideo filename >>=
            getWidthHeightString >>=
            splitWidthHeightString >>=
            widthHeightToDouble >>=
            ratio >>=
            windowSize
            where
              getWidthHeightString :: Bool -> IO (Maybe Prelude.String)
              getWidthHeightString False = return Nothing
              getWidthHeightString True = getVideoInfo "-ImageSize" filename
              splitWidthHeightString :: Maybe Prelude.String -> IO (Maybe [Text])
              splitWidthHeightString Nothing = return Nothing
              splitWidthHeightString (Just string) = return (Just (Data.Text.splitOn "x" (pack string)))
              widthHeightToDouble :: Maybe [Text] -> IO (Maybe Double, Maybe Double)
              widthHeightToDouble (Just (x:y:_)) = return (readMaybe (unpack x) :: Maybe Double, readMaybe (unpack y) :: Maybe Double)
              widthHeightToDouble _ = return (Nothing, Nothing)
              ratio :: (Maybe Double, Maybe Double) -> IO (Maybe Double)
              ratio (Just width, Just height) =
                if width <= 0.0 then return Nothing else return (Just (height / width))
              ratio _ = return Nothing
              windowSize :: Maybe Double -> IO (Maybe (Int32, Int32))
              windowSize Nothing = return Nothing
              windowSize (Just ratio') =
                return (Just (fromIntegral desiredVideoWidth :: Int32, round ((fromIntegral desiredVideoWidth :: Double) *  ratio') :: Int32))
          
          getDesiredVideoWidth :: GI.Gtk.ComboBoxText -> IO Int
          getDesiredVideoWidth = fmap (\ x -> read (Data.Text.unpack x) :: Int) . GI.Gtk.comboBoxTextGetActiveText
          
          setWindowSize ::
            Int32 ->
            Int32 ->
            GI.Gtk.FileChooserButton ->
            GI.Gtk.Widget ->
            GI.Gtk.Window ->
            IO ()
          setWindowSize width height fileChooserButton drawingArea window = do
            GI.Gtk.setWidgetWidthRequest fileChooserButton width
          
            GI.Gtk.setWidgetWidthRequest drawingArea width
            GI.Gtk.setWidgetHeightRequest drawingArea height
          
            GI.Gtk.setWidgetWidthRequest window width
            GI.Gtk.setWidgetHeightRequest window height
            GI.Gtk.windowResize window width (if height <= 0 then 1 else height)
          
          resetWindowSize ::
            (Integral a) =>
            a ->
            GI.Gtk.FileChooserButton ->
            GI.Gtk.Widget ->
            GI.Gtk.Window ->
            IO ()
          resetWindowSize width' fileChooserButton drawingArea window = do
            let width = fromIntegral width' :: Int32
            GI.Gtk.widgetQueueDraw drawingArea
            setWindowSize width 0 fileChooserButton drawingArea window

          Собираем Movie Monad


          Мы настроили наше сборочное окружение и подготовили весь исходный код, можно собирать Movie Monad и запускать исполняемый файл.


          cd movie-monad/
          stack clean
          stack install
          stack exec -- movie-monad
          # Or just `movie-monad` if `stack path | grep local-bin-path` is in your `echo $PATH`

          Если всё в порядке, то Movie Monad должен запуститься.


          Заключение


          Пересмотрев проект Movie Monad, мы заново сделали приложение с помощью программных библиотек GTK+ и GStreamer. Благодаря им приложение осталось таким же портируемым, как и Electron-версия. Movie Monad теперь может обрабатывать большие видеофайлы и имеет все стандартные элементы управления.


          Другим преимуществом использования GTK+ стало уменьшение потребления памяти. Если сравнивать резидентный размер в памяти при старте, то версия GTK+ занимает ~50 Мб, а версия Electron — ~300 Мб (500%-ное увеличение).


          Наконец, вариант с GTK+ имеет меньше ограничений и требует меньше программирования. Для обеспечения такой же функциональности, вариант с Electron требует использования громоздкой клиент-серверной архитектуры. Но благодаря прекрасным сборкам haskell-gi мы смогли избежать решения на базе веба.


          Если хотите посмотреть другие приложения, построенные с помощью GTK+ и Haskell, то обратите внимание на Gifcurry. Оно умеет брать видеофайлы и на их основе создавать гифки с наложенным текстом.

          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338176/


          Метки:  

          С чего начать молодым разработчикам мобильных игр из России [Часть 3]

          Вторник, 19 Сентября 2017 г. 14:49 + в цитатник
          EgorHMG сегодня в 14:49 Разработка

          С чего начать молодым разработчикам мобильных игр из России [Часть 3]

            Всем доброго!



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



            Ранние публикации можно прочитать тут:
            Часть 1
            Часть 2

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

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

            1) Коммит в репозитории делается после каждого плюс – минус значительного изменения;
            2) Тестирование на «живых» устройствах проводится не менее трех раз в день, чтобы в случае чего можно было безболезненно откатить изменения;
            3) Разработка ведется небольшими итерациями и продолжается только после полного теста небольшого кусочка;
            4) Оптимизация – наше всё;
            5) Билд для внешних тестировщиков не заливается раньше, чем выполнен внутренний полный тест и не убраны «жесткие баги»;
            6) Глобальное обновление ни в коем случае не должно выходить перед праздниками и выходными.
            7) Чем больше внешних тестировщиков – тем лучше;

            Пойдем по пунктам:

            Коммиты



            Думаю, ни для кого не новость, что без контроля версии вести разработку категорически противопоказано. Если разработка ведется более, чем одним человеком – это смертельно опасно. После каждого изменения, которое влияет на механику или алгоритм игры необходимо сделать коллаборацию между всеми разработчиками. Я думаю каждый, кто вел разработку в таких условиях сталкивался с ситуацией, когда все разом залили изменения, что-то перестало работать, а в чем причина – неясно. Еще хуже – если понятно, что причиной нерабочей версии стали плоды целого рабочего дня разработчика и нужно откатывать все изменения назад. Менее неприятно, если причиной стал конфликт работы и просто нужно немного переделать. Идеально, если все видят изменения в режиме реального времени и знают, что в случае чего – максимальный откат назад будет минут в 10-15.

            Физические устройства



            То, что мы видим в среде разработки, бывает далеко от того, что мы увидим на устройствах. Если с ПК версией у всех всё плюс-минус одинаково, то с мобильными устройствами всё обстоит несколько иначе.
            Некоторые пункты нужно будет калибровать «на глаз», некоторые оптимизировать и даже делать даунгрейд (этого тоже бояться не нужно).

            Короткие задачи



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

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

            Если вам хватило упорства на одном дыхании за пол дня что-то сконструировать, а потом вы увидели, что всё ок (в лучшем случае), но плохо работает только небольшая деталь, вы имеете большие шансы переделывать из-за мелкой недоработки очень большой и вполне работоспособный кусок уровня.

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

            Оптимизация



            Я думаю, что объяснять зачем её делать не нужно. От себя хочу сказать, что оптимизировать всё и сразу тоже неправильно. В данном случае работает правило 20/80, когда 20% усилий приносят 80% пользы. Подходите к вопросу с умом.

            Внутреннее тестирование и выходные

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

            Кейс из жизни:


            После фикса UI мы не заменили скрипт на кнопке запуска уровня.

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

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

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

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

            Аудитория



            Для сбора аналитики пока что мы используем только Firebase и фидбек от тестировщиков. Это позволило нам оптимизировать игру под старые графические чипы и свести лаги к минимуму, по сравнению с первыми версиями. Чем больше устройств – тем полней картинка. Иногда мы связывались напрямую с человеком и оптимизировали игру именно под его устройство. На выходе получалось так, что вмести с этим устройством был ощутимый прирост и на других до этого проблемных девайсах.

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

            P.S.



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

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

            В общем, кто решится на установку нашего творения — энжой и большая благодарность.

            Будем рады любому фидбеку.

            Ссылка на обновляемый билд бета тестирования в Google play 

            Всем спасибо!

            Продолжение следует.
            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/338242/


            Метки:  

            Как увеличить показатели сервиса в 7 раз за три месяца с помощью HADI-циклов и приоритизации гипотез

            Вторник, 19 Сентября 2017 г. 14:44 + в цитатник
            friifond сегодня в 14:44 Управление

            Как увеличить показатели сервиса в 7 раз за три месяца с помощью HADI-циклов и приоритизации гипотез

              image

              Меня зовут Илья Китанин, я более 7 лет руковожу разработкой в различных компаниях, сейчас — в Преакселераторе ФРИИ. В этой статье я расскажу, как с помощью HADI-циклов и Теории ограничений Голдратта (TOC) мы смогли вырастить ключевые показатели сервиса Cofoundit в 7 раз за 3 месяца и продолжаем активно расти сейчас. В этом материале — кейсы применения методологии ФРИИ, грабли, которые мы прошли, и необходимый минимум теории.

              Cofoundit — это внутренний стартап ФРИИ для подбора сотрудников и сооснователей в команду, где стартапы и кандидаты могут быстро найти друг друга и вместе побежать к международной компании.

              Я с моей коллегой Юлей Копыловой начали заниматься этим сервисом 15 марта этого года. Сервис существовал уже полгода, но значимых результатов не показывал. И перед нами стояла задача до 1 июля доказать состоятельность сервиса. Для этого необходимо было кратно увеличить количество закрытых вакансий в месяц, а также ускорить этот процесс.

              Как это обычно бывает, перед нами стояло несколько ограничений:

              1. Сроки: как я писал выше, надо было успеть показать результат к 1 июля.
              2. Время: я и Юля продолжили выполнять наши основные задачи, а сервисом занимались только в свободное время. Я руководил разработкой, а Юля выполняла обязанности аккаунт-менеджера.
              3. Бюджет на разработку был знатно ограничен.



              Дано: что из себя представлял сервис, когда мы взялись за него


              Механика была следующая:

              Стартап заполнял анкету Преакселератора и в блоке «Команда» создавал вакансию.

              image
              Примеры вопросов анкеты для стартапа

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



              Со стороны кандидата путь был чуть проще: он заполнял небольшую анкету, где отвечал на несколько вопросов и прикреплял своё резюме. После этого он видел не более 5 вакансий, каждой из которых он мог отказать (нажать «не интересно») или заинтересоваться (лайкнуть), после этого выбрать предполагаемое время встречи. Стартапу при этом уходило письмо, что им заинтересовался кандидат.

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



              При такой механике ключевые показатели сервиса были недостаточно хороши:

              • 11 закрытых вакансий в квартал;
              • Среднее время до первой заинтересованности кандидатом (лайка) — 48 дней.

              Решение: инструмент HADI-циклов и методология ТОС для быстрого тестирования и приоритизации гипотез


              Посмотрев на стоящие перед нами цели и ограничения, мы решили использовать инструмент HADI-циклов, по которому работают стартапы Акселератора ФРИИ.

              HADI-циклы — это очень простая методика проверки гипотез, которая состоит из 4 этапов:

              1. Постановка Гипотезы,
              2. Действия по её реализации,
              3. Сбор данных о качестве работы гипотезы,
              4. Выводы о результате работы гипотезы, могут включать некоторые следующие гипотезы.



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

              Сложность здесь — определить, какие гипотезы стоит тестировать первыми. Для приоритизации гипотез мы решили использовать часть Теории Ограничений Голдратта (ТОС), связанную с определением узких мест. Для понимания этой методологии в Акселераторе стартапам рекомендуют к прочтению книгу Голдратта «Цель».

              ТОС — также достаточно простая методология, для повышения пропускной способности системы (=повышения результативности какого-либо процесса) по ней необходимо:

              1. Найти «узкое место» — ограничение системы, на котором «застревает» процесс производства (или какой-то иной), т.е. накапливаются задачи,
              2. Решить, как максимально использовать «мощность» этого ограничения,
              3. Подчинить систему этому решению (=сделать так, чтобы «мощность» ограничения не простаивала),
              4. Расширить ограничение,
              5. Если ограничение устранено, вернуться к шагу 1. Если необходимо еще расширить — к шагу 4.



              Мы использовали именно эти 2 методологии, так как HADI-циклы идеально подходят для быстрого роста показателей, а TOC помогает определиться с тем, какую именно гипотезу тестировать в первую очередь.

              Для начала мы решили определить ограничения или «узкие места» системы, для этого подняли и проанализировали всю статистику, которая на тот момент собиралась (оказалось, что и тут у нас были ограничения).




              И сразу увидели 2 «узких места», где срезается конверсия.

              1. Стартапы хорошо отвечают на лайки кандидатов только первые 5 дней после публикации вакансии, а дальше отвечают в 2,5 раза хуже.
              2. Кандидаты хорошо открывают письма с переподбором (=похожими вакансиями), но очень редко переходят в профили стартапов после просмотра этих писем.

              Гипотеза 1. По 1-ой проблеме мы решили выкидывать из подбора те стартапы, которые не отвечают кандидатам более двух недель. Нам сразу хотелось сделать красивое решение, где мы сможем часть стартапов выделить в отдельный сегмент и автоматически отправлять им письма, мотивирующие их вернуться в систему. Но наша цель была БЫСТРО протестировать гипотезу — и в лучших традициях MVP мы просто убрали часть проектов из подборки. Реализацию дополнительного функционала отложили до момента, когда уже проверим, сработала гипотеза или нет — увеличилась ли конверсия в ответы кандидатам после наших действий.

              Гипотеза 2. По 2-ой проблеме мы не знали причину и решили заставить кандидатов переходить из письма в систему (где мы сможем узнать, какие именно стартапы он просмотрел и т.д.), чтобы собрать больше статистики. Мы убрали из писем описания проектов и предложили пользователю перейти в систему, чтобы их посмотреть.

              Было




              Стало




              Таким образом получили первые две гипотезы на проверку.

              Гипотеза 3. Также добавили 3-ю гипотезу — убрать механизм премодерации, это должно было высвободить время Юли, которая занималась ручным просмотром проектов, и ускорить попадание вакансий в подбор. В результате стартапам не нужно будет ожидать проверки, а время от момента создания вакансии до начала подбора сократится от 2-3 дней до 4 часов.

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

              Гипотеза 4. Мы решили сделать «безопасную задачу», которая никак не может повлиять на метрики предыдущих гипотез, — расширить статистику с расчетом на то, что она позволит нам в будущем быстрее и лучше проверять гипотезы. Мы добавили в аналитику информацию о том, смотрел ли кандидат вакансию и когда именно. Тем самым мы смогли разбить конверсию из подобранных вакансий в «лайки» на две:

              1) из подобранных — в просмотренные,
              2) из просмотренных — в «лайки».

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

              Гипотеза 5. Добавить мотивацию стартапам выставлять статус, что они работают с кандидатом. Мы решили добавить в письмо по итогам собеседования «бонус»: стартапы, которые выставили в профиле статус, что они работают с кандидатом, получат бесплатную 3-х недельную программу по развитию бизнеса. Это должно было увеличить количество закрытых вакансий в системе — до этого приходилось дополнительно обзванивать стартапы и выставлять статусы вручную.

              Гипотеза 6. И мы не смогли удержаться и добавили гипотезу, также влияющую на скорость получения лайка стартапом: присылать кандидатам повторные подборки вакансий не 1 раз в неделю, а 2. Тем самым хотели увеличить количество лайков в неделю, а также ускорить этот процесс.

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

              Приоритизация гипотез с помощью «плеча» метрик


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

              Мы вспомнили об основном инструменте Акселератора для приоритизации гипотез — это расчет unit-экономики и «плеча» метрик. Чтобы определить, на какой показатель эффективнее всего влиять при тестировании гипотез, на основе текущих данных прогнозируется изменение показателей системы при влиянии на каждую метрику.

              Для этого мы построили «карту возможного будущего» — таблицу, которая прогнозирует изменение главного для нас параметра при росте каждой из конверсий перехода кандидата по воронке до значения, в которое мы верим.

              Мы посчитали, что главным для нас параметром является количество новых кандидатов, необходимых для одного закрытия вакансии, посчитали показатели сервиса на тот момент, построили формулы и получили следующую таблицу:



              Из таблицы мы увидели, что наиболее важными в тот момент показателями для нас были конверсия в match (закрытие вакансии) и конверсия в анкету, — они должны принести максимальный эффект. Мы начали работать над гипотезами, влияющими на эти параметры.

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

              1. Описываем гипотезу, которую хотим проверить.
              2. Действие — как именно мы хотим её проверить.
              3. Метрика, на которую хотим повлиять.
              4. Ожидаемый эффект по росту данной метрики.
              5. Планируемая дата релиза (чтобы не тестировать гипотезы, влияющие на одну и ту же метрику, параллельно).
              6. Фактическая дата реализации — старта тестирования гипотезы.
              7. Дата, когда посчитаем эффект (устанавливается исходя из необходимого количества данных и времени на их сбор, также нужна, чтобы не тестировать гипотезы, влияющие на одну и ту же метрику, параллельно).
              8. Полученный эффект (как изменилась метрика в результате).
              9. Следующие шаги — здесь пишем о том, какие выводы сделали по итогу проверки гипотезы.



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


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

              4 из них были настоящим прорывом, только 1 ухудшила показатели системы, а остальные дали относительно небольшой рост. Это позволило улучшить показатели до:



              И к 1 июля мы смогли доказать ценность сервиса для Фонда и продолжить развивать его, сильно снизив градус напряжения и заметно уменьшив все ограничения. Перед нами перестал маячить дедлайн, с Юли сняли другие задачи и согласовали новый бюджет на разработку.

              Что мы делали не так — выводы


              В процессе работы над проектом мы совершили 3 ошибки, от которых хотим предостеречь другие стартапы:

              1. Не хотели отказываться от неподтвержденных гипотез


              У нас была гипотеза, которую мы очень любили.

              Раньше кандидат при заполнении анкеты прикреплял своё резюме — и только потом мог посмотреть стартапы с открытыми вакансиями.

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

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

              Всё это был самообман — в сентябре мы наконец вернули резюме обратно. На это решение мы потратили 3 месяца. 3 месяца, Карл! То есть наш сервис хуже работал целый квартал.

              2. Не декомпозировали гипотезы


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

              Мы на самом деле тестировали несколько гипотез в рамках одной. Каждое отдельное письмо — это гипотеза.

              Чтобы начать проверку этой гипотезы, мы потратили почти месяц: неделю искали с Юлей время для двухчасовой встречи, 3-4 дня готовили ТЗ и сами письма, а еще полторы недели разработчики реализовывали функционал отправки писем по событию.

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

              3. Тестировали связанные гипотезы, которые влияют на один и тот же показатель


              Мы были уверены, что этого не делаем. Когда мы добавляли мотивацию с помощью 3-х недельной программы по развитию бизнеса, конверсия в закрытие вакансии сильно возросла. Потом мы методично уменьшали стоимость мотивации для нас, пока не убрали её совсем. При этом конверсия не падала!

              Мы аккуратно разобрали статистику и выяснили, что гипотеза «Убрать неактивные стартапы из подбора» повлияла и на конверсию в закрытие вакансии (match) — остались более мотивированные команды, и они не только чаще отвечали лайкнувшим их кандидатам, но и выставляли статусы после встречи.

              Резюмируя:

              1. HADI-циклы — эффективный инструмент для развития бизнеса!
              2. Тестируйте гипотезы как можно быстрее, для приоритизации можно использовать инструмент измерения «плеча» метрики.
              3. Не допускайте наших ошибок:
                a. Будьте решительны с неподтвержденными гипотезами,
                b. Декомпозируйте гипотезы,
                c. Не тестируйте связанные гипотезы, влияющие на одну метрику.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338240/


              [Из песочницы] Продажи в Unity Asset Store. Личный опыт

              Вторник, 19 Сентября 2017 г. 14:39 + в цитатник
              derzkydj сегодня в 14:39 Маркетинг

              Продажи в Unity Asset Store. Личный опыт

              Я человек простой, работу люблю, особенно если эта работа на себя и без помошников. Даже в школу ходил вечернюю, потому как днём работал, а потом до 6 утра просиживал за ПК, разбирался в 3DMax и Photoshop, клепал небольшие моды для игр того времени и вглядывался в API разных старых движков. А в остальном был обычный пацан.

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

              И вот какая ирония. Я изучал много по Unreal Engine, очень ждал выхода полноценной версии UE4 и пропустил всё что было связано с Unity. Пару раз брал с оффсайта, не знаю, казался неудобным, интерфейс отталкивал, просто сносил не разбираясь. А в итоге именно тогда и нужно было его изучать и кроить вдоль и поперёк, теперь-то я это понимаю. Но ни чего не поделаешь, asset store от Unity сегодня изобилует тысячами ассетов, скриптов, моделей, текстур, звуков и прочего, а цены варьируются от free до 500$.

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

              Сначала я расскажу о разделе 3д моделей, а именно авто, в котором если можно так сказать попробовал свои силы сам. Я большой автолюбитель и игр серии GTA, за весь мой опыт, я отмоделил как просто для себя так и в качестве модов сотни авто. Разодрал множество игр на готовые модели автомобилей, это были NFS, TDU, конечно FM, да в общем то не важно. Факт в том, что я научился видеть и определять эти модели у других, ну это проще на самом деле чем кажется, стоит только сетку мешей в максе или ZModeler сравнить и всё будет понятно, а особо дерзкие выкладывают свои модели в скетчфабе, а он как известно повзоляет посмотреть сетку (Sketchfab — сервис, позволяющий просматривать 3д модели с возможностью встривания как на сайт, так и на ассет стор).

              Оценив рынок, выбранный мной раздел, цены и качество работ, пообщавшись с продавцами лично, я понял что нужно чтобы заработать так скажем по-быстрому. Да, можете в меня кидать палками, но вначале встал я на не очень добрый путь. Я отмоделил пару авто, а вот с текстурками немного похалтурил, набрал с других источников. Но всё-же принялся запускать в пендинг (Pending Review — как и на многих подобных площадках, это время рассмотрения ассета, составляет на UAS примерно 20 дней).

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

              Итак вернёмся. Спустя месяц, приняв во внимание эти требования с их стороны, мои ассеты верно начали появляться в сторе и я начал ждать, обновляя вкладку Sales чуть ли не каждые 15 минут. Прошло недели 2 и наконец первая моделька за 30$ была продана, получил я чуть больше 20$, так как забирают они 30 процентов, но меня это вообще не волновало, я прыгал до потолка наверное. Затем ещё покупка и ещё, в итоге за 2 месяца (не считая времени рассмотрения) мне удалось заработать 160 долларов с копейками. И вот тут то всё и началось.

              Из этих денег я потратил несколько долларов на доменное имя, сверстал сайт, загрузил свои модельки в скетчфаб. Даже видео несколько сделал, всё завертелось. Я моделил и запускал в пендинг, обустраивал сайт и отвечал на вопросы покупателей. Но в один прекрасный момент спустя месяца 3 всё накрылось.

              Все мои модельки были удалены с сайта, а в админке появился статус declined. Я сам виноват, тектстурки нужно было нарисовать самому, тем более что это не заняло бы много времени. Но я не сдавался. После обращений в ассет стор с предоставлением доказательств о том что модели мои (в качестве доказательств я использовал свежеснятое видео, где я моделю) и с объяснением моего косяка, мы смогли разрешить вопрос. Я заменил текстуры и мои модельки снова появились в сторе. Но, когда я залил следующую модель, она была отклонена. Я получил сообщение о том, что моя история аккаунта им не нравиться и все ассеты снова были отозваны. Больше ни чего доказать я им не смог, аккаунт был закрыт, сайт ныне не существует, аккаунт скечфаб пуст.

              Жаль, такие дела. Ну да ладно. По началу я был разгневан, так как пытался им доказать что на их сайте продают действительно ворованные модельки, показывал сетки мешей, доказывал что я продаю модель, а не текстуры, доходило даже до того, что я писал прямо в EA, в Criterion Games и? Практически все нечестные продавцы в разделе авто были заблокированы за это лето. Конечно это не только моя заслуга, да и не совсем заслуга, писали там и на форуме по этому поводу очень многие и обращений в стор было много. А что вы хотели ребята? Это бизнес.

              Я не сдался, не имею такой привычки. Но как то вот не прёт и всё тут. Начал большой проект по всем канонам, на разработку ушло 3 месяца и… более дешёвые и абсолютно абсурдные модели вроде дорожного конуса за 5$ допускают до продаж и они постоянно во вкладке апдэйт, как буд-то каждый день их обновляют, это действительно напрягает. При этом ребята на комьюнити говорят, что их модели посчитали слишком простыми и отклонили, хотя модели то очень даже. Мой проект довольно дорогой, но качество на порядок выше многих там, это сказали даже сами Unity, когда принимали ассет, но совсем не идёт как-то, пропали даже те, кто ждал и следил за этим проектом постоянно. Этим вопросом озадачены очень многие в комьюнити кто делает действительно крутые проекты, достойные быть более чем в топах и на главной странице, а в итоге их просто не возможно найти вообще по каким либо тегам. Проекты, которые были победителями разных конкурсов от юнити, с трудом удаётся отрыть на их форуме. Конечно, внешне раскручивать ассет довольно просто, обычно достаточно инстаграмма, но зачастую это не приносит прямых продаж, лучше всего работает только комьюнити, фэйсбук и твиттер та же ситуация, скажу больше — мой аккаунт фэйсбук был заблокирован, причины я так и не понял, да и не разбирался, прямо скажем.

              Я вынес много уроков и научился многому, сегодня я делаю качественный и большой контент, за хорошую цену. У меня появились связи через разные соц.сети, я участвую в конкурсах, иногда ассеты попадают в топы разных соц.групп от Unity, вернул сайт на новом домене и занимаюсь рекламой, веду ветки с развитием моих проектов и вовремя исправляю и обновляю ассеты, это моя работа. Сегодня ассет стор мне приносит в небольшой доход с учётом того, что на новом аккаунте у меня 3 крупных ассета, а самый амбициозный из них так и не принёс пока ни одной продажи. Доход достаточно мал, но в принципе стабилен и следующим шагом будет перенос моих ассетов на UE Marketplace. Вскоре я обязательно поделюсь с вами опытом, так как там модерация посерьёзней.

              И напоследок я немного скажу о выводе средств. У меня есть карта Payoneer и в Unity вполне есть графа Wire, где перевод денег на карту обойдётся в 20$, а минимальная сумма вывода 250$. С PayPal проблем не было ни разу, выводятся автоматом каждые 15 дней без процентов и задержек. В целом сервис мне нравиться, даже очень, но вот люди обитающие там, зачастую не желают ценить работу других, выкладывают за смешную цену контент с использованием чужих решений и идей или что ещё хуже, вовсе не принадлежащий им контент. Но, это бизнес. Есть конечно и та малая часть людей, кто имея самые минимальные продажи, всё равно стоят на своём, прося за свою работу 300, а то и 500$, такие люди у меня вызывают только уважение. Кто делает сам и вкладывает душу — хочет донести что-то новое, ценит себя и свою работу.

              Верьте в себя. Больших вам успехов и продаж.
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338238/


              Метки:  

              Быстрая разработка скриптов мониторинга с помощью Bash, Outthentic и Sparrow

              Вторник, 19 Сентября 2017 г. 14:10 + в цитатник
              alexey_melezhik сегодня в 14:10 Разработка

              Быстрая разработка скриптов мониторинга с помощью Bash, Outthentic и Sparrow

              • Tutorial

              В данном посте я расскажу о том, как просто и быстро писать различные скрипты проверки состояния инфраструктуры с помощью инструментов Bash, Outthentic и Sparrow ...


              Задача — у нас есть сервер, на который мы устанавливаем приложения и делаем настройку конфигурации. Хочется написать скрипт, который быстро даст нам ответ, что с сервером все хорошо, и приложение настроено и работает корректно. Этакий smoke тест, который будет нам полезен, когда мы будем заниматься поиском проблем или же просто проверять, что очередной деплоймнет ничего не сломал. Предвидя возможные вопросы, я знаю, что уже существуют инструменты, которые делают что-то подобное ( inspec ), тем не менее, хочу рассказать об альтернативном походе. ( Будет интересно сравнить ).


              Выбор инструментария


              Итак, почему Bash? Потому что он достаточно прост в использовании и позволяет быстро и эффективно писать разного рода скрипты, imho я бы не стал использовать Bash для более сложных задач, но для данного рода проблем он вполне подходит.


              Затем, что такое Outthentic и как он нам здесь пригодится? Outthentic — это фреймворк для написания скриптов, позволяющий быстро написать, настроить и запустить ваш скрипт ( в данном случае, написанный на Bash, но можно и на других языках ), так же, что немаловажно, Outthentic имеет встроенный DSL, подходящий под написание скриптов в стиле автоматизированных тестов, что может быть удобным при написании скриптов мониторинга.


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


              Практический пример


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


              • запущен tomcat, то есть виден в списке процессов


              • http запросы на некоторые ресурсы приложения ( развернутого на сервере )
                возвращают успешный http код (200) — GET 127.0.0.1:8080/healthcheck


              • с целевого сервера доступен сервер баз данных (192.168.0.2), на уровне доступа по tcp порту ( очень часто из-за некорректно настроенной политики безопасности это может быть не так, что приводит к неработоспособности приложения )

              Да, и важно отметить, что все проверки выполняются прямо на целевом сервере:


              $ ssh   target-server bash 
              $ bash /path/to/check/script.bash

              Bash скрипт


              В данном случае скрипт будет тривиальным:


              $ cat script.bash
              
              #!/bin/bash
              
              ps uax | grep tomcat | grep -v grep
              
              echo; echo
              
              timeout 5 curl -sL 127.0.0.1:8080/healthcheck -w "GET /healhcheck --- %{http_code}\n" -o /dev/null
              
              echo; echo
              
              timeout 5 bash -c "echo OK | telnet 192.168.0.2 3306"
              

              Запустив скрипт на целевом сервере получим в выводе что-то похоже на это: ( на данном этапе пока никаких проверок не происходит, просто убедимся, что скрипт отрабатывает ):


              $ bash script.bash
              
              GET /healhcheck --- 200
              
              tomcat    8264  0.0 32.1 2222884 326452 ?      Sl   Sep14   4:04 /usr/lib/jvm/java-1.8.0/bin/java -Djava.util.logging.config.file=/usr/share/tomcat8/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xmx128M -Djava.awt.headless=true -Djava.endorsed.dirs=/usr/share/tomcat8/endorsed -classpath /usr/share/tomcat8/bin/bootstrap.jar:/usr/share/tomcat8/bin/tomcat-juli.jar -Dcatalina.base=/usr/share/tomcat8 -Dcatalina.home=/usr/share/tomcat8 -Djava.io.tmpdir=/usr/share/tomcat8/temp org.apache.catalina.startup.Bootstrap start
              
              Trying 192.168.0.2 ...
              Connected to 192.168.0.2.
              Escape character is '^]'.
              Connection closed by foreign host.
              

              Проверка вывода скрипта


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


              Для начала установим пакет как CPAN модуль:


              $ cpanm Outthentic

              Далее слегка модифицируем наш скрипт, что бы его можно было запускать через Outthentic:


              • переименуем скрипт в strory.bash — это соглашение на имеование скриптов во фреймворке Outthentic:

              $ mv script.bash story.bash

              • запустим скрипт через консольный клиента strun, который поставляется вместе с фреймворком Outthentic и собственно запускает скрипты:

              $ strun

              Получим вывод, аналогично тому, когда мы запускали скрипт напрямую. Пока, что польза Outthentic не очевидна. Доходим до использования DSL. Создадим несколько простых проверочных правил для валидации вывода скрипта и положим правила в файл story.check:


              $ cat story.check
              
              GET /healhcheck --- 200
              tomcat8
              Connected to 192.168.0.2
              

              Запустим снова strun:


              $ strun 
              2017-09-18 17:39:55 :  [path] /
              GET /healhcheck --- 200
              
              tomcat    8264  0.0 32.1 2222884 326452 ?      Sl   Sep14   4:04 /usr/lib/jvm/java-1.8.0/bin/java -Djava.util.logging.config.file=/usr/share/tomcat8/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xmx128M -Djava.awt.headless=true -Djava.endorsed.dirs=/usr/share/tomcat8/endorsed -classpath /usr/share/tomcat8/bin/bootstrap.jar:/usr/share/tomcat8/bin/tomcat-juli.jar -Dcatalina.base=/usr/share/tomcat8 -Dcatalina.home=/usr/share/tomcat8 -Djava.io.tmpdir=/usr/share/tomcat8/temp org.apache.catalina.startup.Bootstrap start
              
              Trying 192.168.0.2 ...
              Connected to 192.168.0.2.
              Escape character is '^]'.
              Connection closed by foreign host.
              ok  scenario succeeded
              ok  text has 'GET /healhcheck --- 200'
              ok  text has 'tomcat8'
              ok  text has 'Connected to 192.168.0.2'
              STATUS  SUCCEED

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


              $ strun --format production

              Вот как будет выглядеть отчет в случае если, по каким-то причинам, у нас не запущен tomcat сервер:


              $ strun  --format production
              2017-09-18 17:44:43 :  [path] /
              not ok  text has 'tomcat8'
              GET /healhcheck --- 200
              
              Trying 192.168.0.2 ...
              Connected to 192.168.0.2.
              Escape character is '^]'.
              Connection closed by foreign host.
              STATUS  FAILED (2)

              Параметризация скрипта мониторинга


              Outthentic еще удобен и тем, что он позволяет просто добавлять и настраивать входные параметры для ваших скриптов. Допустим, чисто теоретически, мы захотим задавать хост и порт для сервера баз данных.


              Добавим дефолтные значения входных параметров через suite.yaml — файл для хранения дефолтных настроек в терминологии Outthentic:


              $ cat suite.yaml
              ---
              db_server:
                ip_address: "192.168.0.2"
                port: 3306
              

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


              Чуть-чуть изменим скрип мониторинга, что бы он брал свои входные параметры из-вне :


              $ cat script.bash
              
              #!/bin/bash
              
              db_server_address=$(config db_server.ip_address)
              db_server_port=$(config db_server.port)
              
              # ... следующие строчки кода остаются неизменными  
              # ...
              
              timeout 5 bash -c "echo OK | telnet $db_server_address $db_server_port $db_server_port"

              Теперь мы можем запустить наш скрипт с дефолтными параметрами:


              # для сервера баз данных будут использоваться ip address 192.168.0.2 и порт 3306
              $ strun 

              Или же переопределить параметры через командную строку:


              $ strun --param db_server.ip_address=192.168.0.3 --param db_server.port=3307

              Идем дальше, все что нам осталось сделать — это передать скрипт в эксплуатацию всем заинтересованным лицам, для этого нам понадобится Sparrow.


              Дистрибуция скрипта в виде Sparrow плагина


              Sparrow предоставляет две основные возможности дистрибуции скриптов — посредством публичного репозитария SparrowHub и посредством приватных репозитаиев, построенных на использовании удаленных репозиториях Git.


              Скорее всего, когда мы пишем чисто внутренние скрипты, нам больше подходит второй способ. Он к тому же еще и более простой, так как требует только того, что бы исходники скрипта лежали в некотором удаленном Git репозитарии, что же сделаем это:


              $ git init .
              $ git add .
              $ git commit -a -m "outthentic monitoring script"

              Добавив основные файлы проекта (story.bash и story.check ), нам остается настроить файл с мета данными ( который собственно и дает понять, что это не просто скрипт, а Sparrow плагин ) :


              $ cat sparrow.json
              
              {
                  "name" : "server-check"
                   "description" : "check server health"
              }

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


              Окей, мы фактически сделали наш первый Sparrow плагин, осталось отправить файлы в git remote:


              git add sparrow.json
              git commit -a -m "add sparrow meta file"
              git remote add origin $remote-git-repository
              git push -u origin master

              Использование готового скрипта мониторинга как Sparrow плагина


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


              Для начала, что бы использовать созданный нами плагин на каком-то целевом сервере, нам необходимо установить на этом сервере Sparrow клиент:


              $ cpanm Sparrow  

              Далее все просто, т.к. плагин — приватный и не будет загружен с общего репозитариия, уведомляем об этом Sparrow:


              $ echo "server-check $remote-git-repository" >> ~/sparrow.list

              Пара значений которую мы должны положить в локальный индексный файл ~/sparrow.list это название плагина ( не обязательно должно совпадать с тем, что мы использовали в предыдущей части и URL удаленного репозитария, где лежит исходный код плагина )


              Теперь обновляем индекс sparrow, что бы стал доступен добавленный нами плагин:


              $ sparrow index update 

              И устанавливаем сам плагин:


              $ sparrow plg install server-check

              Теперь мы можем запустить плагин как есть:


              $ sparrow plg install server-check

              Или же, передав ему параметры:


              $ sparrow plg install server-check --param db_server.ip_address=192.168.0.3 --param db_server.port=3307

              И наконец-то, все тоже самое можно запустить в виде Sparrow задачи:


              $ sparrow project create monitoring
              $ sparrow task add monitoring app1 server-check
              $ sparrow task ini monitoring/app1
              
              ---
              db_server:
                ip_address: "192.168.0.2"
                port: 3306
              
              ---
              
              $ sparrow task run monitoring/app1
              

              Последний вариант запуска удобен тем, что вы можете создать несколько задач ( с разными конфигурациями ) для запуска одного и того же плагина. По сути задача — это именованная конфигурация запуска Sparrow плагина, а проект — группа задач.


              PS


              На этом все. Если кому-то будет интересно, вот о чем я еще не сказал:


              • приватные Sparrow репозитарии можно настраивать не только через локальные индекс файлы ~/sparrow.list ( что неудобно при большом количестве плагинов ), но и через Sparrow::Nest — API управления приватными Sparrow репозитариями.


              • Sparrow плагины можно запускать удаленно на серверах ( через ssh ) с автоматической предустановкой Sparrow клиента — добро пожаловать в проект Sparrowdo. Там есть еще Perl6 API для Sparrow и много другого (-: !


              • Outthentic DSL позволяет создавать гораздо более сложные и интересные проверочные правила, чем просто соответствие подстроке. Среди них — проверка по Perl5 regexp, проверка по диапазону и последовательности строк, а так же динамическая генерация правила с помощью языков программирования общего назначения.

              Как всегда — вопросы, замечания, предложения, конструктивная критика — приветствуется.


              С уважением.


              Алексей

              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338144/


              Метки:  

              В режиме turbo. Как построить DevOps за 2 месяца

              Вторник, 19 Сентября 2017 г. 13:49 + в цитатник
              EFS_programm сегодня в 13:49 Администрирование

              В режиме turbo. Как построить DevOps за 2 месяца

                За небольшой промежуток времени DevOps в Программе «Единая фронтальная система» (ЕФС) прошел огромный путь, охватив ежедневную практику всех команд. Но интенсивные работы по развитию DevOps продолжаются, и в недалеком будущем жизненный цикл ЕФС претерпит новые изменения, направленные на ускорение ввода в эксплуатацию программного обеспечения (continuous delivery) и улучшения его качества (сквозное автотестирование). Но об этом чуть позже, а пока немного истории.



                Часть первая. Continuous Integration (CI) и Continuous Deployment (CD)


                Было время, когда все приложения Программы ЕФС собирались локально и деплоились на DEV среду вручную:

                • Java-приложения  с помощью Apache Maven на сервера приложений IBM WebSphere Application Server;
                • JS-код — с помощью npm+webpack/gulp на nginx.

                Функциональных подсистем, из которых состоит ЕФС, становилось все больше, добавлялись общие элементы архитектуры — IBM WebSphere eXtreme Scale, БД Oracle. Команды стали переходить на хранение кода в Atlassian Bitbucket, где сразу же выстраивали работу с ветками на основе gitflow.
                Появилась необходимость в частых сборках и автоматическом деплое. В качестве системы непрерывной интеграции остановились на Jenkins — благодаря удобной интеграции с Bitbucket, простоте конфигурирования, множеству дополнительных плагинов.

                Первые сборки в Jenkins запускались при появлении нового pull request’a и просто собирали Java или JS код, отправляя уведомление в Bitbucket об успешности или неуспешности прохождения сборки.

                Следующим шагом стали сборки дистрибутивов в NEXUS для последующего деплоя отдельных подсистем на стенды тестирования, запуск в Sonar’е Unit тестов и статического анализа.

                Наш Continuous Deployment начался с конца – с наката на  промышленные среды. В начале июня 2017 г. прилетела срочная задача – нужно раскатать все функциональные подсистемы ЕФС на промышленные среды в течение месяца. Но у нас с собой было работающее решение -Инсталлятор, которое позволяло устанавливать системы подобного уровня на любые среды на IBM WAS надежно и с гарантией установки.

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

                • найти информацию и контакты разработчиков каждой отдельной функциональной подсистемы;
                • сконфигурировать конфигурационные файлы;
                • оттестировать решение и …
                • … собственно внедрить!

                Поиск всех необходимых контактов и получение информации заняли около двух недель, параллельно разработчики начали заполнять конфигурации. Оставалось их только выверить, поправить ошибки и отдать на тестирование. Тестирование и отладка конфигураций заняли еще две недели. И 8 июля 2017 г. мы вышли в промышленную эксплуатацию.

                Как так быстро получилось?

                Все дело в парадигме построения Инсталлятора: модульность и отделение кода ядра от инструкций, а уже инструкций от конечных конфигураций.

                Система состоит из трех компонентов:

                1. ядро, большей частью на Jython, которое интерпретирует инструкции и конфигурации;
                2. инструкции в формате Json, где каждый объект, это какая-то сущность в IBM WAS, будь то DataSource или MQ или что-то еще;
                3. конфигурации в простейшем формате java properties, которые сами по себе являются парами «ключ-значение».

                Ядро принимает в качестве параметра вызова код подсистемы, который необходимо установить. Далее по коду системы находит набор инструкций в соответствующем JSON файле и параметры конкретной среды из property-файлов. Объединяясь, все объекты начинают интерпретироваться, фактически отдавая управление той или иной библиотечной функции.

                Преимущества  такой системы:

                • отделение кода от инструкций дает возможность накатывать настройки и автоматизировано выверять их. Эту особенность Инсталлятора на текущий момент не предоставляет ни одно из решений, доступных для IBM WAS на рынке;
                • реально быстрый старт: на обучение разработчиков уходят десятки минут, а не дни. А с новой системой GUI интерфейса, которая бы позволила делать все эти процедуры по клику, разработчиков вообще не пришлось бы отвлекать от бизнес-задач;
                • надежность: отлаженный однажды код можно переиспользовать многократно и быть уверенным, что он точно и безошибочно обслужит среду.

                Максимально актуально для промышленных сред.

                Часть вторая. Сегодня


                Система скриптов Деплоя или гибридные скрипты


                Исходные данные


                Функциональные подсистемы ЕФС в количестве 70 штук, жаждущие перейти со стадии CI к стадии CD.

                Соответственно, дистрибутив в составе каталогов:

                • BH – бинарные war-файлы для установки приложений на WAS;
                • PL – статичные JavaScript CSS, картинки, шрифты, подлежащие кэшированию средствами NGINX;
                • DB – содержит SQL процедуры для создания объектов БД и наполнения их данными, завернутые в liquibase;
                • XS – бинарные war-файлы для установки приложений, обеспечивающих конфигурацию для работы с сервером eXtreme Scale.


                Системное программное обеспечение для корректного функционирования разрабатываемого прикладного программного обеспечения в составе:
                • ОС;
                • СУБД Oracle;
                • сервер приложений (IBM WebSphere Application Server (WAS));
                • программное обеспечение класса MiddleWare, обеспечивающее гарантированную доставку сообщений (IBM WebSphere MQ);
                • распределенный кэш для кэширования неперсистентных данных (IBM WebSphere eXtreme Scale);
                • веб-сервер, обеспечивающий раздачу статики и балансировку на сервера приложений (Nginx).

                Ключевые задачи при разработке системы скриптов деплоя


                • Управление конфигурацией программного обеспечения класса MiddleWare;
                • Развертывание дистрибутива функциональных подсистем;
                • Автоматизированный рабочий процесс — для непрерывной доставки результата труда разработчика на все среды (dev, test, prod).

                Для реализации библиотек настройки ресурсов WAS и деплоя приложений используется Jython (Java-реализация языка сценариев). Для выполнения всех остальных задач, а также запуска скриптов Jython (хранятся в виде шаблонов Jinja) используется Ansible.

                Почему Ansible?

                Появившись позднее других аналогичных решений на рынке продуктов для конфигурационного управления, Ansible предлагает самый низкий порог вхождения. Обучиться работе с Ansible можно за достаточно короткое время. Создание отдельных модулей, расширяющих возможности продукта, не представляет большого труда, так как код продукта написан на Python. Язык сценариев – playbooks — достаточно прост и использует в качестве основы язык разметки YAML.
                Также важным преимуществом является использование SSH для управления узлами, отсутствие на узлах дополнительных агентов.

                Архитектура системы скриптов деплоя (ССД)


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

                Модель работы системы скриптов деплоя полностью соответствует концепции DevOps и предполагает интеграцию кода скриптов и дистрибутива приложения для продвижения по конвейеру от разработки до промышленной эксплуатации. Работа и интеграция скриптов основана на связке программных средств Ansible-Nexus-Jenkins.



                Схема крупнее.

                Рассмотрим подробнее функции и компоненты ССД.

                Основные функции:


                • установка ресурсов для серверов приложений WAS (DataSource, фабрики и очереди MQ и подобные);
                • управление приложениями функциональных подсистем ЕФС: установка, удаление, обновление, старт-стоп;
                • установка и настройка базовых и тонких настроек серверов приложений WAS;
                • обновление таблиц базы данных, используемых приложениями ЕФС;
                • обновление статики приложений ЕФС для сервера Nginx;
                • детальное логирование всех процессов выполнения вышеописанного функционала;
                • шифрование всей конфиденциальной информации с возможностью разграничения доступа к зашифрованной информации.

                Пакет системы скриптов развертывания можно разделить условно на 4 фрагмента, интересующие различные группы, участвующие в цикле IT-систем:

                1. Ядро – программная часть, функционал, разрабатываемый в рамках отдельных ролей Ansible. Реализуется разработчиками скриптов.

                1. Плейбуки Ansible – раннеры для запуска определенного функционала, реализованного в ролях с использованием параметров конкретной функциональной подсистемы. Используются системными администраторами для запуска скриптов, а также при интеграции с Jenkins. Запуск плейбука основан на соответствующем инвентори функциональной подсистемы, описывающем состав хостов и конфигурационных параметрах, используемых для отработки ролей.

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

                1. Параметры разработчиков – это файлы параметров, предназначенные для разработчиков функциональных подсистем для реализации определения ресурсов, необходимых для их приложений. Входят в состав дистрибутива поставляемой функциональной подсистемы (каталог conf).

                1. Системные настройки – настройки для системных администраторов конкретной среды: параметры подключения к веб-серверам, URL БД, логины, пароли и т.д., пароли хранятся в зашифрованном виде.

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

                Продолжение следует


                В ближайшем будущем мы планируем доработку процесса DevOps с целью сделать его более автоматизированным, более дружелюбным для всех участников — разработчиков, тестировщиков, админов dev и test сред, админов прома, а самое главное – более надежным. Мы планируем внедрить множество графических систем, помогающих на каждом шагу и берущих рутинные действия сотрудников на себя, для руководства – панель управления и мониторинга, где в режиме реального времени можно будет видеть движение процесса разработки, динамику автодеплоя на стенды тестирования, прохождение  автотестов.

                Для нас построение DevOps — новый интересный процесс, у которого нет универсальной инструкции. Будем рады пообщаться, обсудить ваш опыт и ответить на вопросы в комментариях!
                Original source: habrahabr.ru (comments, light).

                https://habrahabr.ru/post/338228/


                Метки:  

                Parrot Security OS — альтернатива Kali Linux

                Вторник, 19 Сентября 2017 г. 13:37 + в цитатник
                LukaSafonov сегодня в 13:37 Разработка

                Parrot Security OS — альтернатива Kali Linux

                  image
                   
                  Parrot Security OS — набирающий популярность security-дистрибутив, основанный на Debian-linux. Простой в освоении, подходит и для новичков и для профессионалов. В этой статье я расскажу об этом дистрибутиве и о развитии проекта от одного из контрибьюторов M. Emrah "UNS"UR с которым мне удалось пообщаться.

                  Parrot Secuirty OS


                  Набирающий популярность security-дистрибутив, основанный на Debian-linux. Довольно простой в освоении, подходит и для новичков и для профессионалов. Этот дистрибутив нацелен как на проведение тестирования на проникновение, так и на анонимную работу в сети Интернет.

                  Довольно легкий и эффективный инструмент, многие security специалисты нашли в нем замену все более «прожорливому» Kali, тем более что Parrot использует репозитории Kali для обновления. Использует оболочку MATE и дисплей-менеджер LightDM.



                  По фукнционалу он похож на Kali Linux, здесь тоже вместе с системой поставляется огромное количество специального программного обеспечения для тестирования безопасности.

                  Из отличительных особенностей можно назвать больший, нежелив Kali уклона в анонимность: интеграция I2P (invisible internet project) и предустановленные сервисы TOR.

                  Существует несколько версий (включающих 32 и 64 битные платформы) Parrot Security OS (3.8 — JollyRoger):

                  • Parrot Security 3.8 Full Edition — полная версия.
                  • Parrot Lite 3.8 Home Edition — без утилит, как основа для вашей сборки.
                  • Parrot AIR 3.8 — для тестирования беспроводных сетей.
                  • Parrot Cloud Edition — версия для деплоя в облачных сервсиах.
                  • Embedded Devices and IoT — Raspberry Pi, Orange Pi, Pine64.

                  Развитие проекта


                  Данный дистрибутив выглядит довольно перспективным, поэтому мне захотелось узнать о нем немного больше и я пообщался с одним из контрибьюторов и активным участником проекта — M. Emrah "UNS"UR.

                  M. Emrah "UNS"UR (meu @ Parrot Project) исследователь по безопасности в частной компании. Является контрибьютором и модератором форума проекта Parrot.

                  Команда состоит из добровольцев, которые в основном разделены на тех, кто занимается сообществом
                  и тех, кто пишет код. Команда Parrot OS всегда рада новым участникам:


                  Зачем создавать свой дистрибутив? Есть несколько причин сделать что-то подобное. Основная причина заключается в том, чтобы по рукой всегда было большинство используемых программных продуктов в самой удобной упаковке. В случае Parrot Security OS разработчиков по той или иной причини не устраивали текущие дистрибутивы, в виде простой и готовой к использованию сборки, с постоянными обновлениями, современными технологиями и актуальным списком ПО, которое реально используется при проведении пентеста. (Самое время вспонимть BlackArch c > 1500 утилит). Также этот дистрибутив должен подходить для повседневной работы.

                  Для тех, кто жалуется на фрагментацию проектов, есть информация о том, как работают сообщества с открытым исходным кодом: фрагментация вызывает диверсификацию и свободное выражение идей от других команд аналогичных проектов. Также в рамках работы над проектом ведется сотрудничество
                  Debian pkg-security.

                  В планах по развитию — создание полного пула документов и текстовых книг по разработке, системному администрированию, сетевому взаимодействию, криптографии, анализу вредоносных программ, пентестингу и т. д. Еще один вектор развития — это поддержка новых встроенных устройств. Также, основная проблема — аппаратная совместимость — требует много усилий, и в планах по развитию — привлечение новых членов сообщества, знающих Python, C, Bash.

                  Итог


                  Если вы занимаетесь практической информационной безопасностью — рекомендую ознакомиться с этим дистрибутивом, возможно он поможет сделать вашу работу по тестированию на проникновение более продуктивной и комфортной. Если у вас есть время и желание участвовать в развитии проекта Parrot Security OS — пишите в официальное сообщество, участники будут рады любой помощи проекту.
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/337712/


                  Метки:  

                  [Из песочницы] Из хирурга в разработчики: как в 40 лет сменить профессию?

                  Вторник, 19 Сентября 2017 г. 13:00 + в цитатник
                  alxpotapov сегодня в 13:00 Разное

                  Из хирурга в разработчики: как в 40 лет сменить профессию?

                  image


                  Привет! Меня зовут Алексей, я тимлид в крупной IT-компании. Сейчас мне 43, только в 40 лет я стал разработчиком, а до этого 15 лет был практикующим врачом-хирургом. Делюсь с вами, как в середине жизни я поменял профессию, о страхах, рисках и планах с этим связанных.


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


                  В 1998 году я закончил Самарский государственный медицинский университет, в 2000 – ординатуру по специальности «Хирургия» и одновременно защитил кандидатскую диссертацию. Переехал в г. Усинск (Республика Коми), где 8 лет проработал хирургом, потом был г. Ханты-Мансийск (Югра), где я продолжил трудиться по специальности.


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


                  За время работы повышал профессиональный уровень с помощью дополнительных специализаций, в том числе в больницах и госпиталях Франции, Чехии и США.


                  В целом моя карьера складывалась удачно, были профессиональные перспективы, но были и сложности. В России врач – это призвание. Не в том смысле, чтобы любить свое дело и посвящать ему себя полностью. Этого хватало. Несмотря на то, что ты ежедневно отвечаешь за жизнь и здоровье людей, тебе и твоей семье при этом приходится практически выживать. На севере (Республика Коми, ХМАО) еще можно получать хорошую зарплату врача, но в средней полосе ситуация крайне сложная. Туда мне предстояло вернуться: на малой родине (г. Пенза) остались родители, которым нужно помогать и поддерживать.


                  А в этом регионе с зарплатами совсем туго. Чтобы не оказаться без денег после очередного переезда, нужно было позаботиться о будущем заранее. Помогло хобби. В свободное время я выручал знакомых – настраивал программное обеспечение. Даже одно время подрабатывал программистом в пожарной части в Усинске. Начальник пожарной части был у меня пациентом, а потом предложил дополнительный заработок. В основном делал внешние отчеты и дорабатывал конфигурацию 1С Предприятия под их организацию. В общем пришлось освоить нехитрый язык 1С. Помимо этого написал и поддерживал систему учета в пожарной части на FireBird & Delphi.


                  Я был самоучкой, специальных знаний не имел, мне просто нравилось программирование само по себе. Решил, что дополнительная профессия не помешает, а станет моей подстраховкой. Потому в 2011 году поступил на заочное отделение в Томский государственный университет систем управления и радиоэлектроники по специальности «Программное обеспечение вычислительных систем и автоматизированных комплексов». Закончил его экстерном в 2014 году.


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


                  В том же 2014 году я с семьей переехал в Поволжье. Во всех городах средней полосы ситуация с зарплатами плачевная. На 20 тысяч докторской з/п, что мне предложили в Пензе, невозможно обеспечить достойную жизни для себя и семьи. Предстояло решить, что делать дальше. С одной стороны, привычная жизнь, профессиональные успехи, но критично низкая зарплата и грустная перспектива – в финансовом отношении ждать изменений не приходилось. С другой стороны, стартовая позиция в новой профессии и не факт, что «выстрелит» и я в возрасте «далеко за 30» чего-то достигну. Однако надежда поднять уровень жизни семьи и хорошо зарабатывать в будущем перевесила страхи.


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


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


                  В нынешней компании я оказался случайно. Жена увидела открытую вакансию, мы обсудили и решили: а почему бы не попробовать? Тогда я сомневался: крупная компания, серьезный продукт, у меня мало опыта, совсем не был уверен, что из этого что-то выйдет. Но на вакансию откликнулся, решил, что попытаться стоит. Выполнил тестовое, меня пригласили на собеседование в головной офис. Волновался, но все прошло гладко и меня взяли на испытательный срок на 2 месяца.


                  Плюсы, которые, я сразу оценил: хорошая зарплата, крутая команда, возможность работать удаленно и перспективы роста. Начал с позиции рядового разработчика, за 1,5 года дорос до тимлида. Сейчас практически все мое рабочее время занимает SIEM: подготовка кандидат-релизов, написание коннекторов, разработка дальнейшей функциональности. Иногда исправляю старые «баги», доставшиеся мне по наследству от коллег по работе, участвую в разработке общего для всех продуктов SDK взаимодействия между компонентами (REST). Задачи интересные, команда сильная.


                  Сейчас я пишу на Delphi, Go, немного поработал с C#. В качестве БД на хорошем уровне изучил MSSQL и MongoDB. Теперь я могу жить и работать в том регионе, где мне удобно, заниматься делом, которое мне по душе, и при этом не быть ущемленным финансово.


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


                  Теперь оглядываясь назад, вижу, что можно было бы сделать лучше и что стоит сделать обязательно, если вы также решите сменить профессию:


                  1. Оценить насколько нынешняя профессия схожа с новой. Конечно, проще уходить в смежную область или просто в коммерческую сферу, к примеру, были школьным учителем – пошли в репетиторы или педагогом в престижный частный лицей. В моем случае может показаться, что профессии совершенно разные, но сейчас могу сказать, что в хирургии и в программировании есть общее – в обоих случаях работа строится на определенных алгоритмах. Сходства помогают легче адаптироваться, не менять кардинально мышление.
                  2. Не верить в миф, что 30-40-летним сложнее усваивать знания. Проверил на себе: если поддерживать себя в хорошей физической форме, заниматься спортом и саморазвитием, то хватит концентрации и сил на освоение даже целой области знаний.
                  3. Оценить риски. В юности это редко кто делает, в 30-40 ты уже выбираешь осознанно, потому что несешь ответственность за близких. Кардинальные перемены требуют тщательной проработки и оценка ситуации и рисков нужна обязательно.
                  4. Подготовить подушку безопасности. Все просто: рассчитайте, чтобы вам было на что жить 5-6 месяцев.
                  5. Приготовьтесь к тому, что возможно первое время ваш доход будет ниже привычного. Вы пойдете на стартовую позицию, а значит, что полгода-год должны будете жить скромнее, а работать больше. Мой первый год в программировании уходил на работу, обучение и сон. Доход при этом был чуть выше зарплаты оперирующего хирурга на 1.5 ставки с высшей категорией и кандидатской ученой степенью. Это больше важно принять психологически, потому что будут мучить мысли о том, что все происходящее – большая ошибка, и возникнет желание вернуться в привычную колею.

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


                  Могло показаться, что я решился на авантюру, уйдя из профессии, но на самом деле, 80% успеха зависит от того, как вы спланируете перемены. Конечно, я понимал, что при должной подготовке могу рассчитывать и на хорошую должность, и на хороший доход, и на интересные задачи в крупном проекте. Так и вышло. Но повторюсь, что это результат не столько удачи и везения, сколько здравой оценки ситуации и планирования своих действий.

                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338232/


                  Метки:  

                  [Перевод] Оптимизация веб-серверов для повышения пропускной способности и уменьшения задержки

                  Вторник, 19 Сентября 2017 г. 12:52 + в цитатник
                  max_m сегодня в 12:52 Администрирование

                  Оптимизация веб-серверов для повышения пропускной способности и уменьшения задержки

                  • Перевод


                  Привет! Меня зовут Макс Матюхин, я работаю в SRV-команде Badoo. Мы в Badoo не только активно пишем посты в свой блог, но и внимательно читаем блоги наших коллег из других компаний. Недавно ребята из Dropbox опубликовали шикарный пост о различных способах оптимизации серверных приложений: начиная с железа и заканчивая уровнем приложения. Его автор – Алексей Иванов – дал огромное количество советов и ссылок на дополнительные источники информации. К сожалению, у Dropbox нет блога на Хабре, поэтому я решил перевести этот пост для наших читателей.


                  Это расширенная версия моего выступления на nginx.conf 2017 в сентябре этого года. В качестве старшего инженера по контролю качестве (SRE) в команде Dropbox Traffic я отвечаю за нашу сеть Edge: её надёжность, производительность и эффективность. Это proxy-tier-сеть, построенная на базе nginx и предназначенная как для обработки чувствительных к задержке метаданных, так и для передачи данных с высокой пропускной способностью. В системе, обрабатывающей десятки гигабитов в секунду и одновременно – десятки тысяч транзакций, чувствительных к задержкам, используются различные оптимизации эффективности и производительности: начиная с драйверов и прерываний, сквозь ядро и TCP/ IP-стек, и заканчивая библиотеками и настройками уровня приложения.


                  Пояснения


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


                  Это не пост о производительности Linux (хотя я и буду часто ссылаться на bcc, eBPF и perf) и не исчерпывающее руководство по использованию инструментов профилирования производительности (если вы хотите узнать о них больше, почитайте блог Брендана Грегга).


                  Это также не пост о производительности браузеров. Я буду упоминать о клиентской производительности применительно к оптимизациям задержек, но очень коротко. Хотите узнать больше – прочитайте статью High Performance Browser Networking Ильи Григорика.


                  И это не компиляция на тему лучших методик TLS. Хотя я и буду упоминать TLS-библиотеки и их настройки, вы и ваша команда обеспечения безопасности должны самостоятельно оценивать их производительность и влияние на безопасность. Чтобы узнать, насколько ваши серверы отвечают набору лучших методик, можете воспользоваться Qualys SSL Test. Если хотите узнать больше о TLS в целом, подпишитесь на рассылку Feisty Duck Bulletproof TLS Newsletter.


                  Структура поста


                  Мы рассмотрим оптимизации эффективности/ производительности на разных уровнях системы. Начнём с самого нижнего, аппаратно-драйверного, уровня: эти настройки можно применить практически к любому высоконагруженному серверу. Затем я перейду к ядру Linux и его TCP/IP-стеку: можете покрутить эти ручки на своих ящиках, активно использующих TCP. Наконец, мы обсудим настройки на уровне библиотек и приложений, которые по большей части применимы ко многим веб-серверам и в частности к nginx.


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


                  Оборудование


                  Процессор


                  Для хорошей производительности асимметричного RSA/EC выбирайте процессоры как минимум с поддержкой AVX2 (avx2 в /proc/cpuinfo) и желательно подходящие для вычислений с большими целыми числами (bmi и adx). Для симметричного шифрования выбирайте AES-NI для AES-шифров и AVX-512 – для ChaCha+Poly. У Intel есть сравнение производительности разных поколений процессоров с OpenSSL 1.0.2, где рассматривается влияние этих аппаратных оптимизаций.


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


                  Если выбираете среди продукции Intel, то смотрите на процессоры с архитектурой Haswell/ Broadwell, а лучше Skylake. У AMD впечатляющую производительность демонстрируют EPYC-модели.


                  Сетевая карта


                  Вам нужно как минимум 10 Гбит, а лучше – 25 Гбит. Если хотите передавать через один сервер с TLS ещё больше, то описанных здесь настроек может быть недостаточно – возможно, придётся сдвинуть TLS-фрейминг на уровень ядра (FreeBSD, Linux).


                  Что касается программного уровня, поищите open-source-драйверы с активными списками рассылки и сообществами. Это будет очень важным фактором, если (скорее «когда») вы будете заниматься решением проблем, связанных с драйверами.


                  Память


                  Эмпирическое правило: задачи, чувствительные к задержке, требуют более быстрой памяти; задачи, чувствительные к пропускной способности, требуют больше памяти.


                  Диски


                  Всё зависит от ваших требований к буферизации/кэшированию. Если вам нужно много буферизировать или кэшировать, то лучше выбрать SSD-диски. Некоторые даже устанавливают заточенные под флеш файловые системы (обычно log-structured), но они не всегда показывают более высокую производительность по сравнению с обычными ext4/ xfs.
                  В любом случае не сгубите свои флеш-накопители, забыв включить TRIM или обновить прошивку.


                  Операционные системы: низкий уровень


                  Прошивка


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


                  Драйверы


                  Здесь можно дать тот же совет, что и в отношении прошивки: по возможности используйте свежие версии, но не последние. Старайтесь разделить апгрейды ядра и обновления драйверов. Например, можете упаковать драйверы с помощью DKMS или предварительно скомпилировать их для всех версий ядра, которые вы используете. Благодаря этому, если после обновления ядра что-то пойдёт не так, вы быстрее поймёте, в чём проблема.


                  Процессор


                  Ваш лучший друг — репозиторий ядра (и инструменты, поставляемые с ним). В Ubuntu/ Debian вы можете установить пакет linux-tools с набором утилит, но в этом посте мы будем использовать только cpupower, turbostat и x86_energy_perf_policy. Для проверки связанных с процессором оптимизаций вы можете провести стресс-тестирование своего ПО с помощью любимого генератора нагрузки (например, Yandex.Tank). Вот презентация о лучших методиках нагрузочного тестирования от разработчиков nginx: NGINX Performance testing.


                  cpupower


                  $ cpupower frequency-info
                  ...
                    driver: intel_pstate
                    ...
                    available cpufreq governors: performance powersave
                    ...            
                    The governor "performance" may decide which speed to use
                    ...
                    boost state support:
                      Supported: yes
                      Active: yes

                  Проверьте, включён ли Turbo Boost, а если у вас процессор Intel, удостоверьтесь, что система работает с intel_pstate, а не с acpi-cpufreq или pcc-cpufreq. Если вы всё ещё используете acpi-cpufreq, обновите ядро. Если это невозможно, используйте режим performance. При работе с intel_pstate даже режим powersave должен выполняться с хорошей производительностью, но вам придётся проверить это самостоятельно.


                  Что касается простоя, чтобы посмотреть, что реально происходит с вашим процессором, вы можете с помощью turbostat напрямую заглянуть в процессорные MSR и извлечь информацию о питании, частоте и так называемых Idle States:


                  # turbostat --debug -P
                  ... Avg_MHz Busy% ... CPU%c1 CPU%c3 CPU%c6 ... Pkg%pc2 Pkg%pc3 Pkg%pc6 ...

                  Здесь вы видите реальную частоту процессора (да, /proc/cpuinfo вам врёт), а также текущее состояние ядра/набора ядер.


                  Если даже с драйвером intel_pstate процессор тратит на простой больше времени, чем вы думали, вы можете:


                  • переключить регулятор на performance;
                  • для повышения производительности настроить x86_energy_perf_policy.

                  А для очень чувствительных к задержке задач можно:


                  • использовать интерфейс /dev/cpu_dma_latency;
                  • для UDP-трафика использовать busy-polling.

                  Узнать больше об управлении питанием процессора в целом и P-состояниями в частности можно из презентации Balancing Power and Performance in the Linux Kernel с LinuxCon Europe 2015.


                  Привязка к процессору


                  Можно ещё больше уменьшить задержку, привязав поток или процесс к CPU. Например, в nginx есть директива worker_cpu_affinity, которая автоматически привязывает каждый процесс веб-сервера к конкретному ядру. Это позволяет исключить миграцию процесса / потока на другое ядро, уменьшить количество промахов кэша и ошибок страниц памяти, а также слегка увеличить количество инструкций в цикле. Всё это можно проверить через perf stat.


                  Но процессорная привязка негативно влияет на производительность, поскольку процессам дольше приходится ждать освобождения процессора. Это можно отслеживать с помощь запуска runqlat на одном из ваших PID nginx-воркера:


                  usecs               : count     distribution
                      0 -> 1          : 819      |                                        |
                      2 -> 3          : 58888    |******************************          |
                      4 -> 7          : 77984    |****************************************|
                      8 -> 15         : 10529    |*****                                   |
                     16 -> 31         : 4853     |**                                      |
                     ...
                   4096 -> 8191       : 34       |                                        |
                   8192 -> 16383      : 39       |                                        |
                  16384 -> 32767      : 17       |                                        |

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


                  Память


                  Все настройки Memory Management обычно сильно зависят от рабочего процесса, так что могу дать лишь такие рекомендации:



                  Современные процессоры представляют собой несколько отдельных процессоров, связанных очень быстрой шиной и совместно использующих различные ресурсы, начиная с кэша L1 на HT-ядрах и заканчивая кэшем L3 применительно к пакетам, памятью и PCIe-соединениями в рамках сокетов. Это и есть NUMA: многочисленные модули исполнения и хранения с быстрой шиной обмена данными.


                  Исчерпывающее описание NUMA и её применения содержится в статье Фрэнка Деннемана NUMA Deep Dive Series.


                  Короче, вы можете:


                  • игнорировать её, отключив в BIOS или выполняя своё ПО под numactl --interleave=all (так вы получите посредственную, но достаточно стабильную производительность);
                  • отказаться от неё, используя одноузловые серверы, как это делает Facebook с платформой OCP Yosemite;
                  • принять её, оптимизируя размещение процессора/ памяти в пространствах ядра и пользователя.

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


                  Для правильного использования NUMA вам нужно рассматривать каждый её узел в качестве отдельного сервера. Проверьте топологию с помощью numactl --hardware:


                  $ numactl --hardware
                  available: 4 nodes (0-3)
                  node 0 cpus: 0 1 2 3 16 17 18 19
                  node 0 size: 32149 MB
                  node 1 cpus: 4 5 6 7 20 21 22 23
                  node 1 size: 32213 MB
                  node 2 cpus: 8 9 10 11 24 25 26 27
                  node 2 size: 0 MB
                  node 3 cpus: 12 13 14 15 28 29 30 31
                  node 3 size: 0 MB
                  node distances:
                  node   0   1   2   3
                    0:  10  16  16  16
                    1:  16  10  16  16
                    2:  16  16  10  16
                    3:  16  16  16  10

                  Что нужно проверять:


                  • количество узлов;
                  • объём памяти для каждого узла;
                  • количество процессоров для каждого узла;
                  • расстояние между узлами.

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


                  Это можно проверить с помощью numastat:


                  $ numastat -n -c
                                    Node 0   Node 1 Node 2 Node 3    Total
                                  -------- -------- ------ ------ --------
                  Numa_Hit        26833500 11885723      0      0 38719223
                  Numa_Miss          18672  8561876      0      0  8580548
                  Numa_Foreign     8561876    18672      0      0  8580548
                  Interleave_Hit    392066   553771      0      0   945836
                  Local_Node       8222745 11507968      0      0 19730712
                  Other_Node      18629427  8939632      0      0 27569060

                  Также с помощью numastat можно получить статистику использования памяти по каждому узлу в формате /proc/meminfo:


                  $ numastat -m -c
                                   Node 0 Node 1 Node 2 Node 3 Total
                                   ------ ------ ------ ------ -----
                  MemTotal          32150  32214      0      0 64363
                  MemFree             462   5793      0      0  6255
                  MemUsed           31688  26421      0      0 58109
                  Active            16021   8588      0      0 24608
                  Inactive          13436  16121      0      0 29557
                  Active(anon)       1193    970      0      0  2163
                  Inactive(anon)      121    108      0      0   229
                  Active(file)      14828   7618      0      0 22446
                  Inactive(file)    13315  16013      0      0 29327
                  ...
                  FilePages         28498  23957      0      0 52454
                  Mapped              131    130      0      0   261
                  AnonPages           962    757      0      0  1718
                  Shmem               355    323      0      0   678
                  KernelStack          10      5      0      0    16

                  Теперь рассмотрим пример более простой топологии.


                  $ numactl --hardware
                  available: 2 nodes (0-1)
                  node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
                  node 0 size: 46967 MB
                  node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
                  node 1 size: 48355 MB

                  Поскольку узлы по большей части симметричны, мы можем привязать экземпляр нашего приложения к каждому NUMA-узлу с помощью numactl --cpunodebind=X --membind=X, а затем открыть его на другом порте. Пропускная способность увеличится благодаря использованию обоих узлов и уменьшению задержки за счёт сохранения локальности памяти.


                  Проверить эффективность размещения NUMA можно по задержке операций в памяти. Например, с помощью funclatency в BCC измерьте задержку операции, активно использующей память, допустим, memmove.
                  Наблюдать за эффективностью на стороне ядра можно с помощью perf stat, отслеживая соответствующие события памяти и планировщика:


                  # perf stat -e sched:sched_stick_numa,sched:sched_move_numa,sched:sched_swap_numa,migrate:mm_migrate_pages,minor-faults -p PID
                  ...
                                   1      sched:sched_stick_numa
                                   3      sched:sched_move_numa
                                  41      sched:sched_swap_numa
                               5,239      migrate:mm_migrate_pages
                              50,161      minor-faults

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


                  PCIe


                  Обычно нет нужды углубляться в решение проблем с PCIe, если только не возникает какой-то аппаратный сбой. Однако стоит хотя бы просто создать для своих PCIe-устройств «ширину шины», «скорость шины» и предупреждения RxErr/BadTLP. Это должно сэкономить вам часы на отладку из повреждённого железа или сбойного PCIe-согласования. Для этого можете воспользоваться lspci:


                  # lspci -s 0a:00.0 -vvv
                  ...
                  LnkCap: Port #0, Speed 8GT/s, Width x8, ASPM L1, Exit Latency L0s <2us, L1 <16us
                  LnkSta: Speed 8GT/s, Width x8, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
                  ...
                  Capabilities: [100 v2] Advanced Error Reporting
                  UESta:  DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- ...
                  UEMsk:  DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- ...
                  UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- ...
                  CESta:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr-
                  CEMsk:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+

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



                  Также советую прочитть статью Understanding PCIe Configuration for Maximum Performance, в ней подробнее рассматривается конфигурация PCIe, что может быть полезно при высоких скоростях, когда происходит потеря пакетов между картой и ОС.


                  Intel предполагает, что иногда управление питанием PCIe (ASPM) может приводить к большим задержкам, а значит, и к потере большего количества пакетов. Эту функцию можно отключить, введя pcie_aspm=off в командной строке ядра.


                  Сетевая карта


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


                  Также можете поискать руководства для вашей ОС. Например, в руководстве по настройке сетевой производительности в Linux от Red Hat Enterprise объясняются многие из упомянутых выше оптимизаций. У Cloudflare тоже есть хорошая статья о настройке этой части сетевого стека, хотя по большей части она посвящена ситуациям, когда нужна низкая задержка.


                  В ходе оптимизации вашим лучшим другом будет ethtool.
                  Примечание: если вы используете достаточно свежее ядро (а вам следует это сделать!), то вы также столкнётесь с некоторыми аспектами вашего пользовательского пространства. Например, для сетевых операций вы, вероятно, захотите использовать более свежие версии пакетов ethtool, iproute2 и, быть может, iptables/nftables.


                  Получить ценные сведения о том, что происходит с вашей сетевой картой, можно с помощью ethtool -S:


                  $ ethtool -S eth0 | egrep 'miss|over|drop|lost|fifo'
                       rx_dropped: 0
                       tx_dropped: 0
                       port.rx_dropped: 0
                       port.tx_dropped_link_down: 0
                       port.rx_oversize: 0
                       port.arq_overflows: 0

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


                  Что касается ядра, то нужно смотреть /proc/interrupts, /proc/softirqs и /proc/net/softnet_stat. Здесь есть два полезных BCC-инструмента: hardirqs и softirqs. Цель вашей оптимизации сети заключается в такой настройке системы, чтобы процессор использовался минимально, а пакеты не терялись.


                  Привязка прерываний


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


                  • для максимальной пропускной способности можно распределить прерывания по всем NUMA-узлам;
                  • для минимизации задержки можно ограничить прерывания одним NUMA-узлом (для этого вам может понадобиться уменьшить количество очередей, чтобы не превысить возможности одного узла (обычно приходится уменьшать вдвое с помощью ethtool -L).

                  Как правило, для этого вендоры предоставляют скрипты. Например, у Intel это set_irq_affinity.


                  Размеры кольцевого буфера


                  Сетевым картам нужно обмениваться информацией с ядром. Обычно это делается через структуру данных, называющуюся «кольцо». Текущий/ максимальный размер этого кольца можно посмотреть с помощью ethtool -g:


                  $ ethtool -g eth0
                  Ring parameters for eth0:
                  Pre-set maximums:
                  RX:                4096
                  TX:                4096
                  Current hardware settings:
                  RX:                4096
                  TX:                4096

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


                  • в более старых ядрах или драйверах без поддержки BQL высокие значения могут относиться к более высокому bufferbloat на TX-стороне;


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

                  Объединение прерываний


                  Этот механизм обеспечивает задержку уведомления ядра о новых событиях за счёт объединения нескольких сообщений в одно прерывание. Текущие настройки можно посмотреть с помощью ethtool -c:


                  $ ethtool -c eth0
                  Coalesce parameters for eth0:
                  ...
                  rx-usecs: 50
                  tx-usecs: 50

                  Также вы можете придерживаться статичных пределов (static limits), жёстко ограничив максимальное количество прерываний в секунду на одно ядро, или положиться на автоматическую аппаратную подстройку частоты прерываний в зависимости от пропускной способности.


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


                  Разгрузки


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


                  Все возможные разгрузки можно просмотреть с помощью ethtool -k:


                  $ ethtool -k eth0
                  Features for eth0:
                  ...
                  tcp-segmentation-offload: on
                  generic-segmentation-offload: on
                  generic-receive-offload: on
                  large-receive-offload: off [fixed]

                  Все ненастраиваемые разгрузки помечены суффиксом [fixed]. О них можно долго рассказывать, но я только приведу несколько эмпирических правил:


                  • не включайте LRO – вместо этого используйте GRO;
                  • будьте осторожны с TSO, поскольку это сильно зависит от качества ваших драйверов/ прошивки;
                  • не включайте TSO/ GSO на старых ядрах, потому что это может привести к чрезмерному bufferbloat.

                  Регулирование пакетов


                  Все современные сетевые карты оптимизированы под многопроцессорные системы, поэтому они распределяют пакеты по виртуальным очередям (обычно по одной на процессор). Когда это выполняется аппаратно, то называется RSS; когда за балансировку пакетов между процессорами отвечает ОС, это называется RPS (TX-эквивалент называется XPS). Если ОС пытается регулировать потоки к процессорам, которые в данный момент обрабатывают этот сокет, это называется RFS. А когда этим занимается железо, это называется «ускоренный RFS» или aRFS.


                  Вот несколько хороших методик:


                  • если вы используете новое оборудование 25 Гбит+, то в нём, вероятно, есть достаточно очередей и огромная таблица косвенной переадресации (indirection table), чтобы можно было применять RSS между всеми ядрами (некоторые более старые карты могут использовать только первые 16 процессоров);
                  • можете попробовать включить RPS, если:

                  1) у вас больше процессоров, чем аппаратных очередей, и вы хотите пожертвовать задержкой в пользу пропускной способности;
                  2) вы используете внутреннее туннелирование (например, GRE/ IPinIP), при котором сетевая карта не может применять RSS;


                  • не включайте RPS, если у вас достаточно старый процессор, не имеющий x2APIC;
                  • привязка каждого процессора к собственной TX-очереди посредством XPS – в целом хорошая идея;
                  • эффективность RFS во многом зависит от вашей рабочей нагрузки, а также от того, примените ли вы процессорную привязку.

                  Flow Director и ATR


                  Включённый Flow Director (или fdir в терминологии Intel) по умолчанию оперирует в режиме Application Targeting Routing, при котором реализуется aRFS посредством семплирования пакетов и регулирования потоков в процессорное ядро, где они, по-видимому, обрабатываются. Статистику можно посмотреть с помощью ethtool -S:$ ethtool -S eth0 | egrep ‘fdir’ port.fdir_flush_cnt: 0 …


                  Хотя Intel заявляет, что fdir в некоторых случаях увеличивает производительность, результаты одного исследования говорят о том, что это может также привести к переупорядочиванию 1% пакетов, что может довольно негативно сказаться на производительности TCP. Поэтому протестируйте самостоятельно и посмотрите, будет ли Flow Director полезен при вашей рабочей нагрузке, проверяя счётчик TCPOFOQueue.


                  Операционные системы: сетевой стек


                  Существует огромное количество книг, видео и руководств по настройке сетевого стека Linux, в которых растиражирован «карго-культ sysctl.conf». И хотя свежие версии ядра уже не требуют такого объёма настройки, как десять лет назад, а большинство новых TCP/ IP-свойств по умолчанию включены и хорошо настроены, люди продолжают копипастить свои старые sysctls.conf, которые они использовали для настройки ядер версий 2.6.18/ 2.6.32.


                  Для проверки эффективности сетевых оптимизаций сделайте следующее:


                  • с помощью /proc/net/snmp and /proc/net/netstat соберите TCP-метрики в рамках системы;
                  • добавьте метрики подключения, собранные с помощью ss -n --extended --info или при вызове внутри сервера getsockopt(``[TCP_INFO]``)/getsockopt(``[TCP_CC_INFO]``);
                  • соберите tcptrace(1)`ы образцов TCP-потоков;
                  • проанализируйте RUM-метрики приложения/ браузера.

                  В качестве источников информации о сетевых оптимизациях я обычно использую выступления специалистов по CDN, потому что, как правило, они знают, что делают. Например, Fastly on LinuxCon Australia. Полезно также послушать, что говорят разработчики ядра Linux, к примеру, на NetDevConf и Netconf.


                  Также стоит упомянуть про подробные материалы от PackageCloud по сетевому стеку Linux, особенно в свете того, что они сделали акцент на мониторинг, а не на «слепую» настройку:



                  И позвольте дать совет напоследок: обновите ядро ОС! Существует множество новых сетевых улучшений, и я говорю даже не об IW10 (который 2010) – я говорю о таких новинках, как автоматический выбор размера TSO, FQ, pacing, TLP и RACK. В качестве бонуса от апгрейда вы получите ряд улучшений масштабируемости, например, убранный кэш рутинга, неблокирующие сокеты прослушивания, SO_REUSEPORT и многое другое.


                  Обзор


                  Из недавних документов по работе с сетью в Linux особенно выделяется Making Linux TCP Fast. В нём на четырёх страницах собраны улучшения в ядре ОС за много лет. TCP-стек на стороне отправителя разбит на функциональные части:



                  Fair queueing и pacing


                  Fair queueing отвечает за соблюдение «справедливости» и уменьшает блокировку очереди между TCP-потоками, что положительно сказывается на частоте отбрасывания пакетов. Pacing, в свою очередь, равномерно распределяет пакеты во времени с частотой, определяемой Congestion Control, что ещё больше уменьшает долю потерянных пакетов, тем самым увеличивая пропускную способность.


                  Попутно хочу заметить, что fair queueing и pacing доступны в Linux посредством fq qdisc. Обе фичи требуются для BBR (впрочем, уже нет), но их можно использовать и с CUBIC, добиваясь 15–20%-ного снижения потери пакетов, а значит, и повышения пропускной способности в алгоритмах управления перегрузками (loss-based CCs). Только не используйте их на старых ядрах (<3.19), поскольку вы станете регулировать обычные ACKs и сломаете аплоад/ RPCs.


                  Автоматический выбор размера TSO и TSQ


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


                  Управление перегрузками


                  CC-алгоритмы сами по себе – объёмная тема, и в последние годы о них было много разговоров. Что-то из этого вылилось в код: tcp_cdg (CAIA), tcp_nv (Facebook) и tcp_bbr (Google). Мы не будем углубляться в их устройство, скажу лишь, что индикация о перегрузке во всех них основана больше на увеличении отсрочки (delay), чем на отбрасывании пакетов.


                  BBR – один из наиболее задокументированных, протестированных и практичных из всех новых алгоритмов управления перегрузками. На основании доли доставленных пакетов создаётся модель сетевого пути, а затем для увеличения ширины пропускания и минимизации RTT выполняются управляющие циклы. Это именно то, что мы ищем в нашем прокси-стеке.


                  Предварительные результаты экспериментов с BBR на наших Edge PoP показали увеличение скорости скачивания файлов:



                  Шестичасовой эксперимент с TCP BBR в Tokyo PoP: ось x — время, ось y — скорость скачивания на клиенте


                  Увеличение скорости наблюдалось по всем перцентилям. При изменениях бэкенда такого не происходит — обычно положительный результат наблюдается только p90+ пользователей (у которых самое быстрое интернет-подключение), поскольку мы считаем, что у всех остальных уже ограничена полоса пропускания. Настройки на сетевом уровне вроде изменения управления перегрузками или включения FQ/ pacing демонстрируют, что у пользователей ограничена не полоса пропускания, а, я бы сказал, присутствует «ограниченность TCP».


                  Если вы хотите больше узнать о BBR, то у APNIC есть хороший обзор для новичков (и сравнение с loss-based-управлением перегрузками). Более глубокую информацию можно извлечь из архивов почтовой рассылки bbr-dev (там сверху закреплено множество полезных ссылок). Если вас в целом интересует тема управления перегрузками, то можете понаблюдать за активностью Internet Congestion Control Research Group.


                  ACK-обработка и обнаружение пропадания пакетов


                  Теперь поговорим об обнаружении пропадания пакетов (loss detection). Снова упомяну про важность использования свежей версии ядра ОС. В TCP постоянно добавляются новые эвристики вроде TLP и RACK, а старые (наподобие FACK и ER) убираются. Нововведения работают по умолчанию, так что вам не придётся настраивать систему после апгрейда.


                  Приоритизация пользовательского пространства и HOL


                  API сокета пользовательского пространства (userspace socket API) предоставляют механизм явной буферизации, и после отправки чанков их уже невозможно перегруппировать. Поэтому при использовании мультиплексирования (например, в HTTP/2) это может привести к Head-of-Line блокировке и инверсии h2-приоритетов. Для решения этой проблемы были разработаны опция сокета и соответствующая опция sysctl net.ipv4.tcp_notsent_lowat. Они позволяют настраивать границы, в пределах которых сокет считает себя доступным для записи (то есть epoll в вашем приложении будет врать). Это может решить проблемы с HTTP/2-приоритизацией, но при этом плохо повлиять на пропускную способность, так что рекомендую проверить самостоятельно.


                  Sysctls


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



                  Лучше сделайте вот что:


                  • net.ipv4.tcp_slow_start_after_idle=0: главная проблема с медленным стартом (slow start) после простоя заключается в том, что «простой» определяется как один RTO, а этого слишком мало;
                  • net.ipv4.tcp_mtu_probing=1: полезно при наличии ICMP-«чёрных дыр» между вами и клиентами (наверняка они есть );
                  • net.ipv4.tcp_rmem, net.ipv4.tcp_wmem: нужно настроить так, чтобы подходило к BDP; только не забудьте, что больше – не значит лучше;
                  • echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect: если вы используете FQ+CUBIC, то это может помочь решить проблему слишком раннего выхода tcp_cubic из медленного старта.

                  Стоит упомянуть, что существует RFC-черновик (хотя и подзаброшенный) от Дэниела Штенберга, автора curl, под названием TCP Tuning for HTTP, в котором сделана попытка собрать все системные настройки, которые могут быть полезны для HTTP.


                  Уровень приложения: средний уровень


                  Инструментарий


                  Как и в случае с ядром ОС, пользовательское пространство крайне важно актуализировать. Начните с обновления своего инструментария, например, можете упаковать более свежие версии perf, bcc и так далее.


                  После этого можно приступать к настройке и отслеживанию поведения системы. В этой части поста мы будем по большей части опираться на профилирование процессора с помощью perf top, on-CPU flame-графики и ad hoc-гистрограммы из funclatency в bcc.



                  Инструментарий для компилирования


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


                  Помимо производительности, новые компиляторы могут похвастаться и новыми свойствами обеспечения безопасности (например, -fstack-protector-strong или SafeStack). Также современный инструментарий будет полезен, если вы хотите прогонять тесты через бинарные файлы, скомпилированные с использованием санитайзеров (например, AddressSanitizer и других).


                  Системные библиотеки


                  Рекомендую обновить системные библиотеки вроде glibc, иначе вы можете не получить свежих оптимизаций низкоуровневых функций из -lc, -lm, -lrt и так далее. Стандартное предупреждение: тестируйте самостоятельно, поскольку могут встречаться неожиданные регрессии.


                  zlib


                  Обычно за компрессию отвечает веб-сервер. В зависимости от объёма данных, проходящих через прокси, вы можете встретить упоминание zlib в perf top, например:


                  # perf top
                  ...
                     8.88%  nginx        [.] longest_match
                     8.29%  nginx        [.] deflate_slow
                     1.90%  nginx        [.] compress_block

                  Это можно оптимизировать на самом низком уровне: Intel и Cloudflare, как и отдельный проект zlib-ng, имеют собственные zlib-форки, обеспечивающие более высокую производительность за счёт использования новых наборов инструкций.


                  malloc


                  При обсуждении оптимизаций до этого момента мы по большей части ориентировались на процессор. Теперь же поговорим о памяти. Если вы активно используете Lua с FFI или тяжёлые сторонние модули, которые самостоятельно управляют памятью, то могли заметить рост потребления памяти из-за фрагментации. Эту проблему можно попытаться решить переключением на jemalloc или tcmalloc.


                  Использование кастомного malloc даёт следующие преимущества:



                  Если в конфигурации nginx вы используете многочисленные сложные регулярные выражения или активно применяете Lua, то могли встретить в perf top упоминание PCRE. Это можно оптимизировать, скомпилировав PCRE с JIT, а также включив её в nginx посредством pcre_jit on;.


                  Результат оптимизации можно проверить на flame-графиках или с помощью funclatency:


                  # funclatency /srv/nginx-bazel/sbin/nginx:ngx_http_regex_exec -u
                  ...
                       usecs               : count     distribution
                           0 -> 1          : 1159     |**********                              |
                           2 -> 3          : 4468     |****************************************|
                           4 -> 7          : 622      |*****                                   |
                           8 -> 15         : 610      |*****                                   |
                          16 -> 31         : 209      |*                                       |
                          32 -> 63         : 91       |                                        |

                  TLS


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


                  Сегодня первое, что вам нужно решить, — какую TLS-библиотеку вы будете использовать: Vanilla OpenSSL, OpenBSD’s LibreSSL или BoringSSL от Google. Определившись, вам нужно правильно её собрать: к примеру, у OpenSSL есть куча сборочных эвристик, позволяющих использовать оптимизации на базе сборочного окружения; у BoringSSL есть детерминистские сборки, но они более консервативны и по умолчанию просто отключают некоторые оптимизации. В любом случае, здесь вы наконец-то ощутите выгоду от выбора современного процессора: большинство TLS-библиотек могут использовать всё, от AES-NI и SSE до ADX и AVX-512. Можете воспользоваться встроенными тестами производительности. Например, в случае с BoringSSL это bssl speed.


                  Производительность по большей части зависит не от вашего железа, а от наборов шифров, которые вы собираетесь использовать, так что оптимизируйте их с осторожностью. Также знайте, что изменения в данном случае повлияют на безопасность вашего веб-сервера — самые быстрые наборы не обязательно лучшие. Если не знаете, какое шифрование использовать, можете начать с Mozilla SSL Configuration Generator.


                  Асимметричное шифрование


                  Если у вас “front”-сервис (сервис к которому пользователи подключаются напрямую), то вы могли столкнуться со значительным количеством TLS-«рукопожатий», а значит, немалая доля ресурсов вашего процессора тратится на асимметричное шифрование, которое необходимо оптимизировать.


                  Для оптимизации использования серверного процессора можете переключиться на сертификаты ECDSA, которые в десять раз быстрее, чем RSA. К тому же они значительно меньше, что может ускорить «рукопожатия» при наличии потерь пакетов. Но ECDSA сильно зависят от качества генератора случайных чисел в вашей системе, так что если вы используете OpenSSL, то удостоверьтесь, что у вас достаточно энтропии (в случае с BoringSSL об этом можно не волноваться).


                  И ещё раз напоминаю, что больше – не значит лучше, то есть использование сертификатов 4096 RSA ухудшит производительность в десять раз:


                  $ bssl speed
                  Did 1517 RSA 2048 signing ... (1507.3 ops/sec)
                  Did 160 RSA 4096 signing ...  (153.4 ops/sec)

                  Но меньше тоже не значит лучше: при использовании малораспространённого поля p-224 для ECDSA вы получите 60%-ное снижение производительности по сравнению с обычным p-256:


                  $ bssl speed
                  Did 7056 ECDSA P-224 signing ...  (6831.1 ops/sec)
                  Did 17000 ECDSA P-256 signing ... (16885.3 ops/sec)

                  Эмпирическое правило: самое распространённое шифрование обычно самое оптимизированное.


                  При запуске правильно оптимизированной библиотеки на основе OpenTLS, использующей сертификаты RSA, в своём perf top вы должны увидеть следующие трейсы: процессоры, использующие AVX2, а не ADX (например, с архитектурой Haswell), должны использовать кодовый путь AVX2:


                    6.42%  nginx                [.] rsaz_1024_sqr_avx2
                    1.61%  nginx                [.] rsaz_1024_mul_avx2

                  Более новые модели должны использовать обычный алгоритм Монтгомери с кодовым путём ADX:


                    7.08%  nginx                [.] sqrx8x_internal
                    2.30%  nginx                [.] mulx4x_internal

                  Симметричное шифрование


                  Если у вас много массовых передач данных вроде видео, фото и прочих файлов, то можете начать отслеживать в данных профилировщика упоминания о симметричном шифровании. Тогда просто удостоверьтесь, что ваш процессор поддерживает AES-NI и что вы настроили на сервере применение шифров AES-GCM. При правильно настроенном оборудовании в perf top должно выдаваться:


                    8.47%  nginx                [.] aesni_ctr32_ghash_6x

                  Но заниматься шифрованием/ дешифрованием будут не только ваши серверы, но и клиенты, причём у них априори гораздо более слабые процессоры. Без аппаратного ускорения это может быть достаточно сложной операцией, поэтому позаботьтесь о выборе алгоритма, который работает быстро без аппаратных технологий ускорения работы с шифрованием, например, ChaCha20-Poly1305. Это снизит TTLB для части мобильных клиентов.


                  В BoringSSL из коробки поддерживается ChaCha20-Poly1305, а в OpenSSL 1.0.2 можете использовать патчи Cloudflare. BoringSSL также поддерживает «шифрогруппы равного предпочтения», так что можете использовать следующую конфигурацию, которая позволит клиентам решать, какие шифры использовать, отталкиваясь от своих аппаратных возможностей (бесстыдно украдено из cloudflare/sslconfig):


                  ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256:ECDHE+3DES:RSA+3DES';
                  ssl_prefer_server_ciphers on;

                  Уровень приложения: высокоуровневые оптимизации


                  Для анализа эффективности ваших оптимизаций на этом уровне вам нужно собирать RUM-данные. В браузерах можно применять API Navigation Timing и Resource Timing. Ваши главные метрики — TTFB и TTV/ TTI. Вам сильно упростит итерирование, если эти данные будут представлены в форматах, удобных для составления запросов и графиков.


                  Компрессия


                  В nginx компрессия начинается с файла mime.types, определяющего соответствие между расширением файла и MIME-типом. Затем вам нужно определить, какой тип вы хотите передавать компрессору, с, например, gzip_types. Если хотите завершить этот список, то для автоматического генерирования mime.types с добавлением compressible == true to gzip_types можете воспользоваться mime-db.


                  Включая gzip, имейте в виду:


                  • это увеличивает потребление памяти (проблема решается путём ограничения gzip_buffers);
                  • вследствие буферизации это увеличивает TTFB (проблема решается с помощью gzip_no_buffer).

                  Отмечу, что HTTP-компрессия не ограничивается одним gzip: в nginx есть сторонний модуль ngx_brotli, который способен сжимать на 30% лучше, чем gzip.


                  Что касается настроек сжатия, давайте рассмотрим два отдельных случая: статичные и динамические данные.


                  В случае со статичными данными можно архивировать коэффициенты сжатия с помощью предварительной компрессии статичных ресурсов, сделав эту процедуру частью процесса сборки. Для gzip и brotli это подробно рассмотрено в посте Deploying Brotli for static content.


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


                  Буферизация внутри прокси может сильно влиять на производительность веб-сервера, особенно с учётом задержки. В прокси-модуле nginx есть разные настройки буферизации, которые можно регулировать в зависимости от местонахождения буферов и каждая из которых полезна в определённых случаях. С помощью proxy_request_buffering и proxy_buffering можно отдельно управлять буферизацией в обоих направлениях. Если включена буферизация, то верхняя граница потребления памяти определяется с помощью client_body_buffer_size и proxy_buffers, и по достижении этой границы запрос/ ответ будут буферизоваться на диске. Для ответов это можно отключить, присвоив proxy_max_temp_file_size значение 0.


                  Наиболее распространённые примеры использования:


                  • буферизация запроса/ ответа до определённого предела — в памяти, а затем сбрасывание на диск. Если включена буферизация запросов, вы можете отправить запрос на бэкенд только после его полного получения. А если включена буферизация ответов, вы можете мгновенно освободить поток выполнения бэкенда, как только тот будет готов ответить. При таком подходе улучшаются пропускная способность и защита бэкенда, но при этом растут задержка, потребление памяти и количество операций ввода/ вывода (хотя, если вы используете SSD, это не будет особой проблемой);
                  • буферизация отключена. Это делается при наличии маршрутов, чувствительных к уровню задержки, особенно в случае со стримингом. Но если вы отключите буферизацию, то вашему бэкенду придётся как-то работать с медленными клиентами (включая обработку атак Slow POST/ Slow Read);
                  • также посредством заголовка X-Accel-Buffering можно реализовать управляемую приложением буферизацию ответов.

                  Что бы вы ни выбрали, не забудьте протестировать это на TTFB и TTLB. Как уже упоминалось, буферизация может повлиять на количество операций ввода/ вывода и даже использование бэкенда, так что отслеживайте и эти моменты.


                  TLS


                  Теперь поговорим о высокоуровневых аспектах TLS и уменьшения задержки, которые можно реализовать с помощью правильной конфигурации nginx. Большинство оптимизаций, которые я буду упоминать, описаны в разделе Optimizing for TLS High Performance Browser Networking и в выступлении Making HTTPS Fast(er) на nginx.conf 2014. Настройки, описываемые в этой части поста, повлияют на производительность и безопасность вашего веб-сервера, так что, если вы в них не уверены, обратитесь к руководству Mozilla’s Server Side TLS Guide и/ или проконсультируйтесь со своими коллегами, отвечающими за безопасность.


                  Для проверки результатов оптимизаций можно использовать:



                  Возобновление сессии


                  Как любят говорить DBA, «самый быстрый запрос – тот, который вы не делали». Это касается и TLS: можно уменьшить задержку с помощью одного RTT, если вы кэшируете результаты «рукопожатия». Это можно сделать двумя способами:


                  • попросить клиент хранить все параметры сессии (в подписанном и зашифрованном виде) и отправлять их вам во время следующего «рукопожатия» (как и куки). На стороне nginx это конфигурируется посредством директивы ssl_session_tickets. Это не приводит к потреблению памяти на сервере, но имеет ряд недостатков:

                  1) понадобится инфраструктура для создания, ротации и распределения случайных ключей шифрования/ подписи для TLS-сессий. Помните, что не следует: 1) использовать управление ресурсами для хранения тикет-ключей; 2) генерировать эти ключи на основе каких-то неэфемерных вещей вроде даты или сертификата;
                  2) PFS будет зависеть не от конкретной сессии, а от TLS-тикет-ключа, так что если злоумышленник завладеет тикет-ключом, то сможет расшифровать любой перехваченный трафик в течение всего действия тикета;
                  3) ваше шифрование будет ограничено размером тикет-ключа. Не имеет смысла использовать AES-256, если вы применяете 128-битный тикет-ключ. Nginx поддерживает 128-битные и 256-битные ключи;
                  4) не все клиенты поддерживают тикет-ключи (хотя они поддерживаются всеми современными браузерами);


                  • хранить параметры TLS-сессии на сервере и отдавать клиенту только ссылку (ID). Это делается посредством директивы ssl_session_cache. Преимущество подхода в том, что PFS сохраняется между сессиями, а видов возможных атак становится гораздо меньше. Хотя у тикет-ключей тоже есть недостатки:

                  1) они потребляют на сервере ~256 байтов памяти на каждую сессию, так что вы не сможете хранить слишком много ключей слишком долго;
                  2) нет простого способа использовать их одновременно несколькими серверами. Так что вам понадобится ещё и балансировщик нагрузки, который будет отправлять тот же клиент на тот же сервер, чтобы сохранить локальн


                  Метки:  

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

                  Вторник, 19 Сентября 2017 г. 12:38 + в цитатник
                  pyrk2142 сегодня в 12:38 Разработка

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

                    Всем привет.

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

                    image

                    Большая часть хостингов выглядит примерно так

                    В далеком 2014 году я начал с проверки хостинга, которым пользовался в то время, и сразу же нашел CSRF на смену данных учетной записи. Если рассказывать о CSRF максимально просто, то эта уязвимость позволяет подделывать запросы от имени пользователя. Когда вы отправляете HTML-форму с одного домена на другой, браузер автоматически добавит в запрос ваши куки, установленные для целевого домена. Это позволяет злоумышленнику, не имея доступа к вашим кукам, отправить запрос на целевой домен от вашего имени с вашими куками. Для защиты от этой атаки используются CSRF-токены, проверка заголовка referer или ввод пароля для подтверждения важных запросов (это очень странное и небезопасное решение). Более подробно можно прочитать об этом в Википедии.

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

                    Однако, большая часть хостингов использует готовые панели управления. При этом администратор — это такой же аккаунт пользователя с кучей дополнительный прав: часто он имеет доступ ко всем аккаунтам на сервере. Для успешной атаки достаточно было создать страницу, код на которой автоматически отправит запрос на смену почты администратора, создание новой учётной записи с дополнительными правами или выполнение любого другого действия, которое возможно в панели управления. При желании можно сменить расширение такой странице на .jpg и разместить на ней скриншот, чтобы скрыть то, что происходит на ней на самом деле. После этого можно создавать тикет «Помогите, сайт не работает, вот скриншот ошибки» и прикладывать ссылку на страницу. После перехода администратора на эту страницу злоумышленник получает доступ ко всем аккаунтам пользователей хостинга.

                    Для выбора хостингов я объединил данные от нескольких рейтингов, после этого проверял цели на наличие CSRF-уязвимостей. Обычно я проверял хостинг только на наличие CSRF, так как это требует не очень много времени, но если случайно находил что-то еще, то сообщал об этих уязвимостях тоже.

                    Результаты и технические подробности


                    Как я писал выше, из десяти крупных хостингов больше половины были уязвимы. Всего из 100 хостингов, проверенных в 2014 году, 63 были уязвимы. Обычно это были простые CSRF, хотя встречались удивительные уязвимости, о которых я расскажу чуть позже.

                    В случае с самописными панелями обычно можно использовать CSRF для смены почты, а уже через почту восстановить пароль. Наиболее популярными из уязвимых панелей оказались ISPmanager, DirectAdmin, WHMCS и RootPanel. Я не могу назвать себя экспертом в панелях управления хостинга, поэтому могу ошибиться с версиями или другим тонкостями.

                    image


                    Это очень популярная панель, при этом ее актуальная версия не содержит CSRF. Однако, очень часто используется крайне устаревшая и дырявая старая версия панели.

                    image


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

                    image


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

                    image


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

                    Реакция хостингов


                    Возможно, самым правильным было бы опубликовать эту информацию в 2014 году и наблюдать за массовыми взломами и жалобами на хакеров. Но тогда я решил, что лучше сообщить об уязвимостях хостингам, и начал это делать. Я сообщил 63 уязвимым хостингам о проблеме, ответили всего 52. Через некоторое время 48 хостингов уязвимость у себя исправили, остальные ничего делать не стали.

                    Самое интересное началось потом. Через некоторое время я снова проверил эти хостинги, на части из них уязвимости волшебным образом появились снова. В 2015 и в 2016 я выбирал еще по 20-30 хостингов, проверял их и писал компаниям об уязвимостях. Ситуация похожая: больше половины хостингов уязвимы, через некоторое время после исправления часть уязвимостей появляется снова.

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

                    Некоторые компании были готовы исправлять уязвимость только путем установки обновления используемой панели управления («Мы используем последнюю версию панели, это проблема разработчиков»). При этом используемое ими поколение панели давно уже не обновляется.

                    В какой-то момент я решил заканчивать этот цирк и публиковать информацию о существующих проблемах.

                    Масштаб проблемы


                    Количество сайтов, которые могли быть взломаны с использованием этих уязвимостей, подсчитать довольно сложно. Если исходить из числа уязвимых хостингов (порядка 90-100) и минимального числа размещаемых сайтов в 1000 на каждом хостинге (я практически уверен, что реальное число намного больше), то это не менее 100 000 сайтов. Однако, косвенные признаки (ID аккаунтов и сайтов в системе, собственные заявления хостингов и информация из рейтингов) позволяют предполагать, что больше нескольких миллионов сайтов были в опасности. Понятно, что большую часть сайтов посещает пара человек в месяц, но даже при этом ситуация пугает.

                    Краткие результаты и состояние на сегодняшний день


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

                    Однако, есть бочка дегтя: даже сейчас ряд крупных хостингов, входящих в топ 10-20 самых популярных, содержат простые CSRF, которые позволяют получить доступ к аккаунту пользователя. Есть подозрение, что похожим способом можно выполнять запросы от имени администратора, но это отдельный вопрос (они используют свои панели управления, формат запросов администратора я не знаю). Среди менее крупных хостингов еще достаточно много тех, кто по каким-то причинам не исправил уязвимости. Кроме того, есть очень много сопутствующих уязвимостей, уровень безопасности в сфере хостинга удивительно низкий.

                    Несколько замечаний и забавных ситуаций


                    * Вознаграждения. Обычно предлагали бесплатный хостинг, при этом условия очень отличались: от действительно адекватных 10 000 — 20 000 тысяч на лицевой счет, до скидки в 120 рублей на 1 месяц, я впечатлен этой щедростью. Денежные вознаграждения предлагали гораздо реже, обычно это были символические 1000-2000 рублей, хотя было два намного более приятных случая. Но это единичные ситуации, большинство не предлагали ничего.

                    * Адекватность. Я ни разу не столкнулся с грубостью или угрозами. Самое неприятное — игнорирование сообщений или ответ «Мы не считаем это уязвимостью».

                    * Гениальные решения проблем с безопасностью. Несколько компаний решили бороться с угрозой CSRF странным образом: «администраторы не будут переходить по ссылкам от пользователей», а пользователи должны «позаботиться о себе сами».

                    * Другие уязвимости. Я искал только CSRF, но иногда находились и другие уязвимости. Самое частое — неправильная настройка HTTPS, иногда он отсутствовал вовсе, встречалась куча XSS, были странные утечки данных. Забавно, что у хостинга, который позиционировал себя, как самый безопасный (надежные системы, контроль доступа, круглосуточный мониторинг), можно было получить доступ к любому аккаунту, просто перебрав идентификаторы. Видимо, затраты на пиар слишком велики, чтобы делать безопасные сервисы.

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

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

                    Надеюсь, что на следующей неделе я расскажу еще что-нибудь интересное, например про незакрытые уязвимости в Телеграме.
                    Original source: habrahabr.ru (comments, light).

                    https://habrahabr.ru/post/337906/


                    Метки:  

                    Поиск сообщений в rss_rss_hh_new
                    Страницы: 1437 ... 1150 1149 [1148] 1147 1146 ..
                    .. 1 Календарь