Docker(七)—— 如何用Dockerfile制作自己的镜像

目录

需求:

一、步骤 

二、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

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方法实现呢?我们先看一下上述步骤都改变了什么

  1. yum下载了vim和net-tools
  2. 暴露了80端口
  3. 把工作目录从官方centos默认的“/”改成“/usr/local”
  4. 指明默认命令行/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包

可以实现两人之间的聊天啦!

  • 8
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值