# Настройка Gitea Actions для Go проекта: Полное руководство

---
**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. [Заключение](#заключение)
9. [Инфраструктура](#инфраструктура)
10. [Установка Gitea в кластере K3s](#установка-gitea-в-кластере-k3s)
11. [Настройка Gitea Runner в LXC контейнере](#настройка-gitea-runner-в-lxc-контейнере)

## Введение

Gitea Actions — это встроенная система непрерывной интеграции и развертывания (CI/CD) в Gitea, которая позволяет автоматизировать процессы сборки, тестирования и развертывания ваших проектов. Большой плюс этой системы в том, что она достаточна не требовательна к ресурсам и может быть развернута в собственном изолированном окружении. 

В этой статье мы рассмотрим пример работы с данной системой, на примере того как настроить Gitea Actions для Go проекта с автоматической сборкой мультиплатформенных бинарников, созданием Docker образов и публикацией релизов.

### Итак, что мы будем делать

- Настроим автоматическую сборку Go приложения для разных платформ
- Создадим Docker образы для Linux AMD64 и ARM64
- Настроим публикацию в Docker Hub
- Автоматизируем создание релизов с бинарниками
- Настроим триггеры на основе Git тегов

## Подготовка проекта

### Структура проекта

Собственно функциональная часть проекта Go не блещет оригинальностью и имеет следующую простую структуру:

```
hello_gitea/
├── main.go              # Основной код приложения
├── go.mod               # Зависимости Go
├── go.sum               # Хеши зависимостей
├── Dockerfile           # Docker образ
├── .gitea/              # Конфигурация Gitea Actions
   └── workflows/
       └── build.yaml   # Workflow для сборки
├── 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("/health", 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-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 "========================"
          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

          # Setup Docker Buildx for multi-platform builds
          docker buildx create --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
          # 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 \
            .

  create-release:
    runs-on: ubuntu-latest
    container:
      image: golang:1.21
    needs: create-docker-image
    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
          mkdir -p bin

          # Build for all platforms
          GOOS=linux GOARCH=amd64 go build -o bin/hello-api-linux-amd64 main.go
          GOOS=linux GOARCH=arm64 go build -o bin/hello-api-linux-arm64 main.go
          GOOS=windows GOARCH=amd64 go build -o bin/hello-api-windows-amd64.exe main.go
          GOOS=darwin GOARCH=amd64 go build -o bin/hello-api-darwin-amd64 main.go
          GOOS=darwin GOARCH=arm64 go build -o bin/hello-api-darwin-arm64 main.go

          # Create archives
          cd bin
          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

          ls -la

      - name: Create Release
        run: |
          cd hello_gitea
          # Create release using Gitea API
          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"

          # Upload assets
          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
          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
```

### Разберемся как работает workflow

**Триггеры:**
- Workflow запускается при создании тега, начинающегося с `v*` (например, `v1.0.0`)

**Jobs:**

1. **create-docker-image:** - создание образов docker с нашим проектом
   - Используем 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)

чтобы авторизация сработала на докерхабе надо внести секреты DOCKERHUB_TOKEN, DOCKERHUB_USERNAME или вцелом для всей gitea в настройках или в настройках конкретного репозитория
я использовал докерхаб, но можно заморочиться и настроить работу с приватным репозиторием ...

2. **create-release:**
   - Запускается после успешной сборки Docker образов
   - Использует Go контейнер для сборки бинарников (image: golang:1.21)
   - Собирает бинарники для всех платформ (Linux, Windows, macOS)
   - Создает архивы с бинарниками
   - Создает релиз через Gitea API
   - Загружает бинарники как assets релиза

при каждом запуске, как я понимаю, данного job будет скачиваться и устанавливаться в контейнере, созданном на базе golang:1.21

```yaml
      - name: Setup Go
        run: |
          # Install jq for JSON parsing
          apt-get update && apt-get install -y jq
          git --version
          go version
          jq --version
```

решение как говорится в лоб: чтобы убрать эту работу при каждом выполнении workflow надо сделать свой образ

``` Dockerfile
FROM golang:1.24

# Install some packages
RUN apt-get update && apt-get install -y git ca-certificates jq
```

собрать его и запушить (желательно с мультиплатформенностью). конечно если вам не надо билдить образы для разных платформ тогда все попроще

```bash
#!/bin/bash

docker buildx build \
    --platform linux/amd64,linux/arm64 \
    --tag ${DOCKERHUB_USERNAME}/my-build-golang-runner:latest \
    --push \
    -f Dockerfile_for_runner_image \
    .
```


## Настройка секретов

### Необходимые секреты

В настройках репозитория (или инстанса) → "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/health
   ```

### Запуск Actions

1. **Создаем тег:**
   ```bash
   
    git tag v1.1.20
    git push origin v1.1.20

    # или скрипт


    #!/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 -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 -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"

    # Выполняем 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 "   - Создан коммит с сообщением 'Release v$VERSION'"
    echo "   - Создан тег v$VERSION"
    echo "   - Изменения отправлены в удаленный репозиторий" 

   ```

2. **Мониторинг выполнения:**
   - Переходим в репозиторий → "Actions"
   - Находим запущенный workflow
   - Отслеживаем выполнение каждого job

### Ожидаемый результат

После успешного выполнения:

1. **Docker образы** будут опубликованы в Docker Hub:
   - `username/hello-api:v1.1.20`
   - `username/hello-api:latest`

2. **Релиз** будет создан в 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`

## Мониторинг и отладка

### Просмотр логов

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 предоставляет мощные возможности для автоматизации процессов разработки, и с правильной настройкой вы можете значительно упростить процесс доставки вашего ПО.

---

## Инфраструктура

### Архитектура системы

Этот и последующие разделы описывают инфраструктуру на которой я проверял все описанное выше.
Если у вас используются другие подходы, то можете пропустить их или ознакомиться для общего развития.

Итак инфраструктура состоит из следующих компонентов:

```
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 архитектура
- Минимум 8GB RAM
- 100GB свободного места
- Поддержка LXC контейнеров

**K3s Cluster:**
- Kubernetes 1.24+
- Helm 3.8+
- Ingress Controller (Traefik)
- Persistent Storage (Longhorn)

**LXC Container:**
- Ubuntu 22.04 LTS
- 4GB RAM
- 20GB дискового пространства
- Docker Engine
- Go 1.21+

## Установка 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            <none>        3000/TCP             543d
gitea-postgres                  ClusterIP   10.xx.xx.xx   <none>        5432/TCP             543d
gitea-ssh                       ClusterIP   None            <none>        22/TCP               543d
gitea-valkey-cluster            ClusterIP   10.xx.xx.xx     <none>        6379/TCP             30h
gitea-valkey-cluster-headless   ClusterIP   None            <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 доступен с внешнего адреса https://direct-dev.ru/gitea

в /data/gitea/conf/app.ini

[server]
ROOT_URL = https://direct-dev.ru/gitea
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
```

``` 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" (например, https://gitea.com)

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 записи:**

```bash
# Добавить A-запись в DNS (у меня сloudflare)
gitea.your-domain.com -> внешний (белый) Ip - за ним пробросы портов если нужно ... чтобы в итоге запрос поступил на traefik
```

2. **Создание ClusterIssuer (если не существует):**

```bash
# Создание ClusterIssuer для Let's Encrypt - лучше через dns resolver
cat <<EOF | KUBECONFIG=~/.kube/{configfile} kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  annotations:
  name: letsencrypt-dns-cloudflare
spec:
  acme:
    email: info@somedomain.ru
    privateKeySecretRef:
      name: le-issuer-acct-key
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            key: api-token
            name: cloudflare-api-token-secret
          email: info@somedomain.ru
      selector:
        dnsZones:
        - somedomain.ru
        - '*.somedomain.ru'
EOF
```

### Доступ к Gitea

1. **Временный доступ через port-forward:**

```bash
# Доступ к Gitea через port-forward
kubectl port-forward -n gitea svc/gitea-http 3000:3000

# В браузере http://localhost:3000/gitea
```

2. **Постоянный доступ через ingressroute:**

``` bash
# Проверка сертификата
kubectl get certificate -n gitea
```

3. **Проверка доступа:**

```bash
# Проверка через curl
curl -I https://gitea.your-domain.com

# Проверка SSL сертификата
openssl s_client -connect gitea.your-domain.com:443 -servername gitea.your-domain.com
```

если нет сертификата

```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  annotations:
  name: root-somedomain-ru
  namespace: default
spec:
  commonName: somedomain.ru
  dnsNames:
  - somedomain.ru
  - www.somedomain.ru
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-dns-cloudflare
  secretName: le-root-somedomain-ru
```

## Настройка Gitea Runner в LXC контейнере

### Создание LXC контейнера в Proxmox

Учитывая мою ARM64 архитектуру (Rockchip RK3588), контейнер надо создать с правильным шаблоном:

1. **Создание контейнера через веб-интерфейс:**

```bash

# Или через веб-интерфейс Proxmox:
# 1. Перейдите в Datacenter → local → CT Templates
# 2. Скачайте ubuntu-22.04-standard_22.04-1_arm64.tar.zst
# 3. Создайте контейнер с параметрами:
#    - Template: ubuntu-22.04-standard_22.04-1_arm64
#    - Memory: 4096 MB
#    - Cores: 2
#    - Root disk: 20 GB
#    - Network: DHCP
```

2. **Настройка сети:**

```bash
# В контейнере
ip addr show

# в силу особенностей развертывания gitea раннер будет получать к нему доступ через интернет - в моем случае контейнер надо выпустить в интернет

```

### Подготовка контейнера

1. **Обновление системы:**

```bash
# В контейнере
apt update && apt upgrade -y
apt install -y curl wget git vim htop
```

2. **Установка Docker:**

```bash
# Удаление старых версий
apt remove -y docker docker-engine docker.io containerd runc

# Установка зависимостей
apt install -y \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg \
  lsb-release

# Добавление GPG ключа Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Добавление репозитория Docker
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /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 <<EOF
# just run `./act_runner generate-config > 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 <<EOF
[Unit]
Description=Gitea Actions Runner
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
User=gitea-runner
Group=gitea-runner
WorkingDirectory=/opt/gitea-runner
ExecStart=/usr/local/bin/act_runner daemon --config /opt/gitea-runner/config.yaml
Restart=always
RestartSec=10
Environment=PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[Install]
WantedBy=multi-user.target
EOF

# Перезагрузка systemd
systemctl daemon-reload

# Включение и запуск сервиса
systemctl enable gitea-runner
systemctl start gitea-runner

# Проверка статуса
systemctl status gitea-runner
```

### Настройка мониторинга

1. ** Cкрипт мониторинга:**

```bash
cat > /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 <<EOF
{
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "default-ulimits": {
    "nofile": {
      "Hard": 64000,
      "Name": "nofile",
      "Soft": 64000
    }
  }
}
EOF

systemctl restart docker
```

2. **Настройка ограничений ресурсов:**

```bash
# Обновление systemd сервиса с ограничениями
cat > /etc/systemd/system/gitea-runner.service <<EOF
[Unit]
Description=Gitea Actions Runner
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
User=gitea-runner
Group=gitea-runner
WorkingDirectory=/opt/gitea-runner
ExecStart=/usr/local/bin/act_runner daemon --config /opt/gitea-runner/config.yaml
Restart=always
RestartSec=10
Environment=PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Ограничения ресурсов
MemoryMax=3G
CPUQuota=200%

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl restart gitea-runner
```

### Резервное копирование

1. **Создание скрипта резервного копирования:**

```bash
cat > /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
```