在本文中,我将介绍一些在使用Kubernetes(K8s)时使用的最佳实践。
作为最流行的容器编排系统,K8s是现代云工程师应该掌握的一项技能。K8s是一个众所周知的复杂系统,因此了解您应该做什么,不应该做什么,这会对您的部署如虎添翼。
这些建议涵盖了应用程序开发、治理和集群配置这三个广泛的类别中的常见问题。
在K8s中,命名空间对于组织对象、在集群内创建逻辑分区以及出于安全目的是非常重要的。默认情况下,K8s集群中有三个命名空间,分别是default、kube-public和kube-system。
命名空间主要是起到一个隔离作用:
RBAC可以用于控制对特定命名空间的访问,以限制组的访问权限,控制可能发生的任何错误的影响范围,例如,开发人员组可能只能访问名为dev的命名空间,并且无法访问生产命名空间。限制不同团队对不同命名空间的访问能力可以避免重复工作或资源冲突。
还可以针对命名空间配置LimitRange对象,以定义部署在命名空间中的容器的标准大小。ResourceQuotas也可以用于限制命名空间内所有容器的总资源消耗。网络策略可以用于针对命名空间限制Pod之间的流量。
那namespace的相关操作有哪些呢?
# View existing namespaces
kubectl get namespaces
# List the pods contained in a namespace
kubectl get pods --namespace ingress-nginx
# Note the short format for namespace can be used (-n)
kubectl get pods -n ingress-nginx
# Create a new namespace called jacks-blog
kubectl create namespace jacks-blog
# Delete a namespace called jacks-blog
kubectl delete namespace jacks-blog
# Describe a namespace
kubectl describe namespace nginx-ingress
就绪探针和存活探针本质上是健康检查的一种类型。这些是K8s中要利用的另一个非常重要的概念。
就绪探针确保只有在Pod准备好提供请求时,请求才会被定向到该Pod。如果它尚未准备就绪,请求将被定向到其他地方。对于每个容器定义就绪探针非常重要,因为在K8s中没有为这些设置默认值。
例如,如果一个Pod需要20秒才能启动,并且缺少就绪探针,那么在启动期间定向到该Pod的任何流量都会导致失败。就绪探针应该是独立的,不考虑对其他服务的任何依赖,比如后端数据库或缓存服务。
存活探针测试应用程序是否正在运行,以标记其为健康状态。例如,可以测试Web应用程序的特定路径以确保其响应。如果未响应,Pod将不会被标记为健康,探针失败将导致kubelet启动一个新的Pod,然后再次进行测试。这种类型的探针用作在进程变得无响应时的恢复机制。
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: registry.k8s.io/liveness:0.1
ports:
- containerPort: 8080
livenessProbe:
exec:
command:
- cat
- /usr/share/liveness/html/index.html
initialDelaySeconds: 5
periodSeconds: 5
如果文件不存在,pod将会被杀死后重启
apiVersion: v1
kind: Pod
metadata:
name: liveness
labels:
app: liveness-tcp
spec:
containers:
- name: liveness
image: registry.k8s.io/liveness:0.1
ports:
- containerPort: 8080
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
kubelet将尝试与改特定port建立socket如果失败,pod将会被杀死后重启
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: registry.k8s.io/liveness:0.1
livenessProbe:
httpGet:
path: /health
port: 8080
httpHeaders:
- name: Custom-Header
value: ItsAlive
initialDelaySeconds: 5
periodSeconds: 5
which will send an HTTP GET request on port 8080 to the /health path. If a code between 200–400 is returned, the probe is considered successful.
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-gRPC
spec:
containers:
- name: liveness
image: registry.k8s.io/liveness:0.1
ports:
- containerPort: 2379
livenessProbe:
grpc:
port: 2379
initialDelaySeconds: 5
periodSeconds: 5
用grpc协议检测2379是否打开
在适当的情况下,可以使用自动缩放来动态调整Pod的数量(水平Pod自动缩放器)、Pod消耗的资源量(垂直自动缩放器)或集群中的节点数量(集群自动缩放器),具体取决于资源的需求。
水平Pod自动缩放器还可以根据CPU需求来调整replication controller, replica set, or statefulset的规模。
使用缩放也带来一些挑战,比如不要将持久数据存储在容器的本地文件系统中,因为这会阻止水平自动缩放。相反,可以使用PersistentVolume。。
当集群上存在高度变化的工作负载,可能根据需求在不同时间需要不同数量的资源时,集群自动缩放器就非常有用。自动删除未使用的节点也是节省成本的好方法。
具体pvc的操作如下:
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) k8s.io/minikube-hostpath Delete Immediate false 7d2h
这个就是主机存储
apiVersion: v1
kind: PersistentVolume
metadata:
name: demo-pv
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
storageClassName: standard
hostPath:
path: /tmp/demo-pv
$ kubectl apply -f pv.yaml
persistentvolume/demo-pv created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
demo-pv 1Gi RWO Retain Available standard 113s
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-dynamic
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard
$ kubectl apply -f pvc-dynamic.yaml
persistentvolumeclaim/pvc-dynamic created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
demo-pv 1Gi RWO Retain Bound default/demo-pvc standard 33m
pvc-fd022049-eabf-415c-937a-94927c84ef6f 1Gi RWO Delete Bound default/pvc-dynamic standard
#Dynamically created PVs default to using the Delete retention policy. This means the PV is automatically deleted when the PVC is destroyed.
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: pvc-pod-container
image: nginx:latest
volumeMounts:
- mountPath: /data
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-dynamic
pvc存储是独立于pod的
使用RBAC保护存储资源。配置集群的RBAC授权策略,以帮助保护您的PV和PVC免受意外或恶意更改和删除。
始终为您的持久卷(PV)指定存储类。没有存储类的PV将使用集群中的默认存储类,如果值发生更改或未设置,可能会导致不可预测的行为。
资源请求和限制(容器中可使用的资源的最小和最大量)应设置以避免容器在没有分配所需资源的情况下启动,或者集群耗尽可用资源。
没有限制,Pod可能会使用超过所需资源的资源,导致总可用资源减少,这可能会影响集群上的其他应用程序。节点可能会崩溃,并且调度器可能无法正确放置新的Pod。
如果没有请求,如果无法为应用程序分配足够的资源,则在尝试启动或执行时可能会失败。
资源请求和限制定义了以毫核和mebibytes为单位的CPU和内存量。请注意,如果您的进程超过内存限制,进程将被终止,因此在所有情况下设置这个值可能并不总是合适。如果您的容器超过CPU限制,进程将被限制。
单个Pod永远不应该单独运行。为了提高容错性,它们应始终作为部署(Deployment)、守护进程集(DaemonSet)、副本集(ReplicaSet)或有状态集(StatefulSet)的一部分。然后,可以使用部署中的反亲和规则在节点之间部署Pod,以避免所有Pod在单个节点上运行,这可能会导致宕机时间。
在单个节点上运行K8s如果您想构建容错性并不是一个好主意。您的集群应该使用多个节点,以便工作负载可以在它们之间分配。
角色(Role):角色是一组权限集合,允许用户对一组定义的Kubernetes资源类型(如Pod、部署和命名空间)执行特定操作(动词,例如“get”、“create”和“delete”)。角色是有命名空间的对象;它们授予的权限仅适用于角色所属的命名空间内。
集群角色(ClusterRole):集群角色与角色类似,但是它们是用于集群级资源的非命名空间替代方案。您需要使用集群角色来控制对不属于任何命名空间的对象(如节点)的访问。集群角色还允许您全局地访问跨所有命名空间的有命名空间资源,例如集群中的每个Pod。
角色绑定(RoleBinding):角色绑定表示您的角色与用户或服务账户之间的关联。角色绑定允许您引用一个角色,然后将这些权限授予一个或多个用户(称为主体)。
集群角色绑定(ClusterRoleBinding):集群角色绑定类似于角色绑定,但是针对的是集群角色资源而不是角色。
在您的K8s集群中使用RBAC对于正确保护您的系统至关重要。用户、组和服务账户可以被分配权限来执行特定命名空间(Role)或整个集群(ClusterRole)上的许可操作。每个角色可以有多个权限。为了将定义的角色与用户、组或服务账户联系起来,将使用RoleBinding或ClusterRoleBinding对象。
RBAC角色应该设置为遵循最小权限原则,即只授予所需的权限。例如,管理员组可能对所有资源具有访问权限,而您的运维组可能可以部署但无法读取机密信息。
$ kubectl api-versions | grep rbac
rbac.authorization.k8s.io/v1
To manually enable RBAC support, you must start the Kubernetes API server with the --authorization-mode=RBAC
flag set:
$ kube-apiserver --authorization-mode=RBAC
$ kubectl create serviceaccount demo-user
serviceaccount/demo-user created
创建认证token
$ TOKEN=$(kubectl create token demo-user)
kube config文件添加认证
$ kubectl config set-credentials demo-user --token=$TOKEN
User "demo-user" set.
设置context
$ kubectl config set-context demo-user-context --cluster=default --user=demo-user
Context "demo-user-context" created.
检查当前context,方便后续切回
$ kubectl config current-context
default
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: demo-role
namespace: default
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- create
- update
$ kubectl apply -f role.yaml
role.rbac.authorization.k8s.io/demo-role created
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: demo-role-binding
namespace: default
roleRef: # 啥角色
apiGroup: rbac.authorization.k8s.io
kind: Role
name: demo-role
subjects: # 绑定了哪些用户
- namespace: default
kind: ServiceAccount
name: demo-user
$ kubectl apply -f rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/demo-role-binding created
在自己的硬件上托管K8s集群可能是一个复杂的任务。云服务提供K8s集群作为平台即服务(PaaS),如Azure上的AKS(Azure Kubernetes Service)或亚马逊网络服务上的EKS(Amazon Elastic Kubernetes Service)。利用这一点意味着基础设施将由您的云提供商管理,以及围绕扩展您的集群的任务,如添加和移除节点,可以更容易地实现,从而使您的工程师可以专注于管理K8s集群本身上运行的内容。
除了引入新功能外,新的K8s版本还包括漏洞和安全修复程序,这使得在集群上运行最新版本的K8s至关重要。对于较旧版本的支持可能不如较新版本。
然而,迁移到新版本应该谨慎对待,因为某些功能可能会被弃用,同时还会添加新功能。此外,在升级之前,应检查在您的集群上运行的应用程序是否与新的目标版本兼容。
监控K8s控制平面中的组件对于控制资源消耗至关重要。控制平面是K8s的核心,这些组件使系统保持运行,因此对于正确的K8s操作至关重要。Kubernetes API、kubelet、etcd、controller-manager、kube-proxy和kube-dns构成了控制平面。
控制平面组件可以输出以Prometheus最常见的K8s监控工具可以使用的格式的指标。
应该使用自动化监控工具,而不是手动管理警报。
在启动kube-apiserver时可以打开K8s中的审计日志,以便使用您选择的工具进行更深入的调查。审计日志将详细记录对K8s API的所有请求,并应定期检查是否存在可能在集群上造成问题的问题。Kubernetes集群的默认策略在audit-policy.yaml文件中定义,可以根据需要进行修改。
可以使用日志聚合工具(例如Azure Monitor)将日志从AKS发送到日志分析工作区,以便将来使用Kusto查询进行详细调查。在AWS中,可以使用Cloudwatch。第三方工具还提供更深入的监控功能,如Dynatrace和Datadog。
最后,应该为日志设定一个明确的保留期限,通常为30-45天。
K8s配置文件应该在版本控制系统(VCS)中进行控制。这样可以带来一系列好处,包括增强安全性,启用更改审计跟踪,并提高集群的稳定性。应该为所做的任何更改设置批准门,以便团队可以在将更改提交到主分支之前进行同行审查。
成功部署K8s需要考虑团队使用的工作流程。使用基于git的工作流程通过CI/CD(持续集成/持续交付)管道的自动化,可以提高应用程序部署效率和速度。CI/CD还将提供部署的审计跟踪。Git应该是所有自动化的唯一真相来源,并将实现K8s集群的统一管理。您还可以考虑使用专用的基础设施交付平台,如Spacelift,该平台最近引入了对Kubernetes的支持。
较小的镜像大小将有助于加快构建和部署速度,并减少容器在K8s集群中消耗的资源量。应尽可能删除不必要的软件包,并优先选择小型操作系统分发镜像,如Alpine。较小的镜像可以比较大的镜像更快地拉取,并且占用更少的存储空间。
遵循这种方法还将提供安全性方面的好处,因为恶意行为者的攻击向量将更少。
K8s标签是附加到对象的键值对,以便组织您的集群资源。标签应该是有意义的元数据,提供一种跟踪K8s系统中不同组件交互方式的机制。
官方K8s文档中推荐的Pod标签包括名称、实例、版本、组件、部分和管理者。
标签也可以类似于在云环境中在资源上使用标记,以跟踪与业务相关的事物,如对象所有权和对象应属于的环境。
此外,建议使用标签详细说明安全要求,包括保密性和合规性。
应该使用网络策略来限制K8s集群中对象之间的流量。默认情况下,所有容器都可以在网络中互相通信,如果恶意行为者访问容器,则会导致安全风险,允许它们遍历集群中的对象。网络策略可以控制IP和端口级别的流量,类似于云平台中安全组的概念,以限制对资源的访问。通常,默认情况下应拒绝所有流量,然后应设置允许规则以允许所需流量。
除了使用网络策略在K8s集群中限制内部流量外,还应该在K8s集群前放置防火墙,以限制外部世界对API服务器的请求。应该将IP地址列入白名单,并限制开放端口。
声明性配置涉及在YAML或JSON文件中指定资源的期望状态,然后使用诸如kubectl apply之类的工具将这些配置应用于您的集群。
您描述资源应该是什么样子,而不是发出命令来实现特定状态。声明性配置是幂等的,这意味着多次应用相同的配置会产生相同的期望状态。
https://spacelift.io/blog/kubernetes-best-practices
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。