Статьи

Установки Jenkins для рабочих процессов в Kubernetes и Docker

вступление

Летом у меня была возможность немного поиграть с  Дженкинсом  в  Кубернетесе . Точнее, я хотел посмотреть, как лучше всего запустить  Docker Workflow Plugin  .

Итак, идея заключалась в том, чтобы иметь Pod, работающий с  Jenkins,  и использовать его для запуска сборок, которые определены с помощью  Docker Workflow Plugin . После долгих чтений и экспериментов я обнаружил, что есть много способов сделать это, с разными плюсами и минусами. 

В этом сообщении рассматриваются все доступные варианты. Более конкретно:

  1. Строит работает прямо на Мастер
  2. Использование  плагина Docker  для запуска Slaves
  3. Использование  Docker Plugin  и  Docker в Docker
  4. Использование   клиентов Swarm
  5. Рой  с  докером в докере

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

Docker Plugin

Дженкинс  плагин , который использует  Docker  для создания и использования рабов. Он использует http для связи с  Docker  и создания новых контейнеров. Эти контейнеры должны быть готовы только для Java, а также запускать  SSHD , чтобы мастер мог подключиться к ним и выполнить свою магию. В Интернете есть много изображений для ведомых контейнеров, самым популярным на момент моего присоединения был  раб Еварга Дженкинс .

Плагин можно использовать, но он немного ненадежен, так как создает   контейнер Docker, но иногда ему не удается подключиться к ведомому устройству и повторять  попытки (обычно требуется от 2 до 3 попыток) . Опробовано много разных ведомых образов и много разных методов аутентификации  (пароль, аутентификация ключа и т. Д.)  С похожим опытом. 

роиться

Наличие плагина для создания ведомого является одним из подходов. Другой — «Приведи своих собственных рабов», и это в значительной степени то, что представляет собой рой. Идея состоит в том, что   мастер Jenkins запускает   плагин Swarm, а пользователи отвечают за запуск клиентов Swarm (это всего лишь процесс Java). 

java -jar /path/to/swarm-client.jar http://jenkins.master:8080

Клиент подключается к мастеру и сообщает, что он запущен и работает. Затем мастер может начать сборку на клиенте.

Docker Workflow Plugin

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

Почему?

Инкапсулировать все требования вашей сборки в   образ Docker и не беспокоиться о том, как их установить и настроить.

Вот как выглядит пример сценария Docker Workflow: 

node('docker') {
   docker.image('maven').inside {
      git 'https://github.com/fabric8io/example-camel-cdi'
      sh 'mvn clean install'
   }
}

Примечание . Вам не нужно использовать  подключаемый модуль Docker  для  подключаемого модуля Docker Workflow .

Также плагин Docker Workflow  использует  бинарный файл Docker . Это означает, что вам нужно установить Docker- клиент везде, где вы собираетесь использовать   Docker Workflow Plugin .

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

Теперь мы готовы посмотреть, каковы возможные настройки.

Нет рабов

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

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

Как использовать докер-хост внутри Kubernetes?

Есть два подхода:

  1. Использование API Kubernetes
  2. Путем монтирования /var/run/docker.sock

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

#!/bin/bash
KUBERNETES=https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT
TOKEN=`cat /var/run/secrets/kubernetes.io/serviceaccount/token`
POD=`hostname`
curl -s -k -H "Authorization: Bearer $TOKEN" $KUBERNETES/api/v1/namespaces/$KUBERNETES_NAMESPACE/pods/$POD | grep -i hostIp | cut -d "\"" -f 4

Вы можете (2), указав монтирование тома hostDir на  Jenkins  POD.

{
"volumeMounts": [
  {
    "name": "docker-socket",
    "mountPath": "/var/run/docker.sock",
    "readOnly": false
  }
],


"volumes": [
  {
    "name": "docker-socket",
    "hostPath": {
      "path": "/var/run/docker.sock"
    }
  }
]
}

Фактический пример такой настройки можно найти  здесь .

Pros

  1. Самый простой возможный подход
  2. Минимальное количество плагинов

Cons

  1. Не масштабируется
  2. Прямой доступ к   демону Docker
  3. Требуется доступ к определенным путям на хосте (см. Примечания к  Docker Workflow Plugin )

Docker Plugin Управляемые Рабы

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

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

Как упоминалось выше, мастер должен будет подключиться через ssh к подчиненному. Для этого нужно либо настроить учетные данные, либо соответствующие ключи ssh. В обоих случаях необходимо обновить XML-конфигурацию подключаемого модуля Docker, чтобы ссылаться на идентификатор   конфигурации учетных данных  Jenkins (например, см. Этот  config.xml ) .

Так что же это за идентификатор?

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

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

#Generate master.key and secret
MAGIC="::::MAGIC::::"
mkdir -p /var/jenkins_home/secrets
openssl rand -hex 128 > /var/jenkins_home/secrets/master.key
openssl dgst -sha256 -binary /var/jenkins_home/secrets/master.key > /tmp/master.hashed
HEX_MASTER_KEY=`head -c 16 /tmp/master.hashed | xxd -l 16 -p`
openssl rand 259 > /tmp/base
echo $MAGIC >> /tmp/base
openssl enc -aes-128-ecb -in /tmp/base -K $HEX_MASTER_KEY -out /var/jenkins_home/secrets/hudson.util.Secret

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

#!/bin/bash
IN=`echo $1 | base64`
SUFFIX="::::MAGIC::::"
MASTER_KEY=`cat /var/jenkins_home/secrets/master.key`
HASHED_MASTER_KEY=`echo -n $MASTER_KEY | sha256sum | cut -d " " -f 1`
HASHED_MASTER_KEY_16=${HASHED_MASTER_KEY:0:16}
openssl enc -d -aes-128-ecb -in /var/jenkins_home/secrets/hudson.util.Secret -K $HASHED_MASTER_KEY -out /tmp/hudson.key
HUDSON_KEY=`cat /tmp/hudson.key`
HUDSON_KEY_TRIMMED=${HUDSON_KEY:0:-16}
HUDSON_KEY_16=${HUDSON_KEY_TRIMMED:0:16}
echo $HUDSON_KEY_16 > /tmp/hudson16.key
echo "$IN$SUFFIX" > /tmp/jenkins.password
openssl enc -aes-128-ecb -in /tmp/hudson16.key -out /tmp/jenkins.password.enc -K $IN

На самом деле шифровать пароли. Я не рекомендовал бы это никому, я просто показываю сценарии, чтобы подчеркнуть, насколько это сложно. Конечно, подобные сценарии также используют детали, встроенные в  плагин Credentials,   и также немного хакерские. Что я нашел немного более элегантный подход к настройке учетных данных, бросив следующий сценарий groovy внутри Jenkins init.groovy.d: 

import jenkins.model.*
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.common.*
import com.cloudbees.plugins.credentials.domains.*
import com.cloudbees.plugins.credentials.impl.*
import com.cloudbees.jenkins.plugins.sshcredentials.impl.*
import hudson.plugins.sshslaves.*;

domain = Domain.global()
store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore()

priveteKey = new BasicSSHUserPrivateKey(
CredentialsScope.GLOBAL,
"jenkins-slave-key",
"root",
new BasicSSHUserPrivateKey.UsersPrivateKeySource(),
"",
""
)

usernameAndPassword = new UsernamePasswordCredentialsImpl(
  CredentialsScope.GLOBAL,
  "jenkins-slave-password", "Jenkis Slave with Password Configuration",
  "root",
  "jenkins"
)

store.addCredentials(domain, priveteKey)
store.addCredentials(domain, usernameAndPassword)

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

Pros

  1. Достаточно просто

Cons

  1. Docker Plugin  в настоящее время еще не существует?
  2. Прямой доступ к   демону Docker
  3. Требуется доступ к определенным путям на хосте (см. Примечания к  Docker Workflow Plugin )

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

Docker Plugin Управляемые Рабы с DIND

Почему нужно использовать  Docker  в  Docker ?

В нашем случае, чтобы не отставать от  Кубернетеса  назад.

Число возможностей здесь растет. Можно использовать DIND непосредственно на  мастере Kubernetes  , или можно объединить его с  плагином Docker,  чтобы каждый раб работал со своим собственным демоном и был на 100% изолирован.

В любом случае, то, что происходит во время сборки, полностью изолировано от остального мира. С другой стороны, это требует использования привилегированного  режима. Это может быть проблемой, так как режим может быть недоступен в некоторых средах  (т. Е. Он не был доступен в  Google Container Engine в  прошлый раз, когда я проверял).

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

Pros

  1. 100% изоляция
  2. Не требует доступа к определенным путям на внешнем докере!

Cons

  1. сложность
  2. Требуется привилегированный режим
  3. Изображения Docker не «кэшируются»

Использование клиентов Swarm

DIND или нет, еще нужно найти решение для масштабирования, и  Docker Plugin  до сих пор не кажется идеальным решением. Также эквивалент  плагина Docker  для  Kubernetes  (  плагин Kubernetes ) действительно требует немного большего внимания. Итак, мы остались с  Роем .

Использование  Swarm  действительно подходит, так как мы используем  Kubernetes  и его довольно тривиально для запуска N контейнеров, на которых работает   клиент Swarm . Мы могли бы использовать контроллер репликации с соответствующим образом.

Pros

  1. Быстрый
  2. Масштабируемость
  3. крепкий

Cons

  1. Рабы должны управляться извне.
  2. Требуется доступ к определенным путям на хосте (см. Примечания к  Docker Workflow Plugin )

Использование клиентов Swarm с DIND

Основная проблема, связанная с DIND в этом случае использования, заключается в том, что изображения в «в  Docker » не кэшируются. Можно попробовать поэкспериментировать с разделением  реестра Docker,  но я не уверен, возможно ли это вообще.

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

Решение, которое решает обе вышеупомянутые проблемы, состоит в том, чтобы объединить  Swarm  с DIND

С  Swarm  клиенты остаются  (а не стираются после каждой сборки).  Это решает проблемы кэширования изображений.

Кроме того, с DIND нам больше не нужно использовать хост-монтирования через Kubernetes.

Так что у нас есть победа — победа.

Pros

  1. Быстрый
  2. Масштабируемость
  3. крепкий
  4. 100% изоляция
  5. Изображения кешируются

Cons

  1. Рабы должны управляться извне

Заключительные мысли

Я устал от всех вышеперечисленных установок как часть документа, который я делал: « Jenkins для Docker Workflow в Kubernetes », и я подумал, что должен поделиться. Есть еще вещи, которые я бы хотел попробовать:

  • Используйте  секреты  для аутентификации рабов.
  • Удалить беспорядок
  • и т.д

Не стесняйтесь добавлять впечатления, предложения, исправления в комментариях.

Я надеюсь, что вы сочли полезным.