容器化部署 Springboot 应用

CentOS7 上安装 JDK8

这些年来 Docker 是越来越流行了,企业的环境部署也普遍都容器化了,虽说 Docker 当前也支持在 Windows 上安装,但真正的线上环境估计没有人选用 Windows 作为容器化平台,所以我们还是选择在 Linux 上容器化部署应用。既然 Springboot 应用基于 Java 来开发,那就先在 Linux 上安装 JDK 吧。这里有个小问题值得注意下,我们知道 Linux 下都有在线安装软件工具,比如 CentOS 的 yum,如果 JDK 也选择在线安装,那么一般安装下来的是 OpenJDK,这个跟 Oracle JDK 还是有些区别的,所以强烈建议还是到 Oracle 官网上去下载 JDK 版本。

我这里还是安装 JDK8,首先到JDK8 下载地址找到 jdk-8u152-linux-x64 版本,然后下载(需要注册账号才可以下载,那就注册个账号吧)。

我已经预先安装好了 CentOS7 虚拟机,关于 CentOS 的安装我就不在这里介绍了,感兴趣的同学可以查找相关资料去学习。我这里选用 xshell 作为 ssh 工具,用 xftp 作为 Windows 与 Linux 之间的文件传输工具。先将下载下来的软件包 jdk-8u152-linux-x64.tar.gz 上传到 /root/目录下:

1
2
3
4
5
# pwd
/root

# ll jdk-8u152-linux-x64.tar.gz
-rw-r--r--. 1 root root 189784266 5月 3 12:08 jdk-8u152-linux-x64.tar.gz

然后创建 JDK 的安装目录(在 Linux 下一般都将第三方软件安装到 /usr/local/目录下):

1
# mkdir -p /usr/local/java

接着将软件包解压到 /usr/local/java 目录下:

1
# tar -xzvf jdk-8u152-linux-x64.tar.gz -C /usr/local/java/

进入 /usr/local/java/ 目录,查看解压是否成功了:

1
2
3
4
5
6
7
8
9
# cd /usr/local/java/

# ls
jdk1.8.0_152

# cd jdk1.8.0_152/

# ls
bin COPYRIGHT db include javafx-src.zip jre lib LICENSE man README.html release src.zip THIRDPARTYLICENSEREADME-JAVAFX.txt THIRDPARTYLICENSEREADME.txt

解压成功了,这里解压后的文件就是可执行文件了,不用再编译、安装(configure、 make、 make install),接下来配置下环境变量就可以了。打开 CentOS 下的系统配置文件 /etc/profile:

1
# vi /etc/profile

在文末追加以下内容:

1
2
3
4
export JAVA_HOME=/usr/local/java/jdk1.8.0_152
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

然后保存文件,执行 source 命令使其生效:

1
# source /etc/profile

最后验证下是否安装成功了:

1
2
3
4
# java -version
java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

OK 了。

CentOS7 上安装 Docker

在 CentOS 上安装 Docker,我们可以使用 yum 工具在线安装,先为 yum 增加安装 Docker 的国内源,这样会使安装过程快点:

1
# yum-config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo

然后安装一些 Docker 依赖包:

1
# yum install -y yum-utils device-mapper-persistent-data lvm2

接着更新 yum 软件源缓存并安装 Docker:

1
2
3
# yum -y makecache fast

# yum -y install docker-ce

启动 Docker

1
2
3
# systemctl enable docker

# systemctl start docker

建立 docker 用户组,不然当前用户下可能无法使用 Docker:

1
2
3
# groupadd docker

# usermod -aG docker $USER

好了,至此应该可以正常使用 Docker 了,为保万无一失,我们拉取个 nginx 镜像验证下吧:

1
2
3
4
5
6
7
8
9
10
11
# docker pull nginx:latest
latest: Pulling from library/nginx
27833a3ba0a5: Pull complete
ea005e36e544: Pull complete
d172c7f0578d: Pull complete
Digest: sha256:edeeacc4b2605cfbdc5b9e4dc502e3269c8346aa4d257d2fab74dccfc0f7b972
Status: Downloaded newer image for nginx:latest

# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 27a188018e18 2 weeks ago 109MB

启动 Docker 容器(docker run 命令用来创建容器,–name 参数是给这个容器起个名字,-d 是让容器在后台运行,-p 是指定容器的访问端口,其中 9999 是对外访问的接口, 80 是 nginx 容器的内部端口, 最后的参数就是指定镜像名称):

1
2
3
# docker run --name nginxtest -d -p 9999:80 nginx
WARNING: IPv4 forwarding is disabled. Networking will not work.
03e6aabe8a0957cb547e41a183161a11f8feceec87f5b0e601c17bcfa9adeb45

用命令 docker ps 查看容器是否起来了:

1
2
3
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e6aabe8a09 nginx "nginx -g 'daemon of…" 5 seconds ago Up 2 seconds 0.0.0.0:9999->80/tcp nginxtest

好,那我们访问下 nginx 首页吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# curl 192.168.18.128:9999/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

完美。

编写 Dockerfile

接着上篇 使用 Springboot 编写 Helloworld 应用 我们完成了使用 Springboot 编写 Helloworld 应用的过程,接下来我们要基于这个简单的应用制作 Dockerfile 文件,以构建 Helloworld 应用的镜像,实现容器化部署。

先将 hellospringboot 工程拷贝到 CentOS7 机器上来,然后用 gradlew 打包应用:

1
2
3
# chmod + gradlew

# ./gradlew build

这时,在 ./build/libs/ 目录下就可以看到打包好了的 jar 包了,这时候运行命令:

1
# java -jar ./build/libs/hellospringboot-0.0.1-SNAPSHOT.jar

应用就可以跑起来了,访问 ip:8080 就可以看到有 Helloworld 返回了。但是这还不是我们想要的,我们要实现容器化部署。于是我编写了 Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM java:8-jre

MAINTAINER <chenliangtang>

COPY ./build/libs/* docker-entrypoint.sh /

RUN chmod +x docker-entrypoint.sh && ln -s /docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

WORKDIR /

EXPOSE 8080

CMD ["docker-entrypoint.sh"]

其中, docker-entrypoint.sh shell 脚本内容如下所示:

1
2
3
4
5
#!/bin/bash

jarFile=$(ls . | grep jar)

java -jar $jarFile

对 Dockerfile 内容的解释:其中 Dockerfile 这个名称不能变,就如 Makefile 文件是 make 编译的文件说明一样,这是规范;FROM 这行表示我构建的镜像基础是 jdk8,因为 hellospringboot 应用依赖于 jdk8;MAINTAINER 写上作者;COPY 行是把需要生成镜像的 jar 包和 shell 脚本拷贝进镜像中;RUN 行把 shell 脚本改成可执行文件,同时将脚本链接到环境变量中,以方便后面的 CMD 执行;WORKDIR 指明镜像的工作目录;EXPOSE 指明镜像的开放端口,因为 hellospringboot 端口为 8080 所以这里也设置为 8080;最后一行的 CMD 是指在启动容器时会执行的命令,这里就是通过执行 java -jar 命令将服务启动起来。

docker-entrypoint.sh 脚本就很简单了,就是获取到 jar 包,然后通过 java -jar 命令把应用启动起来。

构建镜像、容器化启动应用

上面我们已经完成 Dockerfile 文件的编写,接下来就简单了,在 Dockerfile 文件的当前目录下执行命令 docker build 即可构建镜像(-t 参数是给镜像起个名字并以 v1 作为 tag 标识):

1
2
3
4
# docker build -t hellospringboot:v1 .
......
Successfully built dfbdde43b0aa
Successfully tagged hellospringboot:v1

提示成功了,执行 docker images 命令查看构建成功的镜像:

1
2
3
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hellospringboot v1 dfbdde43b0aa 38 minutes ago 328MB

很好,通过 docker run 启动容器(–name 是给容器起个名字;-d 是让其在后台运行;-p 是指定服务对外提供的访问端口,其中 8888是对外的端口, 8080 是容器内部端口,就是 Dockerfile 文件中 EXPOSE 指定的那个端口):

1
2
3
4
5
6
# docker run --name hello -d -p 8888:8080 hellospringboot:v1
574d0ed0ed6940d6a45f994a84eda45d2be8f6e85663aa9ba4a9b07970fb178f

# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
574d0ed0ed69 hellospringboot:v1 "docker-entrypoint.sh" 39 minutes ago Up 39 minutes 0.0.0.0:8888->8080/tcp hello

哈哈,访问下吧:

1
2
# curl 192.168.18.128:8888
HelloWorld.

至此 over。

遇到的几个坑

坑1,在 Windows 上写好的脚本,上传到 Linux 构建镜像出错

这个问题其实是格式的问题,Windows 上的换行符是 \r\n,而 Linux 上的换行符是 \n,什么?不信?我们来看看吧。下面 docker-entrypoint.sh 是我刚从 Windows 上上传到 Linux 上的脚本:

1
2
file docker-entrypoint.sh 
docker-entrypoint.sh: a /usr/bin/env bash\015 script, ASCII text executable, with CRLF line terminators

后面显示的 CRLF 就是换行符说明,那怎么转换成 Linux 格式?推荐使用 dos2unix 命令,发现没有该命令?,没事安装下就行:

1
2
3
4
5
6
7
# yum -y install dos2unix

# dos2unix docker-entrypoint.sh
dos2unix: converting file docker-entrypoint.sh to Unix format ...

]# file docker-entrypoint.sh
docker-entrypoint.sh: Bourne-Again shell script, ASCII text executable

perfect,问题解决。

坑2,容器起来了,本机可以访问服务,但是其他机器却无妨访问

这个问题是防火墙的问题,CentOS 默认将防火墙打开,关掉防火墙再重启 docker 服务即可。

1
2
3
4
# systemctl stop firewalld.service
# systemctl disable firewalld.service
# systemctl restart docker
# docker start 574d0ed0ed69

其实 容器在启动时可以加上参数 –restart=always,到时 docker 重启或宿主机重启后,容器都会自动重启了。