diff --git a/cert.yaml b/cert.yaml new file mode 100644 index 0000000..8b1b49c --- /dev/null +++ b/cert.yaml @@ -0,0 +1,15 @@ +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 diff --git a/docs/gitea-actions-guide.md b/docs/gitea-actions-guide.md new file mode 100644 index 0000000..47db366 --- /dev/null +++ b/docs/gitea-actions-guide.md @@ -0,0 +1,1497 @@ +# Настройка 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 + +## Содержание + +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-in-Docker контейнер + - Настраивает Docker Buildx для мультиплатформенной сборки + - Авторизуется в Docker Hub + - Собирает образы для Linux AMD64 и ARM64 + - Публикует образы с тегом версии и `latest` + +2. **create-release:** + - Запускается после успешной сборки Docker образов + - Использует Go контейнер для сборки бинарников + - Собирает бинарники для всех платформ (Linux, Windows, macOS) + - Создает архивы с бинарниками + - Создает релиз через Gitea API + - Загружает бинарники как assets релиза + +## Настройка секретов + +### Необходимые секреты + +В настройках репозитория → "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. Скопируйте токен + +**Docker Hub Token:** +1. Войдите в Docker Hub +2. Перейдите в Account Settings → Security +3. Создайте новый Access Token +4. Скопируйте токен + +## Тестирование и запуск + +### Локальное тестирование + +1. **Проверьте синтаксис workflow:** + ```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.0.0 + git push origin v1.0.0 + ``` + +2. **Мониторинг выполнения:** + - Перейдите в репозиторий → "Actions" + - Найдите запущенный workflow + - Отслеживайте выполнение каждого job + +### Ожидаемый результат + +После успешного выполнения: + +1. **Docker образы** будут опубликованы в Docker Hub: + - `username/hello-api:v1.0.0` + - `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 + +### Оптимизация + +1. **Кэширование зависимостей:** + ```yaml + - name: Cache Go modules + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + ``` + +2. **Параллельная сборка:** + - Разделите сборку бинарников на отдельные jobs + - Используйте matrix strategy для разных платформ + +## Заключение + +В этой статье мы рассмотрели полный процесс настройки 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 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 доступен с внешнего адреса 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 < /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] + +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 - +``` + +Теперь у вас есть полностью настроенная инфраструктура с Gitea в K3s кластере и Gitea Runner в LXC контейнере на Proxmox ARM64! + +## Работа с вашим кластером + +### Основные команды для работы с кластером + +```bash +# Установка переменной окружения для работы с кластером +export KUBECONFIG=~/.kube/config_hlab + +# Проверка состояния кластера +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 ingress -A +``` + +### Мониторинг и обслуживание + +```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 +``` + +--- + +**Автор:** [Ваше имя] +**Дата:** [Дата публикации] +**Версия:** 1.0.0 \ No newline at end of file diff --git a/makefile b/makefile index f797d9b..1e1d1b7 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,4 @@ -.PHONY: build clean test +.PHONY: build clean test release BIN_DIR=bin APP_NAME=hello-api @@ -12,4 +12,14 @@ clean: rm -rf $(BIN_DIR) test: - go test -v ./... \ No newline at end of file + go test -v ./... + +# Задача для создания релиза +# Использование: make release VERSION=1.0.25 +release: + # @if [ -z "$(VERSION)" ]; then \ + # echo "Ошибка: Необходимо указать версию"; \ + # echo "Использование: make release VERSION=1.0.25"; \ + # exit 1; \ + # fi + @./scripts/release-interactive.sh $(VERSION) \ No newline at end of file diff --git a/scripts/release-interactive.sh b/scripts/release-interactive.sh new file mode 100755 index 0000000..ca82cf9 --- /dev/null +++ b/scripts/release-interactive.sh @@ -0,0 +1,114 @@ +#!/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 " - Изменения отправлены в удаленный репозиторий" \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..acdd560 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Скрипт для автоматизации релиза +# Использование: ./scripts/release.sh +# Пример: ./scripts/release.sh 1.0.25 + +set -e # Остановить выполнение при ошибке + +# Проверка аргументов +if [ $# -eq 0 ]; then + echo "Ошибка: Необходимо указать версию" + echo "Использование: $0 " + echo "Пример: $0 1.0.25" + exit 1 +fi + +VERSION=$1 + +# Проверка формата версии (простая проверка) +if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Ошибка: Неверный формат версии. Используйте формат X.Y.Z (например, 1.0.25)" + exit 1 +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 " - Изменения отправлены в удаленный репозиторий" \ No newline at end of file