Стадия жизненного цикла модуля: Предварительная версия (preview)
У модуля есть требования для установки
В этом руководстве показано, как аутентифицировать CI/CD-пайплайн в хранилище
образов и пушить образы в проект. Примеры платформонезависимы (переносимый shell
с docker); перенесите шаги на примитивы job’ов и секретов вашей CI-системы.
Хранилище аутентифицирует клиентов по токенам Kubernetes (проверка через
TokenReview) и авторизует их через RBAC на виртуальном ресурсе
payloadrepositoryaccesses — см. Аутентификация и авторизация.
Поэтому пайплайну нужны: сервисный аккаунт робота, RBAC с правом push и
токен, которым пайплайн логинится.
Предварительные условия
-
Адрес хранилища:
payload-registry.${PUBLIC_DOMAIN}. -
CI-раннер должен доверять TLS-сертификату хранилища. Если сертификат публично доверенный — делать ничего не нужно. Если используется собственный CA, добавьте CA на раннер, чтобы
docker/craneмогли его проверить, например:# Docker читает CA для конкретного registry по этому пути: install -d /etc/docker/certs.d/payload-registry.${PUBLIC_DOMAIN} cp ca.crt /etc/docker/certs.d/payload-registry.${PUBLIC_DOMAIN}/ca.crt
1. Создайте сервисный аккаунт робота и выдайте право push
Образы живут под неймспейсом проекта: первый сегмент пути в ссылке на образ — это
неймспейс (например, project-1/app/... лежит в неймспейсе project-1). Создайте
отдельный ServiceAccount для CI и выдайте ему create (push) на пути, в которые
он может писать. Сужайте resourceNames максимально.
apiVersion: v1
kind: ServiceAccount
metadata:
name: ci-robot
namespace: project-1
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: payload-registry-ci-push
namespace: project-1
rules:
- apiGroups: ["payload-registry.deckhouse.io"]
resources: ["payloadrepositoryaccesses"]
# Сузьте до путей, в которые CI может писать, например app/** для project-1/app/...
resourceNames: ["app/*", "app/**"]
# create = push, get = pull, list = список тегов, delete = удаление
verbs: ["get", "list", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: payload-registry-ci-push
namespace: project-1
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: payload-registry-ci-push
subjects:
- kind: ServiceAccount
name: ci-robot
namespace: project-1Соответствие глагол→операция (create = push, get = pull, list = список
тегов, delete = удаление) описано в разделе Уровни доступа.
2. Получите токен для робота
Пайплайн логинится в хранилище с именем пользователя token и токеном Kubernetes
в качестве пароля. Есть два надёжных способа получить токен; выберите по вашей
политике безопасности.
Вариант A — долгоживущий токен ServiceAccount (рекомендуется для CI)
Создайте Secret типа токена ServiceAccount. Kubernetes наполнит его
неистекающим токеном, привязанным к SA робота. Это самый простой надёжный
вариант: токен не истекает посреди пайплайна, для его выпуска не нужны
дополнительные права в кластере, а радиус поражения ограничен RBAC выше.
apiVersion: v1
kind: Secret
metadata:
name: ci-robot-token
namespace: project-1
annotations:
kubernetes.io/service-account.name: ci-robot
type: kubernetes.io/service-account-tokenПрочитайте токен и положите его в маскированную / защищённую CI-переменную
(например, REGISTRY_TOKEN):
kubectl -n project-1 get secret ci-robot-token -o jsonpath='{.data.token}' | base64 -dТак как токен сам по себе не истекает, относитесь к нему как к любому долгоживущему секрету: храните маскированным/защищённым, не выводите в лог, периодически ротируйте и отзывайте, когда он больше не нужен (см. Отзыв и ротация креденшла).
Вариант B — короткоживущий токен на каждый job
Большинству пайплайнов это не нужно — предпочитайте вариант A. Используйте только если ваша политика безопасности запрещает долгоживущие токены. Выпускайте свежий токен в начале каждого job через TokenRequest API:
kubectl -n project-1 create token ci-robot --duration=2hНюансы:
- Идентичности, выполняющей эту команду, нужно право
createнаserviceaccounts/tokenдляci-robot(у обычного cluster-admin kubeconfig его по умолчанию нет) — то есть CI всё равно хранит кластерный креденшл (kubeconfig) с этим правом. - Время жизни ограничено флагом apiserver
--service-account-max-token-expiration. Запрашивайте не меньше максимальной длительности job, и перелогинивайтесь, если job может пережить токен.
Отзыв и ротация креденшла
Токен проверяется относительно ServiceAccount (а для варианта A — ещё и
относительно его токен-Secret) при каждом логине. Отозвать можно двумя способами:
-
Ротация одного креденшла (вариант A): удалите токен-
Secretи создайте новый, затем обновите CI-переменную. Старый токен перестаёт работать вскоре после этого (проверка токена кратко кэшируется apiserver, поэтому отзыв вступает в силу за десятки секунд, а не мгновенно).kubectl -n project-1 delete secret ci-robot-token kubectl -n project-1 apply -f ci-robot-token.yaml # пересоздать, затем перечитать токен -
Полностью отключить робота: удалите
ServiceAccount. Это инвалидирует все его токены сразу (и вариант A, и вариант B — они больше не соответствуют существующему ServiceAccount) и убирает его как RBAC-subject.kubectl -n project-1 delete serviceaccount ci-robot
Удаление RoleBinding оставит токен валидным, но снимет авторизацию на push/pull
(логин пройдёт, но любая операция будет запрещена).
3. Пайплайн: логин, сборка, push
Переносимый job (ложится напрямую в script: GitLab CI, шаги run: GitHub
Actions и т.д.). REGISTRY_TOKEN — маскированная CI-переменная из шага 2.
set -euo pipefail
REGISTRY="payload-registry.${PUBLIC_DOMAIN}"
IMAGE="${REGISTRY}/project-1/app/myservice:${CI_COMMIT_SHORT_SHA}"
# 1. Аутентификация (имя пользователя всегда литерал "token").
echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" -u token --password-stdin
# 2. Сборка и тег.
docker build -t "${IMAGE}" .
# 3. Push.
docker push "${IMAGE}"Используйте --password-stdin (а не -p), чтобы токен не попал в список
процессов и в лог job’а.
Квота хранилища в пайплайнах
Push проверяется на квоту хранилища проекта (ресурс PayloadRegistryQuota, если
он задан в неймспейсе проекта). Когда проект превысил квоту, push отклоняется с
HTTP 413 и кодом ошибки QUOTA_EXCEEDED; docker push завершается с ошибкой.
Проверьте и при необходимости поднимите квоту:
kubectl -n project-1 get prquota defaultЧтобы пайплайны не забивали проект, удаляйте устаревшие теги и полагайтесь на сборку мусора (GC) хранилища (учтите: квота считает физический футпринт, который освобождается именно GC, а не просто удалением манифеста).
Pull из CI
Тот же токен (с доступом хотя бы get к пути) работает и для pull:
echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" -u token --password-stdin
docker pull "${REGISTRY}/project-1/app/myservice:${TAG}"Чтобы использовать образы внутри кластера (в Pod’ах), используйте
imagePullSecrets — см. Использование образов в кластере.
Диагностика
| Симптом | Причина / решение |
|---|---|
401 Unauthorized при логине |
Неверное имя пользователя (должно быть token), истёкший/невалидный токен, либо SA/Secret удалён. |
denied / 403 при push |
У SA робота нет create на этот путь. Проверьте resourceNames в Role и RoleBinding. |
x509: certificate signed by unknown authority |
Раннер не доверяет CA хранилища — см. Предварительные условия. |
413 QUOTA_EXCEEDED при push |
Проект превысил квоту хранилища — см. Квота хранилища. |