别忘了泡上一杯茶!

绕不开的存在

当然,谈到 Git 服务时,肯定绕不过 GitHub 。为什么要大费周章自建呢?GitHub 不好吗?

是很好,但是也没那么好,因为:

  • GitHub 是一家商业公司,那么首先盈利肯定是首要目的,为了不倒闭,指不准未来哪天就变质了。
  • 同上,如果还是个自由软件爱好者的话,想必 GitHub 最近一些事件带来的臭味也不必我再提了……
  • Git 是分布式的,那么选择同样也是……不知道现在还有多少人还傻傻认为 GitGitHub 是一体的呢?

也可参见 GiveUpGitHub 一文章。

Gitea

Gitea 是一个用 Go 编写、面向自建的、轻量级的 Git 服务。其安装十分便捷,直接下载个可执行文件也可快速搭建起来,当然为了后续跟其它服务联动,还是用 docker-compose 方便点。

话不多说,新建一个文件夹然后往里建 docker-compose.yml 文件并写入以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
version: "3.7"

services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
volumes:
- ./data/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
- "2222:22"
networks:
- cicd_net

drone:
container_name: drone
image: drone/drone:latest
restart: unless-stopped
depends_on:
- gitea
environment:
# https://docs.drone.io/server/provider/gitea/
- DRONE_DATABASE_DRIVER=sqlite3
- DRONE_DATABASE_DATASOURCE=/data/database.sqlite
- DRONE_GITEA_SERVER=https://my.git.server/
- DRONE_GIT_ALWAYS_AUTH=false
- DRONE_RPC_SECRET=changeme...
- DRONE_SERVER_PROTO=https
- DRONE_SERVER_HOST=https://cicd.git.server
- DRONE_GITEA_CLIENT_ID=changeme...
- DRONE_GITEA_CLIENT_SECRET=changeme...
ports:
- "127.0.0.1:3001:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data/drone:/data
networks:
- cicd_net

drone-runner:
container_name: drone-runner
image: drone/drone-runner-docker:latest
restart: unless-stopped
depends_on:
- drone
environment:
# https://docs.drone.io/runner/docker/installation/linux/
# https://docs.drone.io/server/metrics/
- DRONE_RPC_PROTO=http
- DRONE_RPC_HOST=drone
- DRONE_RPC_SECRET=changeme...
- DRONE_RUNNER_NAME="action-runner"
- DRONE_RUNNER_CAPACITY=2
- DRONE_RUNNER_NETWORKS=cicd_net
- DRONE_DEBUG=false
- DRONE_TRACE=false
networks:
- cicd_net
volumes:
- /var/run/docker.sock:/var/run/docker.sock

networks:
cicd_net:
name: cicd_net

因为预想场景是私人自用,所以选择了 SQlite3 数据库,觉得别扭可以自己改掉……

然后准备两个域名,这里用 my.git.server 域名指定用来运行 Gitea 服务的域名,一个 cicd.git.server 域名用来指定运行后续的自动构建服务的域名(可选)。

首先为 my.git.server 域名设定DNS 后申请证书,certbot certonly --nginx -d my.git.server

,申请后写入 nginx 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#不要忘了替换成自己的域名
server {
listen 80;
listen [::]:80;
server_name my.git.server;

location /.well-known/acme-challenge {}
location / {
return 301 https://$host$request_uri;
}
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name my.git.server;

access_log /var/log/nginx/access.log;
#root /home/plume/Plume/ ;

ssl_certificate /etc/letsencrypt/live/my.git.server/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my.git.server/privkey.pem;

# for ssl conf: https://cipherli.st/
ssl_protocols TLSv1.2 TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;# openssl dhparam -out /etc/letsencrypt/ssl-dhparam.pem 4096
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 9.9.9.9 80.67.169.12 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
#如果不想显示外链图片可把 img-src 一栏改掉
add_header Content-Security-Policy "default-src 'self'; img-src *; frame-ancestors 'self'; frame-src https:";

location / {
proxy_pass http://localhost:3000/;
proxy_set_header Host $http_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;
client_max_body_size 50m;
}
}

之后 nginx -s reload 重载配置,然后 docker-compose up -d giteaGitea 服务启动,之后前往 my.git.server 地址进行配置并新建一个管理员账户。

Drone

到此服务就基本可用了,但是我相信很多人割舍不下 GitHub 的一个原因就是因为它的 Actions 很香。不过可以选择 Drone ,一个同样用 GO 编写的、轻量级的自动构建服务,也可以让体验往 GitHub Actions 靠拢。

首先为 cicd.git.server 域名申请证书并写入 nginx 配置文件,配置文件可复用上面的,只需要更改相关域名和 nginx 配置文件里的 location / 里的 proxy_pass 端口号就行(本例子是 3001)。

之后为 DroneGitea 联动做准备,登陆自己 Gitea 实例的帐号→设置→应用→创建新的 OAuth2 应用程序,应用名称随意填(本例子填 drone),重定向 URI 填入 https://cicd.git.server/login (替换成自己的域名)。

点击创建应用→会生成一个客户端 ID 和一个客户端密钥→替换到上述 docker-compose.ymldrone 一栏的 DRONE_GITEA_CLIENT_IDDRONE_GITEA_CLIENT_SECRET

之后再使用 openssl rand -hex 16 命令生成一串随机字符串给 DRONE_RPC_SECRET 使用(有两个地方需要替换)。

确认无误后使用 docker-compose up -d 启动全部服务,之后前往 https://cicd.git.server 地址查看是否正常运行,是的话点击登陆,看是否能够和 Gitea 进行联动,如果显示错误请检查是否与上述配置一致(例如我之前就遇到了无法验证的错误,排查后发现是某个路径多带了一个 / 导致之后生成的验证路径出错,去掉 / 之后就正常了)。

仓库操作

因为宿主机的 22 端口已被占用,所以克隆或者 PUSH 仓库使用的端口应该为 2222,觉得别扭也有其他教程教如何与宿主机共用端口,我就不折腾了(反正 Just work! )。

还有如果为网站使用了 Cloudflare 之类的 CDN 服务的话,那么 SSH 协议也是不能用的,得用网站域名下的真实 IP 地址克隆或 PUSH 仓库。

如果像我什么都没改的话,想要克隆仓库就会得到这么一个奇怪的地址:

git clone ssh://git@机子真实IP:2222/用户名/仓库名.git

反正 Just work

一个案例

那么接下来就讲讲我用这套服务的案例吧,自动生成文件并推送一个 HUGO 博客。

首先为仓库根目录写下 .drone.yml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kind: pipeline
name: build

steps:
- name: build
image: klakegg/hugo:alpine
commands:
- hugo

- name: deploy
image: node
environment:
CLOUDFLARE_API_TOKEN:
from_secret: api_token
CLOUDFLARE_ACCOUNT_ID:
from_secret: account_id
commands:
- npm install -g wrangler
- npx wrangler pages publish public --project-name 项目名 --commit-dirty=true

可以见到和 GitHub Actions 的配置还是挺像的,琢磨琢磨还是挺快上手的。

其中 steps 指定了所需步骤,image 指定了需要什么样的 docker 镜像,第一栏拉取了 klakegg/hugo 镜像并使用 hugo 命令生成静态文件。

之后是 deploy 一栏里的 environment ,像 GitHub 一样,密钥相关可以使用 secret 功能导入到环境变量中,可在自己的 cicd.git.server →相关仓库→ SettingsSecrets 里进行导入。

接下来使用 node 镜像安装了个 wrangler 包,这是 Cloudflare Pages 部署要用的,如果也用 Cloudflare Pages 部署的话可以参考:

Wrangler pages commands

Running Wrangler in CI/CD

PS:不得不吐槽相关操作首先必须要用 project create 命令里的 --production-branch 显式指定部署分支否则接下来的部署都会被识别为预览而不会真正部署到……被这个坑卡了好一会……

之后再进行相关操作后应该能看见自己 Gitea 实例也有小绿勾了(小红叉也行,至少证实自动部署服务有在用了)!

加主题

默认主题还挺程序员风格的,既然都自建了,肯定要加点主题快乐一下了。

现代主题为例。

如果是以上面的配置文件搭建起来的话要在自己文件夹下找到 ./data/gitea/gitea 路径,并在里头新建 public/csswget https://codeberg.org/Freeplay/Gitea-Modern/raw/branch/main/Gitea/theme-gitea-modern.css 获取主题文件。

之后再编辑 ./data/gitea/gitea/conf/app.ini 文件,在最后面加上:

1
2
请自己想象下……
因为现在我服务器炸了……

之后再用 docker-compose downdocker-compose up -d 重启服务。

安全

如果没有相关要求,请务必关闭实例的注册功能。

参见,记一次自建 Gitea + Drone 实例被挖矿的经历

也是修改 ./data/gitea/gitea/conf/app.ini 里的:

1
2
请自己查阅……
因为我现在服务器炸了

之后再用 docker-compose downdocker-compose up -d 重启服务,然后再检查站点是否关闭了注册入口。

参考

CICD With DroneCI and Gitea Using Docker Compose

Gitea Docs

Drone Docs

透過 Drone 建立自動部署流程,部署排程設定與權限管理 - 薛丁格的工程師

如不想授权 Giscus 应用,也可以点击下方左上角数字直接跳转到 Github Discussions 进行评论。
⬆︎TOP