云计算概述

发展历程:

  • IT需求较小,单机阶段
  • IT需求增多,资源集中管理
  • 对稳定性安全性提出更高的要求
  • 业务操作系统迁往虚拟机
  • 分布式计算需求
  • 业务应用隔离与虚拟化

云计算的三种服务模式:

  • SaaS(Software as a Service)软件服务
  • PaaS(Platform as a Service)平台服务
  • IaaS (Infrastructure as a Service)基础设施服务

Docker介绍与安装 - 图1

传统企业应用构建

应用被直接部署在操作系统上,操作系统直接安装于硬件上

应用被操作系统绑定

操作系统被硬件绑定

缺点:

  • 部署非常慢
  • 成本非常高
  • 资源浪费
  • 难于迁移和拓展
  • 被限定硬件厂商

虚拟化应用部署

应用被直接部署在操作系统上,操作系统安装于虚拟硬件之上,虚拟硬件依赖于硬件

应用被操作系统绑定

操作系统被封装于文件中,可以在硬件设备之间进行传播

优点:

  • 隔离分区
  • 文件封装
  • 资源池
  • 易拓展
  • 易上云

缺点:

  • 资源浪费
  • 启动速度慢
  • 迁移过程慢
  • 不能适应SOA架构

容器部署应用

在容器引擎中运行容器,在独立的容器中运行应用

应用与容器捆绑

容器只依赖于容器引擎

容器不与操作系统和硬件绑定

Docker与虚拟机比较

Docker介绍与安装 - 图2

特性 容器 虚拟机
启动速度 秒级 分钟级
性能 接近原生 较弱
内存代价 很小 较多
硬盘使用 一般为MB 一般为GB
运行密度 单机支持上千个容器 一般几十个
隔离性 安全隔离 完全隔离
迁移性 优秀 一般

总而言之,Docker在宿主机器的操作系统上创建Docker引擎,直接在宿主主机的操作系统上调用硬件资源,因此操作速度很快

优点:

  • 对软件和其依赖的标准化打包
  • 应用之间相互隔离
  • 共享同一个OS kernel
  • 可以运行在很多主流操作系统上
  • 秒级的启动和重建

Docker介绍

Docker是一个在2013年开源的应用容器引擎,并且是一个基于Go语言编写的PaaS服务

Docker最早采用LXC技术,之后改为自己研发并开源的runc技术运行容器

Docker相比虚拟机的交付速度更快,资源消耗更低,Docker采用客户端、服务端架构,使用远程API来管理和创建Docker容器

Docker的三大理念是:Build(构建)、Ship(运输)、Run(运行)

Docker遵从apache2.0协议,并通过namespace、cgroup等技术来提供容器的资源隔离与安全保障

一个完整的Docker有以下几个部分组成:

  1. DockerClient 客户端
  2. Docker Daemon 守护进程
  3. Docker Image 镜像
  4. DockerContainer 容器

Docker的Logo如下图所示,很好地体现了它的理念,服务是上面的那些集装箱,可以到处运输

Docker介绍与安装 - 图3

Docker与虚拟机对比

Docker介绍与安装 - 图4

特性 容器 虚拟机
创建速度 秒级(<10s)相当于建立索引 分钟级(>2分钟)
启动速度 秒级(<1s) 启动慢(>30s) 读文件逐个加载
性能 计算/存储无损耗,接近原生 较弱
内存代价 很小 较多
硬盘使用 Docker容器镜像200~300M,且公共基础镜象实例化时可以共享 虚拟机镜像庞大(十几G~几十G),且实例化时不能共享
运行密度 单机支持上千个容器,适合大规模部署 一般几十个
隔离性 共享内核和OS,隔离性弱,但是安全隔离 有独立的GUEST OS,隔离性强
迁移性 优秀,一次打包,随处迁移 一般
标准 Docker提供了容器应用镜象事实标准,OCI推动进一 步标准化 虚拟机镜象缺乏统一标准

总而言之,Docker在宿主机器的操作系统上创建Docker引擎,直接在宿主主机的操作系统上调用硬件资源,因此操作速度很快

Docker容器的优点:

  • 对软件和其依赖的标准化打包
  • 应用之间相互隔离
  • 共享同一个OS kernel
  • 可以运行在很多主流操作系统上
  • 秒级的启动和重建

Docker的组成

  • Docker Host:一个物理机或者虚拟机,用于运行docker服务进程和容器
  • Docker Server:docker的守护进程,运行docker容器
  • Docker Client:客户端使用docker命令或其他工具调用docker API
  • Docker Repository:保存镜像的仓库,类似于git或svn这样的版本控制器,可以从仓库中拉取镜像

  • Docker Images:镜像可以理解为创建实例使用的模板

    • 镜像是一个只读的模板
  • Docker Container:容器是镜像生成的对外提供服务的一个服务

    • 一个镜像可以创建多个容器

容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的

容器与镜像的关系类似于面向对象编程中的对象与类:

Docker 面向对象
容器 对象
镜像

Docker介绍与安装 - 图5

Docker的安装

  • 安装docker-ce以及客户端
  1. yum install wegt -y
  2. rm -rf /etc/yum.repos.d/*
  3. wget -O /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
  4. wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo
  5. wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  6. yum install docker-ce -y
  • 启动docker
  1. systemctl enable docker.service
  2. systemctl start docker.service
  • 快速开始,部署Nginx容器
  1. [root@server ~]# docker pull nginx
  2. [root@server ~]# docker pull centos:7
  3. [root@server ~]# docker images
  4. REPOSITORY TAG IMAGE ID CREATED SIZE
  5. nginx latest c919045c4c2b 11 days ago 142MB
  6. centos latest 5d0da3dc9764 5 months ago 231MB
  7. [root@server ~]# docker run -d -p 80:80 nginx
  8. 27458a10b920aac4ad074626687f4e813a65ba0998d23a164837e1028c06b545
  9. [root@server ~]# docker ps
  10. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  11. 27458a10b920 nginx "/docker-entrypoint.…" 32 seconds ago Up 30 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp condescending_turing
  12. [root@server ~]# docker exec -it 27458a10b920 bash
  13. root@27458a10b920:/# cd /usr/share/nginx/html/
  14. root@27458a10b920:/usr/share/nginx/html# ls
  15. 50x.html index.html
  16. root@27458a10b920:/usr/share/nginx/html# echo 'docker nginx test' > index.html
  17. root@27458a10b920:/usr/share/nginx/html#
  18. exit
  19. [root@server ~]# curl 192.168.31.99:80
  20. docker nginx test

Docker介绍与安装 - 图6

Linux namespace技术

容器的隔离性是很弱的,但是能做到安全隔离,那么这些隔离是怎么完成的呢,当一个宿主机运行了N个容器的时候,多个容器带来的以下的问题应该怎么解决呢?

  1. 怎么样保证每个容器都有不同的文件系统并且能互不影响
  2. 一个Docker主进程内的各个容器都是其子进程,那么如何实现同一个主进程下不同类型的子进程? 各个子进程间通信能相互访问吗?
  3. 每个容器怎么解决IP及其端口分配的问题呢?
  4. 多个容器的主机名能一样吗?
  5. 每个容器都要不要有root用户?怎么解决账户重名问题呢?

Namespace(命名空间)是 Linux 内核用来隔离内核资源的方式,通过namespace可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在

具体的实现方式是把一个或多个进程的相关资源指定在同一个namespace中

Linux 内核实现namespace的一个主要目的就是实现轻量级虚拟化(容器)服务,也就是说linux内核提供的namespace技术为docker等容器技术的出现和发展提供了基础条件

目前主要通过以下技术实现容器运行空间的相互隔离:

隔离类型 功能 系统调用参数
MNT Namespace 提供磁盘挂载点和文件 系统的隔离能力 CLONE_NEWNS
IPC Namespace 提供进程间通信的隔离 能力 CLONE_NEWIPC
UTS Namespace 提供主机名隔离能力 CLONE_NEWUTS
PID Namespace 提供进程隔离能力 CLONE_NEWPID
Net Namespace 提供网络隔离能力 CLONE_NEWPID
User Namespace 提供用户隔离能力 CLONE_NEWUSER

MNT Namespace

每个容器都要有独立的根文件系统有独立的用户空间,以实现容器里面启动服务并且使用容器的运行环境,每个容器其实都是被宿主机锁在了一个指定的目录中

启动三个容器然后进入某一个容器中,创建一个文件

  1. [root@server ~]# docker run -d --name nginx-1 nginx
  2. 503b173b5137238b2fff4cea006b622ccee293102992c326207ee172efc05546
  3. [root@server ~]# docker run -d --name nginx-2 nginx
  4. d65f43fcb58fcdab66608033a686af3a164094b060fea37d25ec16df634e867a
  5. [root@server ~]# docker run -d --name nginx-3 nginx
  6. 2d6eb9cc7d39241c90301706f6ff48177d503d69f453afb428495d3bd96b7f55
  7. [root@server ~]# docker exec -it nginx-1 bash
  8. root@503b173b5137:/# echo "hello world" > /root/test.txt
  9. root@503b173b5137:/#
  10. exit

可以在宿主机上找到这个容器的运行目录

  1. [root@server ~]# find / -name test.txt
  2. /var/lib/docker/overlay2/86ea9522895a04b1bd3bcda9c367efa2a7afda013c2b4202a256b0e97be33ba5/diff/root/test.txt
  3. /var/lib/docker/overlay2/86ea9522895a04b1bd3bcda9c367efa2a7afda013c2b4202a256b0e97be33ba5/merged/root/test.txt

IPC Namespace

一个容器内的进程间通信,允许一个容器内的不同进程数据互相访问,但是不能跨容器访问其他容器的数据

IPC Namespace 实现了进程间通信的隔离,包括常见的几种进程间通信机制,如信号量,消息队列和共享内存,要完成IPC,就需要申请一个全局唯一的标识符,即IPC标识符,所以IPC资源隔离主要完成的就是隔离IPC标识符

UTS Namespace

UTS Namespace提供了主机名和域名的隔离,这样每个容器就拥有独立的主机名和域名了,在网络上就可以被视为一个独立的节点,在容器中对hostname的命名不会对宿主机造成任何影响

PID Namespace

Linux系统中,有一个PID为1的进程(init/systemd)是其他所有进程的父进程,那么在每个容器内也要有一个父进程来管理其下属的进程,因此多个容器的进程就要通过PID Namespace进程隔离

  1. [root@aa9ffc193b51 /]# ps -ef
  2. UID PID PPID C STIME TTY TIME CMD
  3. root 1 0 0 13:00 pts/0 00:00:00 /bin/bash
  4. root 15 0 0 13:00 pts/1 00:00:00 bash
  5. root 138 0 0 13:02 ? 00:00:00 nginx: master process nginx
  6. nginx 139 138 0 13:02 ? 00:00:00 nginx: worker process
  7. nginx 140 138 0 13:02 ? 00:00:00 nginx: worker process
  8. nginx 141 138 0 13:02 ? 00:00:00 nginx: worker process
  9. nginx 142 138 0 13:02 ? 00:00:00 nginx: worker process
  10. root 143 15 0 13:02 pts/1 00:00:00 ps -ef
  • 那么在宿主机中的PID和容器内的PID有什么关系呢?

如下图所示,红框的部分为上面那个容器的部分,可以看到后面的进程分支都是一一对应的,2个bash,5个nginx,只是在宿主机中的进程号和容器中的进程号是不同的

Docker介绍与安装 - 图7

  • 使用这个命令也可以查看,当然,这些个Nginx进程肯定不是宿主机里的啦
  1. [root@server ~]# ps -aux | grep nginx
  2. root 10496 0.0 0.0 39308 1052 ? Ss 21:02 0:00 nginx: master process nginx
  3. polkitd 10497 0.0 0.0 39696 1828 ? S 21:02 0:00 nginx: worker process
  4. polkitd 10498 0.0 0.0 39696 1828 ? S 21:02 0:00 nginx: worker process
  5. polkitd 10499 0.0 0.0 39696 1828 ? S 21:02 0:00 nginx: worker process
  6. polkitd 10500 0.0 0.0 39696 1828 ? S 21:02 0:00 nginx: worker process
  7. root 11013 0.0 0.0 112724 988 pts/1 S+ 21:07 0:00 grep --color=auto nginx

Net Namespace

每一个容器都类似于虚拟机一样有自己的网卡、监听端口、TCP/IP协议栈等,Docker使Net Namespace启动一个vethX接口,这样容器将拥有它自己的桥接IP地址,通常是docker0,而docker0实质就是linux的虚拟网桥

  1. [root@server ~]# yum install bridge-utils.x86_64 -y
  2. [root@server ~]# brctl show
  3. bridge name bridge id STP enabled interfaces
  4. docker0 8000.0242ea1a62f6 no veth851760b
  5. [root@server ~]# docker run -d -it --name centos-1 centos:7

在Centos-1中的IP地址:

Docker介绍与安装 - 图8

宿主机中的网络信息:

Docker介绍与安装 - 图9

架构图

Docker介绍与安装 - 图10

User Namespace

各个容器内可能会出现重名的用户和用户组名称,或重复的用户UID或者GID,那么怎么隔离各个容器内 的用户空间呢?

User Namespace允许在各个宿主机的各个容器空间内创建相同的用户名以及相同的uid和gid,只是此用户的有效范围仅仅是当前的容器内,不能访问另外一个容器内的文件系统,即相互隔离、互不影响、永不相见

Linux control groups

在一个容器内部,如果不对其做任何资源限制,则宿主机会允许其占用无限大的内存空间,有时候会因为代码BUG程序会一直申请内存,直到把宿主机内存占完,为了避免此类的问题出现,宿主机有必要对容器进行资源分配限制,比如cpu、内存等,Linux Cgroups的全称是Linux control Groups

  • 验证系统内核层已经默认开启cgroup功能
  1. [root@server ~]# cat /boot/config-3.10.0-957.el7.x86_64 | grep cgroup -i
  2. CONFIG_CGROUPS=y
  3. # CONFIG_CGROUP_DEBUG is not set
  4. CONFIG_CGROUP_FREEZER=y
  5. CONFIG_CGROUP_PIDS=y
  6. CONFIG_CGROUP_DEVICE=y
  7. CONFIG_CGROUP_CPUACCT=y
  8. CONFIG_CGROUP_HUGETLB=y
  9. CONFIG_CGROUP_PERF=y
  10. CONFIG_CGROUP_SCHED=y
  11. CONFIG_BLK_CGROUP=y
  12. # CONFIG_DEBUG_BLK_CGROUP is not set
  13. CONFIG_NETFILTER_XT_MATCH_CGROUP=m
  14. CONFIG_NET_CLS_CGROUP=y
  15. CONFIG_NETPRIO_CGROUP=y
  • 关于内存的模块
  1. [root@server ~]# cat /boot/config-3.10.0-957.el7.x86_64 | grep mem -i | grep cg -i
  2. CONFIG_MEMCG=y
  3. CONFIG_MEMCG_SWAP=y
  4. CONFIG_MEMCG_SWAP_ENABLED=y
  5. CONFIG_MEMCG_KMEM=y
  1. CPU:使用调度程序为cgroup任务提供 CPU 的访问
  2. cpuacct:产生cgroup任务的 CPU 资源报告
  3. cpuset:如果是多核心的CPU,这个子系统会为cgroup任务分配单的CPU和内存
  4. devices:允许或拒绝cgroup任务对设备的访问
  5. freezer:暂停和恢复cgroup任务
  6. memory:设置每个cgroup 的内存限制以及产生内存资源报告
  7. net_cls:标记每个网络包以供 cgroup方便使用
  8. ns:命名空间子系统
  9. perf event:增加了对每个group的监测跟踪的能力,可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程

参考博客:Docker之Cgroup

容器规范

容器技术除了Docker之外,还有coreOS的rkt,还有阿里的Pouch,还有红帽的Podman,为了保证容器生态的标准性和健康可持续发展,包括Linux基金会、Docker、微软、红帽、谷歌和IBM等公司在2015年6月共同成立了一个叫open container(OCI)的组织,其目的就是制定开放的标准的容器规范,目前OCI一共发布了两个规范分别是runtime specimage format spec,不同的容器公司只需要兼容这两个规范,就可以保证容器的可移植性和相互可操作性

runtime是真正运行容器的地方,因此运行了不同的容器runtime需要和操作系统内核紧密合作相互在支持,以便为容器提供相应的运行环境,目前主流的三种runtime:

xc:linux上早期的runtime,Docker早期就是采用lxc作为runtime

runc:是目前docker默认的runtime,runc遵守oci规范,因此可以兼容lxc

rkt:是coreOS开发的容器runtime,也负荷oci规范

镜像加速配置

Docker介绍与安装 - 图11

  • 然后复制命令到宿主机中

Docker info

  1. [root@server ~]# docker info
  2. Client:
  3. Context: default
  4. Debug Mode: false
  5. Plugins:
  6. app: Docker App (Docker Inc., v0.9.1-beta3)
  7. buildx: Docker Buildx (Docker Inc., v0.8.0-docker)
  8. scan: Docker Scan (Docker Inc., v0.17.0)
  9. Server:
  10. Containers: 2 # 当前主机运行容器总数
  11. Running: 1 # 有几个容器是正在运行的
  12. Paused: 0 # 有几个容器是暂停的
  13. Stopped: 1 # 有几个容器是停止的
  14. Images: 2 # 当前服务器的镜像数
  15. Server Version: 20.10.13 # 服务端版本
  16. Storage Driver: overlay2 # 正在使用的存储引擎
  17. Backing Filesystem: xfs # 后端文件系统,即服务器的磁盘文件系统
  18. Supports d_type: true # 是否支持d_type
  19. Native Overlay Diff: true # 是否支持差异数据存储
  20. userxattr: false
  21. Logging Driver: json-file # 日志文件类型
  22. Cgroup Driver: cgroupfs # cgroups类型
  23. Cgroup Version: 1
  24. Plugins: # 插件
  25. Volume: local # 卷
  26. Network: bridge host ipvlan macvlan null overlay
  27. Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
  28. Swarm: inactive # 是否支持swarm
  29. Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
  30. Default Runtime: runc # 默认的runtime
  31. Init Binary: docker-init # 初始化容器的守护进程
  32. containerd version: 2a1d4dbdb2a1030dc5b01e96fb110a9d9f150ecc
  33. runc version: v1.0.3-0-gf46b6ba
  34. init version: de40ad0
  35. Security Options: # 安全选项
  36. seccomp
  37. Profile: default
  38. Kernel Version: 3.10.0-957.el7.x86_64 # 宿主机内核版本
  39. Operating System: CentOS Linux 7 (Core) # 宿主机操作系统
  40. OSType: linux # 宿主机操作系统类型
  41. Architecture: x86_64 # 宿主机架构
  42. CPUs: 4 # 宿主机cpu数量
  43. Total Memory: 1.777GiB # 宿主机总内存
  44. Name: server # 宿主机主机名
  45. ID: A3JD:AEXQ:HLA2:VPW5:GVSA:L4AC:CIMG:RNTU:ORYA:66HH:ZVTI:WIYH
  46. Docker Root Dir: /var/lib/docker # 宿主机数据保存目录
  47. Debug Mode: false
  48. Registry: https://index.docker.io/v1/ # 镜像仓库
  49. Labels:
  50. Experimental: false # 是否是测试版
  51. Insecure Registries:
  52. 127.0.0.0/8
  53. Registry Mirrors: # 镜像加速
  54. https://lgni0v8s.mirror.aliyuncs.com/
  55. Live Restore Enabled: false # 是否开启活动容器(重启不关闭容器)

docker 存储引擎

目前docker的默认存储引擎为overlay2,不同的存储引擎需要相应的系统支持,如需要磁盘分区的时候传递d-type文件分层功能,即需要传递内核参数开启格式化磁盘的时候指定功能

官网关于存储引擎的信息:

https://docs.docker.com/storage/storagedriver/select-storage-driver/

由于存储引擎选择错误引起的血案(扩展阅读):

CentOS系统故障 | 一桩”血案”引发的容器存储驱动比较 - 有容云 - 博客园 (cnblogs.com)