美文网首页spring cloud / spring boot 专题
Docker构建Java项目运行容器学习笔记一

Docker构建Java项目运行容器学习笔记一

作者: 昆仑枫 | 来源:发表于2019-03-11 10:11 被阅读12次

    基础学习资料

    Docker比较完整和详细的指令  https://yeasy.gitbooks.io/docker_practice/network/port_mapping.html

    Dockerfile 中的 CMD 与 ENTRYPOINT区别详细说明,建议看完通篇文章后再看 https://www.cnblogs.com/sparkdev/p/8461576.html

    官方文档https://docs.docker.com/engine/reference/builder/

    docker的定位,它本质上是一个轻量级的虚拟机,但是实际应用中说是 进程的容器工具 更贴切。创建一个容器当做虚拟系统来使用,然后再里面部署多个应用程序,不是docker推荐的用法。因为docker的主要优点是如下两点

    轻量级的资源隔离

    简洁一致的部署环境

    为了尽量发挥优点,所以应该每个容器运行一个应用,一般是只有一个进程,单一功能。比如证券交易系统有资金、交易、用户系统,各对应一个项目,应该部署到不同的容器。一个应用如果有多个进程,尽量分开部署到不同容器实例。如果应用要部署一个网关nginx做反向代理,部署到应用的容器里还勉强合理。

    接着要理解docker的重要概念

    镜像和容器

    镜像和容器的关系类似于虚拟机(比如VM或Virtual Box)的系统镜像和系统实例,镜像是容器的构建基础,容器是镜像的实例。基于镜像建立容器后可以做很多定制化的修改,而且也可以把修改后的容器再保存为新的镜像。

    有两种方式可以部署一个有自己的程序的docker容器,首先我们先做一些基础操作

    准备工作

    首先根据上面的第一个资料链接安装好docker,这部分不详述。

    然后通过docker search openjdk指令搜索openjdk的公开镜像,取第一个star最多的

    接着通过 docker pull openjdk:8 将jdk版本为8的openjdk镜像拉取到本地

    通过指令docker images 可以查看到是否已经拉取到镜像

    第一种方法

    直接将项目文件放在镜像里,然后每次构建带有最新项目文件的新的镜像,并根据镜像建立新的容器。

    下面通过Dockerfile构建定制镜像,新建一个目录,新建名为Dockerfile的文件,写入定制化配置

    示例:

    FROM openjdk:8

    ENV TZ=Asia/Shanghai

    COPY distribute-all.zip /home/

    RUN cd /home/ && unzip distribute-all.zip

    EXPOSE 28080 9001

    WORKDIR /home/distribute/

    CMD java -cp ./classes:./lib/*:$JAVA_HOME/lib/tools.jar:./test-classes com.dd.http.server.WebServer --config config-test.json --env test distribute > /dev/null 2>&1

    FROM指的是基于什么镜像做配置,官方已经有很多基础镜像,包括redis、mysql、jdk、openjdk,我们基于这些镜像作进一步的定制化即可。

    ENV 一行是配置时区为上海

    Run是在镜像里执行指令,比如一些前置的指令建立文件夹,安装工具等。比如用RUN apt-get update和RUN apt-get-y install vim可以安装vim到镜像里

    COPY 是将主机目录里的文件复制到镜像中

    EXPOSE 是声明镜像后期需要暴露的端口,仅仅起到声明作用

    WORKDIR 设定后续指令工作目录,因为镜像构建配置这里的指令都是无状态的,无法继承前一条指令的状态如执行目录

    CMD 设定启动指令,有两种方式,shell方式,如CMD top -a ;以及 exec方式,如CMD  ["top”,”-a”] 。两者区别是最终在容器执行的指令为 /bin/sh -c top -a 和 top -a。其中前者会导致sh才是容器的1号进程。

    然后通过build指令构建镜像,下面指令最后的点号是根据本目录进行构建的意思

    docker build -t distribute-img .

    执行后通过docker images 发现得到了一个名为distribute-img的镜像,如果是执行成功,执行过程中会有一些中间容器生成,并被删掉,如果完全成功只会有镜像不会有中间容器被留下来。

    通过镜像创建并启动容器

    docker run -d -p 28080:28080 -p 9001:9001 distribute-img

    启动成功后通过dockercontainer ls -a可以看到一个容器正在运行。这个容器的IMAGE是distribute-img ,command是 "/bin/sh -c 'java -c…”,STATUS 是 Up xxx seconds,PORTS映射关系是0.0.0.0:9001->9001/tcp, 0.0.0.0:28080->28080/tcp。注意这个容器的配置方式会使得pid1 是sh而不是Java进程。

    如果修改为如下的写法,pid 1就会是java进程,住下这中用法指令的参数里不能有 $JAVA_HOME 这种环境变量,因为是没办法识别的。而且指令参数要划分的很细,是实际使用时如果将”cp部分写成”-cp ./classes:./lib/*:/docker-java-home/lib/tools.jar:./test-classes",进程是不能正常起来的。

    CMD ["java","-cp","./classes:./lib/*:/docker-java-home/lib/tools.jar:./test-classes","com.dd.http.server.WebServer","--config","config-test.json","--env","test","distribute","> /dev/null","2>&1"]

    第二种方法(有本地日志以及文件产生的推荐此种方式)

    项目文件放在镜像和容器外。

    具体做法是在宿主机新建一个目录,然后用run新建和启动容器时将这个目录挂载为数据卷。

    首先修改第一种方法中的Dockerfile改为如下

    FROM openjdk:8

    ENV TZ=Asia/Shanghai

    EXPOSE 28080 9001

    WORKDIR /home/volume/distribute/

    CMD java -cp ./classes:./lib/*:$JAVA_HOME/lib/tools.jar:./test-classes com.dd.http.server.WebServer --config config-test.json --env test distribute > /dev/null 2>&1

    主要是去除了移动程序文件进入镜像并解压的操作

    然后通过build指令构建镜像

    docker build -t distribute-img-volume1 .

    通过镜像创建并启动容器,注意在这个步骤绑定了外部目录作为数据盘

    docker run -d -p 28080:28080 -p 9001:9001  --mount type=bind,source=/Users/workandstudy/14_docker/01_distribute_volume1/volume,target=/home/volume distribute-img-volume1

    应用就运行起来了,可以通过docker container ls -a查看

    这个时候在宿主机或者容器里操作这个文件夹,任何变更都可以实时双向体现。

    数据卷还有一种建立方式,就是先建立一个独立的docker数据卷,然后在docker run指令挂载到新建的容器,这种方式比较复杂,在建立独立数据卷部分以后再做探讨。

    上述操作用到的主要指令

    搜索公版镜像,如搜索openjdk的公版镜像

    docker search openjdk

    查看容器列表

    docker container ls -a

    查看容器的操作日志

    docker logs -t --since="2018-02-08T13:23:37" --until "2018-02-09T12:23:37” CONTAINER_ID

    docker logs -t --since="2018-02-08T13:23:37" CONTAINER_ID

    启动已有容器

    docker container start CONTAINER_ID

    对于不做修改直接建立容器无法持续运行的镜像比如openjdk,可以通过如下指令建立容器观察其结构,openjdk:8是镜像的名字版本

    docker run -it openjdk:8  /bin/bash

    已经启动的容器直接进入容器的shell

    docker exec -it CONTAINER_ID bash

    查看数据卷列表

    docker volume ls

    Java程序的资源控制

    docker run指令建立启动容器时可以通过 --cpus 1 -m 1G 控制cpu和内存资源,但是容器里的jvm在10之前还是会只感知到宿主机的资源限制,所以需要jdk10之前要做一些额外的jvm配置参数。详细资料如下

    http://www.concurrent.work/docker/java/jvm/gc/pitfalls-about-running-java-inside-container/

    建立独立数据卷

    建立数据卷时用下面的命令构建一个数据卷,但这个指令无法指定数据卷的挂载位置,不太好用。

    docker volume create distribute_volume1

    用下面的指令查看数据卷信息,发现有个配置是 "Mountpoint": "/var/lib/docker/volumes/distribute_volume1/_data”,但这个目录无法在宿主机直接访问,不存在,即是数据卷挂在到机器中并且在里面建立了文件还是没东西,后面需要进一步了解这种用法

    docker volume inspect distribute_volume1

    总结

    以上是Docker的本地用法,两种方式里面,如果程序是无状态的,推荐使用第一种方式,每次修改后重新构建镜像发布;而程序会产生本地日志和文件的,推荐用第二种方法。Docker的使用对开发和运维人员工作量有一定增加,很多无docker时很简单的操作会麻烦很多。它优势在于大规模分布式部署和管理应用,隔离应用并管理资源使用。Docker实际使用中还需要有一些额外工具的辅助,比如Kubernetes或者Docker Compose等快速分部署的工具,在后续的文章中再做详细介绍。

    相关文章

      网友评论

        本文标题:Docker构建Java项目运行容器学习笔记一

        本文链接:https://www.haomeiwen.com/subject/oykvpqtx.html