使用Drone+docker配置本地轻量化CI/CD

2023-09-09717

  前几天完成了 nuxt /server 的迁移工作,以后本站的部署不再仅限于 vercel,而是可以用任何工具sticker(见 nitro deploy)。   发现一个比较轻量的 CI/CD 工具:Drone,成功给本站安排上了,但是只安排了一点sticker(下面会说明),地址是 https://blog-drone-cf.yunyuyuan.net/,运行在我家里的 linux 服务器上,使用 cloudflared tunnel 做内网穿透,关于 cloudflared tunnel 详见之前的文章

之前我写过一篇 jenkins+github 配置 CI/CD 的文章,主要是入门 Jenkins,当时觉得 Jeninks 太吃资源,而且各种配置混杂,管理页也很丑,所以后面没有学下去了。

安装 drone

准备

  • 一台 linux 服务器
  • 已安装 docker
  • 已安装 docker-compose

安装 drone server

  drone server 用于接受 webhook 通知,然后调度 drone-runner。由于 drone server 没有依赖,我选择安装进 docker。drone 提供多种集成方式,我这里使用的是 github。首先按照官方教程创建一个 Github OAuth 应用,然后再安装 drone server。   下面是我的 drone/docker-compose.yml:

version: '3.5'
services:
  app:
    image: drone/drone:2
    restart: always
    user: 1000:1001
    volumes:
      - /var/lib/drone:/data
    environment:
      # Github OAuth app 信息
      - DRONE_GITHUB_CLIENT_ID=your_github_oauth_client_id
      - DRONE_GITHUB_CLIENT_SECRET=your_github_oauth_client_secret
      # 用来和drone runner通信
      - DRONE_RPC_SECRET=your_custom_secret
      - DRONE_SERVER_HOST=drone.yourdomain.com
      - DRONE_SERVER_PROTO=https
      # 只允许特定github账号使用drone server
      - DRONE_USER_FILTER=your_primary_github_account,your_another_github_account
      # 指定一个账号为管理员
      - DRONE_USER_CREATE=username:your_primary_github_account,admin:true,machine:false
      # mysql数据库
      - DRONE_DATABASE_DRIVER=mysql
      - DRONE_DATABASE_DATASOURCE=drone:your_mysql_passwd@tcp(mariadb-app:3306)/drone?parseTime=true
    ports:
      - 9323:80
    networks:
      - mariadb-net

networks:
  mariadb-net:
    external: true

  我有一个已经在运行的 mariadb (即 mysql) 容器,并且已经接入到了我创建的 docker network bridge,名为 mariadb-net,所以上面直接使用就行。   启动前,先在 mysql 里新建一个名为 drone 的用户,并新建一个名为 drone 的 database,授权给用户 drone。   drone server 提供了一个控制面板,docker-compose up -d 启动后,打开 ip:9323 就可以看到控制面板的欢迎页了。然后授权 Github 登录,授权后可以看到所有仓库都显示出来了,点击 nuxt3-blog 进行配置 (请确保已经 fork nuxt3-blog),首次进入会提示仓库未激活,点击激活。激活后,进入设置,打开这两个选项: 设置   然后,配置环境变量(MONGODB_URI 是秘密内容,不能写在 config.ts 里,因为 MOGODB_URI 只会在服务端使用,浏览器无法看到。而另外三个是浏览器使用的,可以公开写在 config.ts 里):环境变量   关于 drone server 的配置只有如上两步。

安装 drone-runner

  drone-runner 用于执行 pipeline,drone 提供了多种 runner。我目前配置的 pipeline 需要用到 docker-runner,安装 docker-runner 比较简单,另写一个新的 drone-runner/docker-compose.yml:

version: '3.5'
services:
app:
    image: drone/drone-runner-docker:1
    restart: always
    volumes:
      # 使用宿主机的docker管理
      - /var/run/docker.sock:/var/run/docker.sock
    environment: 
      # 与drone server的DRONE_RPC_SECRET保持一致
      - DRONE_RPC_SECRET=your_custom_secret
      - DRONE_RPC_HOST=drone.yourdomain.com
      - DRONE_RPC_PROTO=https
      - DRONE_RUNNER_CAPACITY=2
      - DRONE_RUNNER_NAME=my-runner
    ports:
      - 9324:3000

直接 docker-compose up -d 启动就好了,不需要做其他配置。

流程介绍

以下是比较通用的 drone 工作流程: 通用流程 以下是我的简陋版工作流程: 简陋流程   与通用流程相比,我没有上传到 image registry,也没有另外的 production server(一般是 k8s)拉取 image 并部署,而是直接部署在安装 drone 的机器上,All in one!


  drone server 配置完毕,推送代码到 github,应该就能触发 drone 的 runner,编译完成后,docker 打包 image,然后重新运行新的 image,网站更新。 自动化编译部署   此时可以看到有三个运行的容器: 容器

Pipeline

  这是 nuxt3-blog 的 pipeline

kind: pipeline
type: docker
name: build

trigger:
  event:
    - push
  branch:
    - master

platform:
  os: linux
  arch: amd64

steps:
  # 使用node编译nuxt3-blog
  - name: build_project
    image: node:18
    volumes:
      - name: cache
        path: /drone/src/node_modules
    environment:
      MONGODB_URI:
        from_secret: MONGODB_URI
      # 也可以不写下面三个,转而写在config.ts
      CommentRepoId:
        from_secret: CommentRepoId
      CommentDiscussionCategoryId:
        from_secret: CommentDiscussionCategoryId
      CloudflareAnalyze:
        from_secret: CloudflareAnalyze
    commands:
      - npm i -g pnpm
      - pnpm i
      - pnpm build

  # 使用docker打包.output, docker in docker!
  - name: build_docker
    image: docker:latest
    volumes:
      - name: docker
        path: /var/run/docker.sock
    environment:
      APP_NAME: nuxt3-blog-app
      IMAGE_NAME: nuxt3-blog:latest
      MONGODB_URI:
        from_secret: MONGODB_URI
    commands:
      # 先停止并删除已有的容器/镜像
      - docker ps -q --filter "name=$APP_NAME" | xargs -r docker stop
      - docker ps -aq --filter "name=$APP_NAME" | xargs -r docker rm
      - docker images --filter "reference=$IMAGE_NAME" -q | xargs -r docker rmi
      # 打包镜像
      - docker build -t $IMAGE_NAME .
      # 部署
      - > 
        docker run -d
        --name=$APP_NAME
        --restart=always
        -e MONGODB_URI=$MONGODB_URI
        -p 127.0.0.1:8451:3000
        $IMAGE_NAME

volumes:
  - name: cache
    host:
      path: /var/cache/drone-nuxt3-blog
  # 把drone runner的docker.sock传进来,而drone runner使用宿主机的docker.sock,所以这里相当于直接操作宿主机
  - name: docker
    host:
      path: /var/run/docker.sock

附:定期备份图片

  Drone 的 cronjob 可以做定期任务,我目前使用第三方免费图床 smms.app 存储本站图片,但第三方图床没有办法保证稳定性,所以定期备份是很有必要的。   vercel 也支持 cronjob,但我没做,原因是没地方缓存已下载的图片,而且需要找一个支持 api 的网盘,不太方便。   本站已经有两个脚本,可以手动备份图片,分别是 npm run genimgnpm run downimggenerate-img-map.ts 搜索全站所有图片 url,并输出到 img.jsondownload-img.ts 读取 img.json,下载图片到 /imgs 文件夹。   npm run genimg 需要输入密码和正则表达式,因为本站的内容是可以加密的,必须解密,才能获取全部图片的 url,而正则表达式用来匹配图片,例如 smms 的图片格式是 https?://[\w-]+\.loli\.net/\d{4}/\d{2}/\d{2}/[a-zA-Z0-9]+\.(jpg|jpeg|webp|gif|png)

新增环境变量与 cronjob

  新增两个环境变量,NB_PASSWDNB_IMG_REGEX,分别代表密码和正则表达式(可选变量:FILE_USERFILE_GROUP,分别代表下载的图片的 owner id 和 group id)。新增一个名为 backup_img 的 cronjob,每周执行: Drone server 配置

修改 pipeline

# ......
# .drone.yml 新增如下内容
---

kind: pipeline
type: docker
name: backup_img

trigger:
  # 由cronjob触发
  event:
    - cron
  cron:
    - backup_img

platform:
  os: linux
  arch: amd64

steps:
  - name: backup_img
    image: node:18
    volumes:
      - name: cache
        path: /drone/src/node_modules
      - name: imgs
        path: /drone/src/imgs
    environment:
      FILE_USER:
        from_secret: FILE_USER
      FILE_GROUP:
        from_secret: FILE_GROUP
      NB_PASSWD:
        from_secret: NB_PASSWD
      NB_IMG_REGEX:
        from_secret: NB_IMG_REGEX
    commands:
      - npm i -g pnpm
      - pnpm genimg # 生成img.json
      - pnpm downimg # 读取img.json,下载


volumes:
  - name: cache
    host:
      path: /var/cache/drone-nuxt3-blog
  - name: imgs
    host:
      # 图片将被保存在宿主机的如下路径,脚本不会下载已有的图片
      path: /data/nuxt3-blog-imgs

运行效果

评论