使用 Docker 部署 Gitea 并使用 Nginx 反向代理配置 SSL

本文的目标是记录 Docker 部署 Gitea 的全过程,操作全程在云服务器上。默认拥有一台云服务器。并在云服务器上部署 Gitea 服务并将其通过 Nginx 反向代理到 gitea.guosx.net 域名下,同时配置 ssl。

部署完成后的架构图:

Craft whiteboard

解释:

  • 内部使用 pgsql 作为 gitea 数据库
  • Docker 容器之间通过 gitea 容器网络连接,只有 nginx 的 223/443 端口被对外暴露。
  • gitea http 端口为 3000,ssh 端口为 22,nginx 通过反向代理将代理到外部 443/223 端口(https,ssh)

前提

  1. 一台可连接外网的云服务器
  2. 一个已备案域名
  3. 在云服务器上安装好 Docker,见[craftdocs://open?blockId=FD663801-FD21-4653-8169-24D4016B127A&spaceId=a93bee32-f7c1-884f-45ac-5a402985bf96](./Docker 安装.md)

获取 ssl 证书文件

本文使用腾讯云的免费ssl 证书,并使用 guosx.net 的子域名 gitea.guosx.net 作为 gitea 服务的地址。

登录云服务商控制台 > 搜索 SSL 证书

Image

点击免费申领证书

Image

填写申请 SSL 证书的域名,本文为 gitea.guosx.net 和申请邮箱(接收通知)。点击提交证书申请,

注意选择自动 DNS 验证,并且开启自动删除验证。 否则需要手动往域名解析里添加一条解析,验证完后再手动删除。所以直接让云服务器自己弄好了

Image

提交生成完成域名认证后可以下载 SSL 证书文件,选择下载 Nginx 的证书文件。

Image

Image

部署 gitea 容器

参考: Installation with Docker (rootless) | Gitea Documentation

创建一个容器网络,后续启用 gitea actions 功能需要用到:

docker network create gitea

基础

在服务器任意目录下创建 web-app 作为工作目录,并创建 docker-compose.yml 文件:

mkdir web-app
cd web-app
vim docker-compose.yml

写入如下内容:

networks:
  # 上一步定义的容器网络名
  gitea:
    external: true
 
volumes:
  # 创建命名卷并挂载到容器中作为数据持久化存储
  gitea:
    driver: local
 
services:
  # gitea 容器定义
  gitea-server:
    image: gitea/gitea:1-rootless
    # 内部服务之间通过 gitea 作为主机名沟通
    hostname: gitea
    # gitea 镜像支持的环境变量配置
    environment:
      - DOMAIN=gitea.guosx.net
      - SSH_DOMAIN=gitea.guosx.net
      # git 仓库 ui 上显示的克隆地址端口号
      # 为啥填 223 ?因为最后会通过 nginx 将容器的 22 端口代理到外部 223,因此页面上需要显示 223
      - SSH_PORT=223
      # 容器实际使用的 SSH 端口号
      - SSH_LISTEN_PORT=22
      - HTTP_PORT=3000
      - GITEA__service__ROOT_URL=https://gitea.guosx.net/
    restart: always
    # 将容器加入到 gitea 网络
    networks:
      - gitea
    volumes:
      # 将容器中的数据挂载到 gitea 命名卷中
      - gitea:/var/lib/gitea
      # 让容器使用外部主机一样的时间
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro

数据库

默认 gitea 容器使用内置的 sqlite,我们改为使用pgsql。

同样是在docker-compose.yml 文件中添加如下内容:

...
volumes:
  ...
  # 创建一个名为 postgres 的命名卷
  postgres:
    driver: local
 
services:
  gitea-server:
    ...
    environment:
      ...
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=ginowfj9ioan52goa@is34sdfe5
    # 让 gitea-server 容器依赖于 db 容器
    #(就是启动 gitea-server 容器前会先自动启动 db 容器)
    depends_on:
      - db
 
  db:
    image: postgres:16.3-alpine
    # 注意,gitea 文档有说明必须将 db 用作数据库主机名。(容器主机名默认是容器名)
    hostname: db
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=ginowfj9ioan52goa@is34sdfe5
      - POSTGRES_DB=gitea
    # db 容器也加入到 gitea 网络中
    networks:
      - gitea
    volumes:
      # 将命名卷挂载到容器里,相当于将 pgsql 的数据持久化到命名卷中
      - postgres:/var/lib/postgresql/data

环境变量

我们在上述 gitea 容器中使用了很多环境变量,这些环境变量可以在启动 gitea 后在安装页面上通过 gui 来覆盖。 使用 Docker 安装 | Gitea Documentation

Gitea 容器常用环境变量

您可以通过环境变量配置 Gitea 的一些设置:

(默认值以粗体显示)

  • APP_NAME“Gitea: Git with a cup of tea”:应用程序名称,在页面标题中使用。
  • RUN_MODEprod:应用程序运行模式,会影响性能和调试。“dev”,“prod”或”test”。
  • DOMAINlocalhost:此服务器的域名,用于 Gitea UI 中显示的 http 克隆 URL。
  • SSH_DOMAINlocalhost:该服务器的域名,用于 Gitea UI 中显示的 ssh 克隆 URL。如果启用了安装页面,则 SSH 域服务器将采用以下形式的 DOMAIN 值(保存时将覆盖此设置)。
  • SSH_PORT22:克隆 URL 中显示的 SSH 端口。
  • SSH_LISTEN_PORT%(SSH_PORT)s:内置 SSH 服务器的端口。
  • DISABLE_SSHfalse:如果不可用,请禁用 SSH 功能。如果要禁用 SSH 功能,则在安装 Gitea 时应将 SSH 端口设置为 0
  • HTTP_PORT3000:HTTP 监听端口。
  • ROOT_URL"":覆盖自动生成的公共 URL。如果内部 URL 和外部 URL 不匹配(例如在 Docker 中),这很有用。
  • LFS_START_SERVERfalse:启用 git-lfs 支持。
  • DB_TYPEsqlite3:正在使用的数据库类型[mysql,postgres,mssql,sqlite3]。
  • DB_HOSTlocalhost:3306:数据库主机地址和端口。
  • DB_NAMEgitea:数据库名称。
  • DB_USERroot:数据库用户名。
  • DB_PASSWDempty :数据库用户密码。如果您在密码中使用特殊字符,请使用“您的密码”进行引用。
  • INSTALL_LOCKfalse:禁止访问安装页面。
  • SECRET_KEY"" :全局密钥。这应该更改。如果它具有一个值并且 INSTALL_LOCK 为空,则 INSTALL_LOCK 将自动设置为 true
  • DISABLE_REGISTRATIONfalse:禁用注册,之后只有管理员才能为用户创建帐户。
  • REQUIRE_SIGNIN_VIEWfalse:启用此选项可强制用户登录以查看任何页面。
  • USER_UID1000:在容器内运行 Gitea 的用户的 UID(Unix 用户 ID)。如果使用主机卷,则将其与 /data 卷的所有者的 UID 匹配(对于命名卷,则不需要这样做)。
  • USER_GID1000:在容器内运行 Gitea 的用户的 GID(Unix 组 ID)。如果使用主机卷,则将其与 /data 卷的所有者的 GID 匹配(对于命名卷,则不需要这样做)。

完整的可配置变量见:

配置说明 | Gitea Documentation

需要说明的是,gitea 配置与环境变量的转化,如下配置:

[server]
APP_DATA_PATH=AppWorkPath/data

转化为对应的环境变量为:

...
    environment:
      - GITEA__server__APP_DATA_PATH=APP_DATA_PATH

可以按照需要执行配置。

运行 gitea 容器

完成上述 docker-compose.yml 的配置后就可以启动容器了。

# 启动 gitea-server 容器,此时会顺便启动 db 容器,因为我们配置了 `depends_on`
docker-compose -f docker-compose.yml -p web-app up -d gitea-server

docker-compose ps 可查看启动成功的容器

运行 nginx 容器

我们需要自定义 nginx 配置文件来设置反向代理,因此可以自定义 Docker 镜像将 nginx 配置文件和对应域名的的 ssl 证书拷贝到镜像中,然后启动容器时会自动加载自定义的配置文件。

在当前项目下创建 nginx 配置目录,将云服务器厂商免费的 ssl 证书文件拷贝到目录下:

# 存放 `gitea.guosx.net` 域名的 ssl 证书文件
mkdir -p nginx/ssl
 
# 存放 nginx 配置文件
mkdir -p nginx/config/conf.d
mkdir -p nginx/config/stream.d
 
# 将从云服务器厂商下在的免费 ssl 证书文件上传到 nginx/ssl
# 只需要:xxx_bundle.pem 和 xxx.key 两个文件
# scp gitea.guosx.net_bundle.pem xxx@xxx:/xxx/nginx/ssl/
# scp gitea.guosx.net.key xxx@xxx:/xxx/nginx/ssl/

将 nginx 镜像默认的配置文件 nginx.conf 拷贝到 nginx/config/nginx.conf

docker run --entrypoint="" --rm -it nginx:1.23.2-alpine cat /etc/nginx/nginx.conf > nginx/config/nginx.conf
vim nginx/config/nginx.conf

在文件末尾增加如下内容:

...
 
stream {
    include /etc/nginx/stream.d/*.conf;
}

配置 ssh 代理,创建 nginx/config/stream.d/ssh.conf 配置文件写入如下内容:

用于配置将外部 223 端口 ssh 的反向代理到 gitea 容器的 22 端口

# gitea-ssh-proxy 可以随意命名,`server xxx` 填 gitea 容器主机名以及要代理的 22 端口
upstream gitea-ssh-proxy {
    server gitea:22;
}
 
server {
    # 监听外部主机的 223 端口,并代理到 gitea-ssh-proxy
    listen 223;
    proxy_pass gitea-ssh-proxy;
}

配置 https 代理,创建 nginx/config/conf.d/default.conf 配置文件写入如下内容:

用于:

  1. 将外部主机的 80 端口重定向到 443 端口(强制使用 https)
  2. 将外部主机的 443 端口代理到 gitea 容器的 3000 端口 参考 gitea 文档: 反向代理 | Gitea Documentation
server {
    # 监听的端口号
    listen 443 ssl;
 
    server_tokens off;
    keepalive_timeout 5;
    root /usr/share/nginx/html;
    index index.html index.htm;
 
    # 域名
    server_name gitea.guosx.net;
 
    # ssl 证书文件在容器的路径,到时启动容器时需要将文件拷贝到容器中的如下路径
    ssl_certificate /etc/nginx/ssl/gitea.guosx.net_bundle.pem;
    ssl_certificate_key /etc/nginx/ssl/gitea.guosx.net.key;
 
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_prefer_server_ciphers on;
 
    location / {
        client_max_body_size 512M;
 
        # 反向代理到 gitea 容器的 3000 端口
        proxy_pass http://gitea:3000;
 
        proxy_set_header Connection $http_connection;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
 
# 将主机 80 端口(http)重定向到 443(https)端口
server {
    listen 80;
    server_name gitea.guosx.net;
    return 301 https://$host$request_uri;
}

自定义 nginx 镜像将上述配置文件和 ssl 证书引入。创建 nginx/Dockerfile 文件,写入如下内容:

FROM nginx:1.23.2-alpine
 
COPY nginx/ssl /etc/nginx/ssl
COPY nginx/config/nginx.conf /etc/nginx/nginx.conf
COPY nginx/config/conf.d /etc/nginx/conf.d
COPY nginx/config/stream.d /etc/nginx/stream.d

编辑 docker-compose.yml 文件,添加 nginx 容器编排:

...
 
services:
  ...
  nginx:
    hostname: nginx
    # 指定容器的镜像构建配置
    build:
      # 使用当前路径作为自定义 nginx 镜像的上下文(镜像构建时可以访问上下文的所有内容)
      context: .
      dockerfile: nginx/Dockerfile
    ports:
      # 将容器中的 443/223 暴露到外部主机
      - "443:443"
      - "223:223"
      - "80:80"
    restart: unless-stopped
    # 加入 gitea 容器网络
    networks:
      - gitea
    depends_on:
      - gitea-server

运行 nginx 容器:

docker-compose up -d nginx

初始化 Gitea

此时 nginx 容器已经启动,可以通过浏览器直接访问域名gitea.guosx.net ,此时会进入 gitea 的安装页面。我们可以进行基本的配置(可以覆盖通过环境变量设置的配置)。

云服务器默认不允许外部访问任何端口,因此需要到云服务器防火墙配置页面开启 443 和 223 端口。

Image

浏览器访问 gitea.guosx.net ,会进入 gitea 初始化配置,设置完成后点击立即安装(可以在这里配置一个管理员账号)

Image

如果配置了管理员账号,完成后就会进入用户页面。

Image

启用 Gitea Actions 功能

Gitea Actions 是 Gitea 内置的CI/CD解决方案,基本与 Github 的 Actions 兼容,具体见 gitea 文档: Gitea actions 功能默认启用,但是需要 runner 容器来执行。因此在上述基础上创建 runner 容器。 Act Runner | Gitea Documentation

获取 Runner 注册密钥

可以在不同级别上注册Runner,它可以是:

  • 实例级别:Runner将为实例中的所有存储库运行Job。
  • 组织级别:Runner将为组织中的所有存储库运行Job。
  • 存储库级别:Runner将为其所属的存储库运行Job。

可以直接执行 docker 命令获取注册密钥

docker exec -it web-app_gitea-server_1 gitea actions generate-runner-token

也可以浏览器访问 gitea 页面 > 管理后台 > Actions > Runners 中拷贝

Image

修改默认的 Runner 配置文件

与 nginx 一样,我们需要修改 Runner 容器的默认配置,因此需要先获取 Runner 的默认配置文件,然后修改后重新引入容器中

生成 runner 默认配置文件:

mkdir gitea
docker run --entrypoint="" --rm -it gitea/act_runner:0.2.9 act_runner generate-config > gitea/config.yaml

修改配置文件 gitea/config.yaml ,指定容器网络 gitea

Act Runner | Gitea Documentation

...
container:
  network: "gitea"
...

向 Gitea 容器注册 Runner

创建 gitea/Dockerfile 文件 ,写入如下内容:

FROM gitea/act_runner:0.2.9
COPY gitea/config.yaml /config.yaml

编辑 docker-compose.yml 文件,添加如下内容:

...
 
volumes:
  ...
  runner:
    driver: local
 
services:
  ...
  runner:
    build:
      context: .
      dockerfile: gitea/Dockerfile
    environment:
      CONFIG_FILE: /config.yaml
      GITEA_INSTANCE_URL: "http://gitea:3000"
      GITEA_RUNNER_REGISTRATION_TOKEN: "<填写上一步获取到的 Runner 注册密钥>"
    volumes:
      - runner:/data
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - gitea
    depends_on:
      - gitea-server

运行 Runner 容器

docker-compose up -d runner

启动 Runner 容器后会自动向 gitea 容器注册,注册成功后就可以在 gitea 管理后台页面下看到对应的 runner。

Image

Gitea 创建仓库执行 Actions demo 工程

上述步骤完成后,gitea 就算是完成部署了,下面演示一下 gitea 的基本使用

将自己电脑的 ssh 公钥添加到 gitea

执行这一步后就可以通过 ssh 操作 gitea 仓库了

在本地电脑上生成 ssh 密钥:

ssh-keygen -t rsa

将生成的公钥 ~/.ssh/id_rsa.pub 内容添加到 gitea > 设置 > SSH/GPG 密钥

Image

创建一个 Git 仓库并 clone 到本地:

点击右上角加号添加一个仓库,拷贝其中的 ssh 仓库地址:

Image

在本地创建一个新仓库并提交到 远程 gitea 仓库

mkdir gitea-demo
cd gitea-demo
touch README.md
git init
git checkout -b main
git add README.md
git commit -m "first commit"
git remote add origin ssh://git@gitea.guosx.net:223/guosongxin/gitea-demo.git
git push -u origin main

完成后可以在 gitea 上看到提交的内容

Image

添加一个 Gitea Actions 构建工程

快速入门 | Gitea Documentation

在本地下面下创建一个 gitea actions 配置文件到 .gitea/workflows/demo.yaml 路径。

mkdir -p .gitea/workflows
vim .gitea/workflows/demo.yaml

写入如下内容:

name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on: [push]
 
jobs:
  Explore-Gitea-Actions:
    runs-on: ubuntu-latest
    steps:
      - run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
      - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
      - run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
      - name: Check out repository code
        uses: actions/checkout@v4
      - run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
      - run: echo "🖥️ The workflow is now ready to test your code on the runner."
      - name: List files in the repository
        run: |
          ls ${{ gitea.workspace }}
      - run: echo "🍏 This job's status is ${{ job.status }}."

提交并推送到远程 gitea 仓库

git add .
git commit -m "actions demo"
git push

此时会触发 ci 运行,运行成功后结果如下:

Image