Строим простой локальный CI/CD на Windows: k3s + Jenkins + Gitea + ArgoCD
Для этого используем 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
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.