直接复制排版不好。。可以去有道云笔记看:http://note.youdao.com/noteshare?id=6cd1a4fd404e8f92fb8d610090e876e9&sub=237CA78BEE39435F8A548121BE41C32E
0x0 前言
什么是K8S?要明白这个问题,需要首先了解一些基础
0x1 并行
0x1.1 物理并行
放眼现在,大家使用电脑一边听歌一边玩游戏一边看直播哪都不是事。那么当年比如30年前,还在使用dos系统的时候(没仔细考究dos是否真的不支持异步多线程等。。如果说错一笑而过),系统同一个时间只能同事运行一个程序,这时候你想一遍听歌一边玩游戏怎么办呢?解决方案就是多加一台电脑。当然这个方案现在来看肯定不够好。那现在怎么解决的,为啥我们可以一次性运行多个程序?
0x1.2 进程
对于进程是啥网上都有解释,虽然我感觉那个解释对一个正常人都不是太好理解。用java的说法有点类似于,程序就是你的一个类,进程就是你的那个类的实例化对象。。然后有个问题两个进程是如何做到隔离的。原因是保护模式,由于保护模式每个进程都有独立的4G虚拟内存空间,程序都是用的虚拟内存而非直接使用物理内存。(这也能解释为啥一个程序吃内存有上限,浏览器吃内存是因为每个标签每个扩展都是一个进程),那么还有个最重要的问题,如何做到多个进程一起运行呢?现在只是解决了两个进程如果一起运行了,由于内存隔离不会冲突。答案就是他们其实并没一起运行。只是你看起来好像一起运行。原理就是,每个进程会有时间很短时间片,然后每次运行一个程序时间片走完就挂起,然后运行下一个进程,这样一直循环,只要时间片够短我们看起来就好像好多程序是一起在运行。但是由于时间片分法策略不是简单的进程平均,所以有时候一个程序卡死了,你会发现整个系统都卡了。总结一下,cpu使用时间片使得进场看起来并行,操作系统使用保护模式做到进程之间内存隔离。
0x2 虚拟化
0x2.1 为啥要虚拟化
这个原因网上也好多,不是很想多说,我简单说几点。第一大家用虚拟机不?为啥要用?理由不用我多说了。再比如就前文所说的进程无法做到资源按我所需分配。虚拟化可以。
0x2.2 什么是虚拟化
最简单一个例子来解释虚拟化就是。cpu不是有时间片么,全给虚拟机了,然后一共分到100个,然后你再虚拟机里运行程序,每个程序最多给25个时间片。那么就算你只运行了一个程序,其他75个时间片轮到了也啥都不干,是不是就相当于把cpu虚拟成了4个逻辑cpu,然后那个程序只用了一个。具体下文介绍,这里先介绍一些基本知识
0x2.2.1 操作系统分层
应用程序 | ||
API | ||
内核 | 驱动 | |
HAL | ||
硬件 |
0x2.2.1 ring0 ring3
还是前文所说的进程的例子,操作系统因为保护模式使得进程内部直接读写内存的代码不会冲突,那么如果代码里直接写读取物理内存的代码怎么办?答案是CPU不允许这么干。所有的应用程序都是运行在ring3,ring3的代码会受到CPU的检查,很多事情不能干,比如不能直接访问硬件,不能无限制访问内存,比如高2G内存(linux为高1G)这部分内存是内核内存所有进程共享
0x2.3 虚拟化分类
0x2.3.1 全虚拟化
为啥先讲这个呢?因为大家最熟悉的VMware 就是属于全虚拟化。全虚拟化的意思就是不需要对客户机系统做修改,所以吧对客户机来说不知道自己是个虚拟机(当然了还是有办法知道的)。
0x2.3.1.1 软件全虚拟化
应用程序 | ring3 | ||
Guest OS | |||
VMM | |||
Host OS | ring0 | ||
硬件 |
软件全虚拟化就是,通过VMM把Host OS所有能操作的CPU、内存等抽象成虚拟CPU、内存(CPU虚拟化简单逻辑前文有说)。然后有Guest OS来使用这些虚拟设备
但是这种方法不考虑别的,单纯可行性方面就需要解决两个问题。
1.特权指令
说人话就是,有一些指令只能在ring0运行,比如io设备的,读取特殊寄存器的。但是吧你Guest OS肯定得要用。所以解决方案就是,Guest OS要运行特权指令,往下走,到Host OS触发异常,被VMM捕获,通过VMM把Guest OS的特权指令用一些可以在ring3运行的的指令来模拟出一个只针对Guest OS的特权指令,这样就可以把Guest OS的特权指令的特权解除,这叫做特权解除
2.敏感指令
这个说人话就是,比如关机。Guest OS要关机,下发关机指令到Host OS,它怎么知道是Guest OS要关机还是Host OS要关机。所以吧这时候VMM就要在下发之前先判断,是不是敏感指令,是就和特权指令一样直接模拟了。这玩意叫陷入模拟
说句人话就是,这玩意吧就是VMM干了全部脏活累活。要解决的问题远不止特权指令那么简单,比如内存地址。前文说到两进程操作"同一地址"的内存,由于用的是虚拟内存最终会转换到两块不同的物理内存,那么Guest OS 的程序的虚拟内存 如何转换到Host OS的物理内存,解决方案比如有影子页表
0x2.3.1.2 硬件辅助全虚拟化
应用程序 | Non-Root Mode | ||
Guest OS | |||
VMM | Root Mode | ||
硬件 |
这玩意说人话就是,CPU干了之前软件干的事情,CPU自己可以分辨来自GuestOs的特权指令直接就搞定了,BIOS里要开的VT-X啥的就是,这就是为啥之前经常说BIOS开这玩意虚拟机会块很多,还有VT-D VT-C等,然后这里没Host OS是因为这时候VMM可以加载到Host OS内核里,就不需要向下走Host OS了可以直接对接硬件,比如KVM,比如上门提到的影子页表,Intel CPU支持了EPT之后就不需要使用影子页表提高了不少性能。
0x2.3.2 半虚拟化
知道了全虚拟化,半虚拟化也就好理解了,就是这玩意需要修改Guest OS的部分代码,以此跟好的和和VMM协作。最简单的例子就是vmtools,装了之后就能解锁好多功能。毕竟操作系统一开始设计并没考虑到虚拟化这个事情。
0x2.3.3 裸机架构
这玩意就是说,上面两种不管怎么说虚拟机都是一个软件,要依托于操作系统对接硬件啊啥的。简单来说就是上面的要先安装操作系统,它呢就是可以直接部署在机器上,比如esxi,不过么说是那么说,esxi也是基于Linux开发的,没自己考究我觉得他就是在Linux的base 版本里开发了一个软件,然后打包了。
0x3 容器
0x3.1 由来
为啥会出现这个玩意?简单举个例子,虚拟机有时候很蛋疼,比如我就想运行一个隔离的小服务,然后想隔离就得搞个虚拟机,就先不说资源浪费大不大了,不嫌累吗?主观上就感觉很蛋疼。就和某些玩意打印个hello world要装一堆依赖那样。反正我是觉得很蛋疼。第二点。就是资源浪费。根据前面可以知道。那玩意吧执行一个指令要下发到vmm然后走Host OS 内核,这都是损耗,虽然吧现在有VT-C可以直通网卡 VT-D可以直通IO,性能已经逼近宿主机了,但是吧。总归损耗在,而且很臃肿,开三个虚拟机就要运行四个操作系统,很多都是浪费实在没必要。于是乎,就搞出了容器这么个玩意。这玩意隔离的方式比较像进程
0x3.2 Union FS
由图可知容器虽公用OS但是不公用Bins,(Libs进程也每个进程各一份)。所以可知得一个问题要解决如何让每个容器各自独立一份bins。说人话就是你启动docker容器,不是要先pull镜像,然后启动。它里面得那些文件肯定每个容器都不一样。这个要靠一个东西叫 Union FS,解释我网上超了一段:
联合文件系统(Union File System):可以把多个目录(也叫分支)内容联合挂载到同一个目录下,
而目录的物理位置是分开的。UnionFS允许只读和可读写目录并存,就是说可同时删除和增加内容。
这些分支或者是read-only或者是read-write的,所以当对这个虚拟后的联合文件系统进行写操作时候,系统是真正写到了一个新的文件中。看起来这个虚拟后的联合文件系统是可以对任何文件进行操作的,但是其实它并没有改变原来的文件,这是因为unionfs用到了重要的资管管理技术叫写时复制。
简单解释一下。就是首先镜像都依赖于linux操作系统,自带了各种bins/libs,然后你安装软件,环境,做完之后那玩意就是镜像。别人用就pull下来,然后由于比如nginx和php都是基于Ubuntu构建得,那么他们两个镜像很多都是重复得,所以docker镜像会分层。一个镜像有好多层。底下相同得就可以只下载一份。这就是为啥docker pull 一个镜像会下载好多个东西。但是最终他们得是一个东西。这就需要Union FS把这么多个目录合并成一个。
0x3.3 Namespaces
之前进程说的都是windows得情况。隔离简单提了下,因为保护模式,docker设计得时候是利用了Linux得内核特性实现的隔离,而且公用一个OS内核所以,他的隔离方式和Windows的就不太一样,当然了docker无法构建Windows环境,Windows下也无法直接运行docker,必须在windows下构建一个Linux环境才可以,Windows7是用的virtual box,windows10 则是自带的Hyper-v,但是Hyper-v在2004版本之前无法和VMware共存,因为是两种不太的虚拟化架构。不过2004 VMware16.0开始支持用Hyper-v来创建了。不过virtual box任然不支持。所以目前如果开了Hyper-v 几乎所有Android模拟器全部不能运行。
那么具体docker如何做到隔离,其中一个就是Namespaces,抄的网上解释如下
命名空间(namespaces)是 Linux 为我们提供的用于分离进程树、网络接口、挂载点以及进程间通信等资源的方法。在日常使用 Linux 或者 macOS 时,我们并没有运行多个完全分离的服务器的需要,但是如果我们在服务器上启动了多个服务,这些服务其实会相互影响的,每一个服务都能看到其他服务的进程,也可以访问宿主机器上的任意文件,这是很多时候我们都不愿意看到的,我们更希望运行在同一台机器上的不同服务能做到完全隔离,就像运行在多台不同的机器上一样
这玩意比较多
UTS命名空间(系统调用CLONE_NEWUTS)
隔离主机名和网络信息
IPC命名空间(系统调用CLONE_NEWIPC)
隔离IPC(进程通讯的方式)的相关,入共享内存,信号量,消息队列等
PID命名空间(系统调用CLONE_NEWPID)
隔离PID
Mount命名空间(系统调用CLONE_NEWNS)
隔离挂载点,功能上和chroot差不多
Network命名空间
隔离网络资源
User命名空间
隔离用户
0x3.4 CGroup
完成了隔离还是有之前进程的老问题,如何分配资源。这就需要用到CGroup了。简单来说他就是可以实现,限制资源使用,控制优先级,审计统计,挂起恢复等功能。这玩意不太好解释。有兴趣自己百度吧
0x4 虚拟化,容器对比
随便网上找了个,因为有多重水印,我就自己做了个表,就这样吧,反正也就这点东西
特性 | 虚拟机 | 容器 |
隔离级别 | 操作系统 | 进程 |
隔离策略 | Hypervisor | CGroups |
系统资源 | 5%-15% | 0-5% |
启动时间 | 分钟 | 秒 |
镜像存储 | GB-TB | KB-MB |
集群规模 | 上百 | 上万 |
高可用策略 | 备份,容灾,迁移 | 弹性,负载,动态 |
0x5 K8S
0x5.1 什么是K8S
很明显容器有他存在的意义,而且集群规模还很大,一个东西一旦多了,怎么管理他就是个问题。要不然肯定一团糟。这就是Kubernetes,掐头去尾中间还剩8个字母,就叫K8S了。(我能说我要是写代码这么缩写肯定要被打么。。。)这玩意就是来管理一堆容器的(是容器,不是docker,docker只是其中一种容器技术,而且貌似k8s快不支持docker了。说docker设计之初就不是为了加入k8s设计的。废话。。。。。)
0x5.2 K8S配置lnmp简介
先上张图,当然也是网上找的。
1.MASTER NODE
首先左边的叫master,右边的是node,
master,简单来说是管理的,node就是干活的。默认不会在master上调度pod,当然你可以手动删除master解点默认污点。
然后k8s不是一个软件。是一堆软件。讲几个比较重要的而且第一次配就得接触的。
kubelet:管理容器的
kubectl:kubelet的客户端,还有个高权限版。也就初始化的时候用,这两 类似于mysql 和 mysqld
apiservice:通讯用走flannel网络。要通讯只能靠他
kube-proxy:负载均衡网络流量。
然后吧node都需要通过apiservice访问master的api然后完成各种调度。当然是走的一个内网,这个内网要自己设置
2.nginx,php负载均衡标准流程
1.准备好docker容器。
2.配置pod,启动pod部署到node上
3.配置service,使得外部可以访问到pod
然后,访问就是,外部请求到service,然后交给kube-proxy,然后转给对应pod(负载均衡也在这里,如果有多个目标pod会自动负载均衡)
4.docker php容器安装插件必须到指定目录,和 yum apt等包管理工具不一样不能随便一个目录就安装。
3.遇到的坑
1.一开始想错了,以为只要在master配置好会自动同步到node,但是docker映射文件需要在node映射(当然使用nfs同步到远程位置就无所谓了,但是本机目录需要在每个node配置)
2.nginx 配置文件php路径不能写php service 内网ip。好像只能写php.default.svc.cluster.local,不然就报404
3.同样自己build的docker镜像如果没push到公网,node无法自动pull,需要手动去每个nodebuild一次。
4.配置过程
k8s节点上线具体过程。。。实在懒得网上抄下来了。基本照着做就行。。。比如:
https://blog.51cto.com/3241766/2405624
配置nginx,php坑上文说完了,步骤参考:
https://blog.csdn.net/bbwangj/article/details/82954187