вступление
Летом у меня была возможность немного поиграть с Дженкинсом в Кубернетесе . Точнее, я хотел посмотреть, как лучше всего запустить Docker Workflow Plugin .
Итак, идея заключалась в том, чтобы иметь Pod, работающий с Jenkins, и использовать его для запуска сборок, которые определены с помощью Docker Workflow Plugin . После долгих чтений и экспериментов я обнаружил, что есть много способов сделать это, с разными плюсами и минусами.
В этом сообщении рассматриваются все доступные варианты. Более конкретно:
- Строит работает прямо на Мастер
- Использование плагина Docker для запуска Slaves
- Использование Docker Plugin и Docker в Docker
- Использование клиентов Swarm
- Рой с докером в докере
Прежде чем я рассмотрю все возможные настройки, я думаю, что было бы полезно описать все эти плагины.
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?
Есть два подхода:
- Использование API Kubernetes
- Путем монтирования /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
- Самый простой возможный подход
- Минимальное количество плагинов
Cons
- Не масштабируется
- Прямой доступ к демону Docker
- Требуется доступ к определенным путям на хосте (см. Примечания к 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
- Достаточно просто
Cons
- Docker Plugin в настоящее время еще не существует?
- Прямой доступ к демону Docker
- Требуется доступ к определенным путям на хосте (см. Примечания к Docker Workflow Plugin )
Даже если мы оставим проблемы с плагином Docker в стороне, я все же хотел бы пойти на подход, который бы напрямую не связывался с демоном Docker , который работает за Kubernetes .
Docker Plugin Управляемые Рабы с DIND
Почему нужно использовать Docker в Docker ?
В нашем случае, чтобы не отставать от Кубернетеса назад.
Число возможностей здесь растет. Можно использовать DIND непосредственно на мастере Kubernetes , или можно объединить его с плагином Docker, чтобы каждый раб работал со своим собственным демоном и был на 100% изолирован.
В любом случае, то, что происходит во время сборки, полностью изолировано от остального мира. С другой стороны, это требует использования привилегированного режима. Это может быть проблемой, так как режим может быть недоступен в некоторых средах (т. Е. Он не был доступен в Google Container Engine в прошлый раз, когда я проверял).
Примечание: размещение демона докера в ведомом устройстве освобождает нас от необходимости использовать томовые монтирования на внешнем докере (помните, что для совместного использования рабочего пространства необходим только исполнитель и шаги рабочего процесса).
Pros
- 100% изоляция
- Не требует доступа к определенным путям на внешнем докере!
Cons
- сложность
- Требуется привилегированный режим
- Изображения Docker не «кэшируются»
Использование клиентов Swarm
DIND или нет, еще нужно найти решение для масштабирования, и Docker Plugin до сих пор не кажется идеальным решением. Также эквивалент плагина Docker для Kubernetes ( плагин Kubernetes ) действительно требует немного большего внимания. Итак, мы остались с Роем .
Использование Swarm действительно подходит, так как мы используем Kubernetes и его довольно тривиально для запуска N контейнеров, на которых работает клиент Swarm . Мы могли бы использовать контроллер репликации с соответствующим образом.
Pros
- Быстрый
- Масштабируемость
- крепкий
Cons
- Рабы должны управляться извне.
- Требуется доступ к определенным путям на хосте (см. Примечания к Docker Workflow Plugin )
Использование клиентов Swarm с DIND
Основная проблема, связанная с DIND в этом случае использования, заключается в том, что изображения в «в Docker » не кэшируются. Можно попробовать поэкспериментировать с разделением реестра Docker, но я не уверен, возможно ли это вообще.
С другой стороны, с большинством оставшихся опций нам нужно использовать монтирование hostPath, которое может не работать в некоторых средах.
Решение, которое решает обе вышеупомянутые проблемы, состоит в том, чтобы объединить Swarm с DIND
С Swarm клиенты остаются (а не стираются после каждой сборки). Это решает проблемы кэширования изображений.
Кроме того, с DIND нам больше не нужно использовать хост-монтирования через Kubernetes.
Так что у нас есть победа — победа.
Pros
- Быстрый
- Масштабируемость
- крепкий
- 100% изоляция
- Изображения кешируются
Cons
- Рабы должны управляться извне
Заключительные мысли
Я устал от всех вышеперечисленных установок как часть документа, который я делал: « Jenkins для Docker Workflow в Kubernetes », и я подумал, что должен поделиться. Есть еще вещи, которые я бы хотел попробовать:
- Используйте секреты для аутентификации рабов.
- Удалить беспорядок
- и т.д
Не стесняйтесь добавлять впечатления, предложения, исправления в комментариях.
Я надеюсь, что вы сочли полезным.