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


    目录

    需求:

    一、步骤 

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

    1. FROM centos
    2. CMD ["ls","-a"]

    构建并运行这个镜像 ,在run命令后加一个-l,发现并不能实现“ls -al”的功能。因为“-l”把“ls -a”整个替换掉了。

    如果我们用ENTRYPOINT去代替CMD,就可以实现命令的追加

    ADD                   :  把宿主机文件拷贝到镜像中。

    1. ADD
    2. #
    3. 为./或.表示“docker build -f Dockerfile -t name [上下文路径]”中指定的上下文路径;
    4. 为 *.jar 表示上下文路径(如下文所述,最好设置成dockerfile所在的目录下)的所有jar包
    5. # 一般写成目录的形式
    6. 为./或.表示镜像的工作目录
    7. 为 /root/test 表示拷贝到/root/test目录下,如果不存在会自动创建
    8. # ADD . . :默认是上下文路径,默认是镜像的工作目录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命令时,也可以只写一个“.”就行了。

    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. 在容器内运行如下命令

    1. yum -y install vim
    2. 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文件,编辑内容如下

    1. FROM openjdk:8
    2. WORKDIR /usr/local/helloworld
    3. # 把宿主机/root/myDocker3/路径下所有文件拷贝到 容器的/usr/local/helloworld下
    4. COPY . .
    5. # 运行容器的时候,直接运行这个jar包
    6. 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文件,编辑内容如下:

    1. # mycentos:0.2是我们自己构建的,已经安装了ip addr命令
    2. FROM mycentos:0.2
    3. # 我们的程序看作软件,放在/usr/local文件夹下
    4. WORKDIR /usr/local/ChatApp
    5. # copy and compress jdk to /usr/local
    6. ADD jdk-8u311-linux-x64.tar.gz /usr/local/
    7. # copy chat.jar to WORKDIR
    8. COPY ./*.jar .
    9. # 设置jdk的环境变量
    10. ENV JAVA_HOME /usr/local/jdk1.8.0_311
    11. ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
    12. ENV PATH $PATH:$JAVA_HOME/bin

    为什么要写的这么麻烦?用下面的Dockerfile不行吗? 

    1. FROM openjdk:8
    2. WORKDIR /usr/local/ChatApp
    3. 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. 运行镜像 并实现两个镜像之间的通信

    1. docker run -it -p 8888 --name="JJ" chat:0.1 /bin/bash
    2. 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包

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

  • 相关阅读:
    Python是否被高估了?
    Codeforces Round #658 (Div. 2) B. Sequential Nim
    kafka初体验
    公司oa是什么?一般公司oa有什么样功能?
    应急监管双重预防机制数字化管理解决方案
    ICP问题 SVD方法推导(Markdown版)
    【SQLAlChemy】表之间的关系,外键如何使用?
    二维矩阵内的BFS搜索
    京东11.11真便宜闭眼买,联合众品牌共倡“真低价”
    河北安新复合型水稻 国稻种芯·中国水稻节:雄安生态示范区
  • 原文地址:https://blog.csdn.net/qq_44886213/article/details/127845262