智源大会结束,虽然还是很忙,但是总归有了一些时间,可以开始对之前的文章计划进行补完操作,本篇是第一篇补全,聊聊如何在容器环境下,花比较少的资源,来搞定高质量发布。

这个方案适用于小型团队、个人HomeLab,本文有别于我们当前团队使用的 GitLab Runner 相对比较重的方案,如果你面临的是更大规模的团队协作、项目管理需求和追求更全面的 CI/CD 阶段解耦,可以浏览我之前写的关于 GitLab 的内容。

写在前面

部署属于持续集成中场景的一环,而持续集成中和部署相关有几个步骤必不可少:管理代码、产物部署、产物版本管理。也正是因为有这些清晰的步骤划分,配合其他的措施,比如产物检测、安全检测、健康检查等我们才能够做到高效的秒级部署、多分支、多项目快速迭代。

我个人和团队虽然都使用 GitLab 作为 HomeLab 服务器的代码管理方案,但是这个方案如果放在公有云上,对个人/小团队而言,较多的资源消耗对于个人而言还是一个不能忽视的成本,所以这里需要使用一个轻量的解决方案。

诸如 rsyncscp 等无版本控制的方式在此忽略,因为我们还是期望有一些简单的版本回滚能力的。

下面聊两种不同的简单使用的方案。

方案一:Git Over SSH

最轻量的安全方案便是使用 SSH + Git,对资源的消耗几乎可以忽略不计,Git 官方社区文档中也有对这种方案进行描述: Git on the Server - Setting Up the Server 

首先在服务器端创建一个空的仓库,这里不一定需要使用 bare 模式进行创建,一般模式也是可以的,比如将仓库创建在 /repo-path/ 目录。

考虑到服务端安全问题,我们一般使用 RSA KEY 进行 SSH 交互,可以在 ~/.ssh/config 定义服务器配置,减少在使用过程中的命令复杂度:

Host pub-server
    Hostname        123.456.789.000
    User            user
    Port            12345
    IdentityFile    ~/.ssh/id_rsa_for_server
    ControlPath     ~/.ssh/pub-%r@%h:%p
    ControlPersist  yes
    TCPKeepAlive    yes
    Compression     yes
    ForwardAgent    yes

当你将你的 RSA PUBLIC KEY 在目标服务器授权之后,便可以和服务器进行数据交互了:

git clone ssh://pub-server/repo-path/repo-name/ dest-name
Cloning into 'dest-name'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 9 (delta 1), reused 9 (delta 1)
Receiving objects: 100% (9/9), done.
Resolving deltas: 100% (1/1), done.

...
git pull
git commit -m "your message"
git push
...

当然因为没有启动 Git Server 所以,这里的仓库需要提前在服务端创建好,或者将非服务端的内容先进行初始化并同步至服务端。

而触发 CI 命令也很简单,使用 Git Hook 即可。

但是这种方案也有一些小缺陷:

  1. 必须使用 SSH 协议进行交互,不少场景下使用 SSH 或者公开使用 SSH 并不是一个十分稳定/安全的方案,比如会受到网络设备干扰等、或不使用密钥使用密码等。
  2. 适合项目不多的场景,项目多了之后,不易管理。

方案二:使用轻量 Git 服务软件

关于 Git 轻量软件的基础搭建使用,之前的文章中有提到过: 使用 Docker 和 Traefik v2 搭建轻量代码仓库(Gitea)使用 Docker 和 Traefik v1 搭建轻量代码仓库(Gogs) ,感兴趣可以进行了解,接下来我们基于第一款软件继续聊聊。

先给出一份参考配置:

version: '3.6'

services:

  gitea:
    image: gitea/gitea:1.10.3
    container_name: gitea.lab.com
    ports:
      - 127.0.0.1:22:22
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - APP_NAME=Private
      - RUN_MODE=prod
      - RUN_USER=git
      - SSH_DOMAIN=gitea.lab.com
      - OFFLINE_MODE=true
      - HTTP_PORT=3000
      - ROOT_URL=https://gitea.lab.com
      - LFS_START_SERVER=false
      - DB_TYPE=mysql
      - DB_HOST=db
      - DB_NAME=gitea
      - DB_USER=gitea
      - DB_PASSWD=gitea
      - DB_CHARSET=utf8
      - INSTALL_LOCK=false
      - DISABLE_GRAVATAR=true
    networks:
      - traefik
      - gitea
    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(`gitea.lab.com`)"
      - "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(`gitea.lab.com`)"
      - "traefik.http.services.giteabackend.loadbalancer.server.scheme=http"
      - "traefik.http.services.giteabackend.loadbalancer.server.port=3000"
    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:
      - "gitea.lab.com:127.0.0.1"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider --proxy off localhost:3000 || exit 1"]
      interval: 5s

  db:
    image: mysql:5.7.16
    restart: always
    networks:
      - gitea
    expose:
      - 3306
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: gitea
      MYSQL_DATABASE: gitea
      MYSQL_USER: gitea
      MYSQL_PASSWORD: gitea
      TZ: Asia/Shanghai
    volumes:
      - ./mysql:/var/lib/mysql
      # 标准 Linux 系统下使用
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    healthcheck:
      test: ["CMD-SHELL", "/etc/init.d/mysql status"]
      interval: 30s

networks:
  gitea:
    internal: true
  traefik:
    external: true

这里有几个小技巧:

  • 分离应用网络避免不同应用都暴露在相同网络,尤其是公网
  • 使用 Traefik 进行服务按需暴露
  • 将 SSH 等端口暴露在服务器本地,用于服务器内部其他服务调用,比如部署服务
  • 对于非长时间使用的服务,也可以在使用的时候进行启动,非活跃时间进行关闭,这个小技巧后面的文章再展开聊吧

最后

三年前我曾在 GitHub 上开了一个项目,想聊聊HomeLab:soulteary/Home-Network-Note,三年过去了,HomeLab 建设的差不多了,但是过程中的不少文章却永远成为了草稿。今天大会结束了,或许是一个整理的好时机。

–EOF