前段时间公司培训,运维大佬提供的材料,有列举出docker的基本架构,在学习docker和Kubernetes的时候,其实先学习内部架构和对象的话,更有助于提升对整体应用的理解。
概述
Docker 是一个应用程序开发、部署、运行的平台,使用 go 语言开发。相较于传统的主机虚拟化,Docker提供了轻量级的应用隔离方案,并且为我们提供了应用程序快速扩容、缩容的能力。
Docker依赖于LXC(Linux Containers)技术,充分利用了其中的Namespace这个内核级特性,实现了容器之间的资源隔离。本质上来看,每一个Docker容器就是宿主机进程,不同 Docker 容器就对应不同的宿主机进程,这样,不同容器(即不同进程)就可以采用 Namespace 资源隔离,使得每一个容器看起来都像是一个独立的小虚拟机
docker其它核心技术:
- cgroup:资源控制
- rootfs:文件系统隔离
- aufs:高级分层文件系统(Advanced Multi-layered unification filesytem)
架构
静态部件描述
Docker采用的是CS架构,包含了三个主要部分:dockerd守护进程、REST API接口层、cli接口层(管理容器、镜像、网络、存储等等),docker client 通过Unix套接字或者网络接口访问 docker daemon,从而完成容器、镜像等内容的管理
流程描述
对象
镜像
镜像是一个用来构建容器的只读模版,通常一个镜像会依赖其他的镜像。例如我们编写的一个Node程序需要依赖Node环境,那在构建这个应用镜像时就需要依赖基础的Node镜像。
我们可以创建自己的镜像,也可以使用仓库中已经创建好的镜像。创建镜像需要创建一个Dockerfile文件。每个Dockerfile定义镜像文件中的一层,当定义发生变化的时候,只需要更新这一层的文件即可。
容器
容器是一个运行时状态下的镜像,通过docker命令我们可以创建、启动、停止、删除容器
启动我们的第一个容器:
1 docker run --rm hello-world
- 如果本地没有hello-world镜像,那么首先拉取镜像
- 自动创建一个容器,相当于命令dock container create
- Docker分配一块文件系统给容器
- Docker创建网络接口、分配网络地址
- 启动容器,并且执行目标入口命令
- 容器关闭并退出
- 容器销毁
网络
Docker的网络子系统是可插拔的,支持bridge、host、overlay、macvlan、none等网络模式
bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将主机上的Docker容器连接到docker0虚拟网桥上,docker0网桥由docker后台服务启动时创建,默认在172.17.0.0/16段每一个容器运行时会生成一对veth网络设备直连,再通过网桥使得同一个主机上的容器间可以相互通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15+-------------------------------------------------------------------------------+
| |
| +---------------------------------------------------------------+ |
| | docker0 | |
| +---------------------------------------------------------------+ |
| ↑ ↑ |
|.............................|...............|.................................|
| ↓ ↓ |
| +----------+ +-----------+ +-----------+ +-----------+ |
| | veth2 |<-->| veth1 | | veth3 |<-->| veth4 | |
| +----------+ +-----------+ +-----------+ +-----------+ |
| ↑ ↑ |
| +-------------------------------------------------+ |
| 172.16.17.2 172.16.17.3 |
+-------------------------------------------------------------------------------+host模式下,容器会共享主机的network namespace,所以拥有主机的全部网络通信能力,通常用于docker容器测试
存储
默认情况下,容器中的应用生成的所有文件都存放在一个可写的容器层,意味着这些数据的生命周期和容器保持一致,这些文件与容器高度关联,因此会带来下面几个问题:
- 不能在宿主机上很方便地访问容器中的文件
- 无法在多个容器之间共享数据
- 当容器删除时,容器中产生的数据将丢失
为此,Docker提供了两种方案解决数据问题:
- bind mount volumes:将host上已存在的目录或文件挂载到容器中使用
- docker managed volumes:docker自己管理的数据卷存储
对比一下两种方式各自的特点:
- | bind mount Volumes | docker managed volumes |
---|---|---|
volume 位置 | 可任意指定 | /var/lib/docker/volumes/… |
对已有mount point 影响 | 隐藏并替换为 volume | 原有数据复制到 volume< |
是否支持单个文件 | 支持 | 不支持,只能是目录 |
权限控制 | 可设置为只读,默认为读写权限 | 无控制,均为读写权限 |
移植性 | 移植性弱,与 host path 绑定 | 移植性强,无需指定 host 目录 |
不管使用哪种方式,容器内看起来都是一样的,或者作为一个文件夹存在、或者作为一个文件存在。
上图说明了不同方式的区别,Volumes 是存在本地文件系统中的一部分,其他应用程序不能对这个文件系统进行修改,Linux下在/var/lib/docker/volumes。这是数据持久化的最好方案。Bind Mount 允许将主机中任何位置的数据挂载,这些数据的读写没有收到保护。tmps是存储在主机内存中的数据。
常用操作
cli
下载镜像
1 | docker pull urlpath:[tag] |
查询镜像
1 | docker images |
运行镜像(启动容器)
1 | docker run [-d] \ |
- -d可以指定当前容器在后台运行,释放当前终端控制台不阻塞,用户可以继续往下输入命令
- –rm可以指定容器停止即刻销毁删除
- -p可以指定主机和容器的端口映射,可以重复指定多个映射
- -v可以指定主机的存储目录挂载至容器中,可以重复指定多个挂载,默认读写,ro选项可以指定只读保护数据
- hostpath为绝对路径:开户bind mount模式
- hostpath为一个变量或连同“:”一起省略:开户docker managed模式
- 使用hostpath变量时只能为:[a-zA-Z0-9][a-zA-Z0-9_.-]
- -e可以预置一些环境变量供容器使用,可以重复指定多个环境变量
构建镜像(打包)
1 | docker build -t {image_name}:[tag] [Dockerfile目录] |
查询容器
1 | docker ps [-a] |
进入容器
1 | docker exec -ti {container_name|container_id} sh/bash ... |
查看容器日志
1 | docker logs {container_name|container_id} [--tail n] [-f] |
查看网络
1 | docker network ls |
查看存储
1 | docker volume ls |
注:docker volume只能查看docker managed volumes,对于bind mount volumes只能通过docker inspect来查看
Restful api
操作 | API |
---|---|
查询镜像 | curl -X GET http://{IP}:{PORT}/images/json |
查询容器 | curl -X GET http://{IP}:{PORT}/containers/json |
启动容器 | curl -X POST http://{IP}:{PORT}/images/create?fromImage=hello-world:lates |