作者:@diyun

镜像

镜像操作

image.png

  • 拉取镜像,使用docker pull命令拉取远程仓库的镜像到本地 ;

—- 实际上执行docker pull busybox命令,都是先从本地搜索,如果本地搜索不到busybox镜像则从 Docker Hub 下载镜像。

  • 重命名镜像,使用docker tag命令“重命名”镜像 ;
  • 查看镜像,使用docker image lsdocker images命令查看本地已经存在的镜像 ;
  • 删除镜像,使用docker rmi命令删除无用镜像 ;
  • 构建镜像,构建镜像有两种方式。第一种方式是使用docker build命令基于 Dockerfile 构建镜像,也是我比较推荐的镜像构建方式;第二种方式是使用docker commit命令基于已经运行的容器提交为镜像。 ```bash $ docker run —rm —name=busybox -it busybox sh / #

    执行完上面的命令后,当前窗口会启动一个 busybox 容器并且进入容器中。

    在容器中,执行以下命令创建一个文件并写入内容:

    / # touch hello.txt && echo “I love Docker. “ > hello.txt

另开一个窗口,提交当前改动为新的镜像

$ docker commit busybox busybox:hello

  1. <a name="FBT2A"></a>
  2. #### 使用Dockerfile生成镜像
  3. Dockerfile 是一个包含了用户所有构建命令的文本。通过`docker build`命令可以从 Dockerfile 生成镜像。<br />使用 Dockerfile 构建镜像具有以下特性:
  4. - Dockerfile 的每一行命令都会生成一个独立的镜像层,并且拥有唯一的 ID;<br />
  5. - Dockerfile 的命令是完全透明的,通过查看 Dockerfile 的内容,就可以知道镜像是如何一步步构建的;<br />
  6. - Dockerfile 是纯文本的,方便跟随代码一起存放在代码仓库并做版本管理。<br />
  7. 看到使用 Dockerfile 的方式构建镜像有这么多好的特性,你是不是已经迫不及待想知道如何使用了。别着急,我们先学习下 Dockerfile 常用的指令。
  8. | Dockerfile 指令 | 指令简介 |
  9. | --- | --- |
  10. | FROM | Dockerfile 除了注释第一行必须是 FROM ,FROM 后面跟镜像名称,代表我们要基于哪个基础镜像构建我们的容器。 |
  11. | RUN | RUN 后面跟一个具体的命令,类似于 Linux 命令行执行命令。 |
  12. | ADD | 拷贝本机文件或者远程文件到镜像内 |
  13. | COPY | 拷贝本机文件到镜像内 |
  14. | USER | 指定容器启动的用户 |
  15. | ENTRYPOINT | 容器的启动命令 |
  16. | CMD | CMD 为 ENTRYPOINT 指令提供默认参数,也可以单独使用 CMD 指定容器启动参数 |
  17. | ENV | 指定容器运行时的环境变量,格式为 key=value |
  18. | ARG | 定义外部变量,构建镜像时可以使用 build-arg = 的格式传递参数用于构建 |
  19. | EXPOSE | 指定容器监听的端口,格式为 [port]/tcp 或者 [port]/udp |
  20. | WORKDIR | 为 Dockerfile 中跟在其后的所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 命令设置工作目录。 |
  21. **案例如下:**
  22. ```shell
  23. # nginx.repo
  24. [nginx]
  25. name=nginx repo
  26. baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
  27. gpgcheck=0
  28. enabled=1
  1. FROM centos:7
  2. COPY nginx.repo /etc/yum.repos.d/nginx.repo
  3. RUN yum install -y nginx
  4. EXPOSE 80
  5. ENV HOST=mynginx
  6. CMD ["nginx","-g","daemon off;"]
  1. $ docker build -t nginx .

镜像分层

这里 Docker 使用的是 overlay2 文件驱动,进入到/var/lib/docker/overlay2目录下使用tree .命令查看产生的镜像文件:

  1. $ tree .
  2. # 以下为 tree . 命令输出内容
  3. |-- 3e89b959f921227acab94f5ab4524252ae0a829ff8a3687178e3aca56d605679
  4. | |-- diff # 这一层为基础层,对应上述 Dockerfile 第一行,包含 busybox 镜像所有文件内容,例如 /etc,/bin,/var 等目录
  5. ... 此次省略部分原始镜像文件内容
  6. | `-- link
  7. |-- 6591d4e47eb2488e6297a0a07a2439f550cdb22845b6d2ddb1be2466ae7a9391
  8. | |-- diff # 这一层对应上述 Dockerfile 第二行,拷贝 test 文件到 /tmp 文件夹下,因此 diff 文件夹下有了 /tmp/test 文件
  9. | | `-- tmp
  10. | | `-- test
  11. | |-- link
  12. | |-- lower
  13. | `-- work
  14. |-- backingFsBlockDev
  15. |-- bec6a018080f7b808565728dee8447b9e86b3093b16ad5e6a1ac3976528a8bb1
  16. | |-- diff # 这一层对应上述 Dockerfile 第三行,在 /tmp 文件夹下创建 testdir 文件夹,因此 diff 文件夹下有了 /tmp/testdir 文件夹
  17. | | `-- tmp
  18. | | `-- testdir
  19. | |-- link
  20. | |-- lower
  21. | `-- work
  22. ...

通过上面的目录结构可以看到,Dockerfile 的每一行命令,都生成了一个镜像层,每一层的 diff 夹下只存放了增量数据。
image.png
分层的结构使得 Docker 镜像非常轻量,每一层根据镜像的内容都有一个唯一的 ID 值,当不同的镜像之间有相同的镜像层时,便可以实现不同的镜像之间共享镜像层的效果。

容器

容器是基于镜像创建的可运行实例,并且单独存在,一个镜像可以创建出多个容器。运行容器化环境时,实际上是在容器内部创建该文件系统的读写副本。 这将添加一个容器层,该层允许修改镜像的整个副本。
image.png

容器的生命周期

容器的生命周期是容器可能处于的状态,容器的生命周期分为 5 种。

  1. created:初建状态
  2. running:运行状态
  3. stopped:停止状态
  4. paused: 暂停状态
  5. deleted:删除状态

image.png
通过docker create命令生成的容器状态为初建状态,初建状态通过docker start命令可以转化为运行状态,运行状态的容器可以通过docker stop命令转化为停止状态,处于停止状态的容器可以通过docker start转化为运行状态,运行状态的容器也可以通过docker pause命令转化为暂停状态,处于暂停状态的容器可以通过docker unpause转化为运行状态 。处于初建状态、运行状态、停止状态、暂停状态的容器都可以直接删除。

容器的操作

(1)创建并启动容器

  1. $ docker create -it --name=busybox busybox
  2. $ docker ps -a| grep busybox
  3. # docker run等同于执行了docker create后又执行了docker start
  4. $ docker run -it --name=busybox busybox

当使用docker run创建并启动容器时,Docker 后台执行的流程为:

  • Docker 会检查本地是否存在 busybox 镜像,如果镜像不存在则从 Docker Hub 拉取 busybox 镜像;
  • 使用 busybox 镜像创建并启动一个容器;
  • 分配文件系统,并且在镜像只读层外创建一个读写层;
  • 从 Docker IP 池中分配一个 IP 给容器;
  • 执行用户的启动命令运行镜像。

上述命令中, -t 参数的作用是分配一个伪终端,-i 参数则可以终端的 STDIN 打开,同时使用 -it 参数可以让我们进入交互模式。 在交互模式下,用户可以通过所创建的终端来输入命令,例如:
复制代码

  1. $ ps aux
  2. PID USER TIME COMMAND
  3. 1 root 0:00 sh
  4. 6 root 0:00 ps aux

我们可以看到容器的 1 号进程为 sh 命令,在容器内部并不能看到主机上的进程信息,因为容器内部和主机是完全隔离的。同时由于 sh 是 1 号进程,意味着如果通过 exit 退出 sh,那么容器也会退出。所以对于容器来说,杀死容器中的主进程,则容器也会被杀死。

(2)终止容器

  1. $ docker stop busybox
  2. $ docker ps -a
  3. CONTAINERID IMAGE COMMAND CREATED STATUS PORTS NAMES
  4. 28d477d3737a busybox "sh" 26 minutes ago Exited (137) About a minute ago
  5. $ docker start busybox
  6. $ docker restart busybox

(3)进入容器

处于运行状态的容器可以通过docker attachdocker execnsenter等多种方式进入容器。

  1. $ docker attach busybox

注意:当我们同时使用docker attach命令同时在多个终端运行时,所有的终端窗口将同步显示相同内容,当某个命令行窗口的命令阻塞时,其他命令行窗口同样也无法操作。attach直接使用当前1号进程进入终端。

  1. $ docker exec -it busybox sh

exec 进入终端会启动一个新的sh进程。进入容器后,可以看到容器内有两个sh进程,这是因为以exec的方式进入容器,会单独启动一个 sh 进程,每个窗口都是独立且互不干扰的,也是使用最多的一种方式。

(4)删除容器

  1. docker rm busybox
  2. # 强行删除正在运行中的容器,需要添加-f参数
  3. docker rm -f busybox

(5)导出导入容器

  1. $ docker export busybox > busybox.tar

执行以上命令后会在当前文件夹下生成 busybox.tar 文件,我们可以将该文件拷贝到其他机器上,通过导入命令实现容器的迁移。

使用docker import命令导入上一步导出的容器

  1. $ docker import busybox.tar busybox:test

此时,busybox.tar 被导入成为新的镜像,镜像名称为 busybox:test 。

结语

到此,已经了解了容器的基本概念和组成,并已经熟练掌握了容器各个生命周期操作和管理。镜像包含了容器运行所需要的文件系统结构和内容,是静态的只读文件,而容器则是在镜像的只读层上创建了可写层,并且容器中的进程属于运行状态,容器是真正的应用载体。