研究背景

由于稳定安全的高要求,以及寻求专业技术支持的考虑,银行大多数重要业务系统还是使用传统的商用软件。因此考虑到开发、运维人员习惯,和降低现有业务系统迁移到容器环境的改造成本,我们首先进行的是传统中间件的容器化。在此过程中,有一些心得,也遇到了一些问题。

随着云技术和容器化在传统行业的普及,银行也在逐步使用开源软件如tomcat等。当前的技术趋势下,在容器中,使用较为轻量的开源软件才是主流。因此我们也进行了开源应用中间件容器化的研究。

本文主要谈一谈应用中间件容器化的思路,以及在此过程中的一些心得和踩过的坑,兼有传统中间件和开源中间件容器化的对比.内容以经验教训为主,技术细节尽量简略。

本文中传统应用中间件以weblogic为例,开源应用中间件以tomcat为例。

2

架构设计

中间件镜像的构建分为四层,如图所示。首先第一层构建操作系统镜像,第二层在操作系统基础上部署JDK,第三层在第二层基础上安装中间件。前三层由系统管理部门负责维护。          

第四层为应用层,在第三层的基础上,部署应用、新建数据源、参数配置等。这一层由开发部门负责创建各个应用的镜像。系统管理部门开发了方便开发部署的接口,并提供配置规范



所有容器自动重启的原因 容器启动失败的原因_所有容器自动重启的原因

图:中间件容器化四层架构

这种架构一方面可以将开发人员从基础环境的配置中解放出来,可以更专注于代码本身;另一方面也增加了中间件环境的专业性和规范性,可以避免开发人员比较重功能实现,而轻性能、安全配置的问题。

3

应用中间件容器化三阶段



所有容器自动重启的原因 容器启动失败的原因_tomcat子容器启动失败_02

3.1 第一阶段:装进去,启起来

3.1.1 安装软件

Tomcat安装非常简单,只需使用COPY指令将按规范配置好的tomcat目录直接拷贝到容器即可。

 Weblogic需要安装,构建镜像不能采用图形界面了,可以使用weblogic的静默安装方式。使用RUN指令将安装的命令串起来即可。

这里有一个小技巧,一个RUN指令只算一层,安装介质可以通过网络下载,安装完成后删除,这样构建出的镜像不包含安装介质的大小。如果使用一个RUN下载介质,一个RUN指令删除介质,则就算介质删除了,镜像大小也会包含安装介质的大小。

示例:安装weblogic部分Dockerfile



所有容器自动重启的原因 容器启动失败的原因_tomcat_03

这里需要指出的是,weblogic安装后接近1G,tomcat仅需要100M左右,造成weblogic的镜像大小要大很多。

3.1.2 部署应用,启动

Tomcat部署应用只需将应用war目录拷贝到webapps目录下。启动时会发现tomcat默认是后台启动,作为容器主进程要改为前台启动,只需要把/bin/startup.sh 中启动方式从exec "$PRGDIR"/"$EXECUTABLE" start "$@"改为exec "$PRGDIR"/"$EXECUTABLE" run "$@"即可

这里踩过一个坑。一开始有人使用startup.sh && tail -f tomcat日志作为主进程,也可以实现前台启动并且将日志输出到前台。但是有两个缺点,一是日志增大后tail -f会占用很多内存,二是即使tomcat进程异常终止,因为tail -f进程也不会终止,所以容器不能自动重启。

Webogic软件安装之后,还需要建域后才能部署应用。建域和部署应用不像tomcat直接拷贝,需要使用脚本。脚本写好之后使用RUN指令构建镜像。Weblogic默认就是前台启动,无需修改。

3.1.3小结

tomcat容器化安装使用比较简单;weblogic安装、建域部署应用都需要写脚本,略复杂。另外weblogic比tomcat镜像大小要大很多。

3.2 第二阶段:用起来,管起来

我们做容器化,目的是要实际使用。只是装进去,启起来可不够,还需要去满足实际项目需求,并进行运维管理。

3.2.1 应用配置

我们在基础镜像里会提供标准配置,但是各应用有可能会有自己的需求,比如调整JVM参数,线程数等。

Tomcat可能改的配置文件一般是server.xml和startup.sh,应用可以在本地修改好后,使用COPY指令替换到镜像里。

Weblogic因为配置文件比较多也比较复杂,不采用替换文件的方式,而是通过构建镜像时执行建域脚本实现。考虑到让开发人员修改脚本会比较困难,我们提供的建域脚本采用脚本+配置文件的模式,开发人员只需要修改配置文件即可。

3.2.2 测试与生产不同的配置

实际使用中,测试环境和生产环境的IP地址、密码是不一样的,构建镜像的时候,测试环境的配置写在了镜像里,在生产环境就要修改。比如tomcat的数据源ip、用户名、密码等配置,weblogic的数据源配置、管理控制台密码就需要修改。



所有容器自动重启的原因 容器启动失败的原因_中间件_04

图 测试与生产数据库不同

这个修改的步骤,是在容器启动的时候实现。

Tomcat因为数据源配置写在server.xml中,直接使用configmap替换即可。

Weblogic因为有多个数据源配置文件,且文件比较复杂,采用了编写脚本的方式修改配置。生产环境的数据源信息作为环境变量,用ENV指令传入容器。我们在weblogic启动脚本中添加部分代码,实现获取环境变量的信息,修改weblogic数据源配置。容器启动时,调用weblogic启动脚本。

不过这种方式有个缺点,就是容器启动的时候,除了启动weblogic,还要执行修改数据源的配置的动作,会影响容器启动速度。

另外weblogic还需要多修改一个管理控制台的密码,tomcat则可以去掉管理控制台。

3.2.3 日志管理

 Tomcat的日志无法控制大小,因此时间长会占用过多宿主机的磁盘。我们采用标准输出日志对接ELK的方式,而在tomcat内部通过修改/conf/logging.properties,仅保留控制台日志ConsoleHandler,注释掉除此之外的配置,这样就可以关闭容器内部生成日志。

     Weblogic本身的日志可以控制大小,因此我们在容器内保留weblogic日志。weblogic标准输出无法控制大小,并且内容最丰富,也对接ELK。

3.2.4 liveness和readiness

为了保证业务正常处理,我们需要保证请求访问的容器都是可以服务的。K8s提供了 liveness和readiness功能,保证容器内应用完全启动后,以及状态正常时,才会有请求转发到这个容器。

这里tomcat和weblogic都可以检测端口状态或者业务页面状态来实现,没有区别。

3.2.5 prestop

容器停止时,如果直接使用docker stop命令,则相当于直接kill 容器内的中间件进程。为了优雅停止容器,很多时候推荐先停止应用程序,再重启容器。Prestop功能可以实现先执行prestop调用的命令,再停止容器。

Tomcat prestop调用/bin/shutdown.sh即可。

Weblogic默认stopWeblogic.sh脚本默认会监听本机的主机名,但因为构建镜像的时候的主机名和实际容器的主机名不同,脚本执行会失败。可以在建域的时候,设置监听地址0.0.0.0,就可以生成正确的stopWeblogic.sh脚本。Prestop调用/bin/stopWeblogic.sh即可。

3.2.6 小结

中间件在容器环境下,会有一些配置与非容器环境不同,如tomcat需要关闭日志,weblogic需要设置监听地址0.0.0.0。

Tomcat在实际项目中,项目开发人员修改标准配置比较容易,只需要configmap替换文件即可;weblogic如果标准配置不满足实际需求,就需要修改脚本,通过脚本去实现所需的功能。

3.3 第三阶段:性能好,够安全

项目实际在生产运行后,下一个阶段目标就是提升性能和安全。性能和安全方面做得好不好,决定了能用和用好之间的差别。

3.3.1 配置文件明文密码加密

Tomcat的数据源配置中,数据库的密码是明文,不符合安全规范。因此我们需要自定义加密的类做成jar包放到tomcat的lib目录下,然后在server.xml数据源的配置中增加一项factory的配置使用自定义的加密类。server.xml的数据源密码写成密文,通过自定义的类解密成tomcat可以使用的内容。

Weblogic数据源的配置中,数据源密码是加密的,因此不用自定义加密类。

3.3.2  性能调优

性能调优没有固定的套路。这里列出容器化的过程中我们遇到了几个性能问题,给大家做参考。

案例1,Parallel GC Threads线程数过多。

有一次我们发现一个应用在容器环境性能明显不如非容器环境,检查线程dump发现Parallel GC Threads数量非常多,于是做了研究。

原来在jdk8u131版本之前,如果没有指定参数,默认Parallel GC Threads数量为物理的核心数。非容器环境下,有时物理机的核心数不多。但容器环境下,物理机一般都是高配,核心数在几十到上百。虽然容器的cpu有限制,比如我们一个容器cpu-limit为4c,但Parallel GC Threads线程数和物理机CPU一致,达到98个,则GC的性能会明显下降。

jdk8u131到jdk 8u191-b12版本前,则是指定--cpuset-cpus ,即绑定时,JAVA可以识别到容器的cpu限制。

jdk 8u191-b12版本之后,JAVA可以识别容器cpu-limit的限制。

因此我们可以通过使用最新的jdk版本来避免这个问题。另外低版本的jdk 可以在JVM参数里用-XX:ParallelGCThreads=直接指定Parallel GC Threads线程数。



所有容器自动重启的原因 容器启动失败的原因_中间件_05

图:各版本jdk对容器cpu限制的识别

案例2,容器宿主机SWAP使用率高。

Suse linux环境下,如果不指定MALLOC_ARENA_MAX这个参数,默认会分配CPU核数*8*64M的内存空间,如果CPU核数较多,内存池就会很大,进而占用SWAP。可以设置MALLOC_ARENA_MAX=1,或者干脆关闭SWAP。

案例3,开启超线程后压测性能下降。

开启超线程之后,逻辑cpu的数量翻倍,比如一台物理机有48个物理cpu,单核,则未开启超线程前是48个逻辑cpu,开启后达到96个逻辑cpu,物理机器实际没有变化,每个逻辑cpu的性能显然是不同的。

那在开启超线程前,创建容器分配cpu limit为4,开启超线程后,因为deployment.yaml没有变,cpu limit仍然为4,性能下降是合理的。

这里不是说开启超线程会降低总体的性能,而是提醒大家,性能测试时要考虑到超线程的影响。

3.3.3 小结

Tomcat不提供数据源密码加密功能,weblogic提供这个功能。

性能调优方面,tomcat和weblogic都基于JAVA规范,基本一样。

4

总结

本文总结了应用中间件容器化的架构和推进思路,介绍了我们在容器化过程中的经验和踩的一些坑。

对比tomcat和weblogic容器化,可以发现:

1,tomcat日志管理、数据源密码加密功能不如weblogic,可以通过ELK、自行开发的数据源加密类来实现相应功能。

2,weblogic在配置方面较为复杂,需要自行开发脚本实现参数修改,tomcat使用configmap替换文件即可。

3,weblogic的镜像大小要tomcat镜像大很多。

从容器化的复杂度来说,tomcat和weblogic各有各的优势;镜像大小tomcat比weblogic有明显优势。

 


所有容器自动重启的原因 容器启动失败的原因_winform 界面 xml化_06

苏人

民生银行信息科技部,拥有十余年中间件运维经验,近期主要致力于中间件容器化、自动化、消息中间件等工作。



作者:苏人

编辑:民生运维文化建设组