Docker 镜像的使用

2018/02/06 Docker

Docker Hub 上有大量高质量的镜像可以用,由于国内网络的问题可能在获取镜像时速度较慢,为了解决这个问题 Docker 官方提供了中国区镜像加速。Docker 中国官网镜像加速可通过 https://www.docker-cn.com/registry-mirror 访问。

1. 配置镜像加速

配置 Docker 使用 Docker 中国区镜像加速,如下:

(1) 配置daemon.json

# 添加下面的内容,如果daemon.json文件不存在创建出来即可
# vim /etc/docker/daemon.json
{
  "registry-mirrors": ["https://registry.docker-cn.com"]
}

(2) 重启 Docker 服务

# systemctl restart docker.service

(3) 检查加速器是否生效 在命令行输入 docker info 命令如果在最下面出现如下信息说明加速器生效

Registry Mirrors:
 https://registry.docker-cn.com/

2. 搜索镜像

docker search image-name

3. 获取镜像

从 Docker 镜像仓库获取镜像的命令是 docker pull 其命令格式为:

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名:标签

示例 1:从 Docker 官方镜像仓库获取 CentOS 7.4.1708 的基础镜像

# docker pull centos:7.4.1708
7.4.1708: Pulling from library/centos
18b8eb7e7f01: Pull complete 
Digest: sha256:2a61f8abd6250751c4b1dd3384a2bdd8f87e0e60d11c064b8a90e2e552fee2d7
Status: Downloaded newer image for centos:7.4.1708

上面的命令中没有给出 Docker 镜像仓库的地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 CentOS:7.4.1708,因此获取官方镜像 library/centos 仓库中标签为 7.4.1708 的镜像。

示例 2:从私有仓库中获取镜像

# docker pull 10.100.4.214:1180/jdk/jdk:1.8.0v1
1.8.0v1: Pulling from jdk/jdk
18b8eb7e7f01: Already exists 
3d4e30b0bc0e: Pull complete 
c266636462c4: Pull complete 
7a43b6a21453: Pull complete 
Digest: sha256:7d085bc7f96d9ccd5541b08b9d2198d860cfa1ae57fba8fc66fd0b8e780b0a5a
Status: Downloaded newer image for 10.100.4.214:1180/jdk/jdk:1.8.0v1

上面的命令中给出了具体的私有仓库地址及端口号,因此将会从私有仓库中获取镜像。而镜像名称是 jdk:1.8.0v1 ,因此会从私有仓库中 jdk/jdk 仓库中获取标签为 1.8.0v1 的镜像。其中第一个 jdk/ 是harbor中的项目名,第二个 jdk 才是仓库的名字。

4. 运行容器

有了镜像后,就可以以这个镜像为基础启动一个容器来运行。以上面的 CentOS:7.4.1708 为例,如果我们打算启动里面的 bash 并且进行交互的话,可以执行下面的命令。

root@wangenzhi-test-bjqw:~ # docker run -it --rm --name centos1 centos:7.4.1708 bash
[root@385feb4f163e /]# cat /etc/os-release 
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

docker run 就是运行容器的命令,简要说明一下上面用到的参数。

-it:这是两个参数,一个是 -i 交互式操作,一个是 -t 终端。因为打算进入 bash 执行一些命令并查看返回结果,因此需要交互式终端。
--rm:容器退出后随之将其删除
--name:给这个容器起一个名字,应该做到见名知意
centos:7.4.1708:这里指用 centos:7.4.1708 镜像为基础来启动容器。
bash:放在镜像名后的是命令,这里我们希望有个交互式命令 shell ,因此用的是 bash 。

5. 列出镜像

列出镜像使用 docker images 命令。

# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
10.100.4.214:1180/jdk/jdk   1.8.0v1             34d36992bfd5        7 days ago          588MB
zookeeper                   latest              d44757f71e4f        3 weeks ago         146MB
centos                      7.4.1708            3afd47092a0e        7 weeks ago         197MB

列表包含了 仓库名、标签、镜像ID、创建时间以及所占用的空间。

6. 查看镜像体积

使用 docker system df 命令可以便捷的查看镜像、容器、数据卷所占用的空间。

# docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              3                   0                   734.6MB             734.6MB (100%)
Containers          0                   0                   0B                  0B
Local Volumes       0                   0                   0B                  0B
Build Cache                                                 0B                  0B

7. 利用 commit 制作镜像

生产中我们制作镜像应该使用Dockerfile 制作镜像,使用 commit 制作的镜像如果不是自己制作的我们无法知道别人在里面究竟做了什么。

现在我们定制一个 Nginx Web 服务器为例子演示docker commit 如何制作镜像: 1.使用官方的 nginx 镜像启动一个容器并映射了 80 端口

# docker run -d --name webserver -p 80:80 nginx 

2.使用curl 命令直接访问本机的80端口可以看到默认的 Nginx 欢迎页面

3.修改默认的页面 将默认的 Welcome to Nginx 修改为 Welcome to Docker 我们可以使用 docker exec 命令进入容器,修改内容

# docker exec -it webserver bash
root@37d9627e4d29:/# echo "<h1>Welcome to Docker</h1>" > /usr/share/nginx/html/index.html
root@37d9627e4d29:/# cat /usr/share/nginx/html/index.html 
<h1>Welcome to Docker</h1>
root@37d9627e4d29:/# exit
exit

此时在使用curl命令请求本机 80 端口:

现在定制好了默认首页的内容,想保存下来成为镜像。

docker commit 的语法格式为:

docker commit [选项] <容器名或容器ID> [<仓库名>[:<标签>]]
常用的选项:
-a:指定修改的作者
-m:注释信息,记录本次修改的信息
-p:使用commit制作镜像时先暂停容器

我们可以使用下面的命令将容器保存为镜像:

# docker commit -a "wangenzhi <W_enzhi@163.com>"  -m "修改了默认首页" -p webserver nginx:v2
sha256:1938fe4a9b542739696fa7041ff8ef388009fc609a3aadaa4205169022487796

现在可以使用 docker images 查看我们刚刚制作的镜像:

我们还可以使用docker history 查看镜像内的历史记录,如果比较 nginx:latest 的历史记录,会发现我们刚刚提交的这一层。

新的镜像制作好以后,我们可以来运行这个镜像:

root@wangenzhi-test-bjqw:~ # docker run -d --name web2 -p 81:80 nginx:v2
ce70eba9dadee26d73d3ba5f45866dd417c99cecfcf2ee19b7a3f8cccb521dfe

这里命名新的容器名字为 web2 ,映射了本机的 81 端口通过 http://localhost:81 就可以访问。

8、 上传镜像到docker HUB

root@docker-node3-4-195:~ # docker tag centos:latest wangenzhi/centos:latest 
root@docker-node3-4-195:~ # docker login -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker login [OPTIONS] [SERVER]

Log in to a Docker registry

Options:
  -p, --password string   Password
      --password-stdin    Take the password from stdin
  -u, --username string   Username
root@docker-node3-4-195:~ # docker login -u wangenzhi
Password: 
Login Succeeded

root@docker-node3-4-195:~ # docker push wangenzhi/centos:latest
The push refers to repository [docker.io/wangenzhi/centos]
e15afa4858b6: Mounted from library/centos 
latest: digest: sha256:7e94d6055269edb455bcfb637292573117e4a8341e9b9abbc09b17d8aafe8fbe size: 529

9、使用 Dockerfile 制作镜像

Dockerfile 是一个文本文件,包含了众多指令,每一条指令构建一层,因此每条指令的内容就是描述该层镜像如何构建。还是以上次使用 commit 制作镜像的例子,这次使用 Dockerfiel 来制作。 在一个空白的目录中建立一个文本文件,并命名为 Dockerfile:

# mkdir -p /data/docker/images/nginx
# cd /data/docker/images/nginx
# vim Dockerfile
FROM nginx
MAINTAINER "wangenzhi <W_enzhi@163.com>"
RUN echo '<h1>Hello, Docker</h1>' > /usr/share/nginx/html/index.html

这个 Dockerfile 很简单,一共就三行。涉及到了三条指令 FROM、MAINTAINER、RUN 。

使用 docker build 命令构建镜像。格式为:

docker build [选项] <上下文路径/URL>

接下来构建镜像:构建镜像时必须在 Dockerfile 所在的目录下

# docker build -t nginx:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM nginx
 ---> 3f8a4339aadd
Step 2/3 : MAINTAINER "wangenzhi <W_enzhi@163.com>"
 ---> Running in c4114aac0de8
Removing intermediate container c4114aac0de8
 ---> 7da2594f3708
Step 3/3 : RUN echo '<h1>Hello, Docker</h1>' > /usr/share/nginx/html/index.html
 ---> Running in f742a44e929f
Removing intermediate container f742a44e929f
 ---> 0296de096c2a
Successfully built 0296de096c2a
Successfully tagged nginx:v1

10、Dockerfile 常用指令说明

FROM指令

FROM 指令是最重要的一个且必须为 Dockerfile 文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境。

实践中,基准镜像可以是任何可用镜像文件,默认情况下,docker build 会在 docker 主机上查找指定的镜像文件,在其不存在时,则会从 docker hub registry 上拉取所需的镜像文件。 如果找不到指定的镜像文件,docker build 会返回一个错误信息。

语法格式:

FROM <image>[:<tag>]
或
FROM <image>@<digest>
    <image>:指定作为base image的名称;
    <tag>:base image的标签,为可选项,省略时默认为latest;

MAINTAINER指令

用于让镜像制作者提供本人的详细信息。 Dockerfile 并不限制 MAINTAINER 指令可在出现的位置,但推荐将其放置于FROM指定之后。 语法格式:

MAINTAINER <author's detail>
    <author's detail>:可以是任何文本信息,但约定俗称地使用作者名称及邮件地址;

COPY指令

用于从docker主机复制文件至创建的新镜像文件; 语法格式:

COPY <src>...<dest>或
COPY ["<src>",..."<dest>"]
    <src>:要复制的源文件或目录,支持使用通配符;
    <dest>:目标路径,既正在创建的image的文件系统路径;建议为<dest>使用绝对路径,否则,COPY指令则以WORKDIR为起始路径;

注意:在路径中有空白字符时,通常使用第二种格式;

文件复制准则:

  1. <src>必须是 build 上下文中的路径,不能是其父目录中的文件;
  2. 如果 <src> 是目录,则其内部文件或子目录会被递归复制,但 <src> 目录自身不会被复制;
  3. 如果指定了多个 <src> ,或在 <src> 中使用了通配符,则 <dest> 必须是一个目录,且必须以「/」结尾;
  4. 如果 <dest> 事先不存在,它将会被自动创建,这包括其父目录路径;

示例:复制 htdocs 目录中的 index.html 目录至容器中的 /data/webapps 目录中

COPY htdocs /data/webapps/

ADD指令

ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径; 语法格式:

ADD <src>...<dest>或
ADD ["<src>",..."<dest>"]

操作准则:同COPY指令

  1. 如果<src>为 URL 且<dest>不以/结尾,则<src>指定的文件将被下载并直接被创建为<dest>
  2. 如果<dest>以/结尾,则文件名 URL 指定的文件将被直接下载并保存为 <dest>/<filename>
  3. 如果<src>是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于tar -xf指令;然而,通过 URL 获取到的tar文件将不会自动展开;
  4. 如果 <src> 有多个,或其间接或直接使用了通配符,则 <dest> 必须是一个以/结尾的目录路径;如果 <dest> 不以/结尾,则其被视作一个普通文件,<src> 的内容将被直接写入到 <dest>

示例:复制两个YUM仓库配置文件到创建镜像的/etc/yum.repos.d/目录下

ADD http://mirrors.aliyun.com/repo/Centos-7.repo http://mirrors.aliyun.com/repo/epel-7.repo /etc/yum.repos.d/

示例:复制tomcat的源码包到新创建镜像的/usr/local/目录下

ADD apache-tomcat-8.5.13.tar.gz /usr/local/

ADD指令会自动将解压后的目录复制到/usr/local/目录下。

WORKDIR指令

用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指令设定工作目录; 语法格式:

WORKDIR <dirpath>

在Dockerfile文件中,WORKDIR指令可以出现多次,其路径也可以为相对路径,不过,其是相对此前一个WORKDIR指令指定的路径; 另外,WORKDIR也可以调用由ENV指令定义的变量。

例如:

WORKDIR /var/log
WORKDIR $STATEPATH

VOLUME指令

用于在image中创建一个挂载点目录,以挂载 Docker host 上的卷或其它容器上的卷; 语法格式:

VOLUME <mountpoint>或
VOLUME ["<mountpoint>"]
    <mountpoint>:为在容器中的目录,而在宿主机上的目录是在/var/lib/docker/volumes/目录下的随机字符串

如果挂载点目录路径下此前文件存在,docker run命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中;

EXPOST指令

用于为容器打开指定要监听的端口以实现与外部通信; 语法格式:

EXPOSE <port>[/<protocol>] [port][/<protocol>]...]
    <protocol>:用于指定传输层协议,可为tcp或udp二者之一,默认为tcp协议;

EXPOSE指令可一次指定多个端口,例如:

EXPOSE 11211/udp 11211/tcp

注意:这里暴露的端口为容器的端口,而宿主机上的端口是动态的;

ENV指令

用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用; 调用格式为: $variable_name 或 ${variable_name} 语法格式:

ENV <key> <value>或
ENV <key>=<value> ...

# 第一种格式中,<key>之后的所有内容均会被视作其<value>的组成部分,因此,一次只能设置一个变量;

# 第二种格式可用一次设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<value>中包含空格,可以以反斜线 (\) 进行转义,也可通过对<value>加引号进行标识;另外,反斜线也可用于续行;

定义多个变量时,建议使用第二种格式,以便在同一层中完成所有功能;

RUN指令

用于指定docker build过程中运行的程序,其可以是任何命令; 语法格式:

RUN <command>或
RUN ["<executable>","<paraml>","<paraml2>"]
  1. 第一种格式中,通常是一个shell命令,且以”/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1 不能接收Unix信号,因此,当使用docker stop 命令停止容器时,此进行接收不到SIGTERM信号;
  2. 第二种格式中的参数是一个JSON格式的数组,其中为要运行的命令,后面的为传递给命令的选项或参数,然而,此种格式指定的命令不会以"/bin/sh -c"来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
RUN ["/bin/bash","-c","<executable>","paraml"]

RUN 指令中可以使用 && 执行多个命令从而避免使用多个 RUN,也可以使用 \ 来进行换行操作,从而使每一行显示一个命令让Dockerfile变得更加易于阅读。

示例:在执行build过程中,在容器中安装nginx服务

[root@node1-codegreen-cn nginx]# cat Dockerfile 
# Dockerfile RUN
FROM centos:7.4.1708
MAINTAINER "wangenzhi <W_enzhi@163.com>"
ADD http://mirrors.aliyun.com/repo/epel-7.repo /etc/yum.repos.d/

RUN yum -y install nginx && \
    yum clean all
    
EXPOSE 80/tcp

CMD指令

类似于 RUN 指令,CMD 指令也可以用于运行任何命令或应用程序,不过二者的运行时间点不同;

RUN 指令运行与镜像文件构建过程中,而CMD指令运行与基于Dockerfile构建出的新镜像文件启动一个容器时;

CMD 指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD 指定的命令其可以被 docker run 的命令行选项所覆盖;

在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效; 语法格式:

CMD <command>或
CMD ["<executable>","<paraml>","<param2>"]或
CMD ["<paraml>","<param2>"]
# 前两种语法格式的意义同RUN
# 第三种则用于为ENTRYPOINT指令提供默认参数;

示例:指定容器启动运行 nginx -g “daemon off;” 前台启动

CMD ["/usr/sbin/nginx","-g","daemon off;"]

ENTRYPOINT指令

类似CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序。

与CMD不同的是,有ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序;

不过docker run命令的–entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序; 语法格式:

ENTRYPOINT <command>
ENTRYPOINT ["<executable>","<paraml>","<param2>"]

docker run 命令传入的命令参数会覆盖 CMD 指令的内容并且附加到 ENTRYPOINT Dockerfile 文件中也可以存在多个 ENTRYPOINT 指令,但仅最后一个会生效。

USER指令

用于指定运行image时的或运行Dockerfile中任何RUN、CMD或ENTRYPOINT指令指定的程序时的用户名或UID; 默认情况下,容器的运行身份为root用户; 语法格式:

USER <UID>|<UserName>
# 需要注意的是,<UID>可以为任意数字,但实践中其必须为/etc/passwd中某用户的有效UID,否则,docker run命令将运行失败;
# 多数情况下无需指定,因为容器中的root并非真root;

ONBUILD指令

用于在Dockerfile中定义一个触发器; Dockerfile 用于 build 镜像文件,此镜像文件亦可作为base image被另一个 Dockerfile 用作 FROM 指令的参数,并以之构建新的镜像文件;

在后面的这个Dockerfile中的FROM指令在build过程中被执行时,将会触发创建其base image的Dockerfile文件中的ONBUILD指令定义的触发器;

语法格式:

ONBUILD <INSTRUCTION>

尽管任何指令都可注册成为触发器指令,但 ONBUILD 不能自我嵌套,且不会触发 FROM 和 MAINTAINER 指令; 使用包含 ONBUILD 指令的 Dockerfile 构建的镜像应该使用特殊的标签,例如:ruby:2.0-onbuild;

在ONBUILD指令中使用 ADD 或 COPY 指令应该格外小心,因为新构建过程的上下文在缺少指定的源文件时会失败;

Search

    Table of Contents