跳转到内容

通过Gitea部署自己的DevOps平台

gitea-setup-poster

当讨论自托管DevOps时,脑海里面出现的可能是GitLab+Jenkins+Nexus的组合,但是搭建一套这样的DevOps平台对基础设施的要求是非常高的。Gitea基于Go语言实现,在包含代码托管、CI/CD、项目管理、制品库等功能的同时做到非常轻量,2C1G的机器就能满足小型团队的需求,是中小规模实现DevOps的不错选择。

更详细的部署教程请访问官方文档

  • 树莓派Pi3功能强大,足以运行 Gitea 来处理小型工作负载。
  • 对于小型团队/项目而言,2 个 CPU 内核和 1GB 内存通常就足够了。
  • 在 UNIX 系统上,Gitea 应使用专用的非 root 系统账户运行。
    • 注意:Gitea 管理 ~/.ssh/authorized_keys 文件。以普通用户身份运行 Gitea 可能会破坏该用户的登录能力。
  • Git 需要 2.0.0 或更高版本。
    • Git LFS 如果启用,且 Git 版本大于等于 2.1.2,则该选项可用
    • 如果 Git 版本大于等于 2.18,将自动启用 Git 提交历史图形化展示功能

Gitea 支持 PostgreSQL(>= 12)、MySQL(>= 8.0)、MariaDB(>= 10.4)、SQLite(内置) 和 MSSQL(>= 2012 SP4)这几种数据库。推荐使用PostgreSQL。

  1. 执行git --version检查系统是否安装git,未安装时通过apt install git安装

  2. 下载页面下载对应平台的最新版可执行程序,将可执行文件移动到/usr/local/bin并赋予执行权限

Terminal window
wget -O gitea https://dl.gitea.com/gitea/x.x.x/gitea-x.x.x-linux-amd64
mv gitea /usr/local/bin/gitea
chmod +x /usr/local/bin/gitea
  1. 创建组和用户
Terminal window
# 创建组
groupadd gitea
# 创建用户并加入到组
adduser \
--system \
--shell /bin/bash \
--gecos 'Git Version Control' \
--ingroup gitea \
--disabled-password \
--home /home/git \
git
  1. 初始化工作目录和配置目录
Terminal window
mkdir -p /var/lib/gitea/{custom,data,log}
chown -R git:gitea /var/lib/gitea/
chmod -R 750 /var/lib/gitea/
mkdir /etc/gitea
chown git:gitea /etc/gitea
chmod 770 /etc/gitea
  1. 编辑/etc/systemd/system/gitea.service文件内容如下:
[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target
[Service]
RestartSec=2s
Type=simple
User=git
Group=gitea
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
  1. 启动gitea服务
Terminal window
systemctl enable gitea
systemctl start gitea
# 查看是否启动成功
systemctl status gitea
  1. 访问http://<you-host>:3000完成配置
  • 数据库按照实际情况选择,填写正确的连接信息
  • 站点名称填写自己公司/团队名即可
  • 服务器域名填写类似gitea.example.com或保留默认
  • 基础URL填写类似https://gitea.example.com或保留默认
  • 可选设置中主要设置管理员账号密码,邮箱和第三方服务设置按照实际需求设置
  1. 完成后点击立即安装,等待安装完成并刷新。如果上一步未设置管理员账号,现在注册成为管理员

  2. 现在可以正常创建团队或仓库并推送代码了

Gitea Actions是官方内置的CI/CD解决方案,需要将Job委托给单独部署的Gitea Runner

由于Gitea Actions将任务运行在Docker中,所以请保证系统已经安装Docker

  1. 下载页面下载对应平台的最新版二进制文件,将可执行文件移动到/usr/local/bin并赋予执行权限
Terminal window
wget -O runner https://dl.gitea.com/gitea-runner/x.x.x/gitea-runner-x.x.x-linux-amd64
mv runner /usr/local/bin/runner
chmod +x /usr/local/bin/runner
  1. 创建用户并添加到组
Terminal window
adduser \
--system \
--shell /bin/bash \
--gecos 'Gitea Actions Runner' \
--ingroup gitea \
--disabled-password \
--home /home/runner \
runner
usermod -aG docker runner
  1. 创建工作目录和配置文件
Terminal window
mkdir /etc/runner
mkdir /var/lib/runner
chown -R runner:gitea /var/lib/runner
chown -R runner:gitea /etc/runner
runner config generate > /etc/runner/config.yaml
  1. 按需调整配置文件内容,示例如下:
log:
level: info
runner:
file: /etc/runner/.runner
capacity: 1
env_file: /etc/runner/.env
timeout: 3h
shutdown_timeout: 0s
insecure: true
fetch_timeout: 5s
fetch_interval: 2s
fetch_interval_max: 5s
workdir_cleanup_age: 24h
idle_cleanup_interval: 10m
log_report_interval: 5s
log_report_max_latency: 3s
log_report_batch_size: 100
state_report_interval: 5s
report_close_timeout: 10s
github_mirror: ''
action_shallow_clone: true
set_act_env: true
labels:
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
allocate_pty: true
post_task_script: ''
post_task_script_timeout: 5m
cache:
enabled: true
dir: "/var/lib/runner/cache"
host: "192.168.100.40"
port: 4000
external_server: ""
external_secret: ""
offline_mode: false
container:
network: ""
network_create_options:
enable_ipv4: true
enable_ipv6: false
privileged: true
options:
workdir_parent:
valid_volumes:
- '**'
docker_host: ""
force_pull: false
force_rebuild: false
require_docker: false
docker_timeout: 0s
bind_workdir: true
host:
workdir_parent: /var/lib/runner/workspace
metrics:
enabled: false
addr: "127.0.0.1:9101"
  1. 将runner注册到gitea实例
Terminal window
cd /etc/runner
# instance是Gitea实例的地址,如http://192.168.8.8:3000或https://gitea.example.com
# token 用于身份验证和标识,可以设置不同层级的runner,一般团队只用设置实例级别
# 实例级别:管理员设置页面,例如 <your_gitea.com>/admin/actions/runners
# 组织级别:组织设置页面,例如 <your_gitea.com>/<org>/settings/actions/runners
# 存储库级别:存储库设置页面,例如 <your_gitea.com>/<owner>/<repo>/settings/actions/runners
# name是runner名称
runner register \
--no-interactive \
--config /etc/runner/config.yaml \
--instance https://gitea.example.com \
--token <token> \
--name <name>
# 注册后,当前目录中将出现一个名为 .runner 的新文件件存储了注册信息
  1. 编辑/etc/systemd/system/runner.service文件内容如下:
[Unit]
Description=Gitea Actions runner
Documentation=https://gitea.com/gitea/runner
After=docker.service
[Service]
ExecStart=/usr/local/bin/runner daemon --config /etc/runner/config.yaml
ExecReload=/bin/kill -s HUP $MAINPID
WorkingDirectory=/var/lib/runner
TimeoutSec=0
RestartSec=10
Restart=always
User=runner
Group=gitea
[Install]
WantedBy=multi-user.target
  1. 启动runner
Terminal window
systemctl enable runner
systemctl start runner
# 检查runner运行状态
systemctl status runner

Gitea流水线通过yaml文件定义,存放在项目根目录下的.gitea/workflows文件夹下。流水线语法请参考Github Actions语法

当存在多个流水线时,Gitea Actions按照文件在.gitea/workflows文件夹下的顺序执行,如果想要编排不同流水线,可通过文件名前缀实现,例如:

001-server-nest.yaml
002-server-gateway.yaml
003-server-calendar.yaml

流水线应该只在影响部署结果的内容发生变化时才重新运行,触发条件可以在yaml配置中通过如下示例设置:

on:
push:
# 只管列出的分支
branches:
- master
# 只管列出路径匹配的文件内容是否变动
paths:
# 流水线本身配置变化
- .gitea/workflows/demo.yaml
# 代码内容变化
- apps/demo/**/*.ts
# monorepo中可能存在代码没变,但是内部依赖更新导致需要重新部署的情况
- apps/demo/package.json

用于拉取代码,最基础的插件。使用方式如下:

- name: Checkout Code
uses: actions/checkout@v7

设置pnpm环境,详情参考官方文档。使用方式如下:

- name: Install Pnpm
uses: pnpm/action-setup@v6
# 一般设置缓存
- name: Setup Pnpm Store
run: pnpm config set store-dir /workspace/pnpm

设置node环境,详情参考官方文档。使用方式如下:

- name: Setup Node
uses: actions/setup-node@v7
with:
# 使用项目根目录的node-version安装特定版本node
node-version-file: .node-version
# 使用pnpm缓存
cache: 'pnpm'

Docker仓库登录插件,登录后支持推送、拉取镜像等操作,详情参考官方文档。使用方式如下:

# 基于 git commit hash 设置镜像tag
- name: Setup Image Tag
env:
FULL_SHA: ${{ gitea.sha }}
DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}
WORKFLOW_NAME: ${{ gitea.workflow }}
run: |
IMAGE_NAME="$DOCKER_REGISTRY/ergate/$WORKFLOW_NAME:${FULL_SHA:0:7}"
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITEA_ENV
echo "$IMAGE_NAME"
# 登录Docker仓库
- name: Login Docker
uses: docker/login-action@v4
with:
# 支持自定义DOCKER_REGISTRY
registry: ${{ vars.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
# 推送镜像并删除本地镜像
- name: Push Image
run: |
docker push ${{ env.IMAGE_NAME }}
docker rmi ${{ env.IMAGE_NAME }}

SSH到远程主机执行命令,一般用于部署,详情参考官方文档。使用方式如下:

- name: Deploy Remote
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
script: |
cat /etc/os-release
ip addr show

基于rsync实现文件同步,适合部署前端项目打包结果到远程机器,详情参考官方文档。使用方式如下:

- name: Transfer Remote
uses: easingthemes/ssh-deploy@v6
with:
REMOTE_HOST: ${{ secrets.SSH_HOST }}
REMOTE_USER: ${{ secrets.SSH_USERNAME }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_KEY }}
TARGET: '/var/www/demo' # 远程服务器的目标部署目录
SOURCE: 'apps/demo/dist'
ARGS: '-avzr --delete' # 增量同步并清理旧文件
name: server-gateway
run-name: Deploy GATEWAY service
on:
push:
branches:
- master
paths:
- .gitea/workflows/*-server-gateway.yaml
- apps/server-gateway/**/*.ts
- apps/server-gateway/package.json
- Dockerfile
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v7
- name: Setup Image Tag
env:
FULL_SHA: ${{ gitea.sha }}
DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }}
WORKFLOW_NAME: ${{ gitea.workflow }}
run: |
IMAGE_NAME="$DOCKER_REGISTRY/$WORKFLOW_NAME:${FULL_SHA:0:7}"
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITEA_ENV
echo "$IMAGE_NAME"
- name: Install Pnpm
uses: pnpm/action-setup@v6
- name: Setup Pnpm Store
run: pnpm config set store-dir /workspace/pnpm
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version-file: .node-version
cache: 'pnpm'
- name: Prune Service
run: |
pnpm add turbo --global
turbo prune @ergate/server-gateway --docker
- name: Build Image
run: |
rm -rf deploy && cp -r out/json deploy
cd deploy
pnpm install --frozen-lockfile --prefer-offline
cp -r ../out/full/. ./
turbo build --filter="./libs/*" --filter="./packages/*"
pnpm install --frozen-lockfile --prefer-offline
turbo build
pnpm --filter=@ergate/server-gateway deploy --prod app
docker build -f ./Dockerfile -t ${{ env.IMAGE_NAME }} ./app
- name: Login Docker
uses: docker/login-action@v4
with:
registry: ${{ vars.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Push Image
run: |
docker push ${{ env.IMAGE_NAME }}
docker rmi ${{ env.IMAGE_NAME }}
- name: Deploy Remote
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.GATEWAY_HOST }}
username: ${{ secrets.GATEWAY_USERNAME }}
password: ${{ secrets.GATEWAY_PASSWORD }}
script: |
# 删除原有容器
docker rm -f gateway || true
# 启动新容器
docker run -d \
--name gateway \
--network host \
-v /app/platform/gateway/config.yaml:/app/config.yaml \
-v /app/platform/data:/app/data \
${{ env.IMAGE_NAME }}
name: web-www
run-name: Deploy WWW Website
on:
push:
branches:
- master
paths:
- .gitea/workflows/web-www.yaml
- apps/web-www/**/*.{ts,css,astro,md,mdx}
- apps/web-www/package.json
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v7
- name: Install Pnpm
uses: pnpm/action-setup@v6
- name: Setup Pnpm Store
run: pnpm config set store-dir /workspace/pnpm
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version-file: .node-version
cache: 'pnpm'
- name: Prune Website
run: |
pnpm add turbo --global
turbo prune @ergate/web-www
- name: Build
run: |
cd out
pnpm install --frozen-lockfile --prefer-offline
turbo build --filter=@ergate/web-www
- name: Transfer Remote
uses: easingthemes/ssh-deploy@v6
with:
REMOTE_HOST: ${{ secrets.SSL_HOST }}
REMOTE_USER: ${{ secrets.SSL_USERNAME }}
SSH_PRIVATE_KEY: ${{ secrets.SSL_KEY }}
TARGET: '/var/www/web-www' # 远程服务器的目标部署目录
SOURCE: 'out/apps/web-www/dist/'
ARGS: '-avzr --delete' # 增量同步并清理旧文件