• docker 容器镜像无法重定向日志标准输出到指定文件排错


    背景

    用下面Dockerfile构建了个服务镜像,启动之后发现无论怎么测试都不会生成我们想要的日志文件/opt/service.out

    FROM openjdk:8-jdk-alpine
    COPY service.jar /opt/service.jar
    ENTRYPOINT  ["java", "-Dfile.encoding=utf-8", "-Xms512M","-Xmx1024M","-jar", "/opt/service.jar",">>","/opt/service.out", "2>&1"]
    
    • 1
    • 2
    • 3

    尝试用nohup后台方式启动也只会在根目录下面生成nohup.out文件而不是我们想要的service.out文件nohup 方式如下

    ENTRYPOINT  ["nohup","java", "-Dfile.encoding=utf-8", "-Xms512M","-Xmx1024M","-jar", "/opt/service.jar",">>","/opt/service.out", "2>&1","&"]
    
    • 1

    这个问题让人头大, 有必要深入了解一下了

    解决

    在了解Dockfile 的ShellExec 两种命令方式的区别1 时突然想到会不会是在sh 命令中才能使用重定向功能, 于是将镜像中启动命令改造成下面的 sh -c的方式

    ENTRYPOINT  ["/bin/sh","-c","java", "-Dfile.encoding=utf-8", "-Xms512M","-Xmx1024M","-jar", "/opt/service.jar",">>","/opt/service.out"]
    
    • 1

    进入容器中查看service.out文件能成功生成了, 但是查看进程会发现有两个进程, 其中一个是bash 进程

    root@e6704bc7d50c:/opt# ls
    service.jar  service.out
    
    root@e6704bc7d50c:/opt# ps -ef -w -w
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 22:32 pts/0    00:00:00 /bin/sh -c java  -Dfile.encoding=utf-8  -Xms512M -Xmx1024M -jar  /opt/service.jar  >> /opt/service.out 2>&1
    root         8     1 99 22:32 pts/0    00:00:40 java -Dfile.encoding=utf-8 -Xms512M -Xmx1024M -jar /opt/service.jar 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    bash 进程会负责回收僵尸进程, 但是它有一个比较严重的问题,bash 不会传递信号给它启动的进程,优雅停机等功能无法实现2 对于这种情况可以使用 docker 官方的轻量级 init 系统, 运行命令如下

    docker run -it --init you_docker_image_id
    
    • 1

    这种启动方式会以 /sbin/docker-init 作为 PID 为 1init 进程,不会把 Dockerfile 中 CMD 作为第一个启动进程。例如:

    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 15:30 pts/0    00:00:00 /sbin/docker-init -- /app/node-default
    root         6     1  0 15:30 pts/0    00:00:00 ./signal_test
    
    • 1
    • 2
    • 3

    另外, 如果容器中存在多个进程的话有可能会产生孤儿进程和僵尸进程3

    • 父进程先于子进程退出,那么子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)接管,并由init进程对它完成状态收集(wait/waitpid)工作。
    • 子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程

    如果不得不在容器中使用多个程序进程的话可以考虑使用dumb-init,tini,runit,Monit,Skaware S6,Supervisor等三方进程守护工具4, 详细介绍请参考: 如何在一个Docker中同时运行多个程序进程?

    回到问题本身,如果我只想在我的容器中只保留一个java -jar的进程并且不用sh -c 的方式启动,那我应该怎么做呢?

    答案是:
    springboot日志的--logging.file.name 参数可以指定日志文件,如果是其他日志框架应该也有对应的配置参数

    启动命令修改后:

    ENTRYPOINT  ["java", "-Dfile.encoding=utf-8", "-Xms512M","-Xmx1024M","-jar", "/opt/service.jar","--logging.file.name=/opt/service.out"]
    
    • 1

    启动后查看容器只有一个java -jar进程

    root@d048b0c9df8e:/opt# ps -ef -w -w
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0 99 23:27 pts/0    00:01:15 java -Dfile.encoding=utf-8 -Xms512M -Xmx1024M -jar /opt/service.jar  --logging.file.name=/opt/service.out
    
    • 1
    • 2
    • 3

    参考


    1. Dockerfile: ENTRYPOINT和CMD的区别 ↩︎

    2. 一次 Docker 容器内大量僵尸进程排查分析 ↩︎

    3. Docker和孤儿进程、僵尸进程 ↩︎

    4. 如何在一个Docker中同时运行多个程序进程? ↩︎

  • 相关阅读:
    设计模式--单例模式
    Hadoop
    【轻量应用服务器】k3s部署mysql
    LinkedIn技巧-领英怎么只给选择的好友群发消息?
    【无标题】
    数据结构 -作用及基本概念
    第二章 开发一个Vue组件
    JAVA注解-Async原理解析
    Echarts:双折线图的值一样,高度却不一样
    毕业设计-基于微信小程序的智能垃圾分类回收系统
  • 原文地址:https://blog.csdn.net/qq_26545503/article/details/126695376