docker容器运行java应用

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

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
  • 添加Docker稳定版仓库地址
$ sudo add-apt-repository \
  "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
1
2
  • 安装docker社区版
$ sudo apt update
$ sudo apt install docker-ce
1
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
  • -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
  • 安装完成后可以检查是否安装成功
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

每次只需要更新jar包后,启动该脚本即可,脚本可放在任意路径。

$ ./docker_start.sh
1

执行脚本后会有两次交互,分别需要填入jar包文件名以及日志文件名。 如果容器中的/opt/servers/template_demo路径下不存在所填入的文件,则会提示文件不存在。 如果日志文件不存在,则会新建一份。交互效果大致如下

$ 请输入application_name: TemplateDemoForJDK17-0.0.1.jar
$ 请输入日志名称(不含后缀): template231110
1
2

脚本执行过程中会有如下日志

容器未运行,尝试启动容器
ubuntu-jdk17
启动完毕
尝试启动java应用
应用启动成功。
1
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

# 10. 其它

上述的docker容器配置都是基于命令行指令,为的是更好地梳理逻辑关系。实际生产时,可以采取DockerFile文件的方式简化配置过程,并提供更好的复用性。

Last Updated: 1/20/2024, 5:10:01 PM