
В этой статье мы рассмотрим, как более-менее прилично организовать процесс тестирования и публикации чартов, встреченные при этом подводные камни, а также рассмотрим пару великолепных инструментов, которые совершенно незаслуженно получили крайне мало внимания не только на Хабре, но и вообще в русскоязычном сегменте интернета.
Очень кстати, в недавно вышедшем релизе Gitlab 14.1, появился долгожданный функционал хранения Helm-чартов во встроенном Package Registry. Отлично, заодно и разберемся, как его использовать.
Используемые инструменты
KinD
Первый инструмент, о котором пойдет речь, буквально kubernetes-in-docker, позволяет запустить практически полноценный кластер локально на нодах-контейнерах. Под капотом использует kubeadm для настройки узлов и kustomize для слияния предоставленного конфига и сгенерированной внутри конфигурации. Полную документацию можно найти тут. Также по желанию можно установить не только ingress-nginx, манифесты которого для KinD можно найти в его репозитории, но и Ambassador или Contour.

Еще одним моментом бу��ет отсутствие поддержки сервисов типа LoadBalancer. Поэтому до развернутого приложения нужно ходить по INTERNAL-IP ноды, узнать который можно с помощью kubectl или docker inspect, например:
kubectl get no -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k8s-test-control-plane Ready master 36h v1.19.11 172.18.1.2 <none> Ubuntu 21.04 5.4.0-80-generic containerd://1.5.2 k8s-test-worker Ready compute 36h v1.19.11 172.18.1.3 <none> Ubuntu 21.04 5.4.0-80-generic containerd://1.5.2
Запущенные контейнеры-ноды помечаются лейбами io.x-k8s.kind.cluster и io.x-k8s.kind.role, в дальнейшем нам это пригодится.
Сейчас и далее предполагается, что локально уже установлен Docker, описывать в этой статье инструкцию его установки сочту излишним. Аналогично поступим и с Helm.
Устанавливаем kind в систему:
curl -Lo ./kind "https://kind.sigs.k8s.io/dl/v0.11.1/kind-$(uname)-amd64" chmod +x ./kind sudo mv ./kind /usr/local/bin/kind
Chart Testing
Второй инструмент - chart-testing, репо и документацию которого можно найти на GitHub. В паре с chart-releaser широко применяется для организации репо Helm-чартов в Github-pages. Оба имеют соответствующие экшены: chart-testing-action и chart-releaser-action.
Инструмент позволит упростить автоматизацию тестирования наших чартов, причем отберет из них только измененные по сравнению с master/main веткой origin. Будучи добавлен в pre-commit хук, не позволит забыть увеличить версию Helm-чарта. Что он нам поможет делать:
Прогон линта по нашим свеженаписанным Helm-чартам.
Валидацию Chart.yaml.
Развертывание Helm-чарта в отдельный сгенерированный неймспейс текущего активного кластера kubernetes (не обязательно KinD).
Очистка окружения за собой вне зависимости от успешности развертывания.
Понятно, что все вышеперечисленное можно сделать и с помощью Bash-полотна, python, костыля и пары велосипедов, но мы ведь не за этим сюда пришли, правда? Хотя Bash-полотно все-таки будет.
Установка:
sudo mkdir -p /tmp/ct /etc/ct curl -sL "https://github.com/helm/chart-testing/releases/download/v3.4.0/chart-testing_3.4.0_linux_amd64.tar.gz" | tar -xvz -C /tmp/ct sudo mv /tmp/ct/etc/chart_schema.yaml /etc/ct/chart_schema.yaml sudo mv /tmp/ct/etc/lintconf.yaml /etc/ct/lintconf.yaml sudo mv /tmp/ct/ct /usr/local/bin/ct sudo chmod +x /usr/local/bin/ct rm -rf /tmp/ct
На этом вводное рассмотрение инструментария заканчиваем и переходим к подготовке пайплайна.
Кейс первый: публичный репозиторий и DinD
Создаем и клонируем к себе git-репо, можно использовать и уже существующий проект, но для простоты повествования опишу процесс создания “с нуля”. В корне проекта создаем и переходим в директорию charts. Создаем наш первый чарт:
mkdir charts cd charts helm create test-chart
В Chart.yaml нужно добавить keywords и maintainers:
keywords: - example maintainers: - name: <Your Name> email: <mail@example.com>
Сразу стоит уточнить, что проверка мейнтейнеров проводится по наличию пути https://<git-repo-domain>/<maintainer-name>. Отключить её можно в следующем конфиге ct.yaml, добавив validate-maintainers: false.
Зачастую необх��димо производить тестирование чарта с какими-либо предопределенными параметрами. Чтобы их передать при раскатке с помощью ct, нужно создать директорию ci в директории конкретного чарта и в ней разместить *-values.yaml, например, ./charts/test-chart/ci/test-values.yaml. Таких файлов может быть несколько, но не указывайте в них пересекающиеся значения, т.к. порядок применения или только некоторые из них выбрать нельзя.
Далее создадим файлы:
ct.yaml - конфиг chart-testing, позволит минимизировать указание ключей при запуске.
ct.yaml
# See https://github.com/helm/chart-testing#configuration remote: origin target-branch: main chart-dirs: - charts helm-extra-args: --timeout=120s
kind-cluster.yaml - конфиг KinD. Кластер kubernetes будет разворачиваться внутри сервиса DinD (docker-in-docker), соответственно, описываем, что api-сервер должен слушать все интерфейсы, патч ClusterConfiguration, где добавляем хостнейм, с которым будем к нему подключаться, тип и количество нод.
Версия kubernetes выбирается через версию docker-имаджа KinD. Доступные варианты перечислены в release notes на GitHub.
Ноды добавляются просто повторением одной и той же строки, например, при указании двух строк - role: worker будет создано две воркер-ноды. Таким же образом можно варьировать и количество мастер-нод.
kind-cluster.yaml
kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: ipFamily: ipv4 apiServerAddress: "0.0.0.0" kubeadmConfigPatchesJSON6902: - group: kubeadm.k8s.io version: v1beta2 kind: ClusterConfiguration patch: | - op: add path: /apiServer/certSANs/- value: docker nodes: - role: control-plane - role: worker
.gitlab-ci.yml - описание пайплайна. Документацию по синтаксису можно найти здесь. Публичный GitLab “из коробки” предоставляет облачные раннеры с Docker, нам же потребуется настроить запуск сервиса DinD рядом с контейнером, в котором будет бежать выполнение пайплайна. Также и в сами образы, используемые для запуска джоб, в процессе выполнения нужно будет добавить недостающие бинари и конфиги утилит. Конфиг исключительно для примера, никто не запрещает собрать свой образ со всем необходимым для этого пайплайна.
.gitlab-ci.yml
# Объявляем стадии пайплайна stages: - "lint" - "chart-test" - "chart-release" variables: # Выбираем версию kubectl, соответственно используемой версии kubernetes CI_KUBECTL_VER: v1.19.0 # Объявляем версии Helm, chart-testing и KinD CI_HELM_VER: v3.6.3 CI_CT_VER: v3.4.0 CI_KIND_VERSION: v0.11.1 # Часть пути, по которому мы в дальнейшем будем подключать Helm-репо CI_HELM_CHANNEL: stable # Название создаваемого кластера kubernetes CI_KIND_CLUSTER_NAME: k8s-test # Для удобства переписываем в переменные имаджи нод с дайджестами # в соответствии с используемой версией KinD CI_KIND_IMAGE_1_17: 'kindest/node:v1.17.17@sha256:66f1d0d91a88b8a001811e2f1054af60eef3b669a9a74f9b6db871f2f1eeed00' CI_KIND_IMAGE_1_18: 'kindest/node:v1.18.19@sha256:7af1492e19b3192a79f606e43c35fb741e520d195f96399284515f077b3b622c' CI_KIND_IMAGE_1_19: 'kindest/node:v1.19.11@sha256:07db187ae84b4b7de440a73886f008cf903fcf5764ba8106a9fd5243d6f32729' CI_KIND_IMAGE_1_20: 'kindest/node:v1.20.7@sha256:cbeaf907fc78ac97ce7b625e4bf0de16e3ea725daf6b04f930bd14c67c671ff9' CI_KIND_IMAGE_1_21: 'kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6' # Указываем, какой образ будем использовать в этом пайплайне CI_KIND_IMAGE: $CI_KIND_IMAGE_1_19 # Описываем небольшие сниппеты, которые легко переиспользовать в нескольких джобах, # а также улучшают читабельность кода. .check-and-unshallow: &check-and-unshallow - git version - | if [ -f "$(git rev-parse --git-dir)/shallow" ]; then echo "this is a shallow repository"; git fetch --unshallow --prune --prune-tags --verbose else echo "not a shallow repository"; git fetch --prune --prune-tags --verbose fi - git rev-parse --verify HEAD - git rev-list HEAD --count - git rev-list HEAD --count --first-parent .get-kube-binaries: &get-kube-binaries - apk add -U wget - wget -O /usr/local/bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/${CI_KIND_VERSION}/kind-linux-amd64" - chmod +x /usr/local/bin/kind - wget -O /usr/local/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/${CI_KUBECTL_VER}/bin/linux/amd64/kubectl" - chmod +x /usr/local/bin/kubectl .install-ct: &install-ct - | export CT_URL="https://github.com/helm/chart-testing/releases/download/${CI_CT_VER}" export CT_TAR_FILE="chart-testing_${CI_CT_VER#v}_linux_amd64.tar.gz" echo "install chart-testing ${CI_CT_VER} from \"${CT_URL}/${CT_TAR_FILE}\"" mkdir -p /tmp/ct /etc/ct wget -O "/tmp/${CT_TAR_FILE}" "${CT_URL}/${CT_TAR_FILE}" tar -xzvf "/tmp/${CT_TAR_FILE}" -C /tmp/ct mv /tmp/ct/etc/chart_schema.yaml /etc/ct/chart_schema.yaml mv /tmp/ct/etc/lintconf.yaml /etc/ct/lintconf.yaml mv /tmp/ct/ct /usr/bin/ct rm -rf /tmp/ct ct version .install-helm: &install-helm - | export HELM_URL="https://get.helm.sh" export HELM_TAR_FILE="helm-${CI_HELM_VER}-linux-amd64.tar.gz" echo "install HELM ${CI_HELM_VER} from \"${HELM_URL}/${HELM_TAR_FILE}\"" mkdir -p /tmp/helm wget -O "/tmp/${HELM_TAR_FILE}" "${HELM_URL}/${HELM_TAR_FILE}" tar -xzvf "/tmp/${HELM_TAR_FILE}" -C /tmp/helm mv /tmp/helm/linux-amd64/helm /usr/bin/helm rm -rf /tmp/helm chmod +x /usr/bin/helm helm version # Добавляем Package Registry проекта с авторизацией через job-token # и плагин для push в Helm-репо .helm-add-project-as-repo: &helm-add-project-as-repo - >- helm repo add --username gitlab-ci-token --password "${CI_JOB_TOKEN}" "${CI_PROJECT_NAME}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/${CI_HELM_CHANNEL}" - helm plugin install https://github.com/chartmuseum/helm-push.git - helm repo list # Описываем сами джобы # Линт. Тут просто берем родной имадж chart-testing, ничего дабавлять не требуется chart-lint: stage: lint image: quay.io/helmpack/chart-testing:v3.4.0 tags: - "docker" script: - *check-and-unshallow - ct lint --config ct.yaml only: - pushes except: - master - main # Здесь добавляем все необходимые бинари, поднимаем KinD # и разворачиваем в него тестируемые чарты chart-test: stage: chart-test image: docker:20.10-git variables: # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" # Сервис DinD нам не нужен в остальных джобах, поэтому укажем его здесь, # чтобы не замедлять весь пайплайн services: - name: docker:20.10-dind alias: docker tags: - "docker" script: - *check-and-unshallow - apk add -U wget # Добавляем недостающие бинари - *get-kube-binaries - *install-ct - *install-helm # разворачиваем KinD - >- kind create cluster --name ${CI_KIND_CLUSTER_NAME} --image ${CI_KIND_IMAGE} --config=kind-cluster.yaml --wait 5m # Правим kubeconfig, чтобы указанный хост api-server соответствовал хосту DinD, # который, в свою очередь, мы ранее указали в патче ClusterConfiguration - sed -i -E -e 's/127\.0\.0\.1|0\.0\.0\.0/docker/g' "$HOME/.kube/config" # Разворачиваем наши чарты - ct install --config ct.yaml after_script: # не обязательно, т.к. сервис все равно будет погашен с завершением джобы - kind delete cluster --name k8s-test only: - pushes except: - master - main # Упаковываем чарты и публикуем их в Package Registry chart-release: stage: chart-release image: quay.io/helmpack/chart-testing:v3.4.0 tags: - "docker" script: - *check-and-unshallow - apk add jq yq - *helm-add-project-as-repo # используем доработанный скрипт из экшена chart-releaser - >- ./gitlab-cr.sh --charts-dir charts --charts-repo-url "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/${CI_HELM_CHANNEL}" --repo "${CI_PROJECT_NAME}" only: - master - main
Зачем тут unshallow? Chart-testing просматривает историю гит для поиска измененных чартов, а GitLab по умолчанию делает shallow clone с глубиной 50 коммитов, что дает вероятность его падения, когда он пытается просмотреть больше. Это поведение можно настроить через clone, а не fetch или через git depth, на вкус и цвет, так сказать.
Канал Helm-repo лучше использовать один, например, stable (название на самом деле можно выбрать любое) или же минимальное их разнообразие. Дело в том, что в интерфейсе GitLab определить какой чарт из какого канала можно примерно никак и разнообразие нейминга в этом случае приведет только к увеличению хаоса. В целом работает аналогично Chartmuseum и Helm-repo в GitHub-pages. Получить и распарсить индекс можно по пути "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/${CI_HELM_CHANNEL}/index.yaml"
Также обращу внимание, что поиск изменений происходит при сравнении текущего HEAD и указанного в конфиге ct target-branch в origin. Это еще один повод не пушить в мастер, т.к. в таком случае внутри пайплайна сравнивать будет не с чем.
Представленный пайплайн предполагает разработку в отдельной ветке, там же прогоняется линт и тестирование, затем мердж в мастер, где происходит упаковка и публикация чарта в Package Registry.
gitlab-cr.sh Скрипт упаковки и публикации чарта мы подсмотрим в оригинальном GitHub экшене. К сожалению, chart-releaser с GitLab пока не работает, поэтому перепишем на использование helm для поиска версии чарта по Helm-repo, а затем упаковки и публикации, если текущей версии найдено не буд��т:
gitlab-cr.sh
#!/usr/bin/env bash # Copyright The Helm Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail needy_tools=("jq" "yq" "helm") show_help() { cat << EOF Usage: $(basename "$0") <options> -h, --help Display help -d, --charts-dir The charts directory (default: charts) -u, --charts-repo-url The Gitlab helm package registry URL (default: "<CI_API_V4_URL>/projects/<CI_PROJECT_ID>/packages/helm/stable") -r, --repo The repo name EOF } main() { local charts_dir=charts local repo= local charts_repo_url= parse_command_line "$@" for tool in "${needy_tools[@]}"; do assert_tools "$tool" done local repo_root repo_root=$(git rev-parse --show-toplevel) pushd "$repo_root" > /dev/null echo 'Looking up latest tag...' local latest_tag latest_tag=$(lookup_latest_tag) echo "Discovering changed charts since '$latest_tag'..." local changed_charts=() readarray -t changed_charts <<< "$(lookup_changed_charts "$latest_tag")" if [[ -n "${changed_charts[*]}" ]]; then rm -rf .cr-release-packages mkdir -p .cr-release-packages rm -rf .cr-index mkdir -p .cr-index for chart in "${changed_charts[@]}"; do if [[ -d "$chart" ]]; then package_chart "$chart" else echo "Chart '$chart' no longer exists in repo. Skipping it..." fi done release_charts "$repo" else echo "Nothing to do. No chart changes detected." fi popd > /dev/null } parse_command_line() { while :; do case "${1:-}" in -h|--help) show_help exit ;; -d|--charts-dir) if [[ -n "${2:-}" ]]; then charts_dir="$2" shift else echo "ERROR: '-d|--charts-dir' cannot be empty." >&2 show_help exit 1 fi ;; -u|--charts-repo-url) if [[ -n "${2:-}" ]]; then charts_repo_url="$2" shift else echo "ERROR: '-u|--charts-repo-url' cannot be empty." >&2 show_help exit 1 fi ;; -r|--repo) if [[ -n "${2:-}" ]]; then repo="$2" shift else echo "ERROR: '--repo' cannot be empty." >&2 show_help exit 1 fi ;; *) break ;; esac shift done if [[ -z "$repo" ]]; then echo "ERROR: '-r|--repo' is required." >&2 show_help exit 1 fi if [[ -z "$charts_repo_url" ]]; then charts_repo_url="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/stable" fi } lookup_latest_tag() { git fetch --tags > /dev/null 2>&1 if ! git describe --tags --abbrev=0 2> /dev/null; then git rev-list --max-parents=0 --first-parent HEAD fi } filter_charts() { while read -r chart; do [[ ! -d "$chart" ]] && continue local file="$chart/Chart.yaml" if [[ -f "$file" ]]; then echo "$chart" else echo "WARNING: $file is missing, assuming that '$chart' is not a Helm chart. Skipping." 1>&2 fi done } lookup_changed_charts() { local commit="$1" local changed_files changed_files=$(git diff --find-renames --name-only "$commit" -- "$charts_dir") local depth=$(( $(tr "/" "\n" <<< "$charts_dir" | sed '/^\(\.\)*$/d' | wc -l) + 1 )) local fields="1-${depth}" cut -d '/' -f "$fields" <<< "$changed_files" | uniq | filter_charts } lookup_chart_in_repo_by_version() { local chart="$1" local chart_version chart_version="$(yq r "${chart}/Chart.yaml" 'version')" local chart_name chart_name="$(yq r "${chart}/Chart.yaml" 'name')" helm search repo "${repo}/${chart_name}" --version "$chart_version" -o json | jq -r '.[].version' } package_chart() { local chart="$1" local chart_version_in_repo chart_version_in_repo="$(lookup_chart_in_repo_by_version "$chart")" local args=("$chart" --destination .cr-release-packages) if [[ -z "$chart_version_in_repo" ]]; then echo "Packaging chart '$chart'..." helm package "${args[@]}" else echo "$chart with version $chart_version_in_repo already exist in repo. Skipping..." fi } release_charts() { local repo="$1" echo 'Releasing charts...' for f in .cr-release-packages/*.tgz do [ -e "$f" ] || break helm push "$f" "$repo" done } assert_tools() { local tool="$1" command -v "$tool" >/dev/null 2>&1 || { echo "ERROR: ${tool} is not installed." >&2 exit 1 } } main "$@"
Готово. Коммитим, пушим в develop и наблюдаем, как пайплайн становится зеленым, после чего мерджим нашу ветку в master(main) и по завершению пайплайна получаем тарболл чарта в Package Registry.

Добавляем Helm-repo (подставьте свой ID проекта):
helm repo add habr-test https://gitlab.com/api/v4/projects/<project_ID>/packages/helm/stable "habr-test" has been added to your repositories
Проверяем, что тестовый чарт опубликован:
helm search repo habr-test/test-chart NAME CHART VERSION APP VERSION DESCRIPTION habr-test/test-chart 0.1.0 1.16.0 A Helm chart for Kubernetes
Кейс второй: раннер с монтированием сокета Docker
Сразу оговорюсь, что монтирование сокета с хоста в контейнеры, создаваемые раннером не лучшее решение в плане безопасности, но пока что необходимо, например, если вы пользуетесь werf. В таком случае запустить DinD будет как минимум не самой тривиальной задачей, т.к. сокет монтируется и в контейнеры сервисов.
Тем не менее задача легко решаема, поскольку контейнеры KinD в этом случае будут подниматься в Docker хоста, где запущен раннер, а интерфейс docker0 доступен изнутри контейнера и практически всегда имеет адрес 172.17.0.1. Как и в случае с сервисом DinD мы добавим этот адрес через патч ClusterConfiguration в kind-cluster.yaml:
kubeadmConfigPatchesJSON6902: - group: kubeadm.k8s.io version: v1beta2 kind: ClusterConfiguration patch: | - op: add path: /apiServer/certSANs/- value: 172.17.0.1
И sed’ом по ходу выполнения пайплайна заменим 0.0.0.0 в кубконфиге на адрес интерфейса docker0: sed -i -E -e 's/0\.0\.0\.0/172\.17\.0\.1/g' "$HOME/.kube/config"
Кроме того, нужно позаботиться и об удалении контейнеров KinD в случае отмены задания GitLab, т.к. контейнер задания будет просто убит kill без выполнения after_script. Подробнее с сутью проблемы можно ознакомиться в этом issue. В этом нам помогут лейбы, которые KinD ставит на запускаемые контейнеры нод. Сперва добавляем CI_JOB_ID в переменную названия кластера, затем, перед запуском кластера, создаем контейнер “мертвой руки”, который после некоторого таймаута будет завершать контейнеры с лейбой io.x-k8s.kind.cluster=job-${CI_JOB_ID}.
Пример пайплайна для работы с docker0
# Объявляем стадии пайплайна stages: - "lint" - "chart-test" - "chart-release" variables: # Выбираем версию kubectl, соответственно используемой версии kubernetes CI_KUBECTL_VER: v1.19.0 # Объявляем версии Helm, chart-testing и KinD CI_HELM_VER: v3.6.3 CI_CT_VER: v3.4.0 CI_KIND_VERSION: v0.11.1 # Часть пути, по которому мы в дальнейшем будем подключать Helm-репо CI_HELM_CHANNEL: stable # Название создаваемого кластера kubernetes CI_KIND_CLUSTER_NAME: job-${CI_JOB_ID} # Название контейнера отложенного удаления кластера CI_KIND_REAPER_NAME: job-${CI_JOB_ID}-reaper # Для удобства переписываем в переменные имаджи нод с дайджестами # в соответствии с используемой версией KinD CI_KIND_IMAGE_1_17: 'kindest/node:v1.17.17@sha256:66f1d0d91a88b8a001811e2f1054af60eef3b669a9a74f9b6db871f2f1eeed00' CI_KIND_IMAGE_1_18: 'kindest/node:v1.18.19@sha256:7af1492e19b3192a79f606e43c35fb741e520d195f96399284515f077b3b622c' CI_KIND_IMAGE_1_19: 'kindest/node:v1.19.11@sha256:07db187ae84b4b7de440a73886f008cf903fcf5764ba8106a9fd5243d6f32729' CI_KIND_IMAGE_1_20: 'kindest/node:v1.20.7@sha256:cbeaf907fc78ac97ce7b625e4bf0de16e3ea725daf6b04f930bd14c67c671ff9' CI_KIND_IMAGE_1_21: 'kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6' # Указываем, какой образ будем использовать в этом пайплайне CI_KIND_IMAGE: $CI_KIND_IMAGE_1_19 # Описываем небольшие сниппеты, которые легко переиспользовать в нескольких джобах, # а также улучшают читабельность кода. .check-and-unshallow: &check-and-unshallow - git version - | if [ -f "$(git rev-parse --git-dir)/shallow" ]; then echo "this is a shallow repository"; git fetch --unshallow --prune --prune-tags --verbose else echo "not a shallow repository"; git fetch --prune --prune-tags --verbose fi - git rev-parse --verify HEAD - git rev-list HEAD --count - git rev-list HEAD --count --first-parent .get-kube-binaries: &get-kube-binaries - wget -nv -O /usr/local/bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/${CI_KIND_VERSION}/kind-linux-amd64" - chmod +x /usr/local/bin/kind - wget -nv -O /usr/local/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/${CI_KUBECTL_VER}/bin/linux/amd64/kubectl" - chmod +x /usr/local/bin/kubectl .install-ct: &install-ct - | export CT_URL="https://github.com/helm/chart-testing/releases/download/${CI_CT_VER}" export CT_TAR_FILE="chart-testing_${CI_CT_VER#v}_linux_amd64.tar.gz" echo "install chart-testing ${CI_CT_VER} from \"${CT_URL}/${CT_TAR_FILE}\"" mkdir -p /tmp/ct /etc/ct wget -nv -O "/tmp/${CT_TAR_FILE}" "${CT_URL}/${CT_TAR_FILE}" tar -xzvf "/tmp/${CT_TAR_FILE}" -C /tmp/ct mv /tmp/ct/etc/chart_schema.yaml /etc/ct/chart_schema.yaml mv /tmp/ct/etc/lintconf.yaml /etc/ct/lintconf.yaml mv /tmp/ct/ct /usr/bin/ct rm -rf /tmp/ct ct version .install-helm: &install-helm - | export HELM_URL="https://get.helm.sh" export HELM_TAR_FILE="helm-${CI_HELM_VER}-linux-amd64.tar.gz" echo "install HELM ${CI_HELM_VER} from \"${HELM_URL}/${HELM_TAR_FILE}\"" mkdir -p /tmp/helm wget -nv -O "/tmp/${HELM_TAR_FILE}" "${HELM_URL}/${HELM_TAR_FILE}" tar -xzvf "/tmp/${HELM_TAR_FILE}" -C /tmp/helm mv /tmp/helm/linux-amd64/helm /usr/bin/helm rm -rf /tmp/helm chmod +x /usr/bin/helm helm version # Добавляем Package Registry проекта �� авторизацией через job-token # и плагин для push в Helm-репо .helm-add-project-as-repo: &helm-add-project-as-repo - >- helm repo add --username gitlab-ci-token --password "${CI_JOB_TOKEN}" "${CI_PROJECT_NAME}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/${CI_HELM_CHANNEL}" - helm plugin install https://github.com/chartmuseum/helm-push.git - helm repo list # Создаем контейнер отложенной очистки .create-kind-reaper: &create-kind-reaper - >- docker run --rm -d --name ${CI_KIND_REAPER_NAME} -e CI_KIND_CLUSTER_NAME=${CI_KIND_CLUSTER_NAME} -e CI_REAPER_SLEEP=${CI_REAPER_SLEEP:-300} -v /var/run/docker.sock:/var/run/docker.sock docker:20.10 sh -c 'sleep ${CI_REAPER_SLEEP}; docker rm -fv $(docker ps -aq --filter label=io.x-k8s.kind.cluster=${CI_KIND_CLUSTER_NAME})' # Описываем сами джобы # Линт. Тут просто берем родной имадж chart-testing, ничего дабавлять не требуется chart-lint: stage: lint image: quay.io/helmpack/chart-testing:v3.4.0 tags: - "docker" script: - *check-and-unshallow - ct lint --config ct.yaml only: - pushes except: - master - main # Здесь добавляем все необходимые бинари, поднимаем KinD # и разворачиваем в него тестируемые чарты chart-test: stage: chart-test image: docker:20.10-git tags: - "docker" script: - *check-and-unshallow - apk add -U wget # Добавляем недостающие бинари - *get-kube-binaries - *install-ct - *install-helm # запускаем рипер - *create-kind-reaper # разворачиваем KinD - >- kind create cluster --name ${CI_KIND_CLUSTER_NAME} --image ${CI_KIND_IMAGE} --config=kind-cluster.yaml --wait 5m # Правим kubeconfig, чтобы указанный хост api-server соответствовал хосту docker0, # адрес которого, в свою очередь, мы ранее указали в патче ClusterConfiguration - sed -i -E -e 's/0\.0\.0\.0/172\.17\.0\.1/g' "$HOME/.kube/config" # Разворачиваем наши чарты - ct install --config ct.yaml after_script: # удаляем кластер KinD - kind delete cluster --name ${CI_KIND_CLUSTER_NAME} # удаляем контейнер отложенной очистки - docker rm -fv ${CI_KIND_REAPER_NAME} only: - pushes except: - master - main # Упаковываем чарты и публикуем их в Package Registry chart-release: stage: chart-release image: quay.io/helmpack/chart-testing:v3.4.0 tags: - "docker" script: - *check-and-unshallow - apk add jq yq - *helm-add-project-as-repo # используем доработанный скрипт из экшена chart-releaser - >- ./gitlab-cr.sh --charts-dir charts --charts-repo-url "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/${CI_HELM_CHANNEL}" --repo "${CI_PROJECT_NAME}" only: - master - main
Для подключения к приватному Helm-репо на pull будет достаточно прав read_api персонального токена, но намного правильнее использовать для этого deploy token с правами read_package_registry. Аналогичный пример подключения Helm-репо с авторизацией токеном задания GitLab можно найти в сниппете "helm-add-project-as-repo".
Заключение
Статья получилась довольно насыщенной ссылками на документацию, но возможно, это облегчит путь тех, кто еще не слишком уверенно ориентируется в предметной области. Кроме того все материалы собраны в репо на GitHub.
Благодарю за внимание и надеюсь, что этот дебют получился достаточно интересным и поможет вам в автоматизации своего проекта или послужит отправной точкой в изучении новых инструментов.
