CI/CD: деплой в кластер Kubernetes serverplus
Идея
Доступ к кластеру (kubeconfig) кладётся в секреты CI, а пайплайн запускает kubectl apply при каждом пуше. Ниже — как это устроить в GitLab CI и GitHub Actions для кластера Kubernetes в serverplus, и как сделать это безопасно, не раздавая admin-доступ.
Что кладём в секреты CI
- kubeconfig кластера — в идеале не admin, а ограниченный (от ServiceAccount, см. ниже). Кодируем в base64 и сохраняем как маскированную переменную CI (
KUBE_CONFIG). - application credential id/secret — только если пайплайн трогает саму инфраструктуру OpenStack (например, через Terraform).
# get kubeconfig and encode it for the CI secret
openstack coe cluster config my-cluster
base64 -w0 ./config # result -> KUBE_CONFIG variableНикогда не коммитьте kubeconfig в репозиторий. Только маскированные секреты CI. Один файл kubeconfig — это полный доступ к кластеру.
Ограниченный доступ (ServiceAccount + RBAC)
Вместо admin-kubeconfig заведите сервис-аккаунт с правами только в одном namespace:
kubectl create namespace app
kubectl -n app create serviceaccount deployer
kubectl -n app create rolebinding deployer-edit \
--clusterrole=edit --serviceaccount=app:deployer
# short-lived token for this account
kubectl -n app create token deployer --duration=24hНа основе этого токена и адреса кластера собирается отдельный kubeconfig для CI — он может менять только namespace app.
GitLab CI
.gitlab-ci.yml — деплой при пуше в main:
deploy:
stage: deploy
image: bitnami/kubectl:latest
script:
- echo "$KUBE_CONFIG" | base64 -d > kubeconfig
- export KUBECONFIG=$PWD/kubeconfig
- kubectl -n app apply -f k8s/
- kubectl -n app rollout status deployment/web
rules:
- if: $CI_COMMIT_BRANCH == "main"KUBE_CONFIG задаётся в Settings → CI/CD → Variables как masked переменная (base64 от kubeconfig).
GitHub Actions
.github/workflows/deploy.yml:
name: deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: kubeconfig
run: |
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig
echo "KUBECONFIG=$PWD/kubeconfig" >> $GITHUB_ENV
- name: deploy
run: |
kubectl -n app apply -f k8s/
kubectl -n app rollout status deployment/webKUBE_CONFIG добавляется в Settings → Secrets and variables → Actions.
Сборка и приватный реестр
Типичный полный цикл: собрать образ, запушить в реестр, обновить деплой.
docker build -t registry.example/web:$TAG .
docker push registry.example/web:$TAG
kubectl -n app set image deployment/web web=registry.example/web:$TAGЕсли реестр приватный, добавьте в кластер imagePullSecret и укажите его в манифесте деплоя.
Безопасность
- Не admin-kubeconfig. Отдельный ServiceAccount с ролью
editв одном namespace (RBAC) — компрометация раннера не даст доступ ко всему кластеру. - Только маскированные секреты CI, раздельные креды на окружения (stage/prod) и их ротация.
- Ограничьте, кто запускает деплой: protected branches и protected environments.
Частые вопросы
kubectl: connection refused / timeout из пайплайна. В kubeconfig должен быть внешний адрес control plane (floating IP), а не внутренний 10.x. Раннер CI должен иметь сетевой доступ к этому адресу.
Ошибка «forbidden». Сервис-аккаунту не хватает прав в RBAC — проверьте rolebinding и namespace.
Ошибка сертификата x509. Устаревший kubeconfig (кластер пересоздавали). Сгенерируйте заново: openstack coe cluster config my-cluster --force.