一、环境
- Java版本:JDK-21
- SpringBoot版本:SpringBoot-3.2.1
- Docker版本:Docker-24.0.7
二、流程
1.使用Maven将SpringBoot项目打包成Jar包
在项目根目录下执行mvn clean package
,则会在项目根目录的target文件夹下生成Jar包。
2.编写Dockerfile
本项目的Dockerfile主要包含以下命令:FROM
, LABEL
, COPY
, EXPOSE
, CMD
。
FROM amazoncorretto:21
LABEL author="LibraStalker"
LABEL version="1.0"
LABEL description="SmartTourism项目,当前版本包含查询景点列表的接口"
COPY . /applications/smart-tourism/
EXPOSE 18180
CMD java -jar /applications/smart-tourism/smart-tourism-0.0.1-SNAPSHOT.jar --spring.config.location=/applications/smart-tourism/conf/application.yml
2.1 FROM命令
FROM
命令指定一个镜像作为构建自定义镜像的基础镜像,在这个基础镜像之上进行修改定制,这个指令也必须是Dockerfile的第一条指令。
由于我的是Java项目,因此应该到DockerHub官网找到Java环境的基础镜像,首先在DockerHub上搜索java
,发现java
的官方基础镜像已经停止维护,应该调整为openjdk
镜像,
然后搜索openjdk
官方镜像,发现openjdk
镜像也被弃用了,
于是按官方的指引,找到amazoncorretto
官方镜像,
然后再Tags
选项栏中搜索21
,找到需要的jdk-21
的基础镜像
这里将使用amazoncorretto:21
作为基础镜像,Dockerfile命令为FROM amazoncorretto:21
。
2.2 LABEL命令
Label
命令使用键值对的方式定义一些元数据信息,比如作者、版本、关于镜像的描述信息。
2.3 COPY命令
COPY
命令有以下两种语法形式:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
COPY
命令主要用于将宿主机内的源路径的内容,复制到容器的目标路径中。
COPY命令和ADD命令的区别
COPY
命令和ADD
命令都能将文件从宿主机复制到容器,ADD
命令支持自动解压缩,但是ADD
命令会使构建镜像时的缓存失效,导致构建镜像的速度比较慢,官方推荐使用COPY
进行文件复制,在需要自动解压缩的场景可以使用ADD
命令。
2.4 EXPOSE命令
EXPOSE
指令通知Docker容器在运行时侦听指定的网络端口。
2.5 CMD命令
CMD
指令用于指定默认的容器主进程的启动命令的,Dockerfile中只能有一条CMD
指令,如果有多条CMD
指令,则只有最后一个CMD
会生效。
CMD
指令有以下两种语法格式:
shell
格式:CMD <命令>
exec
格式:CMD ["可执行文件", "参数1", "参数2"...]
exec
格式需要明确指定shell
路径,否则变量可能不会生效。
CMD命令和RUN命令的区别
Docker中每一条命令都会建立一层镜像,RUN
命令也是会在前面命令构建的镜像的基础上构建新的一层镜像,RUN
命令实际上是运行一个命令并提交结果,可以有多条RUN
命令,而CMD
命令在构建时不执行任何操作,只是指定容器运行时的默认命令,且只有最后一条CMD
命令生效。
CMD命令和ENTRYPOINT命令的区别
ENTRYPOINT
命令和CMD
命令一样,都是指定容器的启动程序及参数,ENTRYPOINT
在运行时也可以被替代,但是需要通过docker run
的参数--entrypoint
来指定替代的命令。
如果没有指定--entrypoint
,docker run
跟在镜像名后面的是command
,运行时会替换CMD
的默认值,而对于ENTRYPOINT
来说是添加在ENTRYPOINT
后面的参数。
ENTRYPOINT
指令不会被RUN
指令覆盖,而CMD
指令会被RUN
指令覆盖。
2.6 VOLUME命令
VOLUME
命令在容器内声明一个位置作为匿名卷,容器中在该位置产生的数据都会持久化到主机中,从而避免容器删除后数据丢失,这个匿名卷可以被其他容器共享,匿名卷是在Docker宿主机的默认目录创建的。
docker run -v 主机目录:容器目录
中的-v
命令是指定容器内的一个位置跟主机的一个位置共享数据,容器内在该位置产生的数据都会持久化到主机目录的对应位置,所以-v
命令可以说是创建了一个具名卷,这个卷在主机上的位置由用户指定,VOLUME
命令创建一个匿名卷,匿名卷在主机的位置是Docker的默认目录。
3.构建Docker镜像
docker build -t <镜像名>:<标签名> .
会使用当前目录的Dockerfile文件构建镜像,如果要指定Dockerfile文件,可以使用docker build -f <dockerfile文件位置> -t <镜像名>:<标签名> .
来构建,注意构建命令最后的.
表示在当前目录构建镜像,不能漏掉。
如果构建镜像过程下载镜像很慢,可以先使用docker pull
命令将镜像拉取到本地,构建时会更快一些,但这样本机镜像也会存在拉取下来的镜像了,构建完镜像后可以使用docker images
命令查看本机的镜像。
4.运行Docker容器
构建好Docker镜像后,应该使用docker create
命令创建容器,然后使用docker start
命令启动容器,而docker run
命令是两者的结合,在创建容器后同时启动容器,参考命令如下:
docker run -d --name <容器名称> -p <主机端口>:<容器端口> -v <主机目录>:<容器目录> <镜像名>:<标签名>
常用参数解释:
--name
:指定容器名称-d
:指定容器以后台方式启动-p
:指定容器和主机的端口映射-v
:指定容器和主机的目录映射-it
:指定以交互方式运行容器-rm
:指定容器退出后立即删除容器
三、问题
1.SpringBoot项目在容器中运行报错Springboot Failed to start bean 'webServerStartStop
SpringBoot项目打包的Jar包在宿主机上运行正常,但是在容器内就报Springboot Failed to start bean 'webServerStartStop
的错误,出现这个错误一般是项目的端口或ip出现问题,而端口问题一般是端口被占用导致项目无法启动,但是我排查了端口没有占用,最后发现我在SpringBoot项目的配置文件中设置了监听ipv6地址"::"
,而Docker引擎默认是不开启ipv6的,需要自己配置ipv6,然后创建ipv6的网络,再使用ipv6的网络运行容器,觉得比较麻烦就修改监听ipv4地址了。
2.Docker容器内的项目怎么访问宿主机上部署的服务
方式一:在docker run
时添加参数--add-host host.docker.internal:host-gateway
,此后在容器中就可以使用host.docker.internal
作为宿主机的别名来访问宿主机上面的服务了。
方式二:使用默认桥接模式访问主机,大多数Docker引擎会将主机表示为默认docker0
桥接网络上的172.17.0.1
,因此可以使用172.17.0.1
来访问宿主机上面的服务。
四、参考
1.Dockerfile相关
https://zhuanlan.zhihu.com/p/114033550
https://blog.csdn.net/lixuankuan/article/details/119643508
https://blog.csdn.net/wucong60/article/details/83993922
https://blog.51cto.com/u_15344989/4374515
https://docs.docker.com/engine/reference/builder/
2.Docker容器相关
https://blog.csdn.net/ruoshuiyx/article/details/128325694
https://blog.csdn.net/jcmj123456/article/details/131692252
https://blog.csdn.net/taiyangdao/article/details/83066009
https://www.cnblogs.com/1446358788-qq/articles/14071650.html