部署 穷人创业 1—搭建一个可持续交付的框架

sidekiq · 2022年01月04日 · 最后由 kikyous 回复于 2022年01月29日 · 1882 次阅读

19 年和朋友交流,朋友在自主创业,聊到生产部署与持续集成,我毫无自觉的打断他,“k8s 生态已经这么成熟了,直接上 k8s 不香吗?”

将近 3 个月没有写 go 了,熟悉了现在的公司用 Ruby 做的 EventSoucing 这套框架后,心血来潮想用 go 尝试一下,权当练手,正好亲人有个小项目,花了元旦 3 天时间,做了一次穷人创业体验。

业务背景

一个电商小程序,有一些分润和代理的逻辑,没有复杂的促销,单纯下单配送。后端用 golang + pg,管理前端用 react + vite + tailwindcss,小程序用 taro。

资源

只有一台 2C8G 的腾讯云服务器(去年优惠抢的 3 年的),我需要有以下的功能

  • 代码托管(git)
  • 持续集成/部署(自动测试、滚动发布、健康检查)
  • 日志收集

编排

我的预算很紧张,除了服务器花销,期望每个月在这上面的花销能控制在 100 块以内,我最终选择了以下的方案

  • 代码托管(gogs)
  • 持续集成/部署(drone + docker)
  • 日志收集(vector + 腾讯云 CLS)

结论

最终达成的效果还算喜人,没有吃 cpu 的服务,服务器内存稳定剩余 6GB,每次后端发布用时低于 1 分钟,CLS 日志服务按现在的量级,短时间内也不需要担心费用。

看来即使装不起全套的 Github + Gitlab CI-runner + Jenkins + k8s,穷人还是可以在一个小水管机器上进行持续交付的。

步骤

准备 https 泛域名证书

我使用了lego ,自己准备了一个 cron 任务,每天检查证书过期时间并自动更新。

安装 gogs

轻量级的 git 服务,我知道的的有 gitlab/gitea/gogs,我选择了 gogs。性能表现上看,没有突出的 CPU 尖刺,内存占用少,不会影响生产环境;代码质量上看,至少能看懂;安全上看,禁用了自主注册后,能防止大多数泄漏问题。

安装 drone 和 drone runner

drone server 使用 docker 就迅速运行起来了,runner 的选择上,我选择了 exec runner,这种 runner 是直接在宿主机上运行 shell 指令的,主要基于以下两个原因:

  • 我已经做好了安全防护,除了我没有其他 developer
  • 不希望 docker build 的时候重演 Jenkins Docker build 缓存 miss 导致构建慢的问题

编写 docker 打包发布脚本

我编写了一个简单的脚本来执行发布,原理是,为每个需要发布的项目指定一段空闲端口号,在需要部署的数目内,逐个进行如下操作:

  • 接受 docker image name 作为参数,使用一个未被占用的端口启动新的 container
  • 进行健康检查
  • 更新 nginx 配置
  • 移除一个 container
  • 更新 nginx 配置

在代码仓库内编写 drone ci 流水

我的后端流水配置大致如下,准备测试环境数据库,进行测试,再准备生产环境数据库,进行发布

---
kind: pipeline
type: exec
name: default

platform:
  os: linux
  arch: amd64

steps:
- name: prepare_test_db
  commands:
  - source /usr/local/rvm/environments/ruby-2.7.3
  - cd dbconsole
  - cp /data/files/server-db/database.yml config/database.yml
  - RAILS_ENV=test rake db:migrate

- name: test
  commands:
  - source /data/files/server-env/test-env
  - go test ./...
  environment:
    GOPATH: /data/gopath
    GOPROXY: https://goproxy.io,direct

- name: prepare_pd_db
  commands:
  - source /usr/local/rvm/environments/ruby-2.7.3
  - cd dbconsole
  - cp /data/files/server-db/database.yml config/database.yml
  - RAILS_ENV=production rake db:migrate

- name: build_executable
  commands:
  - source /etc/profile
  - go build -o server cmd/server/main.go
  - mv server docker/
  environment:
    GOPATH: /data/gopath
    GOPROXY: https://goproxy.io,direct

- name: deploy
  commands:
  - source /usr/local/rvm/environments/ruby-2.7.3
  - cd docker
  - cp /data/files/server-env/pd-env pd-env
  - export TAG=$(date +"%Y%m%d%H%M%S")
  - echo "image name is $APP_NAME:$TAG"
  - docker build -f Dockerfile-pd -t $APP_NAME:$TAG .
  - ruby ./rolling-update.rb upgrade $TAG
  environment:
    APP_NAME: 'server'
    DEPLOY_COUNT: 4
    INNER_PORT: 8100
    EXPORT_PORT_START: 8100

进行日志收集

按照文档安装腾讯云 CLS 的 log listener,发现不能收集自主管理的 docker container 日志,于是使用 vector 先将多个 docker container 的日志收集到集中的日志存放地,再在腾讯云 CLS 后台配置消费日志。vector 收集 docker container 的配置也非常简单,几行配置就能实现以前需要大量代码实现的功能:

[sources.server]
type = "docker_logs"
docker_host = "/var/run/docker.sock"
include_containers = [ "server" ]

[sinks.server_log_output]
type = "file"
encoding.codec = "text"
inputs = [ "server" ]
compression = "none"
path = "/data/logs/server/%Y-%m-%d.log"

启动 vector 和 log listener 后,就能在腾讯云 TLS 进行日志查询了

问题与解决

整个过程中没有遇到太多的问题,唯一一个让我在休息日还加班的还是 gogs 和 drone 集成的问题,drone 默认支持与 gogs 的集成,但是集成后,自主构建功能无法正常使用,drone 会提示一个反序列化失败的内部错误。

于是加了一会儿班找到了问题,在 19 年末的时候一位老哥不小心更改了 gogs API 接口的路由,导致一个接口不能按原来的约定返回内容了,我本能的逻辑是给下游的 drone 提交 PR,不过看起来有问题的一方是 gogs,由于节假日本就不多,没有太多精力回馈社区,所以放弃了反馈:

自己本地修复后上线,解决了问题。

总结

  • 部署方式上,这个和 capstrano 之流并无太多区别,只是自己想尝试一下低成本 Gitlab + Jenkins + k8s 替代
  • 穷人也可以有一套完备的 CI/CD workflow
  • 未能反馈/回馈社区修复 gogs 和 drone 集成 issue,是我的问题,希望大家踊跃回馈社区。

一个电商小程序,有一些分润和代理的逻辑,没有复杂的促销,单纯下单配送。

请问大佬后端提供了多少个 API?有多少个 test case?

3 天就能交付 C 端和 B 端(前后端),膜拜膜拜 😍

hjiangwen 回复

惭愧的是,我自己并没有实现持续交付,文章只是记录搭建持续集成的过程,并未实现具体业务,目前只写了 3 个 API 9 个 TestCase,其中一个还是 health_check,要等周末和春节才能写业务了。

补一张图,“上了,但没完全上”

喜欢这个题目,穷人创业,有喜感

非常棒,我也想用

穷人创业 交付我用 dokku

pynix 回复

+1, dokku 真的很方便

你这个还是奢侈了,我 2 核 4G 的机器,跑了 4 个 rails 应用。

drone 现在限制商业使用,不是纯粹的开源软件

需要 登录 后方可回复, 如果你还没有账号请 注册新账号