docker容器运行java应用
qinsz 11/10/2023 ubuntudockerMySQLJAVA
# 一、场景
# 1. 服务器中已经安装有JAVA8环境,并运行了一个基于JAVA8的应用,现在需要在服务器中运行一个基于JAVA17的应用。
# 2. 希望将JAVA应用运行在安装好JAVA17的docker容器中,并让该应用能够访问宿主计算机中的MySQL数据库。
# 3. 考虑到JAVA应用的更新升级,应考虑通过脚本docker容器的自动化工作。
# 4. 服务器环境:ubuntu20.04、MySQL8.0.30
# 二、实施
# 1. 安装Docker
如果已经安装了Docker,则可跳过该步骤。下面为安装docker-ce(社区版)的步骤。
- 安装Docker时需要使用到的工具。
$ sudo apt install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
1
2
3
4
5
2
3
4
5
PS: 指令中的""可以实现在指令中进行换行,这里仅为了方便展示,实际操作时可以不用“\”进行换行。
- 使用curl工具将下载的公钥内容添加到apt 包管理器的信任列表。注意命令最后的短横线“-”不要缺失,短横线表示向apt-key add 传递的是公钥内容,而不是公钥文件地址。添加完成后,可以通过apt-key list查看所有的公钥信息。
$ curl -fsSL \
https://download.docker.com/linux/ubuntu/gpg \
| sudo apt-key add -
1
2
3
2
3
- 添加Docker稳定版仓库地址
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
1
2
2
- 安装docker社区版
$ sudo apt update
$ sudo apt install docker-ce
1
2
2
# 2. 上传jar包资源
将jar包和application文件上传到服务器指定路径,同时创建了一个logs路径用于存放运行时产生的日志文件。下面是资源上传后的效果:

# 3. 修改application配置文件中的数据库连接信息
- 通过ifconfig或ip addr show查询服务器的ip地址中的docker网卡信息。
该ip地址即docker容器访问宿主计算机时所使用的地址。

- 使用上述IP地址信息,更新application.yaml文件中的数据库地址。

- 注意:上面Mysql配置中的用户username要具备访问数据的权限(比如mysql.user表中,该用户的host字段需要配置为docker容器的ip地址,或者将其设置为%)
# 4. 修改宿主计算机MySQL配置
- 修改/etc/mysql/mysql.conf.d/mysqld.cnf中bind-address。将bind-address注释掉,或者添加docker网卡的IP地址。
bind-address = 127.0.0.1,172.17.0.1
1
上面添加了docker网卡的地址:172.17.0.1
- 通过“:wq”指令保存退出后重启mysql服务
$ sudo systemctl restart mysql
1
# 5. 修改宿主计算机Redis配置
- 修改/etc/redis/redis.conf中的bind。将bind注释掉,或者添加docker网卡的IP地址。
bind 127.0.0.1 ::1 172.17.0.1
1
上面添加了docker网卡的地址:172.17.0.1
- 通过“:wq”指令保存退出后重启redis-server服务
$ sudo systemctl restart redis-server
1
# 6. 拉取ubuntu镜像并创建容器
- 拉取一个干净的系统镜像
$ sudo docker pull ubuntu:20.04
1
- 创建并运行容器(如果本地没有ubuntu:20.04镜像则会自动拉取)
$ sudo docker run -d -it \
--name test-jdk17 \
-p 9090:8080 \
-v /home/qinsz/server_apps/template_demo_for_jdk17:/opt/servers/template_demo \
ubuntu:20.04
1
2
3
4
5
2
3
4
5
- -d: 后台运行;
- -it: 以交互模式运行(不写会导致容器启动后自动停止)
- --name: 为容器命名
- -p: 端口映射“宿主计算机端口:容器内部端口”
- -v:配置路径映射“宿主计算机路径:容器内路径”,如果“容器内路径”不存在则会自动创建
- 末尾的“ubuntu:20.04”: 创建容器所使用的镜像
# 7. 为容器安装jdk17环境
- 进入容器
$ sudo docker exec -it ubuntu-jdk17 bash
1
- 在容器内安装jdk17
root@fd955ac31356:/# apt update
root@fd955ac31356:/# apt install openjdk-17-jdk
1
2
2
- 安装完成后可以检查是否安装成功
root@fd955ac31356:/# java -version
1
# 8. 使用docker启动java应用
希望每次应用有更新时,通过替换指定位置的jar包,并重启docker就能实现应用的更新。因此可以创建这样的bash脚本。
#!/bin/bash
# 读取用户输入的application_name
read -p "请输入application_name: " application_name
# 读取用户输入的日志文件名称
read -p "请输入日志名称(不含后缀): " log_name
# 检查容器状态
container_name="ubuntu-jdk17"
container_status=$(sudo docker inspect -f '{{.State.Running}}' $container_name 2>/dev/null)
if [ "$container_status" == "true" ]; then
echo "容器已经启动,尝试重启。"
sudo docker restart $container_name
echo "重启完毕"
elif [ "$container_status" == "false" ]; then
echo "容器未运行,尝试启动容器"
sudo docker start $container_name
echo "启动完毕"
else
echo "容器不存在!!!"
# 可以将容器的创建过程在这里实现,这里就不再赘述
exit 1
fi
echo "尝试启动java应用"
# 在容器中执行命令
# 在容器的根目录直接以绝对路径启动jar包,会出现数据库连接失败的错误
# 因此先cd到应用路径,再执行
sudo docker exec $container_name bash -c "
if [ -e /opt/servers/template_demo/$application_name ]; then
cd /opt/servers/template_demo
nohup java -jar $application_name >> logs/${log_name}.log &
echo '应用启动成功。'
else
echo '错误:/opt/servers/template_demo/$application_name 文件不存在,请检查。'
fi
"
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
27
28
29
30
31
32
33
34
35
36
37
38
39
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
27
28
29
30
31
32
33
34
35
36
37
38
39
每次只需要更新jar包后,启动该脚本即可,脚本可放在任意路径。
$ ./docker_start.sh
1
执行脚本后会有两次交互,分别需要填入jar包文件名以及日志文件名。 如果容器中的/opt/servers/template_demo路径下不存在所填入的文件,则会提示文件不存在。 如果日志文件不存在,则会新建一份。交互效果大致如下
$ 请输入application_name: TemplateDemoForJDK17-0.0.1.jar
$ 请输入日志名称(不含后缀): template231110
1
2
2
脚本执行过程中会有如下日志
容器未运行,尝试启动容器
ubuntu-jdk17
启动完毕
尝试启动java应用
应用启动成功。
1
2
3
4
5
2
3
4
5
# 9. 其它bash脚本
- 关闭容器脚本docker_stop.sh
#!/bin/bash
# 检查容器状态
container_name="ubuntu-jdk17"
container_status=$(sudo docker inspect -f '{{.State.Running}}' $container_name 2>/dev/null)
if [ "$container_status" == "true" ]; then
echo "容器正在运行,尝试终止运行..."
sudo docker stop $container_name
echo "容器已停止运行"
elif [ "$container_status" == "false" ]; then
echo "容器未运行"
else
echo "容器不存在!!!"
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 10. 其它
上述的docker容器配置都是基于命令行指令,为的是更好地梳理逻辑关系。实际生产时,可以采取DockerFile文件的方式简化配置过程,并提供更好的复用性。