Шаг 1. Подготовка окружения и загрузка Docker образов
Для этого используем sh скрипт pull‑images‑setup.sh, который выполняет несколько действий: устанавливает k3d и Helm, скачивает необходимые docker образы и создает локальные теги для них, которые будут использоваться через локальный реестр.
Перед запуском скрипта нужно убедиться, что установлены необходимые инструменты:
Docker Desktop — понадобится для запуска контейнеров и дальнейшего создания k3s‑кластера через k3d
App Installer — предоставляет пакетный менеджер
winget, через который скрипт установит k3d и HelmGit Bash — понадобится для запуска
shскриптов в Windows.
#!/bin/bash echo "Установка k3d и Helm..." winget install k3d winget install Helm.Helm echo "Загрузка Docker образов..." docker pull registry:3 docker pull gitea/gitea:1.25 docker pull jenkins/jenkins:2.554 docker pull maven:3.9.14-eclipse-temurin-21 docker pull eclipse-temurin:21.0.10_7-jdk-jammy docker pull jenkins/inbound-agent:jdk21 docker pull gcr.io/kaniko-project/executor:v1.24.0 echo "Создание тегов для локального registry..." docker tag maven:3.9.14-eclipse-temurin-21 localhost:5000/maven:3.9.14-eclipse-temurin-21 docker tag eclipse-temurin:21.0.10_7-jdk-jammy localhost:5000/eclipse-temurin:21.0.10_7-jdk-jammy docker tag gcr.io/kaniko-project/executor:v1.24.0 localhost:5000/kaniko:v1.24.0 echo "Проверка загруженных образов:" docker images | grep -E "registry|gitea|jenkins|maven|eclipse-temurin"
registry— локальное хранилище Docker‑образовgitea— локальный Git‑серверjenkins— CI‑серверmavenиeclipse-temurin— окружение для сборки и запуска Java‑приложенияjenkins/inbound-agent— базовый образ Jenkins Agentkaniko— инструмент для сборки контейнерных образов без использования docker daemon непосредственно в процессе сборки
Шаг 2. Разворачиваем локальный k3s кластер
Теперь можно переходить к созданию самого кластера.
Для этого используется sh скрипт setup‑cluster.sh. Он создаёт docker сеть и необходимые volumes, разворачивает k3s кластер через k3d, устанавливает Argo CD, запускает сервисы из docker compose и подготавливает образы, которые понадобятся Jenkins.
#!/bin/bash echo "=========================================" echo "Запуск полной настройки test-prod-cluster" echo "=========================================" # Создаем сеть echo "Создание сети test-prod-net..." docker network create test-prod-net sleep 5 # Создаём постоянный volume, который будут использовать все агенты echo "Создание volume который будут использовать все агенты..." docker volume create build-workspace # Даём полные права на запись в volume (чтобы jenkins мог писать) docker run --rm -v build-workspace:/workspace alpine sh -c "chmod 777 /workspace" sleep 5 # Создаём volumes для кэша echo "Создание volume для кеша Maven..." docker volume create maven-cache # Даём полные права на запись в volume (Maven будет писать сюда) docker run --rm -v maven-cache:/maven-cache alpine sh -c "chmod 777 /maven-cache" sleep 5 # Создание кластера k3d echo "Создание кластера test-prod-cluster..." k3d cluster create test-prod-cluster \ --network test-prod-net \ --api-port 6550 \ -p "8081:80@loadbalancer" \ -p "8443:443@loadbalancer" \ --agents 1 \ --registry-config registries.yaml echo "Кластер создан. Ожидание 60 секунд..." sleep 60 # Замена URL в kubeconfig echo "Обновление kubeconfig..." KUBECONFIG_PATH="$HOME/.kube/config" sed -i 's|https://host\.docker\.internal:6550|https://localhost:6550|g' "$KUBECONFIG_PATH" # Установка Argo CD echo "Добавление репозитория Argo CD..." helm repo add argo https://argoproj.github.io/argo-helm helm repo update echo "Установка Argo CD..." kubectl create namespace argocd helm install argocd argo/argo-cd -n argocd echo "Установка ArgoCD завершена. Ожидание 60 секунд..." sleep 60 # Запуск Docker Compose echo "Запуск Docker Compose..." docker compose up -d echo "Запуск Docker Compose завершен. Ожидание 30 секунд..." sleep 30 # Применение Application echo "Применение Application..." kubectl apply -f application.yaml echo "Применение Application завершено. Ожидание 20 секунд..." sleep 20 # Сборка и пуш образа Jenkins агента echo "Сборка образа Jenkins агента..." docker build -f Dockerfile.agent -t localhost:5000/jenkins-agent:docker . docker push localhost:5000/jenkins-agent:docker # Пуш Maven образа в registry echo "Пуш Maven образа в registry..." docker tag maven:3.9.14-eclipse-temurin-21 localhost:5000/maven:3.9.14-eclipse-temurin-21 docker push localhost:5000/maven:3.9.14-eclipse-temurin-21 # Пуш eclipse-temurin образа в registry echo "Пуш eclipse-temurin образа в registry..." docker tag eclipse-temurin:21.0.10_7-jdk-jammy localhost:5000/eclipse-temurin:21.0.10_7-jdk-jammy docker push localhost:5000/eclipse-temurin:21.0.10_7-jdk-jammy # Пуш Kanico образа в registry echo "Пуш Kanico образа в registry..." docker tag gcr.io/kaniko-project/executor:v1.24.0 localhost:5000/kaniko:v1.24.0 docker push localhost:5000/kaniko:v1.24.0 echo "=========================================" echo "Настройка завершена!" echo "=========================================" echo "Gitea: http://localhost:3000" echo "Jenkins: http://localhost:8082" echo "Argo CD: kubectl get pods -n argocd"
После установки Argo CD применяется файл application.yaml:
kubectl apply -f application.yaml
Этот манифест создаёт в Argo CD объект Application. В нём указывается git репозиторий с Helm чартами, путь к нужному chart, целевой кластер и namespace, куда должно быть развернуто приложение.
После этого Argo CD начинает следить за репозиторием helm-charts. Когда Jenkins обновляет в нём тег docker образа и отправляет изменения в gitea, argo cd обнаруживает изменение и синхронизирует состояние приложения в k3s кластере.
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: myapp namespace: argocd spec: project: default source: repoURL: http://gitea:3000/<gitea_user>/helm-charts.git path: myapp targetRevision: HEAD destination: server: https://kubernetes.default.svc namespace: myapp-prod syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true
Работать с кластером можно полностью через kubectl, но для учебного стенда удобно дополнительно использовать OpenLens — графический интерфейс для Kubernetes.
Шаг 3. Подготавливаем репозитории в Gitea
Теперь подготовим Git репозитории, которые будут использоваться в нашем учебном CI/CD процессе.
В качестве локального Git сервера используется Gitea. Она уже запускается в составе окружения и доступна по адресу:
http://localhost:3000
Для дальнейшей работы нам понадобятся два репозитория:
test-java-app— исходный код тестового Java‑приложения;helm-charts— helm чарты, которые описывают желаемое состояние приложения в Kubernetes.
Разделение на два репозитория сделано специально. Jenkins будет работать с исходным кодом, собирать docker образ и после успешной сборки обновлять версию образа в helm чарте.
ArgoCD в свою очередь будет следить уже не за репозиторием приложения, а за репозиторием helm-charts. Как только Jenkins изменит там тег образа, ArgoCD увидит изменение и синхронизирует состояние кластера.
После первого запуска Gitea необходимо создать пользователя и затем вручную создать два пустых репозитория:
test-java-app helm-charts
После этого локальные репозитории можно связать с Gitea и отправить туда код.
Для приложения:
git init git remote add origin http://localhost:3000/<username>/test-java-app.git git branch -M main git add * git commit -m "first commit" git push -u origin main
Для Helm чартов:
git init git remote add origin http://localhost:3000/<username>/helm-charts.git git branch -M main git add * git commit -m "first commit" git push -u origin main
На этом этапе Gitea становится точкой хранения исходного кода и конфигурации развертывания, а дальше мы подключим к этим репозиториям Jenkins и ArgoCD.
Шаг 4. Запускаем и настраиваем Jenkins
Следующий компонент нашего стенда Jenkins. В этой схеме он будет отвечать за CI часть процесса:
получать исходный код приложения из Gitea
запускать сборку
собирать docker образ
отправлять образ в локальный репозиторий
обновлять тег образа в репозитории с Helm чартами
Сам Jenkins уже запускается через docker compose внутри нашего sh скрипта, поэтому после выполнения предыдущих шагов его интерфейс должен быть доступен по адресу:
http://localhost:8082
При первом запуске Jenkins попросит ввести начальный пароль администратора. Получить его можно командой (powershell):
docker exec test-prod-jenkins cat /var/jenkins_home/secrets/initialAdminPassword
После ввода пароля Jenkins предложит установить плагины. Для учебного стенда можно сначала выбрать стандартный набор, а затем дополнительно установить плагины, которые понадобятся для работы с docker:
Docker— для создания временных Jenkins Agent через DockerCloudBees Docker Build and Publish— для работы со сборкой и публикацией docker образов
После установки плагинов Jenkins можно перезапустить из интерфейса.
Далее проверим базовые параметры Jenkins:
System Configuration → System → Jenkins Location
В поле Jenkins URL для нашего локального стенда используется внутреннее имя контейнера:
http://jenkins:8080
Это важно: из браузера мы открываем Jenkins через localhost:8082, но другие контейнеры внутри Docker сети обращаются к нему по имени сервиса jenkins.
Добавляем полномочия(credentials) для Gitea
Теперь нужно дать Jenkins доступ к нашим репозиториям в Gitea.
В дальнейшем Jenkins будет использовать эти полномочия для двух задач:
клонирования репозитория с исходным кодом приложения
отправки изменений в репозиторий
helm-charts
В интерфейсе Jenkins переходим:
Настройки Jenkins → Credentials → Global → Add Credentials
В качестве типа credentials выбираем: Username with password
И указываем данные пользователя Gitea:
Username: <ваш пользователь Gitea> Password: <пароль пользователя Gitea> ID: gitea-credentials
Поле ID здесь особенно важно. В нашем pipeline полномочия будут запрашиваться именно по ID.
Настраиваем Docker Cloud в Jenkins
Теперь настроим окружение, в котором Jenkins будет выполнять наши сборки.
В этой конфигурации Jenkins использует Docker Cloud. Его задача — динамически создавать временные Jenkins Agent контейнеры для выполнения pipeline. После завершения сборки такой агент удаляется.
Переходим в: Настройки Jenkins → Clouds → New cloud → Type Docker
Name: my-docker-cloud Docker Cloud details -> Docker Host URI: tcp://host.docker.internal:2375 (включен в Desktop) Enabled: true
Адрес host.docker.internal позволяет контейнеру Jenkins обращаться к Docker Engine, который работает на Windows хосте через Docker Desktop. Порт 2375 используется для подключения к Docker daemon по TCP. Для нашего локального учебного стенда это упрощает взаимодействие Jenkins с Docker.
После этого добавляем шаблон Docker Agent для Kaniko‑сборки.
В разделе Docker Agent templates создаём новый шаблон со следующими параметрами:
Labels: kaniko Enabled: true Name: kaniko-agent Docker Image: localhost:5000/jenkins-agent:docker
Раздел Container settings:
Mounts: type=volume,src=build-workspace,dst=/workspace Network: test-prod-net Remote File System Root: /home/jenkins Connect method: Connect with JNLP Pull strategy: Pull all images every time
Нажми Apply внизу, потом Save
После сохранения шаблона Jenkins сможет динамически создавать временный kaniko-agent для каждой сборки, а после завершения pipeline агент будет удаляться.
Создаём pipeline для Java приложения: сборка через Kaniko
Теперь, когда Jenkins умеет создавать временный агент с лейбломkaniko, можно перейти к самому pipeline.
Создаём новую задачу: New Item → Pipeline
В качестве имени можно использовать: build-java-app-kaniko
Далее выбираем: Definition: Pipeline script
Definition: Pipeline script
Наш pipeline будет выполнять несколько последовательных действий:
создавать рабочую директорию
клонировать исходный код приложения из Gitea
собирать Docker‑образ с помощью Kaniko
отправлять образ в локальный registry
обновлять тег образа в Helm‑чарте
отправлять изменения обратно в Gitea
Важно: перед запуском замените <gitea_user> на имя своего пользователя в Gitea.
Скрипт:
pipeline { agent { label 'kaniko' } environment { IMAGE_TAG = "registry:5000/test-java-app:${BUILD_NUMBER}" GIT_REPO = "http://gitea:3000/<gitea_user>/test-java-app.git" GIT_CREDENTIALS = "gitea-credentials" HELM_REPO = "http://gitea:3000/<gitea_user>/helm-charts.git" } stages { stage('Prepare workspace') { steps { sh """ rm -rf /workspace/repo mkdir -p /workspace/repo """ } } stage('Checkout') { steps { echo "Клонируем репозиторий приложения..." dir('/workspace/repo') { git branch: 'main', url: "${GIT_REPO}", credentialsId: "${GIT_CREDENTIALS}" } } } stage('Build with Kaniko') { steps { sh """ export DOCKER_HOST=tcp://host.docker.internal:2375 docker run \\ -v build-workspace:/workspace \\ -v maven-cache:/root/.m2 \\ --network test-prod-net \\ localhost:5000/kaniko:v1.24.0 \\ --dockerfile /workspace/repo/Dockerfile \\ --destination ${IMAGE_TAG} \\ --context dir:///workspace/repo \\ --insecure \\ --insecure-pull """ } } stage('Update Helm Chart') { steps { echo "Обновляем тег в helm чартах..." withCredentials([usernamePassword( credentialsId: 'gitea-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS' )]) { sh """ git clone ${HELM_REPO} helm-charts cd helm-charts sed -i "s/tag:.*/tag: ${BUILD_NUMBER}/" myapp/values.yaml git config user.email "jenkins@local" git config user.name "Jenkins CI" git add myapp/values.yaml git commit -m "Update app version to ${BUILD_NUMBER}" git push http://${GIT_USER}:${GIT_PASS}@gitea:3000/<gitea_user>/helm-charts.git main """ } } } stage('Verify') { steps { echo "Готово! Образ ${IMAGE_TAG} запушен, helm чарты обновлены" echo "ArgoCD подхватит изменения автоматически" } } } post { always { echo "Очищаем ресурсы Docker..." script { sh """ export DOCKER_HOST=tcp://host.docker.internal:2375 docker ps -a \ --filter "ancestor=localhost:5000/kaniko:v1.24.0" \ --filter "status=exited" -q | xargs -r docker rm """ } } } }
После сохранения pipeline запускаем его через Build Now.
Если всё настроено правильно, Jenkins соберёт новый образ, отправит его в локальный registry, обновит тег в репозитории helm-charts, а ArgoCD подхватит изменения и применит новую версию приложения в кластере.
Удаление учебного стенда
После экспериментов окружение можно удалить с помощью отдельного скрипта delete-k3d-helm.sh.
Он удаляет установленный Argo CD и его namespace, удаляет Helm‑репозиторий Argo CD, удаляет локальные k3d кластеры, а также деинсталлирует Helm и k3d.
Внимание: скрипт предназначен для полного удаления учебного окружения. Команда k3d cluster delete --all удаляет все k3d‑кластеры на компьютере, а не только созданный в рамках этой статьи. Кроме того, скрипт удаляет установленные через winget Helm и k3d. Если эти инструменты или другие k3d кластеры используются вами для других проектов, не запускайте скрипт целиком.
#!/bin/sh echo "=== Удаление Argo CD ===" # Удаление Argo CD (если установлен) helm uninstall argocd -n argocd 2>/dev/null || echo "Argo CD не найден или уже удален" # Удаление namespace Argo CD kubectl delete namespace argocd 2>/dev/null || echo "Namespace argocd не найден" echo "=== Удаление репозитория Helm ===" # Удаление репозитория Argo CD из Helm helm repo remove argo 2>/dev/null || echo "Репозиторий argo не найден" echo "=== Удаление Helm ===" # Удаление Helm через winget winget uninstall Helm.Helm 2>/dev/null || echo "Helm не найден" echo "=== Удаление k3d ===" # Удаление всех кластеров k3d k3d cluster delete --all 2>/dev/null || echo "Нет кластеров k3d для удаления" # Удаление k3d через winget winget uninstall k3d 2>/dev/null || echo "k3d не найден" echo "=== Очистка конфигурации kubectl ===" # Очистка контекста kubectl (опционально) kubectl config unset current-context 2>/dev/null || echo "Контекст не установлен" kubectl config delete-cluster k3d-* 2>/dev/null || echo "Кластеры k3d не найдены в конфиге" kubectl config delete-context k3d-* 2>/dev/null || echo "Контексты k3d не найдены" echo "=== Откат завершен ==="
Ссылка на репозиторий: https://github.com/Evgeni-86/LabClusterV1


