1、镜像的简单介绍:

Docker 镜像(Image):就是一个只读的模板。
例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了 Apache 或用户需要的其它应用程序。

镜像可以用来创建 Docker 容器。

Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像,用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。

2、从 Docker Hub 仓库查询、下载镜像
2.1、查询镜像:
]# docker search centos
INDEX       NAME                                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/centos                          The official build of CentOS.                   2336      [OK]       
docker.io   docker.io/ansible/centos7-ansible         Ansible on Centos7                              74                   [OK]
docker.io   docker.io/jdeathe/centos-ssh              CentOS-6 6.7 x86_64 / CentOS-7 7.2.1511 x8...   25                   [OK]
docker.io   docker.io/jdeathe/centos-ssh-apache-php   CentOS-6 6.7 x86_64 / Apache / PHP / PHP M...   17                   [OK]
docker.io   docker.io/nimmis/java-centos              This is docker images of CentOS 7 with dif...   12                   [OK]
docker.io   docker.io/million12/centos-supervisor     Base CentOS-7 with supervisord launcher, h...   11                   [OK]
docker.io   docker.io/consol/centos-xfce-vnc          Centos container with "headless" VNC sessi...   9                    [OK]
docker.io   docker.io/torusware/speedus-centos        Always updated official CentOS docker imag...   8                    [OK]
docker.io   docker.io/nickistre/centos-lamp           LAMP on centos setup                            4                    [OK]
… … 以下省略

在 docker 的镜像索引网站上面,镜像都是按照 “用户名/镜像名” 的方式来存储的。有一组比较特殊的镜像,比如 ubuntu、centos 这类基础镜像,经过官方的验证,值得信任,可以直接用镜像名来检索到。

2.2、下载镜像:
]# docker pull centos
Using default tag: latest
Trying to pull repository docker.io/library/centos ... latest: Pulling from library/centos
1544084fad81: Pull complete 
df0fc3863fbc: Pull complete 
a3d54b467fad: Pull complete 
a65193109361: Pull complete 
Digest: sha256:1a62cd7c773dd5c6cf08e2e28596f6fcc99bd97e38c9b324163e0da90ed27562
Status: Downloaded newer image for docker.io/centos:latest
]# docker pull centos:6.8
6.8: Pulling from library/centos
b6d7b2ebc0a7: Pull complete 
Digest: sha256:fd9058a6149809b2f4725bb4461294ceebb59e587435f3509c2c8dcc9ee1d5b4
Status: Downloaded newer image for centos:6.8

如果下载时不指定来源,默认从 Docker Hub 仓库中的官方源下载镜像,如果不指定 tag 内容,则会默认下载 tag 标签为 latest 的镜像

2.3、显示本地已有的镜像
]# docker images 
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
docker.io/centos    latest              a65193109361        12 days ago         196.7 MB
  • REPOSITORY:来自于哪个仓库,比如 centos
  • TAG:镜像的标记,用来标记来自同一个仓库的不同镜像。比如 latest
  • IMAGE ID:它的 ID 号,用来唯一标识一个镜像
  • CREATED:创建时间
  • VIRTUAL SIZE:镜像大小

3、通过已有镜像来创建新镜像
3.1、保存对容器的修改

使用下载的镜像,启动容器

]# docker run -t -i centos:6.8 /bin/bash

在启动的容器中,安装 ntp

[root@6dafb63fb2d9 /]# yum install ntp

退出容器

[root@6dafb63fb2d9 /]# exit

通过 docker commit 命令来将我们对容器的修改保存为新的镜像

语法:

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

-m 用来指定提交的说明信息
-a 指定更新的用户信息
]# docker commit -m "Added ntp(commit)" -a "yuankewen <15150551620@139.com>" 6dafb63fb2d9 yue/centos:v1 
sha256:d23ea4e529b777f3f65df8bf018a9dc7adf86363748e51cd7e9a4556737f5cfb

查看新创建的容器

]# docker images 
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
yue/centos                                      v1                  d23ea4e529b7        6 minutes ago       334.2 MB
3.2、通过 Dockerfile 文件生成新的镜像

使用 docker commit 来扩展一个镜像比较简单,但是不方便在一个团队中分享。我们可以使用 docker build 来创建一个新的镜像。

下面我们通过一个 Dockerfile 文件,来完成上面同样的操作,即在 centos:6.8 镜像的基础上,添加一个 ntp 服务

编写 Dockerfile 文件

mkdir mkntp
cd mkntp/
vim Dockerfile
FROM centos:6.8
MAINTAINER "yuankewen <15150551620@139.com>"

RUN yum -y install ntp

通过 Dockerfile 文件生成新的镜像

语法:

docker build [OPTIONS] PATH | URL | -

-t  指定新的镜像的名字和标记。
.   是 Dockerfile 所在的路径(当前目录),也可以替换为一个具体的 Dockerfile 的路径。
]# docker build -t 'yue/centos:v2' .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM centos:6.8
 ---> a374fd62f97f
Step 2 : MAINTAINER "yuankewen <15150551620@139.com>"
 ---> Using cache
 ---> 9ae202f1a68f
Step 3 : RUN yum -y install ntp
 ---> Running in 7efbf2be8f7a
# ... 中间 yum 安装时的输出省略 ...
Complete!
 ---> 5b91fa7247cd
Removing intermediate container 7efbf2be8f7a
Successfully built 5b91fa7247cd

build 进程所执行操作:

  • 首先,它要做的第一件事情就是上传这个 Dockerfile 内容,因为所有的操作都要依据 Dockerfile 来进行。
  • 然后,Dockfile 中的指令被一条一条的执行。每一步都创建了一个新的容器,在容器中执行指令并提交修改。
  • 最后,当所有的指令都执行完毕之后,所有的中间步骤所产生的容器都会被删除和清理,并返回了最终的镜像 id。

此外,还可以利用 ADD 命令复制本地文件到镜像;用 EXPOSE 命令来向外部开放端口;用 CMD 命令来描述容器启动后运行的程序等。

注:

具体 Dockerfile 所使用到的命令,请参见 附A:Dockerfile 的命令

查看新创建的容器

]# docker images 
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
yue/centos                                      v2                  5b91fa7247cd        21 minutes ago      287.2 MB
yue/centos                                      v1                  d23ea4e529b7        3 hours ago         334.2 MB

4、存出和载入镜像
4.1 存出镜像

将镜像从本地镜像库导出到本地文件

语法:

docker save [OPTIONS] IMAGE [IMAGE...]
-o 用来将标准输出,写入一个文件
]# docker save -o centos_6.8.tar centos:6.8
]# ll
total 197480
-rw------- 1 root root 202211840 Aug  1 16:18 centos_6.8.tar
4.2 载入镜像

将镜像从本地文件系统导入本地镜像库

语法:

docker load [OPTIONS]
-i --input 从一个 tar 架构的文件读取
]# docker load -i centos_6.8.tar 
c027abff19e1: Loading layer [==================================================>] 202.2 MB/202.2 MB
]# docker images 
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
centos                                          6.8                 a374fd62f97f        4 weeks ago         194.5 MB
5、删除镜像

docker 中删除 images 的命令是 docker rmi,但有时候执行此命令并不能删除 images

]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
yuebaibai/centos    v5                  36877f7460ff        6 days ago          924.9 MB
yuebaibai/centos    v4                  20621c920fcc        6 days ago          924.9 MB
yuebaibai/centos    v3                  7c82b53eccbb        6 days ago          924.9 MB
yuebaibai/centos    v2                  675c9f34ee8b        6 days ago          924.9 MB
yuebaibai/centos    v1                  8a30f062c71c        6 days ago          924.9 MB
docker.io/centos    6.8                 a374fd62f97f        4 weeks ago         194.5 MB

]# docker rmi 36877f7460ff
Failed to remove image (36877f7460ff): Error response from daemon: conflict: unable to delete 36877f7460ff (must be forced) - image is being used by stopped container 5274568a3492

报错很明确:不能删除这个镜像的原因是,这个镜像正在被一个已停止的容器使用。此时,有两种处理方法:

  • 先删除使用该镜像的容器,然后再删除该镜像
]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                    PORTS               NAMES
5274568a3492        36877f7460ff        "/bin/sh -c '/etc/ini"   6 days ago          Exited (137) 6 days ago                       adoring_mahavira

]# docker rm 5274568a3492
5274568a3492
  • 使用强制删除选项,删除该镜像
]# docker rmi -f 36877f7460ff
Untagged: yuebaibai/centos:v5
Deleted: sha256:36877f7460ff515bd019298b665b5fb8f0f184ec56c531298b35a91ac6d5d40e
Deleted: sha256:560793c708c59d39943a67db69cb8d0570c3874e691ae81a466968a870982044
Deleted: sha256:53527fb3274e15ff8bff4a16c5a3498559afe6346b549784dc88b9d9b2bac61e
Deleted: sha256:8d1b4bb20450952c0723347e1530f22186e83baf9b82bfb8f7a6f6793e5d9429

注1:

推荐使用第一种方式

注2:

可以使用以下命令将所有已停止但未提交的容器删除

]# docker rm $(docker ps -a |grep Exited |awk '{print $1}')
6、其它命令
6.1 显示镜像历史信息

语法:

docker history [OPTIONS] IMAGE

--no-trunc 对输出不进行删节
]# docker history --no-trunc centos:6.8
IMAGE                                                                     CREATED             CREATED BY                                                                                         SIZE                COMMENT
sha256:a374fd62f97f4ba3c639da09a93e43ae84eb2ee19ea1c4c6d383deceaa6efc84   4 weeks ago         /bin/sh -c #(nop) CMD ["/bin/bash"]                                                                0 B                 
<missing>                                                                 4 weeks ago         /bin/sh -c #(nop) LABEL name=CentOS Base Image vendor=CentOS license=GPLv2 build-date=2016-06-02   0 B                 
<missing>                                                                 4 weeks ago         /bin/sh -c #(nop) ADD file:beeefa390db7bc02afe6a885fecdd835435d30121252a0caf1f9215bd8e37fc2 in /   194.5 MB            
<missing>                                                                 4 weeks ago         /bin/sh -c #(nop) MAINTAINER The CentOS Project <cloud-ops@centos.org>                             0 B
6.2 显示镜像或容器底层信息

语法:

docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
6.3 标记一个镜像到一个仓库

语法:

docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

附A: Dockerfile 的命令

基本结构:

  • 基础镜像信息
  • 维护者的信息
  • 镜像操作指令
  • 容器启动时执行指令

注1:

建议每次都新建一个空目录来放置新的 Dockerfile 文件

注2:

Dockerfile 文件使用 # 作为注释

Dockerfile 的指令:
指令格式:

INSTRUCTION arguments

注:

Dockerfile 中 instruction 不区分大小写,但默认约定使用大写表示。
arguments 对大小写敏感。

FROM

FROM <image>
或
FROM <image>:<tag>

每个 Dockerfile 文件都必须以 FROM 指令开始,用于指定新的镜像基于哪个基础镜像创建。

例:

FROM docker.io/centos:6.8

MAINTAINER

MAINTAINER <name>

用于指定镜像创建者的信息。

例:

MAINTAINER yuankewen "15150551620@139.com"

WORKDIR

WORKDIR /path/to/workdir

为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则输出结果为:

/a/b/c

USER

USER daemon

指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:

RUN groupadd -r postgres && useradd -r -g postgres postgres

ENV

ENV <key> <value>

指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。例如

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

RUN

RUN <command>

每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。

例:

RUN sh /root/mk.sh

CMD

CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
或
CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
或
CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。

ENTRYPOINT

ENTRYPOINT ["executable", "param1", "param2"]
或
ENTRYPOINT command param1 param2(shell中执行)。

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。

注:

三者的区别请参见附B: RUN、CMD、ENTRYPOINT 三者的区别

ADD

ADD <src> <dest>

该命令将复制指定的 到容器中的 。 其中 可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。

COPY

COPY <src> <dest>

复制本地主机的 (为 Dockerfile 所在目录的相对路径)到容器中的 。
当使用本地目录为源目录时,推荐使用 COPY。

EXPOSE

EXPOSE <port> [<port>...]

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。

VOLUME

VOLUME ["/data"]。

创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

ONBUILD

ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A。

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。

FROM image-A

#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。


附B:RUN、CMD、ENTRYPOINT 三者的区别

RUN 与 CMD、ENTRYPOINT 的区别:

三者的执行时间不太一样。

RUN 是 在Build 时执行的,先于 CMD 和 ENTRYPOINT。Build 完成了,RUN 也运行完成了。而 CMD 或者 ENTRYPOINT 在容器运行进才会执行。

ENTRYPOINT 和 CMD 的区别:

在于执行 docker run 时参数传递方式,CMD 指定的命令可以被 docker run 传递的命令覆盖,例如,如果用 CMD 指定:

CMD ["echo"]

然后运行

docker run CONTAINER_NAME echo foo

那么 CMD 里指定的 echo 会被新指定的 echo 覆盖,所以最终相当于运行 echo foo,所以最终打印出的结果就是:

foo

而 ENTRYPOINT 会把容器名后面的所有内容都当成参数传递给其指定的命令(不会对命令覆盖),比如:

ENTRYPOINT ["echo"]

然后运行

docker run CONTAINER_NAME echo foo

则 CONTAINER_NAME 后面的 echo foo 都作为参数传递给 ENTRYPOING 里指定的 echo 命令了,所以相当于执行了

echo "echo foo"

最终打印出的结果就是:

echo foo

另外,在 Dockerfile 中,ENTRYPOINT 指定的参数比运行 docker run 时指定的参数更靠前,比如:

ENTRYPOINT ["echo", "foo"]

执行

docker run CONTAINER_NAME bar

相当于执行了:

echo foo bar

打印出的结果就是:

foo bar

附C:Dockerfile 最佳实践

使用缓存

Dockerfile 的每条指令都会将结果提交为新的镜像。下一条指令基于上一条指令的镜像进行构建。如果一个镜像拥有相同的父镜像和指令(除了 ADD ),Docker将会使用镜像而不是执行该指令,即缓存。

因此,为了有效的利用缓存,尽量保持 Dockerfile 一致,并且尽量在末尾修改:

FROM ubuntu

MAINTAINER author <somebody@company.com>

RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe"

RUN apt-get update

RUN apt-get upgrade -y

更改 MAINTAINER 指令会使Docker强制执行 run 指令来更新apt,而不是使用缓存。

如不希望使用缓存,在执行 docker build 的时候加上参数 --no-cache=true 。

Docker 匹配镜像决定是否使用缓存的规则如下:

从缓存中存在的基础镜像开始,比较所有子镜像,检查它们构建的指令是否和当前的是否完全一致。如果不一致则缓存不匹配。

多数情况中,使用其中一个子镜像来比较 Dockerfile 中的指令是足够的。然而,特定的指令需要做更多的判断。
ADD COPY 指令中,将要添加到镜像中的文件也要被检查。通常是检查文件的校验和(checksum)。

缓存匹配检查并不检查容器中的文件。例如,当使用 RUN apt-get -y update 命令更新了容器中的文件,并不会被缓存检查策略作为缓存匹配的依据。

使用标签

除非是在用 Docker 做实验,否则你应当通过 -t 选项来 docker build 新的镜像以便于标记构建的镜像。一个简单可读的标签可以帮助管理每个创建的镜像。

docker build -t="tuxknight/luckypython"
Note

始终通过 -t 标记来构建镜像。

公开端口

Docker 的核心概念是可重复和可移植,镜像应该可以运行在任何主机上并运行尽可能多的次数。在 Dockerfile 中你可以映射私有和公有端口,但永远不要通过 Dockerfile 映射公有端口。这样运行多个镜像的情况下会出现端口冲突的问题。

EXPOSE 80:8080  # 80映射到host的8080,不提倡这种用法

EXPOSE 80 # 80会被docker随机映射一个端口

CMD ENTRYPOINT语法

CMD 和 ENTRYPOINT 支持两种语法:

CMD /bin/echo

CMD ["/bin/echo"]

在第一种方式下,Docker会在命令前加上 /bin/sh -c ,可能会导致一些意想不到的问题。第二种方式下 CMD ENTRYPOINT 是一个数组,执行的命令完全和你期待的一样。

容器是短暂的

容器模型是进程而不是机器,不需要开机初始化。在需要时运行,不需要时停止,能够删除后重建,并且配置和启动的最小化。

.dockerignore 文件

在 docker build 的时候,忽略部分无用的文件和目录可以提高构建的速度。

不要在构建中升级版本

不在容器中更新,更新交给基础镜像来处理。

每个容器只运行一个进程

一个容器只运行一个进程。容器起到了隔离应用,隔离数据的作用,不同的应用运行在不同的容器让集群的纵向扩展以及容器的复用都变的更加简单。

最小化层数

需要掌握号 Dockerfile 的可读性和文件系统层数之间的平衡。控制文件系统层数的时候会降低 Dockerfile 的可读性。而 Dockerfile 可读性高的时候,往往会导致更多的文件系统层数。

使用小型基础镜像

基础镜像足够小并且没有包含任何不需要的包。

使用特定标签

Dockerfile 中 FROM 应始终包含依赖的基础镜像的完整仓库名和标签,如使用 FROM debian:jessie 而不是 FROM debian 。

多行参数顺序

apt-get update 应与 apt-get install 组合,采取 \ 进行多行安装。同时需要注意按照字母表顺序排序避免出现重复。

FROM debian:jessie

RUN apt-get update && apt-get install -y \

build-essential\

git \

make \

python \

RUN dpkg-reconfigure locales && \

locale-gen C.UTF-8 && \

/usr/sbin/update-locale LANG=C.UTF-8

ENV LC_ALL C.UTF-8

ADD 和 COPY

虽然 ADD 与 COPY 功能类似,但推荐使用 COPY 。 COPY 只支持基本的文件拷贝功能,更加的可控。而 ADD 具有更多特定,比如tar文件自动提取,支持URL。 通常需要提取tarball中的文件到容器的时候才会用到 ADD 。

如果在Dockerfile中使用多个文件,每个文件应使用单独的 COPY 指令。这样,只有出现文件变化的指令才会不使用缓存。

为了控制镜像的大小,不建议使用 ADD 指令获取URL文件。正确的做法是在 RUN 指令中使用 wget 或 curl 来获取文件,并且在文件不需要的时候删除文件。

VOLUME

VOLUME 指令应当暴露出数据库的存储位置,配置文件的存储以及容器中创建的文件或目录。由于容器结束后并不保存任何更改,你应该把所有数据通过 VOLUME 保存到host中。

USER

如果服务不需要特权来运行,使用 USER 指令切换到非root用户。使用 RUN groupadd -r mysql && useradd -r -g mysql mysql 之后用 USER mysql 切换用户

要避免使用 sudo 来提升权限,因为它带来的问题远比它能解决的问题要多。如果你确实需要这样的特性,那么可以选择使用 gosu 。

最后,不要反复的切换用户。减少不必要的layers。

WORKDIR

WORKDIR 的路径始终使用绝对路径可以保证指令的准确和可靠。 同时,使用 WORKDIR 来替代 RUN cd … && do-something 这样难以维护的指令。