通过Gitea部署自己的DevOps平台

当讨论自托管DevOps时,脑海里面出现的可能是GitLab+Jenkins+Nexus的组合,但是搭建一套这样的DevOps平台对基础设施的要求是非常高的。Gitea基于Go语言实现,在包含代码托管、CI/CD、项目管理、制品库等功能的同时做到非常轻量,2C1G的机器就能满足小型团队的需求,是中小规模实现DevOps的不错选择。
更详细的部署教程请访问官方文档
- 树莓派Pi3功能强大,足以运行 Gitea 来处理小型工作负载。
- 对于小型团队/项目而言,2 个 CPU 内核和 1GB 内存通常就足够了。
- 在 UNIX 系统上,Gitea 应使用专用的非 root 系统账户运行。
- 注意:Gitea 管理
~/.ssh/authorized_keys文件。以普通用户身份运行 Gitea 可能会破坏该用户的登录能力。
- 注意: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。
-
执行
git --version检查系统是否安装git,未安装时通过apt install git安装 -
从下载页面下载对应平台的最新版可执行程序,将可执行文件移动到
/usr/local/bin并赋予执行权限
wget -O gitea https://dl.gitea.com/gitea/x.x.x/gitea-x.x.x-linux-amd64mv gitea /usr/local/bin/giteachmod +x /usr/local/bin/gitea- 创建组和用户
# 创建组groupadd gitea# 创建用户并加入到组adduser \ --system \ --shell /bin/bash \ --gecos 'Git Version Control' \ --ingroup gitea \ --disabled-password \ --home /home/git \ git- 初始化工作目录和配置目录
mkdir -p /var/lib/gitea/{custom,data,log}chown -R git:gitea /var/lib/gitea/chmod -R 750 /var/lib/gitea/mkdir /etc/giteachown git:gitea /etc/giteachmod 770 /etc/gitea- 编辑
/etc/systemd/system/gitea.service文件内容如下:
[Unit]Description=Gitea (Git with a cup of tea)After=network.target
[Service]RestartSec=2sType=simpleUser=gitGroup=giteaWorkingDirectory=/var/lib/gitea/ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.iniRestart=alwaysEnvironment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]WantedBy=multi-user.target- 启动gitea服务
systemctl enable giteasystemctl start gitea# 查看是否启动成功systemctl status gitea- 访问
http://<you-host>:3000完成配置
- 数据库按照实际情况选择,填写正确的连接信息
- 站点名称填写自己公司/团队名即可
- 服务器域名填写类似
gitea.example.com或保留默认 - 基础URL填写类似
https://gitea.example.com或保留默认 - 可选设置中主要设置管理员账号密码,邮箱和第三方服务设置按照实际需求设置
-
完成后点击
立即安装,等待安装完成并刷新。如果上一步未设置管理员账号,现在注册成为管理员 -
现在可以正常创建团队或仓库并推送代码了
Actions
Section titled “Actions”Gitea Actions是官方内置的CI/CD解决方案,需要将Job委托给单独部署的Gitea Runner
由于Gitea Actions将任务运行在Docker中,所以请保证系统已经安装Docker
- 从下载页面下载对应平台的最新版二进制文件,将可执行文件移动到
/usr/local/bin并赋予执行权限
wget -O runner https://dl.gitea.com/gitea-runner/x.x.x/gitea-runner-x.x.x-linux-amd64mv runner /usr/local/bin/runnerchmod +x /usr/local/bin/runner- 创建用户并添加到组
adduser \ --system \ --shell /bin/bash \ --gecos 'Gitea Actions Runner' \ --ingroup gitea \ --disabled-password \ --home /home/runner \ runnerusermod -aG docker runner- 创建工作目录和配置文件
mkdir /etc/runnermkdir /var/lib/runnerchown -R runner:gitea /var/lib/runnerchown -R runner:gitea /etc/runnerrunner config generate > /etc/runner/config.yaml- 按需调整配置文件内容,示例如下:
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"- 将runner注册到gitea实例
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 的新文件件存储了注册信息- 编辑
/etc/systemd/system/runner.service文件内容如下:
[Unit]Description=Gitea Actions runnerDocumentation=https://gitea.com/gitea/runnerAfter=docker.service
[Service]ExecStart=/usr/local/bin/runner daemon --config /etc/runner/config.yamlExecReload=/bin/kill -s HUP $MAINPIDWorkingDirectory=/var/lib/runnerTimeoutSec=0RestartSec=10Restart=alwaysUser=runnerGroup=gitea
[Install]WantedBy=multi-user.target- 启动runner
systemctl enable runnersystemctl start runner# 检查runner运行状态systemctl status runnerGitea流水线通过yaml文件定义,存放在项目根目录下的.gitea/workflows文件夹下。流水线语法请参考Github Actions语法
当存在多个流水线时,Gitea Actions按照文件在.gitea/workflows文件夹下的顺序执行,如果想要编排不同流水线,可通过文件名前缀实现,例如:
001-server-nest.yaml002-server-gateway.yaml003-server-calendar.yaml流水线应该只在影响部署结果的内容发生变化时才重新运行,触发条件可以在yaml配置中通过如下示例设置:
on: push: # 只管列出的分支 branches: - master # 只管列出路径匹配的文件内容是否变动 paths: # 流水线本身配置变化 - .gitea/workflows/demo.yaml # 代码内容变化 - apps/demo/**/*.ts # monorepo中可能存在代码没变,但是内部依赖更新导致需要重新部署的情况 - apps/demo/package.jsonactions/checkout
Section titled “actions/checkout”用于拉取代码,最基础的插件。使用方式如下:
- name: Checkout Code uses: actions/checkout@v7pnpm/action-setup
Section titled “pnpm/action-setup”设置pnpm环境,详情参考官方文档。使用方式如下:
- name: Install Pnpm uses: pnpm/action-setup@v6# 一般设置缓存- name: Setup Pnpm Store run: pnpm config set store-dir /workspace/pnpmactions/setup-node
Section titled “actions/setup-node”设置node环境,详情参考官方文档。使用方式如下:
- name: Setup Node uses: actions/setup-node@v7 with: # 使用项目根目录的node-version安装特定版本node node-version-file: .node-version # 使用pnpm缓存 cache: 'pnpm'docker/login-action
Section titled “docker/login-action”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 }}appleboy/ssh-action
Section titled “appleboy/ssh-action”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 showeasingthemes/ssh-deploy
Section titled “easingthemes/ssh-deploy”基于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' # 增量同步并清理旧文件部署NestJS应用
Section titled “部署NestJS应用”name: server-gatewayrun-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 }}部署前端网页
Section titled “部署前端网页”name: web-wwwrun-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' # 增量同步并清理旧文件