早期的容器基于Linux的LXC工作. 可以提供轻量级的虚拟化, 以便隔离进程和资源, 而不需要提供指令解释机制以及全虚拟化的其他复杂性. 容器有效地将由单个操作系统管理的资源划分到孤立的组中, 以便更好地在孤立的组之间平衡有冲突的资源使用需求
Linux Container提供了在单一可控主机节点上支持多个相互隔离的Server Container同时执行的机制. Linux Container有点像chroot, 提供了一个拥有自己进程和网络空间的虚拟环境, 但又有区别去虚拟机, 因为lxc是一种操作系统层次上的资源的虚拟化
如果容器已经普遍基于Docker管理, 而Docker是一个可以将应用程序及其依赖打包到几乎可以在任何服务器上运行的容器的工具
Docker是基于Linux内核实现的, Docker最早采用了LXC技术, LXC是Linux原生支持的容器技术, 可以提供轻量级的虚拟化. Docker基于LXC发展, 提供了LXC的高级封装, 标准的配置方法, 在LXC的基础上, Docker提供了一系列更强大的功能. 而虚拟化技术, 比如KVM, 是基于模块实现, 后来Docker改为自己研发并开源的runc技术运行容器
Docker相比虚拟机的交付速度更快, 资源消耗更低, Docker采用客户端/服务器端架构, 使用远程API来管理和创建容器, 其可以轻松的创建一个轻量级的, 可移植的, 自给自足的容器. Docker的三大理念就是build, ship, run. Docker通过namespace和cgroup来提供容器的资源隔离与安全保障等, 所以Docker容器在运行时, 不需要类似虚拟机的额外资源开销, 因此可以大幅度提供资源利用率
传统虚拟机是虚拟出一个主机硬件, 并且运行一个完整的操作系统, 然后在这个系统上安装和运行软件
容器内的应用直接运行在宿主机的内核之上, 容器并没有自己的内核, 也不需要虚拟硬件, 相当轻量化
每个容器间是相互隔离, 每个容器内都要一个属于自己的独立文件系统, 独立的进程空间, 网络空间, 用户空间等, 所以在同一个宿主机上的多个容器之间彼此不会相互影响
资源利用率更高: 开销更小, 不需要启动单独的虚拟机OS内核占用硬件资源, 可以将服务器性能压榨至极致. 虚拟机一般会有5-20%的损耗, 容器运行基本无损耗, 所以生产中一台物理机只能运行数十个虚拟机, 但是一般可以运行数百个容器
启动速度更快: 可以在数秒内完成启动
占用空间更小: 容器一般占用的磁盘空间以MB为单位, 而虚拟机即使是最小化安装也要占1个G多的空间
集成性更好: 和CI/CD相关技术结合性更好, 实现打包镜像发布测试可以一键运行, 做到自动化并快速的部署管理, 实现高效的开发生命周期
Docker主机(Host): 一个物理机或虚拟机, 用于运行Docker服务进程和容器, 也成为宿主机, node节点
Docker服务器端(Server): Docker守护进程, 运行Docker容器
Docker客户端(Client): 客户端使用docker命令或其他工具调用docker API
Docker仓库(Registry): 保存镜像的仓库, 官方仓库:hub.docker.com, 可以搭建私有仓库harbor
Docker镜像(Images): 镜像可以理解为创建实例使用的模本, 相当于RPM或者DEB包
Docker容器(Container): 容器是从镜像生成对外提供服务的一个或一组服务, 相当于RPM包中的程序运行起来
一个宿主机运行了N个容器, 多个容器共用一个OS, 必然带来的以下问题:
怎么样保证每个容器都有不同的文件系统并且互不影响
一个docker主进程内的各个容器都是其子进程, 如何实现同一个主进程下不同类型的子进程? 各个容器子进程间能相互通信吗?
每个容器怎么解决ip及端口分配的问题?
多个容器的主机名能一样么?
每个容器都要不要root用户? 怎么解决账户重名问题?
namespace是Linux系统底层概念, 在内核层实现, 既有一些不同类型的命令空间被部署在内核内, 各个docker容器运行在同一个docker主进程并且共用同一宿主机系统内核, 各个docker容器运行在宿主机的用户空间, 每个容器都要有类似于虚拟机一样的相互隔离的运行空间, 但是容器技术是在一个进程内实现运行指定服务的运行环境, 并且还可以保护宿主机内核不受其他进程的干扰和影响, 如文件系统空间, 网络空间, 进程空间等, 目前主要通过以下技术实现容器运行空间的相互隔离:
Cgroups是Linux内核的一个功能. 一个容器, 如果不对其做任何资源限制, 则宿主机会允许其占用无限大的内存空间, 有时候会因为代码bug程序会一直申请内存, 直到把宿主机内存占完, 为了避免此类问题的出现, 宿主机必要对容器进行资源限制, 比如cpu和内存等
Cgroups最主要的作用, 就是限制一个进程能够使用的资源上线, 包括cpu, 内存, 磁盘, 网络带宽等等. 此外, 还能够对进程进行优先级设置, 资源的计量以及资源的控制(比如: 将进程挂起和恢复等操作)
Cgroups在内核层默认开启, 较新内核的版本支持的功能更多
root@Ubuntu-1804-1:~# grep -i cgroup /boot/config-4.15.0-76-generic
CONFIG_CGROUPS=y
CONFIG_BLK_CGROUP=y
# CONFIG_DEBUG_BLK_CGROUP is not set
CONFIG_CGROUP_WRITEBACK=y
CONFIG_CGROUP_SCHED=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_RDMA=y
CONFIG_CGROUP_FREEZER=y
...
blkio: 块设备限制
cpu: 使用调度程序为Cgroup任务提供cpu的访问
cpuacct: 产生Cgroup任务的cpu资源报告
cpuset: 如果是多核心的cpu, 这个子系统会为Cgroup任务分配单独的cpu和内存
devices: 允许或拒绝Cgroup任务对设备的访问
freezer: 暂停和恢复Cgroup任务
memory: 设置每个Cgroup的内存限制以及产生内存资源报告
net_cls: 标记每个网络包以供Cgroup方便使用
ns: 命名空间子系统
perf_event: 增加了对每个group的检测跟踪能力, 可以监测属于某个特定的group的所有线程以及运行在特定cpu上的线程
root@Ubuntu-1804-1:~# ll /sys/fs/cgroup/
total 0
drwxr-xr-x 15 root root 380 Jan 20 00:57 ./
drwxr-xr-x 10 root root 0 Jan 20 00:57 ../
dr-xr-xr-x 4 root root 0 Jan 20 00:57 blkio/
lrwxrwxrwx 1 root root 11 Jan 20 00:57 cpu -> cpu,cpuacct/
lrwxrwxrwx 1 root root 11 Jan 20 00:57 cpuacct -> cpu,cpuacct/
dr-xr-xr-x 4 root root 0 Jan 20 00:57 cpu,cpuacct/
dr-xr-xr-x 2 root root 0 Jan 20 00:57 cpuset/
dr-xr-xr-x 4 root root 0 Jan 20 00:57 devices/
...
有了以上的chroot, namespace和Cgroups就具备了基础的容器运行环境, 但是还需要有相应的容器创建与删除的管理工具, 以及怎么样把容器运行起来, 容器数据怎么处理, 怎么进行启动与关闭等问题需要解决, 于是容器管理技术出现了, 目前主要使用Docker
Docker启动一个容器也需要一个外部模板, 也称为镜像, Docker的镜像可以保存在一个公共的地方共享使用, 只要把镜像下载下来就可以使用
最主要的是可以在镜像基础之上做自定义配置并且可以再把其提交为一个镜像, 一个镜像可以被启动为多个容器
Docker的镜像是分层的, 镜像底层为库文件且只读层即不能写入也不能删除数据, 从镜像加载启动为一个容器后会生成一个可写层, 其写入的数据会复制到宿主机上对应容器的目录, 但是容器内的数据在删除容器后也会被随着删除
快速部署: 短时间内可以部署成百上千个应用, 更快速交付到线上
高效虚拟化: 不需要为hypervisor支持, 基于Linux内核实现应用虚拟机, 相比虚拟机大幅提高性能和效率
节省开支: 提供服务器利用率, 降低IT支持
简化配置: 将运行环境打包保存至容器, 使用时直接启动即可
环境统一: 将开发, 测试, 生成的应用运行环境进行标准化和统一, 减少环境不一样带来的各种问题
快速迁移和扩展: 可实现跨平台运行在物理机, 虚拟机, 公有云等环境, 良好的兼容性可以方便将应用从A宿主机迁移到B宿主机, 甚至是A平台迁移到B平台
更好的实现面向服务的架构, 一个容器只运行一个应用, 实现分布的应用模型, 可以方便的进行横向扩展, 符合开发中高内聚, 低耦合的要求, 减少不同服务之间的相互影响
隔离性: 多个容器共用宿主机的内核, 各应用之间的隔离不如虚拟机彻底
目前OCI一共发布了两个规范, 分别是runtime spec和image format spec, 有了这两个规范, 不同的容器公司开发的容器只要兼容这两个规范, 就可以保证容器的可移植性和相互可操作性
runtime是容器真正运行容器的地方, 因此为了运行不同的容器runtime需要和操作系统内核紧密合作相互在支持, 以便为容器提供相应的运行环境
目前主流的三种runtime:
LXC: Linux早期的runtime, Docker早期就是采用LXC作为runtime
runc: 目前Docker默认的runtime, runc遵守OCI规范, 因此可以兼容LXC
rkt: 是CoreOS开发的容器tuntime, 也符合OCI规范, 所以使用rktruntime也可以运行Docker容器
管理工具连接runtime与用户, 对用户提供图形或命令方式操作, 然后管理工具将用户操作传递给runtime执行
LXC是LXD的管理工具
runc的管理工具就是docker engine, docker engine包含后台daemon和cli两部分, 经常提到的Docker就是指的docker engine
rkt的管理工具是rkt cli
容器定义工具运行用户定义容器的属性和内容, 以便容器能够被保存, 共享和重建
Docker Image: 是docker容器的模板, runtime依据docker image创建容器
Dockerfile: 包含N个命令的文本文件, 通过Dockerfile创建出docker image
ACI(App Container Image): 与docker image类似, 是CoreOS开发的rkt容器的镜像格式
统一保存镜像而且是多个不同的镜像版本的地方, 叫做镜像仓库
Docker hub: 官方仓库
阿里云, 网易云等第三方镜像仓库
Image Registry: Docker官方提供的私有仓库部署工具, 无web管理界面, 目前使用较少
Harbor: VMware提供的自带web界面自带认证功能的镜像仓库
当多个容器在多个主机运行的时候, 单独管理容器是相当复杂而且很容易出错, 而且也无法实现某一台主机宕机后容器自动迁移到其他主机从而实现高可用的目的,也无法实现动态伸缩的功能, 因此需要有一种工具可以实现统一管理, 动态伸缩, 故障自愈, 批量执行等功能, 这就是容器的编排引擎
容器编排通常包括容器管理, 调度, 集群定义和服务发现等功能
Docker Swarm: Docker开发的容器编排引擎
K8S: Google开发的容器编排引擎, 同时支持Docker和CoreOS
Docker自带的网络docker network仅支持管理单机上的容器网络, 当多主机运行的时候需要使用第三方开源网络, 例如, calico, flannel等
容器的动态扩容特性决定了容器ip也会随之变化, 因此需要一种机制开源自动识别并将用户请求动态转发到新创建的容器上, K8S自带服务发现功能, 需要结合kube-dns服务解析内部域名
可以通过原生命令docker ps/top/stat查看容器运行状态, 另外也可以使用Prometheus, heapster等第三方监控工具监控容器的运行状态
容器的动态迁移会导致其在不同的Host之间迁移, 因此如何保证与容器相关的数据也能随之迁移或随时访问, 可以使用逻辑卷/存储挂载等方式解决
Docker原生的日志查看工具docker logs, 但是容器内部的日只需要通过ELK等专门的日志收集分析和展示工具进行处理