k8s用于大规模部署和运维容器
优点:
master:主节点,其下包含:
worker node:工作节点,其下包含:
CoreDNS:为集群中的SVC创建一个域名IP的对应解析
Dashboard:给集群提供一个B/S访问体系
Ingress Controller:官方只有四成代理,ingress可以实现七层代理
Federation:提供一个跨集群中心多k8s统一管理
Prometheus:提供集群监控
elk:提供日志分析
主机(Master): 用于控制 Kubernetes 节点的计算机。所有任务分配都来自于此。
节点(Node):负责执行请求和所分配任务的计算机。由 Kubernetes 主机负责对节点进行控制。
容器集(Pod):被部署在单个节点上的,且包含一个或多个容器的容器组。同一容器集中的所有容器共享同一个 IP 地址、IPC、主机名称及其它资源。容器集会将网络和存储从底层容器中抽象出来。这样,您就能更加轻松地在集群中移动容器。
复制控制器(Replication controller):用于控制应在集群某处运行的完全相同的容器集副本数量。
服务(Service):将工作内容与容器集分离。Kubernetes 服务代理会自动将服务请求分发到正确的容器集——无论这个容器集会移到集群中的哪个位置,甚至可以被替换掉。
Kubelet:运行在节点上的服务,可读取容器清单(container manifest),确保指定的容器启动并运行。
kubectl: Kubernetes 的命令行配置工具。
Pod分为:
一个pod默认启动时会创建一个初始容器:pause,其余在该pod上的容器会公用pause的【网络栈】和【存储卷】,即一个pod中的容器的端口不能冲突
pod中有多种控制器:
--runtime-config=batch/v2alpha1=true
开启)HPA:平滑扩展,仅适用于Deployment和RS,在v1中支持根据pod的cpu利用率扩容,后面版本支持根据内存和其他值标扩缩容,通过控制控制器来对Pod进行管理,需要提供一个资源控制方案才可以进行控制
Deployment:会通过创建RS,达到管理Pod的能力,滚动更新机制是,创建新的RS,新RS创建新Pod,旧RS删除旧Pod,以此过渡,最终旧RS停用(不删除),回滚时,旧RS启动流程一样
HPA:比如通过监听CPU>80,则创建新的pod,直到指定上限,使用率下来后会减少pod,直到下限
DaemonSet:常用于在每个Node上都要有一个的服务:1 运行集群存储;2 运行日志收集;3 运行监控
CronJob:常用于数据库备份、发送邮件
StatefullSet:该控制器对MySQL依然不太稳定,MongoDB比较稳定
对于RS创建出来的Pod,如果选择方式为:matchLabels,那改变创建出来的Pod的label,则RS会重新创建一个原来label的Pod,因为RS按照选择方式去控制Pod数量,被改变的那个Pod则不归RS管了
RS测试样例:
末尾的containerPort可写可不写,因为内部网络所有端口都可以访问,extensions/v1beta1可以改成apps/v1,查看apiVersion可以:kubectl api-resources |grep Deployment
Deployment与RS关系:
Deployment测试样例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dep
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: mynginx
image: nginx
[root@master ~]# kubectl apply -f deployment-nginx.yaml --record
deployment.apps/nginx-dep created
[root@master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-dep 1/3 3 1 15s
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-dep-fbd97f5b7 3 3 1 22s
[root@master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-dep-fbd97f5b7-dg87j 1/1 Running 0 2m37s name=nginx,pod-template-hash=fbd97f5b7
nginx-dep-fbd97f5b7-fbk6s 1/1 Running 0 2m37s name=nginx,pod-template-hash=fbd97f5b7
nginx-dep-fbd97f5b7-qkvzw 1/1 Running 0 2m37s name=nginx,pod-template-hash=fbd97f5b7
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-dep-fbd97f5b7-dg87j 1/1 Running 0 4m49s 10.244.2.5 node1 <none> <none>
nginx-dep-fbd97f5b7-fbk6s 1/1 Running 0 4m49s 10.244.2.4 node1 <none> <none>
nginx-dep-fbd97f5b7-qkvzw 1/1 Running 0 4m49s 10.244.1.2 node2 <none> <none>
[root@master ~]# curl 10.244.2.5
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Deployment扩容:kubectl scale deployment 名称 --replicas=数量
[root@master ~]# kubectl scale deployment nginx-dep --replicas=10
deployment.apps/nginx-dep scaled
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-dep-fbd97f5b7-2shcs 1/1 Running 0 94s
nginx-dep-fbd97f5b7-cbws9 1/1 Running 0 94s
nginx-dep-fbd97f5b7-dg87j 1/1 Running 0 16m
nginx-dep-fbd97f5b7-fbk6s 1/1 Running 0 16m
nginx-dep-fbd97f5b7-kxtks 1/1 Running 0 94s
nginx-dep-fbd97f5b7-qkvzw 1/1 Running 0 16m
nginx-dep-fbd97f5b7-qskjr 1/1 Running 0 94s
nginx-dep-fbd97f5b7-wt59c 1/1 Running 0 94s
nginx-dep-fbd97f5b7-wzfdz 1/1 Running 0 94s
nginx-dep-fbd97f5b7-xjfrr 1/1 Running 0 94s
Deployment滚动更新:kubectl set image deployment/nginx-dep mynginx=nginx:1.23
[root@master ~]# kubectl set image deployment/nginx-dep mynginx=nginx:1.23
deployment.apps/nginx-dep image updated
[root@master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-dep-7f4688fd7f 5 5 0 39s
nginx-dep-fbd97f5b7 8 8 8 32m
注意:
Deployment回滚:kubectl rollout undo deployment/nginx-dep
[root@master ~]# kubectl rollout undo deployment/nginx-dep
deployment.apps/nginx-dep rolled back
查看当前Deployment回滚状态:kubectl rollout status deployment/nginx-dep
查看当前Deployment回滚历史:kubectl rollout history deployment/nginx-dep
,此处如果在创建Deployment时没有加参数--record
则回滚方式为NONE
指定回退版本:kubectl rollout undo deployment/nginx-dep --to-revision=2
暂停更新:kubectl rollout pause deployment/nginx-dep
Deployment更新历史记录保留策略
可以通过.spec.revisionHistoryLimit
来指定保留多少次记录,默认会保留所有,如果设置为0则不允许回退(因为没有历史记录了)
查看上一条命令的执行结果,0表示成功
echo $?
需要注意的是:虽然daemonset会在所有的节点上创建pod,但是不会在主节点上创建,因为主节点被默认打了污点,不会参与调度
查看pod的控制台显示日志:kubectl log Pod名称
Job Spec设置
CronJob Spec
注意:删除cronjob不会删除job,需要手动删除job,且cronjob无法判断job是否成功执行
通常对于多个副本的Pod提供一种服务的,k8s方案是在Pod上层提供一个service层,用于管理该服务,然后对外或其他Pod提供访问方式,简单来说,可以将提供一个服务的一组Pod加上service 层,看成一个服务去访问,这就是服务发现,用于管理集群中的服务
在创建一个SVC时,过程如下:
通常采用Label Selector进行管理Pod,匹配到标签后,Pod的信息会被写入到svc中,对外提供访问,策略是轮询
注意:SVC虽然能提供轮询的负载均衡机制,但是只能基于IP和端口进行转发,也就是4层负载均衡能力(没有7层,不能通过主机名或域名的方案去做),可以使用Ingress来支持7层
SVC有四种类型:
首先kube-proxy会监控每个Pod的信息,按照标签分类将信息写入iptables中
apiserver通过监控kube-proxy来实现服务的发现
在用户访问某个服务时,访问的是iptables,重定向到某个Pod完成访问
k8s在1.14版本中默认使用ipvs代理
为什么不实用DNS进行负载均衡?因为DNS有缓存,不会及时更新
代理模式分类:
ipvs和iptables都是防火墙配置工具,ipvs支持dnat
kube-proxy监控SVC和Endpoints,调用netlink接口创建ipvs规则,并与SVC和Endpoints同步ipvs规则
ipvs使用哈希表作为底层数据结构,并在内核空间工作,可以更快的重定向流量,同步时也有更好的性能,支持多重负载均衡算法:
注意:ipvs模式要求kube-proxy启动前,节点需要安装ipvs内核模块,并已ipvs代理模式启动,启动时如果验证节点未安装ipvs,kube-proxy会退回到iptables代理模式
先创建Deployment,副本数3:
创建Service:
kubectl delete -f 文件
可以通过这种方式去删除对应的资源(由该文件创建出来的)
headless service:
此时该service无法通过vip的方式进行访问,但是可以通过域名访问,通过查看coredns域名解析pod的解析,可以看到该service下确实管理着之前创建的Pod
NodePort:
查询结果:kubectl get svc
可以看到将80端口代理到了node的xxx端口,通过访问node的xxx端口即可访问Pod
LoadBalancer
和nodeport类似,只是在前面多了一层云服务商提供的负载均衡服务,由k8s的provider cloud去创建的LB,需要购买云服务商提供的LAAS服务
externalname
此时访问域名:SVC名称.命名空间.svc.cluster.local
,会被集群内部DNS重定向到externalName
指定的域名上,发生在DNS层,不会进行代理和转发(简单理解就是做了一个DNS的别名操作)
案例背景:如果对外需要提供一个HTTPS的连接,SVC是没有这个功能的,只能在每个Pod上都要配置HTTPS连接才行
而如果在集群外部配置一个Nginx,Nginx对外可以采用HTTPS连接,对内反向代理采用HTTP连接,这样就可以实现了。(但是外部Nginx和集群是HTTP,所以其实还是有一定风险的)
因此,最优方案是:外部访问集群使用的HTTPS,集群内部提供一个Nginx去做反向代理到不同的SVC,Ingress就是做这样的事。
注意:为什么不自己在内部部署一个nginx服务去做这个呢,因为ingress的nginx配置文件是会自动生成对应的SVC反向代理,无需手动配置。
Ingress内部逻辑:
如果需要高可用,建议将master做主备,然后再在外部使用Nginx做负载均衡连接两台master,master内部通过ingress-nginx做内部的Pod的负载均衡
部署Ingress-Nginx
# 启动ingress 控制器
kubectl apply -f mandatory.yaml
# 设置为nodeport暴露方式
kubectl apply -f service-nodeport.yaml
参考:https://kubernetes.github.io/ingress-nginx/deploy
Ingress HTTP访问
deployment service ingress 配置
apiVersion: extension/v1beta1
kind: Deployment
metadata:
name: nginx-dm
spec:
replicas: 2
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: nginx
image: xxxx/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
port:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80
执行:kubeclt apply -f ingress.http.yaml
# 查看当前创建的svc
kubectl get svc # 可以直接访问下集群的svc地址获取服务
# 查询当前ingress在宿主机上暴露出去的端口
kubectl get svc -n ingress-nginx
# 查看当前的ingress
kubectl get ingress
此时当前NodePort模式暴露后,可以在主机上配置好DNS访问:nodeIP foo.bar.com
宿主机上访问:foo.bar.com:ingress暴露的端口
宿主机访问域名,其实相当于访问nodeport的ip,然后ingress暴露了端口给nodeport,所以外部可以通过这个端口访问到ingress,而ingress代理访问了内部svc的端口和地址,svc又可以访问到pod,服务就联通了
与直接使用svc比,ingress还可以通过不同路径来访问不通服务,svc必须通过不通的域名或者ip来访问不同服务
ingress HTTPS访问
首先需要创建证书:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
创建ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
tls:
- hosts:
- foo.bar.com
secretName: tls-secret
rules:
- host: foo.bar.com
http:
path:
- path: /
backend:
seviceName: nginx-svc
servicePort: 80
Nginx BasicAuth
对Nginx进行基础授权认证,在进入网站时弹出用户名和密码框
yum -y install httpd
htpasswd -c 文件名 用户名
kubectl create secret generic basic-auth --from-file=文件名
这里使用文件名:auth,用户名:foo
apiVersion: extensions/b1beta1
kind: Ingress
metadata:
name: ingress-with-auth
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
rules:
- host: foo2.bar.com
http:
path:
- path: /
backend:
seviceName: nginx-svc
servicePort: 80
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://foo.bar.com:31795/hostname.html
spec:
rules:
- host: foo10.bar.com
http:
path:
- path: /
backend:
seviceName: nginx-svc
servicePort: 80
k8s模型是假定所有Pod都在一个可以直接连通的扁平化的网络空间中,所以,搭建集群时,我们必须确保不同节点上的Docker互相打通。(flannel可以实现扁平网络空间)
同一个Pod内的多个容器之间的通讯:lo(本地回路localhost,即docker0组成的虚拟网桥)
Pod之间的通讯:Overlay Network
Pod与Service通讯:各节点的Iptables规则(建议使用LVS,性能更好)
Pod与外网访问:通过查找路由表,转发到宿主几网卡,然后网卡执行Masquerade,将源IP改为宿主机IP,向外部发送请求(SNAT)
外网与Pod访问:通过Service
Etcd和Flannel之间的关系:
Flannel:CoreOS团队创建,让集群不同Node上的Docker有集群唯一IP,且创建一个Overlay Network,让数据包可以原封不动的从一个容器传送到另一个容器内
k8s中所有内容都被抽象为资源,资源实例化后,叫做对象
名称空间级别
工作负载类型资源:Pod、RS、Deployment、StatefulSet、DaemonSet、Job、CronJob
服务发现及负载均衡类型资源:Service、Ingress
配置与存储资源:Volume、CSI(容器存储接口,可扩展第三方卷存储)
特殊类型存储娟:ConfigMap(当配置中心来使用)、Secret(保护敏感数据)、DownwardAPI(把外部信息输出给容器)
集群级别
Namespace
Node
Role
ClusterRole
RoleBinding
ClusterRoleBinding
元数据级别
HAP
PodTemplate
LimitRange
YAML文件语法tips
对象类型可以写成:hash: { name: pp, age: 18 }
或hash: name: pp age: 18
数组类型可以写成:animal: [Cat, Dog]
或animal: - Cat - Dog
类型强制转换:!!类型
a: !!str 123 b: !!str true
类型:
字符串:可以不用引号,但如果有特殊字符需要放在引号中,且双引号不会对特殊字符转义,单引号中如果还有单引号必须使用两个单引号来表示
k8s定义一个Pod所需yml配置文件的必填字段类型及含义:
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: myapp
version: v1
spec:
containers:
- name: app
image: localhost:5000/xiaopi3/httpd:v1
- name: test
image: localhost:5000/xiaopi3/httpd:v1
注意:如果镜像下载策略为默认的即always,且镜像没有版本号或版本号为latest,则容器每次都会拉取最新镜像进行下载
在容器启动前有很多initC容器进行容器环境初始化
有很多探测,在生命周期中会进行检测
start:容器启动
readiness:容器就绪,可提供服务了
liveness:容器生存
stop:容器停止
init容器
与普通容器很像,必须运行到成功为止,且串行,前一个必须成功才会运行后一个,如果没有成功运行,则pod会重启,直到成功。如果pod对应restartPolicy策略为Never,则不会重启
简单理解:A容器需要访问B容器才能启动,如果AB同时启动,有可能导致A容器启动失败(B并未ready),可以在A容器启动前加入【探测B容器是否正常运行的探测程序】作为init C,然后启动AB即可
init模板
启动两个init C,直到两个都成功结束才会运行主容器,此处必须启动下面这个两个pod才能让init C检测通过(成功运行的含义是容器退出代码为0)
需要注意的是:
容器探针
由kubelet对容器发起定期诊断,有三种探测方式:
注意:诊断结果有三种:成功、失败、未知。未知不会采取任何行动
探测策略有两种:
注意:两种检测策略可以同时存在