使用 Docker 部署 Gitea 并使用 Nginx 反向代理配置 SSL
本文的目标是记录 Docker 部署 Gitea 的全过程,操作全程在云服务器上。默认拥有一台云服务器。并在云服务器上部署 Gitea 服务并将其通过 Nginx 反向代理到
gitea.guosx.net
域名下,同时配置 ssl。
部署完成后的架构图:
解释:
- 内部使用 pgsql 作为 gitea 数据库
- Docker 容器之间通过 gitea 容器网络连接,只有 nginx 的 223/443 端口被对外暴露。
- gitea http 端口为 3000,ssh 端口为 22,nginx 通过反向代理将代理到外部 443/223 端口(https,ssh)
前提
- 一台可连接外网的云服务器
- 一个已备案域名
- 在云服务器上安装好 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 证书
点击免费申领证书
填写申请 SSL 证书的域名,本文为 gitea.guosx.net
和申请邮箱(接收通知)。点击提交证书申请,
注意选择自动 DNS 验证,并且开启自动删除验证。 否则需要手动往域名解析里添加一条解析,验证完后再手动删除。所以直接让云服务器自己弄好了
提交生成完成域名认证后可以下载 SSL 证书文件,选择下载 Nginx 的证书文件。
部署 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_MODE
:prod:应用程序运行模式,会影响性能和调试。“dev”,“prod”或”test”。DOMAIN
:localhost:此服务器的域名,用于 Gitea UI 中显示的 http 克隆 URL。SSH_DOMAIN
:localhost:该服务器的域名,用于 Gitea UI 中显示的 ssh 克隆 URL。如果启用了安装页面,则 SSH 域服务器将采用以下形式的 DOMAIN 值(保存时将覆盖此设置)。SSH_PORT
:22:克隆 URL 中显示的 SSH 端口。SSH_LISTEN_PORT
:%(SSH_PORT)s:内置 SSH 服务器的端口。DISABLE_SSH
:false:如果不可用,请禁用 SSH 功能。如果要禁用 SSH 功能,则在安装 Gitea 时应将 SSH 端口设置为0
。HTTP_PORT
:3000:HTTP 监听端口。ROOT_URL
:"":覆盖自动生成的公共 URL。如果内部 URL 和外部 URL 不匹配(例如在 Docker 中),这很有用。LFS_START_SERVER
:false:启用 git-lfs 支持。DB_TYPE
:sqlite3:正在使用的数据库类型[mysql,postgres,mssql,sqlite3]。DB_HOST
:localhost:3306:数据库主机地址和端口。DB_NAME
:gitea:数据库名称。DB_USER
:root:数据库用户名。DB_PASSWD
:“empty” :数据库用户密码。如果您在密码中使用特殊字符,请使用“您的密码”进行引用。INSTALL_LOCK
:false:禁止访问安装页面。SECRET_KEY
:"" :全局密钥。这应该更改。如果它具有一个值并且INSTALL_LOCK
为空,则INSTALL_LOCK
将自动设置为true
。DISABLE_REGISTRATION
:false:禁用注册,之后只有管理员才能为用户创建帐户。REQUIRE_SIGNIN_VIEW
:false:启用此选项可强制用户登录以查看任何页面。USER_UID
:1000:在容器内运行 Gitea 的用户的 UID(Unix 用户 ID)。如果使用主机卷,则将其与/data
卷的所有者的 UID 匹配(对于命名卷,则不需要这样做)。USER_GID
:1000:在容器内运行 Gitea 的用户的 GID(Unix 组 ID)。如果使用主机卷,则将其与/data
卷的所有者的 GID 匹配(对于命名卷,则不需要这样做)。
完整的可配置变量见:
需要说明的是,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
配置文件写入如下内容:
用于:
- 将外部主机的 80 端口重定向到 443 端口(强制使用 https)
- 将外部主机的 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 端口。
浏览器访问 gitea.guosx.net
,会进入 gitea 初始化配置,设置完成后点击立即安装(可以在这里配置一个管理员账号)
如果配置了管理员账号,完成后就会进入用户页面。
启用 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 中拷贝
修改默认的 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。
Gitea 创建仓库执行 Actions demo 工程
上述步骤完成后,gitea 就算是完成部署了,下面演示一下 gitea 的基本使用
将自己电脑的 ssh 公钥添加到 gitea
执行这一步后就可以通过 ssh 操作 gitea 仓库了
在本地电脑上生成 ssh 密钥:
ssh-keygen -t rsa
将生成的公钥 ~/.ssh/id_rsa.pub
内容添加到 gitea > 设置 > SSH/GPG 密钥
创建一个 Git 仓库并 clone 到本地:
点击右上角加号添加一个仓库,拷贝其中的 ssh 仓库地址:
在本地创建一个新仓库并提交到 远程 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 上看到提交的内容
添加一个 Gitea Actions 构建工程
在本地下面下创建一个 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 运行,运行成功后结果如下: