项目介绍
Comunion是一个分布式的协作网络,通过区块链的技术去重新组织生产力和劳动的交易模式,从而实现全球劳动力、资源的自由、高效的流通和交易的一个平台💕。详情可以查看官网。
目前我在项目中主要负责前端开发和自动化打包发布等工作👷。
CI/CD 流程
- 分支命名约束
 
为了保证前后端仓库的 CI 配置文件可以通用,所以我们约定将master分支作为开发分支,将qa分支作为测试分支,prod分支作为线上版本分支。最终构建的 Docker 镜像在master分支将打上dev和latest标签,qa分支将打上qa标签,prod分支将打上prod标签。
- Docker 打包配置
 
我们约定前后端同时使用 Docker 作为交付结果,所以前后端都要维护自己的Dockerfile甚至是docker-compose.yml。原本前端这边想的是可以做简单点,就只有一个nginx容器,通过volume挂载配合scp上传来实现。但是后来跟后端沟通了下既然后端是需要registry仓库的,那前端也就弄成完整镜像的形式吧。于是Dockerfile就类似下面的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  FROM node:lts-alpine as build-stage WORKDIR /app COPY ./package*.json ./ RUN yarn install COPY . . RUN yarn run build
 
  FROM nginx:stable-alpine as production-stage COPY --from=build-stage /app/dist /usr/share/nginx/html COPY ./docker/nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
 
  | 
 
其中 nginx.conf 的配置如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | server {   listen       80;   server_name  _ default_server;   # charset utf-8;   # access_log  /var/log/nginx/log/access.log  main;
    location / {     root   /usr/share/nginx/html;     index  index.html;     try_files   $uri $uri/ /index.html;   }
    #error_page  404              /404.html;
    # redirect server error pages to the static page /50x.html   #   error_page   500 502 503 504  /50x.html;   location = /50x.html {     root   html;   } }
  | 
 
后端的打包逻辑大概就是go build各个模块,然后用alpine镜像拷贝 Golang 打包好的二进制文件,最后运行二进制文件这样的一个过程,具体可以查看后端代码。
- Github actions 流程和配置
 
先看图👍

其中有几点需要说明。第一个在进行ssh登录并重启时提供的是ssh私钥,为了安全考虑加了一层passphrase,然后这些私密设置都是在Github secret里设置但,但从某种意义上来说这样也不能保证完全安全,毕竟这个appleboy/ssh-action action 也是可以利用收集到的数据进行 ssh 连接的,所以我们只是在开发环境这么使用,正式线上环境可以考虑自建 ci,风险可以自己把控。
另一个目前镜像是推送到自己到仓库里的,其实可以直接推到Docker Hub,毕竟我们本来就是源代码公开的。
还有一个是微信推送用的是国人维护的一个公众号推送服务,然后在Github action里封装了一层,但这个服务仍然有 down 的风险。
前后端流程基本一致,除了代码校验和打包发布的代码稍有不同,主体流程基本一致的。
- 部署机器环境准备
 
首先我们需要给机器安装好docker和docker-compose,这里不做过多说明。然后利用之前博客里提到的利用 Traefik 搭建超简单的 DevOps 平台的方案实现服务自动发现和自动添加https的功能,但是需要去掉Dashboard功能,traefik的配置如下:
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
   | version: "3.7"
  services:   traefik:     image: traefik:latest     container_name: traefik     command:       - "--api=true"       - "--api.dashboard=false"       - "--providers.docker=true"       - "--providers.docker.network=traefik_webgateway"       - "--providers.docker.exposedbydefault=false"       - "--entryPoints.websecure.address=:443"       - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"       - "--certificatesResolvers.mytlschallenge.acme.email=${ACME_EMAIL}"       - "--certificatesResolvers.mytlschallenge.acme.storage=/etc/acme/acme.json"     labels:       - "traefik.enable=false"     networks:       - traefik_webgateway     ports:       - "443:443"     volumes:       - "./acme:/etc/acme"       - "/var/run/docker.sock:/var/run/docker.sock:ro"     environment:       - TZ=${TIME_ZONE} networks:   traefik_webgateway:     name: traefik_webgateway     driver: bridge
   | 
 
然后我们需要准备registry和registry-ui作为docker容器仓库,这个和之前那篇文章一样,就不做过多介绍。接着创建前后端的目录,创建好前后端的docker-compose.yml配置文件,前端的配置文件蛮简单的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | version: "3.7"
  services:   cos-front-com:     image: registry.comunion.io/comunion/cos-front-com:dev     container_name: cos-front-com     restart: always     networks:       - traefik     labels:       - "traefik.enable=true"       - "traefik.docker.network=traefik_webgateway"       - "traefik.http.services.cosFront.loadbalancer.server.port=80"       - "traefik.http.routers.cosFront.rule=Host(`dev.${SERVER_DOMAIN}`)"       - "traefik.http.routers.cosFront.entrypoints=websecure"       - "traefik.http.routers.cosFront.tls.certresolver=mytlschallenge"
  networks:   traefik:     external:       name: traefik_webgateway
   | 
 
但是后端的配置方式折腾了我好几天😭,因为后端是微服务模式,而且我不想增加域名,直接在前端的 url 后面加/api/xxx就能访问到后端服务。最终还是在traefik官网的migration的文章中找到了答案(一开始我怎么也想不到竟然会在迁移教程里找到我想要的内容…),最终配置如下
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
   | version: "3.7"
  networks:   app:   traefik:     external:       name: traefik_webgateway
  services:   comunion-redis:     container_name: comunion-back-redis     image: redis:alpine     restart: always     networks:       - app
       comunion-account:     image: registry.comunion.io/comunion/cos-back-account:dev     container_name: comunion-back-account     volumes:       - /etc/localtime:/etc/localtime     env_file:       - ./comunion-conf.env     environment:       PG_MASTER: postgres://xxx:xxx@comunion-db:5432/xxx?sslmode=disable&connect_timeout=10&search_path=comunion&timezone=Asia/Shanghai     depends_on:       - comunion-redis     restart: always     networks:       - app       - traefik     labels:       - "traefik.enable=true"       - "traefik.docker.network=traefik_webgateway"       - "traefik.http.services.comunion-account.loadbalancer.server.port=80"              - "traefik.http.routers.comunion-account.rule=Host(`dev.${SERVER_DOMAIN}`) && PathPrefix(`/api/account`)"       - "traefik.http.routers.comunion-account.entrypoints=websecure"       - "traefik.http.routers.comunion-account.tls.certresolver=mytlschallenge"              - "traefik.http.routers.comunion-account.middlewares=api-account-stripprefix"       - "traefik.http.middlewares.api-account-stripprefix.stripprefix.prefixes=/api/account"
    comunion-db:     image: postgres:10.3-alpine     container_name: comunion-back-db     networks:       - app     volumes:       - ./data/postgres:/var/lib/postgresql/data     environment:       - POSTGRES_USER=xxx       - POSTGRES_PASSWORD=xxx       - POSTGRES_DB=xxx
   | 
 
- 流程跑通
 
- 前端代码 merge 到
master分支,触发Github actions,开始构建、打包镜像,上传到仓库,ssh连接并拉取最新镜像,重新跑docker容器,完成后发送微信通知。 
- 后端代码 merge 到
master分支,触发Github actions,开始构建、打包镜像,上传到仓库,ssh连接并拉取最新镜像,重新跑docker容器,完成后发送微信通知。 
完美!😏
最后