DCOS云平台之应用容器化改造规范

1    前言

对于传统应用来说,使用和不使用Docker可能并不能直接给企业带来好处,相反使用中遇到了问题肯定会给企业带来麻烦。大家会问是什么原因让大家接受和使用Docker,并将他们的应用迁移至Docker容器使用呢?Docker最大的好处无非是高效的标准化应用交付,它不仅能提高了运维工作效率,并节约硬件成本。据国外的统计使用Docker平均可以提升60%的效率,同时节约40%的成本。

当我们需要迁移一个应用的时候,首先需要了解一个应用当前正在使用的资源有哪些。了解当前系统的网络拓扑,了解当前系统之间互相调用关系。现有的拓扑结构是迁移的最好参考,传统的单体模式下,很多应用程序运行在一台主机之上,但在微服务体系下,我们更倾向于通过容器来解耦合,实际上,并不是所有系统都适合于解耦合,任何一个企业在迁移过程中都不愿意去重新对一个应用模块重新编码,因为有的程序可能根本就找不到代码,有的程序根本就找不到对应开发商,有的系统甚至要全部梳理一遍才能了解系统整体架构。要想让传统应用迁移至容器后上云成功,想必在改造的过程中需要遵循一些规范。

2    应用容器化改造规范

2.1     应用程序改造规范

2.1.1 热点数据缓存规范

1 、问题描述

热点数据指应用系统在运行过程中经常使用的数据,传统方式是应用系统在启动时通过数据库加载到应用实例缓存中,提高数据的访问速度,但往往因为加载的数据过多而导致系统启动过慢。

2 、改造建议

建议应用在设计时将热点数据存放到缓存服务器(如Redis、Memcached)中   ,在启动时避免执行加载缓存动作,提高应用系统启动速度,相关热点数据可通过手动或自动方式更新到缓存服务器中,应用通过访问缓存服务器获取相关热点数据。

2.1.2 应用无状态化规范

1 、问题描述

有状态应用是指用户在登陆后会有一定的会话信息(也就是应用中的session对象)存储在实例中,如果该实例异常停止,该用户的会话信息就会丢失。

2 、改造建议

建议应用开发设计时将用户的会话信息保存到缓存服务器(如Redis、Memcached)中,根据用户的唯一标识session_id来对缓存服务器进行存取用户的会话信息,这样会话信息与应用实例分离,实现应用无状态话。

3 、中间件session存储配置样例

1) 、Tomcat样例

应用服务器本地会话保存改为使用key-value数据库(redis)存储。Session的读取依然由中间件Session Manager进行自定义管理。

例如:tomcat中加载 tomcat-redis-sessionmanage-1.2.jar   、commons-pool-1.6.jar 、jedis-2.1.jar

tomcat/conf/context.xml 中加入如下配置:

“com.orangefunction.tomcat.redissessions.RedisSessionManager”

 maxInactiveInterval= “60”    //当前会话的失效时间

 sentineMaster= “自定义”    //与redis配置保持一致

2 )、php-fpm+nginx样例

nginx 安装使用php70-php-pecl-redis扩展包,然后php连接redis配置

$config['sess_driver'] =   'redis';

$config['sess_cookie_name']   = 'nlf_front_session';

$config['sess_expiration']   = 7200;

$config['sess_save_path']   = '127.0.0.1:6379?auth=Redis@2017';

$config['sess_match_ip'] =   FALSE;

$config['sess_time_to_update']   = 300;

$config['sess_regenerate_destroy']   = FALSE;

2.1.3 应用日志规范

1 )应用日志文件命名规则:[应用名]_[容器ID]_catalina.out_[date]

2 )应用日志统一输出到应用规划目录下

3 )每个容器中应用日志文件个数最大为5个,每个日志文件容量最大为200M

4 日志在测试环境中开启调试开关,但在生产环境中必须关闭调试开关

5 )容器内的应用日志文件统一收集到日志服务器

2.1.4 应用环境配置规范

1 、问题描述

应用系统在开发过程中需要分别在开发环境、测试环境、预生产环境和生产环境,而对于不同环境,应用只是某些配置文件的配置不同(如数据库连接地址,访问接口地址)不同,而程序代码相同,在不同环境中部署时就要进行重新打包。

2 、改造建议

建议应用针对不同环境准备相应的配置文件,并全部打包到应用包中,我们以web.xml为例,命名规则如下:

开发环境—dev_web.xml

测试环境—test_web.xml

预生产环境—pre_web.xml

生产环境—pud_web.xml

并将文件存放路径写入系统集成文档中。

2.2     应用镜像打包改造规范

改造事项

问题描述

改造建议

应用中间件选型

传统方式是可以将轻量级与重量级的中间件软件部署到小机或是虚机是没有问题,因为他会占用着一台主机资源,对启动服务时间要求不是那么高。如果重量级的应用部署到容器内就不适合容器秒级启动的目标,也会出现资源浪费情况。

1 、Java开发语言程序动态应用使用tomcat轻量级中间件

2 、静态应用使用nginx高性能WEB 服务器

3 、Php开发语言程序动态应用使用php-fpm与nginx结合

4 、缓存服务器使用Redis高性能缓存服务器

容器应用进程非root用户启动

如果容器中使用的root用户启动应用,这样在外面的宿主机就可以自由的使用直接docker exec命令获取root权限,安全风险比较大。

1 、docker容器默认以root运行。

2 、随着docker的成熟,越来越多的安全默认选项变得可用。请求root是危险的,可能无法在所有环境中可用。所以镜像应该使用USER命令来指令容器以一个非root用户来运行。

应用固化服务监听端口

对于标准的服务只需提供一个对外服务端口,而有些应用在运行过程中会自己产生一些服务端口对外服务,而且这些端口是随机产生,给管理带来不便。

在应用设计时尽量不要在应用运行时产生额外的服务端口,如果必须使用则需让端口固定下来并体现在系统集成文档中

定时任务与应用分离

传统方式定时任务都与应用打包在一起,通过修改配置文件的特定配置项来控制定时任务是否启动,这样就会出现应用的差异化,在系统运行时就需要去手动配置。

将定时任务与应用分开,运行不同容器。

单应用单容器运行

传统方式是可以将多个应用部署到一套中间件运行环境,但如果多个应用部署到一个容器环境会影响资源争用问题,影响系统性能与系统之间隔离性。

每个容器中只能运行一个应用,不能部署多个应用到一个容器

应用容器中运行单进程

容器中运行多个进程应用会影响系统资源争用,影响问题定位。

运行的容器只跑单个进程

应用容器之间接口调用

服务间接口调用使用非标准协议进行交互(如EJB调用使用t3协议),无法通过负载均衡器进行有效负载

服务间接口调用通过TCP和HTTP协议进行交互,这样可使用负载均衡器进行有效负载

不要依赖IP地址

传统方式部署应用是会有一个独立IP地址,然后注册到LB负载均衡和接口调用地址。但如果部署的容器采用固定IP实现负载就会存在单点故障,因为容器IP地址是随时会变化的,所以注册到LB的地址也要随时变更。

1 )每个容器都有自己的内部IP地址,如果你启动并停止它地址可能会变化。

2 )如果应用或微服务需要与其他容器通讯,由于容器IP是不固定的,应用依赖IP地址将无法与其他容器通讯,可以选择更合适的方式与其他容器进行通讯。

持久化有状态数据

传统方式对接口、附件文件直接存放在服务器,或是FTP服务器,使用都是正常的。但如果将有状态的数据放在容器里面,就会因容器销毁而丢失,因此我们需要将容器里面有状态的数据单独挂出来,以保障数据永久不丢失。

1 )由于容器是一次性的,当容器被停止、销毁或替换时,应用在容器中存储的数据同样会被销毁。

2 )如果应用需要存储数据,需存储在共享数据存储中。

清除不必要的依赖包和文件

容器化镜像打包时间可以将软件依赖包直接以存储方式挂在容器外。不要因导致容器镜像大小越来越大,影响容器快速启动。

容器的一个显著特点是秒级启动,一旦制作了一个超大的镜像将难以分发。就失去了容器的意义了。

不要在镜像中存储凭据、使用环境变量

关于应用容器调用接口服务时应该在打包镜像的时候按规范不能写死IP地址,以免后续数据库地址变更后需要调整镜像。

1 )不要在应用中写死服务的IP和端口(如数据库IP和端口、服务接口IP和端口),采用域名的方式。

2 )环境变量应该以传参的形式传入容器,而不是直接写死在容器中,需要的环境变量可写入系统集成文档中。

3    实际项目容器化经验与总结

以下是我们在某金融项目针对两套系统的应用进行容器化改造后的经验总结:

改造内容

改造子项

DCOS 小组

开发小组

相关文件路径位置

日志持久化

tomcat 日志

在容器/app/bin/startserver.sh中添加行:
  export CATALINA_OPTS="${CATALINA_OPTS} -Dhostname=${HOSTNAME}   -Dmarathon.app.id=${MARATHON_APP_ID}"

1 、更新log4j.xml中value字段,规范如下:
  value="/logs/tomcat/${marathon.app.id}:${hostname}:log4j.log"
 
  2、对应用日志进行切割
  log4j.appendeer.root.layout.conversionPattern=%d(yyy-MM-dd HH:mm:ss) (%-5p)   %c (%F:%L) - %m%n

更改APP的'log4j.properties文件的Value值,文件名改为value="/logs/tomcat/${marathon.app.id}:${hostname}:root.log
  value="/logs/tomcat/${marathon.app.id}:${hostname}:core.log
  value="/logs/tomcat/${marathon.app.id}:${hostname}:torque.log
 
  更改WEB的'log4j.properties文件的Value值,文件名改为value="/logs/tomcat/${marathon.app.id}:${hostname}:spdbcccActivityDY.log
  所有'log4j.properties文件中log4j.appendeer.root.layout.conversionPatter变更为:
  log4j.appendeer.root.layout.conversionPattern=%d(yyy-MM-dd HH:mm:ss) (%-5p)   %c (%F:%L) - %m%n

nginx 日志

在容器内修改配置文件/etc/nginx/nginx.conf中添加日志输出
  error_log  /logs/nginx/error.log error

nginx 代理转发日志文件名:/logs/nginx/error.log

php-fpm 日志

在容器内修改配置文件/etc/opt/remi/php70/php-fpm.conf中添加日志输出error_log = /logs/php-fpm/error.log

php-fpm 代理转发日志文件名: /logs/php-fpm/error.log

会话持久化

php 环境redis

打包redis容器镜像,采用birgde网络协议提供给业务调用
  redis IP预设
  server:redis_ip:6379

1 、涉及到redis应用,将redis信息配置到active_front/suites/config/config.xml文件,信息如下:
  $config['sess_driver'] = 'redis';
  $config['sess_cookie_name'] = 'nlf_front_session';
  $config['sess_expiration'] = 7200;
  $config['sess_save_path'] = '127.0.0.1:6379?auth=Redis@2017';
  $config['sess_match_ip'] = FALSE;
  $config['sess_time_to_update'] = 300;
  $config['sess_regenerate_destroy'] = FALSE;

代码路径:active_front/suites/config/config.xml

tomcat 环境redis

打包redis容器镜像,采用birgde网络协议提供给业务调用
  redis IP预设
  server:redis_ip:6379

1 、涉及到redis应用,将redis信息配置到tomcat\context.Xml文件,信息如下:
   
               maxInactiveInterval="60"
             sentinelMaster="mymaster"   
             sentinels="redis_ip:6379"    />
  2、提供相关文件变更路径及所需变更字段信息
  3、context.xml文件中更改active_front为"/front"

context.xml 文件路径位置:
  context.xml文件中更改active_front为"/front"

持久化有状态数据

接口与附件

通过docker -V数据卷参数将容器中的文件转存到外部共享存储

开发提供接口与附件路径

NFS 共享存储路径:
  /sharefs/cecrm_acitivtycenter/admincert
  容器路径:
  /var/www/cecrm_acitivtycenter/uploads

中间件性能参数

tomcat 参数

将相关配置到容器

提供相关的性能参数

配置文件信息及相对路径:tomcat/conf/
  jdk版本:1.7.0.76
  中间件版本:tomcat 7.0.42

nginx 参数

将相关配置到容器
  server {
      listen       8080;
      server_name  127.0.0.1;
          large_client_header_buffers 4   16k;
          client_max_body_size 300m;
          client_body_buffer_size 128k;
          proxy_connect_timeout 600;
          proxy_read_timeout 600;
          proxy_send_timeout 600;
          proxy_buffer_size 64k;
          proxy_buffers   32 32k;
          proxy_busy_buffers_size 64k;
          proxy_temp_file_write_size 64k;

提供相关的性能参数

配置文件信息及相对路径:
  /etc/nginx/conf.d/website.nginx.conf
  版本号:1.10.3

php 参数

将相关性能参数配置到容器
  sed -e 's/127.0.0.1:9000/9000/' \
  -e '/allowed_clients/d' \
  -e '/catch_workers_output/s/^;//' \
  -e '/error_log/d' \
  -e 's/;listen.backlog = 511/listen.backlog = 1024/' \
  -e 's/pm.max_children = 50/pm.max_children = 300/' \
  -e 's/pm.start_servers = 5/pm.start_servers = 30/' \
  -e 's/pm.min_spare_servers = 5/pm.min_spare_servers = 30/' \
  -e 's/pm.max_spare_servers = 35/pm.max_spare_servers = 60/' \
  -e 's/;pm.max_requests = 500/pm.max_requests = 10240/' \
  -e 's/;request_slowlog_timeout = 0/request_slowlog_timeout = 2/' \
  -e 's/;request_terminate_timeout = 0/request_terminate_timeout = 600/' \
  -e 's/;rlimit_files = 1024/rlimit_files = 65535/' \
  -i    /etc/opt/remi/php70/php-fpm.d/www.conf && \
  sed -e 's/max_execution_time = 30/max_execution_time = 600/' \
  -e 's/max_input_time = 60/max_input_time = 300/' \
  -i /etc/opt/remi/php70/php.ini && \
  sed -e 's/daemonize = yes/daemonize = no/' \
  -e 's/;rlimit_files = 1024/rlimit_files = 65535/' -i   /etc/opt/remi/php70/php-fpm.conf && \

提供相关的性能参数

配置文件信息及相对路径:
  /etc/opt/remi/php70/php-fpm.conf
  /etc/opt/remi/php70/php.ini
  版本号:7.0

应用开发改造

热点数据改造

提供redis镜像包

热点数据缓存放到redis、应用无状态开发、应用上配置的定时任务及应用分离、应用数据存储到共享存储(参考应用改造方案)


 

在实际项目中容器化改造这部分是整个项目中的一个难点,不仅是在应用改造过程会有技术中的一些难点,而最最关键的还是应用开发商配合才能一起顺利完成应用容器化改造,才能正常将应用容器发布到DCOS云平台来进行验收测试。虽然改造过程是反反复复的重复工作,但经过此次项目的经验对容器化应用改造积累下来不少经验,为后续DCOS新项目的开始打下了坚实的基础。

接下来对应用容器化改造做如下几点总结:

1)   建议应用改造优先级可选择,先新系统后到老系统、先小系统后到大系统、先非核心系统后到核心系统。

2)   建议应用改造可选择新系统或非核心又不复杂的系统做试点测试。

3)   建议应用改造可选那些能找得到源代码的程序。

4)   建议应用改造可选那些开发商能主动配合一起改造的系统。

5)   建议应用改造先做原始应用容器化改造交付,后做应用微服务解耦改造。

6)   建议应用改造一定要有详细的改造方案才能实施。

有需要的朋友可以关注我的公众号,文章每日一更


请使用浏览器的分享功能分享到微信等