下面是在工作过遇到的一些实际例子,谨以此作为笔记参考
目录
1.背景
- 部署(迁移)项目时发现,项目的excel导出功能报错,错误如下:
问题原因:我们这个项目依赖的镜像上的jdk缺少某种字体程序导致!
Caused by: java.lang.NullPointerException: null at java.desktop/sun.awt.FontConfiguration.getVersion(Unknown Source) at java.desktop/sun.awt.FontConfiguration.readFontConfigFile(Unknown Source) at java.desktop/sun.awt.FontConfiguration.init(Unknown Source) at java.desktop/sun.awt.X11FontManager.createFontConfiguration(Unknown Source) at java.desktop/sun.font.SunFontManager$2.run(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.desktop/sun.font.SunFontManager.<init>(Unknown Source) at java.desktop/sun.awt.FcFontManager.<init>(Unknown Source) at java.desktop/sun.awt.X11FontManager.<init>(Unknown Source) ... 174 common frames omitted
或者含FontConfiguration
的报错
java.lang.NullPointerException: null at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) at sun.awt.FontConfiguration.init(FontConfiguration.java:107) at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) at sun.font.SunFontManager$2.run(SunFontManager.java:431) at java.security.AccessController.doPrivileged(Native Method) at sun
- 或者我们服务器上镜像的jdk版本是jdk8,而我们的项目需要jdk17
如下图:使用docker images命令
查看
2. 寻找方案
我按照网上步骤一直给docker容器内部使用命令安装字体等操作,类似于如下命令:yum install fontconfig
fc-cache --force
亦或是下面这个
以下为解决步骤: yum -y install fontconfig 在 /usr/share 下多出 fontconfig 和 fonts 目录。 yum -y install zstd 下载字体 ttf-dejavu: Package: mingw-w64-x86_64-ttf-dejavu - MSYS2 Packages tar -I zstd -xvf mingw-w64-x86_64-ttf-dejavu-2.37-3-any.pkg.tar.zst cp mingw64/share/fonts/TTF/* /usr/share/fonts/ fc-cache --force fc-list 重启服务进程 PS:另外一种方案是将SXSSFWorkbook替换成HSSFWorkbook
但是始终没有解决我的问题(ps:以上命令不要轻易在生产服务器尝试)
3. 如何解决
首先要搞清楚的一点就是:(仅针对本次案例)我们是使用的docker-compose.yml
文件来进行服务配置的,(包括数据库、redis、mq等)。我们部署在docker服务器上的web程序启动需要jdk,就好比在本地idea启动需要提前配置好jdk环境一样,程序才可以启动起来。
以下是个人理解:(可以说服务器上存在了三种jdk)
- docker服务器本身的jdk(可使用
docker java -version
命令查看) - 容器在打包前依赖的jdk
- 容器依赖镜像上配置的jdk
以上三种jdk的关系:
- docker服务器本身的jdk:几乎用不到,甚至可以不要
- 容器在打包前依赖的jdk:决定了你要这个程序在其他机器(服务器)启动时,也要依赖这个jdk
- 容器依赖镜像上配置的jdk:就是我们程序启动时需要依赖的jdk(且这个jdk和docker服务器上本身的jdk没有任何关系)
简而言之就是web服务器依赖的jdk是在dokcer镜像里面;
讲清楚概念后,就好理解了,那么我们需要做的就是:
- 1.要么重新生成一个镜像使得该镜像2有我们需要的字体程序;
- 2.要么给我们的web程序之前依赖的镜像1上添加字体程序配置;
我在这选择了方法1,因为镜像1可能也被其他容器(服务)正在使用依赖,若我们擅自修改配置,可能造成其他服务短暂的崩溃或者宕机。
4.解决步骤
我们使用的是DockerFile
文件来进行镜像生成的,大概就长下面这样:
(可以自己去网上找关于DockerFile
文件里面的配置项都是什么意思?怎么写的)
FROM openjdk:8-jre-alpine ENV PROJECT_HOME /home/project ENV FILE_NAME placeholder ENV EXPOSE_PORT 8080 ENV ACTIVE_ENV dev ENV JAVA_OPTS "" RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' >/etc/timezone RUN apk add --update ttf-dejavu fontconfig && rm -rf /var/cache/apk/* RUN set -x && \ mkdir -p /root/logs/$FILE_NAME && \ mkdir -p $PROJECT_HOME WORKDIR $PROJECT_HOME ENTRYPOINT [ "/bin/sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar $PROJECT_HOME/$FILE_NAME.jar --spring.profiles.active=$ACTIVE_ENV" ] EXPOSE $EXPOSE_PORT
其中RUN apk add --update ttf-dejavu fontconfig && rm -rf /var/cache/apk/*
就是我需要的字体程序配置。
4.1 DockerFile
docker镜像是利用DockerFile文件生成的;
关于DockerFile :
- 使用dockerFile文件可生产我们程序需要的jdk版本;同时还可以在里面进行相应的字体程序的添加;也就是生成需要依赖的jdk和字体程序镜像;
- Dockerfile是一个包含用于组合映像的命令的文本文档, Docker通过读取Dockerfile中的指令自动生成映像。
在服务器上随便一个路径建一个文件:名称.Dockerfile,然后进去这个路径后,使用docker build 命令生成该镜像
4.2 现在要做的
最好就在我们的web程序目录下建一个该文件,比如名字为:app.DockerFile;
把我们配置好的代码片粘进去app.DockerFile文件;
使用
docker build -t java-app . -f app.Dockerfile
命令去生成该镜像- 其中
java-app
是你为这个镜像起的名字,不要和我们docker已有的镜像名重复即可 - 不要忘记后面的
.
- 这个命令是已经在
app.DockerFile
文件所在目录下执行的,若是非该目录,需要在后面加上其路径(还可以为我们的镜像使用标签tag,这样就保证了镜像的唯一性,即使名字重复也可以找到可以自己去了解docker生成镜像的步骤)
- 其中
然后我们只需要等待镜像安装下载完毕,然后使用
docker images
命令查看我们的镜像是否已在列表即可(比如我起名的镜像为java-app-font
):最后在我们的
docker-compose.yml
文件里面修改我们当前web程序(容器)所依赖的镜像名称,然后重启一下我们的docker-compose.yml
文件即可
(也别忘记重启你的web服务)
image: java-app-font重启compose命令
docker-compose --compatibility up -d
移除compose配置命令
docker-compose --compatibility down
5. 镜像相关命令
关于docker镜像使用,包括
- 查看本宿主机镜像列表:docker images
- 删除镜像:docker rmi 镜像名
- 构建镜像:docker build
具体的介绍可以移步阿里的菜鸟教程学习:Docker 镜像使用