Kubernetes实战进阶

文章目录

搭建 Kubernetes 集群

搭建方案

minikube

minikube 是一个工具, 能让你在本地运行 Kubernetes。 minikube 在你的个人计算机(包括 Windows、macOS 和 Linux PC)上运行一个一体化(all-in-one)或多节点的本地 Kubernetes 集群,以便你来尝试 Kubernetes 或者开展每天的开发工作。
官方安装文档

kubeadm

你可以使用 kubeadm 工具来创建和管理 Kubernetes 集群。 该工具能够执行必要的动作并用一种用户友好的方式启动一个可用的、安全的集群。

安装 kubeadm 展示了如何安装 kubeadm 的过程。一旦安装了 kubeadm, 你就可以使用它来创建一个集群。

  1. 集群信息
    三台服务器(虚拟机 virtual box):k8s-master、k8s-node1、k8s-node2
    最低配置:2核、2G内存、20G硬盘

在使用virtualbox时总结以下操作

shell 复制代码
# 1.配置网卡
vi /etc/sysconfig/network-scripts/ifcfg-enp0s3

# 修改以下信息
BOOTPROTO=dhcp
ONBOOT=yes

# 2.配置国内yum源
# 备份原有repo文件
mkdir -p /etc/yum.repos.d/backup
mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/

# 使用阿里云
curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
  1. 初始操作
shell 复制代码
# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

# 关闭selinux
sed -i 's/enforcing/disabled/' /etc/selinux/config  # 永久
setenforce 0  # 临时

# 关闭swap
swapoff -a  # 临时
sed -ri 's/.*swap.*/#&/' /etc/fstab    # 永久

# 关闭完swap后,一定要重启一下虚拟机!!!
# 根据规划设置主机名
hostnamectl set-hostname <hostname>

# 在master添加hosts
cat >> /etc/hosts << EOF
192.168.56.115 k8s-master
192.168.56.116 k8s-node1
192.168.56.117 k8s-node2
EOF


# 将桥接的IPv4流量传递到iptables的链
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sysctl --system  # 生效


# 时间同步
yum install ntpdate -y
ntpdate -v ntp.aliyun.com
  1. 安装基础软件(所有节点)
    安装docker
shell 复制代码
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# PS:如果出现如下错误信息
Loaded plugins: fastestmirror
adding repo from: https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
grabbing file https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo to /etc/yum.repos.d/docker-ce.repo
Could not fetch/save url https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo to file /etc/yum.repos.d/docker-ce.repo: [Errno 14] curl#60 - "Peer's Certificate issuer is not recognized."
# 编辑 /etc/yum.conf 文件, 在 [main] 下面添加 sslverify=0 参数
vi /etc/yum.conf
# 配置如下----------------------
[main]
sslverify=0
# -----------------------------

# Step 3: 更新并安装Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce

# Step 4: 开启Docker服务
sudo systemctl start docker

# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ee.repo
#   将[docker-ce-test]下方的enabled=0修改为enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
#   Loading mirror speeds from cached hostfile
#   Loaded plugins: branch, fastestmirror, langpacks
#   docker-ce.x86_64            17.03.1.ce-1.el7.centos            docker-ce-stable
#   docker-ce.x86_64            17.03.1.ce-1.el7.centos            @docker-ce-stable
#   docker-ce.x86_64            17.03.0.ce-1.el7.centos            docker-ce-stable
#   Available Packages
# Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]

# step5 配置镜像加速和cgroup
# exec-opts:这个配置在使用 Kubernetes(K8s) 时尤为重要,因为 Kubernetes 要求容器运行时(如 Docker)与 kubelet 使用相同的 cgroup 驱动,否则会导致节点 NotReady、Pod 无法启动等问题。
# /etc/docker/daemon.json
sudo  tee /etc/docker/daemon.json <<-'EOF'
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker-0.unsee.tech",
    "https://docker.xuanyuan.me",
    "https://docker.m.daocloud.io"
  ]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl status docker # 确保是running状态
sudo systemctl enable docker

添加阿里云yum源

复制代码
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0

gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

安装 kubeadm、kubelet、kubectl

shell 复制代码
yum install -y kubelet-1.23.6 kubeadm-1.23.6 kubectl-1.23.6
systemctl start kubelet
systemctl enable kubelet
  1. 所有节点拉取镜像
shell 复制代码
# 查看初始化需要的镜像
kubeadm config images list

k8s.gcr.io/kube-apiserver:v1.23.17
k8s.gcr.io/kube-controller-manager:v1.23.17
k8s.gcr.io/kube-scheduler:v1.23.17
k8s.gcr.io/kube-proxy:v1.23.17
k8s.gcr.io/pause:3.6
k8s.gcr.io/etcd:3.5.1-0
k8s.gcr.io/coredns/coredns:v1.8.6

# 创建配置 kubeadm-config-image.yaml 替换成阿里云的源
vi kubeadm-config-image.yaml

apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
# 默认为k8s.gcr.io,但是网络不通,所以要替换为阿里云镜像
imageRepository: registry.aliyuncs.com/google_containers

# 确认镜像仓库改变
kubeadm config images list --config kubeadm-config-image.yaml

registry.aliyuncs.com/google_containers/kube-apiserver:v1.23.6
registry.aliyuncs.com/google_containers/kube-controller-manager:v1.23.6
registry.aliyuncs.com/google_containers/kube-scheduler:v1.23.6
registry.aliyuncs.com/google_containers/kube-proxy:v1.23.6
registry.aliyuncs.com/google_containers/pause:3.6
registry.aliyuncs.com/google_containers/etcd:3.5.1-0
registry.aliyuncs.com/google_containers/coredns:v1.8.6

# 拉取镜像,在 所有机器 上执行,把这些镜像提前拉好
kubeadm config images pull --config kubeadm-config-image.yaml
  1. 部署 Kubernetes Master
shell 复制代码
# 在 Master 节点下执行

kubeadm init \
      --apiserver-advertise-address=192.168.56.115 \
      --image-repository registry.aliyuncs.com/google_containers \
      --kubernetes-version v1.23.6 \
      --service-cidr=10.96.0.0/12 \
      --pod-network-cidr=10.244.0.0/16

# 安装成功后,复制如下配置并执行
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes
  1. 加入 Kubernetes Node
shell 复制代码
分别在 k8s-node1 和 k8s-node2 执行

# 下方命令可以在 k8s master 控制台初始化成功后出现的join 命令
kubeadm join 192.168.56.115:6443 --token uvdnzx.a75di0cj5mbef9ly \
        --discovery-token-ca-cert-hash sha256:ebd577352f7a19ff266990186b43fa9ac0f144e970ec4ab0e50c519801af61cc 


# 如果初始化的 token 不小心清空了,可以通过如下命令获取或者重新申请
# 如果 token 已经过期,就重新申请
kubeadm token create

# token 没有过期可以通过如下命令获取
kubeadm token list

# 获取 --discovery-token-ca-cert-hash 值,得到值后需要在前面拼接上 sha256:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'
  1. 部署 CNI 网络插件
shell 复制代码
# 在master上执行
curl https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml>>kube-flannel.yml
kubectl apply -f kube-flannel.yml

二进制安装

利用 k8s 官方 github 仓库下载二进制包安装,安装过程较复杂,但相对较为稳定,推荐生产环境使用。

命令行工具 kubectl

kubectl 是使用 Kubernetes API 与 Kubernetes 集群的控制面进行通信的命令行工具。
参考文档

资源操作

创建对象

shell 复制代码
$ kubectl create -f ./my-manifest.yaml           # 创建资源
$ kubectl create -f ./my1.yaml -f ./my2.yaml     # 使用多个文件创建资源
$ kubectl create -f ./dir                        # 使用目录下的所有清单文件来创建资源
$ kubectl create -f https://git.io/vPieo         # 使用 url 来创建资源
$ kubectl run nginx --image=nginx                # 启动一个 nginx 实例
$ kubectl explain pods,svc                       # 获取 pod 和 svc 的文档

# 从 stdin 输入中创建多个 YAML 对象
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000000"
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox-sleep-less
spec:
  containers:
  - name: busybox
    image: busybox
    args:
    - sleep
    - "1000"
EOF

# 创建包含几个 key 的 Secret
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: $(echo "s33msi4" | base64)
  username: $(echo "jane" | base64)
EOF

显示和查找资源

shell 复制代码
# Get commands with basic output
$ kubectl get services                          # 列出所有 namespace 中的所有 service
$ kubectl get pods --all-namespaces             # 列出所有 namespace 中的所有 pod
$ kubectl get pods -o wide                      # 列出所有 pod 并显示详细信息
$ kubectl get deployment my-dep                 # 列出指定 deployment
$ kubectl get pods --include-uninitialized      # 列出该 namespace 中的所有 pod 包括未初始化的

# 使用详细输出来描述命令
$ kubectl describe nodes my-node
$ kubectl describe pods my-pod

$ kubectl get services --sort-by=.metadata.name # List Services Sorted by Name

# 根据重启次数排序列出 pod
$ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# 获取所有具有 app=cassandra 的 pod 中的 version 标签
$ kubectl get pods --selector=app=cassandra rc -o \
  jsonpath='{.items[*].metadata.labels.version}'

# 获取所有节点的 ExternalIP
$ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 列出属于某个 PC 的 Pod 的名字
# "jq"命令用于转换复杂的 jsonpath,参考 https://stedolan.github.io/jq/
$ sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?}
$ echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# 查看哪些节点已就绪
$ JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \
 && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"

# 列出当前 Pod 中使用的 Secret
$ kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

更新资源

shell 复制代码
$ kubectl rolling-update frontend-v1 -f frontend-v2.json           # 滚动更新 pod frontend-v1
$ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2  # 更新资源名称并更新镜像
$ kubectl rolling-update frontend --image=image:v2                 # 更新 frontend pod 中的镜像
$ kubectl rolling-update frontend-v1 frontend-v2 --rollback        # 退出已存在的进行中的滚动更新
$ cat pod.json | kubectl replace -f -                              # 基于 stdin 输入的 JSON 替换 pod

# 强制替换,删除后重新创建资源。会导致服务中断。
$ kubectl replace --force -f ./pod.json

# 为 nginx RC 创建服务,启用本地 80 端口连接到容器上的 8000 端口
$ kubectl expose rc nginx --port=80 --target-port=8000

# 更新单容器 pod 的镜像版本(tag)到 v4
$ kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f -

$ kubectl label pods my-pod new-label=awesome                      # 添加标签
$ kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq       # 添加注解
$ kubectl autoscale deployment foo --min=2 --max=10                # 自动扩展 deployment "foo"

修补资源

shell 复制代码
$ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' # 部分更新节点

# 更新容器镜像; spec.containers[*].name 是必须的,因为这是合并的关键字
$ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

# 使用具有位置数组的 json 补丁更新容器镜像
$ kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'

# 使用具有位置数组的 json 补丁禁用 deployment 的 livenessProbe
$ kubectl patch deployment valid-deployment  --type json   -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'

编辑资源

shell 复制代码
$ kubectl edit svc/docker-registry                      # 编辑名为 docker-registry 的 service
$ KUBE_EDITOR="nano" kubectl edit svc/docker-registry   # 使用其它编辑器

scale 资源

shell 复制代码
$ kubectl scale --replicas=3 rs/foo                                 # Scale a replicaset named 'foo' to 3
$ kubectl scale --replicas=3 -f foo.yaml                            # Scale a resource specified in "foo.yaml" to 3
$ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql  # If the deployment named mysql's current size is 2, scale mysql to 3
$ kubectl scale --replicas=5 rc/foo rc/bar rc/baz                   # Scale multiple replication controllers

删除资源

shell 复制代码
$ kubectl delete -f ./pod.json                                              # 删除 pod.json 文件中定义的类型和名称的 pod
$ kubectl delete pod,service baz foo                                        # 删除名为"baz"的 pod 和名为"foo"的 service
$ kubectl delete pods,services -l name=myLabel                              # 删除具有 name=myLabel 标签的 pod 和 serivce
$ kubectl delete pods,services -l name=myLabel --include-uninitialized      # 删除具有 name=myLabel 标签的 pod 和 service,包括尚未初始化的
$ kubectl -n my-ns delete po,svc --all                                      # 删除 my-ns namespace 下的所有 pod 和 serivce,包括尚未初始化的

Pod 与集群

与运行的 Pod 交互

shell 复制代码
$ kubectl logs my-pod                                 # dump 输出 pod 的日志(stdout)
$ kubectl logs my-pod -c my-container                 # dump 输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用)
$ kubectl logs -f my-pod                              # 流式输出 pod 的日志(stdout)
$ kubectl logs -f my-pod -c my-container              # 流式输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用)
$ kubectl run -i --tty busybox --image=busybox -- sh  # 交互式 shell 的方式运行 pod
$ kubectl attach my-pod -i                            # 连接到运行中的容器
$ kubectl port-forward my-pod 5000:6000               # 转发 pod 中的 6000 端口到本地的 5000 端口
$ kubectl exec my-pod -- ls /                         # 在已存在的容器中执行命令(只有一个容器的情况下)
$ kubectl exec my-pod -c my-container -- ls /         # 在已存在的容器中执行命令(pod 中有多个容器的情况下)
$ kubectl top pod POD_NAME --containers               # 显示指定 pod 和容器的指标度量

与节点和集群交互

shell 复制代码
$ kubectl cordon my-node                                                # 标记 my-node 不可调度
$ kubectl drain my-node                                                 # 清空 my-node 以待维护
$ kubectl uncordon my-node                                              # 标记 my-node 可调度
$ kubectl top node my-node                                              # 显示 my-node 的指标度量
$ kubectl cluster-info                                                  # 显示 master 和服务的地址
$ kubectl cluster-info dump                                             # 将当前集群状态输出到 stdout                                    
$ kubectl cluster-info dump --output-directory=/path/to/cluster-state   # 将当前集群状态输出到 /path/to/cluster-state

# 如果该键和影响的污点(taint)已存在,则使用指定的值替换
$ kubectl taint nodes foo dedicated=special-user:NoSchedule

资源类型与别名

资源 别名
pods po
deployments deploy
services svc
namespace ns
nodes no

格式化输出

操作 参数
输出 json 格式 -o json
仅打印资源名称 -o name
以纯文本格式输出所有信息 -o wide
输出 yaml 格式 -o yaml

API 概述

REST API 是 Kubernetes 系统的重要部分,组件之间的所有操作和通信均由 API Server 处理的REST AP I调用,大多数情况下, API 定义和实现都符合标准的 HTTP REST 格式,可以通过 kubectl 命令管理工具或其他命令行工具来执行。

官网文档:https://kubernetes.io/zh-cn/docs/reference/using-api/

深入 pod

Pod 配置文件

参考文档:https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/

yaml 复制代码
apiVersion: v1 # api 文档版本
kind: Pod  # 资源对象类型,也可以配置为像Deployment、StatefulSet这一类的对象
metadata: # Pod 相关的元数据,用于描述 Pod 的数据
  name: nginx-demo # Pod 的名称
  labels: # 定义 Pod 的标签
    type: app # 自定义 label 标签,名字为 type,值为 app
    version: 1.0.0 # 自定义 label 标签,描述 Pod 版本号
  namespace: 'default' # 命名空间的配置
spec: # 期望 Pod 按照这里面的描述进行创建
  containers: # 对于 Pod 中的容器描述
  - name: nginx # 容器的名称
    image: nginx:1.7.9 # 指定容器的镜像
    imagePullPolicy: IfNotPresent # 镜像拉取策略,指定如果本地有就用本地的,如果没有就拉取远程的
    command: # 指定容器启动时执行的命令
    - nginx
    - -g
    - 'daemon off;' # nginx -g 'daemon off;'
    workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录
    ports:
    - name: http # 端口名称
      containerPort: 80 # 描述容器内要暴露什么端口
      protocol: TCP # 描述该端口是基于哪种协议通信的
    resources:
      requests: # 最少需要多少资源
        cpu: 100m # 限制 cpu 最少使用 0.1 个核心
        memory: 128Mi # 限制内存最少使用 128兆
      limits: # 最多可以用多少资源
        cpu: 200m # 限制 cpu 最多使用 0.2 个核心
        memory: 256Mi # 限制 最多使用 256兆
  restartPolicy: OnFailure # 重启策略,只有失败的情况才会重启

探针

容器内应用的监测机制,根据不同的探针来判断容器应用当前的状态

类型

  • StartupProbe
    k8s 1.16 版本新增的探针,用于判断应用程序是否已经启动了。
    当配置了 startupProbe 后,会先禁用其他探针,直到 startupProbe 成功后,其他探针才会继续。
    作用:由于有时候不能准确预估应用一定是多长时间启动成功,因此配置另外两种方式不方便配置初始化时长来检测,而配置了 statupProbe 后,只有在应用启动成功了,才会执行另外两种探针,可以更加方便的结合使用另外两种探针使用。

    startupProbe:
    httpGet:
    path: /api/startup
    port: 80

  • LivenessProbe
    用于探测容器中的应用是否运行,如果探测失败,kubelet 会根据配置的重启策略进行重启,若没有配置,默认就认为容器启动成功,不会执行重启策略。

yaml 复制代码
livenessProbe:
  failureThreshold: 5
  httpGet:
    path: /health
    port: 8080
    scheme: HTTP
  initialDelaySeconds: 60
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 5
  • ReadinessProbe
    用于探测容器内的程序是否健康,它的返回值如果返回 success,那么就认为该容器已经完全启动,并且该容器是可以接收外部流量的。
yaml 复制代码
readinessProbe:
  failureThreshold: 3 # 错误次数
  httpGet:
    path: /ready
    port: 8181
    scheme: HTTP
  periodSeconds: 10 # 间隔时间
  successThreshold: 1
  timeoutSeconds: 1

探测方式

  • ExecAction
    在容器内部执行一个命令,如果返回值为 0,则任务容器时健康的。
yaml 复制代码
livenessProbe:
  exec:
    command:
      - cat
      - /health
  • TCPSocketAction
    通过 tcp 连接监测容器内端口是否开放,如果开放则证明该容器健康
yaml 复制代码
livenessProbe:
  tcpSocket:
    port: 80
  • HTTPGetAction
    生产环境用的较多的方式,发送 HTTP 请求到容器内的应用程序,如果接口返回的状态码在 200~400 之间,则认为容器健康。
yaml 复制代码
livenessProbe:
  failureThreshold: 5
  httpGet:
    path: /health
    port: 8080
    scheme: HTTP
    httpHeaders:
      - name: xxx
        value: xxx

参数配置

yaml 复制代码
initialDelaySeconds: 60 # 初始化时间
timeoutSeconds: 2 # 超时时间
periodSeconds: 5 # 监测间隔时间
successThreshold: 1 # 检查 1 次成功就表示成功
failureThreshold: 2 # 监测失败 2 次就表示失败

生命周期

yml 复制代码
lifecycle:
  postStart: # 容创建完成后执行的动作,不能保证该操作一定在容器的 command 之前执行,一般不使用
    exec: # 可以是 exec / httpGet / tcpSocket
      command:
        - sh
        - -c
        - 'mkdir /data'
  preStop: # 在容器停止前执行的动作
    httpGet: # 发送一个 http 请求
      path: /
      port: 80
    exec: # 执行一个命令
      command:
        - sh
        - -c
        - sleep 9

资源调度

Label 和 Selector

标签(Label)

在各类资源的 metadata.labels 中进行配置

shell 复制代码
# 临时创建 label
kubectl label po <资源名称> app=hello

# 修改已经存在的标签
kubectl label po <资源名称> app=hello2 --overwrite

# 查看 label
# selector 按照 label 单值查找节点
kubectl get po -A -l app=hello

# 查看所有节点的 labels
kubectl get po --show-labels

选择器(Selector)

在各对象的配置 spec.selector 或其他可以写 selector 的属性中编写

shell 复制代码
# 匹配单个值,查找 app=hello 的 pod
kubectl get po -A -l app=hello

# 匹配多个值
kubectl get po -A -l 'k8s-app in (metrics-server, kubernetes-dashboard)'
或 

# 查找 version!=1 and app=nginx 的 pod 信息
kubectl get po -l version!=1,app=nginx

# 不等值 + 语句
kubectl get po -A -l version!=1,'app in (busybox, nginx)'

Deployment

功能

创建

shell 复制代码
# 创建一个 deployment
kubectl create deploy nginx-deploy --image=nginx:latest

# --record 会在 annotation 中记录当前命令创建或升级了资源,后续可以查看做过哪些变动操作。
kubectl create -f xxx.yaml --record

# 查看部署信息
kubectl get deployments

# 查看 rs
kubectl get rs

# 查看 pod 以及展示标签,可以看到是关联的那个 rs
kubectl get pods --show-labels

滚动更新

只有修改了 deployment 配置文件中的 template 中的属性后,才会触发更新操作

shell 复制代码
# 修改 nginx 版本号
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

# 或者通过edit进行修改
kubectl edit deployment/nginx-deployment 

# 查看滚动更新的过程
kubectl rollout status deploy <deployment_name>

# 查看部署描述,最后展示发生的事件列表也可以看到滚动更新过程
kubectl describe deploy <deployment_name>

# 通过 kubectl get deployments 获取部署信息,UP-TO-DATE 表示已经有多少副本达到了配置中要求的数目
kubectl get deployments
# 通过 kubectl get rs 可以看到增加了一个新的 rs
kubectl get rs
# 通过 kubectl get pods 可以看到所有 pod 关联的 rs 变成了新的
kubectl get pods

多个滚动更新并行:假设当前有 5 个 nginx:1.7.9 版本,你想将版本更新为 1.9.1,当更新成功第三个以后,你马上又将期望更新的版本改为 1.9.2,那么此时会立马删除之前的三个,并且立马开启更新 1.9.2 的任务

回滚

有时候你可能想回退一个Deployment,例如,当Deployment不稳定时,比如一直crash looping。

默认情况下,kubernetes会在系统中保存前两次的Deployment的rollout历史记录,以便你可以随时会退(你可以修改revision history limit来更改保存的revision数)。

可以通过设置 .spec.revisonHistoryLimit 来指定 deployment 保留多少 revison,如果设置为 0,则不允许 deployment 回退了。

shell 复制代码
# 案例:
# 更新 deployment 时参数不小心写错,如 nginx:1.9.1 写成了 nginx:1.91
kubectl set image deployment/nginx-deploy nginx=nginx:1.91

# 监控滚动升级状态,由于镜像名称错误,下载镜像失败,因此更新过程会卡住
kubectl rollout status deployments nginx-deploy

# 结束监听后,获取 rs 信息,我们可以看到新增的 rs 副本数是 2 个
kubectl get rs

# 通过 kubectl get pods 获取 pods 信息,我们可以看到关联到新的 rs 的 pod,状态处于 ImagePullBackOff 状态

# 为了修复这个问题,我们需要找到需要回退的 revision 进行回退
# 通过 kubectl rollout history deployment/nginx-deploy 可以获取 revison 的列表
kubectl rollout history deployment/nginx-deploy

# 通过 kubectl rollout history deployment/nginx-deploy --revision=2 可以查看详细信息
kubectl rollout history deployment/nginx-deploy --revision=2 

# 确认要回退的版本后,可以通过 kubectl rollout undo deployment/nginx-deploy 可以回退到上一个版本
kubectl rollout undo deployment/nginx-deploy 

# 也可以回退到指定的 revision
kubectl rollout undo deployment/nginx-deploy --to-revision=2

# 再次通过 kubectl get deployment 和 kubectl describe deployment 可以看到,我们的版本已经回退到对应的 revison 上了
kubectl get deployment
kubectl describe deployment

扩容缩容

通过 kube scale 命令可以进行自动扩容/缩容,以及通过 kube edit 编辑 replcas 也可以实现扩容/缩容

扩容与缩容只是直接创建副本数,没有更新 pod template 因此不会创建新的 rs

暂停与恢复

由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作,那么此时如果频繁修改信息,就会产生多次更新,而实际上只需要执行最后一次更新即可,当出现此类情况时我们就可以暂停 deployment 的 rollout

shell 复制代码
# 通过 kubectl rollout pause deployment <name> 就可以实现暂停,直到你下次恢复后才会继续进行滚动更新
kubectl rollout pause deployment <name>

# 尝试对容器进行修改,然后查看是否发生更新操作了
kubectl set image deploy <name> nginx=nginx:1.17.9
kubectl get po 

# 通过以上操作可以看到实际并没有发生修改,此时我们再次进行修改一些属性,如限制 nginx 容器的最大cpu为 0.2 核,最大内存为 128M,最小内存为 64M,最小 cpu 为 0.1 核
kubectl set resources deploy <deploy_name> -c <container_name> --limits=cpu=200m,memory=128Mi --requests=cpu100m,memory=64Mi

# 通过格式化输出 kubectl get deploy <name> -oyaml,可以看到配置确实发生了修改,再通过 kubectl get po 可以看到 pod 没有被更新
kubectl get deploy <name> -oyaml
kubectl get po

# 那么此时我们再恢复 rollout,通过命令 kubectl rollout deploy <name>
kubectl rollout deploy <name>

# 恢复后,我们再次查看 rs 和 po 信息,我们可以看到就开始进行滚动更新操作了
kubectl get rs
kubectl get po

配置文件

yaml 复制代码
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
  labels: # 标签
    app: nginx-deploy # 具体的 key: value 配置形式
  name: nginx-deploy # deployment 的名字
  namespace: default # 所在的命名空间
spec:
  replicas: 1 # 期望副本数
  revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
  selector: # 选择器,用于找到匹配的 RS
    matchLabels: # 按照标签匹配
      app: nginx-deploy # 匹配的标签key/value
  strategy: # 更新策略
    rollingUpdate: # 滚动更新配置
      maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
      maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
    type: RollingUpdate # 更新类型,采用滚动更新
  template: # pod 模板
    metadata: # pod 的元信息
      labels: # pod 的标签
        app: nginx-deploy
    spec: # pod 期望信息
      containers: # pod 的容器
      - image: nginx:1.7.9 # 镜像
        imagePullPolicy: IfNotPresent # 拉取策略
        name: nginx # 容器名称
      restartPolicy: Always # 重启策略
      terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间

StatefulSet

功能

创建

shell 复制代码
kubectl create -f web.yaml

# 查看 service 和 statefulset => sts
kubectl get service nginx
kubectl get statefulset web

# 查看 PVC 信息
kubectl get pvc

# 查看创建的 pod,这些 pod 是有序的
kubectl get pods -l app=nginx

# 查看这些 pod 的 dns
# 运行一个 pod,基础镜像为 busybox 工具包,利用里面的 nslookup 可以看到 dns 信息
kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh
nslookup web-0.nginx

扩容缩容

shell 复制代码
# 扩容
$ kubectl scale statefulset web --replicas=5

# 缩容
$ kubectl patch statefulset web -p '{"spec":{"replicas":3}}'

镜像更新

shell 复制代码
# 镜像更新(目前还不支持直接更新 image,需要 patch 来间接实现)
kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]'

更新策略:

yaml 复制代码
  updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate
    # type: OnDelete

RollingUpdate:

StatefulSet 也可以采用滚动更新策略,同样是修改 pod template 属性后会触发更新,但是由于 pod 是有序的,在 StatefulSet 中更新时是基于 pod 的顺序倒序更新的

灰度发布:

利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效果

例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod

利用该机制,我们可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再主键增大更新的 pod 数量,最终实现全部 pod 更新

OnDelete:只有在 pod 被删除时会进行更新操作

删除

shell 复制代码
# 删除 StatefulSet 和 Headless Service
# 级联删除:删除 statefulset 时会同时删除 pods
kubectl delete statefulset web
# 非级联删除:删除 statefulset 时不会删除 pods,删除 sts 后,pods 就没人管了,此时再删除 pod 不会重建的
kubectl delete sts web --cascade=false
# 删除 service
kubectl delete service nginx

删除 pvc

shell 复制代码
# StatefulSet删除后PVC还会保留着,数据不再使用的话也需要删除
$ kubectl delete pvc www-web-0 www-web-1

配置文件

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:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

DaemonSet

配置文件

yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
spec:
  selector:
    matchLabels:
      app: logging
  template:
    metadata:
      labels:
        app: logging
        id: fluentd
      name: fluentd
    spec:
      containers:
      - name: fluentd-es
        image: agilestacks/fluentd-elasticsearch:v1.3.0
        env:
         - name: FLUENTD_ARGS
           value: -qq
        volumeMounts:
         - name: containers
           mountPath: /var/lib/docker/containers
         - name: varlog
           mountPath: /varlog
      volumes:
         - hostPath:
             path: /var/lib/docker/containers
           name: containers
         - hostPath:
             path: /var/log
           name: varlog

指定 Node 节点

DaemonSet 会忽略 Node 的 unschedulable 状态,有两种方式来指定 Pod 只运行在指定的 Node 节点上:
nodeSelector

只调度到匹配指定 label 的 Node 上

shell 复制代码
# 先为 Node 打上标签
kubectl label nodes k8s-node1 svc_type=microsvc

# 然后再 daemonset 配置中设置 nodeSelector
spec:
  template:
    spec:
      nodeSelector:
        svc_type: microsvc

nodeAffinity

node亲和力,功能更丰富的 Node 选择器,支持集合操作。

nodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。

podAffinity

pod亲和力,调度到满足条件的 Pod 所在的 Node 上。

podAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。

滚动更新

不建议使用 RollingUpdate,建议使用 OnDelete 模式,这样避免频繁更新 ds

应用案例

HPA 自动扩缩容

通过观察 pod 的 cpu、内存使用率或自定义 metrics 指标进行自动的扩容或缩容 pod 的数量。

通常用于 Deployment,不适用于无法扩/缩容的对象,如 DaemonSet。

控制管理器每隔30s(可以通过--horizontal-pod-autoscaler-sync-period修改)查询metrics的资源使用情况。
开启指标服务

shell 复制代码
# 下载 metrics-server 组件配置文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml

# 修改镜像地址为国内的地址
sed -i 's/k8s.gcr.io\/metrics-server/registry.cn-hangzhou.aliyuncs.com\/google_containers/g' metrics-server-components.yaml

# 修改容器的 tls 配置,不验证 tls,在 containers 的 args 参数中增加 --kubelet-insecure-tls 参数

# 安装组件
kubectl apply -f metrics-server-components.yaml

# 查看 pod 状态
kubectl get pods --all-namespaces | grep metrics

cpu、内存指标监控

实现 cpu 或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpu 或 resources.requests.memory 才可以,可以配置当 cpu/memory 达到上述配置的百分比后进行扩容或缩容

shell 复制代码
# 创建一个 HPA:
# 先准备一个好一个有做资源限制的 deployment,cpu占用达到20%进行扩容,最少2个副本,最多5个副本
kubectl autoscale deploy nginx-deploy --cpu-percent=20 --min=2 --max=5
# 获取 HPA 信息
kubectl get hpa

# 测试:找到对应服务的 service,编写循环测试脚本提升内存与 cpu 负载
while true; do wget -q -O- http://<ip:port> > /dev/null ; done

# 可以通过多台机器执行上述命令,增加负载,当超过负载后可以查看 pods 的扩容情况 kubectl get pods

# 查看 pods 资源使用情况
kubectl top pods

# 扩容测试完成后,再关闭循环执行的指令,让 cpu 占用率降下来,然后过 5 分钟后查看自动缩容情况

自定义 metrics

  • 控制管理器开启--horizontal-pod-autoscaler-use-rest-clients
  • 控制管理器的--apiserver指向API Server Aggregator
  • 在API Server Aggregator中注册自定义的metrics API

服务发布

Service

Service 的定义

命令操作

shell 复制代码
# 创建 service
kubectl create -f nginx-svc.yaml

# 查看 service 信息,通过 service 的 cluster ip 进行访问
kubectl get svc 

# 查看 pod 信息,通过 pod 的 ip 进行访问
kubectl get po -o wide

# 创建其他 pod 通过 service name 进行访问(推荐)
kubectl exec -it busybox -- sh
curl http://nginx-svc

# 默认在当前 namespace 中访问,如果需要跨 namespace 访问 pod,则在 service name 后面加上 .<namespace> 即可
curl http://nginx-svc.default

配置

yaml 复制代码
apiVersion: v1
kind: Service #资源类型
metadata:
  name: nginx-svc # Service名字
  labels:
    app: nginx # 自己本身的标签
spec:
  selector: # 匹配那些pod会被该service代理
    app: nginx-deploy # 所有匹配到这些标签的pod都可以通过该service访问
  ports: # 端口映射
  - port: 80 # service自己的端口,使用内网ip时使用
    targetPort: 80 # 目标pod的端口
    name: web #为端口起个名字
    nodePort: 30007  # 可选,不指定则自动分配
  type: NodePort # 随机启动一个端口(30000-32767),映射到prots中的端口,该端口是直接绑定在node上的,且集群中每一个node都会绑定这个端口;也可以用于将服务暴露给外部访问,但是这种方式实现方式实际生产环境不推荐,效率低,而且Service是四层负载,只在测试时使用

代理 k8s 外部服务

代理 k8s 外部服务主要用于各环境访问名称统一、访问 k8s 集群外的其他服务以及项目迁移。

实现方式:

  1. 编写 service 配置文件时,不指定 selector 属性
  2. 自己创建 endpoint
    endpoint 配置:
yaml 复制代码
apiVersion: v1
kind: Endpoints
metadata:
  labels:
    app: svc-external # 与 service 一致
  name: svc-external # 与 service 一致
  namespace: default # 与 service 一致
subsets:
- addresses:
  - ip: <target ip> # 目标 ip 地址
  ports: # 与 service 一致
  - name: http
    port: 80
    protocol: TCP

反向代理外部域名

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  labels:
    app: external-domain
  name: external-domain
spec:
  type: ExternalName
  externalName: www.xxx.cn

常用类型

类型 说明
ClusterIP 只能在集群内部使用,不配置类型的话默认就是 ClusterIP
ExternalName 返回定义的 CNAME 别名,可以配置为域名
NodePort 会在所有安装了 kube-proxy 的节点都绑定一个端口,此端口可以代理至对应的 Pod,集群外部可以使用任意节点 ip + NodePort 的端口号访问到集群中对应 Pod 中的服务。 当类型设置为 NodePort 后,可以在 ports 配置中增加 nodePort 配置指定端口,需要在下方的端口范围内,如果不指定会随机指定端口 端口范围:30000~32767 端口范围配置在 /usr/lib/systemd/system/kube-apiserver.service 文件中
LoadBalancer 使用云服务商(阿里云、腾讯云等)提供的负载均衡器服务

Ingress

Ingress 可以理解为是一种 LB 的抽象,它的实现也是支持 nginx、haproxy 等负载均衡服务的

安装 ingress-nginx

参考文档:https://kubernetes.github.io/ingress-nginx/deploy/#using-helm

shell 复制代码
kubectl create ns ingress-nginx
helm install my-ingress ingress-nginx/ingress-nginx \
  --repo https://ghproxy.com/https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx \
  --set controller.image.repository=registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller \
  --set controller.admissionWebhooks.patch.image.repository=registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen

基本使用

创建一个 ingress

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules: # ingress 规则配置,可以配置多个
  - host: k8s.ingress.cn # 域名配置,可以使用通配符 *
    http:
      paths: # 相当于 nginx 的 location 配置,可以配置多个
      - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service: 
            name: nginx-svc # 代理到哪个 service
            port: 
              number: 80 # service 的端口
        path: /api # 等价于 nginx 中的 location 的路径前缀匹配

多域名配置

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules: # ingress 规则配置,可以配置多个
  - host: k8s.ingress.cn # 域名配置,可以使用通配符 *
    http:
      paths: # 相当于 nginx 的 location 配置,可以配置多个
      - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service: 
            name: nginx-svc # 代理到哪个 service
            port: 
              number: 80 # service 的端口
        path: /api # 等价于 nginx 中的 location 的路径前缀匹配
      - pathType: Exec # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service:
            name: nginx-svc # 代理到哪个 service
            port:
              number: 80 # service 的端口
        path: /
  - host: api.ingress.cn # 域名配置,可以使用通配符 *
    http:
      paths: # 相当于 nginx 的 location 配置,可以配置多个
      - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
        backend:
          service:
            name: nginx-svc # 代理到哪个 service
            port:
              number: 80 # service 的端口
        path: /

注意:要让 Kubernetes 中的 Ingress 资源对外部(集群外)可访问,关键在于 Ingress Controller 的 Service 类型和网络配置。Ingress 本身只是一个规则定义,真正处理流量的是 Ingress Controller(如 ingress-nginx)。

配置与存储

配置管理

ConfigMap

一般用于去存储 Pod 中应用所需的一些配置信息,或者环境变量,将配置于 Pod 分开,避免应为修改配置导致还需要重新构建 镜像与容器。

ConfigMap可以理解为明文的键值对

  • 创建:使用 kubectl create configmap -h 查看示例,构建 configmap 对象
shell 复制代码
  # Create a new config map named my-config based on folder bar
  kubectl create configmap my-config --from-file=path/to/bar

  # Create a new config map named my-config with specified keys instead of file basenames on disk
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
  • 使用ConfigMap
yaml 复制代码
#### docker env
apiVersion: v1
kind: Pod
metadata:
  name: test-env-cm
spec:
  containers:
    - name: env-test
      image: alpine
      command: ["/bin/sh", "-c", "env;sleep 3600"]
      imagePullPolicy: IfNotPresent
      env:
      - name: JAVA_VM_OPTS
        valueFrom:
          configMapKeyRef:
            name: test-env-config  # configmap的名字
            key: JAVA_OPTS_TEST # 表示从name的configmap中获取名字为key的value,将其赋值给本地环境变量JAVA_VM_OPTS
      - name: APP
        valueFrom:
          configMapKeyRef:
            name: test-env-config
            key: APP_NAME

#### 数据卷
apiVersion: v1
kind: Pod
metadata:
  name: test-configfile-po
spec:
  containers:
    - name: env-test
      image: alpine
      command: ["/bin/sh", "-c", "sleep 3600"]
      imagePullPolicy: IfNotPresent
      env:
      - name: JAVA_VM_OPTS
        valueFrom:
          configMapKeyRef:
            name: test-env-config  # configmap的名字
            key: JAVA_OPTS_TEST # 表示从name的configmap中获取名字为key的value,将其赋值给本地环境变量JAVA_VM_OPTS
      - name: APP
        valueFrom:
          configMapKeyRef:
            name: test-env-config
            key: APP_NAME
      volumeMounts: # 加载数据卷
      - name: db-config # 表示加载volumes属性中哪个数据卷
        mountPath: "/usr/local/mysql/conf" # 想要将数据卷重点文件加载到哪个目录下
        readOnly: true #是否只读
  volumes: # 数据卷挂载 configmap、secret
    - name: db-config
      configMap: # 数据卷类型为configMap
        name: test-dir-config # configMap名字,必须与想要加载的configMap相同
        items: # 对configmap中的key进行映射,如果不指定,默认会将configmap重点所有key全面转换为一个同名文件
        - key: "db.properties" # configMap中的key
          path: "db.properties"  # 将该key的值转换为文件

加密数据配置 Secret

与 ConfigMap 类似,用于存储配置信息,但是主要用于存储敏感信息、需要加密的信息,Secret 可以提供数据加密、解密功能。

在创建 Secret 时,要注意如果要加密的字符中,包含了有特殊字符,需要使用转义符转移,例如 转移后为 ,也可以对特殊字符使用单引号描述,这样就不需要转移例如 1289\*-! 转换为 '1289*-!'

shell 复制代码
kubectl create secret -h

Available Commands:
  docker-registry   Create a secret for use with a Docker registry
  generic           Create a secret from a local file, directory, or literal value
  tls               Create a TLS secret

kubectl create secret generic  orig-secret --from-literal=username=admin --from-literal=password=123456

kubectl create secret docker-registry harbor-secret --docker-username=admin --docker-password=123456 --docker-email=daiwl@126.com
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: test-private
spec:
  imagePullSecrets:   # 配置登录docker registry的secret
  - name: harbor-secret
  containers:
    - name: env-test
      image: 59.110.234.51:5000/mynginx:1.0.0
      command: ["/bin/sh", "-c", "env;sleep 3600"]
      imagePullPolicy: IfNotPresent
      env:
      - name: JAVA_VM_OPTS
        valueFrom:
          configMapKeyRef:
            name: test-env-config  # configmap的名字
            key: JAVA_OPTS_TEST # 表示从name的configmap中获取名字为key的value,将其赋值给本地环境变量JAVA_VM_OPTS
      - name: APP
        valueFrom:
          configMapKeyRef:
            name: test-env-config
            key: APP_NAME

SubPath 的使用

使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉,此时我们可能只想覆盖目录中的某一个文件,因此需要使用到 SubPath

配置方式:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/nginx.conf  # 注意:这是文件路径
      subPath: nginx.conf               # 卷中文件的相对路径(即 key 名)
  volumes:
  - name: config-volume
    configMap:
      name: nginx-conf
      # 可选:如果想重命名或控制权限,才用 items
      # items:
      # - key: nginx.conf
      #   path: nginx.conf

配置的热更新

在 Kubernetes 中,我们通常将应用的配置文件存入 ConfigMap,然后挂载到 Pod 中使用。那么,当 ConfigMap 被更新后,Pod 中的配置是否会自动更新?答案是:取决于挂载方式。

不同挂载方式对热更新的支持情况

挂载方式 是否支持热更新 说明
Volume 挂载(完整目录) kubelet 会定期(默认每 1 分钟)将 ConfigMap 的最新内容同步到挂载点。因此配置变更最终会生效,但存在延迟。
subPath 挂载单个文件 这是 Kubernetes 的已知限制,即使 ConfigMap 更新,通过 subPath 挂载的文件内容不会自动刷新。
通过环境变量注入 环境变量在容器启动时注入,后续 ConfigMap 的变更不会反映到运行中的容器中。

注意:即使配置文件内容更新成功,如果应用进程已经打开该文件(如 Nginx 在启动时读取 nginx.conf),它可能仍持有旧文件的 inode。此时需要应用自身支持 动态 reload(例如执行 nginx -s reload)才能加载新配置。

如何绕过 subPath 无法热更新的问题?

一种常用方案是:

  1. 将 ConfigMap 挂载到一个独立的新目录(如 /config/),避免覆盖原有目录;
  2. 使用 软链接(symbolic link) 将挂载的配置文件指向目标路径;
  3. 若目标路径已有文件,需先删除再创建软链接(可通过 postStart 生命周期钩子实现)。

示例:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: config-volume
      mountPath: /config          # 挂载到独立目录
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - -c
          - |
            rm -f /etc/nginx/nginx.conf && \
            ln -sf /config/nginx.conf /etc/nginx/nginx.conf && \
            nginx -s reload  # 可选:触发应用 reload
  volumes:
  - name: config-volume
    configMap:
      name: nginx-conf

软链接方案要求应用能响应配置变化(如支持 reload)。

如果目标文件被进程锁定或只读,rm 或 ln 可能失败,需确保权限和状态允许操作。

如何高效更新 ConfigMap(基于本地配置文件)?

通常我们通过 --from-file 创建 ConfigMap,但 kubectl replace 命令不支持 --from-file 参数。此时可借助 --dry-run=client 生成 YAML 并管道传递给 apply 或 replace。

推荐方式:使用 apply(支持创建 + 更新)

bash 复制代码
kubectl create cm nginx-conf \
  --from-file=nginx.conf \
  --dry-run=client -o yaml | \
  kubectl apply -f -

若确定 ConfigMap 已存在,也可用 replace:

shell 复制代码
kubectl create cm nginx-conf \
  --from-file=nginx.conf \
  --dry-run=client -o yaml | \
  kubectl replace -f -

不可变的 Secret 和 ConfigMap

对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置 configmap 时可以设置 immutable: true 来禁止修改

yaml 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: secure-app-config
data:
  app.properties: |
    debug=false
    timeout=30s
immutable: true  # ← 关键配置

持久化存储

Volumes

HostPath

将节点上的文件或目录挂载到 Pod 上,此时该目录会变成持久化存储目录,即使 Pod 被删除后重启,也可以重新加载到该目录,该目录下的文件不会丢失。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: nginx-volume
    volumeMounts:
    - mountPath: /test-pd # 挂载到容器的哪个目录
      name: test-volume # 挂载哪个 volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data # 节点中的目录
      type: Directory # 检查类型,在挂载前对挂载目录做什么检查操作,有多种选项,默认为空字符串,不做任何检查

类型:

  • 空字符串:默认类型,不做任何检查
  • DirectoryOrCreate:如果给定的 path 不存在,就创建一个 755 的空目录
  • Directory:这个目录必须存在
  • FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限为 644
  • File:这个文件必须存在
  • Socket:UNIX 套接字,必须存在
  • CharDevice:字符设备,必须存在
  • BlockDevice:块设备,必须存在
EmptyDir

EmptyDir 主要用于一个 Pod 中不同的 Container 共享数据使用的,由于只是在 Pod 内部使用,因此与其他 volume 比较大的区别是,当 Pod 如果被删除了,那么 emptyDir 也会被删除。

存储介质可以是任意类型,如 SSD、磁盘或网络存储。可以将 emptyDir.medium 设置为 Memory 让 k8s 使用 tmpfs(内存支持文件系统),速度比较快,但是重启 tmpfs 节点时,数据会被清除,且设置的大小会计入到 Container 的内存限制中。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: nginx-emptydir1
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  - image: nginx
    name: nginx-emptydir2
    volumeMounts:
    - mountPath: /opt
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

NFS 挂载

nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
安装 nfs

shell 复制代码
# 安装 nfs
yum install nfs-utils -y

# 启动 nfs
systemctl start nfs-server

# 查看 nfs 版本
cat /proc/fs/nfsd/versions

# 创建共享目录
mkdir -p /data/nfs
cd /data/nfs
mkdir rw
mkdir ro

# 设置共享目录 export
vim /etc/exports
/data/nfs/rw 192.168.113.0/24(rw,sync,no_subtree_check,no_root_squash)
/data/nfs/ro 192.168.113.0/24(ro,sync,no_subtree_check,no_root_squash)

# 重新加载
exportfs -f
systemctl reload nfs-server

# 到其他测试节点安装 nfs-utils 并加载测试
mkdir -p /mnt/nfs/rw
mount -t nfs 192.168.113.121:/data/nfs/rw /mnt/nfs/rw

配置文件

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    name: test-container
    volumeMounts:
    - mountPath: /my-nfs-data
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: my-nfs-server.example.com # 网络存储服务地址
      path: /my-nfs-volume # 网络存储路径
      readOnly: true # 是否只读

PV 与 PVC

持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。

持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。

生命周期
构建

静态构建

集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。

动态构建

如果集群中已经有的 PV 无法满足 PVC 的需求,那么集群会根据 PVC 自动构建一个 PV,该操作是通过 StorageClass 实现的。

想要实现这个操作,前提是 PVC 必须设置 StorageClass,否则会无法动态构建该 PV,可以通过启用 DefaultStorageClass 来实现 PV 的构建。

绑定

当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。

如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV,若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。

使用

Pod 将 PVC 当作存储卷来使用,集群会通过 PVC 找到绑定的 PV,并为 Pod 挂载该卷。

Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。

回收策略

当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。 目前,数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除)。

  • 保留(Retain)
    回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷:
    (1)删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在。
    (2)根据情况,手动清除所关联的存储资产上的数据。
    (3)手动删除所关联的存储资产。
    如果希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。
  • 删除(Delete)
    对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。管理员需要根据用户的期望来配置 StorageClass; 否则 PV 卷被创建之后必须要被编辑或者修补。
  • 回收(Recycle)
    警告: 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。
    如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。
PV

状态

  • Available:空闲,未被绑定
  • Bound:已经被 PVC 绑定
  • Released:PVC 被删除,资源已回收,但是 PV 未被重新使用
  • Failed:自动回收失败

配置文件

yaml 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0001
spec:
  capacity:
    storage: 5Gi # pv 的容量
  volumeMode: Filesystem # 存储类型为文件系统
  accessModes: # 访问模式:ReadWriteOnce、ReadWriteMany、ReadOnlyMany
    - ReadWriteOnce # 可被单节点独写
  persistentVolumeReclaimPolicy: Recycle # 回收策略
  storageClassName: slow # 创建 PV 的存储类名,需要与 pvc 的相同
  mountOptions: # 加载配置
    - hard
    - nfsvers=4.1
  nfs: # 连接到 nfs
    path: /data/nfs/rw/test-pv # 存储路径
    server: 192.168.113.121 # nfs 服务地址
PVC

Pod 绑定 PVC

在 pod 的挂载容器配置中,增加 pvc 挂载

yaml 复制代码
containers:
  ......
  volumeMounts:
    - mountPath: /tmp/pvc
      name: nfs-pvc-test
volumes:
  - name: nfs-pvc-test
    persistentVolumeClaim:
      claimName: nfs-pvc # pvc 的名称

配置文件:

yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - ReadWriteOnce # 权限需要与对应的 pv 相同
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi # 资源可以小于 pv 的,但是不能大于,如果大于就会匹配不到 pv
  storageClassName: slow # 名字需要与对应的 pv 相同
#  selector: # 使用选择器选择对应的 pv
#    matchLabels:
#      release: "stable"
#    matchExpressions:
#      - {key: environment, operator: In, values: [dev]}
StorageClass

下面是一个 Kubernetes 动态创建 PV 的完整示例,使用 StorageClass + PVC 触发动态供给(Dynamic Provisioning),无需手动创建 PV。示例使用 AWS EBS(需在 AWS EKS 环境)。

yaml 复制代码
---
# 1. StorageClass(AWS EBS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com          # AWS EBS CSI 驱动
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
parameters:
  type: gp3
  fsType: ext4

---
# 2. PersistentVolumeClaim(请求存储)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-pvc
spec:
  storageClassName: ebs-sc
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

---
# 3. Pod 使用 PVC
apiVersion: v1
kind: Pod
metadata:
  name: nginx-ebs
spec:
  containers:
    - name: nginx
      image: nginx:alpine
      volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: ebs-pvc
  restartPolicy: Never

高级调度

CronJob 计划任务

在 k8s 中周期性运行计划任务,与 linux 中的 crontab 相同。

注意:CronJob 执行的时间是 controller-manager 的时间,所以一定要确保 controller-manager 时间是准确的。
cron 表达式

text 复制代码
* * * * * command_to_run
│ │ │ │ │
│ │ │ │ └── 星期几 (0 - 7) (0 和 7 都表示星期日)
│ │ │ └──── 月份 (1 - 12)
│ │ └────── 日期 (1 - 31)
│ └──────── 小时 (0 - 23)
└────────── 分钟 (0 - 59)

配置文件

yaml 复制代码
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  concurrencyPolicy: Allow # 并发调度策略:Allow 允许并发调度,Forbid:不允许并发执行,Replace:如果之前的任务还没执行完,就直接执行新的,放弃上一个任务
  failedJobsHistoryLimit: 1 # 保留多少个失败的任务
  successfulJobsHistoryLimit: 3 # 保留多少个成功的任务
  suspend: false # 是否挂起任务,若为 true 则该任务不会执行
#  startingDeadlineSeconds: 30 # 间隔多长时间检测失败的任务并重新执行,时间不能小于 10
  schedule: "* * * * *" # 调度策略
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

初始化容器 InitContainer

在真正的容器启动之前,先启动 InitContainer,在初始化容器中完成真实容器所需的初始化操作,完成后再启动真实的容器。

相对于 postStart 来说,首先 InitController 能够保证一定在 EntryPoint 之前执行,而 postStart 不能,其次 postStart 更适合去执行一些命令操作,而 InitContainer实际就是一个容器,可以在其他基础容器环境下执行更复杂的初始化功能。

在 pod 创建的模板中配置 initContainers 参数:

yaml 复制代码
spec:
  initContainers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    command: ["sh", "-c", "echo 'inited;' >> ~/.init"]
    name: init-test

污点和容忍

k8s 集群中可能管理着非常庞大的服务器,这些服务器可能是各种各样不同类型的,比如机房、地理位置、配置等,有些是计算型节点,有些是存储型节点,此时我们希望能更好的将 pod 调度到与之需求更匹配的节点上。此时就需要用到污点(Taint)和容忍(Toleration),这些配置都是 key: value 类型的。

污点(Taint)

污点:是标注在节点上的,当我们在一个节点上打上污点以后,k8s 会认为尽量不要将 pod 调度到该节点上,除非该 pod 上面表示可以容忍该污点,且一个节点可以打多个污点,此时则需要 pod 容忍所有污点才会被调度该节点。

shell 复制代码
# 为节点打上污点
kubectl taint node k8s-master key=value:NoSchedule

# 移除污点
kubectl taint node k8s-master key=value:NoSchedule-

# 查看污点
kubectl describe no k8s-master

污点的影响:

  • NoSchedule:不能容忍的 pod 不能被调度到该节点,但是已经存在的节点不会被驱逐
  • NoExecute:不能容忍的节点会被立即清除,能容忍且没有配置 tolerationSeconds 属性,则可以一直运行,设置了 tolerationSeconds: 3600 属性,则该 pod 还能继续在该节点运行 3600 秒

容忍(Toleration)

容忍:是标注在 pod 上的,当 pod 被调度时,如果没有配置容忍,则该 pod 不会被调度到有污点的节点上,只有该 pod 上标注了满足某个节点的所有污点,则会被调度到这些节点

yaml 复制代码
# pod 的 spec 下面配置容忍
tolerations:
- key: "污点的 key"
  value: "污点的 value"
  offect: "NoSchedule" # 污点产生的影响
  operator: "Equal" # 表是 value 与污点的 value 要相等,也可以设置为 Exists 表示存在 key 即可,此时可以不用配置 value

亲和力(Affinity)

NodeAffinity 节点亲和力

进行 pod 调度时,优先调度到符合条件的亲和力节点上

  • RequiredDuringSchedulingIgnoredDuringExecution:硬亲和力,即支持必须部署在指定的节点上,也支持必须不部署在指定的节点上
  • PreferredDuringSchedulingIgnoredDuringExecution:软亲和力,尽量部署在满足条件的节点上,或尽量不要部署在被匹配的节点上
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity: # 亲和力配置
    nodeAffinity: # 节点亲和力
      requiredDuringSchedulingIgnoredDuringExecution: # 节点必须匹配下方配置
        nodeSelectorTerms: # 选择器
        - matchExpressions: # 匹配表达式
          - key: topology.kubernetes.io/zone # 匹配 label 的 key
            operator: In # 匹配方式,只要匹配成功下方的一个 value 即可
            values:
            - antarctica-east1 # 匹配的 value
            - antarctica-west1 # 匹配的 value
      preferredDuringSchedulingIgnoredDuringExecution: # 节点尽量匹配下方配置
      - weight: 1 # 权重[1,100],按照匹配规则对所有节点累加权重,最终之和会加入优先级评分,优先级越高被调度的可能性越高
        preference:
          matchExpressions: # 匹配表达式
          - key: another-node-label-key # label 的 key
            operator: In # 匹配方式,满足一个即可
            values:
            - another-node-label-value # 匹配的 value
#      - weight: 20
        ......
  containers:
  - name: with-node-affinity
    image: pause:2.0

PodAffinity Pod亲和力

将与指定 pod 亲和力相匹配的 pod 部署在同一节点。

  • RequiredDuringSchedulingIgnoredDuringExecution:必须将应用部署在一块
  • PreferredDuringSchedulingIgnoredDuringExecution:尽量将应用部署在一块
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity: # 亲和力配置
    podAffinity: # pod 亲和力配置
      requiredDuringSchedulingIgnoredDuringExecution: # 当前 pod 必须匹配到对应条件 pod 所在的 node 上
      - labelSelector: # 标签选择器
          matchExpressions: # 匹配表达式
          - key: security # 匹配的 key
            operator: In # 匹配方式
            values: # 匹配其中的一个 value
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity: # pod 反亲和力配置
      preferredDuringSchedulingIgnoredDuringExecution: # 尽量不要将当前节点部署到匹配下列参数的 pod 所在的 node 上
      - weight: 100 # 权重
        podAffinityTerm: # pod 亲和力配置条件
          labelSelector: # 标签选择器
            matchExpressions: # 匹配表达式
            - key: security # 匹配的 key
              operator: In # 匹配的方式
              values:
              - S2 # 匹配的 value
          topologyKey: topology.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: pause:2.0

PodAntiAffinity Pod反亲和力

根据策略尽量部署或不部署到一块

  • RequiredDuringSchedulingIgnoredDuringExecution:不要将应用与之匹配的部署到一块
  • PreferredDuringSchedulingIgnoredDuringExecution:尽量不要将应用部署到一块

身份认证与权限

Kubernetes 中提供了良好的多租户认证管理机制,如 RBAC、ServiceAccount 还有各种策略等。

通过该文件可以看到已经配置了 RBAC 访问控制:/usr/lib/systemd/system/kube-apiserver.service

在 Kubernetes(K8s)中,ServiceAccount 和 UserAccount(通常简称"用户") 都用于身份认证和权限控制,但它们的用途、管理方式和生命周期完全不同。

  • UserAccount(用户):代表人类操作者(如开发、运维),由集群外部管理。
  • ServiceAccount(服务账户):代表Pod 中运行的应用程序,由 Kubernetes 内部管理。

身份认证(Authentication)

回答:"你是谁?"
K8s 支持多种认证方式:

  • 客户端证书(X.509)
  • Bearer Token(如 ServiceAccount Token、OIDC Token)
  • 静态 Token 文件 / 用户名密码文件(不推荐)
  • 外部认证(Webhook、OIDC、LDAP 等)
    所有请求必须携带身份凭证,API Server 验证后提取出:
  • User(如 system:admin)
  • Groups(如 system:masters)
  • Extra info(可选)

权限授权(Authorization)

回答:"你能做什么?"

K8s 默认使用 RBAC(基于角色的访问控制):

  • Role / ClusterRole:定义权限集合(如对哪些 API 资源可执行 get/list/create 等操作)。
  • RoleBinding / ClusterRoleBinding:将用户/组/ServiceAccount 绑定到角色,授予其权限。

Role 和 RoleBinding 是命名空间级别。ClusterRole 和 ClusterRoleBinding 是集群级别。

示例

示例流程:

复制代码
kubectl → 携带 kubeconfig(含证书/token) 
→ API Server 认证身份(如 user=dev-user, group=dev-team) 
→ RBAC 检查是否有权限操作目标资源 
→ 允许 or 拒绝

面是一个 完整的 Kubernetes 身份认证与 RBAC 权限控制示例:
步骤 1:创建命名空间

shell 复制代码
kubectl create namespace dev

步骤 2:为用户 dev-user 生成客户端证书(用于认证)

shell 复制代码
# 1. 生成私钥
openssl genrsa -out dev-user.key 2048

# 2. 创建 CSR(证书签名请求),指定 CN=用户名,O=组(可选)
cat <<EOF | openssl req -new -key dev-user.key -out dev-user.csr -subj "/CN=dev-user/O=dev-team" -config /dev/stdin
[req]
distinguished_name = req_distinguished_name
[req_distinguished_name]
EOF

# 3. 用集群 CA 签发证书
openssl x509 -req -in dev-user.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out dev-user.crt -days 365

步骤 3:创建 RBAC 权限(Role + RoleBinding)

dev-user 只能在 dev 命名空间中 读取 Pod,不能操作其他资源(如 Deployments),也不能访问其他命名空间。

yaml 复制代码
# dev-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: pod-reader
rules:
- apiGroups: [""]               # core API group
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-in-dev
  namespace: dev
subjects:
- kind: User
  name: dev-user                # 必须与证书中的 CN 一致
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

应用配置

shell 复制代码
kubectl apply -f dev-role.yaml

步骤 4:为用户配置 kubeconfig

shell 复制代码
# 设置集群信息(假设你的集群名为 my-cluster,API Server 地址为 https://<master-ip>:6443)
kubectl config set-cluster my-cluster \
  --server=https://<YOUR_API_SERVER_IP>:6443 \
  --certificate-authority=/etc/kubernetes/pki/ca.crt \
  --embed-certs=true

# 设置用户凭证
kubectl config set-credentials dev-user \
  --client-certificate=./dev-user.crt \
  --client-key=./dev-user.key \
  --embed-certs=true

# 设置上下文
kubectl config set-context dev-context \
  --cluster=my-cluster \
  --user=dev-user \
  --namespace=dev

# 切换上下文
kubectl config use-context dev-context

步骤 5:验证权限

bash 复制代码
# ✅ 成功:列出 dev 命名空间的 pods
kubectl get pods

# ❌ 失败:尝试列出其他命名空间的 pods
kubectl get pods -n default
# Error: pods is forbidden...

# ❌ 失败:尝试创建 pod(无 create 权限)
kubectl run test --image=nginx
# Error: pods is forbidden: User "dev-user" cannot create resource "pods"...
相关推荐
不想画图2 小时前
dockerfile镜像构建和docker compose编排
docker·容器
伊克罗德信息科技2 小时前
【客户案例】KiwiCloud 携手伊克罗德信息,打造云原生 UEM 平台,实现统一终端管理的多区域高效部署
云原生
AR_xsy2 小时前
云原生数据备份还原利器---【velero】
云原生
峰顶听歌的鲸鱼3 小时前
19.docker 图形化管理界面
运维·笔记·docker·容器·学习方法
Mr.朱鹏3 小时前
分布式接口幂等性实战指南【完整版】
java·spring boot·分布式·sql·spring·云原生·幂等
weixin_46683 小时前
K8S- Calico
云原生·容器·kubernetes
忍冬行者3 小时前
k8s集群容器创建报failed to write 10087 to cgroup.procs处理
云原生·容器·kubernetes
Hui Baby3 小时前
海豹云创建K8S集群
云原生·容器·kubernetes
柠檬汁Dev3 小时前
这套云原生开发工作流,把上线时间从1天缩短到3分钟
云原生