命名空间 Namespace 将对象逻辑上分配到不同 Namespace,可以是不同的项目、用户等区分管理,并设定控制策略,从而实现多租户。
命名空间也称为虚拟集群。通过将 Kubernetes 集群内部的资源对象“分配”到不同的 Namespace 中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
Kubernetes 集群在启动后,会创建一个名为 “default” 的 Namespace,通过 kubectl 可以查看到:
[root@k8s-master ~]# kubectl get namespaces
[root@k8s-master ~]# vim pod-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: pod-test
[root@k8s-master ~]# vim pod-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: pod-test
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
[root@k8s-master ~]# kubectl get pods --namespace pod-test
# 进入指定名称空间中的 pod
[root@k8s-master ~]# kubectl exec -it -n pod-test busybox -- /bin/sh
Annotation 与 Label 类似,也使用 key/value 键值对的形式进行定义。不同的是 Label 具有严格的命名规则,它定义的是 Kubernetes 对象的元数据(Metadata),并且用于 Label Selector。而 Annotation 则是用户任意定义的“附加”信息,以便于外部工具进行查找。通常 Kubernetes 的模块会通过 Annotation 的方式标记资源对象的一些特殊信息。
使用 Annotation 来记录的信息如下:
一个 pod 打多个标签
一个标签可以打给多 pod
Label Selector的表达式有两种:基于等式的(Equality-based)和基于集合的(Set-based)。下面是基于等式的匹配例子。
name=redis-slave:匹配所有标签为 name=redis-slave 的资源对象。
env != production:匹配所有标签env不等于 production 的资源对象。
下面是基于集合的匹配例子
name in (redis-master, redis-slave):匹配所有标签为 name=redis-master 或者 name=redis-slave 的资源对象。
name not in (php-frontend):匹配所有标签 name 不等于php-frontend 的资源对象。
还可以通过多个 Label Selector 表达式的组合实现复杂的条件选择,多个表达式之间用“,”进行分隔即可,几个条件之间是 “AND” 的关系,即同时满足多个条件,例如:
name=redis-slave, env!=production
name not in (php-frontend), env!=production
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
app: myweb
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 3
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- name: nginx1
image: nginx
ports:
- containerPort: 80
selector:
matchLabels:
app: myweb
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
# 所有标签之间都是and关系
# 写几个匹配条件就需要满足几个
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: label-kube
labels:
dep: web
spec:
replicas: 2
selector:
matchLabels:
app: myweb
aaa: bbb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
matchLabels 用于定义一组 Label,与直接写在 Selector 中作用相同;
matchExpressions 用于定义一组基于集合的筛选条件,可用的条件运算符包括:In、NotIn、Exists 和 NotExist。
如果同时设置了 matchLabels 和 matchExpressions,则两组条件为“AND”关系,即所有条件需要同时满足才能完成 Selector 的筛选。
Kube-controller 进程通过资源对象 RC 上定义的 Label Selector 来筛选要监控的 Pod 副本的数量,从而实现 Pod 副本的数量始终符合预期设定的全自动控制流程。
Kube-proxy 进程通过 Service 的 Label Selector 来选择对应的 Pod,自动建立起每个 Service 到对应 Pod 的请求转发路由表,从而实现 Service 的智能负载均衡机制。
通过对某些 Node 定义特定的 Label,并且在 Pod 定义文件中使用 NodeSelector 这种标签调度策略,kube-scheduler 进程可以实现Pod“定向调度”的特性。
下面举个复杂点的例子,假设我们为 Pod 定义了3个Label:release、env和role,不同的Pod定义了不同的Label。如下图所示,如果我们设置了“role=frontend”的Label Selector,则会选取到Node 1和Node 2上的Pod。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jkg8mhUN-1610418329789)(assets/7288a5dd3d0683d40fa07959b40e8be5.png)]
如果我们设置 “release=beta” 的 Label Selector,则会选取到 Node 2 和 Node 3 上的 Pod,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0GlXQaF-1610418329793)(assets/4c2f9ad2c06d184fac94eacebeab1d3a.png)]
使用 Label 可以给对象创建多组标签,Label 和 Label Selector 共同构成了 Kubernetes 系统中最核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 3
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: app-demo
image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
[root@k8s-master ~]# kubectl scale rc redis-slave --replicas=3
[root@k8s-master ~]# vim redis-master-controller-v1.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx
labels:
name: web
version: v1
spec:
replicas: 3
selector:
name: nginx-web
version: v1
template:
metadata:
labels:
name: nginx-web
version: v1
spec:
containers:
- name: nginx
image: nginx:1.14
ports:
- containerPort: 80
[root@k8s-master ~]# kubectl set image rc nginx nginx=nginx:1.14 --record # 只能更新rc缓存信息
RS 是新一代 ReplicationController。确保任何给定时间指定的 Pod 副本数量,并提供声明式更新等功能。
RS 不参与人为管理,由于Replication Controller 与 Kubernetes 代码中的模块 Replication Controller 同名,同时这个词也无法准确表达它的意思,所以从 Kubernetes v1.2 开始,RC升级成了另外一个新的对象—— Replica Set,官方解释为“下一代的 RC”。
RS 与 RC当前存在的唯一区别是:Replica Set 支持基于集合的 Label selector(Set-based selector),而 RC 只支持基于等式的 Label selector(equality-based selector),所以 Replica Set 的功能更强大。
Replica Set 很少单独使用,它主要被 Deployment 这个更高层的资源对象所使用,从而形成一整套 Pod 创建、删除、更新的编排机制。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
使用kubectl delete命令会删除此RS以及它管理的Pod。在Kubernetes删除RS前,会将RS的replica调整为0,等待所有的Pod被删除后,在执行RS对象的删除。
如果希望仅仅删除RS对象(保留Pod),请使用kubectl delete命令时添加 --cascade=false选项。
Deployment 是一个更高层次的 API 对象,它管理 ReplicaSets 和 Pod,并提供声明式更新等功能。
官方建议使用 Deployment 管理 ReplicaSets,而不是直接使用 ReplicaSets,这就意味着可能永远不需要直接操作 ReplicaSet 对象。
Deployment 相对于 RC 的最大区别是可以随时知道当前 Pod “部署”的进度。一个 Pod 的创建、调度、绑定节点及在目标 Node 上启动对应的容器这一完整过程需要一定的时间,所以我们期待系统启动 N个 Pod 副本的目标状态,实际上是一个连续变化的“部署过程”导致的最终状态。
Deployment 的典型使用场景有以下几个:
只需要在 Deployment 中描述想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。也可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
apiVersion: v1
kind: ReplicaSet
metadata:
name: nginx-repset
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: scm
labels:
app: nginx-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx-demo
minReadySeconds: 60 # 这里需要估一个比较合理的值,从容器启动到应用正常提供服务
strategy:
rollingUpdate: # 由于replicas为3,则整个升级,pod个数在2-4个之间
maxSurge: 1 # 更新时允许最大激增的容器数,默认 replicas 的 1/4 向上取整
maxUnavailable: 1 # 更新时允许最大 unavailable(失效) 容器数,默认 replicas 的 1/4 向下取整
template:
metadata:
labels:
app: nginx-demo
spec:
terminationGracePeriodSeconds: 60 # k8s 将会给应用发送 SIGTERM(终止进程 软件终止信号)信号,可以用来正确、优雅地关闭应用,默认为30秒
containers:
- name: nginx-demo
image: nginx
imagePullPolicy: IfNotPresent
livenessProbe: # kubernetes认为该pod是存活的,不存活则需要重启
httpGet:
path: /
port: 80
scheme: HTTP
initialDelaySeconds: 60 # 容器启动60秒后开始第一次检测
timeoutSeconds: 5 # http检测请求的超时时间
successThreshold: 1 # 检测到有1次成功则认为服务是`就绪`
failureThreshold: 5 # 检测到有5次失败则认为服务是`未就绪`
readinessProbe: # kubernetes认为该pod是启动成功的
httpGet:
path: /
port: 80
scheme: HTTP # 链接格式样式
initialDelaySeconds: 30 # 等于应用程序的最小启动时间
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
resources:
requests:
cpu: 50m
memory: 200Mi
limits:
cpu: 500m
memory: 500Mi
env:
- name: PROFILE
value: "test"
ports:
- name: http
containerPort: 80
k8s将会给应用发送 SIGTERM 信号,可以用来正确、优雅地关闭应用,默认为30秒。
如果需要更优雅地关闭,则可以使用k8s提供的pre-stop lifecycle hook 的配置声明,将会在发送SIGTERM之前执行。
livenessProbe是kubernetes认为该pod是存活的,不存在则需要kill掉,然后再新启动一个,以达到replicas指定的个数。
readinessProbe是kubernetes认为该pod是启动成功的,这里根据每个应用的特性,自己去判断,可以执行command,也可以进行httpGet。比如对于使用java web服务的应用来说,并不是简单地说tomcat启动成功就可以对外提供服务的,还需要等待spring容器初始化,数据库连接连接上等等。对于spring boot应用,默认的actuator带有/health接口,可以用来进行启动成功的判断。
其中readinessProbe.initialDelaySeconds可以设置为系统完全启动起来所需的最少时间,livenessProbe.initialDelaySeconds可以设置为系统完全启动起来所需的最大时间+若干秒。
这几个参数配置好了之后,基本就可以实现近乎无缝地平滑升级了。对于使用服务发现的应用来说,readinessProbe可以去执行命令,去查看是否在服务发现里头应该注册成功了,才算成功。
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend,dev,test]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
cpu: 0.1
memory: 32Mi
limits:
cpu: 0.5
memory: 32Mi
可以通过命令 kubectl get deployment 来查看 Deployment 的信息,其中的几个参数解释如下:
Pod 的管理对象,除了 RC、ReplicaSet、Deployment,还有 DaemonSet、StatefulSet、Job 等,分别用于不同的应用场景。
[root@k8s-master ~]# kubectl get deployment --namespace=scm
[root@k8s-master ~]# kubectl get deployment --namespace=scm -o wide
[root@k8s-master ~]# kubectl get deployment --namespace=scm -o yaml
[root@k8s-master ~]# kubectl create deployment nginx-deployment --image=nginx:1.14
[root@k8s-master ~]# kubectl set image deployment nginx-deployment nginx=nginx:1.13 --record
[root@k8s-master ~]# kubectl edit deployment nginx-deployment --record
[root@k8s-master ~]# kubectl rollout pause deployment nginx-deployment
[root@k8s-master ~]# kubectl rollout resume deployment nginx-deployment
[root@k8s-master ~]# kubectl rollout undo deployment nginx-deployment
[root@k8s-master ~]# kubectl rollout history deployments --namespace=scm
[root@k8s-master ~]# kubectl rollout undo deployment nginx-deployment --to-revision=2
[root@k8s-master ~]# kubectl describe deployment nginx-deployment --namespace=scm
[root@k8s-master ~]# kubectl rollout status deployment nginx-deployment --namespace=scm
[root@k8s-master ~]# vim deployment_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12.2
ports:
- containerPort: 80
[root@k8s-master ~]# kubectl create -f deployment_nginx.yml
[root@k8s-master ~]# kubectl get deployment
[root@k8s-master ~]# kubectl get rs
[root@k8s-master ~]# kubectl get pods
[root@k8s-master ~]# kubectl get deployment -o wide
[root@k8s-master ~]# kubectl set image deployment nginx-deployment nginx=nginx:1.13 --record
[root@k8s-master ~]# kubectl get deployment
[root@k8s-master ~]# kubectl get deployment -o wide
[root@k8s-master ~]# kubectl get pods
[root@k8s-master ~]# kubectl rollout history deployment nginx-deployment
[root@k8s-master ~]# kubectl rollout undo deployment nginx-deployment
[root@k8s-master ~]# kubectl get node
[root@k8s-master ~]# kubectl get node -o wide
[root@k8s-master ~]# kubectl expose deployment nginx-deployment --port=80 #指定要暴露的端口
#查看node节点暴露的端口
[root@k8s-master ~]# kubectl get svc
[root@k8s-master ~]# vim httpd.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: httpd
spec:
replicas: 3
selector:
matchLabels:
run: httpd
template:
metadata:
labels:
run: httpd
spec:
containers:
- name: httpd
image: httpd:2.2.31
ports:
- containerPort: 80
[root@k8s-master ~]# kubectl apply -f httpd.yml
[root@k8s-master ~]# kubectl get deployments httpd -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd 3/3 1 3 24m httpd httpd:2.2.31 run=httpd
[root@k8s-master ~]# kubectl set image deployment httpd httpd=httpd:2.2.32 --record
[root@k8s-master ~]# kubectl apply -f httpd.yml
[root@k8s-master ~]# kubectl get deployments httpd -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd 3/3 1 3 26m httpd httpd:2.2.32 run=httpd
[root@k8s-master ~]# kubectl describe deployments httpd
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal ScalingReplicaSet 3m33s deployment-controller Scaled up replica set httpd-94c4dcb56 to 1
Normal ScalingReplicaSet 2m48s deployment-controller Scaled down replica set httpd-8c6c4bd9b to 2
Normal ScalingReplicaSet 2m48s deployment-controller Scaled up replica set httpd-94c4dcb56 to 2
Normal ScalingReplicaSet 2m43s deployment-controller Scaled down replica set httpd-8c6c4bd9b to 1
Normal ScalingReplicaSet 2m43s deployment-controller Scaled up replica set httpd-94c4dcb56 to 3
Normal ScalingReplicaSet 2m38s deployment-controller Scaled down replica set httpd-8c6c4bd9b to 0
事件信息就描述了滚动升级的过程:
这就是滚动的意思,始终保持副本数量为3,控制新旧 pod 的交替,实现了无缝升级。
kubectl apply 每次更新应用时,kubernetes 都会记录下当前的配置,保存为一个 revision,这样就可以回滚到某个特定的版本。
创建3个配置文件,内容中唯一不同的就是镜像的版本号。
创建 v1
[root@k8s-master ~]# vim httpd.v1.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: httpd
spec:
revisionHistoryLimit: 10 # 指定保留最近的几个revision
replicas: 3
selector:
matchLabels:
run: httpd
template:
metadata:
labels:
run: httpd
spec:
containers:
- name: httpd
image: httpd:2.2.34
ports:
- containerPort: 80
[root@k8s-master ~]# vim httpd.v2.yml
...
image: httpd:2.4.10
...
...
image: httpd:2.4.11
...
[root@k8s-master ~]# kubectl apply -f /vagrant/httpd.v1.yml --record
[root@k8s-master ~]# kubectl apply -f /vagrant/httpd.v2.yml --record
[root@k8s-master ~]# kubectl apply -f /vagrant/httpd.v3.yml --record
–record 的作用是将当前命令记录到 revision 中,可以知道每个 revision 对应的是哪个配置文件。
查看 deployment
[root@k8s-master ~]# kubectl get deployments -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
httpd 0/3 1 0 4m4s httpd httpd:2.4.12
[root@k8s-master ~]# kubectl rollout history deployment httpd
deployment.extensions/httpd
REVISION CHANGE-CAUSE
1 kubectl apply --filename=httpd.v1.yml --record=true
2 kubectl apply --filename=httpd.v2.yml --record=true
3 kubectl apply --filename=httpd.v3.yml --record=true
CHANGE-CAUSE 就是 --record 的结果。
回滚到 revision 1
[root@k8s-master ~]# kubectl rollout undo deployment httpd --to-revision=1
[root@k8s-master ~]# kubectl get deployments -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
httpd 0/3 1 0 4m4s httpd httpd:2.2.34
[root@k8s-master ~]# kubectl rollout history deployment httpd
deployment.extensions/httpd
REVISION CHANGE-CAUSE
2 kubectl apply --filename=httpd.v2.yml --record=true
3 kubectl apply --filename=httpd.v3.yml --record=true
4 kubectl apply --filename=httpd.v1.yml --record=true
Horizontal Pod Autoscaler(HPA)POD 横向自动扩展
HPA 与 RC、Deployment 一样,也属于 Kubernetes 资源对象。
通过追踪分析 RC 或 RS 控制的所有目标 Pod 的负载变化情况,来确定是否需要针对性地调整目标 Pod 的副本数。
HPA 有以下两种方式作为 Pod 负载的度量指标:
CPU Utilization Percentage 是一个算术平均值,即目标 Pod 所有副本自带的 CPU 利用率的平均值。
Pod 自身的 CPU 利用率是该 Pod 当前 CPU 的使用量除以它的Pod Request 的值,比如我们定义一个 Pod 的 Pod Request 为 0.4,而当前 Pod 的 CPU 使用量为 0.2,则它的 CPU 使用率为 50%,这样就可以算出来一个 RC 或 RS 控制的所有 Pod 副本的 CPU 利用率的算术平均值。
如果某一时刻CPU Utilization Percentage的值超过 80%,则意味着当前的 Pod 副本数很可能不足以支撑接下来更多的请求,需要进行动态扩容,而当请求高峰时段过去后,Pod 的 CPU 利用率又会降下来,此时对应的Pod副本数应该自动减少到一个合理的水平。
支持对象:DeploymentConfig、ReplicationController、Deployment、Replica Set
RS可以通过HPA来根据一些运行时指标实现自动伸缩,下面是一个简单的例子:(需要安装 metrics-server)
# 下载 metrics-server 修改配置
- name: metrics-server
image: mirrorgooglecontainers/metrics-server-amd64:v0.3.6
args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
replicas: 1
selector:
matchLabels:
app: php-apache
template:
metadata:
name: php-apache
labels:
app: php-apache
spec:
containers:
- name: php-apache
image: siriuszg/hpa-example
resources:
requests:
cpu: 20m
ports:
- containerPort: 80
apiVersion: v1
kind: Service
metadata:
name: php-apache
spec:
ports:
- port: 80
selector:
app: php-apache
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 50
[root@k8s-master ~]# kubectl apply -f dep.yaml
[root@k8s-master ~]# kubectl apply -f svc.yaml
[root@k8s-master ~]# kubectl apply -f hpa.yaml
apiVersion: v1
kind: Pod
metadata:
name: load-generator
spec:
containers:
- name: busybox
image: busybox
command: ["sleep", "3600"]
while true; do wget -q -O- http://php-apache > /dev/null; done
# 增加负载
[root@k8s-master ~]# kubectl run -i --tty load-generator --image=busybox:latest /bin/sh
# 进入容器后执行一下命令
while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done
# 开始查看状态
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 5%/50% 1 10 10 6m5s
# 过1分钟左右再次检查HPA状态和部署状态
[root@k8s-master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 460%/50% 1 10 10 4m25s
# 开始 pod 数量状态
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
load-generator 1/1 Running 0 41m
php-apache-7ddb67b575-c8vcd 1/1 Running 0 2m40s
# 1 分钟后pod数量状态
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
load-generator 1/1 Running 0 43m
php-apache-7ddb67b575-b2qkz 1/1 Running 0 2m53s
php-apache-7ddb67b575-c8vcd 1/1 Running 0 5m23s
php-apache-7ddb67b575-cpjjq 1/1 Running 0 2m37s
php-apache-7ddb67b575-p7rw9 1/1 Running 0 2m22s
php-apache-7ddb67b575-pbrzf 1/1 Running 0 2m53s
php-apache-7ddb67b575-pvmg9 1/1 Running 0 2m22s
php-apache-7ddb67b575-sw82k 1/1 Running 0 2m37s
php-apache-7ddb67b575-tk6tn 1/1 Running 0 2m53s
php-apache-7ddb67b575-xxgxw 1/1 Running 0 2m37s
php-apache-7ddb67b575-z2mdm 1/1 Running 0 2m37s
[root@k8s-master ~]# kubectl get deployment php-apache
# 停压,等1分钟查看状态
查看hpa状态已经超过了阈值,此时查看pod,pod的数量已经增加了。
当停止压力测试之后,过一段时间,Pod数量又会恢复到1。
Volume 是 Pod 中能够被多个容器访问的共享目录。Volume 定义在 Pod 上,被一个 Pod 里的多个容器挂载到具体的文件目录下,当容器终止或者重启时,Volume (volume 类型,临时性和持久性)中的数据也不会丢失。
Kubernetes 支持多种类型的 Volume,例如 本地卷、NFS、GlusterFS、Ceph 等分布式文件系统。
emptyDir 是在 Pod 分配到 Node 时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,它是 Kubernetes 自动分配的一个目录,当 Pod 从 Node 上移除时,emptyDir 中的数据也会被永久删除。
emptyDir 的用途
emptyDir 的定义
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- mountPath: /mydata-data
name: datavol
volumes:
- name: datavol
emptyDir: {}
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
cpu: 0.1
limits:
cpu: 0.2
volumeMounts:
- mountPath: /mydata-data
name: datavol
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
requests:
cpu: 0.1
limits:
cpu: 0.2
volumeMounts:
- mountPath: /mydata-data
name: datavol
volumes:
- name: datavol
emptyDir: {}
# 使用内存作为临时卷
volumes:
- name: datavol
emptyDir:
medium: Memory
需要使用内存作为 emptyDir的可用存储资源也是可以的,只需要在创建 emptyDir卷时增加一个 emptyDir.medium 字段的定义,并赋值为 “Memory” 即可。
使用 tmpfs 文件系统作为 emptyDir 的存储后端时,如果遇到 node 节点重启,则 emptyDi r中的数据也会全部丢失。同时,你编写的任何文件也都将计入 Container 的内存使用限制,即 emptydir 方式保存的数据不能持久化。
使用 hostPath 挂载宿主机上的文件或目录,主要用于以下几个方面:
使用 hostPath 时,需要注意以下两点
hostPath 的定义
volumes:
- name: "persistent-storage"
hostPath:
path: "/data"
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
cpu: 0.1
limits:
cpu: 0.2
volumeMounts:
- mountPath: /mydata-data
name: datavol
volumes:
- name: datavol
hostPath:
path: "/data"
本地卷不提供pod的亲和性,即host path映射的目录在k8s-node01,而pod可能被调度到k8s-node02,导致原来的在k8s-node01的数据不存在,pod一直无法启动起来。
能够提供pv/pvc/storage class的方式使用。
数据能持久化。
使用这种类型的 Volume 表示使用谷歌公有云提供的永久磁盘(Persistent Disk,PD)存放数据,使用 gcePersistentDisk 有以下一些限制条件:
通过 gcloud 命令创建一个PD:
gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
volumes:
- name: test-volume
gcPersistentDisk:
pdName: my-data-disk
fsType: ext4
与 GCE 类似,该类型的 Volume 使用亚马逊公有云提供的 EBS Volume 存储数据,需要先创建一个 EBS Volume 才能使用 awsElasticBlockStore。
使用 awsElasticBlockStore的一些限制条件
通过 aws ec2 create-volume 命令创建一个 EBS volume
aws ec2 create-volume --availability-zone eu-west-la --size 10 --volume-type gp2
volumes:
- name: test-volume
awsElasticBlockStore:
volumeID: aws://-zone>/-id>
fsType: ext4
[root@nfs ~]# vim /etc/hosts
192.168.122.59 nfs
192.168.122.85 client
[root@nfs ~]# yum -y install nfs-utils
[root@nfs ~]# mkdir /data # 存储目录
[root@nfs ~]# vim /etc/exports
/data 192.168.122.0/24(rw,sync,no_root_squash) # 不压制root(当client端使用root挂载时,也有root权限)
[root@nfs ~]# systemctl start nfs-server
[root@nfs ~]# systemctl enable nfs-server
[root@nfs ~]# exportfs -v
/data 192.168.122.0/24(rw,wdelay,no_root_squash,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)
[root@client ~]# vim /etc/hosts
192.168.122.59 nfs
192.168.122.85 client
[root@client ~]# yum -y install nfs-utils
# 1. 验证存储端共享
[root@client ~]# showmount -e nas
Export list for nas:
/data 192.168.122.0/24
# 2、挂载方式
# 手动挂载
[root@client ~]# mount -t nfs nas:/data /data
# 开机自动挂载
[root@client ~]# vim /etc/fstab
nas:/data /data nfs defaults 0 0
[root@client ~]# mount -a
# 查看挂载
[root@client ~]# df
nas:/data 7923136 692416 6821568 10% /data
# 卸载
[root@client ~]# umount /data
volumes:
- name: nfs-volume
nfs:
server: nfs-server.localhost
path: "/"
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
cpu: 0.1
limits:
cpu: 0.2
volumeMounts:
- mountPath: /mydata-data # 容器内部的挂载目录
name: datavol
subPath: newdir # 挂载后创建一个子目录
volumes:
- name: datavol
nfs:
server: 192.168.152.193 # nfs 服务器地址(或域名)
path: "/data" # nfs服务器的数据目录
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: nginx1
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: app-logs
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
volumeMounts:
- name: app-logs
mountPath: /opt
volumes:
- name: datavol
nfs:
server: 192.168.152.193 # nfs 服务器地址(或域名)
path: "/data" # nfs服务器的数据目录
[root@k8s-master ~]# kubectl exec -it volume-pod --container busybox -- /bin/sh
# 创建 html 文件
管理存储是管理计算的一个明显问题。该PersistentVolume子系统为用户和管理员提供了一个API,用于抽象如何根据消费方式提供存储的详细信息。为此,引入了两个新的API资源:PersistentVolume 和 PersistentVolumeClaim
PersistentVolumeClaims 允许用户使用抽象存储资源,但是PersistentVolumes对于不同的问题,用户通常需要具有不同属性(例如性能)。群集管理员需要能够提供各种PersistentVolumes的不同配置方式,而不仅仅是大小和访问模式,对于用户还要实现透明化,不让用户了解这些卷的实现方式。需要满足这些需求需要新的资源类型承担,StorageClass 资源就提供了动态获取存储卷的方法。
StorageClass 为管理员提供了一种描述动态提供的存储的“类”的方法。 不同的类可能映射到服务质量级别,或备份策略,或者由群集管理员确定的任意策略。
Kubernetes 中 StorageClass 是一中如何动态获取持久卷的方法,包含获取持久卷的配置,策略。 这个概念可以理解为存储系统中获取存储的“配置文件”。
Kubernetes 中 Volume 是定义在 Pod 上的,属于“计算资源”的一部分,“网络存储”是相对独立于“计算资源”而存在的一种实体资源。在使用主机的情况下,通常会先创建一个网络存储,然后从中划出一个“网盘”并挂载到主机上
PV 与 Volume 的区别
PV是群集中的资源。PVC是对这些资源的请求,并且还充当对资源的检查。PV和PVC之间的相互作用遵循以下生命周期:
Provisioning ——-> Binding ——–>Using——>Releasing——>Recycling
注:目前只有 NFS 和 HostPath 类型卷支持回收策略,AWS EBS,GCE PD,Azure Disk和Cinder支持删除(Delete)策略。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somepath
server: 172.17.0.2
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
[root@nfs ~]# cd /data/volumes/
[root@nfs volumes]# mkdir v{1,2,3,4,5}
[root@nfs volumes]# ls
index.html v1 v2 v3 v4 v5
[root@nfs volumes]# echo "NFS stor 01
" > v1/index.html
[root@nfs volumes]# echo "NFS stor 02
" > v2/index.html
[root@nfs volumes]# echo "NFS stor 03
" > v3/index.html
[root@nfs volumes]# echo "NFS stor 04
" > v4/index.html
[root@nfs volumes]# echo "NFS stor 05
" > v5/index.html
[root@nfs volumes]# vim /etc/exports
/data/volumes/v1 192.168.130.0/24(rw,no_root_squash)
/data/volumes/v2 192.168.130.0/24(rw,no_root_squash)
/data/volumes/v3 192.168.130.0/24(rw,no_root_squash)
/data/volumes/v4 192.168.130.0/24(rw,no_root_squash)
/data/volumes/v5 192.168.130.0/24(rw,no_root_squash)
[root@nfs volumes]# exportfs -arv
exporting 192.168.130.0/24:/data/volumes/v5
exporting 192.168.130.0/24:/data/volumes/v4
exporting 192.168.130.0/24:/data/volumes/v3
exporting 192.168.130.0/24:/data/volumes/v2
exporting 192.168.130.0/24:/data/volumes/v1
[root@nfs volumes]# showmount -e
Export list for nfs:
/data/volumes/v5 192.168.130.0/24
/data/volumes/v4 192.168.130.0/24
/data/volumes/v3 192.168.130.0/24
/data/volumes/v2 192.168.130.0/24
/data/volumes/v1 192.168.130.0/24
[root@k8s-master volumes]# vim pv-damo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001
labels:
name: pv001
spec:
nfs:
path: /data/volumes/v1
server: nfs
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv002
labels:
name: pv002
spec:
nfs:
path: /data/volumes/v2
server: nfs
accessModes: ["ReadWriteOnce"]
capacity:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
labels:
name: pv003
spec:
nfs:
path: /data/volumes/v3
server: nfs
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 20Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv004
labels:
name: pv004
spec:
nfs:
path: /data/volumes/v4
server: nfs
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv005
labels:
name: pv005
spec:
nfs:
path: /data/volumes/v5
server: nfs
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 15Gi
[root@k8s-master volumes]# kubectl apply -f pv-damo.yaml
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 5Gi RWO,RWX Retain Available 9s
pv002 5Gi RWO Retain Available 9s
pv003 5Gi RWO,RWX Retain Available 9s
pv004 10Gi RWO,RWX Retain Available 9s
pv005 15Gi RWO,RWX Retain Available 9s
[root@k8s-master volumes]# vim vol-pvc-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: default
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 6Gi
---
apiVersion: v1
kind: Pod
metadata:
name: vol-pvc
namespace: default
spec:
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
[root@k8s-master volumes]# kubectl apply -f vol-pvc-demo.yaml
persistentvolumeclaim/mypvc created
pod/vol-pvc created
[root@k8s-master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound pv004 10Gi RWO,RWX 24s
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 5Gi RWO,RWX Retain Available 1m
pv002 5Gi RWO Retain Available 1m
pv003 5Gi RWO,RWX Retain Available 1m
pv004 10Gi RWO,RWX Retain Bound default/mypvc 1m
pv005 15Gi RWO,RWX Retain Available 1m
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
vol-pvc 1/1 Running 0 59s 10.244.2.117 k8s-node02
[root@k8s-master ~]# curl 10.244.2.117
<h1>NFS stor 04</h1>
[root@k8s-master ~]# kubectl patch pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Recycle"}}'
# 保留(Retain),回收(Recycle)和删除(Delete)。
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 2Gi RWO,RWX Retain Available 35m
pv002 5Gi RWO Retain Available 35m
pv003 6Gi RWO,RWX Recycle Available 35m
pv004 8Gi RWO,RWX Retain Available 35m
pv005 10Gi RWO,RWX Retain Available 35m
生成为容器内的环境变量
设置容器启动命令的启动参数(需设置为环境变量)
以 volume 的形式挂载为容器内部的文件或目录
[root@k8s-master ~]# kubectl create configmap special-config --from-literal=special.how=very
configmap "special-config" created
[root@k8s-master ~]# kubectl get configmap special-config -o go-template='{{.data}}'
map[special.how:very]
[root@k8s-master ~]# echo -e "a=b\nc=d" | tee config.env
a=b
c=d
[root@k8s-master ~]# kubectl create configmap special-config --from-env-file=config.env
configmap "special-config" created
[root@k8s-master ~]# kubectl get configmap special-config -o go-template='{{.data}}'
map[a:b c:d]
[root@k8s-master ~]# mkdir config
[root@k8s-master ~]# echo a>config/a
[root@k8s-master ~]# echo b>config/b
[root@k8s-master ~]# kubectl create configmap special-config --from-file=config/
configmap "special-config" created
[root@k8s-master ~]# kubectl get configmap special-config -o go-template='{{.data}}'
map[a:a
b:b
]
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm
[root@k8s-master ~]# kubectl create -f config.yaml
configmap "special-config" created
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appvars
data:
apploglevel: info
appdatadir: /var/data
# 创建configmap
[root@k8s-master ~]# kubectl create -f cm-appvars.yaml
# 查看configmap
[root@k8s-master ~]# kubectl describe configmap
[root@k8s-master ~]# kubectl describe configmap cm-appvars
apiVersion: v1
kind: Pod
metadata:
name: cm-test-pod
spec:
containers:
- name: cm-test
image: busybox
command: ["/bin/sh","-c","sleep 3600"]
env:
- name: APPLOGLEVEL
valueFrom:
configMapKeyRef:
name: cm-appvars # 要和之前创建的ConfigMap的name对应
key: apploglevel
- name: APPDATADIR
valueFrom:
configMapKeyRef:
name: cm-appvars # 要和之前创建的ConfigMap的name对应
key: appdatadir
[root@k8s-master ~]# kubectl apply -f configmap-pod.yaml
pod/cm-test-pod created
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cm-test-pod 1/1 Running 0 11s
[root@k8s-master ~]# kubectl exec -it cm-test-pod /bin/sh
/ # ls
bin dev etc home proc root sys tmp usr var
/ # echo $APPLOGLEVEL
info
/ # echo $APPDATADIR
/var/date
除了可以定义简单的 k-v 键值对,还可以将整个配置文件定义成 ConfigMap
比如 server.xml logging.properties(使用 volumeMount 的形式,挂载到容器内部)
[root@k8s-master ~]# kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
[root@k8s-master ~]# kubectl create configmap env-config --from-literal=log_level=INFO
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: busybox
command: ["/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
- name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.type
restartPolicy: Never
apiVersion: v1
kind: Pod
metadata:
name: vol-test-pod
spec:
containers:
- name: test-container
image: busybox
command: ["/bin/sh", "-c", "cat /etc/config/special.how"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: special-config
restartPolicy: Never
Pod 结束后会输出 very
将创建的 ConfigMap 中 special.how 这个 key 挂载到 / etc/config 目录下的一个相对路径 / keys/special.level。如果存在同名文件,直接覆盖。其他的 key 不挂载
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: ["/bin/sh","-c","cat /etc/config/keys/special.level"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: special-config
items:
- key: special.how
path: keys/special.level
restartPolicy: Never
Pod 结束后会输出 very
ConfigMap 支持同一个目录下挂载多个 key 和多个目录。例如下面将 special.how 和special.type 通过挂载到 / etc/config 下。并且还将 special.how 同时挂载到 / etc/config2下。
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: ["/bin/sh","-c","sleep 36000"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
- name: config-volume2
mountPath: /etc/config2
volumes:
- name: config-volume
configMap:
name: special-config
items:
- key: special.how
path: keys/special.level
- key: special.type
path: keys/special.type
- name: config-volume2
configMap:
name: special-config
items:
- key: special.how
path: keys/special.level
restartPolicy: Never
# ls /etc/config/keys/
special.level special.type
# ls /etc/config2/keys/
special.level
# cat /etc/config/keys/special.level
very
# cat /etc/config/keys/special.type
charm
使用 subpath 将 ConfigMap 作为单独的文件挂载到目录
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: nginx
command: ["/bin/sh","-c","sleep 36000"]
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/special.how
subPath: special.how
volumes:
- name: config-volume
configMap:
name: special-config
items:
- key: special.how
path: special.how
restartPolicy: Never
root@dapi-test-pod:/# ls /etc/nginx/
conf.d fastcgi_params koi-utf koi-win mime.types modules ng
inx.conf scgi_params special.how uwsgi_params win-utf
root@dapi-test-pod:/# cat /etc/nginx/special.how
very
root@dapi-test-pod:/#
[root@k8s-master ~]# vim cm-index.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: index-configmap
data:
key-index.html: |
hello world!
[root@k8s-master ~]# kubectl create -f cm-index.yaml
[root@k8s-master ~]# vim nginx-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: cm-test-app
spec:
containers:
- name: cm-test-app
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: index # 应用下面定义的 volumes名
mountPath: /usr/share/nginx/html
volumes:
- name: index # volumes名
configMap:
name: index-configmap # 这个名字是第二步创建的 configMap
items:
- key: key-index.html
path: index.html # 最终实际配置文件名称 my.cnf
Kubernetes 守护程序集 DaemonSet,DaemonSet 保证在每个 Node 上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用
DaemonSet 的一些典型用法
系统程序,比如 kube-proxy, kube-dns, glusterd, ceph 等
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
selector:
matchLabels:
k8s-app: fluentd-cloud-logging
template:
metadata:
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
containers:
- name: fluentd-cloud-logging
image: fluent/fluentd
resources:
limits:
cpu: 100m
memory: 200Mi
env:
- name: FLUENTD_ARGS
value: -q
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: false
- name: containers
mountPath: /var/lib/docker/containers
volumes:
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log
# 查看
[root@k8s-master ~]# kubectl get daemonsets.apps fluentd-cloud-logging --namespace kube-system
[root@k8s-master ~]# kubectl get pods --namespace kube-system
# 查询历史版本
[root@k8s-master ~]# kubectl rollout history daemonset
# 查询某个历史版本的详细信息
[root@k8s-master ~]# kubectl rollout history daemonset --revision=1
# 回滚
[root@k8s-master ~]# kubectl rollout undo daemonset --to-revision=
# 查询回滚状态
[root@k8s-master ~]# kubectl rollout status ds/
[root@k8s-master ~]# kubectl label nodes node-01 disktype=ssd
spec:
nodeSelector:
disktype: ssd
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: gcr.io/google_containers/pause:2.0
[root@k8s-master ~]# kubelet --pod-manifest-path=/etc/kubernetes/manifests
然后将所需要的 Pod 定义文件放到指定的 manifest 目录中。
注意:静态 Pod 不能通过 API Server 来删除,但可以通过删除 manifest 文件来自动删除对应的 Pod。
Kubernetes job资源对象用于定义并启动一个批处理任务。
Job 负责批量处理短暂的一次性任务 (short lived one-off tasks),即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束
API 版本对照表
Kubernetes 版本 | Batch API 版本 | 默认开启 |
---|---|---|
v1.5+ | batch/v1 | 是 |
Job 类型 | 使用示例 | 行为 | completions | Parallelism |
---|---|---|---|---|
一次性Job | 数据库迁移 | 创建一个 Pod 直至其成功结束 | 1 | 1 |
固定结束次数的Job | 处理工作队列的Pod | 依次创建一个 Pod 运行直至 completions 个成功结束 | 2+ | 1 |
固定结束次数的并行 Job | 多个 Pod同时处理工作队列 | 依次创建多个 Pod 运行直至 completions 个成功结束 | 2+ | 2+ |
并行 Job | 多个 Pod同时处理工作队列 | 创建一个或多个 Pod直至有一个成功结束 | 1 | 2+ |
spec.template格式同Pod
RestartPolicy 仅支持 Never或OnFailure
单个Pod 时,默认 Pod 成功运行后 Job 即结束
.spec.completions 标志 Job 结束需要成功运行的Pod个数,默认为1
.spec.parallelism 标志并行运行的Pod的个数,默认为1
spec.activeDeadlineSeconds 标志失败 Pod 的重试最大时间,超过这个时间不会继续重试
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
metadata:
name: pi
spec:
containers:
- name: pi
image: perl
imagePullPolicy: IfNotPresent
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
[root@k8s-master ~]# kubectl create -f ./job.yaml
job "pi" created
[root@k8s-master ~]# kubectl describe job pi
# 使用'job-name=pi'标签查询属于该 Job 的 Pod
# 注意不要忘记'--show-all'选项显示已经成功(或失败)的 Pod
[root@k8s-master ~]# kubectl get pod --show-all -l job-name=pi
# 使用 jsonpath 获取 pod ID 并查看 Pod 的日志
pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath={.items..metadata.name})
[root@k8s-master ~]# kubectl logs $pods
3.141592653589793238462643383279502...
===================================================================================
apiVersion: batch/v1
kind: Job
metadata:
name: test-job
spec:
completions: 6 # 需要运行的pod数量
parallelism: 2 # 允许并发运行的pod数量
activeDeadlineSeconds: 360 # pod运行的超时时间
template:
metadata:
labels:
app: test-job
spec:
containers:
- name: test-job
image: luksa/batch-job
imagePullPolicy: IfNotPresent
restartPolicy: OnFailure
apiVersion: batch/v1
kind: Job
metadata:
name: busybox
spec:
completions: 3
template:
metadata:
name: busybox
spec:
containers:
- name: busybox
image: busybox
command: ["echo", "hello"]
restartPolicy: Never
CronJob 用于以时间为基准周期性地执行任务,这些自动化任务和运行在Linux或UNIX系统上的CronJob一样。CronJob对于创建定期和重复任务非常有用,例如执行备份任务、周期性调度程序接口、发送电子邮件等。
CronJob 运行流程为 创建job ——> 然后job会创建pod ——> 然后执行调度 ——> 然后销毁pod 理解为 在高频率的定时任务就是不断的创建pod然后删除pod
创建CronJob有两种方式,一种是直接使用kubectl创建,一种是使用yaml文件创建。
[root@k8s-master ~]# kubectl run hello --schedule="*/1 * * * *" --restart=OnFailure --image=busybox -- /bin/sh -c "date; echo Hello from the Kubernetes cluster"
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: test-cronjob
spec:
schedule: "*/1 * * * *" # 参考定时计划任务(分时日月周)
startingDeadlineSeconds: 15 # pod必须在规定时间后的15秒内开始执行,若超过该时间未执行,则任务将不运行,且标记失败
jobTemplate:
spec:
template:
metadata:
labels:
app: test-cronjob
spec:
containers:
- name: test-job
image: busybox
args:
- "/bin/sh"
- "-c"
- "date; echo Hello from the Kubernetes cluster"
restartPolicy: OnFailure
[root@k8s-master ~]# kubectl get cronjob
[root@k8s-master ~]# kuberctl get jobs
[root@k8s-master ~]# kuberctl get pods
可以通过logs查看Pod的执行日志:
[root@k8s-master ~]# kubectl logs -f hello-1558779360-jcp4r
Sat May 25 10:16:23 UTC 2019
Hello from the Kubernetes cluster
如果要删除CronJob,直接使用delete即可:
[root@k8s-master ~]# kubectl delete cronjob hello
apiversion: batch/v1beta1
kind: CronJob
metadata:
labels:
run: hello
name: hello
namespace: default
spec:
concurrencyPolicy: Allow # 并发策略
failedJobsHistorylimit: 1 # 要保留的失败的完成作业数(默认为1)
successfulJobsHistoryLimit: 3 # 要保留的成功完成的作业数(默认为3)
schedule: "*/1 * * * *" # 作业时间表。在此示例中,作业将每分钟运行一次
startingDeadlineSeconds: 60 # job存活时间 默认不设置为永久
jobTemplate: # 作业模板。这类似于工作示例
metadata:
creationTimestamp: null
spec:
template:
metadata:
creationTimestamp: null
labels:
run: hello
spec:
containers:
args:
- "/bin/sh"
- "с"
- "date; echo Hello from the Kubernetes cluster"
image: busybox
imagePullPolicy: Always
name: hello
resourcea: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: OnFailure
reschedulerName: default-scheduler
securitvContext: {}
terminationGracePeriodSeconds: 30
spec.concurrencyPolicy(并发策略):
.spec.successfulJobsHistoryLimit 和 .spec.failedJobsHistoryLimit 这两个字段是可选的。它们指定了可以保留完成和失败 Job 数量的限制。
Service 一个应用服务抽象,定义了 Pod 逻辑集合和访问这个 Pod 集合的策略。反向代理
Service 代理 Pod 集合对外表现是为一个访问入口,分配一个集群 IP 地址及端口,来自这个 IP 的请求将被负载均衡 (kube-proxy)转发到后端 Pod 中的容器。
Service 通过 LableSelector 选择一组 Pod 提供服务。(以标签的形式标识服务)
[root@k8s-master ~]# cat deployment-hello.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: 4
selector:
matchLabels:
run: hello
template:
metadata:
labels:
run: hello
spec:
containers:
- name: hello
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
[root@k8s-master ~]# kubectl create -f deployment-hello.yaml
deployment.extensions/hello created
[root@k8s-master ~]# kubectl get rs # 因为deployment是三层架构,看rs是否成功,我们看自动创建4个rs.名称后面的字符串是 模板的哈希值。是不会发生变化的,最后pod的是随机值
NAME DESIRED CURRENT READY AGE
hello-68df45bc79 4 4 0 8m29s
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 80
targetPort: 8080
selector:
run: hello
上述内容定义了一个名为 “tomcat-service” 的 Service,它的服务端口为 8080,拥有 “tier=frontend” 这个 Label 的所有 Pod 实例。
很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如 Mycat、Codis 等常见中间件。
Kubernetes Service 支持多个 Endpoint,要求每个 Endpoint 定义一个名字来区分,下面是 tomcat 多端口的 Service 定义样例。
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
name: service-port
- port: 8005
name: shutdown-port
selector:
tie: frontend
[root@k8s-master ~]# cat service-hello.yaml
apiVersion: v1
kind: Service
metadata:
name: service-hello
labels:
name: service-hello
spec:
type: NodePort # 这里代表是NodePort类型的,另外还有ingress,LoadBalancer
ports:
- port: 80 # 这里的端口和clusterIP(kubectl describe service service-hello中的IP的port)对应,即在集群中所有机器上curl 10.98.166.242:80可访问发布的应用服务。
targetPort: 8080 # 端口一定要和container暴露出来的端口对应,tomcat暴露出来的端口是8080,所以这里也应是8080
protocol: TCP
nodePort: 31111 # 所有的节点都会开放此端口30000--32767,此端口供外部调用。
selector:
run: hello # 这里选择器一定要选择容器的标签,
2、Kubernetes 的服务发现机制
每个 Kubernetes 中的 Service 都有一个唯一的 Cluster IP 及唯一的名字,而名字是自定义的,可以用服务名访问服务
最早时 Kubernetes 采用了 Linux 环境变量的方式来实现,即每个 Service 生成一些对应的 Linux 环境变量(ENV),并在每个Pod的容器启动时,自动注入这些环境变量,以实现通过 Service 的名字来建立连接的目的。
考虑到通过环境变量获取 Service 的 IP 与端口的方式仍然不方便、不直观,后来 Kubernetes 通过 Add-On 增值包的方式引入了 DNS 系统,把服务名作为 DNS 域名,这样程序就可以直接使用服务名来建立连接了。
3、外部系统访问 Service 的问题
Kubernetes集群里有三种IP地址,分别如下:
外部访问 Kubernetes 集群里的某个节点或者服务时,必须要通过 Node IP 进行通信。
Pod IP 是 Docker Engine 根据 flannel.1 网桥的 IP 地址段进行分配的一个虚拟二层网络IP地址,
Pod 与 Pod 之间的访问就是通过这个虚拟二层网络进行通信的,而真实的TCP/IP 流量则是通过 Node IP 所在的物理网卡流出的。
Service 的 Cluster IP 具有以下特点:
Node IP、Pod IP 和 Cluster IP 之间的通信,采用的是 Kubernetes 自己设计的一种特殊的路由规则,与IP 路由有很大的区别。
如果想让外部访问应用,最常用的作法是使用 NodePort 方式。
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31002
selector:
tier: frontend
NodePort 的实现方式是在 Kubernetes 集群里的每个 Node 上为需要外部访问的 Service 开启一个对应的 TCP 监听端口,外部系统只要用任意一个 Node 的 P 地址+具体的 NodePort 端口号即可访问此服务。
NodePort 还没有完全解决外部访问Service的所有问题,比如负载均衡问题,常用的做法是在 Kubernetes 集群之外部署一个负载均衡器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDRLT24d-1610418329803)(assets/cceea13a51ed6faab6709667f36d1a27.png)]
Load balancer 组件独立于 Kubernetes 集群之外,可以是一个硬件负载均衡器,也可以是软件方式实现,例如 HAProxy 或者 Nginx。这种方式,无疑是增加了运维的工作量及出错的概率。
Kubernetes 提供了自动化的解决方案,如果使用谷歌的 GCE 公有云,只需要将 type: NodePort 改成 type: LoadBalancer,此时 Kubernetes 会自动创建一个对应的 Load balancer 实例并返回它的 IP 地址供外部客户端使用。其他公有云提供商只要实现了支持此特性的驱动,则也可以达到上述目的。
Pod 的管理对象 RC、Deployment、DaemonSet 和 Job 都是面向无状态的服务,但实际中有很多服务是有状态的,比如 Mysql集群、MongoDB集群、ZooKeeper集群等,可以使用 StatefulSet 来管理有状态的服务。
StatefulSet 是Kubernetes中的一种控制器,他解决的什么问题呢?
StatefulSet 里的每个 Pod 都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设 StatefulSet 的名字叫 kafka,那么第1个 Pod 叫 kafka-0,第2个叫kafka-1,以此类推。
StatefulSet 控制的 Pod 副本的启停顺序是受控的,操作第 n 个 Pod 时,前 n-1 个 Pod 已经是运行且准备好的状态。
StatefulSet 里的 Pod 采用稳定的持久化存储卷,通过 PV/PVC 来实现,删除 Pod 时默认不会删除与 StatefulSet 相关的存储卷(为了保证数据的安全)。
StatefulSet的核心功能就是,通过某种方式记录应用状态,在Pod被重建的时候,通过这种方式还可以恢复原来的状态。
StatefulSet由以下几个部分组成:
稳定且有唯一的网络标识符 当节点挂掉,既pod重新调度后其PodName和HostName不变,基于Headless Service来实现
稳定且持久的存储 当节点挂掉,既pod重新调度能访问到相同的持久化存储,基于PVC实现
有序、平滑的扩展、部署 即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。
有序、平滑的收缩、删除 既Pod是有顺序的,在收缩或者删除的时候要依据定义的顺序依次进行(既从N-1到0,既倒序)。
**拓扑状态。**是应用多个实例之间的不完全对等的关系,这些应用实例是必须按照定义的顺序启动的。例如主应用A先于从应用B启动,如果把A和B删掉,应用还要按照先启动主应用A再启动从应用B,且创建的应用必须和原来的应用的网络标识一样(既PodName和HostName)。这样他们就可以按照原来的顺序创建了。
存储状态。应用实例分别绑定了不同的数据存储,Pod A第一次读到的数据要和10分钟后读到的数据,是同一份。哪怕这期间Pod A被重建。这种典型的例子就是数据库应用的多个存储实例。
pod-name.svc-name.namespace.svc.cluster.local
比如一个 3 节点的 kafka 的 StatefulSet 集群,对应的 Headless Service 的名字为 kafka,StatefulSet 的名字为 kafka,则 StatefulSet 里面的 3 个 Pod 的 DNS 名称分别为kafka-0.kafka、kafka-1.kafka、kafka-2.kafka,这些 DNS 名称可以直接在集群的配置文件中固定下来。
通过headless service 可以轻松找到statefulSet 的所有节点,只要访问"kafka-0.kafka.namespace.svc.cluster.local",就会访问到kafka下的kafka-0。
Service DNS的方式下有两种处理方法:
Headleaa Service的定义方式:
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
release: dev
clusterIP: "None"
ports:
- port: 80
targetPort: 80
kubectl explain sts.spec 主要字段解释:
StatefulSet应用特点:
PersistentVolume(PV)是指由集群管理员配置提供的某存储系统上的段存储空间,它是对底层共享存储的抽象,将共享存储作为种可由用户申请使的资源,实现了“存储消费”机制。
PV 通过存储插件机制,PV支持使用多种网络存储系统或云端存储等多种后端存储系统,例如,NFS、RBD和Cinder等。
PV 是集群级别的资源,不属于任何名称空间,用户对PV资源的使需要通过PersistentVolumeClaim(PVC)提出的使申请(或称为声明)来完成绑定,是PV资源的消费者,它向PV申请特定大小的空间及访问模式(如rw或ro),从创建出PVC存储卷,后再由Pod资源通过PersistentVolumeClaim存储卷关联使,
PVC 使得用户可以以抽象的方式访问存储资源,但很多时候会涉及PV的不少属性,例如,由于不同场景时设置的性能参数等,因此集群管理员不得不通过多种方式提供多种不同的PV以满不同用户不同的使用需求,两者衔接上的偏差必然会导致用户的需求无法全部及时有效地得到满足。
Kubernetes从1.4版起引入了一个新的资源对象StorageClass,可用于将存储资源定义为具有显著特性的类(Class)而不是具体的PV,例如 “fast” “slow” 或 “glod” “silver” “bronze”等。
StorageClass 使用户通过PVC直接向意向的类别发出申请,匹配由管理员事先创建的PV,或者由其按需为用户动态创建PV,这样做甚至免去了需要先创建PV的过程。
PV对存储系统的支持可通过其插件来实现,目前,Kubernetes支持如下类型的插件。官方地址:https://kubernetes.io/docs/concepts/storage/storage-classes/
官方插件是不支持NFS动态供给的,可以用第三方的插件来实现。
#master节点安装nfs
[root@k8s-master ~]# yum -y install nfs-utils
#创建nfs目录
[root@k8s-master ~]# mkdir -p /data/volumes/
#编辑export文件,这个文件就是nfs默认的配置文件
[root@k8s-master ~]# vim /etc/exports
/data/volumes/ 192.168.152.0/24(rw,no_root_squash,sync)
#配置生效
[root@k8s-master ~]# exportfs -r
#查看生效
[root@k8s-master ~]# exportfs
#启动rpcbind、nfs服务
[root@k8s-master ~]# systemctl restart rpcbind && systemctl enable rpcbind
[root@k8s-master ~]# systemctl restart nfs && systemctl enable nfs
#查看 RPC 服务的注册状况
[root@k8s-master ~]# rpcinfo -p localhost
#showmount测试
[root@k8s-master ~]# showmount -e 192.168.152.192
[root@k8s-node01 ~]# yum -y install nfs-utils
[root@k8s-node01 ~]# systemctl start nfs && systemctl enable nfs
1、创建 nfs 的 deployment,
[root@k8s-master storage-class]# cat deployment-nfs.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: k8s-master
- name: NFS_PATH
value: /data/volumes/v1
volumes:
- name: nfs-client-root
nfs:
server: k8s-master
path: /data/volumes/v1
[root@k8s-master storage-class]# kubectl apply -f deployment-nfs.yaml
[root@k8s-master storage-class]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-client-provisioner-75bf876d88-578lg 1/1 Running 0 51m 10.244.2.131 k8s-node2 <none> <none>
2、创建StorageClass
[root@k8s-master storage-class]# cat storageclass-nfs.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: fuseim.pri/ifs
parameters:
archiveOnDelete: "false"
[root@k8s-master storage-class]# kubectl apply -f storageclass-nfs.yaml
[root@k8s-master storage-class]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
managed-nfs-storage fuseim.pri/ifs Delete Immediate false 15m
3、配置授权
[root@k8s-master storage-class]# cat rbac.yaml
kind: ServiceAccount
apiVersion: v1
metadata:
name: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
[root@k8s-master storage-class]# kubectl create -f rbac.yaml
4、创建测试 PVC
[root@k8s-master storage-class]# kubectl create -f test-claim.yaml
[root@k8s-master storage-class]# cat test-claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
[root@k8s-master storage-class]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim Bound pvc-a17d9fd5-237a-11e9-a2b5-000c291c25f3 1Mi RWX managed-nfs-storage 34m
[root@k8s-master storage-class]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-a17d9fd5-237a-11e9-a2b5-000c291c25f3 1Mi RWX Delete Bound default/test-claim managed-nfs-storage 34m
进入到NFS的export目录查看对应volume name的目录已经创建出来。
其中volume的名字是namespace,PVC name以及uuid的组合:
[root@k8s-master storage-class]# ls /data/volumes/v1
total 0
drwxrwxrwx 2 root root 21 Jan 29 12:03 default-test-claim-pvc-a17d9fd5-237a-11e9-a2b5-000c291c25f3
5、创建测试Pod
指定pod使用刚创建的PVC:test-claim,
完成之后,如果attach到pod中执行一些文件的读写操作,就可以确定pod的/mnt已经使用了NFS的存储服务了。
[root@k8s-master storage-class]# vim test-pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox:1.24
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && exit 0 || exit 1"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim
[root@k8s-master storage-class]# kubectl create -f test-pod.yaml
[root@k8s-master storage-class]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-client-provisioner-75bf876d88-578lg 1/1 Running 0 51m 10.244.2.131 k8s-node2 <none> <none>
test-pod 0/1 Completed 0 41m 10.244.1.
[root@k8s-master storage-class]# cd /data/volumes/v1
[root@k8s-master v1]# ll
total 0
drwxrwxrwx 2 root root 21 Jan 29 12:03 default-test-claim-pvc-a17d9fd5-237a-11e9-a2b5-000c291c25f3
[root@k8s-master v1]# cd default-test-claim-pvc-a17d9fd5-237a-11e9-a2b5-000c291c25f3/
[root@k8s-master default-test-claim-pvc-a17d9fd5-237a-11e9-a2b5-000c291c25f3]# ll
total 0
-rw-r--r-- 1 root root 0 Jan 29 12:03 SUCCESS
6、清理测试环境
[root@k8s-master storage-class]# kubectl delete -f test-pod.yaml
[root@k8s-master storage-class]# kubectl delete -f test-claim.yaml
7、创建一个nginx动态获取PV
[root@k8s-master storage-class]# cat nginx-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "managed-nfs-storage"
resources:
requests:
storage: 1Gi
[root@k8s-master storage-class]# kubectl get pods,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/nfs-client-provisioner-5778d56949-ltjbt 1/1 Running 0 42m
pod/test-pod 0/1 Completed 0 36m
pod/web-0 1/1 Running 0 2m23s
pod/web-1 1/1 Running 0 2m6s
pod/web-2 1/1 Running 0 104s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-1d54bb5b-9c12-41d5-9295-3d827a20bfa2 1Gi RWO Delete Bound default/www-web-2 managed-nfs-storage 104s
persistentvolume/pvc-8cc0ed15-1458-4384-8792-5d4fd65dca66 1Gi RWO Delete Bound default/www-web-0 managed-nfs-storage 39m
persistentvolume/pvc-c924a2aa-f844-4d52-96c9-32769eb3f96f 1Mi RWX Delete Bound default/test-claim managed-nfs-storage 38m
persistentvolume/pvc-e30333d7-4aed-4700-b381-91a5555ed59f 1Gi RWO Delete Bound default/www-web-1 managed-nfs-storage 2m6s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/test-claim Bound pvc-c924a2aa-f844-4d52-96c9-32769eb3f96f 1Mi RWX managed-nfs-storage 38m
persistentvolumeclaim/www-web-0 Bound pvc-8cc0ed15-1458-4384-8792-5d4fd65dca66 1Gi RWO managed-nfs-storage 109m
persistentvolumeclaim/www-web-1 Bound pvc-e30333d7-4aed-4700-b381-91a5555ed59f 1Gi RWO managed-nfs-storage 2m6s
persistentvolumeclaim/www-web-2 Bound pvc-1d54bb5b-9c12-41d5-9295-3d827a20bfa2 1Gi RWO managed-nfs-storage 104s
[root@k8s-master storage-class]# ll /data/volumes/v1/
total 4
drwxrwxrwx 2 root root 6 Aug 16 18:21 default-nginx-web-0-pvc-ea22de1c-fa33-4f82-802c-f04fe3630007
drwxrwxrwx 2 root root 21 Aug 16 18:25 default-test-claim-pvc-c924a2aa-f844-4d52-96c9-32769eb3f96f
drwxrwxrwx 2 root root 6 Aug 16 18:21 default-www-web-0-pvc-8cc0ed15-1458-4384-8792-5d4fd65dca66
drwxrwxrwx 2 root root 6 Aug 16 18:59 default-www-web-1-pvc-e30333d7-4aed-4700-b381-91a5555ed59f
drwxrwxrwx 2 root root 6 Aug 16 18:59 default-www-web-2-pvc-1d54bb5b-9c12-41d5-9295-3d827a20bfa2
Secret 解决了密码、token、密钥,证书等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec 中。Secret 可以以 Volume 或者环境变量的方式使用。
Secret 保存 kubernetes 中小片敏感数据资源,可以更方便的控制和如何使用涉密数据,减少暴露的风险。例如密码,token,或者秘钥。
Secret 有三种类型:
內建的 Secrets
自定义 Secret
Pod 需要先引用才能使用某个secret
Pod 有2种方式来使用 secret:
Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式:
Pod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt
[root@k8s-master ~]# echo -n 'admin' > ./username.txt
[root@k8s-master ~]# echo -n '1f2d1e2e67df' > ./password.txt
# kubectl create secret指令将用户名密码写到secret中,并在apiserver创建Secret
[root@k8s-master ~]# kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret "db-user-pass" created
[root@k8s-master ~]# kubectl get secrets
NAME TYPE DATA AGE
db-user-pass Opaque 2 51s
[root@k8s-master ~]# kubectl describe secrets/db-user-pass
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
[root@k8s-master ~]# kubectl get secret db-user-pass -o json
[root@k8s-master ~]# echo -n 'admin' | base64
YWRtaW4=
[root@k8s-master ~]# echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
[root@k8s-master ~]# vim secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
[root@k8s-master ~]# kubectl create -f ./secret.yaml
secret "mysecret" created
[root@k8s-master ~]# kubectl get secret mysecret -o yaml
apiVersion: v1
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
creationTimestamp: 2016-01-22T18:41:56Z
name: mysecret
namespace: default
resourceVersion: "164619"
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
# base64解码:
[root@k8s-master ~]# echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df
[root@k8s-master ~]# vim pod_use_secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
每一个被引用的Secret都要在spec.volumes中定义
如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好。
root@mypod:/data# ls /etc/foo/
username
password
root@mypod:/data# cat /etc/foo/username
admin
root@mypod:/data# cat /etc/foo/password
1f2d1e2e67df
被挂载的secret内容自动更新
如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。最长的时间将是一个同步周期加上缓存生命周期(period+ttl)
特例:以subPath(https://kubernetes.io/docs/concepts/storage/volumes/#using-subpath)形式挂载到容器中的secret将不会自动更新
[root@k8s-master ~]# vim pod2_use_secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
- key: password
path: my-group/my-password
username被映射到了文件/etc/foo/my-group/my-username而不是/etc/foo/username
而password没有被使用,这种方式每个key的调用需要单独用key像username一样调用
[root@k8s-master ~]# kubectl exec -it mypod -- /bin/bash
root@mypod:/data# ls /etc/foo/
my-group
root@mypod:/etc/foo# cd my-group
root@mypod:/etc/foo/my-group# ls
my-username
root@mypod:/etc/foo/my-group# cat my-usernam
admin
[root@k8s-master ~]# vim pod3_use_secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
defaultMode: 0400
[root@k8s-master ~]# vim pod4_use_secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
mode: 0600
- key: password
path: my-group/my-password
mode: 0600
创建一个Secret,多个Pod可以引用同一个Secret
修改pod的定义,定义环境变量并使用env[].valueFrom.secretKeyRef指定secret和相应的key
修改镜像或命令行,让它们可以读到环境变量
[root@k8s-master ~]# vim pod5_use_secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
restartPolicy: Never
[root@k8s-master secrets]# kubectl exec -it secret-env-pod -- /bin/bash
root@secret-env-pod:/data# echo $SECRET_USERNAME
admin
root@secret-env-pod:/data# echo $SECRET_PASSWORD
1f2d1e2e67df
kubernetes.io/dockerconfigjson
可以直接用 kubectl 命令来创建用于 docker registry 认证的 secret
[root@k8s-master ~]# kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret "myregistrykey" created.
[root@k8s-master ~]# kubectl get secret myregistrykey -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJET0NLRVJfUkVHSVNUUllfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX0VNQUlMIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0=
kind: Secret
metadata:
creationTimestamp: "2020-08-13T23:22:33Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:.dockerconfigjson: {}
f:type: {}
manager: kubectl
operation: Update
time: "2020-08-13T23:22:33Z"
name: myregistrykey
namespace: default
resourceVersion: "717559"
selfLink: /api/v1/namespaces/default/secrets/myregistrykey
uid: 08d04be4-57c2-40a8-ba04-e1ad7490bb64
type: kubernetes.io/dockerconfigjson
[root@k8s-master ~]# echo "eyJjY3IuY2NzLnRlbmNlbnR5dW4uY29tL3RlbmNlbnR5dW4iOnsidXNlcm5hbWUiOiIzMzIxMzM3OTk0IiwicGFzc3dvcmQiOiIxMjM0NTYuY29tIiwiZW1haWwiOiIzMzIxMzM3OTk0QHFxLmNvbSIsImF1dGgiOiJNek15TVRNek56azVORG94TWpNME5UWXVZMjl0XXXX" | base64 --decode
{"ccr.ccs.tencentyun.com/XXXXXXX":{"username":"3321337XXX","password":"123456.com","email":"[email protected]","auth":"MzMyMTMzNzk5NDoxMjM0NTYuY29t"}}
[root@k8s-master ~]# kubectl create secret docker-registry myregistrykey --from-file="~/.dockercfg"
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistrykey
kubernetes.io/service-account-token
kubernetes.io/service-account-token : 用于被 serviceaccount 引用。
serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。
[root@k8s-master ~]# kubectl create deployment nginx --image=nginx
deployment.apps/nginx created
[root@k8s-master ~]# kubectl get deployments.apps nginx
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 28s
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-f89759699-c4rfp 1/1 Running 0 11s
[root@k8s-master secrets]# kubectl exec nginx-f89759699-c4rfp -- ls /run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token
需要被挂载到Pod中的secret需要提前创建,否则会导致Pod创建失败
secret是有命名空间属性的,只有在相同namespace的Pod才能引用它
单个Secret容量限制的1Mb,这么做是为了防止创建超大的Secret导致apiserver或kubelet的内存耗尽。但是创建过多的小容量secret同样也会耗尽内存,这个问题在将来可能会有方案解决
kubelet只支持由API server创建出来的Pod中引用secret,使用特殊方式创建出来的Pod是不支持引用secret的,比如通过kubelet的–manifest-url参数创建的pod,或者–config参数创建的,或者REST API创建的。
通过secretKeyRef引用一个不存在你secret key会导致pod创建失败
[root@k8s-master ~]# kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
kind: Pod
apiVersion: v1
metadata:
name: secret-test-pod
labels:
name: secret-test
spec:
volumes:
- name: secret-volume
secret:
secretName: ssh-key-secret
containers:
- name: ssh-test-container
image: mySshImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
[root@k8s-master ~]# kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
secret "prod-db-secret" created
[root@k8s-master ~]# kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
secret "test-db-secret" created
apiVersion: v1
kind: List
items:
- kind: Pod
apiVersion: v1
metadata:
name: prod-db-client-pod
labels:
name: prod-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: prod-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
- kind: Pod
apiVersion: v1
metadata:
name: test-db-client-pod
labels:
name: test-db-client
spec:
volumes:
- name: secret-volume
secret:
secretName: test-db-secret
containers:
- name: db-client-container
image: myClientImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
/etc/secret-volume/username
/etc/secret-volume/password
kind: Secret
apiVersion: v1
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
kind: Pod
apiVersion: v1
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: k8s.gcr.io/busybox
command:
- ls
- "-l"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
Secret:作用是帮你把 Pod 想要访问的加密数据,存放到 Etcd 中。然后,就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret 里保存的信息了。
Secret 典型的使用场景:
[root@k8s-master ~]# cat test-projected-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-secret-volume
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
projected:
sources:
- secret:
name: user
- secret:
name: pass
定义了一个容器,它声明挂载的 Volume是 projected 类型 , 并不是常见的 emptyDir 或者 hostPath 类型,
而这个 Volume 的数据来源(sources),则是名为 user 和 pass 的 Secret 对象,分别对应的是数据库的用户名和密码。
这里用到的数据库的用户名、密码,正是以 Secret 对象的方式交给 Kubernetes 保存的。
方法1. 使用 kubectl create secret 指令创建Secret对象
[root@k8s-master ~]# cat ./username.txt
admin
[root@k8s-master ~]# cat ./password.txt
c1oudc0w!
[root@k8s-master ~]# kubectl create secret generic user --from-file=./username.txt
[root@k8s-master ~]# kubectl create secret generic pass --from-file=./password.txt
# username.txt 和 password.txt 文件里,存放的就是用户名和密码;而 user 和 pass,则是为 Secret 对象指定的名字。
# 查看Secret 对象:
[root@k8s-master ~]# kubectl get secrets
NAME TYPE DATA AGE
user Opaque 1 51s
pass Opaque 1 51s
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
user: YWRtaW4=
pass: MWYyZDFlMmU2N2Rm
Secret 对象要求这些数据必须是经过 Base64 转码的,以免出现明文密码的安全隐患
转码操作
[root@k8s-master ~]# echo -n 'admin' | base64
YWRtaW4=
[root@k8s-master ~]# echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
注意:像这样创建的 Secret 对象,它里面的内容仅仅是经过了转码,并没有被加密。生产环境中,需要在 Kubernetes 中开启 Secret 的加密插件,增强数据的安全性。
用yaml方式创建的secret调用方法如下:
[root@k8s-master ~]# cat test-projected-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume1
spec:
containers:
- name: test-secret-volume1
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
secret:
secretName: mysecret
[root@k8s-master ~]# kubectl create -f test-projected-volume.yaml
[root@k8s-master ~]# kubectl exec -it test-projected-volume -- /bin/sh
/ # ls /projected-volume/
user
pass
/ # cat /projected-volume/user
root
/ # cat /projected-volume/pass
1f2d1e2e67df
结果中看到,保存在 Etcd 里的用户名和密码信息,已经以文件的形式出现在了容器的 Volume 目录里。
而这个文件的名字,就是 kubectl create secret 指定的 Key,或者说是 Secret 对象的 data 字段指定的 Key。
注意:报错:上面这条命令会报错如下
[root@k8s-master ~]# kubectl exec -it test-projected-volume /bin/sh
error: unable to upgrade connection: Forbidden (user=system:anonymous, verb=create, resource=nodes, subresource=proxy)
[root@k8s-master ~]# kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
clusterrolebinding.rbac.authorization.k8s.io/system:anonymous created
[root@k8s-master ~]# echo -n '111111' | base64
MTExMTEx
[root@k8s-master ~]# cat mysecret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
user: YWRtaW4=
pass: MTExMTEx
[root@k8s-master ~]# kubectl apply -f mysecret.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
secret/mysecret configured
[root@k8s-master ~]# kubectl exec -it test-projected-volume1 /bin/sh
/ # cat projected-volume/pass
111111/ #
[root@k8s-master ~]# kubectl get secret mysecret -o json
{
"apiVersion": "v1",
"data": {
"pass": "MTExMTEx",
"user": "YWRtaW4="
},
"kind": "Secret",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"pass\":\"MTExMTEx\",\"user\":\"YWRtaW4=\"},\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"mysecret\",\"namespace\":\"default\"},\"type\":\"Opaque\"}\n"
},
"creationTimestamp": "2019-01-21T07:31:05Z",
"name": "mysecret",
"namespace": "default",
"resourceVersion": "125857",
"selfLink": "/api/v1/namespaces/default/secrets/mysecret",
"uid": "82e20780-1d4e-11e9-baa8-000c29f01606"
},
"type": "Opaque"
}
[root@k8s-master rbac]# cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --authorization-mode=Node,RBAC
...
Kubernetes有一个很基本的特性就是它的所有资源对象都是模型化的 API 对象,允许执行 CRUD(Create、Read、Update、Delete)操作(也就是我们常说的增、删、改、查操作),比如下面的这下资源:
可对以上资源对象的存在的操作有:
在更上层,这些资源和 API Group 进行关联,比如Pods属于 Core API Group,而Deployements属于 apps API Group,要在Kubernetes中进行RBAC的管理,除了上面的这些资源和操作以外,还需要另外的一些对象:
基于角色的访问控制:先让一个用户(Users)扮演一个角色(Role),让角色(Role)拥有权限,从而让用户拥有这样的权限,然后在授权机制当中,只需要将权限授予某个角色,此时用户将获取对应角色的权限,从而实现角色的访问控制;
Role和ClusterRole
RoleBinding和ClusterRoleBinding
使用RoleBinding去绑定ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # ""空字符串,表示核心API群
resources: ["pods"]
verbs: ["get", "watch", "list"]
集群角色除了具有和角色一致的命名空间内资源的管理能力,因其集群级别的生效范围,还可以用于以下特殊元素的授权管理上:
下面的集群角色可以让用户有权访问任意一个或所有命名空间的 secrets(视其绑定方式而定):
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: ClusterRole01
# ClusterRole不受限于命名空间,所以省略了namespace name的定义
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
角色绑定或集群角色绑定用来把一个角色绑定到一个目标上,绑定目标可以是User(用户)、Group(组)或者Service Account。使用RoleBinding可以为某个命名空间授权,使用 ClusterRoleBinding可以为集群范围内授权。
RoleBinding 可以引用 Role 进行授权。下例中的 RoleBinding 将在 default 命名空间中把 pod-reader 角色授予用户 jane,这一操作让 jane 可以读取 default命名空间中的 Pod:
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
RoleBinding 可以引用 ClusterRole,对属于同一命名空间内 ClusterRole 定义的资源主体进行授权。一种很常用的做法就是,集群管理员为集群范围预定义好一组角色(ClusterRole),然后在多个命名空间中重复使用这些ClusterRole。这样可以大幅提高授权管理工作效率,也使得各个命名空间下的基础性授权规则与使用体验保持一致。
例如下面,虽然 secret-reader 是一个集群角色,但是因为使用了RoleBinding,所以 dave 只能读取 development 命名空间中的 secret。
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-secrets
namespace: development # 集群角色中,只有在development命名空间中的权限才能赋予dave
subjects:
- kind: User
name: dave
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: ClusterRole01
apiGroup: rbac.authorization.k8s.io
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RN2wFKXo-1610418329805)(assets/dr5oXFvj9ieAZJYkAADJ69-Gdcw245.jpg)]
多数资源可以用其名称的字符串来表达,也就是Endpoint中的URL相对路径,例如pods。然而,某些k8s API包含下级资源,例如Pod的日志(logs)。Pod日志的Endpoint是GET/api/v1/namespaces/{namespace}/pods/{name}/log。
在这个例子中,Pod 是一个命名空间内的资源,log 就是一个下级资源。要在 RBAC 角色中体现,则需要用斜线“/
”来分隔资源和下级资源。
若想授权让某个主体同时能够读取 Pod 和 Pod log,则可以配置 resources 为一个数组:
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
namespace: default
name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
资源还可以通过名字(ResourceName)进行引用(这里指的是资源实例的名子)。在指定ResourceName后,使用get、delete、update和patch动词的请求,就会被限制在这个资源实例的范围内。
例如下面的声明让一个主体只能对一个configmap进行get和update操作:
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
namespace: default
name: configmap-updater
rules:
- apiGroups: [""]
resources: ["configmap"]
resourceNames: ["my-configmap"]
verbs: ["update", "get"]
Kubernetes 没有 User Account 的 API 对象,利用管理员分配给你的一个私钥就可以创建了,可以参考官方文档中的方法,使用OpenSSL证书来创建一个 User,也可以使用更简单的cfssl工具来创建:
给用户 qfedu 创建一个私钥,命名成:qfedu.key:
[root@k8s-master rbac]# openssl genrsa -out qfedu.key 2048
[root@k8s-master rbac]# openssl req -new -key qfedu.key -out qfedu.csr -subj "/CN=qfedu/O=qfeducloud"
CA
的目录,利用该目录下面的ca.crt和ca.key两个文件来批准上面的证书请求[root@k8s-master rbac]# openssl x509 -req -in qfedu.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out qfedu.crt -days 500
[root@k8s-master rbac]# ls
qfedu.csr qfedu.key qfedu.crt
[root@k8s-master rbac]# kubectl config set-credentials qfedu --client-certificate=qfedu.crt --client-key=qfedu.key
[root@k8s-master rbac]# kubectl config set-context qfedu-context --cluster=kubernetes --namespace=kube-system --user=qfedu
[root@k8s-master rbac]# kubectl get pods --context=qfedu-context
Error from server (Forbidden): pods is forbidden: User "qfedu" cannot list pods in the namespace "default"
[root@k8s-master rbac]# vim qfedu-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: qfedu-role
namespace: kube-system
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']
Pod 属于 core 这个 API Group,在YAML中用空字符就可以,
Deployment ,ReplicaSets属于 apps 这个 API Group
rules 下面的 apiGroups 就综合了这几个资源的 API Group:["", “extensions”, “apps”]
verbs 就是对这些资源对象执行的操作,这里需要所有的操作方法,所以也可以使用[’*’]来代替。
然后创建这个Role:
[root@k8s-master rbac]# kubectl apply -f qfedu-role.yaml
[root@k8s-master rbac]# vim qfedu-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: qfedu-rolebinding
namespace: kube-system
subjects:
- kind: User
name: qfedu
apiGroup: ""
roleRef:
kind: Role
name: qfedu-role
apiGroup: ""
[root@k8s-master rbac]# kubectl create -f qfedu-rolebinding.yaml
[root@k8s-master rbac]# kubectl get pods --context=qfedu-context
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-578894d4cd-96kn4 1/1 Running 0 11h
calico-node-8ffrw 1/1 Running 0 11h
calico-node-bt4lv 1/1 Running 0 11h
calico-node-vwqpx 1/1 Running 0 11h
canal-2hp4t 2/2 Running 0 11h
canal-79vfq 2/2 Running 0 11h
canal-dstjf 2/2 Running 0 11h
coredns-66bff467f8-6nj98 1/1 Running 0 13d
coredns-66bff467f8-gfncf 1/1 Running 0 13d
etcd-k8s-master 1/1 Running 0 13d
kube-apiserver-k8s-master 1/1 Running 0 13d
kube-controller-manager-k8s-master 1/1 Running 0 12d
kube-proxy-bt5wj 1/1 Running 0 13d
kube-proxy-f5cr2 1/1 Running 1 13d
kube-proxy-wn67g 1/1 Running 0 13d
kube-scheduler-k8s-master 1/1 Running 0 12d
metrics-server-96d889b76-zkwx6 1/1 Running 0 12d
[root@k8s-master rbac]# kubectl --context=qfedu-context get pods --namespace=default
Error from server (Forbidden): pods is forbidden: User "qfedu" cannot list pods in the namespace "default"
[root@k8s-master rbac]# kubectl create serviceaccount qfedu-sa -n kube-system
也可以定义成YAML文件的形式来创建。
新建一个 Role 对象
[root@k8s-master rbac]# vim qfedu-sa-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: qfedu-sa-role
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
[root@k8s-master rbac]# kubectl create -f qfedu-sa-role.yaml
[root@k8s-master rbac]# vim qfedu-sa-rolebinding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: qfedu-sa-rolebinding
namespace: kube-system
subjects:
- kind: ServiceAccount
name: qfedu-sa
namespace: kube-system
roleRef:
kind: Role
name: qfedu-sa-role
apiGroup: rbac.authorization.k8s.io
[root@k8s-master rbac]# kubectl create -f qfedu-sa-rolebinding.yaml
[root@k8s-master rbac]# kubectl get secret -n kube-system |grep qfedu-sa
qfedu-sa-token-kts4n kubernetes.io/service-account-token 3 5m32s
[root@k8s-master rbac]# kubectl describe secrets qfedu-sa-token-kts4n -n kube-system
Name: qfedu-sa-token-kts4n
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: qfedu-sa
kubernetes.io/service-account.uid: 41a0ba23-445b-4140-94cc-587d175f2945
Type: kubernetes.io/service-account-token
Data
====
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IlZZaGtacVFOLUtrQ3JxSUpQZDBWdG5zNkNJT3M3U25ocmhFVW1XTGZVc1UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJxZmVkdS1zYS10b2tlbi1rdHM0biIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJxZmVkdS1zYSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjQxYTBiYTIzLTQ0NWItNDE0MC05NGNjLTU4N2QxNzVmMjk0NSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTpxZmVkdS1zYSJ9.wjCCrzr9kxna-C6QruMWHzyf-cOVhqnKccztOzWJIGzV92md7bI7PKAxseu5QKMzNh-0SrJD4Z_ejX7AVQRKmyEhWJb2MPoVoDM-XedQY-0krc6jaK66MNhOtUptsjHA-lUIf64LcIdb41voczqVN3S-F8PwYyQlO21l7G4XvUej5Y6tBJSO003tUL_Qr-BWowH3Mhle7a5qWDeV5J9k2Spr-0qk4wUhMykQ9sGZID3AAw8Aa8M3nxJS26Lj_lKxWX6v5s055-bW5NQjrp2l3Cnaaafa6UNHd26kPDvVMcfLEP_N30jxLbm9Ok6Znaam9iVrSghZ1sIOQrQrytlm1A
ca.crt: 1025 bytes
namespace: 11 bytes
# 会生成一串很长的base64后的字符串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzLgtR77-1610418329806)(assets/360截图17290429104140144.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8UXrxvM-1610418329807)(assets/360截图17620509297085.png)]
可以看到可以访问pod列表了,但是也会有一些其他额外的提示:events is forbidden: User “system:serviceaccount:kube-system:qfedu-sa” cannot list events in the namespace “kube-system”,这是因为当前登录用只被授权了访问 pod 和 deployment 的权限,同样的,访问下deployment看看可以了吗?
同样的可以根据自己的需求来对访问用户的权限进行限制,可以自己通过 Role 定义更加细粒度的权限,也可以使用系统内置的一些权限……
[root@k8s-master rbac]# vim qfedu-sa2.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: qfedu-sa2
namespace: kube-system
[root@k8s-master rbac]# kubectl create -f qfedu-sa2.yaml
[root@k8s-master rbac]# vim qfedu-clusterolebinding.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: qfedu-sa2-clusterrolebinding
subjects:
- kind: ServiceAccount
name: qfedu-sa2
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
没有为这个资源对象声明 namespace,因为这是一个 ClusterRoleBinding 资源对象,是作用于整个集群的,
也没有单独新建一个 ClusterRole 对象,而是使用的 cluster-admin 这个对象,这是Kubernetes集群内置的 ClusterRole 对象,
可以使用kubectl get clusterrole 和kubectl get clusterrolebinding查看系统内置的一些集群角色和集群角色绑定,
这里使用的 cluster-admin 这个集群角色是拥有最高权限的集群角色,所以一般需要谨慎使用该集群角色。
创建上面集群角色绑定资源对象,创建完成后同样使用 ServiceAccount 对应的 token 去登录 Dashboard 验证下:
[root@k8s-master rbac]# kubectl create -f qfedu-clusterolebinding.yaml
[root@k8s-master rbac]# kubectl get secret -n kube-system |grep qfedu-sa2
qfedu-sa2-token-kts5n kubernetes.io/service-account-token 3 5m32s
[root@k8s-master rbac]# kubectl describe secrets qfedu-sa2-token-kts5n -n kube-system
Name: qfedu-sa2-token-kts4n
Labels: <none>
Annotations: kubernetes.io/service-account.name: qfedu-sa2
kubernetes.io/service-account.uid: 41a0ba23-445b-4140-94cc-587d175f2946
Type: kubernetes.io/service-account-token
Data
====
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IlZZaGtacVFOLUtrQ3JxSUpQZDBWdG5zNkNJT3M3U25ocmhFVW1XTGZVc1UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJxZmVkdS1zYS10b2tlbi1rdHM0biIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJxZmVkdS1zYSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjQxYTBiYTIzLTQ0NWItNDE0MC05NGNjLTU4N2QxNzVmMjk0NSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTpxZmVkdS1zYSJ9.wjCCrzr9kxna-C6QruMWHzyf-cOVhqnKccztOzWJIGzV92md7bI7PKAxseu5QKMzNh-0SrJD4Z_ejX7AVQRKmyEhWJb2MPoVoDM-XedQY-0krc6jaK66MNhOtUptsjHA-lUIf64LcIdb41voczqVN3S-F8PwYyQlO21l7G4XvUej5Y6tBJSO003tUL_Qr-BWowH3Mhle7a5qWDeV5J9k2Spr-0qk4wUhMykQ9sGZID3AAw8Aa8M3nxJS26Lj_lKxWX6v6s055-bW5NQjrp2l3Cnaaafa6UNHd26kPDvVMcfLEP_N30jxLbm9Ok6Znaam9iVrSghZ1sIOQrQrytlm1A
ca.crt: 1025 bytes
namespace: 11 bytes
# 会生成一串很长的base64后的字符串
刚开始接触到 RBAC 认证的时候,可能不太熟悉,特别是不知道应该怎么去编写rules规则,大家可以利用 kubectl 的 get、describe、 -o yaml 这些操作去分析系统自带的 clusterrole、clusterrolebinding 这些资源对象的编写方法,所以kubectl最基本的用户一定要掌握好。
RBAC只是Kubernetes中安全认证的一种方式,当然也是现在最重要的一种方式。
Kubernetes 暴露服务的方式目前只有三种:LoadBlancer Service、NodePort Service、Ingress
service 的表现形式为IP:PORT,即工作在第四层传输层(TCP/IP层),对于不同的URL地址经常对应用不同的后端服务或者虚拟服务器,这些应用层的转发机制仅通过kubernetes的service机制是无法实现的,这种情况我么可以使用ingress策略定义和一个具体的ingress Controller,两者结合实现一个完整的Ingress 负载均衡,这个负载均衡是基于nginx七层反向代理来实现的,ingress工作原理如下图:
外部客户端通过访问负载均衡器,然后调度到service上,然后在调度到IngressController,IngressController通过Ingress规则(域名或虚拟主机)访问到后端pod,而在Ingress规则当中对应的主机是又service分组来设定的,可以看到,这幅图有2种service,最上面的service是用来对外提供服务的,而下面2个service仅仅是用来分pod组的
通俗的讲:
Kubernetes 并没有自带 Ingress Controller,它只是一种标准,具体实现有多种,需要自己单独安装,常用的是 Nginx Ingress Controller 和 Traefik Ingress Controller。
Ingress 是一种转发规则的抽象,Ingress Controller 的实现需要根据这些 Ingress 规则来将请求转发到对应的 Service,下图方便大家理解:
从图中可以看出,Ingress Controller 收到请求,匹配 Ingress 转发规则,匹配到了就转发到后端 Service,而 Service 代表的后端 Pod 有多个,选出一个转发到那个 Pod,最终由那个 Pod 处理请求。
有同学可能会问,既然 Ingress Controller 要接受外面的请求,而 Ingress Controller 是部署在集群中的,怎么让 Ingress Controller 本身能够被外面访问到呢,有几种方式:
kubectl get svc
我们可以查看到这个端口,这个端口在集群的每个节点都可以访问,通过访问集群节点的这个端口就可以访问 Ingress Controller 了。但是集群节点这么多,而且端口又不是 80和443,太不爽了,一般我们会在前面自己搭个负载均衡器,比如用 Nginx,将请求转发到集群各个节点的那个端口上,这样我们访问 Nginx 就相当于访问到 Ingress Controller 了service资源和pod资源的IP地址仅能用于集群网络内部的通信,所有的网络流量都无法穿透边界路由器以实现集群内外通信。尽管可以为service使用NodePort或LoadBalancer类型通过节点引入外部流量,但它依然是4层流量转发,可用的负载均衡器也为传输层负载均衡机制。
Ingress是k8s的标准资源类型之一,它其实就是一组基于DNS名称或URL路径把请求转发至指定的service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布。
Ingress资源自身并不能进行流量穿透,它仅是规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助,如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器。
Ingress控制器可以由任何具有反向代理功能的服务程序实现,如Nginx、Envoy和Traefik等。
Ingress控制器自身也是运行于集群中的pod资源对象,它与被代理的运行为pod资源的应用运行于同一网络中。使用Ingress资源进行流量分发时,Ingress控制器可基于某Ingress资源定义的规则将客户端的请求流量直接转发至与service对应的后端pod资源之上,这种转发机制会绕过service资源,从而省去了由kube-proxy实现的端口代理开销。
Ingress规则需要由一个service资源对象辅助识别相关的所有pod对象,但Ingress-nginx控制器可经由api.ilinux.io规则的定义直接将请求流量调度至后端pod上,而无须经由service对象api的再次转发。
Ingress资源时基于HTTP虚拟主机或URL的转发规则,需要强调的是,这是一条转发规则。资源清单中annotation用于识别其所属的Ingress控制器的类型,这一点在集群中部署有多个Ingress控制器时尤为重要。
Ingress 中的spec字段是Ingress资源的核心组成部分,主要包含以下3个字段:
spec:
rules:
- hosts: <string>
http:
paths:
- path:
backend:
serviceName: <string>
servicePort: <string>
基于http暴露的每个service资源均可发布于一个独立的FQDN主机名之上,如www.qf.com;也可发布于某主机的URL路径之上,从而将它们整合到同一个web站点,如www.qf.com/nginx。至于是否需要发布为https类型的应用则取决于用户的业务需求。
Ingress的资源类型有以下4种:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: my-svc
servicePort: 80
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-url-demo
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: myapp.syztoo.com
http:
paths:
- path: /v1
backend:
serviceName: myappv1-svc
servicePort: 80
- path: /v2
backend:
serviceName: myappv2-svc
servicePort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myappv1-svc
namespace: default
spec:
selector:
app: myappv1
release: canary
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myappv1-depoly
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myappv1
release: canary
template:
metadata:
labels:
app: myappv1
release: canary
spec:
containers:
- name: myappv1
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myappv2-svc
namespace: default
spec:
selector:
app: myappv2
release: canary
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myappv2-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myappv2
release: canary
template:
metadata:
labels:
app: myappv2
release: canary
spec:
containers:
- name: myappv2
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort: 80
---
# 注意前提:必须部署完 Ingress Controller
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
nodePort: 30080 # 添加端口
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: api.qf.com
http:
paths:
- backend:
serviceName: api
servicePort: 80
- host: wap.qf.com
http:
paths:
- backend:
serviceName: wap
servicePort: 80
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: no-rules-map
spec:
tls:
- secretName: ikubernetesSecret
backend:
serviceName: homesite
servicePort: 80
下载 官网 配置清单
https://github.com/kubernetes/ingress-nginx/tree/master/deploy/static
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sslI3ntc-1610418329808)(assets/360截图17860602115120138.png)]
[root@master ~]# mkdir ingress-nginx
[root@master ~]# cd ingress-nginx/
[root@master ingress-nginx]# wget https://github.com/kubernetes/ingress-nginx/blob/master/deploy/static/mandatory.yaml
[root@master ingress-nginx]# wget https://github.com/kubernetes/ingress-nginx/blob/master/deploy/static/provider/baremetal/service-nodeport.yaml #对外提供服务,如果不需要可以不下载
[root@master ingress-nginx]# sed -i 's#quay.io/kubernetes-ingress-controller/nginx-ingress-controller#registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller#g' mandatory.yaml
# 在Service中定义标注prometheus.io/scrape: ‘true’,表明该Service需要被promethues发现并采集数据
[root@master ingress-nginx]# cat service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
nodePort: 30080 # 添加端口
- name: https
port: 443
targetPort: 443
protocol: TCP
nodePort: 30443 # 添加端口
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
# 上面的文件中,使用了宿主机的固定端口(30080,30443),需保证宿主机的这两个端口未占用。
[root@master ingress-nginx]# kubectl apply -f mandatory.yaml
[root@master ingress-nginx]# kubectl apply -f service-nodeport.yaml
[root@master ingress-nginx]# kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default nginx-7bb7cd8db5-98wvj 1/1 Running 0 62m
ingress-nginx nginx-ingress-controller-d786fc9d4-w5nrc 1/1 Running 0 58m
kube-system coredns-bccdc95cf-8sqzn 1/1 Running 2 4d2h
kube-system coredns-bccdc95cf-vt8nz 1/1 Running 2 4d2h
kube-system etcd-master 1/1 Running 1 4d2h
kube-system kube-apiserver-master 1/1 Running 1 4d2h
kube-system kube-controller-manager-master 1/1 Running 2 4d2h
kube-system kube-flannel-ds-amd64-c97wh 1/1 Running 1 4d1h
kube-system kube-flannel-ds-amd64-gl6wg 1/1 Running 2 4d1h
kube-system kube-flannel-ds-amd64-npsqf 1/1 Running 1 4d1h
kube-system kube-proxy-gwmx8 1/1 Running 2 4d2h
kube-system kube-proxy-phqk2 1/1 Running 1 4d1h
kube-system kube-proxy-qtt4b 1/1 Running 1 4d1h
kube-system kube-scheduler-master 1/1 Running 2 4d2h
[root@master ingress-nginx]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.105.53.207 <none> 80:30080/TCP,443:30443/TCP 58m
1.namespace.yaml
创建一个独立的命名空间 ingress-nginx
2.configmap.yaml
ConfigMap 是存储通用的配置变量的,类似于配置文件,使用户可以将分布式系统中用于不同模块的环境变量统一到一个对象中管理;而它与配置文件的区别在于它是存在集群的“环境”中的,并且支持K8S集群中所有通用的操作调用方式。
从数据角度来看,ConfigMap的类型只是键值组,用于存储被Pod或者其他资源对象(如RC)访问的信息。这与secret的设计理念有异曲同工之妙,主要区别在于ConfigMap通常不用于存储敏感信息,而只存储简单的文本信息。
ConfigMap可以保存环境变量的属性,也可以保存配置文件。
创建pod时,对configmap进行绑定,pod内的应用可以直接引用ConfigMap的配置。相当于configmap为应用/运行环境封装配置。
pod使用ConfigMap,通常用于:设置环境变量的值、设置命令行参数、创建配置文件。
3.default-backend.yaml
如果外界访问的域名不存在的话,则默认转发到default-http-backend这个Service,其会直接返回404:
4.rbac.yaml
负责Ingress的RBAC授权的控制,其创建了Ingress用到的ServiceAccount、ClusterRole、Role、RoleBinding、ClusterRoleBinding
5.with-rbac.yaml
是Ingress的核心,用于创建ingress-controller。ingress-controller的作用是将新加入的Ingress进行转化为Nginx的配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CNz9uO30-1610418329809)(assets/360截图17571112739489.png)]
这里我们已 nginx 为服务为例,创建一个 nginx 和跟 nginx 对应的 service,
**注意:**metadata.name 要和后面创建的 ingress 中的 serviceName 一致,切记!
[root@master ingress-nginx]# cat mynginx.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nginx
namespace: default
spec:
selector:
app: mynginx
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mynginx
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: mynginx
template:
metadata:
labels:
app: mynginx
spec:
containers:
- name: mycontainer
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: nginx
containerPort: 80
[root@master ingress-nginx]# cat ingress-nginx.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-mynginx
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: mynginx.qf.com
http:
paths:
- path:
backend:
serviceName: service-nginx
servicePort: 80
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pT6kCmuf-1610418329810)(assets/360截图17950506102101155.png)]
#查看ingress-controller中的规则
[root@master ingress-nginx]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-d786fc9d4-4vb5z 1/1 Running 0 140m
[root@master ingress-nginx]# kubectl exec -it nginx-ingress-controller-d786fc9d4-4vb5z -n ingress-nginx -- /bin/bash
www-data@nginx-ingress-controller-d786fc9d4-4vb5z:/etc/nginx$ cat nginx.conf
## start server mynginx.qf.com
server {
server_name mynginx.qf.com ;
listen 80 ;
listen 443 ssl http2 ;
set $proxy_upstream_name "-";
ssl_certificate_by_lua_block {
certificate.call()
}
location / {
set $namespace "default";
set $ingress_name "ingress-mynginx";
set $service_name "service-nginx";
set $service_port "80";
set $location_path "/";
rewrite_by_lua_block {
lua_ingress.rewrite({
force_ssl_redirect = false,
ssl_redirect = true,
force_no_ssl_redirect = false,
use_port_in_redirects = false,
})
balancer.rewrite()
plugins.run()
}
header_filter_by_lua_block {
plugins.run()
}
body_filter_by_lua_block {
}
log_by_lua_block {
balancer.log()
monitor.call()
plugins.run()
}
port_in_redirect off;
set $balancer_ewma_score -1;
set $proxy_upstream_name "default-service-nginx-80";
set $proxy_host $proxy_upstream_name;
set $pass_access_scheme $scheme;
set $pass_server_port $server_port;
set $best_http_host $http_host;
set $pass_port $pass_server_port;
set $proxy_alternative_upstream_name "";
client_max_body_size 1m;
proxy_set_header Host $best_http_host;
# Pass the extracted client certificate to the backend
# Allow websocket connections
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Request-ID $req_id;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $best_http_host;
proxy_set_header X-Forwarded-Port $pass_port;
proxy_set_header X-Forwarded-Proto $pass_access_scheme;
proxy_set_header X-Scheme $pass_access_scheme;
# Pass the original X-Forwarded-For
proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;
# mitigate HTTPoxy Vulnerability
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
proxy_set_header Proxy "";
# Custom headers to proxied server
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering off;
proxy_buffer_size 4k;
proxy_buffers 4 4k;
proxy_max_temp_file_size 1024m;
proxy_request_buffering on;
proxy_http_version 1.1;
proxy_cookie_domain off;
proxy_cookie_path off;
# In case of errors try the next upstream server before returning an error
proxy_next_upstream error timeout;
proxy_next_upstream_timeout 0;
proxy_next_upstream_tries 3;
proxy_pass http://upstream_balancer;
proxy_redirect off;
}
}
## end server mynginx.fengzi.com
[root@master ingress-nginx]# cat tomcat.yaml
apiVersion: v1
kind: Service
metadata:
name: tomcat
namespace: default
spec:
selector:
app: tomcat
ports:
- name: http
port: 8080
targetPort: 8080
- name: ajp
port: 8009
targetPort: 8009
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat
namespace: default
spec:
replicas: 5
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
- name: ajp
containerPort: 8009
[root@master ingress-nginx]# cat ingress-tomcat.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-mytomcat
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: mytomcat.qf.com
http:
paths:
- path:
backend:
serviceName: tomcat
servicePort: 8080
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qmabbspx-1610418329811)(assets/360截图17860531433933.png)]
[root@master ingress-nginx]# openssl genrsa -out tls.key 2048
#这里CN=后面要写域名
[root@master ingress-nginx]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=mytomcat.qf.com
#创建secret
[root@master ingress-nginx]# kubectl create secret tls mytomcat-ingress-secret --cert=tls.crt --key=tls.key
[root@master ingress-nginx]# kubectl describe secret mytomcat-ingress-secret
Name: mytomcat-ingress-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1302 bytes
tls.key: 1675 bytes
[root@master ingress-nginx]# cat ingress-tomcat-tls.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-mytomcat-tls
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- mytomcat.qf.com #这里写域名
secretName: mytomcat-ingress-secret #这里写secret证书名称
rules:
- host: mytomcat.qf.com
http:
paths:
- path:
backend:
serviceName: tomcat
servicePort: 8080
[root@k8s-master ingress-nginx]# kubectl apply -f ingress-tomcat-tls.yaml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NbxYwam9-1610418329813)(assets/360截图18270612196024.png)]
1、使用 network policy资源可以配置pod的网络,networkPolicy 是 namespace scoped的,他只能影响某个 namespace 下的 pod 的网络出入站规则。
2、CNI插件需要启用,Calico安装为CNI插件。必须通过传递–network-plugin=cni参数将kubelet配置为使用CNI网络。(在 kubeadm 上,这是默认设置。)
3、支持 kube-proxy 的模式
[root@k8s-master network-policy]# wget https://docs.projectcalico.org/v3.14/getting-started/kubernetes/installation/hosted/canal/canal.yaml
[root@k8s-master network-policy]# POD_CIDR="" \
[root@k8s-master network-policy]# sed -i -e "s?10.244.0.0/16?$POD_CIDR?g" canal.yaml
[root@k8s-master network-policy]# grep image canal.yaml
image: calico/cni:v3.14.2
image: calico/pod2daemon-flexvol:v3.14.2
image: calico/node:v3.14.2
image: quay.io/coreos/flannel:v0.11.0
image: calico/kube-controllers:v3.14.2
[root@k8s-master net]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
calico/node v3.14.2 780a7bc34ed2 3 weeks ago 262MB
calico/pod2daemon-flexvol v3.14.2 9dfa8f25b51c 3 weeks ago 22.8MB
calico/cni v3.14.2 e6189009f081 3 weeks ago 119MB
calico/node v3.15.1 1470783b1474 5 weeks ago 262MB
calico/pod2daemon-flexvol v3.15.1 a696ebcb2ac7 5 weeks ago 112MB
calico/cni v3.15.1 2858353c1d25 5 weeks ago 217MB
calico/kube-controllers v3.15.1 8ed9dbffe350 5 weeks ago 53.1MB
quay.io/coreos/flannel v0.11.0 ff281650a721 18 months ago 52.6MB
[root@k8s-master network-policy]# kubectl apply -f canal.yaml
configmap/canal-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org configured
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org configured
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers unchanged
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers unchanged
clusterrole.rbac.authorization.k8s.io/calico-node configured
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/canal-flannel created
clusterrolebinding.rbac.authorization.k8s.io/canal-calico created
daemonset.apps/canal created
serviceaccount/canal created
deployment.apps/calico-kube-controllers configured
serviceaccount/calico-kube-controllers unchanged
[root@k8s-master network-policy]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-578894d4cd-96kn4 1/1 Running 0 12m
calico-node-8ffrw 1/1 Running 0 12m
calico-node-bt4lv 1/1 Running 0 12m
calico-node-vwqpx 1/1 Running 0 12m
canal-2hp4t 2/2 Running 0 20m
canal-79vfq 2/2 Running 0 20m
canal-dstjf 2/2 Running 0 20m
coredns-66bff467f8-6nj98 1/1 Running 0 13d
coredns-66bff467f8-gfncf 1/1 Running 0 13d
etcd-k8s-master 1/1 Running 0 13d
kube-apiserver-k8s-master 1/1 Running 0 13d
kube-controller-manager-k8s-master 1/1 Running 0 12d
kube-proxy-bt5wj 1/1 Running 0 13d
kube-proxy-f5cr2 1/1 Running 1 13d
kube-proxy-wn67g 1/1 Running 0 13d
kube-scheduler-k8s-master 1/1 Running 0 12d
metrics-server-96d889b76-zkwx6 1/1 Running 0 12d
NetworkPolicy是kubernetes对pod的隔离手段,可以看到,NetworkPolicy实际上只是宿主机上的一系列iptables规则。
Egress 表示出站流量,就是pod作为客户端访问外部服务,pod地址作为源地址。策略可以定义目标地址或者目的端口
Ingress 表示入站流量,pod地址和服务作为服务端,提供外部访问。pod地址作为目标地址。策略可以定义源地址和自己端口
podSelector 规则生效在那个pod上,可以配置单个pod或者一组pod。可以定义单方向。空 podSelector选择命名空间中的Pod。
[root@k8s-master network-policy]# kubectl explain networkpolicy.spec
egress 出站流量规则 可以根据ports和to去定义规则。ports下可以指定目标端口和协议。to(目标地址):目标地址分为ip地址段、pod、namespace
ingress 入站流量规则 可以根据ports和from。ports下可以指定目标端口和协议。from(来自那个地址可以进来):地址分为ip地址段、pod、namespace
podSelector 定义NetworkPolicy的限制范围。直白的说就是规则应用到那个pod上。
podSelector: {},留空就是定义对当前namespace下的所有pod生效。没有定义白名单的话 默认就是Deny ALL (拒绝所有)
policyTypes 指定那个规则 那个规则生效,不指定就是默认规则。
# 创建 dev namespace
[root@k8s-master network-policy]# vim dev-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
labels:
name: development
[root@k8s-master network-policy]# kubectl apply -f dev-namespace.yaml
[root@k8s-master network-policy]# vim networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-policy
spec:
podSelector: {}
policyTypes:
- Ingress
[root@k8s-master network-policy]# kubectl apply -f network-policy.yaml -n dev
# 创建 prod namespace
[root@k8s-master network-policy]# vim prod-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
name: prod
[root@k8s-master network-policy]# kubectl apply -f prod-namespace.yaml
# 创建pod
[root@k8s-master network-policy]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-1
labels:
name: myapp
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
[root@k8s-master network-policy]# kubectl apply -f policy-pod.yaml -n dev
[root@k8s-master network-policy]# kubectl apply -f policy-pod.yaml -n prod
# 测试一下
[root@k8s-master network-policy]# kubectl get pod -o wide -n prod
NAME READY STATUS RESTARTS AGE IP NODE
pod-1 1/1 Running 0 3h 10.244.2.3 k8s-node02
[root@k8s-master network-policy]# kubectl get pod -o wide -n dev
NAME READY STATUS RESTARTS AGE IP NODE
pod-1 1/1 Running 0 3h 10.244.2.2 k8s-node02
[root@k8s-master network-policy]# curl 10.244.2.3
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master network-policy]# 10.244.2.2 不通
[root@k8s-master network-policy]# vim network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: all-policy
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
[root@k8s-master network-policy]# kubectl apply -f network-policy.yaml -n dev
# 测试
[root@k8s-master network-policy]# curl 10.244.2.2
Hello MyApp | Version: v1 | >Pod Name>
[root@k8s-master network-policy]# vim alloy-pod.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-myapp-policy
spec:
podSelector:
matchLabels:
app: myapp # 选择app=myapp 的标签放行
ingress:
- from:
- ipBlock: # 地址段
cidr: 10.24.0.0/16 # 允许这个地址段访问
except: # 排除一下地址不可以访问
- 10.244.0.1/32
ports:
- port: 80 # 只运行访问80端口
protocol: TCP
policyTypes:
- Ingress
[root@k8s-master network-policy]# kubectl apply -f alloy-pod.yaml -n dev
[root@k8s-master network-policy]# curl 10.244.2.2 # 访问失败
[root@k8s-master network-policy]# vim demo-dev.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-dev
namespace: dev
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
[root@k8s-master network-policy]# vim demo-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-test
namespace: test
labels:
app: myapp
type: pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
[root@k8s-master network-policy]# kubectl apply -f demo-test.yaml
pod/demo-test created
[root@k8s-master network-policy]# kubectl apply -f demo-dev.yaml
pod/demo-dev created
[root@k8s-master network-policy]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-dev 1/1 Running 0 2m51s 10.244.2.2 node02 > >
[root@k8s-master network-policy]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-test 1/1 Running 0 3m6s 10.244.1.2 node01 > >
[root@k8s-master network-policy]# vim deny-all-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
[root@k8s-master network-policy]# kubectl apply -f deny-all-ingress.yaml -n dev
networkpolicy.networking.k8s.io/deny-all-ingress created
[root@k8s-master network-policy]# kubectl get networkpolicies -n dev
NAME POD-SELECTOR AGE
deny-all-ingress > 5m25s
[root@k8s-master network-policy]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-dev 1/1 Running 0 2m51s 10.244.2.2 node02 <none> <none>
[root@k8s-master network-policy]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-test 1/1 Running 0 3m6s 10.244.1.2 node01 <none> <none>
[root@k8s-master network-policy]# curl 10.244.2.2
^C
[root@k8s-master network-policy]# curl 10.244.1.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master network-policy]# vim allow-all-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
[root@k8s-master network-policy]# kubectl apply -f allow-all-ingress.yaml -n dev
networkpolicy.networking.k8s.io/allow-all-ingress created
[root@k8s-master network-policy]# kubectl get networkpolicies -n dev
NAME POD-SELECTOR AGE
allow-all-ingress <none> 4s
[root@k8s-master network-policy]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-dev 1/1 Running 0 17m 10.244.2.2 node02 <none> <none>
[root@k8s-master network-policy]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-test 1/1 Running 0 17m 10.244.1.2 node01 <none> <none>
[root@k8s-master network-policy]# curl 10.244.2.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master network-policy]# curl 10.244.1.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master network-policy]# vim allow-myapp.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-myapp
spec:
podSelector:
matchLabels:
app: myapp
type: pod
ingress:
- from:
- ipBlock:
cidr: 10.244.0.0/16
except:
- 10.244.1.2/32
ports:
- protocol: TCP
port: 80
policyTypes:
- Ingress
[root@k8s-master network-policy]# kubectl apply -f allow-myapp.yaml -n dev
networkpolicy.networking.k8s.io/allow-all-myapp created
[root@k8s-master network-policy]# kubectl get networkpolicies -n dev
NAME POD-SELECTOR AGE
allow-all-myapp app=myapp,type=pod 6s
[root@k8s-master network-policy]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-dev 1/1 Running 0 30m 10.244.2.2 node02 <none> <none>
[root@k8s-master network-policy]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-test 1/1 Running 0 31m 10.244.1.2 node01 <none> <none>
[root@k8s-master network-policy]# curl 10.244.2.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master network-policy]# kubectl exec -it -n test demo-test -- /bin/sh
/ # ping 10.244.2.2
PING 10.244.2.2 (10.244.2.2): 56 data bytes
[root@k8s-master network-policy]# vim deny-all-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
spec:
podSelector: {}
policyTypes:
- Egress
[root@k8s-master network-policy]# kubectl apply -f deny-all-egress.yaml -n test
networkpolicy.networking.k8s.io/deny-all-egress created
[root@k8s-master network-policy]# kubectl get networkpolicies -n test
NAME POD-SELECTOR AGE
deny-all-egress <none> 18s
[root@k8s-master network-policy]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-test 1/1 Running 0 41m 10.244.1.2 node01 <none> <none>
[root@master01 ~]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-dev 1/1 Running 0 40m 10.244.2.2 node02 <none> <none>
[root@k8s-master network-policy]# curl 10.244.2.2
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@k8s-master network-policy]# kubectl exec -it -n test demo-test -- /bin/sh
/ # ping 10.244.2.2
PING 10.244.2.2 (10.244.2.2): 56 data bytes
[root@k8s-master network-policy]# vim allow-all-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Egress
[root@k8s-master network-policy]# kubectl apply -f allow-all-egress.yaml -n test
networkpolicy.networking.k8s.io/allow-all-egress created
[root@k8s-master network-policy]# kubectl get networkpolicies -n test
NAME POD-SELECTOR AGE
allow-all-egress <none> 11s
[root@k8s-master network-policy]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-dev 1/1 Running 0 47m 10.244.2.2 node02 <none> <none>
[root@k8s-master network-policy]# kubectl get pod -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo-test 1/1 Running 0 47m 10.244.1.2 node01 <none> <none>
[root@k8s-master network-policy]# kubectl exec -it -n test demo-test -- /bin/sh
/ # ping 10.244.2.2
PING 10.244.2.2 (10.244.2.2): 56 data bytes
64 bytes from 10.244.2.2: seq=0 ttl=62 time=0.837 ms
64 bytes from 10.244.2.2: seq=1 ttl=62 time=1.043 ms
64 bytes from 10.244.2.2: seq=2 ttl=62 time=0.290 ms
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: access-nginx
namespace: default
spec:
podSelector:
matchLabels:
run: nginx
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
另外一些默认的规则:
1、同namespace的pod,入站规则为全部禁止
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
2、同namespace的pod,入站规则为全部开放:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
3、同namespace的pod,出站规则为全部禁止
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Egress
4、同namespace的pod,出站规则为全部开放
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
5、通过创建一个可以选择所有 Pod 但不允许任何流量的 NetworkPolicy,你可以为一个 Namespace 创建一个 “默认的” 隔离策略,如下所示:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector:
6、这确保了即使是没有被任何 NetworkPolicy 选中的 Pod,将仍然是被隔离的。
可选地,在 Namespace 中,如果你想允许所有的流量进入到所有的 Pod(即使已经添加了某些策略,使一些 Pod 被处理为 “隔离的”),你可以通过创建一个策略来显式地指定允许所有流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector:
ingress:
- {}
默认情况下,滚动升级是逐个更新的,当有几十上百个POD需要更新时,再加上
就绪检测
,整个过程将会更慢。
解决方法:
rollingUpdate:
maxSurge: 20% #每个滚动更新的实例数量
maxUnavailable: 10% #允许更新过程中有多少实例不可用
通常,服务重启的时候会有一小段时间是
无法正常提供服务
的。 为了避免这个过程中有请求的流量进来,我们可以使用就绪检测
来检测服务是否就绪可正常接收并处理请求。
......
readinessProbe:
httpGet:
host: api.xxx.com
path: /
port: 80
initialDelaySeconds: 3 # 容器启动3秒后开始第一次检测
periodSeconds: 60 # 每隔60s检测一次
timeoutSeconds: 3 # http检测请求的超时时间
successThreshold: 1 # 检测到有1次成功则认为服务是`就绪`
failureThreshold: 1 # 检测到有1次失败则认为服务是`未就绪`
......
就绪检测是把双利剑,用不好,反而容易出大问题,比如服务全面瘫痪。 我们可以看到上面
就绪检测
的配置,漏洞百出。
比如:
高并发情况下,请求处理不过来,个别服务很容易导致检测请求的超时(504),立马被认为
未就绪
,于是流量被转移到其它服务,进而让本来就高负荷的其它服务出现同样情况,恶性循环,很快,所有服务都被认为是未就绪
,结果产生全面瘫痪
现象。
解决方法:
设置更长的超时时间,以及更高的失败次数。
这种情况可能是误操作,也可能是其它异常导致服务挂了。总之,你需要在用户还在不断尝试请求你服务的时候重启。你会惊讶的发现,一直无法正常启动为
就绪
状态,所有服务都是未就绪。同样的原因,服务启动过程不是一次全部起来,而是逐批启动,这样每批服务启动后都无法hold住流量,于是还是恶性循环,全面瘫痪
。
解决方法:
先去掉就绪检测再重新部署。
自动扩展POD虽然好用,但如果扩展的指标(CPU、内存等)设置的过高,如:50%以上,那么,当突然有翻倍的流量过来时,根本来不及扩展POD,服务直接就超时或挂掉。
解决方法:
尽可能的把指标设置在一个较小的值,对以往流量做参考评估,确保了当有2倍、3倍甚至5倍的流量突袭时不至于hold不住。
通常,节点的自动伸缩依赖于POD的自动扩展时资源是否充足。然而在面对定时突然流量高峰的业务时,这种伸缩显然来不及,甚至常常出现高峰10分钟后才扩容的机器,流量已经回到低谷,完全启不到作用。并且,流量到底是因为业务属性很快回落,还是因为扩容不及时导致的流失?
解决方法:
根据自身业务,参考以住流量数量及推广时间,找到规律,提前或定时触发自动扩容。
如何安全地移除节点?这个节点上面部署了你的业务,甚至包括kube-system的东西。
解决方法:
kubectl drain,可以先把节点上的POD驱逐到其它节点,然后再移出该节点。