将jar包打成docker镜像并部署在Linux上

3 篇文章 0 订阅
2 篇文章 0 订阅

1. 准备工作

  • 本次演示使用的Linux版本为CentOS 7.6,Java项目为SpringBoot项目,使用的jdk版本为1.8
  • 为方便起见,本次演示统一使用root用户执行所有指令

1.1 在Linux上安装docker

安装docker有其它更简单的方法,例如利用宝塔一键式安装

考虑到有些小伙伴可能没有使用过宝塔,所以本次演示使用指令的方式安装docker

1.1.1 卸载原有的docker(可选)

如果你的Linux系统安装过旧版本的Docker,可以使用下面命令卸载

补充知识:当一条命令太长而需要分成多行输入时,可以在行尾使用反斜杠,告诉Shell下一行指令实际上是当前命令的延续,而不是一个新的命令

yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine \
                  docker-ce

1.1.2 安装docker

第一步:安装yum工具

yum install -y yum-utils \
           device-mapper-persistent-data \
           lvm2 --skip-broken

第二步:更新镜像源

# 设置docker镜像源
yum-config-manager \
    --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo

yum makecache fast

指令的详细解释:

  • sed: 流编辑器的命令
  • -i: 选项告诉 sed 直接修改源文件。通常 sed 默认只输出修改后的内容到标准输出,而不修改源文件。使用 -i 选项后,它会直接修改源文件内容
  • 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g': 这是 sed 的替换命令
    • s: 表示执行替换操作
    • download.docker.com: 被替换的字符串,即 Docker 官方仓库的 URL
    • mirrors.aliyun.com/docker-ce: 替换后的字符串,即阿里云提供的 Docker CE(Community Edition)镜像仓库的 URL。注意,由于 /sed 命令中是特殊字符,所以使用 \/ 来表示字面意义上的 /
    • g: 全局替换标志,表示替换每一行中所有匹配的字符串,而不是只替换第一个匹配的字符串
  • /etc/yum.repos.d/docker-ce.repo: 这是 sed 命令要操作的文件路径。这个文件是 Docker CE 的 Yum 仓库配置文件,其中包含了 Docker 软件包的仓库地址

第三步:正式安装

yum install -y docker-ce

docker-ce为docker的社区版本(ce表示community edition)

1.1.3 启动docker

# 启动docker服务
systemctl start docker

# 设置docker服务开机自启
systemctl enable docker

输入以下指令查看docker-ce的版本

docker -v

1.1.4 配置镜像加速

从docker官方镜像仓库下载镜像速度较慢,我们可以使用国内的镜像仓库

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://d4e3ao9w.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

1.2 将Java项目打包成jar包

本次演示使用的是一个较为简单的Java项目,项目的整体结构如下

在这里插入图片描述

HelloWorldController.java

package org.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
    @GetMapping("/")
    public String echo() {
        return "<h1>Hello, World!</h1>";
    }
}

application.yml

server:
  port: 16256

在Java项目的pom.xml文件中添加打包插件(在mainClass标签中填写项目的启动类

SpringBoot项目的pom.xml文件中自带该打包插件,无需添加

记得把打包插件中的 <skip>true<skip> 标签删掉

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot.version}</version>
            <configuration>
                <mainClass></mainClass>
            </configuration>
            <executions>
                <execution>
                    <id>repackage</id>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

用maven将Java项目打包成jar包

在这里插入图片描述

1.3 将jar包上传到Linux上

将jar包上传到Linux的 /tmp/docker 目录下(你也可以选择上传到其它目录)

如果上传失败,可能是 /tmp/docker 目录没有写入的权限,运行以下指令修改docker目录的权限

cd /tmp

chmod 777 -R docker

1.4 编写Dockerfile文件

在jar包所在的目录下,新建Dockerfile文件,文件内容如下(记得将docker-demo-0.0.1-SNAPSHOT.jar换成你的jar包的名称)

# 指定基础镜像
FROM java:8-alpine

# 复制文件到镜像中
COPY ./docker-demo-0.0.1-SNAPSHOT.jar /tmp/docker-demo-0.0.1-SNAPSHOT.jar

# 暴露端口
EXPOSE 16256

# 指定容器启动时运行的指令
ENTRYPOINT ["java", "-jar", "/tmp/docker-demo-0.0.1-SNAPSHOT.jar"]

补充:

  • java:8-alpine镜像中已经内置了jdk1.8,也配置好了JAVA_HOME环境变量
  • 如果想复制多个文件到镜像中,可以使用多个COPY指令
  • 如果想放行多个端口,可以使用多个EXPOSE指令
  • 一个 Dockerfile 只能有一个 ENTRYPOINT 指令,(如果你想在 ENTRYPOINT中执行多条指令,可以参考本文的 1.5 Dockerfile 文件的知识补充 章节
  • 注释只能另起一行,不能写在代码后面,否则会报错
  • 复制文件时,目标路径建议使用绝对路径,如果目标路径不存在,Docker 会自动创建
  • 如果复制的是目录,Docker 会复制目录中的所有内容,包括子目录和文件,但不包括目录本身。如果目录中包含隐藏文件或以点(.)开头的目录,它们也会被复制

注意:

  • COPY指令的源文件请使用相对路径,不要使用绝对路径
  • 请确保jar包在Dockerfile文件所在目录或Dockerfile文件所在的目录的子目录下

如果你想使用其它现成的镜像,可以在阿里云的镜像制品中心中搜索

网址:https://cr.console.aliyun.com/cn-hangzhou/instances/artifact

在这里插入图片描述

1.5 Dockerfile文件的知识补充

1.5.1 在容器启动时执行多个命令

一个 Dockerfile 只能有一个 ENTRYPOINT 指令

如果您想在容器启动时执行多个命令,可以将这些命令放在一个脚本中,然后在 ENTRYPOINT 中调用这个脚本

# 指定基础镜像
FROM java:8-alpine

# 复制文件到镜像中
COPY ./docker-demo-0.0.1-SNAPSHOT.jar /tmp/docker-demo-0.0.1-SNAPSHOT.jar
COPY ./start.sh /tmp/start.sh

# 暴露端口
EXPOSE 16256

# 赋予脚本文件执行权限
RUN chmod +x /tmp/start.sh

# 指定容器启动时运行的指令
ENTRYPOINT ["/tmp/start.sh"]

start.sh脚本文件的内容如下

#!/bin/ash

# 第一个命令
java -jar /tmp/docker-demo-0.0.1-SNAPSHOT.jar

# 第二个命令(如果需要的话)
echo "Hello, World"

目标路径建议使用绝对路径,如果目标路径不存在,Docker 会自动创建

如果复制的是目录,Docker 会复制目录中的所有内容,包括子目录和文件,但不包括目录本身。如果目录中包含隐藏文件或以点开头的文件,它们也会被复制

1.5.2 RUN指令

在 Docker 中,RUN 指令用于在当前构建的镜像层中执行命令并提交结果,从而创建一个新的镜像层。这些命令通常用于安装软件包、创建用户、修改配置文件、下载依赖等,以便为容器设置所需的环境

RUN 指令每一次执行后都会创建一个新的镜像层,因此在使用 RUN 指令时应尽量合并多个指令以减少镜像层的数量,从而减小镜像体积并提高构建效率,可以通过使用 && 来连接多个命令

RUN chmod +x /tmp/start.sh && echo "success"

1.5.3 ENTRYPOINT详解

在 Docker 中,ENTRYPOINT 指令有两种格式:

  1. exec 格式:使用数组语法,例如 ENTRYPOINT ["executable", "param1", "param2"]。这种格式是推荐的方式,因为它将 ENTRYPOINT 中的命令作为 PID 1 的进程运行,这意味着该命令的进程是容器中的顶级进程。这有助于信号传递(例如,SIGTERM)和管理容器的生命周期
  2. shell 格式:使用字符串语法,例如 ENTRYPOINT command param1 param2。这种格式会在 shell 中执行,即 /bin/sh,这意味着 ENTRYPOINT 命令会在 shell 的上下文中运行,而不是作为顶级进程

补充:

  • SIGTERM 是一个 Unix 和 Linux 操作系统中的信号,它用于通知进程终止。当 Docker 容器停止时,Docker 引擎会向容器的 PID 1(顶级进程)发送 SIGTERM 信号,以请求容器内的应用程序优雅地停止
  • 优雅地停止意味着应用程序应该有机会完成当前正在执行的操作,释放资源,保存状态,或者执行必要的清理工作,然后再退出。这是为了保证数据的一致性和系统的稳定性
  • 在 Docker 中,当使用 docker stop 命令时,Docker 引擎会先发送 SIGTERM 信号给容器。如果容器在一段时间内(默认为 10 秒)没有响应并退出,Docker 引擎会接着发送 SIGKILL 信号来强制终止容器。SIGKILL 信号无法被捕获和处理,它会立即终止进程,可能会导致数据丢失
  • 因此,编写能够正确处理 SIGTERM 信号的应用程序对于确保容器的平滑停止至关重要。在 Docker 容器中,PID 1 进程特别重要,因为它负责接收和处理信号。如果 PID 1 进程不处理 SIGTERM,容器可能不会优雅地停止。这也是为什么在 Docker 中推荐使用 exec 格式的 ENTRYPOINT,因为它允许应用程序直接作为 PID 1 进程运行,从而能够接收和处理信号

推荐使用 exec 格式的数组语法的原因:

  • 信号处理:当使用 exec 格式时,容器接收到的信号(如 SIGTERM )会直接传递给 ENTRYPOINT 命令的进程,而不是传递给 shell
  • PID 1:在 exec 格式中,ENTRYPOINT 命令的进程成为容器内的 PID 1,这是 Unix 系统中的顶级进程。它负责创建子进程、处理孤儿进程和收割僵尸进程。如果使用 shell 格式,PID 1 是 shell 进程,而不是你的应用程序,可能会导致一些潜在问题
  • 清晰性:数组语法更清晰地表示了命令和参数,避免了 shell 格式的解析和转义问题
  • 性能:使用 exec 格式可以避免启动额外的 shell 进程,提高性能并减少资源消耗

2. 构建镜像

在Dockerfile文件所在的目录下运行指令

docker build -t docker-demo:1.0 ./

指令的解释:

  • docker build: Docker 的子命令,用于从 Dockerfile 和上下文构建一个镜像
  • -t: 用于指定构建的镜像的名称和标签,在上述指令中,docker-demo 是镜像的名称,1.0 是镜像的标签
  • docker-demo:1.0: 这是指定的镜像名称和标签,冒号 : 用于分隔镜像名称和标签
  • ./: 这个参数指定了构建上下文的路径。构建上下文是指 Docker 在构建镜像时能够访问的文件和目录。在上述指令中,./ 表示构建上下文为运行指令时所在的目录

3. 创建容器并运行

docker run --name docker-demo -p 16256:16256 -d docker-demo:1.0

指令的详细解释:

  • docker run: Docker 的子命令,用于启动一个新容器
  • --name docker-demo: 这个选项用于指定新容器的名称为 docker-demo,如果没有指定名称,Docker 会为该容器生成一个随机名称
  • -p 16256:16256: 这个选项用于映射容器的端口到宿主机的端口。16256:16256 表示将容器的 16256 端口映射到宿主机的 16256 端口。这样,外部网络就可以通过宿主机的 16256 端口访问容器内的服务
  • -d: 这个选项用于让容器在后台运行,如果不加这个选项,容器会占用当前终端,阻止你执行其他命令
  • docker-demo:1.0: 这是指定要启动的镜像的名称和标签。冒号 : 用于分隔镜像名称和标签

4. 测试

在浏览器中访问以下网址(记得将IP地址改为你的虚拟机的IP地址

http://192.168.31.129:16256

看到以下页面,证明项目已经成功部署

在这里插入图片描述

温馨提示:如果访问失败,可能是虚拟机的防火墙没有开放16256端口

可以使用以下指令开放16256端口

firewall-cmd --zone=public --add-port=16256/tcp --permanent
  • 如果你使用的是云服务器,请在安全组中放行16256端口
  • 如果你安装了宝塔,请在宝塔中放行16256端口

5. 可能遇到的问题

5.1 构建容器时可能遇到的问题

问题复现:

ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref db7fbd3d-de65-4abb-a024-61534e7b1c71::m0fc5kkmyd1r862kzq738qidt: "/tmp/docker/docker-demo-0.0.1-SNAPSHOT.jar": not found

在这里插入图片描述

问题产生的原因:

  • Docker 构建容器时默认会将Dockerfile所在目录以及Dockerfile所在目录的子目录作为构建上下文
  • 构建容器时,如果使用或访问了构建上下文以外的目录或文件,Docker将无法访问它们

解决方法:

将构建容器所用到的全部文件移到Dockerfile所在目录下

5.2 运行容器时可能遇到的问题

5.2.1 问题一

问题复现:

no main manifest attribute, in /tmp/JarToDocker-0.0.1-SNAPSHOT.jar

问题产生的原因:

  • Java项目打包出现问题,没有指定启动类

解决方法:

参考本文的 1.2 将Java项目打包成jar包 章节

5.2.2 问题二

问题复现:

exec /tmp/start.sh: no such file or directory

问题产生的原因以及解决方法:

参考我的另一篇文章:docker容器中sh脚本明明存在,启动容器时却一直报错:no such file or directory

  • 35
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 是一个开源的应用程序框架,它可以帮助开发者快速构建独立的、可部署的Spring应用程序。Docker 是一个开源的容器化技术,可以将应用程序和其依赖项打包为一个独立的、运行在任何环境中的镜像。 要将Spring Boot 应用程序打成Docker镜像包,需要按照以下步骤进行操作: 1. 首先,我们需要在pom.xml文件中添加Docker Maven插件依赖。 ```xml <build> <plugins> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.0</version> </plugin> </plugins> </build> ``` 2. 在Dockerfile中定义打包镜像的指令。可以在项目的根目录下创建一个名为Dockerfile的文件,并使用以下内容: ```dockerfile FROM openjdk:8-jdk-alpine COPY target/my-app.jar /app.jar CMD ["java","-jar","/app.jar"] ``` 上述Dockerfile会指定使用OpenJDK 8的JDK基础镜像,并将编译好的Spring Boot应用程序包拷贝到镜像中。然后在启动容器时执行CMD命令运行应用程序。 3. 最后,在终端中进入项目的根目录,执行如下命令来构建Docker镜像: ```bash mvn clean package docker:build ``` 这个命令会使用Docker Maven插件来打包Spring Boot应用程序,并将其构建为Docker镜像。 4. 构建成功后,可以使用以下命令来运行Docker容器: ```bash docker run -p 8080:8080 -t imageName:tag ``` 其中,-p选项用于将容器的8080端口映射到主机的8080端口,imageName:tag则为构建出来的镜像的名称和标签。 以上就是将Spring Boot应用程序打成Docker镜像包的步骤。通过这种方式,可以方便地将应用程序部署到任何支持Docker的环境中,简化了部署的过程,提高了应用程序的可移植性和可扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聂 可 以

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值