# Настройка Gitea Actions для Go проекта: Полное руководство ```metadata id: 4 readTime: 15-20 минут date: 2025-07-27 18:00 author: Direct-Dev(aka Антон Кузнецов) level: Средний tags: #gitea #gitea-actions #ci-cd #go #docker #devops #automation #k3s version: 1.0.0 ``` ## Содержание 1. [Введение](#введение) 2. [Подготовка проекта](#подготовка-проекта) 3. [Настройка Gitea Actions](#настройка-gitea-actions) 4. [Создание workflow файла](#создание-workflow-файла) 5. [Настройка секретов](#настройка-секретов) 6. [Тестирование и запуск](#тестирование-и-запуск) 7. [Мониторинг и отладка](#мониторинг-и-отладка) 8. [Заключение](#заключение) A. [Инфраструктура](#инфраструктура) B. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s) C. [Настройка Gitea Runner в LXC контейнере](#настройка-gitea-runner-в-lxc-контейнере) ## Введение Gitea Actions — это встроенная система непрерывной интеграции и развертывания (CI/CD) в Gitea, которая позволяет автоматизировать процессы сборки, тестирования и развертывания ваших проектов. Большой плюс этой системы в том, что она достаточна не требовательна к ресурсам и может быть развернута в собственном изолированном окружении. В этой статье мы рассмотрим как работать с данной системой, на примере настройки Gitea Actions для Go проекта с автоматической сборкой мультиплатформенных бинарников, созданием Docker образов и публикацией релизов. ### Итак, что мы будем делать - Настроим автоматическую сборку Go приложения для разных платформ - Создадим Docker образы для Linux AMD64 и ARM64 - Настроим публикацию в Docker Hub - Автоматизируем создание релизов с бинарниками - Сделаем коммит в отдельную ветку - которую можно связать с CD системой (flux/ArgoCD) - Настроим триггеры на основе Git тегов ## Подготовка проекта ### Структура проекта Собственно функциональная часть проекта Go не блещет оригинальностью и имеет следующую простую структуру: ``` text hello_gitea/ ├── .gitea/ # Конфигурация Gitea Actions └── workflows/ └── build.yaml # Workflow для сборки ├── main.go # Основной код приложения ├── go.mod # Зависимости Go ├── go.sum # Хеши зависимостей ├── Dockerfile # Docker образ ├── Dockerfile.builder # Docker образ для образа-билдера проекта ├── scripts/ # Вспомогательные скрипты └── release-interactive.sh # Скрипт для пушинга релиза ├── README.md # Документация └── .gitignore # Исключения Git ``` ### Анализ кода Для упрощения востприятия не применяются сложные архитектурные паттерны и концепции - нам надо просто минимальное Go приложение. Тем не менее это работоспособный http api сервер, который можно расширить парой тройкой эндпойнтов и использовать в других проектах как заглушку или тестовый api. Основная задача статьи не в написании api сервера на Go, а в автоматизации процесса его сборки. Наше приложение — это простой REST API сервер на Go с использованием Gin framework: ```go package main import ( "net/http" "os" "github.com/gin-gonic/gin" ) const version = "1.0.0" func main() { gin.SetMode(gin.ReleaseMode) r := gin.Default() // CORS middleware r.Use(func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(http.StatusNoContent) return } c.Next() }) // Endpoints r.GET("/healthz", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "ok", "version": version, }) }) r.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "Hello, World!", "version": version, }) }) // API group api := r.Group("/api/v1") { api.GET("/info", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "service": "hello-api", "version": version, "status": "running", }) }) api.POST("/echo", func(c *gin.Context) { var request struct { Message string `json:"message"` } if err := c.ShouldBindJSON(&request); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid JSON", }) return } c.JSON(http.StatusOK, gin.H{ "echo": request.Message, "version": version, }) }) } port := os.Getenv("PORT") if port == "" { port = "8080" } r.Run(":" + port) } ``` ### Dockerfile Помимо того, что мы будем собирать бинарники для разных платформ, мы также настроим сборку docker image в котором будем запускать наш сервер api - это может быть полезным для развертывания нашего приложения, если мы настроим такое - например через ArgoCD или flux (впрочем это тема отдельнйо статьи). Итак для контейнеризации используем многоэтапную сборку, что как вещают мудрецы является якобы полезным и правильным. Не будем спорить поэтому вот: ```dockerfile # Build stage FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder RUN apk --no-cache add git ca-certificates WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . ARG TARGETPLATFORM ARG BUILDPLATFORM ARG TARGETOS ARG TARGETARCH RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -installsuffix cgo -o hello-api main.go # Final stage FROM alpine:latest RUN apk --no-cache add ca-certificates jq RUN addgroup -g 1001 -S appgroup && \ adduser -u 1001 -S appuser -G appgroup WORKDIR /app COPY --from=builder /app/hello-api . RUN chown appuser:appgroup hello-api USER appuser EXPOSE 8080 CMD ["./hello-api"] ``` **Ремарка:** Тут хочется немного поразмышлять - по хорошему надо или разобраться в вариантах кэширования gitea или в качестве базововго образа для каждого из этапов использовать предварительно собранный образ со всем необходимым. При каждом запуске workflow будет запускаться новый контейнер docker in docker и там запускаться построение образа естественно кэшей не будет или надо как то придумывать как их хранить на ранере и пробрасывать в dind. Ну или руками сделать нужные образы и использоват их в Dockerfile. Правда пулиться они опять же будут походу каждый раз. Короче говоря тут есть чем поразбираться и пооптимизировать ... ## Настройка Gitea Actions ### Включение Actions в Gitea 1. **Проверьте версию Gitea** Gitea Actions доступны начиная с версии 1.17.0. Убедитесь, что ваш сервер Gitea поддерживает Actions. 2. **Включите Actions в настройках репозитория** - Перейдите в настройки репозитория - Найдите раздел "Actions" - Включите "Enable Actions" ### Создание токена доступа 1. **Создайте токен для Actions** - Перейдите в настройки профиля → "Applications" - Создайте новый токен с правами на репозиторий - Скопируйте токен (он понадобится позже) ## Создание workflow файла ### Структура директории Создайте директорию `.gitea/workflows/` в корне вашего проекта: ```bash mkdir -p .gitea/workflows ``` ### Основной workflow файл Создайте файл `.gitea/workflows/build.yaml`: ```yaml name: Release Build on: push: tags: - v* jobs: create-release: runs-on: ubuntu-latest container: # image: golang:1.21 # image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:builder-1.0.32 image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest steps: - name: Checkout repository run: | git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea cd hello_gitea git checkout ${{ github.ref }} - name: Setup Go run: | # Install jq for JSON parsing # apt-get update && apt-get install -y jq git --version go version jq --version - name: Build all binaries run: | cd hello_gitea # Проверяем, изменились ли зависимости if [ ! -f go.sum ] || ! go mod verify >/dev/null 2>&1; then echo "Dependencies changed, downloading..." go mod download fi mkdir -p bin echo "Building for all platforms..." # Build for all platforms using direct go build commands echo "Building for linux amd64..." GOOS=linux GOARCH=amd64 go build -o bin/hello-api-linux-amd64 main.go echo "Building for linux arm64..." GOOS=linux GOARCH=arm64 go build -o bin/hello-api-linux-arm64 main.go echo "Building for windows amd64..." GOOS=windows GOARCH=amd64 go build -o bin/hello-api-windows-amd64.exe main.go echo "Building for darwin amd64..." GOOS=darwin GOARCH=amd64 go build -o bin/hello-api-darwin-amd64 main.go echo "Building for darwin arm64..." GOOS=darwin GOARCH=arm64 go build -o bin/hello-api-darwin-arm64 main.go # Create archives echo "Creating archives..." cd bin # Create archives with correct file names tar -czf hello-api-linux-amd64.tar.gz hello-api-linux-amd64 tar -czf hello-api-linux-arm64.tar.gz hello-api-linux-arm64 tar -czf hello-api-windows-amd64.tar.gz hello-api-windows-amd64.exe tar -czf hello-api-darwin-amd64.tar.gz hello-api-darwin-amd64 tar -czf hello-api-darwin-arm64.tar.gz hello-api-darwin-arm64 echo "Listing bin directory..." ls -la - name: Create Release run: | cd hello_gitea # Create release using Gitea API echo "Creating release..." curl -X POST \ -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ -H "Content-Type: application/json" \ -d '{ "tag_name": "${{ github.ref_name }}", "name": "Release ${{ github.ref_name }}", "body": "Automated release with multi-platform binaries and Docker image", "draft": false, "prerelease": false }' \ "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases" echo "Getting release id..." RELEASE_ID=$(curl -s -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/tags/${{ github.ref_name }}" | \ jq -r '.id') # Upload all binaries echo "Uploading assets..." for file in bin/*.tar.gz; do echo "Uploading $file..." curl -X POST \ -H "Authorization: token ${{ secrets.GITEATOKEN }}" \ -H "Content-Type: application/octet-stream" \ --data-binary @$file \ "https://direct-dev.ru/gitea/api/v1/repos/GiteaAdmin/hello_gitea/releases/$RELEASE_ID/assets?name=$(basename $file)" done echo "Release created successfully" create-docker-image: runs-on: ubuntu-latest container: image: docker:28.3.2-dind needs: create-release steps: - name: Checkout repository run: | # Install git apk add --no-cache git echo "=== GitHub Variables ===" echo "github.ref = ${{ github.ref }}" echo "github.ref_name = ${{ github.ref_name }}" echo "github.sha = ${{ github.sha }}" echo "github.repository = ${{ github.repository }}" echo "========================" git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea cd hello_gitea git checkout ${{ github.ref }} - name: Setup Docker Buildx run: | # Docker is already installed in docker:dind image docker --version echo "Setting up Docker Buildx for multi-platform builds..." # Setup Docker Buildx for multi-platform builds docker buildx create --name go-buildx --use docker buildx inspect --bootstrap - name: Login to Docker Hub run: | echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin - name: Build multi-platform Docker images run: | cd hello_gitea echo "Building multi-platform images using buildx..." # Build multi-platform images using buildx docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:${{ github.ref_name }} \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/hello-api:latest \ --push \ . echo "Multi-platform images built successfully" - name: Remove buildx run: | echo "Removing buildx..." docker buildx rm go-buildx update-to-release-branch: runs-on: ubuntu-latest container: image: docker:28.3.2-dind needs: create-docker-image steps: - name: Create Release Branch run: | echo "Creating release branch..." echo "=== GitHub Variables ===" echo "github.ref = ${{ github.ref }}" echo "github.ref_name = ${{ github.ref_name }}" echo "github.sha = ${{ github.sha }}" echo "github.repository = ${{ github.repository }}" echo "DOCKERHUB_USERNAME = ${{ secrets.DOCKERHUB_USERNAME }}" echo "DOCKERHUB_TOKEN = ${{ secrets.DOCKERHUB_TOKEN }}" echo "GITEATOKEN = ${{ secrets.GITEATOKEN }}" echo "========================" # Clone repository echo "Cloning repository..." git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea cd hello_gitea # Configure git echo "Configuring git..." git config user.email "info@direct-dev.ru" git config user.name "Direct-Dev-Robot" # Check if release branch exists echo "Checking if release branch exists..." if git ls-remote --heads origin release | grep -q release; then echo "Release branch exists, checking out..." git checkout release echo "release branch exists - pulling release branch..." git pull origin release else echo "release branch does not exist - creating new release branch..." git checkout -b release fi # Reset to the tag commit echo "Resetting to the tag commit ${{ github.ref_name }} ..." git reset --hard ${{ github.ref_name }} # Push changes to release branch echo "Pushing changes to release branch..." git push origin release --force echo "Changes pushed to release branch successfully" ``` ### Разберемся как работает workflow **Триггеры:** - Workflow запускается при пуше в Gitea тега, начинающегося с `v*` (например, `v1.1.29`) **Jobs:** 1 **create-release:** - Эта таска запускается первой и не требует никаких зависимых тасков выполненными - Использует контейнер собранный на базе golang:1.24 для сборки бинарников (image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest) - Собирает бинарники для всех платформ (Linux, Windows, macOS) - Создает архивы с бинарниками - Создает релиз с именем текущей версии через Gitea API - Загружает бинарники как assets релиза для работы данного job используется кастомный образ - если бы мы использовали просто golang:1.24, то при каждом запуске данного job необходимо было бы скачивать jq и устанавливаться в контейнере (возможно со временем, что-то еще потребовалось бы ...) ```yaml - name: Setup Go container run: | # Install jq for JSON parsing apt-get update && apt-get install -y jq git --version go version jq --version ``` **решение в лоб:** применено, чтобы убрать эту повторяющуюся работу при каждом выполнении workflow - сделан свой образ - "${DOCKERHUB_USERNAME}"/my-build-golang-runner:latest ``` Dockerfile # базовый образ FROM golang:1.24 # Устанавливаем пакеты (одинаково работают на amd64/arm64) RUN apt-get update && \ apt-get install -y --no-install-recommends \ git \ ca-certificates \ jq && \ rm -rf /var/lib/apt/lists/* # Создаем рабочую директорию WORKDIR /app # Копируем файлы зависимостей COPY go.mod go.sum ./ # Предварительно загружаем все зависимости RUN go mod download && go mod verify # Устанавливаем переменные окружения ENV GOPATH=/go ENV PATH=$PATH:/go/bin:/usr/local/bin # (Опционально) Можно добавить команду по умолчанию CMD ["bash"] ``` Сначала собрал его и запушил (желательно с мультиплатформенностью) на каком-то локальном АРМ (не раннере) ```bash #!/bin/bash docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag ${DOCKERHUB_USERNAME:-defaultdockeruser}/my-build-golang-runner:latest \ --push \ -f Dockerfile_for_runner_image \ . ``` решение рабочее, но надо всегда иметь под рукой комп с buildx - лучше автоматизировать все и сделать задачу для сборки на раннере `.gitea/workflows/build-builder.yaml` ```yaml name: Build Builder Docker Image on: push: tags: - builder-* jobs: create-builder-docker-image: runs-on: ubuntu-latest container: image: docker:28.3.2-dind steps: - name: Checkout repository run: | # Install git apk add --no-cache git echo "=== GitHub Variables ===" echo "github.ref = ${{ github.ref }}" echo "github.ref_name = ${{ github.ref_name }}" echo "github.sha = ${{ github.sha }}" echo "github.repository = ${{ github.repository }}" echo "========================" echo "Cloning..." git clone https://oauth2:${{ secrets.GITEATOKEN }}@direct-dev.ru/gitea/GiteaAdmin/hello_gitea.git hello_gitea cd hello_gitea echo "Checkout to ${{ github.ref }} ..." git checkout ${{ github.ref }} - name: Setup Docker Buildx run: | # Docker is already installed in docker:dind image echo "look at docker version" docker --version # Setup Docker Buildx for multi-platform builds echo "setup buildx" docker buildx create --name builder-builx --use docker buildx inspect --bootstrap - name: Login to Docker Hub run: | echo "login to docker hub ..." echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin - name: Build multi-platform Docker images run: | cd hello_gitea echo "Build multi-platform images using buildx ..." docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:${{ github.ref_name }} \ --tag ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest \ --push \ -f Dockerfile.builder \ . - name: Remove buildx run: | docker buildx rm builder-builx ``` эта задача будет запущена на ранере при пуше тега с префиксом `builder-` 2 **create-docker-image:** - создание образов docker с нашим проектом - Запускается только после успешного завершения `create-release` - Используем Docker-in-Docker контейнер (image: docker:28.3.2-dind) - Настраиваем Docker Buildx для мультиплатформенной сборки (docker buildx create --use docker buildx inspect --bootstrap) - Авторизуемся в Docker Hub (echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin) - Собираем образы для Linux AMD64 и ARM64 (команда docker buildx build \ ...) - Публикуем образы с тегом версии и `latest` (опция --push команды docker buildx build ...) чтобы авторизация сработала на докерхабе надо внести секреты DOCKERHUB_TOKEN, DOCKERHUB_USERNAME или вцелом для всего инстанса gitea в настройках инстанса или в настройках конкретного репозитория я использовал докерхаб, но можно заморочиться и настроить работу с приватным репозиторием ... Уже после ряда тестовых релизо выяснил, что запуски workflow `build.yaml` да собственно как и `build-builder.yaml` порождают на хосте раннера зависшие докер контейнеры buildx ```text СONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2a9ad63c5a31 moby/buildkit:buildx-stable-1 "buildkitd --allow-i…" 11 seconds ago Up 10 seconds buildx_buildkit_test-buldx0 843eee192570 moby/buildkit:buildx-stable-1 "buildkitd --allow-i…" 22 minutes ago Up 22 minutes buildx_buildkit_modest_haibt0 ``` Это происходит потому, что раннер запускает докер в докере контейнер также docker run --privileged -it --rm -v /var/run/docker.sock:/var/run/docker.sock docker:28.3.2-dind sh то есть пробрасывает хостовый сокет докер демона в контейнер и когда там выполняется `docker buildx create --use` контейнер запускается на хосте а не внутри docker:28.3.2-dind Со временем это может превратиться в утечку ресурсов Решения как минимум два - добавить параметр --name - `docker buildx create --name go-buildx --use` - тогда подхватится существующий контейнер или создастся новый. Второй способ удалять buildx `docker buildx rm --name go-buildx` ## Настройка секретов ### Необходимые секреты В настройках репозитория (или инстанса) → "Secrets and variables" → "Actions" добавьте следующие секреты: 1. **GITEATOKEN** - Токен доступа к Gitea API - Используется для клонирования репозитория и создания релизов 2. **DOCKERHUB_USERNAME** - Ваше имя пользователя в Docker Hub - Используется для публикации образов 3. **DOCKERHUB_TOKEN** - Токен доступа к Docker Hub - Используется для авторизации в Docker Hub ### Создание токенов **Gitea Token:** 1. Перейдите в настройки профиля → "Applications" 2. Создайте новый токен с правами на репозиторий 3. Скопируйте токен 4. В разделе Actions репозитория создайте секрет уровня репозитория - я создал с именем GITEATOKEN (такой не даст сделать: GITEA_TOKEN) **Docker Hub Token:** 1. Войдите в Docker Hub 2. Перейдите в Account Settings → Security 3. Создайте новый Access Token 4. Скопируйте токен 5. В разделе Actions настроек инстанса gitea создайте секреты уровня инстанса (если много пользователей работает в gitea, то можно внести в уровень репозитория) я создал с именами DOCKERHUB_USERNAME, DOCKERHUB_TOKEN ## Тестирование и запуск ### Локальное тестирование 1 **Проверим синтаксис workflow:** ну если мы в ide, то наверное все хорошо и уже автоматом отформатировано ... но тем не менее ... ```bash # Убедитесь, что YAML синтаксис корректен yamllint .gitea/workflows/build.yaml ``` 2 **Протестируем сборку локально:** пока ручное тестирование ```bash # Сборка для текущей платформы go build -o hello-api main.go # Сборка для других платформ GOOS=linux GOARCH=amd64 go build -o hello-api-linux-amd64 main.go GOOS=linux GOARCH=arm64 go build -o hello-api-linux-arm64 main.go ``` 3 **Тестирование Docker образа:** ```bash # Сборка образа docker build -t hello-api:test . # Запуск контейнера docker run -p 8080:8080 hello-api:test # Тестирование API curl http://localhost:8080/healthz ``` тут надо погонять curl по эндпойнтам, убедиться что приложение работает (пока тоже вручную) ### Запуск Actions 1 **Создаем тег вручную или скриптом (`scripts/release-interactive.sh`):** ```bash git tag v1.1.20 git push origin v1.1.20 # текст скрипта - запуск можно сделать через make #release-interactive: # @./scripts/release-interactive.sh #!/bin/bash # Интерактивный скрипт для автоматизации релиза # Использование: ./scripts/release-interactive.sh [version] # Если версия не указана, скрипт запросит её интерактивно set -e # Остановить выполнение при ошибке # Функция для получения версии интерактивно get_version_interactive() { echo "🚀 Создание нового релиза" echo "" # Показываем текущую версию CURRENT_VERSION=$(grep 'const version = "' main.go | sed 's/const version = "\([^"]*\)"/\1/') echo "📋 Текущая версия: $CURRENT_VERSION" echo "" # Запрашиваем новую версию read -r -p "Введите новую версию (формат X.Y.Z): " VERSION # Проверяем, что версия не пустая if [ -z "$VERSION" ]; then echo "❌ Версия не может быть пустой" exit 1 fi # Проверяем формат версии if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "❌ Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" exit 1 fi # Подтверждение echo "" echo "📝 Подтверждение:" echo " Текущая версия: $CURRENT_VERSION" echo " Новая версия: $VERSION" echo "" read -r -p "Продолжить? (y/N): " CONFIRM if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then echo "❌ Релиз отменен" exit 0 fi } # Проверяем, передана ли версия как аргумент if [ $# -eq 0 ]; then # Версия не указана, запрашиваем интерактивно get_version_interactive else # Версия указана как аргумент VERSION=$1 # Проверка формата версии if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Ошибка: Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" exit 1 fi fi echo "🚀 Начинаем релиз версии v$VERSION..." # Проверяем, что мы в git репозитории if ! git rev-parse --git-dir > /dev/null 2>&1; then echo "Ошибка: Не найден git репозиторий" exit 1 fi # Проверяем, что нет незакоммиченных изменений # if ! git diff-index --quiet HEAD --; then # echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их." # exit 1 # fi # Обновляем версию в main.go echo "📝 Обновляем версию в main.go..." if [[ "$OSTYPE" == "darwin"* ]]; then # macOS sed -i '' "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go else # Linux sed -i "s/const version = \"[^\"]*\"/const version = \"$VERSION\"/" main.go fi # Проверяем, что изменение применилось if ! grep -q "const version = \"$VERSION\"" main.go; then echo "Ошибка: Не удалось обновить версию в main.go" exit 1 fi echo "✅ Версия обновлена в main.go" # Обновляем версию в makefile echo "📝 Обновляем версию в makefile..." if [[ "$OSTYPE" == "darwin"* ]]; then # macOS sed -i '' "s/^VERSION=.*/VERSION=$VERSION/" makefile else # Linux sed -i "s/^VERSION=.*/VERSION=$VERSION/" makefile fi # Проверяем, что изменение применилось if ! grep -q "^VERSION=$VERSION" makefile; then echo "Ошибка: Не удалось обновить версию в makefile" exit 1 fi echo "✅ Версия обновлена в makefile" # Выполняем git команды echo "📦 Добавляем изменения в git..." git add . echo "💾 Создаем коммит..." git commit -m "Release v$VERSION" echo "🏷️ Создаем тег..." git tag -a "v$VERSION" -m "Release v$VERSION" echo "🚀 Отправляем изменения и теги..." git push git push --tags echo "🎉 Релиз v$VERSION успешно завершен!" echo "📋 Выполненные действия:" echo " - Обновлена версия в main.go" echo " - Обновлена версия в makefile" echo " - Создан коммит с сообщением 'Release v$VERSION'" echo " - Создан тег v$VERSION" echo " - Изменения отправлены в удаленный репозиторий" ``` 2 **Мониторинг выполнения:** - Переходим в репозиторий → "Actions" - Находим запущенный workflow - Отслеживаем выполнение каждого job ### Ожидаемый результат После успешного выполнения: 1 **Релиз** будет создан в Gitea с бинарниками: - `hello-api-linux-amd64.tar.gz` - `hello-api-linux-arm64.tar.gz` - `hello-api-windows-amd64.tar.gz` - `hello-api-darwin-amd64.tar.gz` - `hello-api-darwin-arm64.tar.gz` 2 **Docker образы** будут опубликованы в Docker Hub: - `username/hello-api:v1.1.20` - `username/hello-api:latest` 3 **В ветке relese** появится новый коммит - на него можно, например, настроить деплой ArgoCD/flux в кластере k3s ## Мониторинг и отладка ### Просмотр логов 1 **В Gitea:** - Переходим в репозиторий → "Actions" - Выберем workflow - Нажмем на job для просмотра логов 2 **Отладка ошибок:** - Проверим правильность секретов - Убедимся в корректности путей к репозиторию - Проверим права доступа токенов ### Частые проблемы 1 **Ошибка авторизации в Docker Hub:** - Проверим правильность `DOCKERHUB_TOKEN` - Убедимся, что токен не истек 2 **Ошибка создания релиза:** - Проверим права токена `GITEATOKEN` - Убедимся, что тег не существует 3 **Ошибка сборки:** - Проверим зависимости в `go.mod` - Убедимся в корректности Dockerfile ## Заключение В этой статье мы рассмотрели полный процесс настройки Gitea Actions для Go проекта. Мы создали автоматизированный pipeline, который: - ✅ Собирает мультиплатформенные бинарники - ✅ Создает Docker образы для разных архитектур - ✅ Публикует образы в Docker Hub - ✅ Создает релизы с бинарниками - ✅ Запускается автоматически при создании тегов ### Преимущества такого подхода 1. **Автоматизация:** Минимизация ручной работы 2. **Консистентность:** Одинаковые условия сборки 3. **Мультиплатформенность:** Поддержка разных ОС и архитектур 4. **Безопасность:** Использование секретов для токенов 5. **Масштабируемость:** Легко добавить новые платформы или этапы ### Следующие шаги 1. тестирование в workflow 2. автоматическое развертывание 3. уведомления о результатах сборки 4. мониторинг и алерты Gitea Actions предоставляет мощные возможности для автоматизации процессов разработки, и с правильной настройкой вы можете значительно упростить процесс доставки вашего ПО. --- ## Инфраструктура ### Архитектура системы Этот и последующие разделы описывают инфраструктуру, на которой я проверял все описанное выше. Если у вас используются другие подходы, то можете пропустить чтение этих разделов или ознакомиться для общего развития. Итак инфраструктура состоит из следующих компонентов: ``` text Proxmox VE (ARM64) ├── K3s Cluster ├── Master Node └── Gitea Server (Helm Chart) └── Worker Nodes └── LXC Containers └── Gitea Runner Container ├── Docker Engine ├── Go Toolchain └── Build Tools ``` ### Требования к системе **Proxmox VE:** - ARM64 архитектура (orangepi 5 Plus) - Минимум 8GB RAM (16Gb) - 100GB свободного места (1 Gb) - Поддержка LXC контейнеров (+) **K3s Cluster:** - Kubernetes 1.24+ - Helm 3.8+ - Ingress Controller (Traefik) - Persistent Storage (NFS) **LXC Container:** - Ubuntu 22.04 LTS - 4GB RAM - 20GB дискового пространства - Docker Engine ## Установка Gitea в кластере K3s ### Анализ существующего кластера Текущая конфигурация: **Узлы кластера:** ```bash kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME k3s-control-01 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.xxx.x.2 10.xx.x.2 Ubuntu 20.04.6 LTS 5.10.160-rockchip-rk3588 containerd://2.0.5-k3s1 k3s-control-02 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.xxx.x.3 10.xx.x.3 Ubuntu 20.04.6 LTS 5.10.160-rockchip-rk3588 containerd://2.0.5-k3s1 k3s-control-03 Ready control-plane,etcd,master 544d v1.33.2+k3s1 10.xxx.x.4 10.xx.x.4 Ubuntu 20.04.6 LTS 6.1.31-sun50iw9 containerd://2.0.5-k3s1 ``` **Существующие компоненты:** - Kubernetes v1.33.2+k3s1 - 3 узла control-plane с etcd - Traefik Ingress Controller - NFS Storage Class для persistent storage - Cert-Manager для SSL сертификатов - Gitea уже установлен и работает ### Проверка существующей установки Gitea ```bash # Проверка подов Gitea kubectl get pods -n gitea NAME READY STATUS RESTARTS AGE gitea-798c56b58f-bsp2h 1/1 Running 0 30h gitea-postgres-0 2/2 Running 10 (10d ago) 357d gitea-valkey-cluster-0 1/1 Running 0 30h gitea-valkey-cluster-1 1/1 Running 0 30h gitea-valkey-cluster-2 1/1 Running 0 30h # Проверка сервисов kubectl get svc -n gitea NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE gitea-http ClusterIP None 3000/TCP 543d gitea-postgres ClusterIP 10.xx.xx.xx 5432/TCP 543d gitea-ssh ClusterIP None 22/TCP 543d gitea-valkey-cluster ClusterIP 10.xx.xx.xx 6379/TCP 30h gitea-valkey-cluster-headless ClusterIP None 6379/TCP,16379/TCP 30h # Проверка persistent storage kubectl get pvc -n gitea NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE gitea-shared-storage Bound pvc-c869aea8-7b49-4b40-9b86-1a7fbe9b2ce8 10Gi RWO nfs 543d pgdata-gitea-postgres-0 Bound pvc-c3405a46-594d-401f-91ae-e7398b3c5cc3 15Gi RWO nfs 543d valkey-data-gitea-valkey-cluster-0 Bound pvc-a927baf5-b8f8-4c0b-b12f-68fbc63162d9 8Gi RWO nfs 30h valkey-data-gitea-valkey-cluster-1 Bound pvc-b43a399b-fdf4-4c27-baf4-7056ecc4143b 8Gi RWO nfs 30h valkey-data-gitea-valkey-cluster-2 Bound pvc-368f0ab9-7851-423f-afb9-443287c0c728 8Gi RWO nfs 30h ``` ### Анализ конфигурации **Gitea версия:** 1.24.3 (rootless) **База данных:** PostgreSQL с persistent storage (15Gi) **Кэш:** Valkey cluster (Redis) с 3 репликами **Storage:** NFS Storage Class **Ingress:** Traefik ### Настройка доступа к Gitea Доступ к gitea лучше сделать через ingress или на худой конец через port-forward: 1 **Временный доступ через port-forward:** ```bash # Доступ к Gitea через port-forward kubectl port-forward -n gitea svc/gitea-http 3000:3000 # В другом терминале проверьте доступ curl http://localhost:3000 ``` 2 **Создание IngressRoute для постоянного доступа:** ```yaml # gitea-ingressroute.yaml # image: docker.io/traefik:v3.0.0 поэтому используется абстракция # apiVersion: traefik.io/v1alpha1 # kind: IngressRoute apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: annotations: cert-manager.io/cluster-issuer: letsencrypt-dns-cloudflare creationTimestamp: "2024-05-24T03:50:33Z" name: gitea-https-route namespace: gitea spec: entryPoints: - websecure routes: - kind: Rule match: Host(`direct-dev.ru`) && PathPrefix(`/gitea`) middlewares: - name: strip-gitea-prefix services: - name: gitea-http port: 3000 tls: secretName: le-root-direct-dev-ru ``` ```bash # Применение ingressroute KUBECONFIG=~/.kube/config_hlab kubectl apply -f gitea-ingress.yaml ``` Таким образом мой инстанс gitea доступен с внешнего адреса в /data/gitea/conf/app.ini [server] ROOT_URL = DOMAIN = direct-dev.ru ### Конфигурация Gitea Gitea установлен через Helm. **Версия:** Gitea 1.24.3 (rootless) **Архитектура:** - PostgreSQL для базы данных (развернут отдельно ) - Valkey cluster (Redis) для кэширования - NFS Storage Class для persistent storage ```bash helm repo add gitea-charts https://dl.gitea.io/charts/ helm repo update kubectl create namespace gitea helm install gitea gitea-charts/gitea -n gitea ``` установка с кастомными значениями параметров ``` bash helm show values gitea-charts/gitea > gitea-values.yaml #Отредактируйте файл (например, database или service). helm install gitea gitea-charts/gitea -n gitea -f gitea-values.yaml ``` Обновление ```bash helm repo update helm upgrade gitea gitea-charts/gitea -n gitea -f gitea-values.yaml ``` **Проверка конфигурации:** ```bash # Проверка Helm релиза helm list -n gitea # Просмотр конфигурации helm get values gitea -n gitea ``` ```yaml USER-SUPPLIED VALUES: gitea: admin: existingSecret: xxxxxx config: cache: ADAPTER: memory database: DB_TYPE: postgres HOST: gitea-postgres.gitea.svc.cluster.local:5432 NAME: gitea PASSWD: xxxxxxx SCHEMA: gitea USER: xxxx indexer: ISSUE_INDEXER_TYPE: bleve REPO_INDEXER_ENABLED: true queue: TYPE: level server: APP_DATA_PATH: /data DOMAIN: direct-dev.ru ENABLE_PPROF: false HTTP_PORT: 3000 PROTOCOL: http ROOT_URL: https://direct-dev.ru/gitea SSH_DOMAIN: git.k3s-cluster-01.direct-dev.ru SSH_LISTEN_PORT: 2222 SSH_PORT: 22 service: DISABLE_REGISTRATION: true SHOW_REGISTRATION_BUTTON: false session: PROVIDER: db persistence: enabled: true storageClass: nfs postgresql: enabled: false postgresql-ha: enabled: false redis-cluster: enabled: false ``` Как видите я организовал доступ через внешний IP к URL `https://direct-dev.ru/gitea`, то есть через префикс роута, опыт работы с actions показал, что лучше бы организовать было через поддомен третьего уровня: что то типа `https://gitea.direct-dev.ru` - в этом случае всякие разные предопределенные jobs типа checkout@v3 должны клонирование отрабатывать нормально. ``` bash # Проверка секретов kubectl get secrets -n gitea ``` ### Настройка Actions в существующем инстансе Gitea 1 **Проверка включения Actions:** ```bash # Проверка конфигурации Actions kubectl exec -n gitea deployment/gitea -c gitea -- cat /data/gitea/conf/app.ini" ``` 2 **Включение Actions через веб-интерфейс:** - Откройте Gitea через port-forward или ingress - Перейдите в Site Administration → Actions - Включите "Enable Actions" - Настройте "Default Actions URL" (например, ) 3 **Проверка работы Actions:** ```bash # Проверка логов Gitea на предмет ошибок Actions kubectl logs -n gitea deployment/gitea | grep -i action ``` ### Настройка DNS и SSL Инструкция по установке Cert-Manager не приводится - инструкций вагон и маленькая тележка - нет смысла повторяться: ```bash # Проверка Cert-Manager kubectl get pods -n cert-manager # Проверка ClusterIssuer kubectl get clusterissuer ``` 1. **Настройка DNS записи:** ```text Добавить A-запись в DNS (у меня сloudflare) gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ... чтобы в итоге запрос поступил на traefik ``` 2 **Создание ClusterIssuer (если не существует):** ```bash # Создание ClusterIssuer для Let's Encrypt - лучше через dns resolver cat < /dev/null # Установка Docker apt update apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin # Добавление пользователя в группу docker usermod -aG docker $USER # Запуск Docker systemctl enable docker systemctl start docker # Проверка установки docker --version ``` 3 **Установка Go:** ```bash # Скачивание Go для ARM64 wget https://go.dev/dl/go1.21.0.linux-arm64.tar.gz # Распаковка tar -C /usr/local -xzf go1.21.0.linux-arm64.tar.gz # Настройка переменных окружения echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc echo 'export GOPATH=$HOME/go' >> ~/.bashrc echo 'export GOROOT=/usr/local/go' >> ~/.bashrc source ~/.bashrc # Проверка установки go version # Должно показать: go version go1.21.0 linux/arm64 # Создание рабочей директории mkdir -p $HOME/go/{bin,src,pkg} ``` 4 **Установка дополнительных инструментов:** ```bash # Установка build tools apt install -y build-essential # Установка jq для JSON обработки apt install -y jq # Установка yamllint для проверки YAML apt install -y yamllint # Установка Docker Buildx docker buildx version ``` ### Установка Gitea Runner 1. **Скачивание Gitea Runner:** ```bash # Определение архитектуры ARCH=$(uname -m) case $ARCH in x86_64) ARCH=amd64 ;; aarch64) ARCH=arm64 ;; armv7l) ARCH=armv7 ;; esac # Скачивание последней версии # visit https://dl.gitea.com/act_runner/ copy link to version and arch you need wget https://dl.gitea.com/act_runner/0.2.12/act_runner-0.2.12-linux-arm64 # Переименование и установка mv act_runner-0.2.12-linux-arm64 /usr/local/bin/act_runner chmod +x /usr/local/bin/act_runner # Проверка установки act_runner --version ``` 2 **Создание пользователя для runner:** ```bash # Создание пользователя useradd -m -s /bin/bash gitea-runner usermod -aG docker gitea-runner # Создание директории для конфигурации mkdir -p /opt/gitea-runner chown gitea-runner:gitea-runner /opt/gitea-runner ``` 3 **Настройка конфигурации runner:** ```bash # Переключение на пользователя runner su - gitea-runner # Создание конфигурационного файла cat > /opt/gitea-runner/config.yaml < config.yaml` to generate a config file. log: # The level of logging, can be trace, debug, info, warn, error, fatal level: info runner: # Where to store the registration result. file: .runner # Execute how many tasks concurrently at the same time. capacity: 1 # Extra environment variables to run jobs. envs: A_TEST_ENV_NAME_1: a_test_env_value_1 A_TEST_ENV_NAME_2: a_test_env_value_2 # Extra environment variables to run jobs from a file. # It will be ignored if it's empty or the file doesn't exist. env_file: .env # The timeout for a job to be finished. # Please note that the Gitea instance also has a timeout (3h by default) for the job. # So the job could be stopped by the Gitea instance if it's timeout is shorter than this. timeout: 3h # The timeout for the runner to wait for running jobs to finish when shutting down. # Any running jobs that haven't finished after this timeout will be cancelled. shutdown_timeout: 0s # Whether skip verifying the TLS certificate of the Gitea instance. insecure: true # The timeout for fetching the job from the Gitea instance. fetch_timeout: 5s # The interval for fetching the job from the Gitea instance. fetch_interval: 2s # The labels of a runner are used to determine which jobs the runner can run, and how to run them. # Like: "macos-arm64:host" or "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" # Find more images provided by Gitea at https://gitea.com/docker.gitea.com/runner-images . # If it's empty when registering, it will ask for inputting labels. # If it's empty when execute `daemon`, will use labels in `.runner` file. labels: - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" - "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04" - "ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04" cache: # Enable cache server to use actions/cache. enabled: true # The directory to store the cache data. # If it's empty, the cache data will be stored in $HOME/.cache/actcache. dir: "" # The host of the cache server. # It's not for the address to listen, but the address to connect from job containers. # So 0.0.0.0 is a bad choice, leave it empty to detect automatically. host: "" # The port of the cache server. # 0 means to use a random available port. port: 0 # The external cache server URL. Valid only when enable is true. # If it's specified, act_runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself. # The URL should generally end with "/". external_server: "" container: # Specifies the network to which the container will connect. # Could be host, bridge or the name of a custom network. # If it's empty, act_runner will create a network automatically. network: "" # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker). privileged: false # And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway). options: # The parent directory of a job's working directory. # NOTE: There is no need to add the first '/' of the path as act_runner will add it automatically. # If the path starts with '/', the '/' will be trimmed. # For example, if the parent directory is /path/to/my/dir, workdir_parent should be path/to/my/dir # If it's empty, /workspace will be used. workdir_parent: # Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob # You can specify multiple volumes. If the sequence is empty, no volumes can be mounted. # For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to: # valid_volumes: # - data # - /src/*.json # If you want to allow any volume, please use the following configuration: # valid_volumes: # - '**' valid_volumes: [] # overrides the docker client host with the specified one. # If it's empty, act_runner will find an available docker host automatically. # If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers. # If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work. docker_host: "" # Pull docker image(s) even if already present force_pull: true # Rebuild docker image(s) even if already present force_rebuild: false host: # The parent directory of a job's working directory. # If it's empty, $HOME/.cache/act/ will be used. workdir_parent: EOF ``` ### Регистрация Runner в Gitea 1. **Получение токена регистрации:** - Войдите в веб-интерфейс Gitea - Перейдите в Settings → Actions → Runners - Нажмите "New Runner" - Скопируйте токен регистрации 2 **Регистрация runner:** ```bash # В контейнере LXC под пользователем gitea-runner act_runner register \ --instance https://gitea.your-domain.com \ --token YOUR_REGISTRATION_TOKEN \ --name "k3s-runner-arm64" \ --labels "ubuntu-latest:docker://node:18,ubuntu-22.04:docker://node:18,self-hosted:docker://golang:1.21,arm64:docker://golang:1.21" \ --no-interactive ``` 3 **Создание systemd сервиса:** ```bash # Создание файла сервиса cat > /etc/systemd/system/gitea-runner.service < /opt/gitea-runner/monitor.sh <<'EOF' #!/bin/bash LOG_FILE="/opt/gitea-runner/logs/monitor.log" RUNNER_STATUS=$(systemctl is-active gitea-runner) echo "$(date): Runner status: $RUNNER_STATUS" >> $LOG_FILE if [ "$RUNNER_STATUS" != "active" ]; then echo "$(date): Restarting gitea-runner service" >> $LOG_FILE systemctl restart gitea-runner fi # Проверка свободного места DISK_USAGE=$(df /opt/gitea-runner | tail -1 | awk '{print $5}' | sed 's/%//') if [ $DISK_USAGE -gt 80 ]; then echo "$(date): High disk usage: ${DISK_USAGE}%" >> $LOG_FILE # Очистка старых логов и кэша find /opt/gitea-runner/logs -name "*.log" -mtime +7 -delete docker system prune -f fi EOF chmod +x /opt/gitea-runner/monitor.sh ``` 2 **Настройка cron для мониторинга:** ```bash # Добавление в crontab echo "*/5 * * * * /opt/gitea-runner/monitor.sh" | crontab - ``` ### Тестирование Runner 1. **Проверка подключения:** ```bash # Проверка статуса runner в Gitea curl -H "Authorization: token YOUR_GITEA_TOKEN" \ https://gitea.your-domain.com/api/v1/actions/runners ``` 2 **Создание тестового workflow:** ```yaml # .gitea/workflows/test-runner.yaml name: Test Runner # on: # push: # branches: [main] on: push: tags: - v* jobs: test: runs-on: self-hosted steps: - name: Checkout run: | git clone https://oauth2:${{ secrets.GITEATOKEN }}@gitea.your-domain.com/your-username/your-repo.git cd your-repo - name: Test Go run: | go version go mod download go build -o test-app main.go - name: Test Docker run: | docker --version docker run hello-world - name: Test ARM64 Build run: | # Тестирование сборки для ARM64 GOOS=linux GOARCH=arm64 go build -o test-app-arm64 main.go file test-app-arm64 # Должно показать: ELF 64-bit LSB executable, ARM aarch64 ``` ### Оптимизация производительности 1. **Настройка Docker daemon:** ```bash # Создание daemon.json cat > /etc/docker/daemon.json < /etc/systemd/system/gitea-runner.service < /opt/gitea-runner/backup.sh <<'EOF' #!/bin/bash BACKUP_DIR="/opt/gitea-runner/backups" DATE=$(date +%Y%m%d_%H%M%S) mkdir -p $BACKUP_DIR # Резервное копирование конфигурации tar -czf $BACKUP_DIR/config_$DATE.tar.gz /opt/gitea-runner/config.yaml # Резервное копирование логов tar -czf $BACKUP_DIR/logs_$DATE.tar.gz /opt/gitea-runner/logs/ # Удаление старых резервных копий (старше 30 дней) find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete echo "Backup completed: $DATE" EOF chmod +x /opt/gitea-runner/backup.sh # Добавление в crontab (ежедневно в 2:00) echo "0 2 * * * /opt/gitea-runner/backup.sh" | crontab - ``` ## Работа с кластером ### Основные команды для работы с кластером ```bash # Установка переменной окружения для работы с кластером export KUBECONFIG=~/.kube/{configfile} # Проверка состояния кластера kubectl get nodes -o wide kubectl get pods -A # Работа с Gitea kubectl get pods -n gitea kubectl logs -n gitea deployment/gitea kubectl port-forward -n gitea svc/gitea-http 3000:3000 # Проверка storage kubectl get pvc -n gitea kubectl get storageclass # Проверка ingress и сервисов kubectl get svc -n default | grep traefik kubectl get crds kubectl get ingressroutes.traefik.io -A # Проверка сертификатов kubectl get certificates -A -o wide ``` ### Мониторинг и обслуживание ```bash # Проверка ресурсов кластера kubectl top nodes kubectl top pods -n gitea # Проверка событий kubectl get events -n gitea --sort-by='.lastTimestamp' # Резервное копирование Gitea kubectl exec -n gitea deployment/gitea -- gitea dump -c /data/gitea/conf/app.ini # Обновление Gitea helm repo update helm upgrade gitea gitea-charts/gitea -n gitea --values gitea-values.yaml ``` ### Устранение неполадок ```bash # Проверка логов Gitea kubectl logs -n gitea deployment/gitea -f # Проверка логов PostgreSQL kubectl logs -n gitea statefulset/gitea-postgres -c postgres # Проверка логов Valkey kubectl logs -n gitea statefulset/gitea-valkey-cluster -c valkey # Проверка сетевой связности kubectl exec -n gitea deployment/gitea -- ping gitea-postgres kubectl exec -n gitea deployment/gitea -- ping gitea-valkey-cluster ```