第九课 Kubernetes生产级实践-Ingress-Nginx、共享存储和KubernetsAPI
tags:
k8s慕课网
categories:
定制配置定向流量控制共享储存StatefulSetKubernetsAPI了解
文章目录
第九课 Kubernetes生产级实践-Ingress-Nginx、共享存储和KubernetsAPI第一节 深入Ingress-Nginx1.1 Ingress-Nginx的补充学习1.2 DaemonSet修改Ingress-Nginx1.3 Ingress-Nginx的四层代理1.4 Ingress-Nginx的定制配置1.5 TSL配置HTTPS的配置1.6 访问控制session保持1.7 小流量和定向流量控制
第二节 共享存储2.1 共享存储介绍2.2 共享存储环境搭建2.3 共享存储使用
第三节 StatefulSet -有状态应用守护者3.1 StatefulSet介绍3.2 StatefulSet特性实践-顺序性3.3 StatefulSet特性实践-区分持久存储
第四节 Kubernetes API4.1 Kubernetes API设计和使用方式
第一节 深入Ingress-Nginx
1.1 Ingress-Nginx的补充学习
Deployment管理Ingress-Nginx是否非常合适?节点变化控制是否方便?四层代理 对外提供的是tcp服务而不是http服务如何做 服务发现呢?定制配置 调整Nginx的配置Https 如何配置证书呢访问控制 如果有session保持怎么完成 能不能做小流量?能不能支持AB测试?
1.2 DaemonSet修改Ingress-Nginx
Deployment 部署的副本 Pod 会分布在各个 Node 上,每个 Node 都可能运行好几个副本。DaemonSet 的不同之处在于:每个 Node 上最多只能运行一个副本。使用DaemonSet的好处,我们不用管随机生成的副本,想在哪个执行机上执行只要打个标签即可。如果使用Deployment每次增加减少都要考虑副本数。
kubectl get deploy -n ingress-nginx nginx-ingress-controller -o yaml
kubectl get deploy -n ingress-nginx nginx-ingress-controller -o yaml
> nginx-ingress-controller.yaml
vim nginx-ingress-controller.yaml
kubectl delete deploy -n ingress-nginx nginx-ingress-controller
kubectl apply -f nginx-ingress-controller.yaml
kubectl get pod -n ingress-nginx -o wide
kubectl label node s1 app
=ingress
kubectl get pod -n ingress-nginx -o wide
kubectl label node s1 app-
kubectl get pod -n ingress-nginx -o wide
1.3 Ingress-Nginx的四层代理
TCP服务如何用Ingress-Nginx做服务发现。
kubectl get cm -n ingress-nginx
kubectl get cm -n ingress-nginx -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: tcp-services
namespace: ingress-nginx
data:
"30000": dev/web-demo:80
kubectl apply -f tcp-config.yaml
netstat -ntlp
|grep 30000
http://192.168.242.131:30000/
1.4 Ingress-Nginx的定制配置
自定义配置-改一些配置参数
docker
ps | grep ingress-nginx
docker
exec -it e45667e1185b
bash
ps -ef
| grep nginx
more /etc/nginx/nginx.conf
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app: ingress-nginx
data:
proxy-body-size:
"64m"
proxy-read-timeout:
"180"
proxy-send-timeout:
"180"
kubectl apply -f nginx-config.yaml
docker
exec -it e45667e1185b
bash
more /etc/nginx/nginx.conf
自定义配置-定义全局的header
apiVersion: v1
kind: ConfigMap
data:
proxy-set-headers:
"ingress-nginx/custom-headers"
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: v1
kind: ConfigMap
data:
X-Different-Name:
"true"
X-Request-Start: t
=${msec}
X-Using-Nginx-Controller:
"true"
metadata:
name: custom-headers
namespace: ingress-nginx
kubectl apply -f custom-header-global.yaml
docker
exec -it 15b497b1108e
bash
more /etc/nginx/nginx.conf
自定义配置-自定义ingress下的header-
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet:
|
more_set_headers
"Request-Id: $req_id";
name: web-demo
namespace: dev
spec:
rules:
- host: web-dev.mooc.com
http:
paths:
- backend:
serviceName: web-demo
servicePort: 80
path: /
kubectl create -f custom-header-global.yaml
docker
exec -it 15b497b1108e
bash
more /etc/nginx/nginx.conf
自定义配置-配置模板
vi nginx-ingress-controller.yaml
- --annotations-prefix
=nginx.ingress.kubernetes.io
volumeMounts:
- mountPath: /etc/nginx/template
name: nginx-template-volume
readOnly:
true
terminationGracePeriodSeconds: 30
volumes:
- name: nginx-template-volume
configMap:
name: nginx-template
items:
- key: nginx.tmpl
path: nginx.tmpl
docker
cp 15b497b1108e:/etc/nginx/template/nginx.tmpl
.
scp nginx.tmpl root@m1:~
mv ~/nginx.tmpl
.
kubectl create cm nginx-template --from-file nginx.tmpl -n ingress-nginx
kubectl get cm -n ingress-nginx
kubectl get cm -n ingress-nginx nginx-template -o yaml
kubectl apply -f nginx-ingress-controller.yaml
docker logs 6dd6e5c276ef
docker
exec -it 6dd6e5c276ef
bash
kubectl edit cm -n ingress-nginx nginx-template
docker
exec -it 18a9f76b0035
bash
more /etc/nginx/nginx.conf
1.5 TSL配置HTTPS的配置
编写一个脚本生成证书,然后创建一个secret。
#!/bin/bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mooc.key -out mooc.crt -subj
"/CN=*.mooc.com/O=*.mooc.com"
kubectl create secret tls mooc-tls --key mooc.key --cert mooc.crt
kubectl get secret mooc-tls -o yaml
docker
ps | grep ingress-nginx
docker
exec -it db2305dcc074
bash
/nginx-ingress-controller -h
- --default-ssl-certificate
=default/mooc-tls
kubectl apply -f nginx-ingress-controller.yaml
kubectl get pod -n ingress-nginx
https://web-dev.mooc.com/
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web-demo
namespace: dev
spec:
rules:
- host: web-dev.mooc.com
http:
paths:
- backend:
serviceName: web-demo
servicePort: 80
path: /
tls:
- hosts:
- web-dev.mooc.com
secretName: mooc-tls
kubectl create -f web-ingress.yaml
https://web-dev.mooc.com/hello?name
=qierj
1.6 访问控制session保持
同一个会话里最好一直访问同一个后端。
kubectl get deploy -n dev web-demo -o yaml
| grep image
kubectl get deploy -n dev web-demo-new -o yaml
| grep image
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/session-cookie-hash: sha1
nginx.ingress.kubernetes.io/session-cookie-name: route
name: web-demo
namespace: dev
spec:
rules:
- host: web-dev.mooc.com
http:
paths:
- backend:
serviceName: web-demo
servicePort: 80
path: /
kubectl apply -f ingress-session.yaml
https://web-dev.mooc.com/hello?name
=qierj
1.7 小流量和定向流量控制
比如:服务上去之后不知道有没有问题,先给切流量10%过去,测试下有没有问题。没有问题之后再给他慢慢开启流量。ingress-nginx也帮我们想到这些问题了。但是是比较新的版本。。。
vi nginx-ingress-controller.yaml
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.23.0
kubectl apply -f nginx-ingress-controller.yaml
实现小流量控制
kubectl create ns canary
vimdiff web-canary-a.yaml web-canary-b.yaml
kubectl apply -f web-canary-a.yaml
kubectl apply -f web-canary-b.yaml
kubectl apply -f ingress-common.yaml
docker logs db2305dcc074
kubectl apply -f nginx-ingress-controller.yaml
kubectl get pods -n ingress-nginx
docker logs f3bacef5f19f
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web-canary-b
namespace: canary
annotations:
nginx.ingress.kubernetes.io/canary:
"true"
nginx.ingress.kubernetes.io/canary-weight:
"10"
spec:
rules:
- host: canary.mooc.com
http:
paths:
- path: /
backend:
serviceName: web-canary-b
servicePort: 80
kubectl apply -f ingress-weight.yaml
while sleep 0.2
;do curl http://canary.mooc.com/hello?name
=michael
&& echo ""; done
还有一种需求并不让线上的用户访问,只让测试人员测试下有没有问题。测试后在走上线流程。通过cookie做一个定向流量控制。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web-canary-b
namespace: canary
annotations:
nginx.ingress.kubernetes.io/canary:
"true"
nginx.ingress.kubernetes.io/canary-by-cookie:
"web-canary"
spec:
rules:
- host: canary.mooc.com
http:
paths:
- path: /
backend:
serviceName: web-canary-b
servicePort: 80
kubectl apply -f ingress-cookie.yaml
这种流量定向还有一种真实应用场景,比如针对女性用户做了优化但是不想让男性用户看到升级,可以在用户登陆后做一个用户性别的判断,每次用户在访问就可以访问到我们准备的页面。通过header定向流量。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web-canary-b
namespace: canary
annotations:
nginx.ingress.kubernetes.io/canary:
"true"
nginx.ingress.kubernetes.io/canary-by-header:
"web-canary"
spec:
rules:
- host: canary.mooc.com
http:
paths:
- path: /
backend:
serviceName: web-canary-b
servicePort: 80
kubectl apply -f ingress-header.yaml
curl -H
"web-canary: always" http://canary.mooc.com/hello?name
=michael
通过组合方式定向流量。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: web-canary-b
namespace: canary
annotations:
nginx.ingress.kubernetes.io/canary:
"true"
nginx.ingress.kubernetes.io/canary-by-header:
"web-canary"
nginx.ingress.kubernetes.io/canary-by-cookie:
"web-canary"
nginx.ingress.kubernetes.io/canary-weight:
"90"
spec:
rules:
- host: canary.mooc.com
http:
paths:
- path: /
backend:
serviceName: web-canary-b
servicePort: 80
kubectl apply -f ingress-compose.yaml
第二节 共享存储
2.1 共享存储介绍
前面接触到的服务都是无状态的服务。迁移到docker会比较顺畅,如果是有状态的服务(比如存储)迁移起来就比较麻烦了。
目录挂载到宿主机上,设置好亲和性,让它每次挂载到固定的节点上。这确实是一种方案但是缺陷比较大。因为绑定到一个固定的机器,如果机器出问题,那就不可用了。还有备份容灾的问题。
k8s也想到上面的情况给我们提供了共享存储的方案。
首先介绍其中的一些概念。
PV是PersistentVolume的简写,持久化数据卷。它也是k8s的一种资源,一般由运维提前创建好。capacity.storage容量,accessModes访问模式比如:ReadWriteOnce,读写权限。PVC是PersistentVolumeClaim的简写。描述的是pod希望存储的需求。一般由开发维护。PV和PVC必须建立绑定关系,才能处于可用状态。PV要满足PVC的需求,比如存储大小、读写权限。
Pod使用PVC流程,在volume定义中用了一个字段persistentVolumeClaim指定了pvc的名字
在PV和PVC两层抽象下,共享存储的使用看起来就非常简单Pod中声明了一个PVC名字,PVC里面描述了Pod的需求(权限、多大空间),PVC中绑定个PV,PV中描述的具体的存储后端。后端具体的存储服务和后端的配置有集群管理员实现,剩下由k8s自己解决(把运维定义的PV和开发定义的PVC建议起绑定关系),把pod调度起来和volume设置好。所有的volume都会对应一个目录,像hostpath和emptydir对应的就是本地的普通目录。但是对于共享存储就不是普通目录,每种后端存储服务对应不同的处理过程。像NFS就比较简单,k8s会把目录远程挂载到PV指定的目录中。类似mount
volumes:
- name
:nfs
persistentVolumeClaim:
claimName: nfs
如果有很多Pod需要共享存储,每见到一个就要运维配置个PV,不用还得通知他回收。这样的话太麻烦了。k8s给我们提供了StorageClass,是一套自动管理PV的机制。类似PV的模板。
有了StorageClass, PVC的定义中多了字段storageClassName StorageClass的设计要点
每个PV和PVC都有StorageClass, 如果没有设置就会有一个空的默认的StorageClassStorageClass不需要真实存在,当PVC中指定了StorageClass就会自动绑定,不会在意是否真的存在。 一个PV或者StorageClass必定只能对应一种类型的后端存储。假如有个Pod需要用到后端存储,首先创建pvc用来描述pod想要什么类型的pv。k8s会根据它的需求,匹配pv或者StorageClass。然后建立绑定关系。
2.2 共享存储环境搭建
使用gluster作为存储:https://github.com/gluster/gluster-kubernetes这种方式我们准备三个节点保证每份数据三分备份。每个节点上要有一个裸磁盘没有分区的磁盘。这三个节点内存可以设置小一点。(两个节点会出现意想不到的问题,别用两个节点)
yum install
-y glusterfs glusterfs
-fuse
ps
-ef
| grep apiserver
| grep
-allow
-privileged=true
vi /etc/systemd/system/kubelet.service
kubectl label node s1 storagenode=glusterfs
kubectl label node s2 storagenode=glusterfs
kubectl label node s3 storagenode=glusterfs
kubectl apply
-f glusterfs
-daemonset.yaml
kubectl get pods
-o wide
kubectl apply
-f heketi
-security.yaml
- name: HEKETI_KUBE_GLUSTER_DAEMONSET
value: "y"
- name: HEKETI_ADMIN_KEY
value: "admin123"
kubectl apply
-f heketi
-deployment.yaml
kubectl get pods
-o wide
cat topology.json
docker ps
| grep heketi
docker exec
-it 50eacbc66afc bash
export HEKETI_CLI_SERVER=http
://localhost
:8080
vi topology.json
heketi
-cli
-s $HEKETI_CLI_SERVER
--user admin
--secret 'admin123' topology load
--json=topology.json
heketi
-cli topology info
--user admin
--secret 'admin123'
netstat
-ntlp
| grep glusterd
docker ps
| grep gluster
gluster
peer status
2.3 共享存储使用
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: glusterfs
-storage
-class
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://192.168.242.136:30001"
restauthenabled: "true"
restuser: "admin"
restuserkey: "admin123"
创建pod之前要先创建pvc。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: glusterfs
-pvc
spec:
storageClassName: glusterfs
-storage
-class
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
创建pvc,让它自动绑定到后端的pv上。
kubectl apply -f glusterfs-storage-class.yaml
kubectl apply -f glusterfs-pvc.yaml
kubectl get pvc
kubectl get
pv
pod使用pvc。这里副本数为2 用来测试pv
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
-deploy
spec:
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
selector:
matchLabels:
app: web
-deploy
replicas: 2
template:
metadata:
labels:
app: web
-deploy
spec:
containers:
- name: web
-deploy
image: hub.mooc.com/kubernetes/springboot
-web
:v1
ports:
- containerPort: 8080
volumeMounts:
- name: gluster
-volume
mountPath: "/mooc-data"
readOnly: false
volumes:
- name: gluster
-volume
persistentVolumeClaim:
claimName: glusterfs
-pvc
测试一下
kubectl apply -f web-deploy.yaml
kubectl get pods -o wide
docker
ps | grep web-deploy
docker
exec -it e25436d15b44 sh
cd /mooc-data/
&& echo "hello xixi" > a
docker
exec -it f60 sh
cd /mooc-data/
第三节 StatefulSet -有状态应用守护者
3.1 StatefulSet介绍
Deployment 不能适应所有的编排工作 之前服务的特征
无状态Pod无差别没有顺序性 分布式应用多个实例之间都会存在关系。像主从关系,ETCD集群,zk集群, redis主备,像这种多实例不对等的情况k8s提出了StatefulSet。StatefulSet特性 它抽象两种有状态的场景
顺序性 多个实例有顺序性,有自己的编号。实例间可能互相访问 通讯区分持久存储 pod之间对于同一目录是私有的。
3.2 StatefulSet特性实践-顺序性
先看下headless的Service的定义。 clusterIP: None
无头服务,不会有一个Service的vip可以通过DNS的名字访问到它,返回的是对应的Enterpoint对应的ip列表
apiVersion: v1
kind: Service
metadata:
name: springboot
-web
-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
clusterIP: None
selector:
app: springboot
-web
StatefulSet定义和之前Deployment的定义区别不大。
多了serviceName: springboot-web-svc 告诉statefulSet用哪个headless 服务保证pod的解析
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: springboot
-web
spec:
serviceName: springboot
-web
-svc
replicas: 2
selector:
matchLabels:
app: springboot
-web
template:
metadata:
labels:
app: springboot
-web
spec:
containers:
- name: springboot
-web
image: hub.mooc.com/kubernetes/springboot
-web
:v1
ports:
- containerPort: 8080
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 3
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /hello
?name=test
port: 8080
scheme: HTTP
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 1
successThreshold: 1
timeoutSeconds: 5
测试一下。
kubectl apply -f headless-service.yaml
kubectl get pods -l app
=springboot-web -o wide -w
docker
ps | grep springboot
docker
exec -it 10950afea635 sh
hostname
ping springboot-web-0.springboot-web-svc
ping springboot-web-1.springboot-web-svc
kubectl delete pod -l app
=springboot-web
kubectl get pods -l app
=springboot-web -o wide
3.3 StatefulSet特性实践-区分持久存储
statefulset-volume.yaml上面和之前statefulset是一样的。只有下面volumeClaimTemplates的不一样。
volumeClaimTemplates和podTemplate有点相似volumeClaimTemplates是创建pvc的模板 podTemplate是创建pod的模板为什么不指定pvc呢?指定pvc那是共享存储,而不是pod的私有存储。
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
storageClassName: glusterfs
-storage
-class
resources:
requests:
storage: 1Gi
测试一下
kubectl delete -f statefulset.yaml
kubectl apply -f statefulset-volume.yaml
kubectl get pods -l app
=springboot-web -o wide
kubectl get pvc
kubectl get
pv
docker
ps | grep springboot
docker
exec -it 4e2da179acca sh
echo "hello" > /mooc-data/file
kubectl delete pod -l app
=springboot-web
kubectl delete -f statefulset-volume.yaml
第四节 Kubernetes API
4.1 Kubernetes API设计和使用方式
/api 是非常核心的api。没有api分组的,只有两级。版本和资源/apis是非核心的api, 每个资源都用三级来表示。第一级分组 第二级版本 第三季资源
v1稳定版 v2alpha1不是稳定版 api版本这么复杂 那我在写配置文件时 怎么确定当前配置文件应该使用的版本呢?
进入Kubernetes官网:https://kubernetes.cn/进入具体api页面:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/ kubernetes的Api是由ApiServer负责的。ApiServer是集群的交通枢纽各种资源数据都是通过ApiServer提交到后端存储、Etcd。 Kubernetes各个组件之间也是通过ApiServer的接口实现的解耦。ApiServer对外提供的访问方式基于http的RestFul Api。
netstat -ntlp
| grep 8080
vi /etc/systemd/system/kube-apiserver.service
--insecure-port
=8080
systemctl daemon-reload
&& service kube-apiserver restart
netstat -ntlp
| grep 8080
curl http://localhost:8080/healthz
curl http://localhost:8080/api/v1/pods
curl http://localhost:8080/api/v1/nodes
它有对应多种语言环境的客户端Api:https://github.com/kubernetes-client 比较常用的是python和java的客户端。还有一种使用更广泛的是kubernetes官方的自带的go的api客户端:
官方:thub.com/kubernetesgo客户端:https://github.com/kubernetes/client-go