Docker基本知识总结
本文是学习《每天五分钟玩转Docker容器技术》的总结。是基础知识学习,熟悉docker容器技术,为了更好的研究docker相关漏洞。
容器知识图谱
容器的基本架构
- Docker客户端:
Client
- Docker服务器:
Docker daemon
- Docker镜像:
Image
- Docker仓库:
Registry
- Docker容器:
Container
通过docker我们可以方便地在Host上构建和运行容器。最常用的Docker客户端是docker命令。用户也可以通过REST API与服务器通信。
常用的命令
命令分类 | 命令 | 功能说明 | 常用示例 |
---|---|---|---|
容器生命周期管理 | docker run |
创建并启动容器 | docker run -d -p 80:80 --name mynginx nginx (后台启动 nginx,映射 80 端口) |
docker start [容器名/ID] |
启动已停止的容器 | docker start mynginx |
|
docker stop [容器名/ID] |
停止运行中的容器(优雅关闭) | docker stop mynginx |
|
docker restart [容器名/ID] |
重启容器 | docker restart mynginx |
|
docker rm [容器名/ID] |
删除容器(需先停止,-f 强制删除运行中容器) | docker rm -f mynginx |
|
容器查看 | docker ps |
查看运行中的容器 | docker ps -a (查看所有容器,包括已停止) |
docker inspect [容器名/ID] |
查看容器详细信息(配置、网络等) | docker inspect mynginx |
|
docker logs [容器名/ID] |
查看容器日志(-f 实时跟踪,–tail 100 查看最后 100 行) | docker logs -f mynginx |
|
容器操作 | docker exec -it [容器名/ID] [命令] |
进入运行中的容器并执行命令(-it 交互模式) | docker exec -it mynginx /bin/bash (进入 bash 终端) |
docker cp [本地路径] [容器名/ID]:[容器路径] |
从本地复制文件到容器 | docker cp ./test.txt mynginx:/tmp/ |
|
docker cp [容器名/ID]:[容器路径] [本地路径] |
从容器复制文件到本地 | docker cp mynginx:/tmp/test.txt ./ |
|
docker top [容器名/ID] |
查看容器内运行的进程 | docker top mynginx |
|
镜像管理 | docker images |
查看本地镜像 | docker images -a (包括中间镜像) |
docker pull [镜像名:标签] |
从仓库拉取镜像 | docker pull mysql:8.0 |
|
docker push [镜像名:标签] |
推送镜像到仓库(需先登录) | docker push myrepo/myimage:v1 |
|
docker rmi [镜像名/ID] |
删除本地镜像(-f 强制删除,需先删除依赖容器) | docker rmi -f nginx |
|
docker build -t [镜像名:标签] [Dockerfile路径] |
基于 Dockerfile 构建镜像 | docker build -t myapp:v1 . (当前目录的 Dockerfile) |
|
镜像信息 | docker image inspect [镜像名/ID] |
查看镜像详细信息 | docker image inspect nginx |
docker history [镜像名/ID] |
查看镜像构建历史(各层操作) | docker history nginx |
|
仓库管理 | docker login [仓库地址] |
登录 Docker 仓库(默认 Docker Hub) | docker login (登录 Docker Hub) |
docker logout [仓库地址] |
退出仓库登录 | docker logout |
|
docker search [关键词] |
搜索仓库中的镜像 | docker search python |
|
数据卷管理 | docker volume create [卷名] |
创建数据卷(持久化存储) | docker volume create myvol |
docker volume ls |
查看所有数据卷 | docker volume ls |
|
docker volume inspect [卷名] |
查看数据卷详细信息 | docker inspect myvol |
|
docker volume rm [卷名] |
删除数据卷 | docker volume rm myvol |
|
网络管理 | docker network ls |
查看 Docker 网络 | docker network ls |
docker network create [网络名] |
创建自定义网络(默认 bridge 模式) | docker network create mynet |
|
docker network connect [网络名] [容器名/ID] |
将容器连接到指定网络 | docker network connect mynet mynginx |
|
docker network disconnect [网络名] [容器名/ID] |
断开容器与网络的连接 | docker network disconnect mynet mynginx |
|
系统信息 | docker info |
查看 Docker 系统信息(版本、镜像数、容器数等) | docker info |
docker version |
查看 Docker 客户端和服务端版本 | docker version |
REST API
需要Docker daemon
配置开启端口,很多未授权漏洞就是这个原因:
1 | #Linux 系统 编辑 /etc/docker/daemon.json |
1 | //安全配置 |
Docker镜像
Dockerfile是镜像的描述文件,定义了如何构建Docker镜像。
1 | FROM debian |
新镜像是从base镜像(内核和host一样)一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。分层结构最大的一个好处就是:共享资源
1 | #构建镜像 |
Docker容器
镜像启动的实例。
资源限制
1 | #内存限额 -m 或 --memory |
容器的底层技术
cgroup
实现资源限额,namespace
实现资源隔离。
cgroup
cgroup全称Control Group。Linux操作系统通过cgroup可以设置进程使用CPU、内存和IO资源的限额。上面的 --cpu-shares、-m、–device-write-bps 实际上就是在配置cgroup。可以在宿主机或者容器内 /sys/fs/cgroup中找到它。有一个漏洞CVE-2022-0492(cgroup逃逸), 这里不展开写。
namespace
在每个容器中,我们都可以看到文件系统、网卡等资源,这些资源看上去是容器自己的。拿网卡来说,每个容器都会认为自己有一块独立的网卡,即使host上只有一块物理网卡。Linux实现这种方式的技术是namespace。namespace管理着host中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace实现了容器间资源的隔离。
Linux使用了6种namespace,分别对应6种资源:Mount、UTS、IPC、PID、Network和User。
-
Mount namespace让容器看上去拥有整个文件系统。
-
UTS namespace让容器有自己的hostname。
-
IPC namespace让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与host和其他容器的IPC混在一起。
-
PID namespace 让容器在host中以进程的形式运行。例如当前host中运行了两个容器。通过宿主机
ps axf
可以查看容器进程。所有容器的进程都挂在dockerd进程下,同时也可以看到容器自己的子进程。 -
Network namespace让容器拥有自己独立的网卡、IP、路由等资源。
-
User namespace让容器能够管理自己的用户,host不能看到容器中创建的用户。
Docker网络
单个host上的容器网络
none网络
none网络就是什么都没有的网络。挂在这个网络下的容器除了lo,没有其他任何网卡。
1 | docker run -itd --network=none httpd |
host网络
连接到host网络的容器共享Docker host的网络栈,容器的网络配置与host完全一样。
1 | docker run -itd --network=host httpd |
bridge网络
Docker安装时会创建一个命名为docker0的Linux bridge。如果不指定–network,创建的容器默认都会挂到docker0上。 brctl show可以看网卡
1 | docker run -itd httpd |
user-defined网络
用户也可以根据业务需要创建user-defined网络。
1 | docker network create --driver bridge my_net 1 |
不同的网卡 host 的 iptables 默认DROP 无法连通。如果需要连通,则需添加目标网络网卡
1 | docker network connect my_net2 httpd容器ID |
容器间通信
容器之间可通过IP,
Docker DNS Server
或 joined
容器三种方式通信。两个容器要能通信,必须要有属于同一个网络的网卡。
-
IP
具体做法是在容器创建时通过
--network
指定相应的网络,或者通过docker network connect
将现有容器加入到指定网络。 -
Docker DNS Server
docker daemon
实现了一个内嵌的DNS server
,使容器可以直接通过"容器名"通信。方法很简单,只要在启动时用--name
为容器命名就可以了。 -
joined容器
joined容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined容器之间可以通过127.0.0.1直接通信。
1
2
3docker run -d -it --name=web1 httpd
docker run -it --network=container:web1 busybox
#两个容器使用相同的网卡
将容器与外部世界连接
通过NAT, docker实现了容器对外网的访问。
(1)busybox发送ping包:172.17.0.2 > www.bing.com。
(2)docker0收到包,发现是发送到外网的,交给NAT处理。
(3)NAT将源地址换成enp0s3的IP:10.0.2.15 > www.bing.com。
(4)ping包从enp0s3发送出去,到达www.bing.com。
外部世界访问容器
docker可将容器对外提供服务的端口映射到host的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口。
1 | docker run -d -p [host port]:[container port] httpd |
每一个映射的端口,host都会启动一个docker-proxy进程来处理访问容器的流量。
(1)docker-proxy监听host的32773端口。
(2)当curl访问10.0.2.15:32773时,docker-proxy转发给容器172.17.0.2:80。
(3)httpd容器响应请求并返回结果。
跨多个host的网络
跨主机网络方案:docker原生的overlay和macvlan; 第三方方案:常用的包括flannel、weave和calico。这些方案与docker集成是通过libnetwork
、container network model
libnetwork & CNM
libnetwork是docker容器网络库,最核心的内容是其定义的Container Network Model(CNM)
- Sandbox(类似存储interface、路由表和DNS设置的表):Sandbox是容器的网络栈,包含容器的interface、路由表和DNS设置。LinuxNetwork Namespace是Sandbox的标准实现。Sandbox可以包含来自不同Network的Endpoint。
- Endpoint(类似网卡):Endpoint的作用是将Sandbox接入Network。Endpoint的典型实现是veth pair,一个Endpoint只能属于一个网络,也只能属于一个Sandbox。
- Network(类似交换机):Network包含一组Endpoint,同一Network的Endpoint可以直接通信。Network的实现可以是Linux Bridge、VLAN等。
libnetwork CNM定义了docker容器的网络模型,按照该模型开发出的driver就能与docker daemon协同工作,实现容器网络。
overlay
容器跨主机通信,Docker提供了overlay driver,使用户可以创建基于VxLAN的overlay网络。VxLAN可将二层数据封装到UDP进行传输,VxLAN提供与VLAN相同的以太网二层服务,但是拥有更强的扩展性和灵活性。(就是新建了桥接网卡,容器可添加变为双网卡)
1 | #创建 docker network create -d 指定网络driver |
macvlan
macvlan本身是linux kernel模块,其功能是允许同一个物理网卡配置多个MAC地址,即多个interface,每个interface可以配置自己的IP。macvlan本质上是一种网卡虚拟化技术
1 | docker network create -d macvlan --subnet=172.16.86.0/24 \ |
- docker没有为macvlan提供DNS服务,这点与overlay网络是不同的。无法ping bbox1,只能通过IP。
- macvlan网络是local网络,为了保证跨主机能够通信,用户一般需要自己管理IP subnet。
- docker不会为macvlan创建网关,这里的网关应该是真实存在的,否则容器无法路由。
1 | docker run -itd --name bbox1 --ip=172.16.86.10 --network nac_net1 busybox |
macvlan不依赖Linux bridge, brctl show可以确认并没有创建新的bridge
用sub-interface实现多macvlan网络
macvlan会独占主机的网卡,也就是说一个网卡只能创建一个macvlan网络。macvlan不仅可以连接到interface(如enp0s9),也可以连接到sub-interface(如VLAN enp0s9.xxx)。
不同macvlan网络之间不能通信。但准确的说法应该是:不同macvlan网络不能在二层上通信。在三层上可以通过网关将macvlan连通。可以借助中间网关连通。可以是物理路由,可以是某台主机配置的sub-interface网关IP。开启ip forward,以及iptables来充当虚拟路由器。
如将Host 192.168.56.101配置成一个虚拟路由器,设置网关并转发VLAN10和VLAN20的流量。
···
1 | //ip forward开启 |
第三方需要特别搭建,文章篇幅不拓展了
flannel
flannel是CoreOS开发的容器网络解决方案。flannel为每个host分配一个subnet,容器从此subnet中分配IP,这些IP可以在host间路由,容器间无须NAT和portmapping就可以跨主机通信。同时没有隔离能力。
每个subnet都是从一个更大的IP池中划分的,flannel会在每个主机上运行一个叫flanneld的agent,其职责就是从池子中分配subnet。为了在各个主机间共享信息,flannel用etcd(与consul类似的key-value分布式数据库)存放网络配置、已分配的subnet、host的IP等信息。
weave
weave是Weaveworks开发的容器网络解决方案。weave创建的虚拟网络可以将部署在多个主机上的容器连接起来。对容器来说,weave就像一个巨大的以太网交换机,所有容器都被接入这个交换机,容器可以直接通信,无须NAT和端口映射。除此之外,weave的DNS模块使容器可以通过hostname访问。weave不依赖分布式数据库(例如etcd和consul)交换网络信息,每个主机上只需运行weave组件就能建立起跨主机容器网络。
默认配置下,weave使用一个大subnet(例如10.32.0.0/12),所有主机的容器都从这个地址空间中分配IP,因为同属一个subnet,容器可以直接通信。如果要实现网络隔离,可以通过环境变量WEAVE_CIDR为容器分配不同subnet的IP。
weave是一个私有的VxLAN网络,默认与外部网络隔离。连通需要(1)首先将主机加入到weave网络。(2)然后把主机当作访问weave网络的网关。
calico
Calico是一个纯三层的虚拟网络方案,Calico为每个容器分配一个IP,每个host都是router,把不同host的容器连接起来。与VxLAN不同的是,Calico不对数据包做额外封装,不需要NAT和端口映射,扩展性和性能都很好。Calico依赖etcd在不同主机间共享和交换信息,存储Calico网络状态。host192.168.56.101负责运行etcd。Calico网络中的每个主机都需要运行Calico组件,实现容器interface管理、动态路由、动态ACL、报告状态等。
不同的calico网络,默认不能通行。calico默认的policy规则是:容器只能与同一个calico网络中的容器通信。
Docker存储
Docker为容器提供了两种存放数据的资源:由storage driver
管理的镜像层和容器层和Data Volume
Storage driver
storage driver实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。Docker支持多种storage driver,有AUFS、Device Mapper、Btrfs、OverlayFS、VFS和ZFS。
优先使用Linux发行版默认的storage driver。
Ubuntu默认driver用的是AUFS,底层文件系统是extfs
Redhat/CentOS的默认driver是Device Mapper, SUSE则是Btrfs
容器没有需要持久化的数据,随时可以从镜像直接创建。使用storage driver即可。
Data Volume
Data Volume本质上是Docker Host文件系统中的目录或文件,能够直接被mount到容器的文件系统中。
(1)Data Volume是目录或文件,而非没有格式化的磁盘(块设备)。
(2)容器可以读写volume中的数据。
(3)volume数据可以被永久地保存,使用它的容器销毁不影响它。
在具体的使用上,docker提供了两种类型的volume:bind mount
和docker managed volume
bind mount
bind mount是将host上已存在的目录或文件mount到容器。即使容器没有了,bind mount也还在。bind mount时还可以指定数据的读写权限,默认是可读可写,可指定为只读
1 | -v的格式为 <host path>:<container path> |
docker managed volume
docker managed volume与bind mount在使用上的最大区别是不需要指定mount源,随机在host生成。docker inspect 容器长ID
查看存储源位置。
1 | docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd |
数据共享
容器与host共享数据
对于bind mount是非常明确的:直接将要共享的目录mount到容器。
docker managed volume就要麻烦点。由于volume位于host中的目录,是在容器启动时才生成,所以需要将共享数据复制到volume中。
1 | docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd |
容器之间共享数据
将共享数据放在bind mount
中,然后将其mount到多个容器。
1 | docker run --name web1 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd |
另一种在容器之间共享数据的方式是使用volume container
。volume container是专门为其他容器提供volume的容器。它提供的卷可以是bind mount,也可以是docker managed volume。
1 | docker create vc_data \ |
容器命名为vc_data,docker create命令,这是因为volume container的作用只是提供数据,它本身不需要处于运行状态。
-
bind mount,存放Web Server的静态文件。
-
docker managed volume,存放一些实用工具
-
其他容器可以通过
--volumes-from
使用vc_data这个volume container
1 | docker run --name web1 -d -p 80 --volumes-from vc_data httpd |
data-packed volume container
将数据完全放到volume container中,同时又能与其他容器共享。原理是将数据打包到镜像中,然后通过docker managed volume共享。
1 | FROM busybox:latest |
因为在Dockerfile中已经使用了VOLUME指令,这里就不需要指定volume的mount point了,启动httpd容器并使用data-packed volume container。和volume container其实一样。
1 | docker run --name web2 -d -p 80:80 --volumes-from vc_data httpd |
容器监控
监控子命令:ps、top和stats,然后是几个功能更强的开源监控工具sysdig、Weave Scope、cAdvisor和Prometheus
1 | //监控子命令 |
Prometheus提供了监控数据搜集、存储、处理、可视化和告警一套完整的解决方案。
- Prometheus Server:负责从Exporter拉取和存储监控数据,并提供一套灵活的查询语言(PromQL)供用户使用。
- Exporter:负责收集目标对象(host、container等)的性能数据,并通过HTTP接口供Prometheus Server获取。
- 可视化组件:Grafana能够与Prometheus无缝集成,提供完美的数据展示能力。
- Alertmanager:用户可以定义基于监控数据的告警规则,规则会触发告警。一旦Alertmanager收到告警,会通过预定义的方式发出告警通知。支持的方式包括Email、PagerDuty、Webhook等。
日志管理
Docker logs
1 | docker attach 容器ID //只能记录attach执行后的日志 按Ctrl+p键,然后再按Ctrl+q键才能退出 |
ELK
ELK是三个软件的合称:Elasticsearch、Logstash、Kibana。 Logstash->Elasticsearch->Kibana
- Elasticsearch:一个近乎实时查询的全文搜索引擎。Elasticsearch的设计目标就是要能够处理和搜索巨量的日志数据。
- Logstash:读取原始日志,并对其进行分析和过滤,然后将其转发给其他组件(比如Elasticsearch)进行索引或存储。Logstash支持丰富的Input和Output类型,能够处理各种应用的日志。
- Kibana:一个基于JavaScript的Web图形界面程序,专门用于可视化Elasticsearch的数据。Kibana能够查询Elasticsearch并通过丰富的图表展示结果。用户可以创建Dashboard来监控系统的日志。
1 | docker run -p 5601:5601-p 9200:9200-p 5044:5044-it --name elk sebp/elk |