目录
四、实战 —— 构建jar包的镜像(helloworld版本)
需求:
自己写了一个小程序,如何带着环境打包成一个镜像,然后发布给别人run起来呢?
以前程序员需要交付一个jar包或者war包,但是现在公司的交付标准都是docker镜像!
一、步骤
1. 编写Dockerfile脚本
2. docker build 构建
3. docker run 运行
4.(可选)docker push 发布(dockerhub 、阿里云镜像仓库)
二、Dockerfile
Dockerfile是什么?和Makefile差不多。
1. 指令都是大写字母
2. 从上到下执行
3. “#”表示注释
4. 每一条指令都会创建并提交一个新的镜像层。所以Dockerfile中一定要惜字如金,能写到一行的指令,一定要写到一行,否则会造成镜像臃肿。
5. Dockerfile中提到所有文件一定要和Dockerfile文件在同一级父目录下,可以为Dockerfile父目录的子目录。
6. Dockerfile中相对路径默认都是Dockerfile所在的目录
常用指令:
FROM : 基础镜像。这个镜像基于哪个镜像。dockerHub里99%的镜像都是FROM scratch。
MAINTAINER :作者信息。姓名+邮箱
RUN :镜像build时需要运行的命令。
可以理解为在FROM 基础镜像之上想要运行什么命令得到一个新镜像。拿下文的例子来说,我FROM centos,但是centos这个基础镜像没有vim命令,那我就在这个centos镜像的命令行里执行安装vim的命令,如此需求的命令就用RUN来写。RUN命令得到的结果作为镜像的一部分,而CMD命令的结果是容器的一部分,所以可以理解成镜像和容器的区别。
CMD :指定容器启动run时要运行的命令,只有最后一个会生效。会被run命令行传来的命令替代。
java -jar xx.jar命令一般会写在CMD或者ENTRYPOINT里。
CMD的RUN的不同:
- RUN 是在 docker build 时运行。
- CMD 是在docker run 时运行。
ENTRYPOINT :和CMD一样,指定容器启动时要运行的命令。不会被命令行传来的替代,而是追加在后面执行。
CMD和ENTRYPOINT的区别:
我们写一个dockerfile文件
FROM centos
CMD ["ls","-a"]
构建并运行这个镜像 ,在run命令后加一个-l,发现并不能实现“ls -al”的功能。因为“-l”把“ls -a”整个替换掉了。
如果我们用ENTRYPOINT去代替CMD,就可以实现命令的追加
ADD : 把宿主机文件拷贝到镜像中。
ADD <src 宿主机路径> <des 镜像/容器内部路径> # <src 宿主机路径> 为./或.表示“docker build -f Dockerfile -t name [上下文路径]”中指定的上下文路径; 为 *.jar 表示上下文路径(如下文所述,最好设置成dockerfile所在的目录下)的所有jar包 # <des 镜像/容器内部路径> 一般写成目录的形式 为./或.表示镜像的工作目录 为 /root/test 表示拷贝到/root/test目录下,如果不存在会自动创建 # ADD . . :<src>默认是上下文路径,<des>默认是镜像的工作目录WORKDIR
COPY :类似ADD,将宿主机的文件拷贝到镜像中(同样需求下,官方推荐使用 COPY)
WORKDIR : 指明镜像默认的工作目录。
VOLUME : 挂载到宿主机的哪个目录。代替 run -v命令 (run这个镜像的时候就不用写了,因为已经设置好了)
EXPOSE : 暴露端口。代替 run -p命令
ONBUILD :
ENV :设置镜像的环境变量。替代run -e命令。
问题:使用DockerFile制作镜像和使用commit提交一个镜像有什么区别呢?
请看下面的实战理解。
三、 实战 —— 构建自己的centos
首先我们来看一下从dockerhub上拉取的官方的centos本身是怎样的:
用docker inspect 容器id命令查看一下官方centos开放的端口号,发现官方centos并没有对外暴露端口。
此外,官方的centos只具备最基本的命令,不支持"ifconfig"、"vim"等命令。所以我们要做的就是把官方的centos镜像作为基础镜像,构建支持"ifconfig"、"vim"的centos镜像。
1. dockerfile编写
新建/root/myDocker2目录,然后执行vim Dockerfile:
2. build构建
注意,一般要切换到Dockerfile所在目录下执行build命令。
在执行yum -y install vim命令时报出了错误,这不是步骤出现了错误,而是由于 “2020 年 12 月 8 号,CentOS 官方宣布了停止维护 CentOS Linux 的计划” 带来的yum源的问题,解决方法如下:
【已解决】Error: Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist_华仔仔coding的博客-CSDN博客在 CentOS 8 上使用 yum 包管理工具安装 vim 时,出现以下的报错提示信息Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist解决方法首先,进入到 yum 的 repos 目录cd /etc/yum.repos.d/之后,修改 centos 文件内容sed -i 's/mirrorlist/#mirrohttps://blog.csdn.net/weixin_43252521/article/details/124409151注意应该进入centos镜像(就是Dockerfile里FROM的那个)更改里面的文件,别傻不拉几的把宿主机的给改了(说的就是我自己)
yum update -y之后,记得提交新镜像,要不然无法保存刚才的更改!
哎还要改一下Dockerfile第一行的FROM,改成我们刚刚提交的新镜像。
终于build成功了!!
build命令最后一个“.”的含义很重要!它是上下文路径,指镜像构建时,需要打包上传到Docker server 引擎的目录。
解释:Dockerfile中的COPY、ADD指令需要把宿主机文件拷贝到镜像中,上下文路径就是指明了这些文件的路径,“.”表示上下文路径就是 Dockerfile 所在的位置。
COPY、ADD这些指令是由docker-server去执行的。由于 docker 的运行模式是 C/S,构建过程是在 docker-server 引擎下完成的,docker-server 引擎无法知道我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。build结束后,docker-server会将其自动清理。
理解不了没关系,只需要记住:
1)把镜像要用到的文件(比如jar包、war包)和dockerfile放在同一个目录下
2)在执行docker build前切换到这个目录下,
3)build命令的最后跟着一个“.”
4)写ADD、COPY命令时,<src>也可以只写一个“.”就行了。
PS: 当时用cmake的时候,我们不就是直接背过了build文件夹和CMakeList.txt的位置关系,然后闭眼敲cmake命令的吗!
docker images命令可以查看到我们构建好的镜像
3. run 运行
四、用docker commit实现
如何用commit方法实现呢?我们先看一下上述步骤都改变了什么
- yum下载了vim和net-tools
- 暴露了80端口
- 把工作目录从官方centos默认的“/”改成“/usr/local”
- 指明默认命令行/bin/bash
所以我们接下来的任务就是暴露80端口,把工作目录改成/usr/local,命令行虽然官方也是/bin/bash,但是也演示一下如何指定命令行吧。
1. 运行官方的centos镜像,并进入。
80端口、默认工作目录、命令行都通过docker run的参数去指定。
不理解的是,那个PORTS为什么不显示80端口,而用inspect才能看出来
2. 在容器内运行如下命令
yum -y install vim
yum -y install net-tools
然后我们测试vim和ifconfig命令发现都可以了。
3. 提交容器快照,成为新镜像
四、实战 —— 构建jar包的镜像(helloworld版本)
之前的实战都是直接更改基础镜像,但是我们更多的需求是将jar包和其环境打包成镜像!
说明:在VMware虚拟机中安装docker,然后进行如下操作
1. 登录root用户,在/root目录下新建文件夹myDocker3:/root/myDocker3
2. 通过网易邮箱把hello.jar包发送给虚拟机,下载到/root/myDocker3路径下
3.测试程序是否可以跑起来
4. 在/root/myDocker3路径下新建Dockerfile文件,编辑内容如下
FROM openjdk:8
WORKDIR /usr/local/helloworld
# 把宿主机/root/myDocker3/路径下所有文件拷贝到 容器的/usr/local/helloworld下
COPY . .
# 运行容器的时候,直接运行这个jar包
CMD java -jar hello.jar
5. 构建镜像
在/root/myDocker3路径下执行如下构建命令
docker build -f Dockerfile -t hello:0.1 .
6. 运行镜像
docker run hello:0.1
在容器运行后成功输出Hello world!
7. 其他diy测试
我们在run命令后加上/bin/bash,发现jar包不被运行了。原因是Dockerfile中的CMD指定的命令会被命令行传入的命令所替代,我们的“java -jar hello.jar”命令被"/bin/bash"命令替代了。所以jar包没有被运行。
而第二次尝试中,我们在run命令后面加上java -jar hello.jar,此时Dockerfile中的“java -jar hello.jar”命令被"java -jar hello.jar"命令替代,照样运行jar包。
通过本次尝试我们知道,如果想要在容器里运行jar包,不用非要在Dockerfile里用CMD写java -jar命令,因为有时候jar包的运行是需要带参数的,比如“java -jar xx.jar IP 端口号”我们写dockerfile的时候没法知道IP、端口号是什么。那么通过本次尝试我们知道,可以在docker run的时候通过命令行参数的形式运行jar包,这样就可以手动输入jar包的参数了。
这也是为我们的下一个实战做准备。
五、实战 —— 构建jar包的镜像(两个容器通信版本)
说明:在VMware虚拟机中安装docker,然后进行如下操作
1. 登录root用户,在/root目录下新建文件夹myDocker:/root/myDocker
2. 通过网易邮箱把Chat.jar包和jdk的压缩包发送给虚拟机,下载到/root/myDocker路径下。
3. 测试程序是否可以跑起来。
如上图,运行很正常,说明网络等各方面没问题,如果待会运行不成功一定不是jar包的问题。
4. 在/root/myDocker路径下新建Dockerfile文件,编辑内容如下:
# mycentos:0.2是我们自己构建的,已经安装了ip addr命令
FROM mycentos:0.2
# 我们的程序看作软件,放在/usr/local文件夹下
WORKDIR /usr/local/ChatApp
# copy and compress jdk to /usr/local
ADD jdk-8u311-linux-x64.tar.gz /usr/local/
# copy chat.jar to WORKDIR
COPY ./*.jar .
# 设置jdk的环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_311
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin
为什么要写的这么麻烦?用下面的Dockerfile不行吗?
FROM openjdk:8
WORKDIR /usr/local/ChatApp
COPY . .
哎因为我们要在容器内部使用ip addr命令查看容器的IP地址,但是openjdk:8没有这个命令,甚至没有yum命令,所以无法安装yum -y install net-tools。所以我们只能用之前构建过的带有net-tools的镜像mycentos:0.2(或者mycentos:0.1)作为基础镜像,不能用openjdk:8了。至于jdk就得自己用压缩包ADD到镜像中。
5. 构建镜像
docker build -f Dockerfile -t chat:0.1 .
6. 运行镜像 并实现两个镜像之间的通信
docker run -it -p 8888 --name="JJ" chat:0.1 /bin/bash
docker run -it -p 9999 --name="QQ" chat:0.1 /bin/bash
因为我们要用到容器的端口号,所以在运行时就规定好其端口号是8888,并命名为“JJ”,我们再开启一个容器,规定端口号为9999,命名为“QQ”。获取他们两个的IP地址。
容器JJ:172.17.0.3 端口号:8888
容器QQ:172.17.0.2 端口号:9999
现在我们知道了chat.jar包运行的参数(1)对方IP (2)对方port (3)自己开放的port (4)对方姓名
然后在两个容器中,分别运行chat.jar包
可以实现两人之间的聊天啦!