准备工作
首先需要确保目标服务器上:
- 安装好了Docker,并修改配置文件允许远程连接
- 防火墙或安全规则对2375(非https)2376(https)放行
安装docker 可参考:这里
使用的IDEA 版本为 IntelliJ IDEA 2021.2.2 (Ultimate Edition)
已安装简中语言包: Chinese (Simplified) Language Pack / 中文语言包
新版本IDEA已经捆绑安装了docker插件,如果是旧版需要手动安装
按Ctrl+Alt+S打开设置界面,选择 “构建、执行、部署" - “Docker” 打开docker插件管理界面
- 点击列表区上方的 + 号 添加一个连接
- 在右边填写连接的名称(自定义,方便区分即可)
- 我们是连接远程服务器所以方式选默认的“TCP套接字”
- 非https连接时,API URL 应填 : tcp://服务器ip:2375;https连接时应填 https://服务器ip:2376
- 当使用https连接时需要提供证书文件的存放路径
- 注意:外网服务器如果不使用https链接几乎必定会被入侵成矿机,证书的生成方法参考 这里
修改连接配置后插件会自动尝试连接,如果成功则会有如下提示
配置成功后点确定。
现在在底部工具按钮的“服务”中即可看到配置好的连接,双击连接即可查看docker中的容器、镜像等。
手动部署
为了理解自动部署都干了什么,有必要先了解手动部署的过程。
总流程
- 使用maven pacakge命令将java程序打包成jar包或war包
- 将打包好的程序用 docker build命令打包成docker 镜像
- 使用 docker run 命令使用镜像运行一个容器
第一步
点开IDEA侧边栏工具Maven按钮 ,点开生命周期,双击 clean 执行完毕后再双击 package即可
第二步
在项目根目录(与pom.xml文件路径相同)新建一个文件,命名为“Dockerfile"(无后缀名),docker build命令会在执行名令的当前目录寻找这个文件,并将它的内容作为打包的操作步骤。
使用记事本或其他文本工具打开该文件,填入如下内容
# 使用openjdk:15 作为基础镜像
FROM openjdk:15
# 设置临时文件夹
VOLUME /tmp
# 将打包好的war文件复制到镜像根目录
ADD target/包名.war app.war
# 配置时区,否则打印出的日志时间会差8h
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 配置运行参数 这里的参数是为了解除jdk新版本的反射限制
ENV JAVA_OPTS='--add-opens java.base/java.lang=ALL-UNNAMED'
# 启动java程序命令 --spring.profiles.active=prod -> 使用 application-prod 文件作为springboot的启动配置
ENTRYPOINT java ${JAVA_OPTS} -jar /app.war --spring.profiles.active=prod
详细解释:
- 打包docker镜像需要将一个基础镜像+我们的程序打包在一起,基础镜像会提供一些运行环境,因为我们是java程序所以需要使用有jdk的基础镜像
- ADD target/包名.war app.war 这一句即表示向镜像中添加 当前目录下的target文件夹中的 包名.war 文件,并更名为 app.war。 这个文件即为maven package打包好的文件,需要根据实际文件名修改。
- 最后一行表示容器启动后执行的命令,即为启动java程序 app.war
- spring.profiles.active参数的含义、作用如果不清楚请自行百度
因此我们需要把 Dockerfile文件 和 包名.war 文件 ,上传到服务器的某一目录下,并且保证上述的相对路径关系,然后在该目录下执行如下命令
docker build -t 镜像名称 .
其中
- 镜像名称为自定义
- 最后的英文句号不可省略,它表示Dockerfile文件就在当前目录下
因为我们用到了基础镜像,如果本地不存在该镜像,服务器会先自动下载它,所以初次运行会比较慢。
执行成功后应该可以在IDEA的”镜像“折叠中看到该镜像和用到的基础镜像,或者也可以使用该命令查看
docker images
第三步
执行命令
docker run -d -p 宿主机端口:容器端口 --name 容器名称 镜像名称
详细解释
- 可以把docker简单理解为运行在服务器(宿主机)上的虚拟机管理器,容器就是虚拟机
- -p 宿主机端口:容器端口 表示将宿主机的 一个端口 映射到 容器的一个端口;其中宿主机端口只需要是一个对外开放的端口即可,容器端口即springboot的启动配置中设置的 server.port
- –name 容器名称 镜像名称 表示使用指定镜像创建、运行一个容器,容器名称为自定义,镜像名称为第二步打包好的镜像名称
- 如果程序涉及到一些对文件操作,因为程序对自身容器内的文件操作会随着容器重新创建而丢失,所以需要把宿主机上的某一路径映射到容器内的某一路径,来避免这个情况;此时需要加入参数 -v 宿主机路径:容器内路径 ;个人习惯上两边都使用 /home/项目名称 这个路径
然后执行如下命令查看运行中的容器
docker ps
应该可以看到有指定名称的容器在运行了,此时使用 http://服务器ip:宿主机端口 应该就可以访问到该程序接口
小技巧
在宿主机执行 ifconfig 命令,可以看到一个名为 docker0 的网络 它的 inet ip 应该是形如: 172.17.0.1 这样的,对于运行中的每一个容器而言,使用该ip+上述的“容器端口”,即可访问到其他容器的对外服务,例如:
- 后端程序可以访问其他容器中的数据库、redis
- 前端程序可以访问其他容器中的后端接口
这个ip是一个固定的内网ip,不受部署的服务器ip影响,也不受docker服务重启(每个容器的ip会变化)的影响。
一键部署
修改pom文件
我们需要添加一个 docker-maven 插件来一键执行手动部署中的 第 1-2 步,但需要沿用之前的Dockerfile文件。
打开pom.xml文件,找到 build 标签中的 plugins标签,在其中添加如下内容(注意如果插件重名请自行删除)
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!--跳过测试-->
<skip>true</skip>
</configuration>
</plugin>
<!--docker-maven插件-->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.2.2</version>
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<!--表示在 package完成后执行 docker build 命令-->
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<!--Docker 的主机地址-->
<!-- http访问时 端口为2375 https访问时 端口为2376-->
<dockerHost>https://服务器ip:2376</dockerHost>
<!--docker build 生成的镜像的名称 注意不要有大写-->
<imageName>镜像名称</imageName>
<!-- 使用https 证书访问时需要提供证书文件的存放路径-->
<dockerCertPath>证书文件的存放路径</dockerCertPath>
<!--镜像的 tags-->
<forceTags>true</forceTags>
<!--Dockerfile 的位置 表示在项目根目录-->
<dockerDirectory>${project.basedir}</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.war</include>
</resource>
</resources>
</configuration>
</plugin>
注意修改其中的 服务器ip、镜像名称、证书文件存放路径;服务器ip和证书路径,与链接docker时的配置完全一致,镜像名称的含义与docker build 步骤中的镜像名称含义一致。
修改后注意重新加载使配置生效
配置完成后再执行手动部署的第一步时,会连带第二步也一起执行(执行到打包好docker镜像)
配置脚本
接下来我们把原来的第一步和第三部做成脚本来一键执行
点开这个下拉,选择”编辑配置“
打包
点左上 + 号 选择Maven
- 名称自定义,建议勾选“存储为项目文件”,这样这个脚本会以文件的形式保存在项目中,也即可以保存到github上。
- 命令行中写 clean package ,即依次执行clean 和package 指令(原第一步)
- 确定完成之后在下拉框选择这个脚本,点右箭头执行即可完成打包
- 注意:如果在修改代码之后进行重新打包,新的镜像会使用我们设置好的名字,旧的镜像会以 image id的形式出现;需要及时删除,如果之前的容器仍在运行则需要先删除该容器。
成功打包的话应该在控制台看到类似内容
[INFO] Built 镜像名称
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:33 min
[INFO] Finished at: 2021-09-18T15:01:55+08:00
[INFO] ------------------------------------------------------------------------
运行
点左上 + 号 选择 Docker 镜像
- 名称自定义
- 服务器选择之前配置好的docker连接
- 镜像ID 或 名称 即前述的 镜像名称;容器名称即前述的容器名称
- 绑定端口即前述的 -p 宿主机端口:容器端口 参数
- 绑定挂载即前述的 -v 宿主机路径:容器内路径 参数
- 运行选项中的 –restart always 表示该容器随docker服务的启动一起运行,且如果运行过程中崩溃退出会自动重启。
- 确定完成之后,在完成打包后选择这个脚本运行,即可启动容器。如果已经有同名容器存在则会先删除该容器。
重新部署操作
以上配置完成之后,每次修改代码后,将两个脚本分别执行一次即可完成重新部署。