目录
需求:
一、步骤
二、Dockerfile
三、 实战 —— 构建自己的centos
1. dockerfile编写
2. build构建
3. run 运行
四、用docker commit实现
四、实战 —— 构建jar包的镜像(helloworld版本)
五、实战 —— 构建jar包的镜像(两个容器通信版本)
自己写了一个小程序,如何带着环境打包成一个镜像,然后发布给别人run起来呢?
以前程序员需要交付一个jar包或者war包,但是现在公司的交付标准都是docker镜像!
1. 编写Dockerfile脚本
2. docker build 构建
3. docker run 运行
4.(可选)docker push 发布(dockerhub 、阿里云镜像仓库)
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
# 为./或.表示“docker build -f Dockerfile -t name [上下文路径]”中指定的上下文路径; 为 *.jar 表示上下文路径(如下文所述,最好设置成dockerfile所在的目录下)的所有jar包 # 一般写成目录的形式 为./或.表示镜像的工作目录 为 /root/test 表示拷贝到/root/test目录下,如果不存在会自动创建 # ADD . . : 默认是上下文路径, 默认是镜像的工作目录WORKDIR
COPY :类似ADD,将宿主机的文件拷贝到镜像中(同样需求下,官方推荐使用 COPY)
WORKDIR : 指明镜像默认的工作目录。
VOLUME : 挂载到宿主机的哪个目录。代替 run -v命令 (run这个镜像的时候就不用写了,因为已经设置好了)
EXPOSE : 暴露端口。代替 run -p命令
ONBUILD :
ENV :设置镜像的环境变量。替代run -e命令。
问题:使用DockerFile制作镜像和使用commit提交一个镜像有什么区别呢?
请看下面的实战理解。
首先我们来看一下从dockerhub上拉取的官方的centos本身是怎样的:
用docker inspect 容器id命令查看一下官方centos开放的端口号,发现官方centos并没有对外暴露端口。
此外,官方的centos只具备最基本的命令,不支持"ifconfig"、"vim"等命令。所以我们要做的就是把官方的centos镜像作为基础镜像,构建支持"ifconfig"、"vim"的centos镜像。
新建/root/myDocker2目录,然后执行vim Dockerfile:
注意,一般要切换到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命令时,
也可以只写一个“.”就行了。 PS: 当时用cmake的时候,我们不就是直接背过了build文件夹和CMakeList.txt的位置关系,然后闭眼敲cmake命令的吗!
docker images命令可以查看到我们构建好的镜像
如何用commit方法实现呢?我们先看一下上述步骤都改变了什么
所以我们接下来的任务就是暴露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包和其环境打包成镜像!
说明:在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包的参数了。
这也是为我们的下一个实战做准备。
说明:在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包
可以实现两人之间的聊天啦!