Docker知识点整理
1.Docker架构
和传统虚拟化相比,省去了Hypervisor层。基于内核的Cgroup和Namespace技术,处理逻辑与内核深度融合,所以在很多方面,它的性能与物理机非常接近。
通信上,Docker不会直接和内核交互,它是通过一个更底层的工具Libcontainer与内核交互的。Libcontainer是真正意义上的容器引擎,通过clone系统调用直接创建容器,通过pivot_root系统屌用进入容器,且同坐直接操作cgroupfs文件实现对资源的管控,而Docker本身则侧重于处理更上层的业务。
2.容器的组成
容器=cgroup+namespace+rootfs+容器引擎(用户态工具)
各功能功能如下:
- Cgroup:资源控制
- Namespace:访问隔离
- rootfs:文件系统隔离
- 容器引擎:生命周期隔离。
目前市场上所有Liunx容器项目都包含以上组件。
3.容器创建原理
抽象代码如下:
pid = clone(fun, stack, flags, clone_arg);
(flags: CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET|
CLONE_NEWIPC | CLONE_NEWUTS | ...)
注:通过clone系统调用,并传入各个Namespace对应的clone flag,创建了一个新的子进程,该进程拥有了自己的Namespace.
echo $PID > /sys/fs/cgroup/cpu/tasks
echo $PID > /sys/fs/cgroup/cpuset/tasks
echo $PID > /sys/fs/cgroup/blkio/tasks
echo $PID > /sys/fs/cgroup/memory/tasks
echo $PID > /sys/fs/cgroup/devices/tasks
echo $PID > /sys/fs/cgroup/freezer/tasks
注:将代码一产生的进程pid写入各种Cgroup子系统中,这样该进程就可以收到相应Cgroup子系统的控制。
func ()
{
...
pivot_root("path_of_rootfs/", path);
...
exec("/bin/bash");
...
}
注:该fun函数由上面生成的新进程执行,在fun函数中,通过pivot_root系统屌用,使进程进入一个新的rootfs,之后通过exec系统调用,在新的Namespace、Cgroup、rootfs中执行"/bin/bash"程序。
通过以上操作,成功地在一个“容器”中运行了一个bash程序。
3.Cgroup介绍
Ggroup是 control group的缩写,属于内核提供的特征,用于限制和隔离一组进程对系统资源的使用,做资源Qos,主要包括CPU、内存、block I/O 和网络带宽。
Cgroup中实现的子系统及其作用如下:
- devices: 设备权限控制
- cpuset: 分配指定的CPU和内存节点
- cpu: 控制CPU占用率
- cpuacct:统计CPU使用情况
- memory:限制内存的使用上限
- freezer:冻结(暂停)Cgroup中的进程
- net_cls:配合tc(traffic controller)限制网络带宽
- net_prio:设置进程的网络流量优先级
- huge_tlb: 限制HugeTLB的使用
- pref_event: 允许pref工具基于Cgroup分组做性能监测
3.1Cgroup子系统介绍
3.1.1cpuset子系统
cpuset可以为一组进程分配指定的CPU和内存节点。通过将进程绑定到固定的CPU和内存节点上,避免进程在运行时因跨节点内存访问而导致的性能下降。
cpuset的主要接口如下:
- cpuset.cpus: 允许进程使用的CPU列表(例如0~4,9)
- cpuset.mems: 允许进程使用的内存节点列表(例如0~1)
备注:
NUMA结构下, 每个处理器CPU与一个本地内存直接相连, 而不同处理器之前则通过总线进行进一步的连接, 因此相对于任何一个CPU访问本地内存的速度比访问远程内存的速度要快
linux内核把物理内存按照CPU节点划分为不同的node, 每个node作为某个cpu节点的本地内存, 而作为其他CPU节点的远程内存, 而UMA结构下, 则任务系统中只存在一个内存node, 这样对于UMA结构来说, 内核把内存当成只有一个内存node节点的伪NUMA
3.1.2cpu子系统
cpu子系统用于限制进程的CPU占用率。实际功能如下:
- CPU比重分配。此特征使用的接口是cpu.shares。假设在cgroupfs的根目录下创建了两个Cgroup(c1和c2),并将cpu.shares分别设置为512和1024.当C1和C2争用CPU时,C2将会比C1得到多一倍的CPU占用率。要注意的是,只有当他们争用CPU时cpu share才会起作用,如果c2空闲,c1可以得到全部资源。
- CPU带宽限制。此特征使用的接口是cpu.cfs_period_us和 cpu.cfs_quota_us,这两个接口的单位是毫秒。可以将period设置为1s,将quota设置为0.5s,那么Cgroup中的进程在1s内最多运行0.5s,然后就会被强制睡眠,知道进入下一秒。
- 实时进程的CPU带宽限制。上面两个特征只能限制普通进程,若要限制实时进程,就要使用cpu.rt_period_us和cpu.rt_runtime_us两个接口。方法和上面类似。
3.1.3cpuacct子系统
cpuacct子系统用来统计各个Cgroup的CPU使用情况,有如下接口。
- cpuacct.stat: 报告这个Cgroup分别在用户态和内核态消耗的CPU时间,单位是USER_HZ。USER_HZ在x86上一般是100,即1 USER_HZ等于0.01s。
- cpuacct.usage: 报告这个Cgroup消耗的总CPU时间,单位纳秒。
- cpuacct.usage_percpu: 报告这个Cgroup在各个CPU上消耗的CPU时间,总和也就是cpuacct.usage的值。
3.1.4memory子系统
memory子系统用来限制Group所能使用的内存上限,有如下接口。
- memory.limit_in_bytes: 设置内存上限,单位是字节,也可以使用k/K、m/M和g/G。默认情况下,如果Cgroup使用的内存超过上限,Linux内核会尝试回收内存,如果仍无法将内存使用量控制在上限之内,系统将会触发OOM。
- 例如: echo 1G > memory.limit_in_bytes
- memory.memsw.limit_in_bytes: 设定内存加上交换分区的使用总量。
- memory.oom_control: 如果设置为0,那么在内存使用量超过上限时,系统不会杀死进程,而是阻塞进程知道有内存被释放可供使用时;另一方面,系统会向用户态发送事件通知,用户态的监控程序可以根据该事件来做相应的处理,例如提高内存上限。
- memory.stat: 汇报内存使用信息。
5.1.5blkio子系统
blkio子系统用来限制cgroup的block I/O ,有如下接口。
-
blkio.weight: 设置权重值,范围在100~1000之间,跟cpu.shares类似。
-
blkio.weight_deivce: 对具体的设备设置权重值,这个值会覆盖blkio.weight
- 例如将Group对/dev/vda1的权重设置为最小
# ls -l /dev/vda1 brw-rw---- 1 root disk 254, 1 Sep 22 2021 /dev/vda1 # echo "254:1 100" > blkio.weigth_device
-
blkio.throttle.read_bps_device: 对具体的设备,设置每秒读磁盘的带宽上限。
- 例如对/dev/sda的读带宽限制在1MB/秒; # echo "8:0 1048576" > blkio.throttle.read_bps_device
-
blkio.throttle.write_bps_device: 设置每秒写磁盘的带宽上限。同样需要指定设备。
-
blkio.throttle.read_ips_device: 设置每秒读磁盘的IOPS上限。同样需要指定设备。
-
blkio.throttle.write_ips_device: 设置每秒写磁盘的IOPS上限。同样需要指定设备。
5.5.6 device子系统
device子系统用来控制Cgroup的进程对哪些设备有访问权限。有如下接口。
- devices.list.只读文件,显示目前允许访问的设备列表,每个条目都有三个域,分别为:
- 类型: 可以是a(设备)、c(字符设备)、或b(快设备)
- 设备号: 格式为major:minnor 的设备号
- 权限:rw和m的组合。表示读、写可创建设备结点(mknod)
- 例子
" a *:* rmw"
表示所有设备都可以访问" c 1:3 r"
表示对1:3 这个字符设备即/dev/null只有读权限。
- devices.allow。只写文件,将上面描述的格式写入改文件,就可以拥有访问权限。
- devices.deny。 同上
6. Namespace介绍
Namespace是将内核的全局资源做封装,使用每个Namespace都有一份独立资源,因为不同的进程在各自的Namespace内对同一种资源的使用不会相互干扰。
目前Linux内核总共实现了6种Namespace:
- IPC: 隔离System V IPC 和 POSIX消息队列。
- Network
- Mount
- PID
- UTS: 隔离主机名和域名
- User: 隔离用户ID和组ID
6.1Namespace的接口和使用
Namespace的操作,主要是通过clone、setns和unshare这三个系统调用完成的
clone可以用来创建新的Namespace。它接受一个叫flags的参数,这些flag包括 CLONE_NEWS, CLONE_NEWIPC, CLONE_NEWUTS, CLONE_NEWNET, CLONE_NEWUSER,我们通过传入这些CLONE_NEW*来创建新的Namespace。 CLONE_NEWS是用来创建新Mount Namespace的。
unshare可以为已有的进程创建新的Namespace。调用这个系统调用的进程,会被放进新创建的Namespace里
setns可以将进程放到已有的Namespace里。
6.2 Namespace介绍
6.2.1UTS
UTS Namespace用于对主机名和域名进行隔离。使用UTS的原因,主机名可以替代IP地址,也就可以使用主机名在网络上访问某台机器,如果不做隔离,这个机制在容器里会出问题。
6.2.2 IPC
IPC是 Inter_Process Communication的缩写,也就是进程间通信。IPC Namespace针对的是System V IPC 和Posix消息队列。这些IPC机制都会用到标识符,例如用标识符来区别不同的消息队列,然后通过标识符找到对应的消息队列进行通信等。
IPC Namespace 能做到的事情是,使相同的标识符在两个Namespace中代表不同的消息队列,这样也就使得两个Namespace中的进程不能通过IPC进程通信了。
6.2.3 PID
PID Namespace 用于隔离进程PID号,这样一来,不同的Namespace里的进程PID号就可以是一样的了。
6.2.4 Mount
Mount Namespace 用来隔离文件系统挂载点,每个进程能看到的文件系统都记录在/proc/$$/mounts。在创建了一个新的Mount Namespace后,进程系统对文件系统的挂在/卸载的动作就不会影响到其他Namespace.
6.2.5 Network
这个Namespace会对网络相关的系统资源进行隔离,每个Network Namespace都有自己的网络设备、IP地址、路由表、/proc/net目录、端口号等。
新创建的Network Namespace会有一个loopback设备,除此之外不会有任何其他网络设备,因此用户需要在这里面做自己的网络配置。
6.2.6 User
User Namespace用来隔离用户和组ID。User Namespace最有用的地方在于,host的普通用户进程在容器里可以是0号用户,也就是root用户。这样,进程在容器里可以做各种特权操作,但是它的特权被限制在容器里。
7.Docker Image
7.1 概念介绍
Docker image是用来启动容器的只读模板,是容器启动所需要的rootfs,类似虚拟机的镜像。
Docker镜像的表示方法
Remote-dockerhub.com/namespace/Repository:latest
- Remote docker hub: 集中存储镜像的Web服务器地址。改部分的存在用来区分从不同的镜像库中拉去镜像。若Docker的镜像表示中缺少该部分,说明使用默认的镜像库。
- Namespace: 类似于GitHub中的命名空间,是一个用户或组织中所有镜像的集合。
- Repository: 类似Git仓库,一个仓库可以有多个镜像,不同镜像通过tag来区分
- Tag: 类似Git的tag,区分不同的版本
- Layer: 镜像由一系列层组成,每层都用64位的十六进制数表示
- Image ID: 镜像最上层layer ID 就是该镜像的ID。
7.2使用Docker Image
列出本机的镜像
# docker images
列出悬挂镜像
# docker images --filter "dangling=true"
删除悬挂镜像
# docker images --filter "dangling=true" -q |xargs docker rmi
下载镜像
# docker pull busybox
导入镜像(docker load 一般适用于docker save导出的镜像)
docker save -o busybox.tar busybox
docker load -i busybox.tar
增量生成一个镜像
# docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
7.3 Docker image的组织结构
Docker image 包含着数据及必要的元数据。数据由一层层的image layer组成,元数据则是一些JSON文件,用来描述数据(image layer)之间的关系和容器的一些配置信息。
7.3.1总体信息
- /var/lib/docker/image/overlay2:存储镜像管理数据的目录,以使用的存储驱动命名,在我的ubuntu系统上,使用的驱动为overlay2。
- repositories-overlay文件可以看到该存储目录下的所有image以及对应的layer ID。信息如下。
cat /var/lib/docker/image/overlay2/repositories.json |python -m json.tool
{
"Repositories": {
"debian": {
"debian:10": "sha256:6642e362a8254d7645ed8dae69be5e3edcd2e9a5ee77baeb0655595244082d13",
"debian@sha256:5b57f8c365c40fde437d53b953c436995525be7c481eb0128b1cbf3b49b0df18": "sha256:6642e362a8254d7645ed8dae69be5e3edcd2e9a5ee77baeb0655595244082d13"
},
"registry.cn-hangzhou.aliyuncs.com/ossrs/srs": {
"registry.cn-hangzhou.aliyuncs.com/ossrs/srs:4": "sha256:9cfcbd2070a489f9e62f6b66fb8f3c106a4b831ce81d4ce49add0b0c34ab39c6",
"registry.cn-hangzhou.aliyuncs.com/ossrs/srs@sha256:f936cb86d4bf3c31ef319471c1134c5bdf62931235335ac3bb4df2dbe190f1b7": "sha256:9cfcbd2070a489f9e62f6b66fb8f3c106a4b831ce81d4ce49add0b0c34ab39c6"
}
}
}
- /var/lib/docker/image/overlay2/distribution$: 从远端拉到本地的镜像相关元数据
- /var/lib/docker/image/overlay2/imagedb:镜像数据库
- /var/lib/docker/image/overlay2/imagedb/content:镜像ID.
- /var/lib/docker/image/overlay2/layerdb: 镜像每个layer的元数据。
7.3.2 数据的组织
[
{
"Id": "sha256:84581e99d807a703c9c03bd1a31cd9621815155ac72a7365fd02311264512656",
"RepoTags": [
"nginx:1.7.9"
],
"RepoDigests": [
"nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451"
],
"Parent": "",
"Comment": "",
"Created": "2015-01-27T18:43:46.874407465Z",
"Container": "a90675014650c505222e83897ffabcc42d43edd9465b4702dd4dc170a8713585",
"ContainerConfig": {
"Hostname": "dc534047acbb",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"443/tcp": {},
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.7.9-1~wheezy"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) CMD [nginx -g daemon off;]"
],
"Image": "e90c322c3a1c8416eb76e6eec8ad2aac7ae2c37b9e6fe6d62cce8224f90e3001",
"Volumes": {
"/var/cache/nginx": {}
},
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": [],
"Labels": null
},
"DockerVersion": "1.4.1",
"Author": "NGINX Docker Maintainers \"docker-maint@nginx.com\"",
"Config": {
"Hostname": "dc534047acbb",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"443/tcp": {},
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.7.9-1~wheezy"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "e90c322c3a1c8416eb76e6eec8ad2aac7ae2c37b9e6fe6d62cce8224f90e3001",
"Volumes": {
"/var/cache/nginx": {}
},
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": [],
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 91664166,
"VirtualSize": 91664166,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/23f1fb53a3fc3ae631226f6ed1e1bf818ab1b69e1211826d0be590ca2ae32dae/diff:/var/lib/docker/overlay2/9e766a89d2463b9aa2b2992ec384479b41c3caceef6da8f2bdd32dc2b46e8392/diff:/var/lib/docker/overlay2/68987914ca6e8b569f125680b7ea613f7afece4fcb6ab57908f6773cb8bd4540/diff:/var/lib/docker/overlay2/8ae0ce80fc63fc8ce6363ce01ebaaafbe1aa22a2ec694b006518d9c932300a74/diff:/var/lib/docker/overlay2/3d4f899e8ba4e0fd0a8939996f478e47b2035818b3cf8e77e76798af795d966a/diff:/var/lib/docker/overlay2/8e6f646f4208f9d8f92ad11701113866926bf86e5d2f0ffb5d1468ab55e68e16/diff:/var/lib/docker/overlay2/c66c5303a281b450fdba3ec4d15adb3c2df0923726604cfd9b180311ba7be1d3/diff:/var/lib/docker/overlay2/4edb12e0b836ad7432d9b4ff4764860be0137ef2158e424f3cba9108433fe1a8/diff:/var/lib/docker/overlay2/e0286810bb3d8ecfb6805c8448fbd13bfa8628a838b6afd09f33fb50bb5c5e1f/diff:/var/lib/docker/overlay2/9ca3926b529bfd2acc708de1b32f247e8084764397d9be84da4495227524f8f4/diff:/var/lib/docker/overlay2/9e4153054116b4816d0735ab9934ffc47509227fa0bb8bdb6e63909882112c06/diff:/var/lib/docker/overlay2/aa301becf97cb3ca7d998d7673a3eab2d3d9987921ea80ab97aef9be88305e73/diff",
"MergedDir": "/var/lib/docker/overlay2/b70ccb1704354eb7b16c3eab884210693dfa05db39cc51c1177d65c082fd37f9/merged",
"UpperDir": "/var/lib/docker/overlay2/b70ccb1704354eb7b16c3eab884210693dfa05db39cc51c1177d65c082fd37f9/diff",
"WorkDir": "/var/lib/docker/overlay2/b70ccb1704354eb7b16c3eab884210693dfa05db39cc51c1177d65c082fd37f9/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:dea2e4984e295e43392ea576c8159168eb877c117714940abf3af9b604e874c2",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:e02dce553481aaff0458344505cb6adc66c2714755af96a4234463c7374be46a",
"sha256:63bf84221cce5ef5920911ded704f3bf363f6024929d3b8c9aa31a4b0c7fb46a",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:e387107e2065a133384512ef47c36a2c1a31961310468b3c3eae00f4da47f2c1",
"sha256:ccb1d68e3fb76bc27e1745f004ba389489b56d00f350c6e93f7cc5b8baa03309",
"sha256:4b26ab29a475a42c35ee78b4153a67eaa9ef33e7c9e1c7d1f22c113a71b71604",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
输出的解释:
- Id :Image 的ID (最上层layer ID)
- Parent: 改layer的父层,可以递归获取某个image的所有layer信息。
- Comment: 可以为该层做一些历史记录
- Container:容器的启动需要以image为模板。但又可以把容器保存为镜像,所以一般来说image的layer都保存自一个容易,所以改容器可以说是image layer的模板
- Config: 包含了该Image的一些配置信息
- Architecture: 该image对应的CPU架构
8.Docker网络
Libnetwork 提出了新的容器网络模型(Container Network Model, CNM),定义了标准的API用于为容器配置网络,其底层可以适配多种网络驱动,CNM有三个重要的概念:
-
沙盒。沙盒是一个隔离的网络运行环境,保存了容器网络栈的配置,包括了对网络接口、路由表和DNS配置的管理。Linux平台是用 Linux Network Namespace实现的。一个沙盒可以包括来自多个网络的多个Endpoint(端点)。
-
Endpoint。Endpoint将沙盒加入一个网络,Endpoint的实现可以是一堆veth pair或者OVS内部端口,当前的Libnetwork使用的是veth pair。一个Endpoint只能隶属于一个沙盒及一个网络。通过给沙盒增加多个Endpoint可以将一个沙盒加入多个网络。
-
网络。网络包括一组能互相通信的Endpoint.网络的实现可以是Linux bridge、vlan等。
从CNM的概念角度看,Libnetwork的出现使得Docker具备了跨主机多子网的能力,同一个子网内的不同容器可以运行在不同的主机上。目前Libnetwork实现了五种驱动:
- bridge:Docker默认的容器网络驱动。Container 通过一对veth pair连接到docker0网桥上,由Docker为容器动态分配IP及配置路由、防火墙规则等。
- host: 容器与主机共享同一Network Namespace,同享同一套网络协议栈、路由表以及iptables规则等。容器与主机看到的是相同的网络试图
- nuill: 容器内网络配置为空,需要用户手动为容器配置网络接口及路由等。
- remote: Docker网络插件的实现。Remote driver使用Libnetwork可以通过HTTP RESTful API对接第三方的网络方案。
- overlay: Docker原生的跨主机多子网网络方案。主要通过使用Linux bredge和vxlan隧道实现,底层通过类似于etcd或consul的KV存储系统实现多机的信息同步。
8.1网络配置
在Linux平台下,Docker容器网络资源通过内核的Network Namespace实现隔离,不同的Network Namespace有各自的网络设备、协议栈、路由表、防火墙规则等。同一Network Namespace下的进程共享同一网络试图。
-
none: 不为容器配置任何网络功能。
- 在改模式下,需要以--net=none参数启动容器,容器启动之后,仍然可以手动为容器配置网络。
-
Container: 与另一个运行中的容器共享Network Namespace,共享相同的网络试图。
- 添加参数--net=container: ContainerID
-
host: 与主机共享Root Network Namespace,容器有完整的权限可以操纵主机的协议栈、路由表和防火墙等,所以被认为是不眼圈的
- 添加参数--net=host
-
bridge: Docker设置的NAT网络类型
-
Docker daemon启动时会在主机创建一个Linux网桥(默认为docker0)。容器启动是,Docker会创建一对veth pair(虚拟网络接口)设置,veth设备的特点是成对存在,从一端进入的数据会同时出现在另一端。Docker会将一段挂载到docker0网桥上,另一端放入容器的Network Namespace内,从而实现容器与主机通信的目的。
-
在桥接模式下,Docker容器与Internet的通信,以及不同容器之间的通信,都是通过iptables规则控制的。Docker网络的初始化动作包括:创建docker0网桥、为docker0网桥新建子网及路由、创建相应的iptables规则等。
-
#默认docker0分配了172.17.42.1/16的子网,容器以bridge网络模式运行时默认从这个子网分配IP,这个IP和阿里云分配的IP冲突,我更换了一个。 root@jiangfeng-Debian-Server:~# ip addr show docker0 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:b9:74:10:a8 brd ff:ff:ff:ff:ff:ff inet 192.168.1.5/24 brd 192.168.1.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:b9ff:fe74:10a8/64 scope link valid_lft forever preferred_lft forever #docker会写入路由表规则 root@jiangfeng-Debian-Server:~# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.19.63.253 0.0.0.0 UG 0 0 0 eth0 172.19.0.0 0.0.0.0 255.255.192.0 U 0 0 0 eth0 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0 #以bridge模式启动的容器,默认会从你的bridge子网内分配IP root@jiangfeng-Debian-Server:~# docker run -it --net=bridge debian:10 ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 66: eth0@if67: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:c0:a8:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0 valid_lft forever preferred_lft forever
-
-
overlay: Docker原生的跨主机多子网模式。overlay底层需要蕾西consul活etcd的KV存储系统进行消息同步,核心是通过Linux网桥与vxlan隧道实现跨主机划分子网。
8.2Docker网络参数
8.2.1Docker daemon
Docker daemon部分的网络参数说明如下:
$docker daemon--help
A self- sufficient runtime for linux containers .
Options:
-b,--bridge= Attach containers to a network bridge
# 指定Docker daemon使用的网桥,默认为“docker0"。若设置-b="none”,则禁用Docker的网络功能
--hip= Specify network bridge IP
#指定docker0网桥的IP,注意--bip不能与-b同时使用
--default-gateway= Container default gateway IPv4 address
#设置容器默认的IPv4网关
--default-gateway-v6= Container default gateway IPv6 address
#设置容器默认的IPv6网关
--dns=[] DNS server to use
#设置容器内的DNS服务器地址
--dns-search=[]DNS search domains to use
#设置容器内的search domain,即域名解析时默认添加的域名后缀
--fixed-cidr=IPv4 subnet for fixed IPs
#容器网络模式为bridge时,会从此子网内分配IP,本参数设置的子网必须嵌套于docker0网桥所属子网之内
--fixed-cidr-v6= IPv6 subnet for fixed IPs
#与--fixed-cidr相同,bridge模式下默认分配的IPv6子网地址
-H,--host=[] Daemon socket(s) to connect to
#指定Docker client与Docker daemon通信的socket地址,可以是tcp地址、unix socket地址. w或socket文件描述符,可同时指定多个,例如:
# docker daemon-H tcp://10.110.48.32:10000 -H unix:///var/run/docker.sock
#代表Docker daemon同时监听10.110.48.32;10000及本机/var/run/docker. sock文件
--icc-true Enable inter-container communication
# 允许/禁止容器间通信,禁用icc依赖iptables规则,若--icc-false,则必须--iptables=true
ip=0.0.0.0 Default IP when binding container ports
#容器暴露端口时默认绑定的主机IP,默认为0.0.0.0
--ip-forward=true Enable net.ipv4. ip forward
#使能IP转发功能,为true则向主机/proc/sys/net/ipv4/ip forward写入1
--ip-masq-true Enable IP masquerading
#使能IP地址变形功能(NAT),只有--iptables=true才可生效
--iptables=true Enable addition of iptables rules
#使能iptables。若设置为false,则无法向iptables表格添加规则
--ipv6=false Enable IPv6 networking
#使能IPv6网络功能
--mtu-0 Set the containers network MTU
#设置容器网络MTU(最大传输单元)
--userland-proxy=true Use userland proxy for loopback traffic
#非当设置为true时,Docker会为每个映射到主机端口的容器端口启动一个docker-proxy进程用于数据转发。实际上这会耗用大量CPU资源,Docker社区正在计划去除docker-proxy
8.2.2Docker clinet
$ docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run a command in a new container
--add-host list Add a custom host-to-IP mapping (host:ip)
#在容器内的/etc/hosts文件内增加一行主机对IP的映射
--dns list Set custom DNS servers
#设置容器的DNS服务器
--expose list Expose a port or a range of ports
#暴露容器的端口,而不映射到主机端口
-h, --hostname string Container host name
#设置容器的主机名
--link list Add link to another container
#链接到另一个容器,在本容器中可以通过容器ID或者容器名访问对方
--mac-address string Container MAC address (e.g., 92:d0:c6:0a:29:33)
#设置容器的mac地址
--net=bridge
#这是容器的网络运行模式,当前支持四种模式:bridge、none、host、container
-P,--publish-all=false Publish all exposed ports to random ports
#将容器所有暴露出来的端口映射到主机随机端口
-p, --publish list Publish a container's port(s) to the host
#将容器一段范围内的端口映射到主机指定的端口
两个示例:
docker daemon -H tcp://10.110.52.38:6000 -H unix:///var/run/docker.sock --fixed-cidr=172.17.55.0/24 --icc=false --userland-proxy=false
docker run -itd --net=bridge -p 10000:22/tcp -h docker ubuntu:latest bash
9 Docker 卷管理
Docker容器里产生的数据,如果不通过docker commit 生成新的镜像,使数据作为镜像的一部分保存下来,就会在容器删除后丢失。为了能够持久化保存和共享容器的数据,Docker 提出了卷(volume)的概念。
卷就是目录或文件,由Docker daemon挂在到容器中,不属于联合文件系统,卷中的数据在容器被删除后仍然可以访问。Docker提供了两种管理数据的方式: 数据卷和数据卷容器。
增加新数据卷
#增加新数据卷
$ docker run -v /tmp/data busybox
-v 参数会在容器的/tmp/data目录下创建一个新的数据卷,docker inspect 命令可以查看数据卷在主机中的位置
#将主机的文件或文件夹挂在到容器中
$ docker run -v /host/data:/data busybox
-v 参数的主机目录必须使用绝对路径,如果指定路径不存在,DOcker会自动创建该目录
#只读方式挂在数据卷
$ docker run -v /host/data:/data:ro busybox
创建数据卷容器
# 创建多个Postgres
$ docker create -v /dbdata --name dbdata training/postgres /bin/true
$ docker run -d --volumes-from dbdata --name db1 training/postgres
10 Docker API
配置
$ vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375
$ systemctl daemon-reload
$ systemctl restart docker
运行容器
package main
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"io"
"os"
)
func main() {
ctx := context.Background()
//初始化一个新的 API 客户端
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation(),client.WithHost("tcp://121.43.167.3:2375"))
if err != nil {
panic(err)
}
//ImagePull 请求 docker 主机从远程注册表中提取图像。如果操作未经授权,它会执行特权功能并再试一次。
reader, err := cli.ImagePull(ctx, "docker.io/library/alpine",types.ImagePullOptions{})
io.Copy(os.Stdout, reader)
resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: "alpine",
Cmd: []string{"echo", "helloworld"},
}, nil, nil,nil, "")
if err != nil {
panic(err)
}
//ContainerStart sends a request to the docker daemon to start a container.
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil{
panic(err)
}
//ContainerWait等待指定的容器处于给定条件指示的特定状态,即“未运行”(默认)、“下一个退出”或“已删除”。
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil{
panic(err)
}
case <- statusCh:
}
out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true, Timestamps: true})
if err != nil {
panic(err)
}
stdcopy.StdCopy(os.Stdout, os.Stderr, out)
}
参考文档:
https://pkg.go.dev/github.com/docker/docker/client#section-readme
https://www.jb51.net/article/208930.htm
注意:开放公网端口有被攻击的风险