5-Docker镜像构建

5-Docker镜像构建

docker commit

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

image-20220505154814005

image-20220505155054324

docker ps -a   #列出所有的container

image-20220505155126325

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

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

image-20220505155224067

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

image-20220505155701818

Dockerfile

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

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

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

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 也向我们展示了镜像的分层结构,每一层由上至下排列。

docker history centos-with-test-dockerfile

image-20220505173008066

镜像分层结构

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

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

RFOM centos
...

image-20220505174827453

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

RFOM centos
RUN touch test
COPY testfile /
[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命令

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

初始DockerFile

构建步骤

1. 编写一个dockerFile文件

2.docker build 构建成为一个镜像

3. docker run 运行镜像

4. docker push 发布镜像(DockerHub、阿里云镜像)


#
每个保留关键字(指令)都是必须大写字母
执行从上到下顺序执行
# 表示注释
每个指令都会创建提交一个新的镜像层,并提交!
# 创建一个dockerfile文件, 名字可以随机
# 文件的内容 指定(大写) 参数
 
FROM centos
 
VOLUME ["volume01", "volume02"] #匿名挂载
 
CMD echo "----end----"
CMD /bin/bash
 
# 这里的每一个命令都是镜像的一层!
[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

启动自己的容器

[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 是挂载的目录
[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": ""
            }


# 创建一个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

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

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

-v 卷名:容器内路径 

数据卷容器

多个mysql同步数据

image-20220607181214361

image-20220607181301528

image-20220607181329794

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

image-20220607181419895

#   测试1:删除docker01后,docker02和docker03是否还可以访问原来docker01下创建的的文件  依旧可以访问

#   测试2:删除docker01后,docker02和docker03之间依然 可以完成同步

数据共享

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实现数据共享

[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实现与上一个数据同步
FROM            # 基础镜像,一切从这里开始构建
MAINTAINER      # 镜像是谁写的, 姓名+邮箱
RUN             # 镜像构建的时候需要运行的命令
ADD             # 步骤, tomcat镜像, 这个tomcat压缩包!添加内容
WORKDIR         # 镜像的工作目录
VOLUME          # 挂载的目录
EXPOSE          # 保留端口配置
CMD             # 指定这个容器启动的时候要运行的命令,只有最后一个会生效可被替代
ENTRYPOINT      # 指定这个容器启动的时候要运行的命令, 可以追加命令
ONBUILD         # 当构建一个被继承DockerFile 这个时候就会运行 ONBUILD 的指令,触发指令
COPY            # 类似ADD, 将我们文件拷贝到镜像中
ENV             # 构建的时候设置环境变量!

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

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

编写一个centos dockerfile

基础知识:**

1、每个保留关键字(指令)都是必须是大写字母

2、执行从上到下顺序

3、# 表示注释

4、每一个指令都会创建提交一个新的镜像层,并提交

image-20220607182002778

dockerfile

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

# 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

# 编写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

# 编写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 指定了
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. 构建镜像

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

# 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. 发布项目(由于做了卷挂载,我们就可以直接在本地发布项目了)
在/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/目录下就可以看到日志信息了:

[root@localhost tomcatlogs]# cat catalina.out 

shell格式和exec格式

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

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格式需要指定使用什么脚本来执行 不会直接解析变量

FROM centos:7
CMD [ "echo", "$HOME" ]
#以上会输出$HOME

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

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

posted @ 2022-07-06 16:47  机猿巧合  阅读(158)  评论(0编辑  收藏  举报