超长干货:基于Docker的DevOps CI/CD实践——来自iHealth的分享

前言

相信我,一切事情的发生都是赶鸭子上架,没有例外。人类全部伟大的变革都是无可奈何,可又是那么顺其天然。好比容器(docker)技术的诞生,好比箭在弦上的创业,好比野心勃勃的kubernetes,好比现在已做为左膀右臂的rancher,好比这篇文章。前端

不一样于郑兄的CI/CD实践《如何利用Docker构建基于DevOps的全自动CI》,咱们结合自身情况,构建了一套咱们本身的DevOps CI/CD流程,更轻更小,更适合Startup。node

合适的才是最好的(Node.js & Docker)

若是世界只有FLAG、BAT,那就太无趣了。iHealth是一家初创型公司,我所在的部门有大概10名研发人员,在担负着三端研发工做的同时,全部围绕服务的交付和运维工做也都是咱们来作。git

技术的选型上,服务端、Web端和移动端(Android、iOS)都要上,但人少。因此招人的时候并无以貌取人的资格,部门对外的Title都是全栈。能一门语言通吃三端,群众基础普遍,恐怕没有比Javascript/Typescript(Node.js)更合适的了。github

服务端有Express、Koa、Feather、Nest、Meteor等各有其长的框架,前端大而火的Reactjs、Vuejs和Angular,无论是Server Render仍是先后端分离,均可以驾轻就熟。由于公司的健康设备(血糖仪、血压计、体温计、血氧、体脂秤等等)会有专门的部门研发设计以及提供SDK,因此移动端的研发工做更可能是在设计实现和性能优化上,React Native是一枚大杀器。虽然如今公司并无桌面端的需求,但不可否认的是Electron是一个颇有趣的项目,也为“全栈”这个词增长了更多背书。web

图片描述

另外,选择使用Node/Js/Ts做为全栈的基础会附带有RPC的好处。无需集成传统意义上的RPC框架(如gRPC),只需在编写远程(微)服务方法时,编写相应的npm package,也能够达到相同的目的,且成本更小,更易理解。docker

运维环境的选型上,全部的业务都运行在云端,省去了机房维护和服务器运维的成本。其实在盘古开荒时,咱们也是编写了Node程序后,使用PM2部署在服务器上,并无使用Docker。固然也存在没有使用Docker所带来的一切问题:三端不一样步、环境没法隔离……而Docker带给我最大的惊喜除了超强的可移植性,更在于研发人员能够很是容易对程序的顶级架构进行推理。apache

事实上,咱们直接使用docker-compose作容器编排着实有一段时间,在一次大规模的服务器迁移中,发现须要从新思考愈来愈多的container管理和更完善的编排方案。Rancher(Cattle)就是在这时被应用到技术栈中。npm

一切从Github开始

在运维环境一波三折的同时,DevOps的征程也是亦步亦趋,步步惊心。幸运的是,咱们知道本身缺少什么,想要什么,因此能比较容易的作到“哪里不会点哪里”。如同上一章节所述,合适的才是最好的。持续集成(CI)与持续交付(CD)的迭代过程,从最初的代码拷贝,到结合docker-compose与rsync命令,到使用CI/CD工具,作到相对意义上的自动化……迄今为止,咱们摸索出一套相对好用而且好玩的流程:json

图片描述

故事大体是这样的,当一只代码猴提交代码以后,他须要去接一杯咖啡。在猫屎氤氲的雾气里45°角仰望天花板,手机微信提醒此次构建成功(或失败,并附带污言秽语)。这时他能够开始往工位走,坐下时,微信又会提醒本次部署到Rancher成功(或失败)。后端

这一切开始的地方是github。当开发者写完 BUG 功能以后,须要有地方保存这些宝贵的资料。之因此没有使用Gitlab或Bitbucket搭建私有的Git服务器,是由于咱们认为代码是最直接的价值体现。服务如骨架,终端如皮肤,UE如衣服,三者组成让人赏心悦目的风景,代码是这背后的基础。咱们认为在团队精力没法更分散、人口规模尚小时,购买Github的商业版是稳妥且必要的,毕竟那帮人修复一次故障就像把网线拔下来再插上那样简单。

Drone CI

Drone这个单词在翻译中译做雄蜂、无人机。我特地咨询了一位精通一千零二十四国语言的英国朋友,说这个词的意思是autonomous,works by itself。白话就是有活它本身干,并且是自主的。不过这个解释对于Drone来讲名副其实。这个在Github上拥有13,000+ Stars的开源项目,使用Golang编写,相比Jenkins的大而全,Drone是为Docker而生的CI软件。若是有使用过Gitlab CI的小伙伴,相信对Drone的使用方式不会感到陌生,他们都是使用Yaml风格文件来定义pipeline:

build:
    image: node:latest
    commands:
      - npm install
      - npm run lint
      - npm run test
  publish:
    image: plugins/npm
    when:
      branch: master

Drone的安装方式如同Rancher同样简单,一行docker命令便可。固然,你们也能够看Drone的官方文档,在这里,只讲一下使用Rancher catalog安装Drone的方式:

图片描述

查看大图你们能够看到Drone使用Rancher catalog安装的方法(with github),在Github 的Settings中建立Drone的OAuth App时,Home Page Url务必要写你能访问Drone的IP地址或域名,例如:
http://drone.company.com

而OAuth App的Authorization callback URL应该对应上面的写法:
http://drone.company.com/auth...

小功告成:

图片描述

登陆进Drone以后,在Repositories中找到你想要开启CI的Git Repo,用switch按钮打开它:

图片描述

这表示已经打开了Drone对于这个Repo的webhook,当有代码提交时,Drone会检测这个Repo的根目录中是否包含.drone.yml文件,若是存在,则根据yaml文件定义的pipeline执行CI流程。

Drone与rancher、harbor、企业微信的集成

在决定使用Drone以前,须要知道的是,Drone是一个高度依赖社区的项目。其文档诸多不完善(完善过,版本迭代,文档跟不上了),plugins质量良莠。但对于擅长Github issue、Google、Stackoverflow的朋友来讲,这并非特别困难的事情。Drone也有付费版本,无需本身提供服务器,而是像Github那样做为服务使用。

若是你决定开始使用Drone,截止到上面的步骤,咱们打开了Drone对于Github Repo的监听,再次提醒,须要在代码repo的根目录包含.drone.yml文件,才会真正触发Drone的pipeline。

那么,若是想重现上面故事中的场景,应该如何进行集成呢?

我司在构建CI/CD的过程当中,现使用Harbor做为私有镜像仓库,从提交代码到自动部署到Rancher,其实应当经历以下步骤:

  • 提交代码,触发Github Webhook
  • Drone使用docker插件,根据Dockerfile构建镜像,并推送到Harbor中
  • Drone使用rancher插件,根据stack/service,部署上面构建好的image
  • Drone使用企业微信插件,报告部署结果

在这里节选公司项目中的一段yaml代码,描述了上述步骤:

pipeline:
  # 使用plugins/docker插件,构建镜像,推送到harbor
  build_step:
    image: plugins/docker
    username: harbor_username
    password: harbor_password
    registry: harbor.company.com
    repo: harbor.company.com/registry/test
    mirror: 'https://registry.docker-cn.com'
    tag:
      - dev
    dockerfile: Dockerfile
    when:
      branch: develop
      event: push
  
  # 使用rancher插件,自动更新实例
  rancher:
    image: peloton/drone-rancher
    url: 'http://rancher.company.com/v2-beta/projects/1a870'
    access_key: rancher access key
    secret_key: rancher secret key
    service: rancher_stack/rancher_service
    docker_image: 'harbor.company.com/registry/test:dev'
    batch_size: 1
    timeout: 600
    confirm: true
    when:
      branch: develop
      event: push
      
  # 使用clem109/drone-wechat插件,报告到企业微信
  report-deploy:
    image: clem109/drone-wechat
    secrets:
      - plugin_corp_secret
      - plugin_corpid
      - plugin_agent_id
    title: '${DRONE_REPO_NAME}'
    description: |      构建序列: ${DRONE_BUILD_NUMBER} 部署成功,干得好${DRONE_COMMIT_AUTHOR} !      更新内容: ${DRONE_COMMIT_MESSAGE}    msg_url: 'http://project.company.com'
    btn_txt: 点击前往
    when:
      branch: develop
      status:
        - success

对接企业微信以前,须要在企业微信中新建自定义应用,好比咱们的应用名字叫Drone CI/CD。固然,您也能够给每个项目建立一个企业微信App,这样虽然麻烦,可是可让须要关注该项目的人关注到构建信息。

下面是企业微信测试的截图:

图片描述

企业微信与微信客户端是连通的,可玩性还不错:

图片描述

在这里我认为有必要提醒一下,使用Drone的企业微信插件时,不要使用Drone Plugins列表里的企业微信。翻阅其源码能够发现,其中一个函数会将企业的敏感信息发送至私人服务器。无论做者自己是出于BaaS的好意,仍是其它想法,我认为都是不妥的:

图片描述

代码地址:
https://github.com/lizheming/...

在此Drone Plugins里的企业微信插件出现好久以前,个人好友Clément 克雷蒙同窗写了一个企业微信插件,至今仍在使用。欢迎检查源代码,提issue提bug,为了避免让克雷蒙同窗骄傲,我并不打算号召你们给他star:
clem109/drone-wechat

而在构建完成后,能够看到Drone控制面板里小伙伴们战斗过的痕迹:

图片描述

ELK与Rancher的集成

ELK是ElasticSearch、Logstash与Kibana的集合,是一套很是强大的分布式日志方案。ELK的使用更多在于其自己的优化以及Kibana面向业务时的使用,这自己是一个很大的话题,只ElasticSearch就有许多奇技淫巧。由于人力资源的缘由,咱们使用了兄弟部门搭建的ELK,等同于使用已有的ELK服务。因此在此也再也不赘述ELK的搭建,网上有许多资源可供参考。

在这里要作的事情,就是把rancher中的日志归集到已有的ELK中。

在rancher的catalog中找到logspout,这是一个logstash的adapter,为docker而生:

图片描述

在配置中设置LOGSPOUT=ignore,而后把ROUTE_URIS设置为已经搭建好的logstash地址,就能够将当前环境的日志集成到ELK中:

图片描述

Traefik与Rancher的集成

目前看来一切都很好,对吗?的确是这样。咱们提交了代码,drone自动构建镜像到harbor,自动部署到Rancher,自动发送构建结果,Rancher又能够帮助自动重启死掉的container,使用rancher webhook也能够实现自动弹性计算,而且可使用yaml文件定制构建流程,定制一些report信息,当构建或部署失败时,让企业微信自动侮辱咱们的小伙伴……

但是听说微服务还讲究服务注册和服务发现,若是并不想动用Zookeeper这样的核武器(就像咱们不想用Kong同样,一是有必定学习和维护成本,二是Logo越改越丑),那就须要找到一个轻量级,能知足需求的替代品。何况目前并无遇到须要削峰的处理。

对于域名的解析,咱们选择使用Traefik做为LB,这个一样使用Golang编写,一样拥有将近13,000 Stars,而且兼具简单的服务注册和服务发现功能。更值得一提的是,Rancher catalog里的Traefik很是友好的集成了Let's Encrypt(ACME)的功能,能够作到自动申请SSL证书,过时自动续期。固然,不推荐在生产环境使用,SSL免费证书的数量很是容易达到阈值而使得域名没法访问。

Traefik内部架构图(Image from traefik.io):

图片描述

如何安装Traefik呢?咱们以Rancher catalog中的Traefik为例(不使用ACME):

图片描述

咱们的目的是作域名解析,integration mode应该设置为metadata。Http Port设置为80,Https Port设置为443,Admin Port能够根据本身实际状况填写,默认8000。

此时的Traefik已经准备就绪,可是打开traefik_host:8000查看控制面板时,发现Traefik并无作任何代理。缘由是须要在代理的目标中,使用rancher labels标示出traefik的代理方式。

好比刚才安装的Drone,若是咱们想代理到drone.company.com这个域名,则须要在drone server的container中设置lables:

图片描述

  • traefik.enable=true 表示启用traefik代理
  • traefik.domain=company.com 表示traefik代理的根域名
  • traefik.port=8000 表示这个container对外暴露的端口
  • traefik.alias=drone 表示想将drone server这个container解析为
    drone.company.com

须要注意的是,traefik.alias有可能致使重复解析,同时traefik有本身的一套默认解析规范。更详细的文档请看GitHub 地址:
rawmind0/alpine-traefik

在设置Rancher labels后,能够看到Traefik的控制面板中,已经注册了服务地址:

图片描述

利用Traefik的这个特性和Rancher对于Container的弹性计算,能够作到简单的服务注册和服务发现。

最后须要在域名服务商那里作A记录解析,解析的IP地址应为Traefik的公网地址。 由于域名解析的默认端口是80和443,后面发生的事情就和Nginx的做用一毛同样了。域名解析到Traefik服务器的80端口(https则是443),Traefik发现这个域名已经注册到服务中,因而代理到10.xx开头的虚拟IP,转发请求并发送response。与Nginx Conf一模一样:

图片描述

至此,咱们已经彻底实现从代码提交,到自动部署以及域名解析的自动化。在生产环境的Traefik on Rancher中开启Https,能够把ssl的整个信任链以文本的形式粘贴进去,同时修改Traefik的Https选项为true便可:

图片描述

另外,Traefik并非LB/Proxy的惟一选择,甚至不是最酷的选择,但确是目前与Rancher集成最好的。下面图中的程序都值得作调研(能够小小的注意一下istio,天庭饱满,骨骼轻奇,这还只是2017年7月底的数据……):

图片描述

事实上对于Traefik咱们是又爱又恨。它能很是方便的与Rancher集成,功能简便强大,性能可观。但在最开始着实踩了很多坑,一度打算放弃并回归到传统的Nginx作反向代理的方式,甚至写了PR并被merge到master中。截止目前Rancher catalog中最新的1.5版本,已是一个真正稳定可用的版本了。

小技巧

Node.js的项目中书写Dockerfile时,常常会用到yarn或者npm i来拉取依赖包。但npm的服务器远在世界的另外一端,这时可使用淘宝的镜像进行加速。一般咱们在本地开发时执行会记得加上npm镜像,在服务器上跑Dockerfile也是同样的道理:

FROM node:alpine
WORKDIR /app
COPY package.json .
RUN npm i --registry https://registry.npm.taobao.org
COPY . .
CMD [ "node", "bin/www" ]

Drone在构建镜像并推送到镜像仓库时,须要根据Dockerfile的基础镜像进行构建,而docker服务器也远在世界的另外一端,一样的可使用mirror来指定镜像仓库,并尽可能使用alpine镜像缩小体积:

pipeline:
  build_step:
    image: plugins/docker
    username: harbor_name
    password: harbor_pwd
    registry: harbor.company.com
    repo: harbor.company.com/repo/test
    mirror: 'https://registry.docker-cn.com'

做大死命令,不要在服务器上使用。但本地开发很好用。意思是中止全部container,删除全部container,删除全部image:

docker stop $(docker ps -aq) && docker rm $(docker ps -aq) && docker rmi $(docker images -aq)

结语,附带工具链汇总

罗马不是一天建成,万丈高楼平地起。在企业发展之初,咱们在打基础的同时,也要保证项目高速迭代。短期内没法作到Netflix的体量以及其对于微服务治理的精妙,在运做的细节中也有诸多须要完善的部分,例如BDD、TDD的实践,传统意义上的UAT与蓝绿灰度发布,移动时代的全链路日志,服务熔断、隔离、限流以及降级的能力,亦或是星火燎原的Service Mesh……因此退一步讲,必须先生存,才能生活。咱们能够容许服务死掉,可是要保证无感知或极短感知的状况下,服务能迅速的活过来。

在持续交付的过程当中,咱们也尝试使用sonar代码质量管理,使用phabricator做为code review环节,由于配置的变动和微服务数量的逐渐增多,配置中心(主要考虑携程的Apollo)的引入也迫在眉睫,调用链监控以及代码从新埋点的成本(二节所述npm package rpc的优点又可体现)是否能抵过其带来的好处等等。但因目前还没有达到一个很是成熟的阶段,因此本次再也不分享,仅表述其名来启发各位聪明的小伙伴。

除此以外,技术视野的成长也非朝夕。就像我国政府在你们买不起自行车时就开始修建高速公路,时至今日,还能说它是面子(KPI)工程吗?与社区一同进步,开阔视野的同时,保持独立思考的能力,是比上述全部更为重要的技能。

回到本文开头所写,一切都是赶鸭子上架。与其说笔者天资聪慧才貌过人风度翩翩儒雅风流,不如说这都是被逼的。同事抱怨流程繁琐不直观,若要作到代码和咖啡那样大繁若简,就须要思考CI/CD的目的与本质。大智若愚,真正的天才,必须可以让事情变得简单。

拓展资料:

Rancher: https://github.com/rancher/ra...
Drone: https://github.com/drone/drone
Drone企业微信API插件: clem109/drone-wechat
Harbor: vmware/harbor
Traefik: containous/traefik
Phabricator: phacility/phabricator
SonarQube: SonarSource/sonarqube
Logspout: gliderlabs/logspout
配置中心(携程作的,代码写的还不错): ctripcorp/apollo
SuperSet(BI): apache/incubator-superset

做者简介

郭拓,北京爱和健康科技有限公司(iHealth)。负责公司基础服务构建与研发流程定制,曾供职于乐视、21vianet,高龄攻城狮活跃在一线研发工做中,乐此不疲。

相关文章
相关标签/搜索