什么是运维眼中可部署的软件架构

在之前的文章「优秀的软件或架构应具有哪些特性」中从操作性、一致性和维护性介绍了一个优秀的软件架构应该具有的特点,今天谈一谈操作性分类下的可操作性。

可操作性在日常研发过程中,可能是比较容易忽略的软件非功能性的内容,因为大多数开发都在为业务和 KPI 服务,即使想到了这点,也在开发的过程中容易比较丢弃,因为不管是大公司还是创业公司,大多数开发者都在为业务、项目疲于奔命,有些东西想做好,但是永远没有时间,当前任务都完成不了,当然这种非功能性的内容容易被忽略掉;当然还有些是架构师不作为的原因。不管是时间问题、还是环境问题,都会造成开发者忽略对可部署特性的考虑。

软件工程我觉得是需要有工匠精神的,不管是谁,我想也不可能一开始就把软件设计的很好,总会遗漏点什么,在不断的完善和优化软件,软件的成长就像一个人,通常软件的生命周期会经历类似幼年 --> 青年 --> 成年的这么一个过程。但是在商业化道路浓厚的情况下,这种精神经不起考验,就像老罗一样,有工匠精神又怎样,在商业化道路的逼迫下,还是失败了,造就了网红直播一哥的道路,今天好像有老罗抖音直播。

当你在疲于奔命写一些业务逻辑的时候,作为架构师 or 有自我认知的开发者,你应该不止步于开发完成,上线了就不管了,你应该在时间允许的情况下,继续优化你的项目,多从一些其他角度来提升架构的服务能力,比如我们常说的:可部署、可运维、高可用、容灾、稳定性等。

我们回归正题哦,什么是运维眼中的可部署的软件架构,可部署你也可以立即为容易部署或者叫做轻松部署,那要做到如此,需要有哪些方面的考虑。

 

1. 依赖越少越好

更好的依赖,意味着你开发交付到测试、运维手里的软件,在部署层面越简单。依赖环境、依赖各种库、各种版本,就意味着要依赖别人去做这件事情,但是别人是否可信是要经得起推敲才行,所以较多的依赖,就会增加你软件的风险点。

如下所示,是一个传统 LAMP 架构中,PHP 依赖的模块:

[PHP Modules]
bcmath
bz2
calendar
Core
ctype
curl
date
dom
ereg
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
igbinary
json
JsonNet
libxml
mbstring
mcrypt
mhash
mysql
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
readline
redis
Reflection
SeasLog
session
shmop
SimpleXML
sockets
SPL
sqlite3
standard
sysvmsg
sysvsem
sysvshm
tokenizer
wddx
xml
xmlreader
xmlwriter
xsl
yaf
zip
zlib

对于这类程序,当你问开发的时候,这些模块哪些有用,哪些是否可以去掉,回答基本都是都有用,能去掉的很少,那作为运维基本要吐血了,更可怕的是,这些依赖你还要找一个准环境挨个去看版本,是否版本不同程序运行是否兼容也不知道。

所以从易部署的角度来看,首先要做到的是,整理一份你程序的依赖关系和版本说明,别因为这个消息的传递不到位,运维白费力,虽然按照要求安装了要求的依赖模块,但是因为版本不对的原因,造成重复工作的过程,这样对整体的研发交付过程时间的浪费是极其多的,因为要不断实践、测试、回锅来过,沟通确认。

第二点就是减少对系统和库的依赖,我们先抛开 CPU 架构层面(一般 X86、ARM、MIPS)不说,就说在常规的 X86CPU 架构之上。在 Server 端,一个软件的运行可能只能够在 Centos7 下面运行,换个 Centos6 或者 Ubuntu 就不能够运行了,这种情况就得学习一下 Google 的做法,Google 对于 C/C++和 Golang 等语言的程序都会使用静态编译的做法,这样就是为了减少依赖和减少动态库的版本冲突,而不是交付一个需要运维人员现编译和现解决依赖库问题的软件版本。

这点对于有 Agent 架构的程序来说尤其重要,不管是日志采集的 Agent 还是监控的 Agent,基本上都要面临多系统和多 CPU 架构环境的适配,通常在不同的机器 or 环境(测试、预发布、生产)都各有差异,所以会有多种情况出现,如果少了个库、命令、包等依赖,那问题就显得比较明显了。

如今解决环境依赖无非最典型的利器就是利用 Docker 了,因为只要你的操作系统能够部署上 Docker Deamon,你 build 一个 Docker image,那可以多处跑,容器技术其实出现比较早,最早出现的是 LXC,是一种基于容器的操作系统层级的虚拟化技术,但是直到 Docker Image 的出现,Docker 完全火起来了,因为在没有 Image 的情况下,你的所有流水线 Build、Ship、Run 就没有意义了。

除了 Docker Image 的解决方案,有另外一种解决依赖的手段就是利用 CloudFoundry Buildpack 机制。在 Heroku 和 CloudFoundry 上可以看到,Buildpack 可以把用户代码编译之后,和依赖一起打包,比如 Java Web 程序,Buildpack 会先把源码编译打包成 War,然后和 Tomcat、JDK 一起,打成一个包,称为 Droplet,然后环境部署的时候直接分发 Droplet 就好了。

当然还有就手动编译的方式,那就是梳理好依赖关系和兼容关系,所有的构建都进行编译操作,静态编译不用管,直接归档目标即可,动态编译的话,可以利用ldd -v和readlink命令结合写个打包归档动态链接库的工具,最后在程序启动的脚本里面添加LD_LIBRARY_PATH运行路径。

 

2自动化配置

自动化配置就是减少人工配置项,尽可能的做到Zero Configuration,如果没法做到自动化配置,那就尽可能的让相同实例的配置一致,可能运维都比较烦因为配置不一致导致的问题。

自动探测服务器运行环境(但是要有一些规范),自动配置线程数、根据服务器内存的不同自动设置 Java JVM 参数等,都是典型的自动化配置的表现方式,不过这里如果一台服务器运行多服务、多实例的场景,就相对比较负责,就需要有些规范和策略。

如果是容器环境,就要注意容器的隔离性,因为容器的隔离性较差,获取到的信息是实际物理宿主机的配置,如果根据这些信息设置一些配置,比如 JVM 内存和线程数相关的参数,很有可能造成 OOM,当然你可以通过 API 的方式从 cAdvisor 获取容器的 CPU、内存、和磁盘网络等配置信息。

其次就是根据不同的运行环境来配置不同的配置,通常我们在软件上线的过程中,一般要经历 测试环境(QA) --> 预生产环境(Beta) --> 生产环境(Prod),而且不同的环境机器配置和机器数量也是不一致的,如果都一致化了,那就是成本问题。如果有中心化配置中心的话,比如携程的 Apollo,根据不同的环境标记,应用程序自动从配置中心拉取(或推送)对应环境的配置信息。在没有配置中心的情况下,我们预想要做一些规范,比如主机名的密码规范, 产品-机房位置-环境-服务分类(log-ali-qa-nginx1, log-ali-beta-nginx1,log-ali-prod-nginx1), 根据这些不同的标记准备不同的配置文件,然后根据主机的信息自动判断当前环境,如果是测试环境就自动应用测试环境的配置;如果是预生产环境就自动应用预生产环境的配置;如果是生产环境就自动应用生产环境的配置。

最后一个典型的配置就是关联关系配置,比如 A 模块要调用 B 模块的接口,首先就要知道 B 模块部署在哪些机器上,即对应的ip:port是什么,我们称为 endpoint,A 模块如果要把 B 模块的endpoint列表写死在配置文件里,那么 B 模块要扩缩容就比较麻烦了,需要通知 A 模块去修改配置并发起 A 模块的变更,这样关联配置就有点奔溃了...

典型解法有两个,一个是名字服务注册中心,即 B 模块通过心跳的方式向注册中心汇报自身的endpoint,然后 A 模块再去注册中心获取 B 的endpoint列表,如果 B 模块的某个实例挂了,就不会有心跳了,A 模块从注册中心获取到的endpoint列表,就会自动踢掉挂掉的实例。另一个就是加一个转发层,比如 Lvs 或者 Nginx,这样就交给转发层的程序去做判断,做自动剔除和转发请求分配。

推荐阅读

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值