Короткий ответ
Нужен один пайплайн сборки, который собирает и помечает артефакт уникальным неизменяемым тегом (например, по git-sha), публикует его в registry один раз, а дальше отдельные стадии/пайплайны деплоя последовательно разворачивают именно этот тег в dev, затем staging, затем prod, не пересобирая код и подставляя только конфигурацию, специфичную для окружения.
Как это работает подробнее
стадия `build` производит один артефакт и его метаданные (например, файл `image-tag.txt` с конкретным digest/тегом), который сохраняется как pipeline-артефакт или просто публикуется в registry с уникальным тегом. Стадии деплоя в разные окружения читают этот же тег и применяют его с окружение-специфичными values/ConfigMap, не трогая сам образ.
Пример кода
example.yaml
build:
stage: build
script:
- docker build -t registry.example.com/myapp:$CI_COMMIT_SHORT_SHA .
- docker push registry.example.com/myapp:$CI_COMMIT_SHORT_SHA
deploy_dev:
stage: deploy
script: helm upgrade myapp ./chart --set image.tag=$CI_COMMIT_SHORT_SHA -f values-dev.yaml
deploy_staging:
stage: deploy
script: helm upgrade myapp ./chart --set image.tag=$CI_COMMIT_SHORT_SHA -f values-staging.yaml
needs: ["deploy_dev"]
deploy_prod:
stage: deploy
when: manual
script: helm upgrade myapp ./chart --set image.tag=$CI_COMMIT_SHORT_SHA -f values-prod.yaml
needs: ["deploy_staging"]Что использовать на практике
- На практике. такой подход даёт гарантию, что в prod работает именно то, что было протестировано на staging, упрощает откат (можно вернуть предыдущий известный тег) и ускоряет повторные деплои. Обычно между стадиями ставят gate — автоматические проверки на staging и/или ручное подтверждение перед prod.
- Пример. Ожидаемое поведение: во всех трёх окружениях в конечном итоге работает образ с одним и тем же тегом `$CI_COMMIT_SHORT_SHA`, что можно проверить командой `kubectl get deployment myapp -o jsonpath='{.spec.template.spec.containers[0].image}'` в каждом кластере.
Подводные камни
- Частые ошибки. параметризуют Dockerfile через build-args в зависимости от окружения и пересобирают образ отдельно для каждого окружения — это ломает гарантию идентичности и усложняет диагностику; забывают зафиксировать версии транзитивных зависимостей (lock-файлы), из-за чего даже «тот же» Dockerfile может собрать разный результат при повторной сборке.