按照前篇文章所提,本篇将聊聊如何搭建使用 Gitea 和 Drone。因为内容过多,这个内容我计划拆为多篇来讲述,本篇先聊聊如何搭建使用。

写在前面

为了方便配置域名、证书、以及后续潜在的动态扩容,我们可以搭配 Traefik 一起使用,让 Drone 和 Gitea 都只专注于 CI 和 代码存储相关功能,将“加密证书,流量转发相关”的事务交给 Traefik 处理。

相比较前篇内容中提到的老版本的 GitLab,这个方案对于资源的要求更低,让本地运行一套完整 CI 对于机器的负担降到了非常低的水平,日常运行资源占用几乎可以忽略不计(不算 CI 执行时的容器,即使算上 Traefik ,日常使用内存占用不到 200M):

CONTAINER ID   NAME                 CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O         PIDS
5295526d73f5   runner.nuc.com       0.00%     6.215MiB / 31.23GiB   0.02%     30.4kB / 24.3kB   11.8MB / 0B       17
9e810f12e2b4   drone.nuc.com        0.00%     10.56MiB / 31.23GiB   0.03%     36.5kB / 25.3kB   33.9MB / 0B       13
551b2e8683ba   gitea.nuc.com        2.05%     152MiB / 31.23GiB     0.48%     104kB / 439kB     88.8MB / 459kB    18
f4606080ef23   traefik              2.40%     20.49MiB / 31.23GiB   0.06%     483kB / 282kB     58.1MB 

这套方案对于资源的要求低,本质是因为软件数量/功能相比较 GitLab 少了至少一个数量级,而且软件编写语言单一,相比较非编译优化执行的 Ruby ,编译执行的 Go 语言程序性能上有非常变态的提升,之前我在一篇《重定向的九种方案及性能比较》的文章中也有提过。

如果你好奇完整的 GitLab CI 相关的功能和发展历程,可以翻阅这篇文章《聊聊 GitLab 的CI / CD 功能发展历程》

搭建基础环境

接下来先聊聊如何搭建。

系统环境准备

本文采用容器方式部署,简单来说,只要你的机器环境可以运行 Docker 就可以,所以笔记本也好、NUC也罢都是可以的、更何况是标准的 Linux 系统环境。

如果你对 Linux 不甚熟悉,我推荐使用容器友好的 Ubuntu 系统,如果你希望补充、了解一些基础操作,可以翻阅以往的文章

当然,如果你使用 MacOS ,那么只需要安装 Docker Desktop 即可。

Traefik 前置相关安装配置

Traefik 的搭建和使用,我的老读者都熟悉了,这里不就过多赘述了,不熟悉的同学可以从《更简单的 Traefik 2 使用方式》进行了解,如果你还想了解更多相关内容,可以翻阅这个标签合集

代码仓库 Gitea 安装配置

去年年初《使用 Docker 和 Traefik v2 搭建轻量代码仓库(Gitea)》一文中,我有提到过如何安装,当时选择了使用 Traefik 转发 Git Server 的 SSH 端口,本次我们换一种方式来进行端口暴露,减少应用之间的耦合,以及进一步提升效率。

为了方便后续维护,我们需要先定义一个 .env 文件,在里面配置好后续可能会有变化,以及需要我们自定义的内容:

# 应用名称
SERVICE_NAME=Gitea
# 服务域名
SERVICE_DOMAIN=gitea.nuc.com
# 使用的应用镜像
DOCKER_IMAGE=gitea/gitea:1.13.2
# 允许公网,跨主机访问 Git SSH Server
#SSH_PORT_EXPOSE=22
# 仅允许内部 CI ,本地机器使用 SSH 访问服务
SSH_PORT_EXPOSE=127.0.0.1:22

接着来定义服务编排配置文件,一般情况下你只需要复制粘贴即可,而不需要调整:

version: '3.6'

services:

  gitea:
    image: ${DOCKER_IMAGE}
    container_name: ${SERVICE_DOMAIN}   
    ports:
      - ${SSH_PORT_EXPOSE}:22
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - APP_NAME=${SERVICE_NAME}
      - RUN_MODE=prod
      - RUN_USER=git
      - SSH_DOMAIN=${SERVICE_DOMAIN}
      - SSH_PORT=22
      - SSH_LISTEN_PORT=22
      - HTTP_PORT=80
      - ROOT_URL=https://${SERVICE_DOMAIN}
      - LFS_START_SERVER=true
      - REQUIRE_SIGNIN_VIEW=true
      - DB_TYPE=sqlite3
      - INSTALL_LOCK=false
      - DISABLE_GRAVATAR=true
    networks:
      - traefik
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.giteaweb.middlewares=https-redirect@file"
      - "traefik.http.routers.giteaweb.entrypoints=http"
      - "traefik.http.routers.giteaweb.rule=Host(`${SERVICE_DOMAIN}`)"
      - "traefik.http.routers.giteassl.middlewares=content-compress@file"
      - "traefik.http.routers.giteassl.entrypoints=https"
      - "traefik.http.routers.giteassl.tls=true"
      - "traefik.http.routers.giteassl.rule=Host(`${SERVICE_DOMAIN}`)"
      - "traefik.http.services.giteabackend.loadbalancer.server.scheme=http"
      - "traefik.http.services.giteabackend.loadbalancer.server.port=80"
    volumes:
      # 标准 Linux 系统下使用
      # - /etc/localtime:/etc/localtime:ro
      # - /etc/timezone:/etc/timezone:ro
      - ./repositories:/data/git/repositories
      - ./data:/data/gitea/
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
    extra_hosts:
      - "${SERVICE_DOMAIN}:127.0.0.1"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy off localhost || exit 1"]
      interval: 5s

networks:
  traefik:
    external: true

将上面的内容保存为 docker-compose.yml 后,使用 docker-compose up -d 来启动服务。

使用 docker-compose logs -f 来观察日志:

gitea.nuc.com | Generating /data/ssh/ssh_host_ed25519_key...
gitea.nuc.com | Generating /data/ssh/ssh_host_rsa_key...
gitea.nuc.com | Generating /data/ssh/ssh_host_dsa_key...
gitea.nuc.com | Generating /data/ssh/ssh_host_ecdsa_key...
gitea.nuc.com | Could not load host certificate "/data/ssh/ssh_host_ed25519_cert": No such file or directory
gitea.nuc.com | Could not load host certificate "/data/ssh/ssh_host_rsa_cert": No such file or directory
gitea.nuc.com | Could not load host certificate "/data/ssh/ssh_host_ecdsa_cert": No such file or directory
gitea.nuc.com | Could not load host certificate "/data/ssh/ssh_host_dsa_cert": No such file or directory
gitea.nuc.com | Server listening on :: port 22.
gitea.nuc.com | Server listening on 0.0.0.0 port 22.
gitea.nuc.com | 2021/02/25 16:31:51 cmd/web.go:108:runWeb() [I] Starting Gitea on PID: 15
gitea.nuc.com | 2021/02/25 16:31:51 ...dules/setting/git.go:91:newGit() [I] Git Version: 2.26.2, Wire Protocol Version 2 Enabled
gitea.nuc.com | 2021/02/25 16:31:51 routers/init.go:132:GlobalInit() [T] AppPath: /app/gitea/gitea
gitea.nuc.com | 2021/02/25 16:31:51 routers/init.go:133:GlobalInit() [T] AppWorkPath: /app/gitea
gitea.nuc.com | 2021/02/25 16:31:51 routers/init.go:134:GlobalInit() [T] Custom path: /data/gitea
gitea.nuc.com | 2021/02/25 16:31:51 routers/init.go:135:GlobalInit() [T] Log path: /data/gitea/log
gitea.nuc.com | 2021/02/25 16:31:51 ...dules/setting/log.go:297:newLogService() [I] Gitea v1.13.2 built with GNU Make 4.3, go1.15.7 : bindata, timetzdata, sqlite, sqlite_unlock_notify
gitea.nuc.com | 2021/02/25 16:31:51 ...dules/setting/log.go:343:newLogService() [I] Gitea Log Mode: Console(Console:info)
gitea.nuc.com | 2021/02/25 16:31:51 ...dules/setting/log.go:249:generateNamedLogger() [I] Macaron Log: Console(console:info)
gitea.nuc.com | 2021/02/25 16:31:51 ...dules/setting/log.go:249:generateNamedLogger() [I] Router Log: Console(console:info)
gitea.nuc.com | 2021/02/25 16:31:51 ...les/setting/cache.go:70:newCacheService() [I] Cache Service Enabled
gitea.nuc.com | 2021/02/25 16:31:51 ...les/setting/cache.go:81:newCacheService() [I] Last Commit Cache Service Enabled
gitea.nuc.com | 2021/02/25 16:31:51 ...s/setting/session.go:63:newSessionService() [I] Session Service Enabled
gitea.nuc.com | 2021/02/25 16:31:51 ...s/storage/storage.go:151:initAttachments() [I] Initialising Attachment storage with type: 
gitea.nuc.com | 2021/02/25 16:31:51 ...les/storage/local.go:43:NewLocalStorage() [I] Creating new Local Storage at /data/gitea/attachments
gitea.nuc.com | 2021/02/25 16:31:51 ...s/storage/storage.go:145:initAvatars() [I] Initialising Avatar storage with type: 
gitea.nuc.com | 2021/02/25 16:31:51 ...les/storage/local.go:43:NewLocalStorage() [I] Creating new Local Storage at /data/gitea/avatars
gitea.nuc.com | 2021/02/25 16:31:51 ...s/storage/storage.go:163:initRepoAvatars() [I] Initialising Repository Avatar storage with type: 
gitea.nuc.com | 2021/02/25 16:31:51 ...les/storage/local.go:43:NewLocalStorage() [I] Creating new Local Storage at /data/gitea/repo-avatars
gitea.nuc.com | 2021/02/25 16:31:51 ...s/storage/storage.go:157:initLFS() [I] Initialising LFS storage with type: 
gitea.nuc.com | 2021/02/25 16:31:51 ...les/storage/local.go:43:NewLocalStorage() [I] Creating new Local Storage at /data/git/lfs
gitea.nuc.com | 2021/02/25 16:31:51 routers/init.go:176:GlobalInit() [I] SQLite3 Supported
gitea.nuc.com | 2021/02/25 16:31:51 routers/init.go:56:checkRunMode() [I] Run Mode: Production
gitea.nuc.com | 2021/02/25 16:31:51 cmd/web.go:163:runWeb() [I] Listen: http://0.0.0.0:80
gitea.nuc.com | 2021/02/25 16:31:51 cmd/web.go:166:runWeb() [I] LFS server enabled
gitea.nuc.com | 2021/02/25 16:31:51 ...s/graceful/server.go:55:NewServer() [I] Starting new server: tcp:0.0.0.0:80 on PID: 15
gitea.nuc.com | 2021/02/25 16:31:56 Started GET / for 127.0.0.1
gitea.nuc.com | 2021/02/25 16:31:56 Completed GET / 200 OK in 3.875698ms
gitea.nuc.com | 2021/02/25 16:32:01 Started GET / for 127.0.0.1
gitea.nuc.com | 2021/02/25 16:32:01 Completed GET / 200 OK in 1.131553ms

等待服务日志出现 Starting new server: tcp:0.0.0.0:80 后,打开浏览器访问我们绑定的域名 “gitea.nuc.com” 可以看到服务已经启动就绪了。

完成安装等待配置的 Gitea

不过,应用目前还需要等待我们进一步配置,才能够正常提供服务,我等待 Drone CI 配置完毕,再进行下一步。

Drone 的服务端(Server)配置

同样的,先创建 .env 配置文件,这里有一部分内容,我们需要配置完 Gitea 后才能获取,所以你也可以选择在配置完 Gitea 后,再来完成下面的内容:

# 服务域名
SERVICE_DOMAIN=drone.nuc.com
# 使用的应用镜像
DOCKER_IMAGE=drone/drone:1.10.1
# Drone 服务端和 Runner 之间通讯秘钥
DRONE_RPC_SECRET=YOUR_RANDOM_KEY
# Drone 超级管理员账号,根据自己需求修改
DRONE_ADMIN_USERNAME=soulteary

# Gitea 域名配置
GITEA_DOMAIN=gitea.nuc.com
# Gitea OAuth ClientID / Secret
# 稍后配置 Gitea 后替换即可
DRONE_GITEA_CLIENT_ID=a0da8a47-e89e-48ea-8ea3-08f2554511b1
DRONE_GITEA_CLIENT_SECRET=nrdSbAX_4AXexpUG_ZDw9iF640M8uC79h1raJxnX74I=

服务编排配置文件也比较简单,不需要做修改,直接复制粘贴到你的配置即可:

version: '3.6'

services:

  drone:
    image: ${DOCKER_IMAGE}
    container_name: ${SERVICE_DOMAIN}
    environment:
      - DRONE_GITEA_SERVER=http://${GITEA_DOMAIN}
      - DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID}
      - DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET}
      - DRONE_LOGS_TRACE=true
      - DRONE_AGENTS_ENABLED=true
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
      - DRONE_SERVER_HOST=${SERVICE_DOMAIN}
      - DRONE_SERVER_PROTO=http
      - DRONE_CLEANUP_INTERVAL=60m
      - DRONE_CLEANUP_DISABLED=false
      - DRONE_CLEANUP_DEADLINE_RUNNING=1h
      - DRONE_CLEANUP_DEADLINE_PENDING=2h
      - DRONE_USER_CREATE=username:${DRONE_ADMIN_USERNAME},admin:true
    networks:
      - traefik
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.drone-web.middlewares=https-redirect@file"
      - "traefik.http.routers.drone-web.entrypoints=http"
      - "traefik.http.routers.drone-web.rule=Host(`${SERVICE_DOMAIN}`)"
      - "traefik.http.routers.drone-ssl.middlewares=content-compress@file"
      - "traefik.http.routers.drone-ssl.entrypoints=https"
      - "traefik.http.routers.drone-ssl.tls=true"
      - "traefik.http.routers.drone-ssl.rule=Host(`${SERVICE_DOMAIN}`)"
      - "traefik.http.services.drone-backend.loadbalancer.server.scheme=http"
      - "traefik.http.services.drone-backend.loadbalancer.server.port=80"
    volumes:
      # 标准 Linux 系统下使用
      # - /etc/localtime:/etc/localtime:ro
      # - /etc/timezone:/etc/timezone:ro
      - ./data:/data
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
    extra_hosts:
      - "${SERVICE_DOMAIN}:127.0.0.1"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy off localhost:80/healthz || exit 1"]
      interval: 5s

networks:
  traefik:
    external: true

因为我们尚未配置好 Gitea ,所以先不着急启动服务。

Drone 的客户端(Runner)配置

我们接着来配置 Drone Runner ,还是先创建一套 .env 文件:

# 服务域名
SERVICE_DOMAIN=runner.nuc.com
# 使用的应用镜像
DOCKER_IMAGE=drone/drone-runner-docker:1.6.3
# Drone 服务端和 Runner 之间通讯秘钥
DRONE_RPC_SECRET=YOUR_RANDOM_KEY
# Runner 最大并发数量,根据自己需求来
DRONE_RUNNER_CAPACITY=2
# Drone 服务配置域名
DRONE_SERVER_DOMAIN=drone.nuc.com

然后是定义容器编排配置文件:

version: '3.6'

services:

  drone-runner:
    image: drone/drone-runner-docker:1.6.3
    container_name: ${SERVICE_DOMAIN}
    expose:
      - 3000
    environment:
      - DRONE_RPC_PROTO=http
      - DRONE_RPC_HOST=${DRONE_SERVER_DOMAIN}
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
      - DRONE_RUNNER_CAPACITY=${DRONE_RUNNER_CAPACITY}
      - DRONE_RUNNER_NAME=${SERVICE_DOMAIN}
      - DRONE_RUNNER_NETWORKS=traefik
    networks:
      - traefik
    restart: always
    volumes:
      # 标准 Linux 系统下使用
      # - /etc/localtime:/etc/localtime:ro
      # - /etc/timezone:/etc/timezone:ro
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/data
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
    extra_hosts:
      - "${SERVICE_DOMAIN}:127.0.0.1"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy off localhost:3000/healthz || exit 1"]
      interval: 5s

networks:
  traefik:
    external: true

将上面的配置保存为 docker-compose.yml。和 Drone Server 一样,因为依赖 Gitea 配置,所以这里我们先不着急启动服务。

代码仓库 Gitea 的进一步配置

访问安装后的 Gitea,不论是点击“登陆”亦或者“注册”,界面都将会来到“初始配置”页面:https://gitea.nuc.com/install

配置 Gitea 应用

直到我们配置完毕后,应用才能够真正的开始提供服务。

初始化应用配置

那么来简单讲讲如何进行配置,以及配置过程中的一些细节。

数据库可以根据自己实际情况切换为更为可靠的 PostgreSQL,如果你是个人或者小团队使用,使用 SQLite 问题也不大。

“一般设置”默认已经根据前文中的内容,进行了的自动化填充,这里如果还是想修改,仅建议修改 “站点名称”, 其余内容不建议进行修改。

“可选设置”包含三类配置项目:

  • “可选设置”中的“电子邮箱设置”可以根据你的实际情况完成配置,如果是个人使用,或者现在不想配置,可以先跳过,后续我们将配置更好用的推送通知,不依赖这个配置。
  • “可选设置”中的“服务器和三方设置”,我个人建议勾选“启用本地模式”,其余内容根据自己喜好来勾选即可,如果是个人使用,可以将各种注册方式都去掉。
  • “可选设置”中的“管理员账号设置”是必须完成配置填写的,填写方式可以参考下面的模式,建议全部使用小写英文,避免后续应用升级后出现预期之外的功能问题。

配置管理员账号示例

一切就绪后,点击安装按钮,完成安装,页面会自动跳转到新页面。

完成基础安装配置的 Gitea

配置 Drone 跨应用自动授权(OAuth授权)

还记得前文中我们迟迟没有启动的 Drone Server 和 Drone Runner 吗?前文中我们在 Drone Server 中设置了一套 OAuth ClientID / Secret 变量,当我们正确设置了 OAuth 变量后,Drone 便能够根据 Gitea 进行自动的仓库、用户的创建和管理,而无需我们再进行手动配置。

某种程度来看,Drone 可以看作一套无状态的服务,这方便了后续我们扩容或者同类服务切换的可能性。

下面来就来讲讲如何配置跨应用授权。

点击右上角的个人用户头像,选择下拉菜单中的“设置”,在新页面中选择“应用”选项。

配置 Gitea 的 OAuth 应用

在名称处填写“DroneCI”,重定向 URI 填写之前的配置的域名,并带上 /login 路径:

http://drone.nuc.com/login

点击提交,可以看到我们需要的 OAuth ClientID / Secret 信息已经生成完毕。

获取创建好的 OAuth Key

将内容更新到我们上文中的 Drone Server 的配置中,我们开始对 Drone 进行配置。

完成 Drone 的最后配置

对 Drone Server 的 .env 配置中的信息进行更新,将上面的 OAuth 信息填入配置中:

DRONE_GITEA_CLIENT_ID=ed292553-9dca-4f76-856f-4172c8ee4186
DRONE_GITEA_CLIENT_SECRET=3FxbTuNomJ4fUiUnZuA2NXcX083v1oK76ntsOxIuy6U= 

然后使用 docker-compose up -d 启动服务,顺便进入 Drone Runner 目录,将 Runner 也使用 docker-compose up -d 一并启动,等待大概五秒钟,浏览器访问我们配置的 CI 服务域名:drone.nuc.com,会看到浏览器自动跳转到了 OAuth 配置授权页面:

配置成功后,将能够看到 OAuth 授权提示

点击授权按钮后,我们会以当前用户身份自动登录 Drone 。

至此,基础安装和配置部分便完成啦。

以 Gitea 身份自动登录 Drone

最后

虽然安装配置结束,但是距离我们使用 Drone 进行 CI 来提升开发效率还早,关于 CI 过程的各种实践也还没有涉及到。

譬如“仓库和CI系统的进一步安全认证策略”、“细粒度的配置任务过程提醒”、“根据需求完成节点扩容”、“仓库构建内容持久化”、“CI和仓库之间更安全的数据交互”,以及“如何使用我们本地的机器来服务公网诸如 GitHub 等自动化过程”等话题都还没有聊到。

下一篇内容,我们将聊聊 Drone 的一些实战,将上述内容逐步涉及,以及针对本篇内容中的一些配置进行详细展开。

–EOF