2.1 镜像操作

  • 获取镜像
  • 列出镜像
  • 删除镜像
  • 构建镜像
  • 导入导出镜像**

    2.1.1 获取镜像

    通过 仓库地址/仓库名:标签 能够唯一确定一个镜像

  • 规则

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

    其中[ ] 代表可选。

  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub(docker.io)。

  • 仓库名:仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
    1. $ docker pull ubuntu:18.04
    2. 18.04: Pulling from library/ubuntu
    3. 92dc2a97ff99: Pull complete
    4. be13a9d27eb8: Pull complete
    5. c8299583700a: Pull complete
    6. Digest: sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26
    7. Status: Downloaded newer image for ubuntu:18.04
    8. docker.io/library/ubuntu:18.04
    上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub (docker.io)获取镜像。而镜像名称是 ubuntu:18.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 18.04 的镜像。docker pull 命令的输出结果最后一行给出了镜像的完整名称,即: docker.io/library/ubuntu:18.04

2.1.2 列出镜像

  1. docker image ls
  2. docker images

image.png

  • 镜像id是镜像的唯一标识
  • 一个镜像可以对应多个标签(TAG)
  • 注意:相同镜像id,不同tag对应的是同一份物理空间

2.1.3 删除镜像

tag相当于镜像的一个引用,删除引用不会删除物理镜像,用镜像id删除才会删除物理镜像

  • 规则

    1. # 可以多个镜像删除,image1 image2 ...
    2. docker image rm [选项] <镜像1> [<镜像2> ...]
  • 删除

    1. # 长id删除
    2. docker image rm 62d49f9bab67
    3. docker rm 62d49f9bab67
    4. # 短id删除
    5. docker image rm 62d
    6. docker rm 62d
    7. # 仓库名:标签删除
    8. docker image rm centos:latest # latest可以省略
    9. docker rm centos
    10. # 使用镜像摘要删除
    11. docker image ls --digests # 查看镜像摘要
    12. docker image rm docker/getting-started$sha256:67944b53f8a7d16b3d9909315f9d557ade12c4ee4083cd98068f9fe6d9995808
    13. # 与docker image ls配合删除
    14. docker image rm $(docker image ls -q redis)
    15. docker image rm $(docker image ls -q -f before=mongo:3.2)

    image.png

  • 删除失败的原因

1、要删除的镜像还有依赖其的容器存在(需先删除相应的容器,才可以删除镜像)

2.1.4 构建镜像

构建镜像就是要在原有的镜像上增添新的层,并保存为新的镜像。

构建镜像的三种方式:
1、基于已有的容器创建
2、基于本地模板导入
3、基于Dockerfile创建 (推荐使用这种)

(1)基于已有的容器创建

image.png

(2) 基于本地模板导入

image.png
镜像模板

(3) 基于Dockerfile创建

通过Dockerfile文件来构建,Dockerfile是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层。

步骤1:创建Dockerfile文件(以创建一个定制的nginx镜像为例)
  1. $ mkdir mynginx
  2. $ cd mynginx
  3. $ touch Dockerfile

Dockerfile文件内容:

  1. FROM nginx
  2. RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

涉及到了两条指令,FROMRUN
From: 指定基础镜像
如nginx、redis、mongo、mysql、httpd、php、tomcat等;node、openjdk、python、ruby、gplang等
除了上边提到的基础镜像,docker存在一个特殊的镜像scratch,该镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
以scratch为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
RUN:执行命令行命令,存在两种格式
1、shell格式: RUN <命令>

  1. RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

2、exec格式:RUN [“可执行文件”, “参数1”,”参数2”]

  1. FROM debian:stretch
  2. RUN apt-get update
  3. RUN apt-get install -y gcc libc6-dev make wget
  4. RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
  5. RUN mkdir -p /usr/src/redis
  6. RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
  7. RUN make -C /usr/src/redis
  8. RUN make -C /usr/src/redis install

按照如上这种形式,由于每一个RUN指令都会建立一层,导致一些不需要的如编译环境、更新的软件包等都包含在了镜像中,导致镜像臃肿、增加构建部署的时间、容易出错。
Docker采用Union FS(联合文件系统)具有最大层数限制,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。
正确写法:

  1. # Dockerfile
  2. FROM debian:stretch
  3. RUN set -x; buildDeps='gcc libc6-dev make wget' \
  4. && apt-get update \
  5. && apt-get install -y $buildDeps \
  6. && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
  7. && mkdir -p /usr/src/redis \
  8. && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
  9. && make -C /usr/src/redis \
  10. && make -C /usr/src/redis install \
  11. && rm -rf /var/lib/apt/lists/* \
  12. && rm redis.tar.gz \
  13. && rm -r /usr/src/redis \
  14. && apt-get purge -y --auto-remove $buildDeps
  • 一条RUN指令构建一层即可;
  • Dockerfile 支持行尾添加 \ 进行换行,行首添加 # 进行注释;
  • 确保每一层只添加真正需要的东西,及时清理掉无关的东西。

    步骤2:构建镜像
  • 规则

    1. # 使用docker build来构建
    2. docker build [选项] <上下文路径/URL/...>

    在Dockerfile文件所在目录:

    1. # 不要忽略了结尾的点
    2. $ docker build -t nginx:v3 .
    3. Sending build context to Docker daemon 2.048 kB
    4. Step 1 : FROM nginx
    5. ---> e43d811ce2f4
    6. Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
    7. ---> Running in 9cdc27646c7b
    8. ---> 44aa4490ce2c
    9. Removing intermediate container 9cdc27646c7b
    10. Successfully built 44aa4490ce2c
  • -t nginx:v3 设置新的镜像名为nginx:v3

  • 结尾的点:表示当前目录,当前目录即为上下文路径,按规定应与Dockerfile同目录


Docker build的工作原理:**

Docker运行时,分为Docker引擎和Docker客户端工具; docker客户端在本地根据Docker引擎对外提供的Remote API来完成各项功能,即通过远程调用方式来完成功能,C/S架构; 除了RUN指令,为了将本地文件上传给Docker引擎,采用COPY指令、ADD指令等,在远程构建镜像,上传或者下载需要上下文路径方能把本地文件上传至Docker引擎。

如下在Dockerfile上写:

  1. COPY ./package.json /app/

这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json
使用注意:
1、docker build 命令会将该上下文目录下的内容打包交给 Docker 引擎以帮助构建镜像。
2、一般将Dockerfile文件置于一个空目录下,或者项目的根目录下,

(4)跨平台构建镜像

~后续补充

(5)跨架构构建镜像

~后续补充

2.1.5 导入导出镜像

image.png

2.1.6 上传镜像到仓库

image.png

2.2 容器操作

  • 启动容器
    • 新建并启动容器
    • 后台运行容器
    • 启动已终止容器
    • 重启容器
  • 进入运行中的容器
  • 暂停容器
  • 删除容器
  • 导出与导入容器

    2.2.1 启动容器

    (1)新建并启动容器

    所需要的命令主要为 docker run

例如,下面的命令输出一个 “Hello World”,之后终止容器。

  1. $ docker run ubuntu:18.04 /bin/echo 'Hello world'
  2. Hello world

这跟在本地直接执行 /bin/echo 'hello world' 几乎感觉不出任何区别。

下面的命令则启动一个 bash 终端,允许用户进行交互。

  1. $ docker run -t -i ubuntu:18.04 /bin/bash
  2. root@af8bae53bdd3:/#

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

在交互模式下,用户可以通过所创建的终端来输入命令,例如

  1. root@af8bae53bdd3:/# pwd
  2. /
  3. root@af8bae53bdd3:/# ls
  4. bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从registry下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

(2)后台运行容器

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现。下面举两个例子来说明一下。

如果不使用 -d 参数运行容器。

  1. $ docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
  2. hello world
  3. hello world
  4. hello world
  5. hello world

容器会把输出的结果 (STDOUT) 打印到宿主机上面

如果使用了 -d 参数运行容器。

  1. $ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
  2. 77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a

此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)。

注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关,只要命令不结束,容器也就不会退出。上述命令中,while语句不会让bash退出,因此该容器就不会退出。

使用 -d 参数启动后会返回一个唯一的 id,也可以通过 docker container ls 命令来查看容器信息。

  1. $ docker container ls
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 77b2dc01fe0f ubuntu:18.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright

使用-d启动容器后,会回到host终端;此时如果想要获取容器的输出信息,可以通过 docker container logs 命令。

  1. $ docker container logs [container ID or NAMES]
  2. hello world
  3. hello world
  4. hello world
  5. . . .

(3)启动已终止容器

可以利用 docker container start 命令,直接将一个已经终止(exited)的容器启动运行。

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 pstop 来查看进程信息。

  1. root@ba267838cc1b:/# ps
  2. PID TTY TIME CMD
  3. 1 ? 00:00:00 bash
  4. 11 ? 00:00:00 ps

可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。

停止容器

docker stop可以停止运行的容器。理解:容器在docker host中实际上是一个进程,docker stop命令本质上是向该进程发送一个SIGTERM信号。如果想要快速停止容器,可使用docker kill命令,其作用是向容器进程发送SIGKILL信号。

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. bdf593fda8be ubuntu:15.10 "/bin/bash" 3 minutes ago Up 3 minutes (Paused) cranky_mclaren
  4. $ docker stop bdf593fda8be
  5. bdf593fda8be
  6. $ docker ps
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  8. LM-

备注:docker ps 列出容器,默认列出只在运行的容器;加-a可以显示所有的容器,包括未运行的(例如异常退出(Exited)状态的容器)。

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. bdf593fda8be ubuntu:15.10 "/bin/bash" 18 minutes ago Up 6 minutes cranky_mclaren
  4. $ docker ps -a
  5. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  6. 2a545c90e593 ubuntu:15.10 "/bin/echo -d 'Hello…" 1 second ago Exited (0) 1 second ago blissful_leakey
  7. bdf593fda8be ubuntu:15.10 "/bin/bash" 18 minutes ago Up 6 minutes cranky_mclaren

(4)重启容器

对于已经处于停止状态的容器,可以通过docker start重新启动。

  1. $ docker start bdf593fda8be
  2. bdf593fda8be
  3. $ docker ps
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. bdf593fda8be ubuntu:15.10 "/bin/bash" 11 minutes ago Up 2 seconds cranky_mclaren

docker start会保留容器的第一次启动时的所有参数。docker restart可以重启容器,其作用就是依次执行docker stop和docker start。容器可能因某种错误而停止运行。对于服务类容器,通常希望它能够自动重启。启动容器时设置—restart就可以达到效果。—restart=always意味着无论容器因何种原因退出(包括正常退出),都立即重启;

  1. $ docker run -it ubuntu:15.10 /bin/echo --restart=always -d "Hello world"
  2. --restart=always -d Hello world
  3. $ docker ps -a
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. ad0723ad8383 ubuntu:15.10 "/bin/echo --restart…" 9 seconds ago Exited (0) 8 seconds ago gracious_chatelet
  6. 2a545c90e593 ubuntu:15.10 "/bin/echo -d 'Hello…" 6 minutes ago Exited (0) 6 minutes ago blissful_leakey
  7. bdf593fda8be ubuntu:15.10 "/bin/bash" 25 minutes ago Up 13 minutes cranky_mclaren

2.2.2 进入运行中的容器

在使用 -d 参数时,容器启动后会进入后台,启动完容器之后会停在host端;某些时候需要进入容器进行操作,包括使用 docker attach 命令或 docker exec 命令,推荐使用 docker exec 命令,原因会在下面说明。

(1) attach命令

下面示例如何使用 docker attach 命令。

  1. $ docker run -dit ubuntu
  2. 243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
  3. $ docker container ls
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. 243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia
  6. $ docker attach 243c
  7. root@243c32535da7:/#

注意:** 如果从这个 stdin 中exit回到host端,会导致容器的停止。**

(2)exec命令

docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。

只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。

-i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

  1. $ docker run -dit ubuntu
  2. 69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6
  3. $ docker container ls
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. 69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles
  6. $ docker exec -i 69d1 bash
  7. ls
  8. bin
  9. boot
  10. dev
  11. ...
  12. $ docker exec -it 69d1 bash
  13. root@69d137adef7a:/#

如果从这个 stdin 中 exit回到host端,但不会导致容器的停止。

(3) attach和exec的区别

  • attach直接进入容器启动命令的终端,不会启动新的进程;
  • exec则是在容器中打开新的终端,并且可以启动新的进程;
  • 如果想直接在终端中查看命令的输出,用attach,其他情况使用exec;

2.2.3 暂停容器

有时我们只是希望让容器暂停工作一段时间,比如要对容器的文件系统打个快照,或者docker host需要使用CPU,可以执行:docker pause CONTAINER [CONTAINER…],如图所示:

  1. $ docker pause bdf593fda8be
  2. bdf593fda8be
  3. $ docker ps
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. bdf593fda8be ubuntu:15.10 "/bin/bash" 3 minutes ago Up 3 minutes (Paused) cranky_mclaren

2.2.4 删除容器

可以使用 docker container rm 来删除一个处于终止状态的容器。例如

  1. $ docker container rm trusting_newton
  2. trusting_newton
  3. docker rm bdf

如果要删除一个运行中的容器,可以添加 -f 参数。Docker 会发送 SIGKILL 信号给容器。

(1)清理所有处于终止状态的容器

docker container ls -a 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。

  1. $ docker container prune

(2)批量删除所有已经退出的容器

  1. $ docker rm -v $(docker ps -aq -f status=exited)

2.2.5 导出与导入容器

如果要导出本地某个容器,可以使用 docker export 命令。

  1. $ docker container ls -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 7691a814370e ubuntu:18.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
  4. $ docker export 7691a814370e > ubuntu.tar

可以使用 docker import 从容器快照文件中再导入为镜像,例如

  1. $ cat ubuntu.tar | docker import - test/ubuntu:v1.0
  2. $ docker image ls
  3. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
  4. test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB

此外,也可以通过指定 URL 或者某个目录来导入,例如

  1. $ docker import http://example.com/exampleimage.tgz example/imagerepo

注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。
_