docker——发布一个java应用程序
一、安装docker
使用docker之前,需要在物理机上安装docker。安装过程可参考docker官网,安装完成之后,运行docker。不同的操作系统,docker的安装和启动不同。
二、待发布的java程序说明
需要发布的java服务目录结构如下:
docker-test/
|-bin/
| |-server.sh
|-etc/
| |-config.edn
| |-logback.xml
|-target/
| |-docker-test.jar
| |-docker-test-standalone.jar
|-Dockerfile
Dockerfile
Dockerfile用来定义一个docker容器(container)的运行环境。
# 指定基础镜像openjdk8,用于运行java程序。
FROM openjdk:8
# 指定容器内的工作目录
WORKDIR /app/
# 将Dockerfile所在目录(docker-test)的bin目录复制到容器内(/app/bin/)
COPY bin bin
# 将docker-test目录下的etc目录复制到容器内(/app/etc/)
COPY etc etc
# 将target下以standalone.jar结尾的jar包复制到容器内的lib目录下(/app/lib/docker-test-standalone.jar).
COPY target/*standalone.jar lib/
# 将docker容器内的lib目录下的jar包名字写入到lib.txt(/app/lib.txt)
RUN ls lib/ > lib.txt
# 在docker容器内新建logs目录(/app/logs/)
RUN mkdir logs
# 将物理机时间时区挂在到容器中,这样容器时间与物理机系统时间一致
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
server.sh
server.sh为java服务启动脚本,内容如下:
#!/bin/bash
## java服务启动日志记录文件在容器内的路径
LOGFILE=/app/logs/start.log
## 进入容器内的bin目录
cd /app/bin
## 从/app/lib.txt文件中读取要运行的jar文件。
JAR=$(cat ../lib.txt)
## 运行程序
java ${JAVA_OPTS} -cp ../etc:../lib/${JAR} clojure.main -e "(use 'pay.system)(-main)" >> ${LOGFILE} 2>&1
注意这些目录都是针对docker容器内的目录。
config.edn
java程序运行时所需要的配置文件:
{
;; 提供给网页通信的web服务
:outer-server {:host "0.0.0.0" :port 8035 :join? false}
;; 提供给内部服务,用于接收其他服务器消息的web服务地址
:inner-server {:host "0.0.0.0" :port 8042 :join? false}
}
0.0.0.0
表示该docker容器接收访问端口8035和端口8042的所有ip。
logback.xml
logback日志配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-10contextName %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 拦截debug级别的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<file>/app/logs/docker-test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/app/logs/docker-test.log.%d{yyyy-MM-dd}</fileNamePattern>
<!-- 日志文件保留天数-->
<maxHistory>180</maxHistory>
</rollingPolicy>
<!-- 这里使用普通的输出格式-->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 这里是运行日志 -->
<logger name="app" level="info" additivity="false">
<appender-ref ref="LOG_FILE" />
</logger>
<root level="info">
<appender-ref ref="LOG_FILE" />
</root>
</configuration>
注意,日志文件的路径为docker容器内的路径/app/logs/docker-test.log
而不是物理机上的文件路径。
三、打包程序的docker镜像
进入到docker-test目录,执行下面命令:
docker build -t docker-test:0.1.0
很多时候,我们会迭代发布好多镜像,就需要为这些镜像打上tag(经常用版本号作为tag),此时要带上-t
参数。如果不带tag,则会默认使用latest作为TAG。
这样打包后的镜像被放在本地,可以使用下面命令查看本地镜像:
docker images -a
可以使用命令删除一个本地镜像:
docker rmi docker-test:0.1.0
四、运行docker容器
docker run -itd –name docker-test -e JAVA_OPTS=’-Xmx368m -Dfile.encoding=UTF-8 -Duser.timezone=GMT+08’ -p 8035:8035 -p 8042:8042 -v
/Users/admin/docker-test/logs:/app/logs -v /Users/admin/docker-test/etc:/app/etc
docker-test:0.1.0 /app/bin/server.sh
-d:
表示在后台运行该docker容器
-i:
表示保留STDIN(标准输入),用于控制台交互 。
-t:
分配tty设备,该可以支持终端登录 。
-p:
指定端口或ip映射,将物理机上的8035端口与docker容器的8035端口映射;将物理机上的8042端口与docker容器的8042端口映射。
-v:
给容器挂载存储卷,挂载到容器的某个目录。这里讲物理机上的/Users/admin/docker-test/logs
目录挂载到docker容器的/app/logs目录,这样程序输出的在logback.xml中指定的日志,实际上被写入到物理机/Users/admin/docker-test/logs
目录下对应的日志文件中。
-e:
用于指定环境变量,比如例子中就设定了jvm运行时的参数(JAVA_OPTS),在启动java程序时用到。注意-Duser.timezone=GMT+08
用于解决jvm时区与系统时区不一致,导致日志文件中输出的时间为标准时区的时间,而不是本地时间。
目录挂载的目的
- 挂载日志目录:因为程序运行的日志文件会非常之大,如果不将物理机的目录挂在容器内,那么日志将存储在容器内,而且是存储在内存中,永不了多久,内存就会爆掉。
- 挂载配置文件目录:挂载配置文件,使得可以在物理机上修改配置文件,然后重新运行容器,达到改变配置的目的。
- 注意: 如果docker是部署在集群(如k8s)中,那么无法使用目录挂载,因为你无法确定容器会运行在集群中哪个物理机上。此时,你的配置最好通过环境变量传递,日志则可以输出到标准输出。
docker-test:0.1.0 /app/bin/server.sh
:运行docker-test:0.1.0镜像的/app/bin/server.sh脚本。
至此,一个容器便启动成功。可以尝试访问物理机地址的8035或8042端口,验证是否程序是否正常运行。
但这样运行的话,容器其实是阻塞在运行脚本/app/bin/server.sh
的状态(因为在脚本中,并没有指定程序在后台运行),此时我们不能通过docker的attch
命令进入到容器。
可以通过如下命令运行docker容器:
docker run -itd –name docker-test -e JAVA_OPTS=’-Xmx368m -Dfile.encoding=UTF-8 -Duser.timezone=GMT+08’ -p 8035:8035 -p 8042:8042 -v
/Users/admin/docker-test/logs:/app/logs -v /Users/admin/docker-test/etc:/app/etc
docker-test:0.1.0 /bin/bash
这样容器内会启动/bin/bash终端,通过命令:
docker ps -a
可以找到容器ID(containerID),通过命令:
docker attach containerID
可以进入到该容器,此时界面和普通的unix命令行界面一样。在容器内的命令行界面中,输入命令:
./bin/server.sh
即可启动java程序。
可以使用如下命令查看镜像的容器是否启动成功(在STATUS中如果为up..则启动成功,如果为exit…则未启动成功):
docker ps -a
五、其他常用docker命令
停止容器
docker stop containerID
删除容器
docker rm containerID
进入容器
首先通过docker ps -a
命令可以查看到正在运行的容器ID(CONTAINER ID)。如果容器内的进程是以后台运行的方式启动的,则可以使用命令:
docker attach containerId
进入容器。
如果容器内的进程不是以后台方式启动的,那么就无法通过attach命令进入,可使用命令:
docker exec -it containerID /bin/bash
进入。
导出镜像文件
docker save -o [导出后的文件名] [镜像名称]
如,将test-0.1.0:test
镜像导出为test-0.1.0.tar
:
docker save -o test-0.1.0.tar test-0.1.0:test
导入镜像文件
docker load –input [导出后的文件名]
如,将上面导出的test-0.1.0.tar
再导入:
docker load –input test-0.1.0.tar
查看docker容器中标准输出内容
docker logs [容器名称或容器ID]
拉取docker镜像
docker pull [镜像地址]