文章目录
- 一、前言
- 二、Docker引入:物理机时代-->虚拟机时代-->容器化时代
- 2.1 第一个时代,物理机时代:以物理机为单位隔离资源
- 2.2 第二个时代,虚拟化时代:虚拟化技术实现Vmware,以操作系统为单位隔离资源
- 2.3 第三个时代,容器化时代:容器化技术,以应用进程为单位隔离资源
- 2.4 小结
- 三、Docker中的重要概念和重要命令
- 3.1 镜像 Image
- 3.2 容器 Container (理解了镜像就很好理解容器了)
- 3.3 运行态容器 Running Container
- 3.4 镜像层 Image Layer
- 3.4.1 镜像层:元数据metadata
- 3.4.2 镜像层:指向父层指针
- 3.5 Docker命令(启动)
- 3.5.1 docker create <image-id> 将一个静态Image变为一个容器Container
- 3.5.1.1 docker create 命令的作用
- 3.5.1.2 docker create 命令的原理
- 3.5.2 docker start <container-id> 将一个容器Container变为一个运行态容器Container
- 3.5.2.1 docker start 命令的作用
- 3.5.2.2 docker start 命令的底层
- 3.5.3 docker run <image-id> 将一个镜像Image变为一个运行态容器Container
- 3.6 Docker命令(列出)
- 3.6.1 docker ps
- 3.6.1.1 docker ps 列出所有运行中的容器,但是隐藏了非运行态容器的存在
- 3.6.1.2 docker ps -a 列出所有容器(运行态容器 + 非运行态容器)
- 3.6.2 docker images
- 3.6.2.1 docker images 列出了所有顶层(top-level)镜像
- 3.6.2.2 docker images -a 列出了所有可读层read layer
- 3.7 Docker命令 停止、暂停、删除、commit
- 3.7.1 停止与暂停
- 3.7.1.1 docker stop <container-id> 停止运行中的容器
- 3.7.1.2 docker kill <container-id> 停止运行中的容器
- 3.7.1.3 docker pause <container-id> 暂停运行中的容器
- 3.7.2 删除
- 3.7.2.1 docker rm <container-id> 移除非运行态容器的读写层
- 3.7.2.2 docker rmi <image-id> 移除镜像的可读层
- 3.7.3 docker commit 将读写层变为只读层,将容器变为镜像
- 3.8 Docker命令 其他
- 3.8.1 docker build 重复执行命令,生成新的层
- 3.8.2 docker exec <running-container-id> 在运行中的容器执行一个新进程
- 3.8.3 docker inspect <container-id> or <image-id> 提取出容器或者镜像最顶层的元数据
- 3.8.4 docker save <image-id> 创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用
- 3.8.5 docker export <container-id> 创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容
- 3.8.6 docker history <container-id> 递归地输出指定镜像的历史镜像
- 3.9 Docker重要概念小结
- 四、Docker四种网络模式(记住定义就好)
- 4.1 Docker四种网络模式(记住定义就好)
- 4.1.1 host模式(新创建的容器与宿主机共享网络资源)
- 4.1.2 container模式(新创建的容器与已存在的容器共享网络资源)
- 4.1.3 none模式(新创建的容器单独的Network namespace)
- 4.1.4 bridge模式(默认网络模式)
- 4.2 bridge模式的拓扑
- 4.3 bridge模式下容器的通信
- 五、实践:Docker安装
- 5.1 实践:Docker安装
- 5.1.1 配置仓库
- 5.1.2 安装Docker
- 5.2 实践:Docker镜像基本操作
- 5.2.1 搜索并获取镜像
- 5.2.2 导入、导出、删除镜像
- 5.2.3 容器创建
- 5.2.4 Docker资源限制
- 六、面试金手指
- 6.1 容器化引入:物理机时代、虚拟机时代、容器化时代
- 6.2 Docker重要概念
- 6.2.1 镜像
- 6.2.2 非运行态容器
- 6.2.3 运行态容器
- 6.2.4 镜像层
- 6.3 Docker重要命令
- 6.3.1 docker create / docker start / docker run
- 6.3.2 docker ps / docker ps -a 列出
- 6.3.3 停止/暂停、删除、commit
- 6.3.4 其他命令
- 6.4 Docker四种网络模式
- 6.4.1 host模式(host表示主机,即新创建的容器与宿主机共享网络资源)
- 6.4.2 container模式(container表示容器,即新创建的容器与已存在的容器共享网络资源)
- 6.4.3 none模式(none表示没有,即新创建的容器单独的Network namespace)
- 6.4.4 bridge模式(bridge表示网桥,即Docker容器连接到一个虚拟网桥上)
- 七、尾声
一、前言
Docker容器基础知识,Docker的引入、Docker重要概念、Docker重要命令、Docker四种网络模式,完成了。
二、Docker引入:物理机时代–>虚拟机时代–>容器化时代
2.1 第一个时代,物理机时代:以物理机为单位隔离资源
硬件+操作系统+Java应用
没有虚拟化的时代:
安装操作系统(Linux 和 windowServer)、安装jdk tomcat 环境变量、war部署到服务器上
物理机时代的缺点:
第一个是硬件成本,物理的资源不够,扩展物理机,增加服务器,
第二个是扩展的硬件成本造成资源浪费(单个进程使用整个服务器的资源,资源用不完)
第三个是硬件资源的限制(服务器的木板效应,每个物理机会受到自己资源短板的限制)
第四个是运维人力成本,每一个机器都要环境、补丁
2.2 第二个时代,虚拟化时代:虚拟化技术实现Vmware,以操作系统为单位隔离资源
只要物理机强大(内存128G 64核),可以做多个虚拟机,动态分配硬件资源
更好的利用资源,不会造成资源浪费
容易扩展,容易维护,降低运维成本
容器化
Hypervisor 管理程序,一个计算机虚拟成多个虚拟机
虚拟化的局限:
微服务的横向扩展,比较重
每一个虚拟机的都要一个OS,
只需要一个java程序自己需要的资源,
虚拟化通过构建独立的操作系统完成,操作系统是比较重的,以操作系统为单位隔离资源
容器化通过隔离同一个虚拟机中的物理资源,更加轻,以应用进程为单位隔离资源
2.3 第三个时代,容器化时代:容器化技术,以应用进程为单位隔离资源
可以通过使用虚拟化和容器化,如下图:
Docker微服务部署,good。
Docker上面,image镜像运行之后就变成了容器Container.
2.4 小结
小结,从物理机时代到虚拟机时代,再到容器化时代,隔离资源的单位变得越来越小
从物理机隔离到虚拟机/操作系统隔离,到应用进程隔离
物理机时代的四个问题
第一个是硬件成本,物理的资源不够,扩展物理机,增加服务器,
第二个是扩展的硬件成本造成资源浪费(单个进程使用整个服务器的资源,资源用不完)
第三个是硬件资源的限制(服务器的木板效应,每个物理机会受到自己资源短板的限制)
第四个是运维人力成本,每一个机器都要环境、补丁
虚拟化时代通过动态分配物理资源,解决了第二个资源浪费问题,第三个硬件资源短板问题,运维人力成本问题。
但是,随着微服务架构取代单体架构,在部署方面,容器化诞生,容器化时代更进一步,从虚拟机/操作系统隔离到应用进程隔离,更加细粒度的动态分配物理资源,这是为了适应微服务架构的变迁。仓库
仓库用来集中保存镜像地方当创建了自己的镜像后,可以使用qush命令上传到公共仓库
三、Docker中的重要概念和重要命令
3.1 镜像 Image
镜像(Image)就是一堆只读层(read-only layer)的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义。
对于这个图的解释
金手指:镜像(Image)就是一堆只读层(read-only layer)的统一视角
左边,所有的都是Read Layer只读层,然后两个箭头表示unioning将所有的Read Layer组织起来,箭头从上指到下,表示除了最下面一层,其它层都会有一个指针指向下一层。
右边,就是对于镜像Image的解释:Unioned Read-Only File System,译为统一的只读文件系统,镜像就是统一的只读文件系统。
左边,表示在实际存储中,可以看到多个只读层,它们重叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机(译者注:运行Docker的机器)的文件系统上访问到。
右边,表示在用户的角度看来,只存在一个文件系统。
统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在。
你可以在你的主机文件系统上找到有关这些层的文件。在我的主机上,我发现它们存在于/var/lib/docker/aufs目录下。
sudo tree -L 1 /var/lib/docker/
/var/lib/docker/
├── aufs
├── containers
├── graph
├── init
├── linkgraph.db
├── repositories-aufs
├── tmp
├── trust
└── volumes
7 directories, 2 files
问题:一个read layer在linux中怎么存储的?
回答:需要注意的是,在一个运行中的容器内部,这些层是不可见的。
3.2 容器 Container (理解了镜像就很好理解容器了)
容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
对于这个图的解释
金手指:容器(Container)就是(n-1)个只读层(read-only layer) + 最上面一个读写层 的统一视角
左边,下面(n-1)个都是Read Layer只读层,最上面一个是Read-Write Layer读写层,然后两个箭头表示unioning将所有的Read Layer组织起来,箭头从上指到下,表示除了最下面一层,其它层都会有一个指针指向下一层。
右边,就是对于容器Container的解释:Unioned Read-Write File System,译为统一的读写文件系统,容器就是统一的读写文件系统,因为只操作最上面一层,所以,只要最上面一个是Read-Write就好了。
镜像和容器的关系
镜像:类似于虚拟机的快照,可以理解为一个是面向Docker容器引擎的制度模板。举例子:一个镜像可以看做一个完整的centos操作系统,也可以从官网上下载。
容器:容器是从镜像创建的运行实例,它可以被启动停止,创建,删除。每个一个容器都是相互隔离,互不可见,可以保证平台的安全性。举例子:可以把容器看做一个简易版的Linux环境。
(1)结构上,容器 = 镜像 + 读写层。如图,镜像上面加一个读写层,就变成了容器Container。
(2)创建上,Docker 容器通过 Docker 镜像来创建(类似java中,类创建对象)。容器与镜像的关系类似于面向对象编程中的对象与类,镜像就是类,容器就是对象。
容器和运行态容器的关系
容器的定义并没有提及容器是否在运行,即容器的定义并没有提及是否要运行容器。接下来,我们将会讨论运行态容器。
3.3 运行态容器 Running Container
定义:一个运行态容器(running container) = 一个可读写的统一文件系统(即一个容器Container) + 隔离的进程空间(Process Space)+ 包含其中的进程(Process)。
下面这张图片展示了一个运行中的容器。
运行态容器三部分:读写文件系统Read Write File System + 进程空间 Process Space + 进程 Process(运行的进程就是容器最上面的那个读写层,所以进程目录是 /bin/bash/top)
正是文件系统隔离技术使得Docker成为了一个前途无量的技术。解释:**一个容器中的进程process可能会对文件(下面的happiness.txt文件)进行修改、删除、创建(ps:对统一文件管理系统中的文件修改、删除、创建),这些改变都将作用于可读写层(read-write layer)
正是文件系统隔离技术使得Docker成为了一个前途无量的技术。解释:**一个容器中的进程process可能会对文件(下面的happiness.txt文件)进行修改、删除、创建(ps:对统一文件管理系统中的文件修改、删除、创建),这些改变都将作用于可读写层(read-write layer)。**下面这张图展示了这个行为。
touch happiness.txt 表示
New FIle to be found in read-write top layer 表示
我们可以通过运行以下命令来验证我们上面所说的(即验证“一个容器中的进程process可能会对文件进行修改、删除、创建,这些改变都将作用于可读写层”):
docker run ubuntu touch happiness.txt # 这条命令表示
即便是这个ubuntu容器不再运行,我们依旧能够在主机的文件系统上找到这个新文件。
find / -name happiness.txt # 在主机的文件系统上找到这个新文件
/var/lib/docker/aufs/diff/860a7b...889/happiness.txt
3.4 镜像层 Image Layer
为了将零星的数据整合起来,我们提出了镜像层(image layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。
镜像层image layer和可读层read layer的关系:
image layer = read layer + id + 指向父层指针 + 该层的元数据metadata
所以,镜像层包括四个东西,可读层 + id + 指向父层指针 + 该层元数据metadata
上面已经知道一个read layer在linux是怎么存储的,这里不讲
id 仅仅是仅仅讲解的时候一个逻辑概念,并无实际存储,每个read-layer 都有一个以自己id命名的目录存储元数据json文件。
关键是元数据metadata和指向父层指针在linux上是怎么存储的。
元数据范围:只读层和读写层都包含元数据,
元数据定义:元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。
元数据文件:元数据metadata被保存在名为”json”的文件中
元数据路径:一个容器的元数据好像是被分成了很多文件,但或多或少能在/var/lib/docker/containers/目录下找到,就是一个可读层的id。这个目录下的文件大多是运行时的数据,比如说网络,日志等等。
每一层都包括了一个指向父层的指针,如果一个层没有这个指针,说明它处于最底层。
3.4.1 镜像层:元数据metadata
元数据定义:元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。
元数据范围:只读层和读写层都包含元数据。
解释上图:上图告诉我们,可读层read layer和读写层read-write layer都包含和metadata
Metadata Location:
我发现在我自己的主机上,镜像层(image layer)的元数据metadata被保存在名为”json”的文件中,比如说:
/var/lib/docker/graph/e809f156dc985.../json
e809f156dc985…就是这层的id
一个容器的元数据好像是被分成了很多文件,但或多或少能在/var/lib/docker/containers/目录下找到,就是一个可读层的id。这个目录下的文件大多是运行时的数据,比如说网络,日志等等。
3.4.2 镜像层:指向父层指针
每一层都包括了一个指向父层的指针,如果一个层没有这个指针,说明它处于最底层。
3.5 Docker命令(启动)
3.5.1 docker create 将一个静态Image变为一个容器Container
3.5.1.1 docker create 命令的作用
现在,让我们结合上面提到的实现细节来理解Docker的命令。
docker create <image-id> # 将一个静态Image变为一个容器Container,运行结果如下图
运行结果,将一个静态Image变为一个容器Container
3.5.1.2 docker create 命令的原理
docker create 命令的原理:docker create 命令本质是为指定image_id的镜像(image)添加了一个可读写层,构成了一个新的容器。注意,这个容器并没有运行,还不是运行态容器。
3.5.2 docker start 将一个容器Container变为一个运行态容器Container
3.5.2.1 docker start 命令的作用
docker start <container-id> # 将一个容器Container变为一个运行态容器Container,运行结果如下图
将一个容器Container变为一个运行态容器Container,运行结果如下图
3.5.2.2 docker start 命令的底层
docker start命令底层原理:docker start命令为根据container_id为指定的容器文件系统创建了一个进程隔离空间。注意,每一个容器只能够有一个进程隔离空间。
3.5.3 docker run 将一个镜像Image变为一个运行态容器Container
docker run <image-id>
看到这个命令,读者通常会有一个疑问:docker start 和 docker run命令有什么区别。
从图片可以看出,docker run 命令先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常的方便,并且隐藏了两个命令的细节,但从另一方面来看,这容易让用户产生误解。
题外话:继续我们之前有关于Git的话题,我认为docker run命令类似于git pull命令,git pull命令拉代码就是git fetch 和 git merge两个命令的组合,同样的,docker run就是docker create和docker start两个命令的组合。
3.6 Docker命令(列出)
3.6.1 docker ps
3.6.1.1 docker ps 列出所有运行中的容器,但是隐藏了非运行态容器的存在
docker ps
docker ps 命令会列出所有运行中的容器,但是隐藏了非运行态容器的存在,如果想要找出非运行态容器,我们需要使用下面这个命令。
3.6.1.2 docker ps -a 列出所有容器(运行态容器 + 非运行态容器)
docker ps –a
docker ps –a命令会列出所有的容器,不管是运行的,还是停止的。
3.6.2 docker images
3.6.2.1 docker images 列出了所有顶层(top-level)镜像
docker images
docker images命令会列出了所有顶层(top-level)镜像。实际上,在这里我们没有办法区分一个镜像和一个只读层,所以我们使用docker images命令提出了top-level镜像。只有创建容器时使用的镜像或者是直接pull下来的镜像能被称为顶层(top-level)镜像,并且每一个顶层镜像下面都隐藏了多个镜像层。
3.6.2.2 docker images -a 列出了所有可读层read layer
docker images –a
docker images –a命令列出了所有的镜像,更容易理解,可以说是列出了所有的可读层read layer。如果你想要查看某一个image-id下的所有层,可以使用docker history来查看。
3.7 Docker命令 停止、暂停、删除、commit
3.7.1 停止与暂停
3.7.1.1 docker stop 停止运行中的容器
docker stop
docker stop命令会向运行中的容器发送一个SIGTERM(sigterm)的信号,然后停止所有的进程。
3.7.1.2 docker kill 停止运行中的容器
docker kill
docker kill 命令向所有运行在容器中的进程发送了一个不友好的SIGKILL(sigkill)信号。
3.7.1.3 docker pause 暂停运行中的容器
docker pause
docker stop和docker kill命令会发送UNIX的信号给运行中的进程,docker pause命令则不一样,它利用了cgroups的特性将运行中的进程空间暂停,但是这种方式的不足之处在于发送一个SIGTSTP信号对于进程来说不够简单易懂,以至于不能够让所有进程暂停。
3.7.2 删除
3.7.2.1 docker rm 移除非运行态容器的读写层
docker rm
docker rm命令会移除构成容器的可读写层,然而,读写层只有容器Container中存在,镜像Image中是不存在读写层,所以这个命令仅对容器有用对镜像没用,而且,这个命令只能对非运行态容器执行,只能移除非运行态Container的读写层(可以这样想,如果移除运行态容器的读写层,会影响运行)。
3.7.2.2 docker rmi 移除镜像的可读层
docker rmi
docker rmi 命令会移除构成镜像的一个只读层。你只能够使用docker rmi来移除最顶层(top level layer)(也可以说是镜像),你也可以使用-f参数来强制删除中间的只读层。
3.7.3 docker commit 将读写层变为只读层,将容器变为镜像
docker commit
docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器Container转换成了不可变的镜像Image。
3.8 Docker命令 其他
3.8.1 docker build 重复执行命令,生成新的层
docker build
docker build命令非常有趣,它会反复的执行多个命令。
解释上图:
docker build两步骤:
第一,build命令先根据Dockerfile文件中的FROM指令获取到镜像;
第二,然后重复地 run(create和start)、修改、commit,在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。
3.8.2 docker exec 在运行中的容器执行一个新进程
docker exec
docker exec 命令会在运行中的容器执行一个新进程。
3.8.3 docker inspect or 提取出容器或者镜像最顶层的元数据
docker inspect or
docker inspect命令会提取出容器或者镜像最顶层的元数据。
3.8.4 docker save 创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用
docker save
docker save命令会创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用。
save命令和export命令两个不同:
1、docker save命令为每一个层都保存了它们的元数据,docker export仅保留一层
所以,
1.1 expoxt后的容器再import到Docker中,通过docker images –tree命令只能看到一个镜像;
1.2 save后的容器再import到Docker中,能够看到这个镜像的历史镜像。
2、docker save命令只能对镜像生效,docker export仅能对容器生效。
3.8.5 docker export 创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容
docker export
docker export命令创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容。
3.8.6 docker history 递归地输出指定镜像的历史镜像
docker history
docker history命令递归地输出指定镜像的历史镜像。
3.9 Docker重要概念小结
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
一个完整的Docker有以下几个部分组成:dockerClient客户端、Docker Daemon守护进程、Docker Image镜像、DockerContainer容器
Docker的C/S架构
Docker采用 C/S架构 Docker daemon 作为服务端接受来自客户的请求,并处理这些请求(创建、运行、分发容器)。 客户端和服务端既可以运行在一个机器上,也可以运行在不同机器上,相互直接通过 socket 或者RESTful API 来进行通信即可。Docker daemon 一般在宿主主机后台运行,等待接收来自客户端的消息。 Docker 客户端则为用户提供一系列可执行命令,用户用这些命令实现跟 Docker daemon 交互。
小结:docker使用起来并不难,和git svn差不多,就是docker run docker ps docker exec 三个命令,docker用户linux一键式安装组件,git svn用于团队代码管理。
四、Docker四种网络模式(记住定义就好)
4.1 Docker四种网络模式(记住定义就好)
我们在使用docker run创建Docker容器时,可以用–net选项指定容器的网络模式,Docker有以下4种网络模式:
·host模式,使用–net=host指定。
·container模式,使用–net=container:NAME_or_ID指定。
·none模式,使用–net=none指定。
·bridge模式,使用–net=bridge指定,默认设置。
下面分别介绍一下Docker的各个网络模式。
4.1.1 host模式(新创建的容器与宿主机共享网络资源)
1、Docker的资源隔离是通过Linux命名空间NameSpace实现的:Docker实现的资源隔离本质上通过Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。单就网络资源隔离来说,一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。
2、在网络方面,如果启动容器Docker的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
3、接2,例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。
4、容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
4.1.2 container模式(新创建的容器与已存在的容器共享网络资源)
第一,在网络方面,container 模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。
第二,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
4.1.3 none模式(新创建的容器单独的Network namespace)
第一,在none模式下,Docker容器拥有自己的Network Namespace,
第二,在none模式下,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
4.1.4 bridge模式(默认网络模式)
bridge模式是Docker默认的网络设置,
第一,bridge模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。bridge网络模式是docker默认的网络模式,是重点,4.2解释黑体字。
4.2 bridge模式的拓扑
当Docker server启动时,使用–net=bridge指定为桥接模式,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。
虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。单机环境下的网络拓扑如下,主机地址为10.10.101.105/24。
Docker完成以上网络配置的过程大致是这样的:
1.在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
2.Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看。
3.从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。
网络拓扑介绍完后,接着介绍一下bridge模式下容器是如何通信的。
4.3 bridge模式下容器的通信
在bridge模式下,连在同一网桥上的容器可以相互通信(若出于安全考虑,也可以禁止它们之间通信,方法是在DOCKER_OPTS变量中设置–icc=false,这样只有使用–link才能使两个容器通信)。
容器也可以与外部通信,我们看一下主机上的Iptable规则,可以看到这么一条
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
这条规则会将源地址为172.17.0.0/16的包(也就是从Docker容器产生的包),并且不是从docker0网卡发出的,进行源地址转换,转换成主机网卡的地址。这么说可能不太好理解,举一个例子说明一下。假设主机有一块网卡为eth0,IP地址为10.10.101.105/24,网关为10.10.101.254。从主机上一个IP为172.17.0.1/16的容器中ping百度(180.76.3.151)。IP包首先从容器发往自己的默认网关docker0,包到达docker0后,也就到达了主机上。然后会查询主机的路由表,发现包应该从主机的eth0发往主机的网关10.10.105.254/24。接着包会转发给eth0,并从eth0发出去(主机的ip_forward转发应该已经打开)。这时候,上面的Iptable规则就会起作用,对包做SNAT转换,将源地址换为eth0的地址。这样,在外界看来,这个包就是从10.10.101.105上发出来的,Docker容器对外是不可见的。
那么,外面的机器是如何访问Docker容器的服务呢?我们首先用下面命令创建一个含有web应用的容器,将容器的80端口映射到主机的80端口。
docker run -d --name web -p 80:80 fmzhen/simpleweb
然后查看Iptable规则的变化,发现多了这样一条规则:
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.5:80
此条规则就是对主机eth0收到的目的端口为80的tcp流量进行DNAT转换,将流量发往172.17.0.5:80,也就是我们上面创建的Docker容器。所以,外界只需访问10.10.101.105:80就可以访问到容器中得服务。
除此之外,我们还可以自定义Docker使用的IP地址、DNS等信息,甚至使用自己定义的网桥,但是其工作方式还是一样的。
五、实践:Docker安装
5.1 实践:Docker安装
5.1.1 配置仓库
# vim /etc/yum.repos.d/ghostcloud.repo
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
yum list
5.1.2 安装Docker
yum install docker-engine -y
systemctl start docker //启动docker
systemctl enable docker.service //设置开机自启动
docker version //查看版本
docker info //查看docker基本信息
5.2 实践:Docker镜像基本操作
5.2.1 搜索并获取镜像
docker search cobbler //搜索关键字cobbler
docker pull jasonlix/docker-cobbler //下载搜索出的结果
docker images //查看所有镜像 (也可以单独查询 后面跟仓库名称和标签)
docker inspect //指定ID号查看 详细信息
5.2.2 导入、导出、删除镜像
删除之前确保没有被容器使用,若有容器使用需要先删除容器。
# docker rmi cobbler:cobbler //删除 注:当镜像有多个标签时只是删除标签
存出镜像和载入镜像
# docker save -o cobbler jasonlix/docker-cobbler //将本地镜像存为文件cobbler
# docker load < cobbler //载入镜像
或者
# docker --input cobller //载入镜像
5.2.3 容器创建
docker create -it jasonlix/docker-cobbler /bin/bash 会生一串序列号
docker ps -a 查看容器中所有进程
docker start 启动容器 跟ID号
docker stop 停止同期 跟ID号
docker run jasonlix/docker-cobbler /usr/bin/bash -c ls / 查看镜像根目录
docker run -d jasonlix/docker-cobbler /usr/bin/bash -c "while true;do echo htllo;done" 一直保持在启动
docker ps -a 你要进入的序列号的状态Up About a minute 或者Up 1 second
docker exec -it 1c0b830793a7 /bin/bash 进入到容器中shell环境中前提是保持开启状态 exit退出
5.2.4 Docker资源限制
限制CPU使用速率:
docker run --cpu-quota 20000 centos(容器名) //cpu的使用率限定为20%
按比例分享CPU:
docker run --cpu-shares 1024 centos
限制CPU内核使用:
docker run --cpu-shares 1024 centos
限制CPU内核使用:
docker run --cpuset-cpus 0,1 centos //容器centos独享 第1和第2个内核
限制内存使用:
docker run -m 512m centos //限制cents容器内存512M
对blkio限制:
限制容器的/dev/sda1 的写入ipos为1MB docker run --device-write-bps /dev/sda1:1mb centos
六、面试金手指
第二部分:容器化引入是起手式,吹高逼格
第三部分:
6.1 容器化引入:物理机时代、虚拟机时代、容器化时代
小结,从物理机时代到虚拟机时代,再到容器化时代,隔离资源的单位变得越来越小
从物理机隔离到虚拟机/操作系统隔离,到应用进程隔离
物理机时代的四个问题
第一个是硬件成本,物理的资源不够,扩展物理机,增加服务器,
第二个是扩展的硬件成本造成资源浪费(单个进程使用整个服务器的资源,资源用不完)
第三个是硬件资源的限制(服务器的木板效应,每个物理机会受到自己资源短板的限制)
第四个是运维人力成本,每一个机器都要环境、补丁
虚拟化时代通过动态分配物理资源,解决了第二个资源浪费问题,第三个硬件资源短板问题,运维人力成本问题。
但是,随着微服务架构取代单体架构,在部署方面,容器化诞生,容器化时代更进一步,从虚拟机/操作系统隔离到应用进程隔离,更加细粒度的动态分配物理资源,这是为了适应微服务架构的变迁。
6.2 Docker重要概念
6.2.1 镜像
金手指:镜像(Image)就是一堆只读层(read-only layer)的统一视角
左边,所有的都是Read Layer只读层,然后两个箭头表示unioning将所有的Read Layer组织起来,箭头从上指到下,表示除了最下面一层,其它层都会有一个指针指向下一层。
右边,就是对于镜像Image的解释:Unioned Read-Only File System,译为统一的只读文件系统,镜像就是统一的只读文件系统。
6.2.2 非运行态容器
对于这个图的解释
金手指:容器(Container)就是(n-1)个只读层(read-only layer) + 最上面一个读写层 的统一视角
左边,下面(n-1)个都是Read Layer只读层,最上面一个是Read-Write Layer读写层,然后两个箭头表示unioning将所有的Read Layer组织起来,箭头从上指到下,表示除了最下面一层,其它层都会有一个指针指向下一层。
右边,就是对于容器Container的解释:Unioned Read-Write File System,译为统一的读写文件系统,容器就是统一的读写文件系统,因为只操作最上面一层,所以,只要最上面一个是Read-Write就好了。镜像和容器的关系
镜像
类似于虚拟机的快照,可以理解为一个是面向Docker容器引擎的制度模板。举例子:一个镜像可以看做一个完整的centos操作系统,也可以从官网上下载。
容器
容器是从镜像创建的运行实力,它可以被启动停止,创建,删除。每个一个容器都是相互隔离,互不可见,可以保证平台的安全性。举例子:可以把容器看做一个简易版的Linux环境。
镜像和容器的关系
(1)结构上,容器 = 镜像 + 读写层。如图,镜像上面加一个读写层,就变成了容器Container。
(2)创建上,Docker 容器通过 Docker 镜像来创建(
金手指:类创建对象
)。容器与镜像的关系类似于面向对象编程中的对象与类(
金手指:镜像就是类,容器就是对象,这个比喻好
)。
6.2.3 运行态容器
运行态容器三部分:读写文件系统Read Write File System + 进程空间 Process Space + 进程 Process(运行的进程就是容器最上面的那个读写层,所以进程目录是 /bin/bash/top)
正是文件系统隔离技术使得Docker成为了一个前途无量的技术。解释:**一个容器中的进程process可能会对文件(下面的happiness.txt文件)进行修改、删除、创建(ps:对统一文件管理系统中的文件修改、删除、创建),这些改变都将作用于可读写层(read-write layer)
6.2.4 镜像层
镜像层image layer和可读层read layer的关系:
image layer = read layer + id + 指向父层指针 + 该层的元数据metadata
所以,镜像层包括四个东西,可读层 + id + 指向父层指针 + 该层元数据metadata
上面已经知道一个read layer在linux是怎么存储的,
id 仅仅是仅仅讲解的时候一个逻辑概念,并无实际存储,每个read-layer 都有一个以自己id命名的目录存储元数据json文件。
关键是元数据metadata和指向父层指针在linux上是怎么存储的。
元数据范围:只读层和读写层都包含元数据,
元数据定义:元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。
元数据文件:元数据metadata被保存在名为”json”的文件中
元数据路径:一个容器的元数据好像是被分成了很多文件,但或多或少能在/var/lib/docker/containers/< id>目录下找到,< id>就是一个可读层的id。这个目录下的文件大多是运行时的数据,比如说网络,日志等等。
每一层都包括了一个指向父层的指针,如果一个层没有这个指针,说明它处于最底层。
6.3 Docker重要命令
6.3.1 docker create / docker start / docker run
docker create 将一个静态Image变为一个容器Container
原理:docker create 命令本质是为指定image_id的镜像(image)添加了一个可读写层,构成了一个新的容器。注意,这个容器并没有运行,还不是运行态容器。
docker start 将一个容器Container变为一个运行态容器Container
原理:docker start命令为根据container_id为指定的容器文件系统创建了一个进程隔离空间。注意,每一个容器只能够有一个进程隔离空间。
docker run 将一个镜像Image变为一个运行态容器Container
6.3.2 docker ps / docker ps -a 列出
docker ps 列出所有运行中的容器,但是隐藏了非运行态容器的存在
docker ps -a 列出所有容器(运行态容器 + 非运行态容器)
6.3.3 停止/暂停、删除、commit
docker stop 停止运行中的容器
docker kill 停止运行中的容器
docker pause 暂停运行中的容器
docker rm 移除非运行态容器的读写层
docker rmi 移除镜像的可读层
docker commit 将读写层变为只读层,将容器变为镜像
6.3.4 其他命令
docker build 重复执行命令,生成新的层
docker build两步骤:
第一,build命令先根据Dockerfile文件中的FROM指令获取到镜像;
第二,然后重复地 run(create和start)、修改、commit,在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。
docker exec 在运行中的容器执行一个新进程
docker inspect container-id or image-id 提取出容器或者镜像最顶层的元数据
docker save 创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用
docker export 创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容
save命令和export命令两个不同:
1、docker save命令为每一个层都保存了它们的元数据,docker export仅保留一层
所以,
1.1 expoxt后的容器再import到Docker中,通过docker images –tree命令只能看到一个镜像;
1.2 save后的容器再import到Docker中,能够看到这个镜像的历史镜像。
2、docker save命令只能对镜像生效,docker export仅能对容器生效。
docker history 递归地输出指定镜像的历史镜像
6.4 Docker四种网络模式
6.4.1 host模式(host表示主机,即新创建的容器与宿主机共享网络资源)
1、Docker的资源隔离是通过Linux命名空间NameSpace实现的:Docker实现的资源隔离本质上通过Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。单就网络资源隔离来说,一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。
2、在网络方面,如果启动容器Docker的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
3、接2,例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。
4、容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
6.4.2 container模式(container表示容器,即新创建的容器与已存在的容器共享网络资源)
第一,在网络方面,container 模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。
第二,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
6.4.3 none模式(none表示没有,即新创建的容器单独的Network namespace)
第一,在none模式下,Docker容器拥有自己的Network Namespace,
第二,在none模式下,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
6.4.4 bridge模式(bridge表示网桥,即Docker容器连接到一个虚拟网桥上)
bridge模式是Docker默认的网络设置,
第一,bridge模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。bridge网络模式是docker默认的网络模式,是重点,4.2解释黑体字。
七、尾声
Docker基本命令使用,完成了。