背景
2022年11月,公司启动了智能运维产品的研发项目,该项目基于zabbix进行二次开发,对行业客户的数据中心的基础软硬件设施进行数据采集和分析,通过持续的数据采集和监控,及时发现数据中心软硬件的异常状态并预警,同时为故障处理,根因溯源,提供数据支撑,满足银行日常运维需求。
产品的整体开发思路是基于DevOps思想进行实践的,采用敏捷开发,每两个星期作为一个产品的迭代周期,在每个迭代的结束,团队会审查、评估和改进自己的工作方式,以便更好地满足客户需求和适应变化。
什么是DevOps
DevOps是 Development 和 Operation 组合而成的词。是一种让开发者和运维者共同努力,顺利开发和运维,不断提升系统价值的理念。
开发VS运维
-
开发人员希望快速完成代码并快速部署系统
-
运维人员则希望有足够的时间在部署前进行测试
解决方案:采用 DevOps 消除了两者之间的冲突,并显着改善了系统发布周期。
为什么使用DevOps
提高开发速度
DevOps 通过拥有一种支持开发人员和运维人员之间协作的机制,以及通过实现各种工作的自动化,显着提高了开发速度。借助微服务和持续交付,可以快速发布更新,从而快速响应不断变化的用户需求
提高系统稳定性
在短时间内重复开发,更容易发现错误和缺陷。此外,可以通过自动化任务(如自动编译构建,测试,部署等)来防止人为错误。这些实现提高了工作的可靠性,保持了系统的稳定运行
提高生产力
通过采用 DevOps,利用工具来改善开发人员和运营人员之间的协作,加快发布过程,更容易整合用户反馈,并提高工作效率
提高安全性
通过将安全性集成到持续集成 (CI/CD)、持续交付和持续部署管道中,可以将安全性在产品的构建过程中介入
提高企业竞争力
如果没有 DevOps 系统,将在竞争激烈的社会中落后,比如无法响应管理部门对新服务的请求,无法及时响应用户请求
DevOps 中的开发方法
持续集成(CI): 持续集成是关于运行自动化构建和测试。它使构建和测试能够在一天中频繁且自动地发生,并且是 DevOps 不可或缺的一部分,可以快速发现并修复代码缺陷,从而在短时间内开发出高质量的系统
持续交付(CD): 持续交付创建一个发布管道,自动部署构建,每当代码更改时自动准备发布到生产环境。持续交付有助于我们更好地在内部沟通变化,并有助于关注用户价值
持续部署(CD): 部署自动化,持续高频率交付最新特性。快速响应市场变化的公司使用持续部署来确保最新的软件始终可供其用户使用
持续反馈: 持续反馈是指不断接受用户对服务的意见和要求,并在开发中反映出来。通过聊天、电子邮件和 SNS 上的评论进行的查询也可以通过持续反馈纳入开发
持续监控: 如果系统出现问题,需要实时发现并修复。持续监控是做到这一点的方法。通过持续监控,可以快速查明问题的根本原因,防患于未然发生系统中断,并最大限度地减少对用户造成的潜在问题
DevOps生命周期
DevOps 的生命周期由基本的七个步骤组成:“计划”、“编码”、“构建”、“测试”、“部署”、“运行”和“监控”。
- 计划:定义整个项目的任务管理和开发要求
- 编码:程序员根据开发需求创建代码
- 构建:从源代码创建一个工作应用程序
- 测试:测试构建的应用程序是否存在错误和其他缺陷
- 部署:将应用程序部署到生产环境
- 运营:进行维护管理工作,提供持续服务
- 监控:检查从操作、评价和用户请求中获得的信息
依次、连续地重复这些步骤,就是DevOps的实践。
DevOps文化
2009 年,Flickr 发布了 DevOps 理念,强调四种文化,以在开发人员和运营人员之间建立协作关系。
- 尊重:互相尊重,评价对方的能力和成就
- 信任:相互信任,根据角色委派任务
- 对失败的积极态度:失败是可能的,所以不要责怪别人
- 避免责备:与其责备失败,不如鼓励建设性的讨论,以防止失败的发生
DevOps实践
在研发智能运维产品的过程中,遵循DevOps的理念和生命周期,依次连续地从计划,编码,构建、测试、代码安全扫描、部署,跟进产品上线运行情况,监控用户反馈信息来不断地进行产品迭代和优化。
下面从具体的步骤,来说明我们的产品软件开发是如何进行DevOps实践
上图可见,蓝色的主要是开发的事情,而橙黄色则主要是运维的事情,可以看到,只要产品一旦有新的开发需求,那么这一连串的任务又得重新执行一遍,对于蓝色那部分,目前很难实现自动化,需要架构师进行设计以及程序员去使用代码和算法实现,然而对于运维那部分,每次的需求变动,这部分内容几乎是不会发生变化的,因此可以实现运维部署自动化。
在本项目中,我们的产品代码是托管在gitlab上,通过jenkins来实现整个产品的CI/CD。jenkins采用pipeline,将代码仓库变动后的一系列操作交由jenkins进行监督执行。
计划
产品经理通过在TAPD上面提交对于产品的相关的需求和实现要求,并指派项目组成员进行开发实现。
产品的开发采用敏捷迭代的方式进行,设计每两周为一个周期,对产品的功能需求点进行追踪。
产品的开发追踪采用TAPD工具进行,该TAPD集成了企业微信的通讯录,方便公司成员内部进行协同沟通,以及开发任务的分配。
需求分析
针对产品需求的特点,产品经理可根据需求情况召集公司高层,产品架构师,市场经理,开发经理等角色,参与并分析产品的需求是否合理,讨论需求是否满足市场需要,技术上是否可以实现,公司的技术资源能力能否覆盖需求,有哪些经济/政治/技术风险等
概要设计
开发经理根据讨论后的需求,对需求的技术实现提供一个概要设计,对前后端交互的接口规范进行定义设计
编码
开发工程师,根据产品的计划和需求,接受指定的开发任务,在产品的开发代码仓库中,建立需求分支并在规定的时效内,通过熟悉的开发IDE和编程语言来完成需求任务的开发
本地测试
开发工程师在完成本地需求功能代码开发后,编写单元测试代码,测试该业务功能代码,确保业务代码的单元测试正常通过
提交代码
开发工程师完成本地开发和验证后,需要向代码仓库提交一个Merge Request,将新增或修改后的代码加入代码仓库,由开发经理进行审核,如果验证代码质量没问题,那么批准该代码分支合并到主分支,成为最终产品代码的一部分。
代码安全扫描
在每次main主分支的代码有变动时,漏洞扫描工具将通过预定义的代码安全规则对产品库的代码进行静态扫描,在发现漏洞代码的前提下,召集开发经理和工程师对漏洞进行分析,作为一个Bug需求,在下一次版本进行更新迭代,确保代码没有明显的开发漏洞
漏洞扫描工具将定期同互联网进行同步最新的安全规则
构建应用程序
在通过代码安全扫描的仓库代码,后端服务器将更新代码,重新对代码进行编译构建,前端主要是通过node工程来进行编译,主要用到了npm命令来构建,后端代码是一个基于sprintboot的java工程,主要是通过maven工具来构建
制作镜像文件
在构建完成后生成目标可执行程序,使用Dockefile指令,通过基于官方的基础镜像文件,添加本产品的可执行程序和配置,完成对产品前后端程序的封装
前端Dockerfile文件
FROM node:16.15.1-slim as builder
COPY . /app/
WORKDIR /app/
RUN sed -i 's#http://deb.debian.org#http://mirrors.huaweicloud.com#g' /etc/apt/sources.list && \
apt-get update && \
apt-get install -y python3 make gcc g++
RUN yarn
RUN yarn build:prod
FROM nginx
WORKDIR /app
VOLUME /myvol
COPY --from=builder ["/app/dist","/app/"]
COPY --from=builder ["/app/znyy-web.conf","/etc/nginx/conf.d/"]
RUN chown -R nginx:nginx /app
后端Dockerfile文件
FROM maven:3.8.5-openjdk-11 as builder
COPY . /app/
WORKDIR /app/
RUN mvn package
FROM openjdk:11.0.15-oraclelinux7
ENV ZNYY_DB znyy_db
ENV ZNYY_PORT 3306
ENV ZNYY_USER root
ENV ZNYY_PASS root123
ENV ZABBIX_DB zabbix_db
ENV ZABBIX_PORT 5432
ENV ZABBIXDB_USER zabbix
ENV ZABBIXDB_PASS zabbix
ENV TZ Asia/Shanghai
COPY --from=builder ["/app/znyy-system/target/znyy-system-2.6.jar", "/app/znyy-system-2.6.jar"]
CMD java -jar /app/znyy-system-2.6.jar --spring.profiles.active=prod
推送至制品库
在公司内部,通过harbor构建一个企业级的制品仓库,将封装完成后的镜像文件,推送到制品仓库,以供后续的容器调度引擎进行调度部署
后端制品库
前端制品库
产品部署
当制品库的镜像文件更新时,通过拉取或更新镜像文件,重新对产品的前端/后端镜像进行部署
docker-compose部署
# docker-compose -f srq-znyy.yaml ps
NAME COMMAND SERVICE STATUS PORTS
znyy-prod-db_data_mysql-1 "sh" db_data_mysql exited (0)
znyy-prod-db_data_pgsql-1 "sh" db_data_pgsql exited (0)
znyy-prod-grafana-1 "/run.sh" grafana running 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp
znyy-prod-mysqldb-1 "docker-entrypoint.s…" mysqldb running 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp
znyy-prod-postgres-server-1 "docker-entrypoint.s…" postgres-server running 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp
znyy-prod-redisdb-1 "docker-entrypoint.s…" redisdb running 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp
znyy-prod-zabbix-server-1 "/sbin/tini -- /usr/…" zabbix-server running 0.0.0.0:10051->10051/tcp, :::10051->10051/tcp
znyy-prod-zabbix-web-nginx-pgsql-1 "docker-entrypoint.sh" zabbix-web-nginx-pgsql running (healthy) 0.0.0.0:80->8080/tcp, :::80->8080/tcp
znyy-prod-znyy-admin-1 "/bin/sh -c 'java -j…" znyy-admin running
znyy-prod-znyy-web-1 "/docker-entrypoint.…" znyy-web running (starting) 0.0.0.0:8002->8002/tcp, :::8002->8002/tcp
kubernetes部署
# kubectl get pods -n srq-znyy -o wide
NAME READY STATUS RESTARTS AGE IP NODE
grafana 1/1 Running 2 (3d6h ago) 9d 11.55.3.251 k8s-node1.demo.com
myapp-busybox-pod 1/1 Running 152 (15m ago) 6d7h 11.55.3.6 k8s-node1.demo.com
mynginx-test-6b8665bd9c-rg8r4 1/1 Running 1 (3d6h ago) 7d21h 11.55.3.2 k8s-node1.demo.com
mysqldb 1/1 Running 2 (3d6h ago) 9d 11.55.3.9 k8s-node1.demo.com
postgres-server 1/1 Running 2 (3d6h ago) 9d 11.55.3.5 k8s-node1.demo.com
redis 1/1 Running 1 (3d6h ago) 7d21h 11.55.3.15 k8s-node1.demo.com
zabbix-server 1/1 Running 2 (3d6h ago) 9d 11.55.3.4 k8s-node1.demo.com
zabbix-web-nginx-pgsql 1/1 Running 2 (3d6h ago) 9d 11.55.3.253 k8s-node1.demo.com
znyy-admin 1/1 Running 2 (3d6h ago) 9d 11.55.3.249 k8s-node1.demo.com
znyy-web 1/1 Running 1 (3d6h ago) 7d21h 11.55.3.11 k8s-node1.demo.com
功能验证
-
产品经理来验证产品迭代的功能是否满足需求
产品经理根据功能实现情况,同需求计划,产品实现路线进行验证,判断此次迭代是否满足需求,达到目标,对此次迭代进行评价后,再决定是否开启下一个迭代目标
-
质量工程师验证产品是否满足质量指标,在测试的过程中,是否有明显的用户体验问题
以上各个阶段过程在产品的开发阶段进行循环往复,直到第一个产品版本进行正式发布。