5-Docker镜像构建

5-Docker镜像构建

docker commit#

Copy
Copy
docker run -it ubuntu # -it 参数的作用是以交互模式进入容器,并打开终 exit #退出容器

image-20220505154814005

image-20220505155054324

Copy
Copy
docker ps -a   #列出所有的container

image-20220505155126325

Copy
Copy
docker ps -l   #列出最后一次启动的container

NAMES 是docker为容器随机分配的名字

image-20220505155224067

Copy
Copy
docker commit brave_einstein ubuntu-with-vi #将容器brave_einstein保存为镜像 镜像名字为 ubuntu-with-vi

image-20220505155701818

Dockerfile#

在当前目录创建文件#vim dockerfile 文件内容如下

Copy
Copy
FROM centos RUN cd /etc/yum.repos.d/ RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* RUN yum update -y RUN yum clean all RUN yum makecache EXPOSE 80 RUN yum -y install vim RUN yum -y install net-tools

自2022年1月31日起,CentOS团队从官方镜像中移除CentOS 8的所有包,但软件包仍在官方镜像上保留一段时间。现在被转移到https://vault.centos.org。如需继续运行旧CentOS 8,可以在/etc/yum.repos中更新repos.d,使用vault.centos.org代替mirror.centos.org

Copy
Copy
sudo sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* sudo sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*

运行 docker build 命令,-t 将新镜像命名为 centos-with-test-dockerfile

Copy
Copy
docker history centos-with-test-dockerfile . #-t重命名 .表示当前目录 Sending build context to Docker daemon 32.26 kB           ④    #docker将 build context 发送给daemon #中间就是按照写的顺序执行 Step 10/10 : RUN yum -y install net-tools ---> Running in 49da877d6e87 #启动 ID 为 49da877d6e87 的临时容器,在容器中通过 apt-get 安装net-tools Complete! Removing intermediate container 49da877d6e87 #删除临时容器 ---> 7657afa4abf3 Successfully built 7657afa4abf3 安装 成功其 ID 为 7657afa4abf3

创建过程中出现的第一个id为基础镜像id 第二个为执行命令的临时容器id 最后一个为创建成功的容器id

可以通过docker history+镜像名字 查看创建过程

docker history 也向我们展示了镜像的分层结构,每一层由上至下排列。

Copy
Copy
docker history centos-with-test-dockerfile

image-20220505173008066

镜像分层结构#

Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的

Copy
Copy
FROM debain #1 RUN apt-get install emacs #2 RUN apt-get install apache2 #3 CMD [“/bin/bash”] #4
  1. 新镜像不再是从 scratch 开始,而是直接在 Debian base 镜像上构建。

  2. 安装 emacs 编辑器。

  3. 安装 apache2。

  4. 容器启动时运行 bash。

image-20220505173336636

分层最大的好处就是共享资源 当容器运行的时候 内存中只需要加载一份 base镜像就可以为所有容器服务

磁盘中也只保存一份base镜像就可以了

每个容器内的修改会限制在容器内 当一个容器修改了/etc文件时 其他容器的不会更改

Copy-on-Write 特性#

当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

image-20220505173552136

对镜像所有的修改都只会发生在新添加的容器层内

  1. 只有容器层是可写的,容器层下面的所有镜像层都是只读的

镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。

  1. 添加文件

在容器中创建文件时,新文件被添加到容器层中

  1. 读取文件

在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。

  1. 修改文件

在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。

  1. 删除文件

在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。

只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改 同时镜像可以被多个容器共享

镜像的缓存技术#

镜像的缓存特性

Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。

比如上一个镜像是 centos-with-test-dockerfile

Copy
Copy
RFOM centos ...

image-20220505174827453

本地镜像中已经有了centos镜像 就不会再去下载而是直接使用,在centos镜像的基础上在执行后面的内容

Copy
Copy
RFOM centos RUN touch test COPY testfile /
Copy
Copy
[root@MyMachine ~]# docker build -t centos-docker-test3 . Sending build context to Docker daemon 18.43kB Step 1/3 : FROM centos ---> 5d0da3dc9764 Step 2/3 : RUN touch test ---> Running in 039c08b73f93 Removing intermediate container 039c08b73f93 ---> 8103cb5fd082 Step 3/3 : COPY testfile / COPY failed: file not found in build context or excluded by .dockerignore: stat testfile: file does not exist [root@MyMachine ~]#

#docker build –no-cache 构建的时候不使用缓存

Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的

只要某一层发生变化 其上面所有层缓存都失效

即使改变dockerfile文档里面语句的顺序 缓存也会失效。

除了构建时候需要缓存 docker下载镜像的时候同样需要
docker pull 命令输出显示第一层(base 镜像)已经存在,不需要下载。
会显示 already exitsts
也可以通过docker history验证这个信息

image-20220505180314265

dockerfile命令#

Copy
Copy
FROM #基础镜像,一切从这里开始 MAINTAINER #镜像是谁写的,姓名+邮箱 RUN #镜像构建的时候需要执行的命令 ADD #添加内容, WORKDIR #镜像的工作目录 VOLUME #卷挂载的目录 EXPOSE #暴漏端口配置,与docker run -p 宿主机端口:容器内端口 效果一样 CMD #指定这个容器启动的时候要运行的命令,只有最后一个会生效 ENTRYPOINT #指令这个容器启动的时候执行的命令 ONBUILD #当构建一个被继承的DockerFile时,会运行ONBUILD的指令,触发指令 COPY #类似于ADD,将我们文件拷贝到镜像中 ENV #构建的时候设置环境变量

初始DockerFile

Copy
Copy
构建步骤 1. 编写一个dockerFile文件 2.docker build 构建成为一个镜像 3. docker run 运行镜像 4. docker push 发布镜像(DockerHub、阿里云镜像) # 每个保留关键字(指令)都是必须大写字母 执行从上到下顺序执行 # 表示注释 每个指令都会创建提交一个新的镜像层,并提交!
Copy
Copy
# 创建一个dockerfile文件, 名字可以随机 # 文件的内容 指定(大写) 参数 FROM centos VOLUME ["volume01", "volume02"] #匿名挂载 CMD echo "----end----" CMD /bin/bash # 这里的每一个命令都是镜像的一层!
Copy
Copy
[root@MyMachine ~]# docker build -f dockerfile -t test/centos1.0 . Sending build context to Docker daemon 87.55kB Step 1/4 : FROM centos ---> 5d0da3dc9764 Step 2/4 : VOLUME ["volume01", "volume02"] ---> Running in 078200b78112 Removing intermediate container 078200b78112 ---> 06e2b87f5512 Step 3/4 : CMD echo "----end----" ---> Running in 3791edc49723 Removing intermediate container 3791edc49723 ---> 8e3b48e75e5c Step 4/4 : CMD /bin/bash ---> Running in 9e04335f2ad5 Removing intermediate container 9e04335f2ad5 ---> 63f2cfec005a Successfully built 63f2cfec005a Successfully tagged test/centos1.0:latest [root@MyMachine ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test/centos1.0 latest 63f2cfec005a 48 seconds ago 231MB

启动自己的容器

Copy
Copy
[root@MyMachine ~]# docker run -it test/centos1.0 /bin/bash [root@c9b1c83be32c /]# ls -a . bin home lost+found opt run sys var .. dev lib media proc sbin tmp volume01 .dockerenv etc lib64 mnt root srv usr volume02 [root@c9b1c83be32c /]# volume01 volume02 是挂载的目录
Copy
Copy
[root@MyMachine /]# docker inspect c9b1c83be32c #"Mounts" 会显示挂载目录的宿主机目录路径 "Mounts": [ { "Type": "volume", "Name": "5f75cc516a695fda504a3cd73b5c457d90ef720101b70e4c0f47de9eae007b70", "Source": "/var/lib/docker/volumes/5f75cc516a695fda504a3cd73b5c457d90ef720101b70e4c0f47de9eae007b70/_data", "Destination": "volume01", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" }, { "Type": "volume", "Name": "d015280192a1fb9eed4aff30f138efb8bc2fd40973b9942b6fe7ce90a43fe886", "Source": "/var/lib/docker/volumes/d015280192a1fb9eed4aff30f138efb8bc2fd40973b9942b6fe7ce90a43fe886/_data", "Destination": "volume02", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" }
Copy
Copy
# 创建一个dockerfile文件,名字可以随机,建议 dockerfile [root@localhost docker-test-volume]# vim dockerfile # 文件中的内容:指令(大写) 参数 FROM centos VOLUME ["volume01","volume02"] #匿名挂载 CMD echo"----end----" CMD /bin/bash # 这里的每个命令,就是镜像的一层!

image-20220607180710621

注意:我们这里的 dockerfile 是我们编写的文件名

image-20220607180817297

这两个卷和外部一定有两个同步的目录!

查看一下卷挂载在主机上的路径

docker inspect 容器id

image-20220607180933299

这种方式我们未来使用十分的多,因为我们通常会构建自己的镜像!

假设构建镜像的时候没有挂在卷,要手动镜像挂载即可: (参考上文具名和匿名挂载)

Copy
Copy
-v 卷名:容器内路径

数据卷容器#

多个mysql同步数据

image-20220607181214361

image-20220607181301528

image-20220607181329794

在docker03下创建docker03文件后,进入docker01发现也依旧会同步过来:

image-20220607181419895

Copy
Copy
# 测试1:删除docker01后,docker02和docker03是否还可以访问原来docker01下创建的的文件 依旧可以访问 # 测试2:删除docker01后,docker02和docker03之间依然 可以完成同步

数据共享#

Copy
Copy
docker run -it --name docker01 test/centos1.0 [root@MyMachine ~]# docker run -it --name docker01 test/centos1.0 [root@85c027f10258 /]# ls bin home lost+found opt run sys var dev lib media proc sbin tmp volume01 etc lib64 mnt root srv usr volume02 [root@85c027f10258 /]# echo "hahaha" >volume01/test #共享目录中写入一个文件 [root@85c027f10258 /]# docker run -it --name docker02 --volumes-from docker01 test/centos1.0 #与docker01共享目录 [root@MyMachine /]# docker run -it --name docker02 --volumes-from docker01 test/centos1.0 [root@e47d2ca07444 /]# ls bin home lost+found opt run sys var dev lib media proc sbin tmp volume01 etc lib64 mnt root srv usr volume02 [root@e47d2ca07444 /]# cat volume01/test #可以直接看到docker01写入的内容 hahaha [root@e47d2ca07444 /]#

多个mysql实现数据共享

Copy
Copy
[root@MyMachine ~]# docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 [root@MyMachine ~]# docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7 #from实现与上一个数据同步
Copy
Copy
FROM # 基础镜像,一切从这里开始构建 MAINTAINER # 镜像是谁写的, 姓名+邮箱 RUN # 镜像构建的时候需要运行的命令 ADD # 步骤, tomcat镜像, 这个tomcat压缩包!添加内容 WORKDIR # 镜像的工作目录 VOLUME # 挂载的目录 EXPOSE # 保留端口配置 CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效可被替代 ENTRYPOINT # 指定这个容器启动的时候要运行的命令, 可以追加命令 ONBUILD # 当构建一个被继承DockerFile 这个时候就会运行 ONBUILD 的指令,触发指令 COPY # 类似ADD, 将我们文件拷贝到镜像中 ENV # 构建的时候设置环境变量!

容器之间的配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。

但是 持久化到了本地,本地的数据是不会删除的

编写一个centos dockerfile

Copy
Copy
基础知识:** 1、每个保留关键字(指令)都是必须是大写字母 2、执行从上到下顺序 3、# 表示注释 4、每一个指令都会创建提交一个新的镜像层,并提交

image-20220607182002778

dockerfile#

Copy
Copy
FROM # 基础镜像,一切从这里开始构建 MAINTAINER # 镜像是谁写的,姓名+邮箱 RUN # 镜像构建的时候需要运行的命令 ADD # 步骤:tomcat镜像,这个tomcat压缩包! 添加内容 WORKDIR # 镜像的工作目录 VOLUME # 挂载的目录 EXPOSE # 暴露端口配置,跟 -p 是一个道理 CMD # 指定这个容器启动时要执行的命令,只有最后一个命令会生效,可悲替代 ENTRYPOINT # 指定这个容器启动的时候要执行的命令,可以追加命令 ONBUILD # 当构建一个被继承DockerFile 这个时候就会运行ONBUILD的指令。触发指令 COPY # 类似ADD,将我们文件拷贝到镜像中 ENV # 构建的时候设置环境变量,跟 -e 是一个意思 # CMD 和 ENTRYPOINT 的区别说明:(后面也会介绍) # 若CMD 和 ENTRYPOINT 后跟的都是 ls -a 这个命令,当docker run 一个容器时,添加了 -l 选项,则CMD里的ls -a 命令就会被替换成-l;而ENTRYPOINT中的 ls -a会追加-l变成 ls -a -l

image-20220607182139394

创建一个自己的centos7dockerfile

Copy
Copy
# 1. 编写Dockerfile的文件 [root@MyMachine ~]# cat mydockerfile-centos FROM centos:7 MAINTAINER xiaohong<7758991@qq.com> ENV MYPATH /usr/local #定义变量 WORKDIR $MYPATH # 镜像的工作目录 /usr/local RUN yum -y install vim RUN yum -y install net-tools EXPOSE 80 CMD echo $MYPATH CMD echo "---end---" CMD /bin/bash # 2. 通过这个文件构建镜像 # 命令 docker build -f dockerfile文件路径 -t 镜像名:[tag] . [root@MyMachine ~]# docker build -f mydockerfile-centos -t mycentos:0.1 . Successfully built d2d9f0ea8cb2 Successfully tagged mycentos:0.1

对比:

之前的原生的centos

image-20220607182357824

我们增加之后的镜像

image-20220607182441314

我们可以列出本地镜像的变更历史:

image-20220607182519711

调试dockerfile#

RUN、CMD 和 ENTRYPOINT 这三个 Dockerfile 指令看上去很类似

  1. RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。

  2. CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。如果docker镜像启动成容器时有命令 CMD这个命令会被覆盖掉 run后面必须是完整的命令 命令参数无法直接直接传递给CMD

  3. ENTRYPOINT 配置容器启动时运行的命令 也就是构建好的镜像 镜像启动成容器时执行的命令 可以在启动容器时追加命令参数到dockerfile中的命令

    相当于 会 把 docker run 后 面 的 参 数 当 作 传 递 给
    ENTRYPOINT 指令的参数。

测试CMD

Copy
Copy
# 编写dockerfile文件 $ vim dockerfile-test-cmd FROM centos CMD ["ls","-a"] # 构建镜像 $ docker build -f dockerfile-test-cmd -t cmd-test:0.1 . # 运行镜像 $ docker run cmd-test:0.1 . .. .dockerenv bin dev # 想追加一个命令 -l 成为ls -al $ docker run cmd-test:0.1 -l #这里-l报错 docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown. ERRO[0000] error waiting for container: context canceled # cmd的情况下 -l 替换了CMD["ls","-l"]。 -l 不是命令,所以报错

测试ENTRYPOINT

Copy
Copy
# 编写dockerfile文件 $ vim dockerfile-test-entrypoint FROM centos ENTRYPOINT ["ls","-a"] $ docker run entrypoint-test:0.1 . .. .dockerenv bin dev etc home lib lib64 lost+found ... # 我们的命令,是直接拼接在我们的ENTRYPOINT命令后面的 $ docker run entrypoint-test:0.1 -l #这里-l不报错 total 56 drwxr-xr-x 1 root root 4096 May 16 06:32 . drwxr-xr-x 1 root root 4096 May 16 06:32 .. -rwxr-xr-x 1 root root 0 May 16 06:32 .dockerenv lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin drwxr-xr-x 5 root root 340 May 16 06:32 dev drwxr-xr-x 1 root root 4096 May 16 06:32 etc drwxr-xr-x 2 root root 4096 May 11 2019 home lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64 ....
  1. 编写Dockerfile文件,官方命名: Dockerfile ,build会自动寻找这个文件,就不要 -f 指定了
Copy
Copy
FROM centos:7 MAINTAINER JIYUANQIAOHE<123456@qq.com> COPY readme.txt /usr/local/readme.txt#宿主机目录需要先下 touch readme.txt ADD jdk-8u161-linux-x64.tar.gz /usr/local/ ADD apache-tomcat-8.0.53.tar.gz /usr/local RUN yum -y install vim ENV MYPATH /usr/local WORKDIR $MYPATH ENV JAVA_HOME /usr/local/jdk1.8.0_161 ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV CATALINA_HOME /usr/local/apache-tomcat-8.0.53 ENV CATALINA_BASH /usr/local/apache-tomcat-8.0.53 ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin EXPOSE 8080 CMD /usr/local/apache-tomcat-8.0.53/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.0.53/bin/logs/catalina.out
  1. 构建镜像

    Copy
    Copy
    # docker build -t diytomcat . diytomcat是定义的镜像名
  2. 启动镜像,创建容

Copy
Copy
# docker run -d -p 9090:8080 --name kuangshentomcat02 -v /home/build/tomcat/test:/usr/local/apache-tomcat-8.0.53/webapps/test -v /home/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-8.0.53/logs diytomcat
  1. 发布项目(由于做了卷挂载,我们就可以直接在本地发布项目了)
Copy
Copy
在/home/build/tomcat/test目录下创建WEB-INF目录,在里面创建web.xml文件: <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> </web-app> #在回到test目录,添加一个index.jsp页面: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>hello kuangshen</title>vim </head> <body> Hello World!<br/> <% System.out.println("---my test web logs---"); %> </body> </html>

进入/home/build/tomcat/tomcatlogs/目录下就可以看到日志信息了:

Copy
Copy
[root@localhost tomcatlogs]# cat catalina.out

shell格式和exec格式#

dockfile文件,以shell格式编写
Shell格式底层会调用/bin/sh -c来执行命令,可以解析变量

Copy
Copy
RUN apt-get install python3   CMD echo "Hello world"   ENTRYPOINT echo "Hello world"  当指令执行时,shell 格式 底层会调用 /bin/sh -c <command>。 执行下面片段 ENV name heyman   ENTRYPOINT echo "Hello, $name"  执行 docker run <image> 将输出: Hello, heyman 环境变量 name 已经被值 heyman 替换。

dockfile文件,以exec格式编写
exec格式需要指定使用什么脚本来执行 不会直接解析变量

Copy
Copy
FROM centos:7 CMD [ "echo", "$HOME" ] #以上会输出$HOME FROM centos:7 CMD ["sh", "-c", "echo $HOME"] #以上会输出当前目录 如果是root用户登录的 会输出/root

*CMD 和 ENTRYPOINT 推荐使用 Exec 格式*,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。

posted @   机猿巧合  阅读(173)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示
CONTENTS