第7章:深入理解常用控制器
7.1 Pod与controller的关系
-
controllers:在集群上管理和运行容器的对象。有时也称为工作负载(workload)
-
通过label-selector相关联,如下图所示。
-
Pod通过控制器实现应用的运维,如伸缩,滚动升级等
![](https://i-blog.csdnimg.cn/blog_migrate/17f8942d5b4c9ff3bc26d499c16ef025.png)
7.2 无状态应用部署控制器 Deployment
Deployment功能:
-
部署无状态应用(无状态应用简单来讲,就是Pod可以漂移任意节点,而不用考虑数据和IP变化)
-
管理Pod和ReplicaSet(副本数量管理控制器)
-
具有上线部署、副本设定、滚动升级、回滚等功能
-
提供声明式更新,例如只更新一个新的Image
应用场景:Web服务,微服务
下图是Deployment 标准YAML,通过标签与Pod关联。
![](https://i-blog.csdnimg.cn/blog_migrate/0306b98a670192aa33e4a7f322fe5de1.png)
使用YAML部署一个java应用:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3 # 设置3个副本
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- image: lizhenliang/java-demo
name: java
将这个java应用暴露到集群外部访问:
apiVersion: v1
kind: Service
metadata:
labels:
app: web
name: web
spec:
ports:
- port: 80 # 集群内容访问应用端口
protocol: TCP
targetPort: 8080 # 容器镜像端口
nodePort: 30008 # 对外暴露的端口
selector:
app: web
type: NodePort
查看资源:
kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/web-7f9c858899-dcqwb 1/1 Running 0 18s
pod/web-7f9c858899-q26bj 1/1 Running 0 18s
pod/web-7f9c858899-wg287 1/1 Running 0 48s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 5m55s
service/web NodePort 10.1.157.27 <none> 80:30008/TCP 48s
浏览器输入:http://NodeIP:30008 即可访问到该应用。
升级项目,即更新最新镜像版本,这里换一个nginx镜像为例:
kubectl set image deployment/web nginx=nginx:1.15
kubectl rollout status deployment/web # 查看升级状态
如果该版本发布失败想回滚到上一个版本可以执行:
kubectl rollout undo deployment/web # 回滚最新版本
也可以回滚到指定发布记录:
kubectl rollout history deployment/web # 查看发布记录
kubectl rollout undo deployment/web --revision=2 # 回滚指定版本
扩容/缩容:
kubectl scale deployment nginx-deployment --replicas=5
--replicas设置比现在值大就是扩容,反之就是缩容。
kubectl set image 会触发滚动更新,即分批升级Pod。
滚动更新原理其实很简单,利用新旧两个replicaset,例如副本是3个,首先Scale Up增加新RS副本数量为1,准备就绪后,Scale Down减少旧RS副本数量为2,以此类推,逐渐替代,最终旧RS副本数量为0,新RS副本数量为3,完成本次更新。这个过程可通过kubectl describe deployment web看到。
![](https://i-blog.csdnimg.cn/blog_migrate/a1555036e7ba02657c6590094f4a7b18.png)
7.3 守护进程控制器 DaemonSet
DaemonSet功能:
-
在每一个Node上运行一个Pod
-
新加入的Node也同样会自动运行一个Pod
应用场景:Agent,例如监控采集工具,日志采集工具
![](https://i-blog.csdnimg.cn/blog_migrate/a16822bf23ad0c69d32874942ee37868.png)
7.4 批处理 Job & CronJob
Job:一次性执行
应用场景:离线数据处理,视频解码等业务
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never # 作业失败后会不再尝试创建新的Pod
backoffLimit: 4 # .spec.backoffLimit字段限制重试次数。默认情况下,这个字段默认值是6。
上述示例中将π计算到2000个位置并将其打印出来。完成大约需要10秒。
查看任务:
kubectl get pods,job
CronJob:定时任务,像Linux的Crontab一样。
应用场景:通知,备份
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure # 作业失败并返回状态码非0时,尝试创建新的Pod运行任务
上述示例中将每分钟打印一次Hello。
查看任务:
kubectl get pods,cronjob
第8章:深入理解Service
8.1 Service存在的意义
-
防止Pod失联(服务发现)
-
定义一组Pod的访问策略(负载均衡)
8.2 Pod与Service的关系
-
通过label-selector相关联
-
通过Service实现Pod的负载均衡( TCP/UDP 4层)
![](https://i-blog.csdnimg.cn/blog_migrate/22ddd1dc28ef184afacd0cf325823c40.png)
8.2 Service三种类型
-
ClusterIP:集群内部使用,默认**,**分配一个稳定的IP地址,即VIP,只能在集群内部访问(同Namespace内的Pod)。
-
NodePort:对外暴露应用。在每个节点上启用一个端口来暴露服务,可以在集群外部访问。也会分配一个稳定内部集群IP地址。访问地址::
-
LoadBalancer:对外暴露应用,适用公有云、与NodePort类似,在每个节点上启用一个端口来暴露服务。除此之外,Kubernetes会请求底层云平台上的负载均衡器,将每个Node([NodeIP]:[NodePort])作为后端添加进去。
8.3 Service代理模式
Iptables:
-
灵活,功能强大
-
规则遍历匹配和更新,呈线性时延
IPVS:
-
工作在内核态,有更好的性能
-
调度算法丰富:rr,wrr,lc,wlc,ip hash…
kubectl get cm -n kube-system
kubectl edit cm kube-proxy -n kube-system
Service DNS名称
DNS服务监视Kubernetes API,为每一个Service创建DNS记录用于域名解析。
ClusterIP A记录格式:..svc.cluster.local
示例:my-svc.my-namespace.svc.cluster.local
小结
-
采用NodePort对外暴露应用,前面加一个LB实现统一访问入口
-
优先使用IPVS代理模式
-
集群内应用采用DNS名称访问
第9章:Ingress
8.1 Ingress为弥补NodePort不足而生
NodePort存在的不足:
-
一个端口只能一个服务使用,端口需提前规划
-
只支持4层负载均衡
8.2 Pod与Ingress的关系
-
通过Service相关联
-
通过Ingress Controller实现Pod的负载均衡
- 支持TCP/UDP 4层和HTTP 7层
![](https://i-blog.csdnimg.cn/blog_migrate/f469a93487beeb31ca60705a0adc0662.png)
8.3 Ingress Controller
为了使Ingress资源正常工作,集群必须运行一个Ingress Controller(负载均衡实现)。
所以要想通过ingress暴露你的应用,大致分为两步:
-
部署Ingress Controller
-
创建Ingress规则
整体流程如下:
![](https://i-blog.csdnimg.cn/blog_migrate/58d8a4db776b1fc10835b94153142367.png)
Ingress Controller有很多实现,我们这里采用官方维护的Nginx控制器。
部署文档:https😕/github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md
注意事项:
-
镜像地址修改成国内的:lizhenliang/nginx-ingress-controller:0.20.0
-
使用宿主机网络:hostNetwork: true
# kubectl apply -f ingress-controller.yaml
# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-5r4wg 1/1 Running 0 13s
nginx-ingress-controller-x7xdf 1/1 Running 0 13s
此时在任意Node上就可以看到该控制监听的80和443端口:
# netstat -natp |egrep ":80|:443"
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 104750/nginx: maste
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 104750/nginx: maste
80和443端口就是接收来自外部访问集群中应用流量,转发对应的Pod上。
其他主流控制器:
Traefik: HTTP反向代理、负载均衡工具
Istio:服务治理,控制入口流量
8.4 Ingress
接下来,就可以创建ingress规则了。
在ingress里有三个必要字段:
- host:访问该应用的域名,也就是域名解析
- serverName:应用的service名称
- serverPort:service端口
1、HTTP访问
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
spec:
rules:
- host: example.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
生产环境:example.ctnrs.com 域名是在你购买域名的运营商上进行解析,A记录值为K8S Node的公网IP(该Node必须运行了Ingress controller)。
测试环境:可以绑定hosts模拟域名解析(“C:\Windows\System32\drivers\etc\hosts”),对应IP是K8S Node的内网IP。例如:
192.168.31.62 example.ctnrs.com
2、HTTPS访问
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- sslexample.ctnrs.com
secretName: example-ctnrs-com
rules:
- host: sslexample.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
里面用到了secret名为secret-tls,用于保存https证书。
[root@k8s-master1 ~]# cat cfssl.sh
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl*
mv cfssl_linux-amd64 /usr/bin/cfssl
mv cfssljson_linux-amd64 /usr/bin/cfssljson
mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo
[root@k8s-master1 ~]# cat cert.sh
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing"
}
]
}
EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
cat > binglei.hou.com-csr.json <<EOF
{
"CN": "binglei.hou.com",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
EOF
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes binglei.hou.com-csr.json | cfssljson -bare binglei.hou.com
kubectl create secret tls binglei.hou.com --cert=binglei.hou.com.pem --key=binglei.hou.com-key.pem
这里使用cfssl工具自签证书用于测试,先下载cfssl工具:
curl -s -L -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -s -L -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
curl -s -L -o /usr/local/bin/cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x /usr/local/bin/cfssl*
执行课件中的certs.sh脚本,生成证书:
ls *pem
ca.pem ca-key.pem example.ctnrs.com.pem example.ctrnrs.com-key.pem
将证书保存在secret里:
kubectl create secret tls example-ctnrs-com --cert=example.ctnrs.com.pem --key=example.ctrnrs.com-key.pem
这样,ingress就能通过secret名称拿到要用的证书了。
然后绑定本地hosts,就可以https访问了:https://example-ctnrs-com
3、根据URL路由到多个服务
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: url-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foobar.ctnrs.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 80
- host: foobar.ctnrs.com
http:
paths:
- path: /bar
backend:
serviceName: service2
servicePort: 80
工作流程:
foobar.ctnrs.com -> 178.91.123.132 -> / foo service1:80
/ bar service2:80
4、基于名称的虚拟主机
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.ctnrs.com
http:
paths:
- backend:
serviceName: service1
servicePort: 80
- host: bar.ctnrs.com
http:
paths:
- backend:
serviceName: service2
servicePort: 80
工作流程:
foo.bar.com --| |-> service1:80
| 178.91.123.132 |
bar.foo.com --| |-> service2:80
8.5 Annotations对Ingress个性化配置
参考文档 :https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md
HTTP:配置Nginx常用参数
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx“
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
rules:
- host: example.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
HTTPS:禁止访问HTTP强制跳转到HTTPS(默认开启)
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-example-ingress
annotations:
kubernetes.io/ingress.class: "nginx“
nginx.ingress.kubernetes.io/ssl-redirect: 'false'
spec:
tls:
- hosts:
- sslexample.ctnrs.com
secretName: secret-tls
rules:
- host: sslexample.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
8.6 Ingress Controller高可用方案
如果域名只解析到一台Ingress controller,是存在单点的,挂了就不能提供服务了。这就需要具备高可用,有两种常见方案:
左边:双机热备,选择两台Node专门跑Ingress controller,然后通过keepalived对其做主备。用户通过VIP访问。
右边:高可用集群(推荐),前面加一个负载均衡器,转发请求到后端多台Ingress controller。
第10章:管理应用程序配置
10.1 secret
secret加密数据并存放Etcd中,让Pod的容器以挂载Volume方式访问。
应用场景:凭据
Pod使用secret两种方式:
-
变量注入
-
挂载
例如:创建一个secret用于保存应用程序用到的用户名和密码
echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
创建secret:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
变量注入方式在Pod中使用secret:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: nginx
image: nginx
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
进入到Pod中测试是否传入变量:
# kubectl exec -it mypod bash
# echo $SECRET_USERNAME
admin
# echo $SECRET_PASSWORD
1f2d1e2e67df
数据挂载方式在Pod中使用secret:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
进入到Pod中测试是否写入文件:
# kubectl exec -it mypod bash
#cat /etc/foo/username
admin
# cat /etc/foo/password
1f2d1e2e67df
如果你的应用程序使用secret,应遵循Pod获取该数据的方式。
10.2 configmap
与Secret类似,区别在于ConfigMap保存的是不需要加密配置信息。
应用场景:应用配置
例如:创建一个configmap用于保存应用程序用到的字段值
apiVersion: v1
kind: ConfigMap
metadata:
name: myconfig
namespace: default
data:
special.level: info
special.type: hello
变量注入方式在Pod中使用configmap:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: busybox
image: busybox
command: [ "/bin/sh", "-c", "echo $(LEVEL) $(TYPE)" ]
env:
- name: LEVEL
valueFrom:
configMapKeyRef:
name: myconfig
key: special.level
- name: TYPE
valueFrom:
configMapKeyRef:
name: myconfig
key: special.type
restartPolicy: Never
查看Pod日志就可以看到容器里打印的键值了:
# kubectl logs mypod
info hello
举一个常见的用法,例如将应用程序的配置文件保存到configmap中,这里以redis为例:
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
data:
redis.properties: |
redis.host=127.0.0.1
redis.port=6379
redis.password=123456
---
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: busybox
image: busybox
command: [ "/bin/sh","-c","cat /etc/config/redis.properties" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: redis-config
restartPolicy: Never
查看Pod日志就可以看到容器里打印的文件了:
# kubectl logs mypod
redis.host=127.0.0.1
redis.port=6379
redis.password=123456
10.3 应用程序如何动态更新配置?
ConfigMap更新时,业务也随之更新的方案:
-
当ConfigMap发生变更时,应用程序动态加载
-
触发滚动更新,即重启服务
-
sidecar 模式监听配置文件