SaltStack 用 cmd.run 启动的 java 服务在
问题现象
线上有一个用 tomcat 启动的服务 web-xxx,这个服务有个功能是在 web 端提供下载图片功能。
下载图片功能
用户在界面上点击【打包下载全部图片】后,看到的 zip 文件里目录名和文件名都是乱码。
目录名和文件名都是乱码
原因分析
web-xxx 的逻辑是:下载图片文件时 --> 创建一个临时目录 --> 保存图片文件到这个临时目录 --> 压缩打包临时目录为 zip 文件 --> 上传 zip 文件到文件系统 --> 返回文件系统的 zip 文件地址给前端 --> 前端下载 zip 文件。
怀疑是编码不一致导致的,考虑从以下方面排查:
- web-xxx 服务创建目录和文件时的编码
- tomcat 编码
- linux 系统编码
- linux 系统字体
1、排查 web-xxx 服务创建目录和文件时的编码,和开发确认,为 UTF-8。
2、排查 tomcat 编码,为 UTF-8。
// catalina.sh 文件里的 JAVA_OPTS 配置
JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS -Dfile.encoding=UTF-8"
3、排查系统编码,为 en_US.UTF-8。
# locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
4、 排查 linux 系统字体也没问题,常见中英文字体都有。
# fc-list
/usr/share/fonts/lyx/eufm10.ttf: eufm10:style=LyX
/usr/share/fonts/lyx/cmsy10.ttf: cmsy10:style=LyX
/usr/share/fonts/lyx/msam10.ttf: msam10:style=LyX
/usr/share/fonts/foobar/simsun.ttc: SimSun,宋体:style=Regular,常规
省略其他输出……
5、排查启动方式: 在命令行终端直接用 startup.sh 启动 tomcat
和 saltstack cmd.run 命令远程执行 startup.sh 启动 tomcat
-
在命令行终端直接用 startup.sh 启动 tomcat
,下载的 zip 压缩文件里没有出现目录名和文件名乱码问题 -
saltstack cmd.run 命令远程执行 startup.sh 启动的 tomcat
,下载的 zip 压缩文件里出现了目录名和文件名乱码问题
这里就定位到是 saltstack cmd.run 的问题了,如果系统不设置 LC_ALL 的值,cmd.run 模块启动 web-xxx 服务时的 locale 就会被修改配置为 C,而 web-xxx 服务创建目录和文件时的编码、tomcat 编码、linux 系统编码都是 UTF-8,这就导致了 zip 压缩文件里出现了目录名和文件名乱码问题。
// saltstack cmd.run 模块中的代码实现
# vim /usr/lib/python2.7/site-packages/salt/modules/cmdmod.py
if reset_system_locale is True:
if not salt.utils.platform.is_windows():
# Default to C!
# Salt only knows how to parse English words
# Don't override if the user has passed LC_ALL
env.setdefault('LC_CTYPE', 'C')
env.setdefault('LC_NUMERIC', 'C')
env.setdefault('LC_TIME', 'C')
env.setdefault('LC_COLLATE', 'C')
env.setdefault('LC_MONETARY', 'C')
env.setdefault('LC_MESSAGES', 'C')
env.setdefault('LC_PAPER', 'C')
env.setdefault('LC_NAME', 'C')
env.setdefault('LC_ADDRESS', 'C')
env.setdefault('LC_TELEPHONE', 'C')
env.setdefault('LC_MEASUREMENT', 'C')
env.setdefault('LC_IDENTIFICATION', 'C')
env.setdefault('LANGUAGE', 'C')
else:
# On Windows set the codepage to US English.
if python_shell:
cmd = 'chcp 437 > nul & ' + cmd
解决方法
1、设置 reset_system_local 为 False,用 saltstack cmd.run 远程启动 tomcat,问题没有解决,下载的 zip 压缩文件目录名和文件名还是乱码
cmd.run:
- name: /usr/local/tomcat/bin/startup.sh
- reset_system_local: False
2、设置 LC_ALL 为空,用 saltstack cmd.run 远程启动 tomcat,问题没有解决,下载的 zip 压缩文件目录名和文件名还是乱码
cmd.run:
- name: /usr/local/tomcat/bin/startup.sh
- env:
- LC_ALL: ""
4、设置 linux 系统的 LC_ALL 为 en_US.UTF-8,startup.sh 加上 . /etc/profile
用 saltstack cmd.run 远程启动 tomcat,问题解决,下载的 zip 压缩文件里没有出现目录名和文件名乱码问题。
# vim /etc/profile.d/locale.sh
export LC_ALL=en_US.UTF-8
# source /etc/profile.d/locale.sh
# vim /usr/local/tomcat/bin/startup.sh
#!/bin/sh
. /etc/profile
省略其他输出……
目录名和文件名都正常显示了
参考:
1、How salt cmd.run Works with OS ENV:https://github.com/saltstack/salt/issues/19776
2、I think salt is no need to change locale when exec cmd.run:https://github.com/saltstack/salt/issues/7190