什么是容器化的应用

我们运行容器的时候,显然不是从零开始的,而是要先拉取一个“镜像”(image),再从这个“镜像”来启动容器。

镜像是什么?它又和“容器”有什么关系呢?

我们都知道光盘镜像,硬盘镜像,系统镜像,这些“镜像”都有一些相同点:只读,不允许修改,以标准格式存储了一系列的文件,然后在需要的时候再从中提取出数据运行起来。

什么是镜像?

容器技术里的镜像也是同样的道理。因为容器是由操作系统动态创建的,那么必然就可以用一种办法把它的初始环境给固化下来,保存成一个静态的文件。
也就是把运行程序所需要的文件系统、依赖库、环境变量、启动参数等所有信息打包整合到了一起保存成了一个静态文件,这个静态文件就是镜像。
从功能上来看,镜像和常见的 tar、rpm、deb 等安装包一样,都打包了应用程序,但最大的不同点在于它里面不仅有基本的可执行文件,还有应用运行时的整个系统环境。

好处:

这就让镜像具有了非常好的跨平台便携性和兼容性,能够让开发者在一个系统上开发(例如 Ubuntu),然后打包成镜像,再去另一个系统上运行(例如 CentOS),完全不需要考虑环境依赖的问题,是一种更高级的应用打包方式。

容器化的应用

docker pull busybox 就是获取了一个打包了 busybox应用的镜像,里面固化了 busybox 程序和它所需要的完整运行环境。
docker run busybox echo hello word ,就是提取镜像里的各种信息,运用 namespace、cgroup、chroot 技术创建出隔离环境,然后再运行 busybox 的 echo 命令,输出 hello word 字符串。
这两个步骤,由于是基于标准的 Linux 系统调用和只读的镜像文件,所以,无论是在哪种操作系统上,或者是使用哪种容器实现技术,都会得到完全一致的结果。
推而广之,任何应用都能够用这种形式打包再分发后运行,这也是无数开发者梦寐以求的“一次编写,到处运行(Build once, Run anywhere)”的至高境界。
所以,所谓的“容器化的应用”,或者“应用的容器化”,就是指应用程序不再直接和操作系统打交道,而是封装成镜像,再交给容器环境去运行。
现在知道了,镜像就是静态的应用容器,容器就是动态的应用镜像,两者互相依存,互相转化,密不可分。
在 Docker 里的核心处理对象就是镜像(image)和容器(container)

镜像操作:

镜像操作命令的组成

  • 名字表明了应用的身份,比如 busybox、Alpine、Nginx、Redis 等等。
  • 标签(tag)则可以理解成是为了区分不同版本的应用而做的额外标记,任何字符串都可以:比如 3.15 是纯数字的版本号、jammy 是项目代号、1.21-alpine 是版本号加操作系统名等等。其中有一个比较特殊的标签叫“latest”,它是默认的标签,如果只提供名字没有附带标签,那么就会使用这个默认的“latest”标签。

    操作:

    现在,你就可以把名字和标签组合起来,使用 docker pull 来拉取一些镜像了:

    1. docker pull alpine:3.15
    2. docker pull ubuntu:jammy
    3. docker pull nginx:1.21-alpine
    4. docker pull nginx:alpine
    5. docker pull redis

    docker images

    有了这些镜像之后,我们再用 docker images 命令来看看镜像列表和它们的具体信息:
    image.png

    images列表解释:

  • REPOSITORY 列就是镜像的名字

  • TAG 就是这个镜像的标签
  • IMAGE ID 是镜像唯一标识

    说下 IMAGE ID

    镜像唯一的标识,就好像是身份证号一样。比如这里我们可以用“ubuntu:jammy”来表示 Ubuntu 22.04 镜像,同样也可以用它的 ID“d4c2c……”来表示。
    截图里的两个镜像“nginx:1.21-alpine”和“nginx:alpine”的 IMAGE ID 是一样的,都是“a63aa……”。这就像是人的身份证号码是唯一的,但可以有大名、小名、昵称、绰号,同一个镜像也可以打上不同的标签,这样应用在不同的场合就更容易理解。
    IMAGE ID 还有一个好处,因为它是十六进制形式且唯一,Docker 特意为它提供了“短路”操作,在本地使用镜像的时候,我们不用像名字那样要完全写出来这一长串数字,通常只需要写出前三位就能够快速定位,在镜像数量比较少的时候用两位甚至一位数字也许就可以了。

    docker rmi

    它用来删除不再使用的镜像,可以节约磁盘空间,注意命令 rmi ,实际上是“remove image”的简写。
    1. docker rmi redis # 删除 redis 镜像
    2. docker rmi d4c # 删除IMAGE ID的前缀是 d4c 的镜像
    image.png

    容器操作:

    docker run:

    使用 docker run 命令把这些静态的应用运行起来,变成动态的容器了。
    基本的格式是“docker run 设置参数”,再跟上“镜像名或 ID”,后面可能还会有附加的“运行命令”。
    1. docker run -h srv alpine hostname

    命令解释:

    -h srv 就是容器的运行参数,alpine 是镜像名,它后面的 hostname 表示要在容器里运行的“hostname”这个程序,输出主机名。
    docker run 是最复杂的一个容器操作命令,有非常多的额外参数用来调整容器的运行状态,可以加上 —help 来看它的帮助信息

    几个常用参数:

  1. -it 表示开启一个交互式操作的 Shell,这样可以直接进入容器内部,就好像是登录虚拟机一样。(它实际上是“-i”和“-t”两个参数的组合形式)
  2. -d 表示让容器在后台运行,这在我们启动 Nginx、Redis 等服务器程序的时候非常有用。
  3. —name 可以为容器起一个名字,方便我们查看,不过它不是必须的,如果不用这个参数,Docker 会分配一个随机的名字。

三个参数,分别运行 Nginx、Redis 和 Ubuntu:

  1. docker run -d ngix:alpine # 后台运行nginx
  2. docker run -d --name red_srv redis # 后台运行Redis 并起名 red_srv
  3. docker run -it --name ubuntu 2e6 sh # 使用IMAG ID 登录Ubuntu18.04

docker ps

因为第三个命令使用的是 -it 而不是 -d ,所以它会进入容器里的 Ubuntu 系统,我们需要另外开一个终端窗口
使用 docker ps 命令来查看容器的运行状态:
image.png
每一个容器也会有一个“CONTAINER ID”,它的作用和镜像的“IMAGE ID”是一样的,唯一标识了容器。

docker exec

命令在容器里面执行另一个程序
因为容器已经存在,所以不会创建新的容器。它最常见的用法是使用 -it 参数打开一个 Shell,从而进入容器内部,例如:

  1. docker exec -it red_srv sh

这样我们就“登录”进了 Redis 容器,可以很方便地查看服务的运行状态或者日志。

docker stop

运行中的容器可以使用 docker stop 命令来强制停止。
这里我们仍然可以使用容器名字,不过或许用“CONTAINER ID”的前三位数字会更加方便。

  1. docker stop ed4 d60 45c

容器被停止后使用 docker ps 命令就看不到了,不过容器并没有被彻底销毁。

docker ps -a

用 docker ps -a 命令查看系统里所有的容器,当然也包括已经停止运行的容器:
image.png
这些停止运行的容器可以用 docker start 再次启动运行,如果你确定不再需要它们,可以使用 docker rm 命令来彻底删除。

docker rm

注意,这个命令与 docker rmi 非常像,区别在于它没有后面的字母“i”,所以只会删除容器,不删除镜像。
运行 docker rm 命令,使用“CONTAINER ID”的前两位数字来删除这些容器:

  1. docker rm ed d6 45

执行删除命令之后,再用 docker ps -a 查看列表就会发现这些容器已经彻底消失了。

让 Docker 自动删除不需要的容器

在执行 docker run 命令的时候加上一个 —rm 参数,这就会告诉 Docker 不保存容器,只要运行完毕就自动清除,省去了我们手工管理容器的麻烦。比如:

  1. docker run -d --rm nginx:alpine
  2. docker run -d --rm redis

然后我们用 docker stop 停止容器,再用 docker ps -a ,就会发现不需要我们再手动执行 docker rm ,Docker 已经自动删除了这三个容器。
image.png

总结

  • 镜像是容器的静态形式,它打包了应用程序的所有运行依赖项,方便保存和传输。使用容器技术运行镜像,就形成了动态的容器,由于镜像只读不可修改,所以应用程序的运行环境总是一致的。
  • 而容器化的应用就是指以镜像的形式打包应用程序,然后在容器环境里从镜像启动容器。
  • 由于 Docker 的命令比较多,而且每个命令还有许多参数,参考 Docker 自带的帮助或者官网文档(https://docs.docker.com/reference/

镜像操作和容器操作小结:

  • 常用的镜像操作有 docker pull、docker images、docker rmi,分别是拉取镜像、查看镜像和删除镜像。
  • 用来启动容器的 docker run 是最常用的命令,它有很多参数用来调整容器的运行状态,对于后台服务来说应该使用 -d。
  • docker exec 命令可以在容器内部执行任意程序,对于调试排错特别有用。
  • 其他常用的容器操作还有 docker ps、docker stop、docker rm,用来查看容器、停止容器和删除容器。