Testops之路2.Docker入门
Docker架构
imageDocker 采用了 C/S 架构,包括客户端(Client)和服务端(Server),服务端通过 socket 接受来自客户端的请求,这些请求可以是创建镜像,运行容器,终止容器等等。
image可以通过docker version命令,看到docker server和clien对应的版本。
image image image- Client是docker的客户端,执行常用的docker命令。服务端既可以运行在本地主机,也可以运行在远程服务器或者云端,只要你可以访问docker的服务端,你甚至可以在容器里运行容器。
- DOCKER_HOST是docker的server端,也就是docker daemon的安装的机器。
- Registry是 Docker仓库的位置。
镜像
imageDocker 镜像是一个特殊的文件系统,类似于 Linux 的 root 文件系统,镜像提供了容器运行时所需的程序、库、资源、配置等文件,还包含了一些为运行时准备的一些配置参数。镜像是一个静态的概念,镜像不包含任何动态数据,其内容在构建之后也不会被改变。
由于镜像包含完整的 Linux root 文件系统,所以它可能会很庞大。因此,Docker 的设计者充分利用 Unions FS 技术,把 Docker 设计为分层存储的结构。什么意思呢?也就是说,镜像是分层构建的,每一层是上面一层的基础,每一层在构建完成之后都不会再发生变化。
构建镜像的时候我们要保证每一层都只包含我们的应用需要的东⻄,不要包含不需要的文件,因为每一层在构建之后不再发生变化,所以即使你在之上的层删除了那些不需要的文件,这些文件也只是被标记为删除,实际上并没有真正删除
下面,用一个形象的例子来讲解镜像拷贝的工作原理。
首先,我们有一张没有斑点的奶牛图片(相当于我们有一个基础镜像)
image
现在,我希望从这张图片得到一张有斑点的奶牛图像,我不是直接在这个原始图片画斑点,而是将斑点单独作为一层,把斑点层和原始图片叠加,就得到了斑点奶牛:
image image这样做有什么好处呢?设想一下,如果你直接把你想要的黑色斑点画到原图上,那么其他人想在原始的无斑点奶牛图片上做一些其它的创作就会很麻烦。但是,如果采取分层的方式,他们也只需要设计自己想要的斑点就可以了,从而原始的无斑点奶牛图片可以共享:
image这样,每次你想查看自己的斑点奶牛的时候,就会把原始的无斑点奶牛复制一份给你,然后叠加你的斑点图层,你就可以看到你的斑点奶牛了。而你不查看的时候,原始的无斑点奶牛图片是共享存储的,这就是写时拷贝——只有在需要叠加斑点图给你看效果的时候才把原图拷贝一份给你,在拷贝的上面叠加一层来画斑点,否则,你与其他人的无斑点奶牛图层是共享存储的。
image通过上面的图片也可以看出来,docker在从镜像仓库Regsitry拉取镜像的时候也是分层拉取的。
image通过dockerfile,也能看出来docker的分层机制。
分层存储机制使得镜像之间可以共享很多层,节约大量存储空间!
可以从docker hub上拉取镜像。
从镜像到容器
#拉取一个镜像
docker pull ubuntu:16.04
#查看拉取下来的镜像
docker images
好了,现在我们已经有一个 Ubuntu 镜像了,下面我们从这个镜像运行一个容器,Docker run 命令可以从一个镜像运行一个包含一个主进程进程的容器:
imagedocker run -ti --name first ubuntu:16.04 bash
命令解释:
Docker run 是从一个镜像运行一个容器的指令。
-ti 参数的含义是:terminal interactive,这个参数可以让我们进入容器的交互式终端。
--name 指定容器的名字,后面的 first 就是我们给这个容器起的名字。
ubuntu:16.04 是致命从哪个镜像运行容器,ubuntu是仓库名,16.04是标签。
bash 指明我们使用 bash 终端。
运行完以上命令,你会得到下面的结果,你会发现我们已经进入了容器:
image现在,你可以在容器里运行一些指令,就好像在一个全新的系统里一样,比如我们运行一个创建文件的指令:
touch daidekun.py
image
在上一节课我们强调,镜像是一个静态的概念,容器是一个动态的概念。这里我们就来看看这是什么意思,现在我们从相同的镜像再运行一个新的容器:
image会发现之前创建的文件没有了。
从容器到镜像
image并不是容器被删除了,而是容器被终止了,我们依旧可以看到我们的容器,运行以下指令可以查看我们当前的容器:
docker ps -a
image
我们刚刚在名字为 first 的容器里创建了一个文件“daidekun.py”,如果这是很重要的文件,你以后还需要它,你希望每次运行一个新容器,这个文件都要存在于新的容器中,怎么办呢?我们可以把容器变成一个新镜像。
image#把刚才名为 first 的容器提交为一个新的镜像:
docker commit first my_image:v1.0
其中,my_image:v1.0 就是“仓库名:版本号”。
image其他容器操作
Docker run
我们一直在用“Docker run”命令来创建容器,那么“Docker run”到底做了什么?
- 检查本地是否存在指定的镜像,不存在就从公共仓库下载;
- 利用镜像创建并启动一个容器;
- 给容器包含一个主进程(Docker 原则之一:一个容器一个进程,只要这个进程还存在,容器就会继续运行);
- 为容器分配文件系统,IP,从宿主主机配置的网桥接口中桥接一个虚拟接口等。
守护态运行
其实就是后台运行(background running)
在运行“docker run”命令的时候加上 “-d”参数(-d means detach)
下面举个例子说明一下:
如果不使用“-d”参数:
docker run --name withoutD ubuntu:16.04 bash -c "echo hello world"
容器会把输出结果打印到宿主主机上。
docker run -d --name withD ubuntu:16.04 bash -c "echo hello world"
如果使用“-d”参数的话,容器启动后会返回容器 ID,但是不会将输出结果打印到宿主主机:
进入容器
常用的两种进入容器的方法
imagessh 登录
attach 和 exec
删除容器
docker rm nameOfContainer
image
停止容器
sudo kill nameOfContainer
image
查看日志
比如你在后台运行一个容器,可是你把“echo”错误输入成了“eceo”:
docker run -d --name logtest ubuntu:16.04 bash -c "eceo hello"
image
其他命令
#查看容器详细信息
sudo docker inspect [nameOfContainer]
#查看容器最近一个进程
sudo docker top [nameOfContainer]
#停止一个正在运行的容器
sudo docker stop [nameOfContainer]
#继续运行一个被停止的容器
sudo docker restart [nameOfContainer]
#暂停一个容器进程
sudo docker pause [nameOfContainer]
#取消暂停
sudo docker unpause [nameOfContainer]
#终止一个容器
sudo docker kill [nameOfContainer]
容器的生命周期
image要在容器里面保存重要文件,因为容器应该只是一个进程,数据需要使用数据卷保存。
尽量坚持“一个容器,一个进程”的使用理念,当然,在调试阶段,可以使用exec命令为容器开启新进程。