Статьи

Установки 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

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

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

роиться

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

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

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

Docker Workflow Plugin

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

Почему?

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

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

1
2
3
4
5
6
node('docker') {
   docker.image('maven').inside {
      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), используя простой скрипт оболочки, подобный приведенному ниже.

1
2
3
4
5
#!/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.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
{
"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 и Kubernetes уже существуют, звучит как хорошая идея использовать их в качестве пула ресурсов.

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

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

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

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

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

1
2
3
4
5
6
7
8
9
#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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
#!/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:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 управляемый Slaves с 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 », и я подумал, что должен поделиться. Есть еще вещи, которые я бы хотел попробовать:

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

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

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