首页 > 其他分享 >Springboot项目镜像制作&传递环境变量、设置hostname、动态设置JVM参数、cmd&entrypoint区别

Springboot项目镜像制作&传递环境变量、设置hostname、动态设置JVM参数、cmd&entrypoint区别

时间:2024-05-09 23:01:13浏览次数:31  
标签:Springboot app cmd jar sh entrypoint 设置 docker 8081

实现制作一个springboot 的镜像,并且可以传递环境变量实现动态JVM参数和端口。

0. 准备 & cmd、entrypoint 区别

1. 准备

  1. springboot 项目

一个简单的springboot 项目,默认启动8001 端口,里面只有一个接口。

xxx % curl localhost:8081
index
  1. docker 环境

2. CMD、entrypoint 区别

在Docker中,CMD和ENTRYPOINT都是Dockerfile中用于指定容器启动时运行的命令,但它们之间有重要的区别:
CMD:
用途:CMD是可选的,它提供了一个默认的命令或参数集,可以被docker run命令中的参数覆盖。
执行方式:CMD可以以两种形式存在:
Shell形式:CMD command param1 param2 ...(内部使用/bin/sh -c 运行)
Exec形式:CMD ["executable", "param1", "param2"](不依赖shell,直接执行)
覆盖性:当使用docker run命令时,如果提供了命令,CMD的内容会被替换。

ENTRYPOINT:
用途:ENTRYPOINT是强制性的,它定义了一个容器启动时的入口点,类似于一个固定前缀,不会被docker run命令中的参数覆盖,而是将这些参数附加到ENTRYPOINT后面。
执行方式:ENTRYPOINT也支持两种形式,但通常推荐使用Exec形式,因为它更稳定且不依赖于shell。
Shell形式:ENTRYPOINT command param1 param2 ...(内部使用/bin/sh -c 运行)
Exec形式:ENTRYPOINT ["executable", "param1", "param2"]
组合性:即使docker run提供了命令,这些命令也会作为参数传递给ENTRYPOINT,而不是替换它。

举例来说,如果你有一个Dockerfile,其中设置了ENTRYPOINT ["java", "-jar", "app.jar"],然后运行docker run myimage arg1 arg2,容器将会启动并执行java -jar app.jar arg1 arg2。而如果只有CMD,docker run命令中的参数会替换CMD,不再是默认命令。
总结来说,ENTRYPOINT更像是容器的固定启动脚本,而CMD提供了默认参数,这两者结合使用可以创建出灵活且可配置的容器启动行为。

如果同一个Dockerfile 写了多个CMD或者ENTRYPOINT, 只有最后一个生效。

1. 第一版本: 构建基础能跑的

1. 编写dockerfile

# 使用官方的Java 8运行时作为基础镜像
FROM openjdk:8-jdk-alpine

# 维护者信息
LABEL maintainer="[email protected]"

# 设置工作目录
WORKDIR /app

# 将本地的JAR文件复制到容器中的工作目录
COPY app.jar /app/app.jar

# 暴露应用的端口,假设应用默认监听8081端口
EXPOSE 8081

# 使用exec形式的ENTRYPOINT以获得更好的信号处理能力
ENTRYPOINT ["/usr/bin/java", "-jar", "/app/app.jar"]

# 或者直接使用CMD,根据个人偏好和需求选择
# CMD ["java", "-jar", "app.jar"]

2. 构建和运行

xxx % docker build -t qlq_app:latest .
...

xxx % docker run -d -p 8081:8081 qlq_app
9c31ed3c0b3777a6fea2c855d1b8654e5dc949bc957f990d27a315b00384db32

2. 第二版本:增加一个sh 脚本验证cmd、entrypoint 区别

1. 操作-cmd

  1. 准备docker-entrypoint, 内容如下:
#!/bin/bash

# 定义日志文件路径
LOG_FILE="/app/app.log"

echo 'start~~~' >> "$LOG_FILE"

# 使用追加模式(>>)将所有参数输出到日志文件
echo "Parameters passed to the script: $*" >> "$LOG_FILE"
echo "Each parameter on a new line:" >> "$LOG_FILE"
for arg in "$@"; do
    echo "$arg" >> "$LOG_FILE"
done

echo 'end~~~' >> "$LOG_FILE"

/usr/bin/java -jar /app/app.jar
  1. 编写Dockerfile
# 使用官方的Java 8运行时作为基础镜像
FROM openjdk:8-jdk-alpine

# 维护者信息
LABEL maintainer="[email protected]"

# 设置工作目录
WORKDIR /app

# 将本地的文件复制到容器中的工作目录
COPY app.jar /app/app.jar
COPY docker-entrypoint.sh /app/docker-entrypoint.sh

RUN chmod +x /app/docker-entrypoint.sh

# 改用root启动
USER root

# 使用exec形式的ENTRYPOINT以获得更好的信号处理能力
# ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"]

# 或者直接使用CMD,根据个人偏好和需求选择
CMD ["/bin/sh", "/app/docker-entrypoint.sh"]
  1. 构建后执行-正常执行启动
# 构建
docker build -t app_cmd:latest .

# 启动
docker run -d -p 8081:8081 app_cmd
05f08bf21e7df4852b8e4bb146503280ec1f7fbbe1a3bf051883b0bd084ab022

# 测试
xxx % docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED         STATUS         PORTS                                                      NAMES
05f08bf21e7d   app_cmd                  "/bin/sh /app/docker…"   4 seconds ago   Up 3 seconds   0.0.0.0:8081->8081/tcp                                     pedantic_merkle
17d8e50240b9   portainer/portainer-ce   "/portainer"              6 weeks ago     Up 4 hours     0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp, 9443/tcp   portainer
xxx % curl localhost:8081
index%
# 进入容器查看日志
/app # cat app.log
start~~~
Parameters passed to the script:
Each parameter on a new line:
end~~~
  1. 尝试启动传递其他命令
# 启动传递其他命令
xxx % docker run -d -p 8081:8081 app_cmd sh -c "cat /app/app.log"
bff4d7c8144db99664fb46f4c04fe1d2b8e448e8d1675dbdae77ebac61459f44
# 查看容器日志
xxx % docker logs bff4d7c8144db99664fb46f4c04fe1d2b8e448e8d1675dbdae77ebac61459f44
cat: can't open '/app/app.log': No such file or directory
# 查看容器,可以看到。 命令被覆盖, 也就是Dockerfile CMD 指定的命令没有执行,被自己的sh -c "cat /app/app.log" 覆盖
xxx % docker ps -a
CONTAINER ID   IMAGE                         COMMAND                   CREATED          STATUS                      PORTS                                                      NAMES
bff4d7c8144d   app_cmd                       "sh -c 'cat /app/app…"   11 seconds ago   Exited (1) 10 seconds ago                                                              interesting_agnesi

2. 操作-entrypoint

  1. 操作都相同,只是Dockerfile 启动缓存entrypoint
# 使用官方的Java 8运行时作为基础镜像
FROM openjdk:8-jdk-alpine

# 维护者信息
LABEL maintainer="[email protected]"

# 设置工作目录
WORKDIR /app

# 将本地的文件复制到容器中的工作目录
COPY app.jar /app/app.jar
COPY docker-entrypoint.sh /app/docker-entrypoint.sh

RUN chmod +x /app/docker-entrypoint.sh

# 改用root启动
USER root

# 使用exec形式的ENTRYPOINT以获得更好的信号处理能力
ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"]
  1. 正常启动-没问题
  2. 尝试启动传递其他命令
# 启动
xxx % docker run -d -p 8081:8081 app_entry sh -c "cat /app/app.log"
d644cb6be72757959296d97b56588d14c3aa6206f6eb2bfced2c2ff683fc4818
# 查看容器状态
xxx % docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED         STATUS         PORTS                                                      NAMES
d644cb6be727   app_entry                "/bin/sh /app/docker…"   3 seconds ago   Up 2 seconds   0.0.0.0:8081->8081/tcp                                     gracious_proskuriakova
# 进容器查看日志
/app # cat app.log
start~~~
Parameters passed to the script: sh -c cat /app/app.log
Each parameter on a new line:
sh
-c
cat /app/app.log
end~~~

3. 两者结合

  1. Dockerfile
# 使用官方的Java 8运行时作为基础镜像
FROM openjdk:8-jdk-alpine

# 维护者信息
LABEL maintainer="[email protected]"

# 设置工作目录
WORKDIR /app

# 将本地的文件复制到容器中的工作目录
COPY app.jar /app/app.jar
COPY docker-entrypoint.sh /app/docker-entrypoint.sh

RUN chmod +x /app/docker-entrypoint.sh

# 改用root启动
USER root

# 使用exec形式的ENTRYPOINT以获得更好的信号处理能力
ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"]
CMD ["default", "argument"]
  1. 构建
docker build -t app_entry_cmd:latest .
  1. 正常执行然后查看容器内日志 (可以看到cmd 作为 参数传进去了)
# 执行
docker run -d -p 8081:8081 app_entry_cmd
# 容器内查看日志
/app # cat app.log
start~~~
Parameters passed to the script: default argument
Each parameter on a new line:
default
argument
end~~~
  1. 增加外部参数调用(可以看到覆盖默认的cmd参数)
# 执行
docker run -d -p 8081:8081 app_entry_cmd arg1 arg2 arg3

# 容器内查看日志
/app # cat app.log
start~~~
Parameters passed to the script: arg1 arg2 arg3
Each parameter on a new line:
arg1
arg2
arg3
end~~~

4. 结论

容器的启动脚本可以用cmd,也可以用entrypoint, 也可以两者结合。

外部命令:(sh -c "cat /app/app.log" 可以理解为自己定义的新命令)

docker run -d -p 8081:8081 app_entry sh -c "cat /app/app.log"

如果是cmd,外部的命令会替换cmd 指定的脚本,也就是cmd 指定的将不会执行;

如果是 entrypoint :外部命令会作为参数传递给自己的entrypoint 命令;

也可以两者结合,两者结合的情况下:cmd 会作为参数传递给entrypoint的程序,如果有外部命令,外部命令会覆盖cmd然后继续传给entrypoint 作为参数。

3. 终版-传递环境变量、设置hostsname,脚本动态调整启动服务参数

1. 设置环境变量启动java 程序

  1. Dockerfile
# 使用官方的Java 8运行时作为基础镜像
FROM openjdk:8-jdk-alpine

# 维护者信息
LABEL maintainer="[email protected]"

# 设置工作目录
WORKDIR /app

# 将本地的文件复制到容器中的工作目录
COPY app.jar /app/app.jar
COPY docker-entrypoint.sh /app/docker-entrypoint.sh

RUN chmod +x /app/docker-entrypoint.sh

# 改用root启动
USER root

# 使用exec形式的ENTRYPOINT以获得更好的信号处理能力
ENTRYPOINT ["/bin/sh", "/app/docker-entrypoint.sh"]
  1. docker-entrypoint.sh

读取环境变量,然后设置JVM 参数

#!/bin/bash

# 定义日志文件路径
LOG_FILE="/app/app.log"
echo 'start~~~' >> "$LOG_FILE"

# 1. 处理默认参数
# 使用追加模式(>>)将所有参数输出到日志文件
echo "Parameters passed to the script: $*" >> "$LOG_FILE"
echo "Each parameter on a new line:" >> "$LOG_FILE"
for arg in "$@"; do
    echo "$arg" >> "$LOG_FILE"
done

echo 'end~~~' >> "$LOG_FILE"

# 2. 处理java 相关参数
# 2.1 默认的Java选项
DEFAULT_JAVA_OPTS="-Xms256m -Xmx256g"
# 检查JAVA_OPTS环境变量是否存在
if [ -z "${JAVA_OPTS}" ]; then
    echo "JAVA_OPTS is not set. Using default settings: ${DEFAULT_JAVA_OPTS}" >> "$LOG_FILE"
    JAVA_OPTS="${DEFAULT_JAVA_OPTS}"
else
    echo "JAVA_OPTS is set to: ${JAVA_OPTS}" >> "$LOG_FILE"
fi
# 2.2 默认的端口
DEFAULT_SERVER_PORT='8081'
if [ -z "${SERVER_PORT}" ]; then
    echo "SERVER_PORT is not set. Using default SERVER_PORT: ${DEFAULT_SERVER_PORT}" >> "$LOG_FILE"
    SERVER_PORT="${DEFAULT_SERVER_PORT}"
else
    echo "SERVER_PORT is set to: ${SERVER_PORT}" >> "$LOG_FILE"
fi

# 执行Java应用,使用最终确定的信息
/usr/bin/java -jar ${JAVA_OPTS} -Dserver.port=${SERVER_PORT} /app/app.jar
  1. 打包运行测试
# build
docker build -t app:latest .

# run 默认设置
xxx % docker run -d -p 8081:8081 app
aed44593836eca3e3f5e727bfe5abe8024214d8528311634158fb9a3b458c85d
xxx % docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED         STATUS         PORTS                                                      NAMES
aed44593836e   app                      "/bin/sh /app/docker…"   3 seconds ago   Up 2 seconds   0.0.0.0:8081->8081/tcp                                     confident_ardinghelli
xxx % curl localhost:8081
index
# 查看启动日志
/app # cat app.log
start~~~
Parameters passed to the script:
Each parameter on a new line:
end~~~
JAVA_OPTS is not set. Using default settings: -Xms256m -Xmx256g
SERVER_PORT is not set. Using default SERVER_PORT: 8081
/app # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh /app/docker-entrypoint.sh
    7 root      0:10 /usr/bin/java -jar -Xms256m -Xmx256g -Dserver.port=8081 /app/app.jar
   52 root      0:00 sh
   69 root      0:00 ps -ef
/app # jps -lv
7 /app/app.jar -Xms256m -Xmx256g -Dserver.port=8081
90 sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/java-1.8-openjdk -Xms8m
  1. 自己设置环境变量、主机名称然后启动
# 启动
xxx tmp % docker run -d -p 8082:8082 \
    -e JAVA_OPTS="-Xmx100m -Xms100m" \
    -e SERVER_PORT=8082 \
    --hostname 8082host \
    app
9f2c71511097005bc523db236ce75b7952fac52470afb6d5cce592ebd10a0f2c
xxx tmp % docker ps
CONTAINER ID   IMAGE                    COMMAND                   CREATED         STATUS         PORTS                                                      NAMES
9f2c71511097   app                      "/bin/sh /app/docker…"   3 seconds ago   Up 3 seconds   0.0.0.0:8082->8082/tcp                                     beautiful_kilby
17d8e50240b9   portainer/portainer-ce   "/portainer"              6 weeks ago     Up 5 hours     0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp, 9443/tcp   portainer
xxx tmp % curl localhost:8082
index
# 外部查看容器传递的环境变量
docker inspect 9f2c71511097 

# 容器内部查看信息
xxx tmp % docker exec -it 9f2c71511097 sh
# 启动日志
/app # cat app.log
start~~~
Parameters passed to the script:
Each parameter on a new line:
end~~~
JAVA_OPTS is set to: -Xmx100m -Xms100m
SERVER_PORT is set to: 8082
# java 进程信息
/app # jps -lv
64 sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/java-1.8-openjdk -Xms8m
7 /app/app.jar -Xmx100m -Xms100m -Dserver.port=8082
# 环境变量
/app # env
JAVA_ALPINE_VERSION=8.212.04-r0
HOSTNAME=8082host
SHLVL=1
HOME=/root
JAVA_VERSION=8u212
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin
JAVA_OPTS=-Xmx100m -Xms100m
LANG=C.UTF-8
JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk
PWD=/app
SERVER_PORT=8082
# 主机名称
/app # hostname
8082host

2. 设置网络,多个容器可以互相访问

1. 创建桥接网络

1. 创建自定义网络: 首先,使用 docker network create 命令创建一个自定义的Docker网络。例如,创建一个名为 my_network 的桥接网络:
# 创建
docker network create my_network

# 查看
xxx tmp % docker network ls
NETWORK ID     NAME         DRIVER    SCOPE
8668a26df163   bridge       bridge    local
1135abf81b7e   host         host      local
d462de8fcc37   my_network   bridge    local
2357d5b05bcd   none         null      local

# 查看详细信息
xxx tmp %  docker network inspect my_network
[
    {
        "Name": "my_network",
        "Id": "d462de8fcc3734136d0ba33231de22123a98091c2d2bc7e10b1cbb938c92fe4a",
        "Created": "2024-05-09T08:30:19.530915045Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

2. 启动两个容器&测试

  1. 启动
docker run -d -p 8082:8082 \
    -e JAVA_OPTS="-Xmx100m -Xms100m" \
    -e SERVER_PORT=8082 \
    --hostname 8082host \
    --network my_network \
    app
    
docker run -d -p 8083:8083 \
    -e JAVA_OPTS="-Xmx100m -Xms100m" \
    -e SERVER_PORT=8083 \
    --hostname 8083host \
    --network my_network \
    app    
  1. 测试
1. 查看
xxx tmp % docker ps | grep app
4febe1c700fd   app                      "/bin/sh /app/docker…"   13 seconds ago   Up 13 seconds   0.0.0.0:8083->8083/tcp                                     stupefied_hamilton
8667b58f2c43   app                      "/bin/sh /app/docker…"   50 seconds ago   Up 49 seconds   0.0.0.0:8082->8082/tcp                                     frosty_chebyshev
2. 查看容器详细信息
docker inspect 4febe1c700fd
# 输出
...
"Networks": {
                "my_network": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "4febe1c700fd",
                        "8083host"
                    ],
                    "MacAddress": "02:42:ac:12:00:03",
                    "NetworkID": "d462de8fcc3734136d0ba33231de22123a98091c2d2bc7e10b1cbb938c92fe4a",
                    "EndpointID": "b6118332fc47744d6321f07da2d289002fe28cd20c0126827d3d85d96ae96582",
                    "Gateway": "172.18.0.1",
                    "IPAddr
...
3. 容器内部ping
可以用主机名称互相ping 通,证明可以访问到
ping 8082host
ping 8083host

标签:Springboot,app,cmd,jar,sh,entrypoint,设置,docker,8081
From: https://www.cnblogs.com/qlqwjy/p/18183265

相关文章

  • springboot3.2.3如何集成swagger
    在SpringBoot中集成Swagger可以方便地生成API文档并进行接口测试。要在SpringBoot3.2.3中集成Swagger,你可以按照以下步骤进行操作:1.添加Swagger依赖到pom.xml文件中:点击查看代码<dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter<......
  • openfeign接口Springboot启动Bean报错未找到Singleton bean creation not allowed whi
    检查步骤检查springboot启动类是否标注@EnableFeignClients注解,未标注该注解会导致无法注入bean检查远程调用模块是否标注注解@FeignClient检查@FeignClient注解中是否写了正确的微服务名称(区分大小写)检查@FeignClient注解中标识的微服务是否启动​​原因:此处接......
  • springboot+vue项目
    1MyBatisPlus的分页插件是怎么生效的?体现在哪里?PaginationInnerInterceptor是通过拦截数据库操作来实现分页功能的。 MyBatisPlus的分页插件PaginationInnerInterceptor是通过拦截数据库操作来实现分页功能的。它的工作原理如下:配置分页插件:在你的SpringBoot应用......
  • SpringBoot随手笔记
    SpringBoot随手笔记0关于火狐浏览器什么时候会发出http请求的说明在抓包的情况下(按下F12后的模式),不管是刷新页面还是在浏览器地址栏回车,该页面中的图片都会发出http请求;但如果不是抓包的模式下,如果访问的页面和上一次访问的页面相同(地址栏的地址没有更改),不管是刷新页面还......
  • springboot seata 全局捕获异常失效
    问题:Springboot使用@ControllerAdvice或@RestControllerAdvice全局捕获异常时,捕获不到自己抛出的相应异常首先看一下全局异常组件有么有被扫描到如何查看,很简单只需要写一段类加载打印代码,如下 如果启动时,打印了你写的字符串就说明时烧苗到了 这就说明是其他的问题了,那就......
  • Springboot项目的jar包的运行方式以及使用yum安装java后忘记了位置
    SpringBoot项目打包后的jar的部署方式这里我写了五种部署方式1.直接启动java-jarxxx.jar这种方式就只适合自己在测试时用一下,关闭会话就能停止运行属实是方便。2.后台启动java-jarxxx.jar&在后台静默启动,同样关闭会话也会停止,优点是和上面一样,日志是打印在窗口的3......
  • Springboot - [06] yaml语法讲解
    Yaml是一种标记语言,Yaml也不是一种标记语言。 一、yaml写法示例application.yaml#普通的key-valuename:harleyserver.port:8081#对象student:name:harleyage:25#对象的行内写法student:{name:harley,age:25}#数组pets:-cat-......
  • SpringBoot - [04] 自动装配原理
    题记部分   SpringBoot的自动装配(Auto-Configuration)原理是其简化Spring应用开发的关键特性之一,它能自动配置Spring框架及第三方库,极大地减少了手动配置的工作量。以下是SpringBoot自动装配的核心原理和步骤:(1)条件化配置(ConditionalConfiguration):  SpringBoot利用Spr......
  • Springboot - [05] 彩蛋~
    题记部分  彩蛋一:如何更换Springboot启动时的logo(1)访问https://www.bootschool.net/ascii-art/search,搜索到佛祖的ASCII艺术字(图)集(2)将内容拷贝到src/main/resources下的文件中,文件名称为banner.txt(3)启动SpringBoot的应用  彩蛋二:  彩蛋三:   —要......
  • 优化cmd中,查询表字段过长情况下的展示效果
    当我们遇到table字段比较多,cmd无法在一行内展示所有字段的情况时可以切换查询语句的结束格式:由以分号;结尾select*fromtable;切换为以/G结尾select*fromtable/G可以切换table的展示格式为以竖列的形式展示一行一行的数据......