Files
hello_gitea/docs/gitea-actions-guide.md
Anton Kuznetcov bf643393ad
All checks were successful
Release Build / create-release (push) Successful in 6m30s
Release Build / create-docker-image (push) Successful in 3m41s
Release Build / update-to-release-branch (push) Successful in 8s
Release v1.0.30
2025-07-28 14:05:01 +06:00

1837 lines
65 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Настройка Gitea Actions для Go проекта: Полное руководство
```metadata
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
- Автоматизируем создание релизов с бинарниками
- Настроим триггеры на основе Git тегов
## Подготовка проекта
### Структура проекта
Собственно функциональная часть проекта Go не блещет оригинальностью и имеет следующую простую структуру:
``` text
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("/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:latest
needs: create-release-branch
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
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
# 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 \
.
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 "=== 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
```
### Разберемся как работает 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/*
# (Опционально) Можно добавить команду по умолчанию
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 --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 \
.
```
эта задача будет запущена на ранере при пуше тега с префиксом `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 <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
```
Как видите я организовал доступ через внешний 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" (например, <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 записи:**
```text
Добавить 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
```