[Docker 3] Docker 镜像

13 篇文章 0 订阅
7 篇文章 0 订阅

第 3 章 Docker 镜像

3.1 镜像的内部结构

3.1.1 hello-world

  1. 拉取最小镜像——hello-world

    # 拉取 hello-world镜像
    docker pull hello-world
    
    # 查看
    docker images hello-world
    
    # 运行
    docker run hello-world
    
  2. hello-world 的 Dockerfile

    FROM scratch
    COPY hello /
    CMD ["/hello"]
    

3.1.2 bash 镜像

  • 不依赖其他镜像,从 scratch 构建
  • 其他镜像可以以之为基础进行扩展
  • 通常是各种 Linux 发行版的 Docker 镜像,如Ubuntu、Debian、CentOS等
# 下载一个 bash 镜像
docker pull centos

# 查看镜像描述:REPOSITORY TAG ID CREATED SIZE
docker images centos
  1. rootfs

    包含 /dev、/proc、/bin 等目录。内核空间是 kernel ( bootfs文件系统,Linux 刚启动时会加载 bootfs 文件系统,之后bootfs 会被卸载掉 )。

    base 镜像底层使用 Docker Host 的 kernel (bootfs),本身提供 rootfs。

  2. base 镜像提供最小安装的 Linux 发行版本

    CentOS 镜像的 Dockerfile:

    FROM scratch
    ADD centos-7-docker.tar.xz /
    CMD ["/bin/bash"]
    

    ADD : 添加 CentOS 7 rootfs 的 tar 包。制作镜像时,tar 会自动解压到 / 目录下,生成 /dev、/proc、/bin 。

  3. 支持运行多种 Linux OS

    • Ubuntu 14.04: 使用 upstart 管理服务,apt 管理软件包

    • CentOS 7:使用 systemd 和 yum

    • Debian、BusyBox ( 一种嵌入式 Linux )

3.1.3 镜像分层

  1. Docker 支持通过扩展现有镜像,创建新的镜像:

    新镜像是从 base 镜像一层层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

  2. 共享资源:

    镜像的每一层都可以被共享,源于 Copy-on-Write 特性。

  3. Copy-on-Write 特性:

     所有对容器的改动,无论添加、删除,还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
    

3.2 如何构建镜像?

3.2.1 docker commit

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

OPTIONS说明:

  • -a : 提交的镜像作者;
  • -c : 使用Dockerfile指令来创建镜像;
  • -m : 提交时的说明文字;
  • -p : 在commit时,将容器暂停。

实例1: 进入容器,修改,并保存为新镜像

# 1. 运行,并进入容器
docker run -it ubuntu

# 2. 修改容器,比如安装 vi
apt-get install -y vim

# 3. 打开新的 host 端口,查看运行中的<容器名>
docker ps

# 4. 保存为新镜像
docker commit <容器名> <新的容器名>

# 5. 进入新镜像,查看vi
docker run -it <新的容器名>
which vi

实例2: 将容器直接保存为新镜像

# 将容器 a404c6c174a2 保存为新的镜像,并添加提交人信息 "Spade_" 和说明信息 "my apache",版本仓为 mymysql:v1 
root@CentOS:~$ docker commit -a "Spade_" -m "my apache" a404c6c174a2  mymysql:v1 
sha256:37af1236adef1544e8886be23010b66577647a40bc02c0885a6600b33ee28057

# 查看 mymysql:v1
root@CentOS:~$ docker images mymysql:v1
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mymysql             v1                  37af1236adef        15 seconds ago      329 MB

3.2.2 Dockerfile

  1. 创建自己的 Dockerfile

    vim Dockerfile
    # 添加如下内容:
    FROM ubuntu
    RUN apt-get update && apt-get install -y vim
    
  2. docker build

# 构建镜像,命名为 "ubuntu-with-vi-dockerfile","." 当前目录
docker build -t ubuntu-with-vi-dockerfile .
  1. 执行步骤

    1. 执行FROM,选择基础镜像

    2. 执行RUN,安装软件 -> 启动临时容器 -> 将容器保存为镜像 ( 实际上是 docker commit ,会生成一个镜像ID ) -> 创建容器ID -> 删除临时容器 -> 构建成功

3.2.3 镜像的特性

  1. 分层结构

    # 查看 ubuntu 镜像的分层
    docker history ubuntu
    
    # 查看 ubuntu-with-vi-dockerfile 镜像的分层
    docker history ubuntu-with-vi-dockerfile
    

    注意:missing 表示无法获取 IMAGE ID,通常从 Docker Hub 下载的镜像会有这个问题。

  2. 缓存特性

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

    • docker build --no-cache 可以取消构建镜像时使用缓存的机制。

    • Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失败。

3.2.4 调试 Dockerfile

总结一下通过 Dockerfile 构建镜像的过程:

(1) 从 base 镜像运行一个容器。

(2) 执行一条指令,对容器做修改。

(3) 执行类似 docker commit 的操作,生成一个新的镜像层。

(4) Docker 再给予刚刚提交的镜像运行一个新的容器。

(5) 重复 2-4 步,直到 Dockerfile 中的所有指令执行完毕。

如果 Dockerfile 由于某种原因执行到某个指令失败了,我们也将能够得到前一个指令成功构建出的镜像,这对调试 Dockerfile 非常有帮助。

调试例子:

# 编写 Dockerfile
vim Dockerfile
# 添加如下内容:
FROM busybox
RUN touch tmpfile
RUN /bin/bash -c echo "continue to build..."
COPY testfile /

# debug 显示调试过程
docker build -t image-debug
`第二步 touch tmpfile 执行成功之后,会返回一个镜像 ID`

# 调试
docker run -it <镜像 ID>
RUN /bin/bash -c echo "continue to build..."
`发现busybox 镜像中没有 bash `

3.4.5 Dockerfile 常用指令

  • FROM:指定 bash 镜像。

  • MAINTARNER:设置镜像的作者,任意字符串。

  • COPY:将文件从 build context 复制到镜像。COPY src destCOPY ["src", "dest"],src 只能指定 build context 中的文件或目录。

  • ADD:与COPY类似,从 build context 复制文件到镜像。不同的是,如果 src 是归档文件 (tar、zip、tgz、xz 等),文件会被自动解压到 dest。

  • ENV:设置环境变量,环境变量可被后面的指令使用。例如:

    ENV MY_VERSION 1.3 RUN apt-get install -y mypackage=$MY_VERSION

  • EXPOSE:指定容器中的进程会监听某个端口,Docker 可以将该端口暴露出来。

  • VOLUME:将文件或目录声明为 volume。

  • WORKDIR:为后面的 RUN、CMD、ENTERPOINT、ADD 或 COPY 指令设置镜像中的当前工作目录。

  • RUN:在容器中运行指定的命令。

  • CMD:容器启动时运行指定的命令。多个CMD只有最后一个生效。CMD 可以被 docker run 之后的参数替换。

  • ENTERPOINT:设置容器启动时运行的命令。多个ENTERPOINT只有最后一个生效。CMD 或 docker run 之后的参数会被当做参数传递给 ENTERPOINT

3.4.6 Dockerfile 示例

  1. 编写 Dockerfile ( vim Dockerfile )
FROM busybox	# 执行 bash 镜像为 busybox (一种嵌入式Linux系统)
MAINTAINER spade_	# 镜像的作者
WORKDIR /testdir	# 工作目录,不存在则 Docker 会自动创建
RUN touch tmpfile1	# 运行命令
COPY ["tmpfile2", "."]		# 复制 tmpfile2 到"." 当前目录
ADD ["bunch.tar.gz", "."] 	# 解压 bunch.tar.gz 到 "." 当前目录
ENV WELCOME "You are in my container, welcome! "	# 设置一个字符串环境变量 WELCOME 
  1. 构建镜像
root@ubuntu:~# ls
Dockerfile bunch.tar.gz tmpfile2

# my-image 是镜像名
root@ubuntu:~# docker build -t my-image .
...
  1. 运行容器,验证镜像内容
root@ubuntu:~# docker run -it my-image
/testdir #
/testdir # ls 				
bunch tmpfile1 tmpfile2
/testdir # echo $WELCOME		# 输出 Dockerfile 设置的环境变量
You are in my container, welcome! 

3.3 RUN vs CMD vs ENTERPOINT

(1) RUN:执行命令并创建镜像层,RUN 经常用于安装软件包。

(2) CMD:设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。

(3) ENTRYPOINT:配置容器启动时运行的命令。

3.3.1 Shell 和 Exec格式

Shell 格式
<instruction> <command>

例如:

RUN apt-get install python3
CMD echo "Hello World"
ENTRYPOINT echo "Hello World"

**当指令执行时,shell 格式底层会调用 /bin/sh -c [command]。**例如下面的 Dockerfile 片段:

ENV name Spade_		# ENV 指令用于设置环境变量,环境变量可以被后面的指令调用
ENTRYPOINT echo "Hello, $name"	# 调用环境变量 name

执行 docker run [image] 将输出:

Hello, Spade_	# 环境变量 name 已经被值 Spade_ 替换
Exec 格式
<instruction> ["executable", "param1", "param2", ...]

例如:

RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello World"]
ENTRYPOINT ["/bin/echo", "Hello World"]

当指令执行时,会直接调用 [command],不会被 shell 解析。

例如下面的 Dockerfile 片段:

ENV name Spade_ 
ENTRYPOINT ["/bin/echo", "Hello, $name"]

运行容器将输出:

Hello, $name

注意:环境变量 name 没有被替换。

如果希望使用环境变量,做如下修改 Dockerfile:

ENV name Spade_
ENTRYPOINT ["/bin/echo", "-c", "echo Hello, $name"]

运行容器将输出:

Hello, Spade_
二者区别

Shell 格式:当指令执行时,shell 格式底层会调用 /bin/sh -c [command]

Exec 格式:**当指令执行时,会直接调用 [command],不会被 shell 解析。**若需要使用环境变量,需要添加 -c 参数。

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

3.3.2 RUN

RUN 常用于安装应用和软件包。

RUN 在当前镜像的顶部执行命令,并创建新的镜像层。Dockerfile 中常常包含多个 RUN 指令。

RUN 有两种格式:

(1) Shell 格式:RUN

(2) Exec 格式:RUN [“executable”, “param1”, “param2”]

使用 RUN 安装多个最新版包的例子:

RUN apt-get update && apt-get install -y \bzr\cvs\git\mercurial\subversion

3.3.3 CMD

CMD 指令允许用户指定容器默认执行的命令。

此命令会在容器启动且 docker run 没有指定其他命令时运行。

  • 如果 docker run 指定了其他命令,CMD 指定的默认命令将被忽略。
  • 如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效。

CMD 有三种格式:

(1) Exec 格式:CMD [“executable”, “param1”, “param2”]。 推荐使用。

(2) Shell 格式:CMD command param1 param2

(3) CMD [“param1”, “param2”] 为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 Exec 格式。

CMD Dockerfile 片段:

CMD echo "Hello World"

运行容器 docker run -it [image] 将输出:

Hello World

运行容器 docker run -it [image] /bin/bash, CMD 会被忽略掉,命令 bash 将被很执行:

root@10ds037c8458:/#

3.3.4 ENTRYPOINT

ENTRYPOINT 指令可以让容器以应用程序或者服务的形式运行。

ENTRYPOINT 看上去与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时执行了其他命令。

  • 多个 ENTRYPOINT 只有一个生效。

ENTRYPOINT 有两种格式:

(1) Exec 格式:ENTRYPOINT [“executable”, “param1”, “param2”] 。这是 ENTRYPOINT 的推荐格式。ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。

(2) Shell 格式:ENTRYPOINT command param1 param2。会忽略任何 CMD 或者 docker run 提供的参数。

ENTRYPOINT Dockerfile 片段:

ENTRYPOINT ["/bin/echo", "Hello"] CMD ["World"]

docker run -it [image]

Hello World

docker run -it [image] Spade_

Hello Spade_

3.4 如何分发镜像?

  1. 用相同的 Dockerfile 在其他 host 构建镜像。

  2. 将镜像上传到公共 Registry

    (1) 需要 Internet 连接,而且下载和上传速度慢。

    (2) 上传到 Docker Hub 的镜像任何人都能够访问,虽然可以用私有 repository,但不是免费的。

    (3) 因安全原因很多组织不允许将镜像放到外网。

  3. 搭建私有的 Registry

3.4.1 镜像名

# 镜像名
[image name] = [repository]:[tag]

# 构建一个镜像
docker build -t <镜像名>

# 查看镜像信息
docker images <镜像名>

# docker build 默认tag使用latest
docker build -t <镜像名>:<latest>
# repository 的完整格式, 只有 Docker Hub 上的镜像可以省略 registry-host:[port]
[registry-host]:[port]/[username]/[image name]:[tag]

3.4.2 tag

我们知道: [image name] = [repository]:[tag], 那么我们如何指定tag?

假设目前发布了一个镜像 myimage,版本为 v1.9.1,我们可以打上 4 个 tag:1.9.1、1.9、1 和 latest。

(1) myimage:1 始终指向 1 这个分支中最新的镜像。

(2) myimage:1.9 始终指向 1.9.x 中最新的镜像。

(3) myimage:latest 始终指向所有版本中最新的镜像。

(4) 如果想使用特定版本,可以选择myiamge:1.9.1、myiamge:1.9.2 或 myiamge:2.0.0

# 给版本v1.9.1的镜像 `myimage` 打tag
docker tag myimage-v1.9.1 myiamge:1 
docker tag myimage-v1.9.1 myiamge:1.9
docker tag myimage-v1.9.1 myiamge:1.9.1
docker tag myimage-v1.9.1 myiamge:latest

# 假如版本v1.9.2发布了, 1、1.9、latest迁移为1.9.2
docker tag myimage-v1.9.2 myiamge:1 
docker tag myimage-v1.9.2 myiamge:1.9
docker tag myimage-v1.9.2 myiamge:1.9.2
docker tag myimage-v1.9.2 myiamge:latest

# 假如版本v2.0.0发布了, latest迁移为2.0.0
docker tag myimage-v2.0.0 myiamge:2 
docker tag myimage-v2.0.0 myiamge:2.0
docker tag myimage-v2.0.0 myiamge:2.0.0
docker tag myimage-v2.0.0 myiamge:latest

3.4.3 使用公共 Registry

——————

3.4.4 搭建本地 Registry

——————

3.5 小结-常用命令

  • images:显示镜像列表。

  • history:显示镜像构建历史。

  • commit:从容器创建新镜像。

  • tag:给镜像打 tag。

  • pull:从 registry 下载镜像。

  • push:将镜像上传到 registry。

  • rmi:删除 Docker host 中的镜像。例如删除 httpd镜像:

    # 查看所有 httpd 容器
    [root@CentOS7 ~]# docker ps -a | grep httpd
    d7050c2cabf0        httpd               "httpd-foreground"       13 days ago         Exited (0) 12 days ago                       pensive_faraday
    63dcc8bd1687        httpd               "httpd-foreground"       13 days ago         Exited (0) 13 days ago                       condescending_lamport
    
    # 删除所有 httpd 镜像创建容器 
    [root@CentOS7 ~]# docker ps -a | grep httpd | awk '{print $1}' | xargs -n1 -I {} docker rm {}
    
  • search:搜索 Docker host 中的镜像。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值