Docker内核虚拟化技术
docker主要基于PAAS平台为开发者提供服务,可移植性和轻量级是他的特点,基于go语言开发(现代版c语言)
Libcontainer(runC)
Docker并不会直接与内核交互,它是通过一个更底层的工具Libcontainer(类似libvirt)与内核交互。Libcontainer后被runC替代(也就是改了个名字)
Libcontainer主要功能:容器创建和初始化、容器生命周期管理。
Libcontainer工作过程:Docker收集和整理运行一个容器所需的信息,这些信息最终将汇总在一个Config结构体中。libcontainer根据Config中的信息创建容器,并在后续进行容器生命周期管理。
LXC: Linux Container
Docker Engine容器引擎
Docker Engine是一个Client/Server应用程序。Docker engine主要组件有3部分:
- 服务器:是一个长期运行的程序,称为daemon进程。
Docker daemon用于创建和管理docker对象,如容器镜像、容器、网络、卷。
- 命令行界面客户端(docker CLl) :
CLlI使用Docker REST API通过脚本或直接的CLl命令与Docker daemon交互。 - 一个REST API: Client可以用它来与daemon进程通信交互。
Docker和KVM对比
传统虚拟化(完整虚拟化)每个虚拟机都有自己独立的Guest OS(通过linxu内核隔离,安全性好,但是影响性能啊,应用程序(app)产生价值,Guest OS比较尴尬)
Docker则是多app运行在同lib/bin上(共享内核),节省资源,可以将资源分配给app,但是安全性较差,容器和容器之间没有隔离机制。
Docker的构成
Docker由仓库,镜像,容器组成
Docker的分层结构
镜像的分层: Docker 的镜像通过联合文件系统( union filesystem )将各层文件系统叠加在一起
容器镜像采用分层结构,可分为镜像层和容器层:
- 镜像层:只读,每一个镜像层都可以共享。
- 容器层:可读写,在容器启动时被加载到镜像层之上。
bootfs:用于系统引导的文件系统,包括bootloader 和 kernel,容器启动完成后会被卸载以节省内存资源
roofs:位于bootfs 之上,表现为Docker容器的跟文件系统
传统模式中,系统启动时,内核挂载rootfs时会首先将其挂载为“只读”模式,完整性自检完成后将其挂载为读写模式
Docker 中,rootfs 由内核挂载为“只读”模式,而后通过UPS 技术挂载一个”可写”层
Docker的运行程序是由镜像组成的,是分层的,容器层依赖镜像层,并且上层数据会覆盖下层,有不同的会放在空白层。
UnionFS联合文件系统
Docker镜像分层结构的实现,借助于UnionFS联合文件系统的能力。
Union File System联合文件系统,是实现Docker镜像的技术基础。它是一种轻量级的高性能分层文件系统,支持将文件系统中的修改进行提交和层层叠加。这个特性使得镜像可通过分层实现和继承。同时支持将不同目录挂载到同一个虚拟文件系统下
以Overlay2为例:Overlay2是一种堆叠文件系统,它依赖并建立在其它的文件系统之上(如ext4fs、xfs等),并不直接参与磁盘空间结构的划分,仅将原来底层文件系统中不同的目录进行“合并”,然后向用户呈现,这也就是联合挂载技术。
举例:查看ubuntu的分层结构
docker inspect ubuntu
Overlay2文件系统中的目录说明:
- LowerDir:指向镜像层,用于存放镜像层;
- UpperDir:指向容器层,在容器中创建文件后,文件出现在此目录;
- MergedDir:容器挂载点,lowerdir和upperdir整合起来提供统一的视图给容器,作为根文件系统;
- WorkDir:用于实现copy_up操作
cow特性
常用命令
Docker基础命令
docker run后常用选项:创建并运行一个容器
-d:以后台方式启动容器。
-p︰容器端口与宿主机端口进行映射。
-P(大写):将容器内部使用的网络端口随机映射到我们使用的主机上。
-it:在容器启动后,通过命令行终端与容器交互。
-h:指定容器的hostname。
--restart=always:容器的自动启动
-h x.xx.xx:设置主机名
-dns xx.xx.xx.xx:设置容器使用的NDS服务器
--add-host hostaname:IP:注入hostname ip解析
--rm:服务停止时自动删除
docker pull 镜像:镜像下载
docker images:镜像的查询
docker ps:容器的查询
docker rmi:删除镜像
docker create:创建一个容器
docker start/stop:启动或停止一个容器
docker pause/unpause:暂停或恢复一个容器
docker rm:删除一个处于终止状态的容器
docker kill:杀死容器进程
docker build:从零开始创建一个新镜像,来构建自己的镜像
-t:指定要创建的镜像名
docker tag 标签名:修改镜像标签
docker port 容器id:查看端口绑定情况
dcoker commit 容器名 镜像名:将容器封装成镜像
docker commit,实际上是在容器运行起来后,把最上层的“可读写层”,加上原先容器镜像的只读层,打包组成了一个新的镜像。
单一容器管理命令
docker inspect 容器id/name :查看容器所有基本信息
docker logs 容器id/name :查看容器内部输出
docker stats 容器id/name :查看容器占用的资源
docker exec -it 容器名 /bn/bash :登入容器的bash
docker export 容器id > ubuntu.tar :导出容器
docker import ubuntu.tar test/ubuntu:v1 导入镜像
举例:
容器状态显示
CONTAINER ID: 容器 ID。
IMAGE: 使用的镜像。
COMMAND: 启动容器时运行的命令。
CREATED: 容器的创建时间。
STATUS: 容器状态。
状态有7种:
created(已创建)
restarting(重启中)
running 或 Up(运行中)
removing(迁移中)
paused(暂停)
exited(停止)
dead(死亡)
PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。
NAMES: 自动分配的容器名称。
查找镜像
$ docker search httpd
NAME: 镜像仓库源的名称
DESCRIPTION: 镜像的描述
OFFICIAL: 是否 docker 官方发布
stars: 类似 Github 里面的 star,表示点赞、喜欢的意思
AUTOMATED: 自动构建。
Docker 容器连接
网络端口映射
docker
- -P :是容器内部端口随机映射到主机的高端口。
- -p : 是容器内部端口绑定到指定的主机端口。
$ docker run -d -p 5000:5000 training/webapp python app.py
33e4523d30aaf0258915c368e66e03b49535de0ef20317d3f639d40222ba6bc0
$ docker ps
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
33e4523d30aa training/webapp "python app.py" ... 0.0.0.0:5000->5000/tcp berserk_bartik
fce072cc88ce training/webapp "python app.py" ... 0.0.0.0:32768->5000/tcp grave_hopper
上面的例子中,默认都是绑定 tcp 端口,如果要绑定 UDP 端口,可以在端口后面加上 /udp。
docker port 命令可以让我们快捷地查看端口的绑定情况。
runoob@runoob:~$ docker port adoring_stonebraker 5000
127.0.0.1:5001
Docker 容器互联
端口映射并不是唯一把 docker 连接到另一个容器的方法。docker 有一个连接系统允许将多个容器连接在一起,共享连接信息。docker 连接会创建一个父子关系,其中父容器可以看到子容器的信息。
容器命名
当我们创建一个容器的时候,docker 会自动对它进行命名。另外,我们也可以使用 –name 标识来命名容器,例如:
$ docker run -d -P --name test training/webapp python app.py
43780a6eabaaf14e590b6e849235c75f3012995403f97749775e38436db9a441
我们可以使用 docker ps 命令来查看容器名称。
runoob@runoob:~$ docker ps -l
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
43780a6eabaa training/webapp "python app.py" ... 0.0.0.0:32769->5000/tcp test
新建网络
Docker提供如下5种原生的Network drivers。
下面先创建一个新的 Docker 网络。
$ docker network create -d bridge test-net
参数说明:
-d:参数指定 Docker 网络类型,有 bridge、overlay。
其中 overlay 网络类型用于 Swarm mode,在本小节中你可以忽略它。
创建另一个网桥,并指定ip子网和网关
docker network create -d bridge --subnet 192.168.5.0/24 --gateway 192.168.5.1 net2
连接容器
运行一个容器并连接到新建的 test-net 网络:
$ docker run -itd --name test1 --network test-net ubuntu /bin/bash
打开新的终端,再运行一个容器并加入到 test-net 网络:
$ docker run -itd --name test2 --network test-net ubuntu /bin/bash
下面通过 ping 来证明 test1 容器和 test2 容器建立了互联关系。
这样,test1 容器和 test2 容器建立了互联关系。
如果你有多个容器之间需要互相连接,推荐使用 Docker Compose
配置 DNS
我们可以在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS:
{
"dns" : [
"114.114.114.114",
"8.8.8.8"
]
}
设置后,启动容器的 DNS 会自动配置为 114.114.114.114 和 8.8.8.8。
配置完,需要重启 docker 才能生效。
查看容器的 DNS 是否生效可以使用以下命令,它会输出容器的 DNS 信息:
$ docker run -it --rm ubuntu cat etc/resolv.conf
手动指定容器的配置
如果只想在指定的容器设置 DNS,则可以使用以下命令:
$ docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=test.com ubuntu
参数说明:
--rm:容器退出时自动清理容器内部的文件系统。
-h HOSTNAME 或者 --hostname=HOSTNAME**: 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。
--dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
--dns-search=DOMAIN: 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。
如果在容器启动时没有指定 –dns 和 –dns-search,Docker 会默认用宿主主机上的 /etc/resolv.conf 来配置容器的 DNS。
Docker数据管理
方式:①volume将容器的数据持久化存放到宿主机中 ②bind mount 将宿主机中的数据存放到容器中
volume
volume由Docker管理,是将特定目录挂载给容器。docker会在指定路径下为每个volume生成一个目录,作为mount源。volume在宿主机上存放的路径:/var/lib/docker/volumes
Volume的内容存在于容器的生命周期之外:删除容器后,Docker数据卷仍然存在。
格式:-v 容器路径
作用:主要是为了将容器产生的数据进行持久化存放!!!并且volume可以和宿主机共享数据
Volume常用操作命令
docker volume create:创建卷。
docker volume ls:查看卷信息。
docker volume rm:删除卷。
docker volume inspect 卷id:查看卷信息
docker volume --volumes-from 容器名:引用其他容器的数据卷
bind mount
bind mount是将宿主机上已有的目录或文件mount到容器中。
容器运行过程中,对bind mount目录中改动的数据,将被保存。删除容器后,bindmount中的数据仍然存在。
Namespace 和 Cgroups
Namespace和cgroup是Linux Kernel中原有的特性。
Namespace:命名空间
作用:资源隔离
原理: namespace将内核的全局资源进行封装,使得每个namespace都有一份独立的资源。因此不同进程在各自namespace内对同一种资源的使用不会相互干扰。
Cgroups: Linux Control Group
作用:限制一个进程组对系统资源的使用上限,包括CPU、内存、Block /O等。
Cgroups还能设置进程优先级,对进程进行挂起和恢复等操作。
原理:将一组进程放在一个Cgroup中,通过给这个Cgroup分配指定的可用资源,达到控制这一组进程可用资源的目的。
实现:在Linux中,Cgroups以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下。该路径中所有的资源种类均可被cgroup限制。
Cgroups中各个子系统对应的路径下有都有多个配置文件,通过这些配置文件可以对相应的资源进行限制,如:
cpu type cgroup:设置进程的cpu使用限制。
cpuset type cgroup∶为进程分配单独的CPU核和对应的内存节点。
memory type cgroup:设置进程的内存使用限制。
blkio type cgroup:设置进程的块设备I/O使用限制。
CPU资源限制
--cpu-shares:权重值,表示该进程能使用的CPU资源的权重值。
cpu.cfs_period_us和cpu.cfs_quota_us:这两个配置参数一般配合使用,表示限制进程在长度为cpu.cfs_period_us的一段时间内,只能被分配到总量为cpu.cfs_quota_us的CPU时间。
例如:某容器的cpu.cfs_period_us=100000,cpu.cfs_quota_us=10000。则表示该容器只能使用10%的CPU资源。
对进程CPU使用限制的可配置参数,详见cgroup 下cpu子系统中的配置文件:/sys/fs/cgroup/cpu/docker。
参数:-c 指定使用cpu容量 1024
注:只有容器间需要竞争使用CPU资源使用时,–cpu-shares权重值才会起作用。
内存资源限制
默认情况下,宿主机不限制容器对内存资源的使用。可使用如下参数来控制容器对内存资源的使用:
--memory:设置内存资源的使用限额
--memory-swap:设置内存和SWAP资源的使用限额
对进程内存使用限制的详细配置参数在/sys/fs/cgroup/memory目录
磁盘io限制
Block lO指的是磁盘的读写,可通过如下3种方式限制容器读写磁盘的带宽。设置相对权重
--blkio-weight:设置相对权重
--device-read-bps:设置bps:每秒读写的数据量
---device-write-bps
--device-read-iops:设置iops:每秒IO次数
--device-write-iops
文章内容仅用于作者学习使用,如果内容侵犯您的权益,请立即联系作者删除,作者不承担任何法律责任。