第二部分 Kubernetes快速入门

kubernetes是实现云原生的重要基础设施,在本章节我们将快速学习Kubernetes常用的概念与命令.通过此章节,我们可以了解Kubernetes的核心概念,并且能熟练部署Pod,在Kubernetes中部署自己的pod

第一章 Kubernetes介绍

1. 什么是Kubernetes

image-20220308185502179

​ 自容器诞生以后,随之而来的就是容器自动化与无人化,我们简单介绍一下比较有名的编排工具

Docker Swarm

Docker Swarm是Docker的原生编排工具,用于跨多个主机进行容器编排.项目核心设计是将几台安装Docker的服务器组合成一个大的集群.在Docker1.12.0版本以前 Docker Swarm是独立的产品,在Docker1.12.0以后的版本,已经集成在Docker引擎汇总,Docker SwarmKubernetes一样都是使用YAML配置文件,Docker Swarm是Docker Datacenter这个更大产品中的一部分.

Kubernetes

Kubernetes,又称k8s,因为有是一个编排容器的工具,其实也是管理应用的全生命周期的一个工具,从创建应用,应用的部署,应用提供服务,扩容缩容应用,应用更新,都非常的方便,而且可以做到故障自愈,例如一个服务器挂了,可以自动将这个服务重新启动将该服务调度到另外一个主机上进行运行,无需进行人工干涉。Kubernetes屏蔽了硬件层的资源,对资源进行了更高级的抽象.

2. 为什么要使用Kubernetes

​ 主要有以下有基点:

  • 自动化容器的部署和复制
  • 随时扩展或收缩容器规模
  • 将容器组织成组,并且提供容器间的负载均衡
  • 很容易地升级应用程序容器的新版本,并且也易于版本回退
  • 提供容器弹性,如果容器失效就替换它
  • 简化运维操作,屏蔽底层的复杂操作

3. 整体架构

​ 首先我们看一下Kubernetes的整体架构图

image-20220103200223762

​ 从上可以看到Kubernetes的构成主要是Master节点与Node节点.然后和一些组件,下面我们将一一介绍

kubectl

kubectl是用户使用的指令工具,用来管理Kubernetes集群的,kubectl将用户的指令发送到Master节点上的API Server

Master节点

MasterKubernetes集群的主节点,是核心服务所在的机器.Master节点收到用户使用kubectl发送的指令后, 将命令发送到Node节点的kubelet服务,kubelet服务负责执行相应的指令,一般Master节点是不参与到集群工作中的,当然也可以通过一些手段才让Master节点参与到工作中.

Node节点

NodeKubernetes的工作节点,您可以理解为是一台宿主机

API Server

​ 该组件位于Master节点上主节点上负责提供 Kubernetes API 服务的组件;它是 Kubernetes 控制面的前端。kube-apiserver 在设计上考虑了水平扩缩的需要。 我们通过部署多个实例可以实现集群的高可用

Scheduler

Scheduler是集群的默认调度器,Scheduler通过kubernetes的监测机制来发现集群中新创建且尚未被调度到 Node 上的 Pod。 Scheduler会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。 Scheduler会依据下文的调度原则来做出调度选择。如果没有合适的节点,则将Pod置于挂起状态,直到出现合适的节点

Controller-Manager

Controller-Manager作为集群内部的管理控制中心,负责集群内的Node、Pod副本、服务端点(Endpoint)、命名空间(Namespace)、服务账号(ServiceAccount)、资源定额(ResourceQuota)的管理,当某个Node意外宕机时,Controller-Manager会及时发现并执行自动化修复流程,确保集群始终处于预期的工作状态。

ETCD

​ 是Kubernetes提供默认的存储系统,保存所有集群数据.当然该组件可以部署到另外的机器上.我们经常所说的Kubernetes的高可用,本质就是ETCD的高可用

Node节点

​ Node是Pod真正运行的主机,可以物理机,也可以是虚拟机。为了管理Pod,每个Node节点上至少要运行Container Runtime(比如docker)、kubelet和kube-proxy服务。业务容器都会部署到该节点上.在此节点上,我们可以依然使用Docker的相关命令来查看,已经存在的镜像与容器.

1) kubelet

​ 每个Node节点上都运行一个 kubelet 服务进程,默认监听 10250 端口,接收并执行 Master 发来的指令,管理 Pod 及 Pod 中的容器。每个Kubelet进程会向 API Server 注册所在Node节点的信息,定期向 **Master **节点汇报该节点的资源使用情况,并通过 cAdvisor 监控节点和容器的资源。kubelet需要随机启动.

2) kube-proxy

kube-proxy确保每个节点都获得其IP地址,实现本地iptables和规则以处理路由和流量负载均衡。

第二章 部署Kubernetes集群

1. 服务器规划

​ 在本章节,我将使用虚拟机部署一套Kubernetes集群,服务器规划如下,服务器IP根据实际情况进行配置

系统节点用途IP规格
centos8master主节点192.168.137.462核,2G
centos8node1工作节点192.168.137.1472核,2G
centos8node2工作节点192.168.137.182核,2G

2. 安装虚拟机与Docker

​ 可以参考第一部分 Docker快速入门

3. 系统设置

​ 虚拟机与Docker安装完毕后,需要一定的系统设置

  1. 将所有虚拟机设置静态IP,使用以下命令进行设置
#1 编辑以下路径文件
vi /etc/sysconfig/network-scripts/ifcfg-eth0

#2 改为以下内容 如果不存在则添加
BOOTPROTO="static"
ONBOOT="yes"
IPADDR="192.168.137.46"
GATEWAY="192.168.137.1"
DNS1="192.168.137.1"
NETMASK="255.255.255.0"
1
2
3
4
5
6
7
8
9
10
  1. 所有虚拟机需要设置hosts
#编辑以下路径文件
vi /etc/hosts
#填写以下内容
192.168.137.147 node1
192.168.137.18 node2
192.168.137.46  master
1
2
3
4
5
6
  1. 所有虚拟机关闭防火墙
#使用以下命令
systemctl stop firewalld && systemctl disable firewalld
1
2
  1. 所有虚拟机关闭swap与SELinux
#分别使用以下命令进行关闭
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
swapoff -a
setenforce 0 
1
2
3
4
5
  1. 所有虚拟机配置Docker
#编辑以下文件
vi /etc/docker/daemon.json
#填写以下内容
{
"registry-mirrors": ["这里填写加速源地址"],
"exec-opts": ["native.cgroupdriver=systemd"]
}

#保存文件后,需要重启docker
systemctl restart docker
1
2
3
4
5
6
7
8
9
10

4. 安装

  1. 所有虚拟机创建安装源
#由于Kubernetes官方安装源在Google服务器,所以我们使用阿里云的安装源
#编辑以下文件
vi /etc/yum.repos.d/kubernetes.repo
#填写以下内容
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
1
2
3
4
5
6
7
8
9
10
11
  1. 安装
#在master节点执行以下命令
yum install -y kubectl kubelet kubeadm

#在node节点执行以下命令
yum install -y  kubelet kubeadm

#在所有节点设置kubelet随机启动并随机启动
systemctl enable kubelet --now
1
2
3
4
5
6
7
8
  1. 初始化master节点
  • #首先在master节点确认kubectl的版本
    kubectl version
    
    [root@master ~]# kubectl version
    Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:41:01Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
    
    1
    2
    3
    4
    5
  • #在master节点上执行以下命令,该命令的意思是使用阿里云的镜像初始化最新版本的kubernetes,指定版本与kubectl的版本一致,所有的设置指令使用默认,将安装日志写到当前目录的init.log中
    #kubeadm为集群的初始化命令,
    
    kubeadm init  --image-repository registry.aliyuncs.com/google_containers   --kubernetes-version v1.23.1  | tee init.log
    
    1
    2
    3
    4
  • 提示安装成功

    Your Kubernetes control-plane has initialized successfully!
    
    To start using your cluster, you need to run the following as a regular user:
    
      mkdir -p $HOME/.kube
      sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    Alternatively, if you are the root user, you can run:
    
      export KUBECONFIG=/etc/kubernetes/admin.conf
    
    You should now deploy a pod network to the cluster.
    Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
      https://kubernetes.io/docs/concepts/cluster-administration/addons/
    
    Then you can join any number of worker nodes by running the following on each as root:
    
    kubeadm join 192.168.137.46:6443 --token ut40xz.kghms83ni0ubarbt \
            --discovery-token-ca-cert-hash sha256:743b7018266c00ac095426a67fba9b090bf5ec080758538992ecfa485d724f04
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    其中有2个注意的地方

    #1. 在master节点需要执行以下这个命令,这是kubctl与api server通讯的认证,如果忘记了,可以在刚刚保存在的init.log中查看,如果您在别的机器上(比如A)安装了kubectl组件的话,可以在该机器(A)上执行以下命令,这样这台机器(A)就具有访问了api server的能力,当然前台是网络是能通的
    
      mkdir -p $HOME/.kube
      sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      sudo chown $(id -u):$(id -g) $HOME/.kube/config
      
    #2. 当执行完以上命令的时候,可以看到客户端与服务端的版本
    [root@master ~]# kubectl version
    Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:41:01Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
    Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:34:54Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
    
    #3. node节点加入集群使用以下命令,但是此时node节点不要加入集群
    kubeadm join 192.168.137.46:6443 --token ut40xz.kghms83ni0ubarbt \
            --discovery-token-ca-cert-hash sha256:743b7018266c00ac095426a67fba9b090bf5ec080758538992ecfa485d724f04
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 安装集群网络插件

    # 安装 Calico
    kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
    
    1
    2

    Kubernetes支持多种的网络插件,比如Wave,Flannel等,大家可以选择任意一个网络插件安装使用.本课程主要面相的是研发人员,所以不对Kubernetes的网络做深入介绍.

​ 至此,我的Master节点就是安装成功了.接下来让我们加入Node节点

  1. node节点加入集群
#在node节点使用指令加入加群
kubeadm join 192.168.137.46:6443 --token ut40xz.kghms83ni0ubarbt \
        --discovery-token-ca-cert-hash sha256:743b7018266c00ac095426a67fba9b090bf5ec080758538992ecfa485d724f04
        
#这个时候在master节点查看,node节点的status目前处于NotReady状态,是因为节点正在下载所需要的镜像

[root@master ~]# kubectl get node
NAME     STATUS     ROLES                  AGE    VERSION
master   Ready      control-plane,master   133m   v1.23.1
node1    NotReady   <none>                 23s    v1.23.1
node2    NotReady   <none>                 16s    v1.23.1

#过一段时间在看node节点状态,已经处于ready状态了
[root@master ~]# kubectl get node
NAME     STATUS   ROLES                  AGE     VERSION
master   Ready    control-plane,master   138m    v1.23.1
node1    Ready    <none>                 4m53s   v1.23.1
node2    Ready    <none>                 4m46s   v1.23.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

到此为止,目前集群已经安装完毕,处于可用状态.

第三章 Pod

1. 什么是Pod

​ 在Kubernetes集群中,Kubernetes不会直接管理容器,而是通过各种控制器来管理和规定Pod,.Pod是所有业务类型的基础,也是Kubernetes管理的最小单位级与调度单元,它是一个或多个容器的组合。pod内的容器会自动被安排到同一node节点上, 这组容器共享存储、网络和命名空间,以及如何运行的规范。在Pod中,所有容器都被同一安排和调度,并运行在共享的上下文中。通俗的来说,就是我们部署的一个应用服务就是一个或多个pod.

Pod有两个特点:

  1. 网络:每一个Pod都会被指派一个唯一的IP地址,在Pod中的每一个容器共享网络命名空间,包括IP地址和网络端口。在同一个Pod中的容器可以同locahost进行互相通信。当Pod中的容器需要与Pod外的实体进行通信时,则需要通过端口等共享的网络资源。

  2. **存储:**Pod能够被指定共享存储卷的集合,在Pod中所有的容器能够访问共享存储卷,允许这些容器共享数据。存储卷也允许在一个Pod持久化数据,以防止其中的容器需要被重启。

Kubernetes不会直接管理容器,而是管理pod.在实际应用中通常我们不会直接创建Pod,而是通过控制器来管理和调度Pod.控制器的概念我们稍后再讲.

2. 了解kubectl

kubectlKubernetes自带的客户端,是管理Kubernetes集群的工具,只要在机器上安装kuebctl以后,然后复制好认证token以后,就可以远程对集群进行控制.

kubectl的常用命令如下

[root@master ~]# kubectl
kubectl controls the Kubernetes cluster manager.

 Find more information at: https://kubernetes.io/docs/reference/kubectl/overview/

Basic Commands (Beginner):
  create        Create a resource from a file or from stdin
  expose        Take a replication controller, service, deployment or pod and expose it as a new Kubernetes service
  run           在集群中运行一个指定的镜像
  set           为 objects 设置一个指定的特征

Basic Commands (Intermediate):
  explain       Get documentation for a resource
  get           显示一个或更多 resources
  edit          在服务器上编辑一个资源
  delete        Delete resources by file names, stdin, resources and names, or by resources and label selector

Deploy Commands:
  rollout       Manage the rollout of a resource
  scale         Set a new size for a deployment, replica set, or replication controller
  autoscale     Auto-scale a deployment, replica set, stateful set, or replication controller

Cluster Management Commands:
  certificate   修改 certificate 资源.
  cluster-info  Display cluster information
  top           Display resource (CPU/memory) usage
  cordon        标记 node 为 unschedulable
  uncordon      标记 node 为 schedulable
  drain         Drain node in preparation for maintenance
  taint         更新一个或者多个 node 上的 taints

Troubleshooting and Debugging Commands:
  describe      显示一个指定 resource 或者 group 的 resources 详情
  logs          输出容器在 pod 中的日志
  attach        Attach 到一个运行中的 container
  exec          在一个 container 中执行一个命令
  port-forward  Forward one or more local ports to a pod
  proxy         运行一个 proxy 到 Kubernetes API server
  cp            Copy files and directories to and from containers
  auth          Inspect authorization
  debug         Create debugging sessions for troubleshooting workloads and nodes

Advanced Commands:
  diff          Diff the live version against a would-be applied version
  apply         Apply a configuration to a resource by file name or stdin
  patch         Update fields of a resource
  replace       Replace a resource by file name or stdin
  wait          Experimental: Wait for a specific condition on one or many resources
  kustomize     Build a kustomization target from a directory or URL.

Settings Commands:
  label         更新在这个资源上的 labels
  annotate      更新一个资源的注解
  completion    Output shell completion code for the specified shell (bash, zsh or fish)

Other Commands:
  alpha         Commands for features in alpha
  api-resources Print the supported API resources on the server
  api-versions  Print the supported API versions on the server, in the form of "group/version"
  config        修改 kubeconfig 文件
  plugin        Provides utilities for interacting with plugins
  version       输出 client 和 server 的版本信息

Usage:
  kubectl [flags] [options]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

3. pod基本操作

3.1 创建pod

接下来我们使用nginx镜像创建一个pod

#使用以下命令创建一个名为:nginx-pod的pod,暴漏端口为80
[root@master ~]# kubectl run nginx-pod --image=nginx --port=80
pod/nginx-pod created

#可以看见创建成功,然后在查看pod列表,-owide为可选参数,意味列出pod的详细信息
[root@master ~]# kubectl get pod -owide
1
2
3
4
5
6

image-20220121143043159

一个简单的nginx pod就已经被创建.可以看到nginx-pod状态为ready,并且已经部署到了node1节点上,一般情况下我们部署的pod只会部署到node节点上,不会部署到master节点上,内部IP地址为172.16.166.148

创建Pod有两种方式

  1. 第一种 如上述所使用kubectl run pod名称 --image=镜像地址

    我们使用kubectl run --help来查看其他选项

    [root@master ~]# kubectl run --help
    Create and run a particular image in a pod.
    
    Examples:
    # Start a nginx pod
    kubectl run nginx --image=nginx
    
    # Start a hazelcast pod and let the container expose port 5701
    kubectl run hazelcast --image=hazelcast/hazelcast --port=5701
    
    # Start a hazelcast pod and set environment variables "DNS_DOMAIN=cluster" and "POD_NAMESPACE=default" in the
    container
    kubectl run hazelcast --image=hazelcast/hazelcast --env="DNS_DOMAIN=cluster" --env="POD_NAMESPACE=default"
    
    # Start a hazelcast pod and set labels "app=hazelcast" and "env=prod" in the container
    kubectl run hazelcast --image=hazelcast/hazelcast --labels="app=hazelcast,env=prod"
    
    # Dry run; print the corresponding API objects without creating them
    kubectl run nginx --image=nginx --dry-run=client
    
    # Start a nginx pod, but overload the spec with a partial set of values parsed from JSON
    kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }'
    
    # Start a busybox pod and keep it in the foreground, don't restart it if it exits
    kubectl run -i -t busybox --image=busybox --restart=Never
    
    # Start the nginx pod using the default command, but use custom arguments (arg1 .. argN) for that command
    kubectl run nginx --image=nginx -- <arg1> <arg2> ... <argN>
    
    # Start the nginx pod using a different command and custom arguments
    kubectl run nginx --image=nginx --command -- <cmd> <arg1> ... <argN>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
  2. 第二种声明式创建,就是使用yaml文件,

    #1. 我们可以使命创建pod的命令导出yaml,进行修改,推荐使用该方式
    #2. 也可以自己书写yaml,
    #我们使用第一种方式,来创建yaml
    
    [root@master ~]# kubectl run nginx --image=nginx  --dry-run=client -o yaml > nginx_pod.yaml
    W0121 15:21:48.224142 3517232 helpers.go:555] --dry-run is deprecated and can be replaced with --dry-run=client.
    
    #增加 --dry-run=client选项参数,输出pod的yaml模板
    #可以看到在当前目录中生成了名为nginx_pod.yaml文件,我来查看一下该文件内容
    
    [root@master ~]# vi nginx_pod.yaml 
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #指定api版本,该选项为固定
    apiVersion: v1
    #指定类型为pod,
    kind: Pod
    metadata:
    labels:
     run: nginx
    #pod的名称
    name: nginx
    spec:
    #pod中的容器的定义
     containers:
    #容器使用的镜像,这里可以指定多个镜像,来运行多个容器
      - image: nginx
        name: nginx
        resources: {}
      dnsPolicy: ClusterFirst
    #重启策略  
      restartPolicy: Always
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 使用 kubectl apply -f nginx_pod.yaml
    [root@master ~]# kubectl apply -f nginx_pod.yaml
    pod/nginx created
    [root@master ~]# kubectl get pod
    NAME    READY   STATUS              RESTARTS   AGE
    nginx   0/1     ContainerCreating   0          4s
    #可以看到nginx_pod被创建,状态为ContainerCreating
    
    1
    2
    3
    4
    5
    6
    7

3.2 查看pod

#查看pod列表
[root@master ~]# kubectl get pod -owide
NAME    READY   STATUS              RESTARTS   AGE   IP       NODE    NOMINATED NODE   READINESS GATES
nginx   0/1     ContainerCreating   0          12s   <none>   node2   <none>           <none>
[root@master ~]#

#查看指定pod的信息,可以使用'kubectl get pod pod名称'来查看
[root@master ~]# kubectl get pod nginx -owide
NAME    READY   STATUS    RESTARTS   AGE     IP             NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          4m37s   172.16.104.4   node2   <none>           <none>


#如果想要单个Pod的更多信息,可以使用'kubectl describe pod pod名称'该命令查看 
[root@master ~]# kubectl describe pod nginx
Name:         nginx
Namespace:    default
Priority:     0
Node:         node2/192.168.137.18
Start Time:   Fri, 21 Jan 2022 10:53:27 -0500
Labels:       run=nginx
Annotations:  cni.projectcalico.org/containerID: e92841643ed06a3247e1413770d5747760e15da42deb78247c4f30b298a0ef92
              cni.projectcalico.org/podIP: 172.16.104.3/32
              cni.projectcalico.org/podIPs: 172.16.104.3/32
Status:       Running
IP:           172.16.104.3
IPs:
  IP:  172.16.104.3
Containers:
  nginx:
    Container ID:   docker://cb8b6ecda69e2c854d9a723826b6c7e49bb3e831f589430e5e04237c8b21354f
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Fri, 21 Jan 2022 10:53:44 -0500
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-wv8bb (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  kube-api-access-wv8bb:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  60s   default-scheduler  Successfully assigned default/nginx to node2
  Normal  Pulling    58s   kubelet            Pulling image "nginx"
  Normal  Pulled     42s   kubelet            Successfully pulled image "nginx" in 15.3975152s
  Normal  Created    42s   kubelet            Created container nginx
  Normal  Started    42s   kubelet            Started container nginx
[root@master ~]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

3.3 编辑pod

#使用 kubectl edit pod pod名称,来对正在运行的pod进行编辑
[root@master ~]# kubectl edit pod nginx

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: Pod
metadata:
  annotations:
    cni.projectcalico.org/containerID: e92841643ed06a3247e1413770d5747760e15da42deb78247c4f30b298a0ef92
    cni.projectcalico.org/podIP: 172.16.104.3/32
    cni.projectcalico.org/podIPs: 172.16.104.3/32
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"creationTimestamp":null,"labels":{"run":"nginx"},"name":"nginx","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"nginx","resources":{}}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always"},"status":{}}
  creationTimestamp: "2022-01-21T15:53:26Z"
  labels:
    run: nginx
  name: nginx
  namespace: default
  resourceVersion: "35881"
  uid: 49225fbf-0adf-404b-af78-96e84cb1fcba
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: nginx
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-wv8bb
      readOnly: true
  dnsPolicy: ClusterFirst  
 .........
#这个时候出现了该pod的一个详细的信息,并以yaml格式进行输出,我可以直接更改其中的一些选项,然后保存即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

3.4 删除pod

​ 删除pod有两种方案

  1. 第一种使用利用创建的yaml文件进行删除,使用命令kubectl delete -f yaml文件名称
#执行kubectl delete -f  nginx_pod.yaml
[root@master ~]# kubectl delete -f nginx_pod.yaml
pod "nginx" deleted
[root@master ~]#
#可以看到Pod被删除
1
2
3
4
5
  1. 直接使用命令kubectl delete pod pod名称
#执行以下命令,pod名称
[root@master ~]# kubectl delete pod nginx
pod "nginx" deleted
[root@master ~]#
1
2
3
4

3.5 进入pod

​ 在Kubernetes中,我们同样可以进入容器内部,使用方式与Docker进入容器几乎一样,kubectl exec -it pod名称进入容器内部,该命令在当前版本是可用的,不过提示在未来版本可能会被更换.

​ 进入容器内部,我们就可以执行我们需要的命令,当前前提是该镜像在构建时,已经打包进去了.

[root@master ~]# kubectl exec -it nginx  bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx:/# ls
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@nginx:/#
1
2
3
4
5

3.6 pod日志

​ 我们可以使用命令kubect logs pod名称来查看内部容器日志

[root@master ~]# kubectl logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/01/21 16:26:57 [notice] 1#1: using the "epoll" event method
2022/01/21 16:26:57 [notice] 1#1: nginx/1.21.5
2022/01/21 16:26:57 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2022/01/21 16:26:57 [notice] 1#1: OS: Linux 4.18.0-147.el8.x86_64
2022/01/21 16:26:57 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2022/01/21 16:26:57 [notice] 1#1: start worker processes
2022/01/21 16:26:57 [notice] 1#1: start worker process 31
2022/01/21 16:26:57 [notice] 1#1: start worker process 32
2022/01/21 16:26:57 [notice] 1#1: start worker process 33
[root@master ~]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

4. pod高级特性

​ 前几章我们学习了pod的一些基本操作,接下来我们来看一下pod的一些高级特性,这些高级特性都是日常工作中经常用到的.

​ 首先我们来查看一下讲解一下相对比较完整的的pod的yaml文件

apiVersion: v1
kind: Pod
#4.1 元数据
metadata:
  namespace: default
  labels:
    run: nginx
    app: nginxtest
  #pod的名称
  name: nginx-pod-name
spec:
#4.2 节点调度  
  nodeSelector:
   app: app
  containers:
    #镜像地址
  - image: nginx
#4.3 镜像拉取策略  
    imagePullPolicy: IfNotPresent
    #这是容器的名称
    name: nginx-container-name
#4.4 环境变量
    env: 
     - name: ASPNETCORE_ENVIRONMENT
       value: Production
     - name: TZ
       value: Asia/Shanghai
    ports:
    - containerPort: 80
#4.5 健康检查    
    livenessProbe:
     httpGet:
        path: /
        port: 80
     initialDelaySeconds: 10
     periodSeconds: 3
     timeoutSeconds: 1
     successThreshold: 1
     failureThreshold: 3
    readinessProbe:
     httpGet:
        path: /
        port: 80
     initialDelaySeconds: 10
     periodSeconds: 3 
     timeoutSeconds: 1
     successThreshold: 1
     failureThreshold: 3
#4.6 资源限制
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 256Mi
#4.7 重启策略
  restartPolicy: Always

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

4.1 元数据

1. namespace

namespace简称ns命名空间,属于一种顶级的逻辑隔离,不同namespace中的资源是属于逻辑隔离状态,无法直接进行通讯,当然借助于后续第五章 Service的能力来实现跨命名空间调用,我们依然是可以通讯的.Kubernetes集群默认有三个namespace

  1. default

    如果我们在创建资源时,没有指定任何命名空间的话,Kubernetes会将资源创建到该命名空间下

  2. kube-system

    属于集群的命名空间,通常我们不会对该命名空间做任何的资源操作.

  3. kube-public

    该命名空间也是Kubernetes自动创建,但是一般我们不会使用该命名空间.

查看namspace

我们可以使用kubectl get namespaces来查看,集群上的已经存在的命名空间.

[root@master ~]# kubectl get namespaces
NAME              STATUS   AGE
default           Active   9d
kube-public       Active   9d
kube-system       Active   9d
[root@master ~]# 

1
2
3
4
5
6
7

也可以查看某个命名空间下的指定资源kubectl get pod -n kube-system

[root@master ~]# kubectl get pod -n kube-system
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-75f8f6cc59-9gjg6   1/1     Running   0          9d
calico-node-csf8l                          0/1     Running   0          9d
calico-node-h2qz9                          0/1     Running   0          9d
coredns-7f6cbbb7b8-d4bz7                   1/1     Running   0          9d
coredns-7f6cbbb7b8-kpklt                   1/1     Running   0          9d
etcd-master                                1/1     Running   0          9d
kube-apiserver-master                      1/1     Running   0          9d
kube-controller-manager-master             1/1     Running   0          9d
kube-proxy-8rpnm                           1/1     Running   0          9d
kube-proxy-pvpv9                           1/1     Running   0          9d
kube-scheduler-master                      1/1     Running   0          9d
1
2
3
4
5
6
7
8
9
10
11
12
13

创建namspace

​ 创建namespace也非常容易,既可以通过kubect create namespace 命名空间名称来创建,也可以通过声明式的yaml文件来创建

#通过kubectl create namespace 创建命名空间

[root@master ~]# kubectl create namespace dotnet
namespace/dotnet created

1
2
3
4
5
#通过yaml文件创建命名空间
#创建以下内容并保存为dotnet.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dotnet
  
[root@master ~]# kubectl apply -f dotnet.yaml 
namespace/dotnet created

1
2
3
4
5
6
7
8
9
10

删除namspace

​ 同样的删除namspace依然可以用两种方式,通过命令kubectl delete namspace 命名空间名称,也可以通过yaml文件来删除

#通过kubectl delete namespace来删除命名空间

[root@master ~]# kubectl delete namespace dotnet
namespace "dotnet" deleted

1
2
3
4
5
#通过yaml文件来删除namespace

[root@master ~]# kubectl delete -f  dotnet.yaml 
namespace "dotnet" deleted

1
2
3
4
5

删除namesapce会删除该命名空间以下所有的资源.

2. labels

labels意为标签,我们可以给pod打上多种多样的标签,通过标签我们可以对pod进行分组,还可以实现对pod的更高级的应用,比如应用控制器.

labels的表现形式key=value的形式

增加标签

​ 除了在yaml文件里定义label,我们还可以对已经运行中的pod进行打标签

#通过kubectl label 命令对pod打上标签
[root@master ~]# kubectl label pod nginx unhealthy=true
pod/nginx labeled

1
2
3
4

查看标签

#增加 --show-labels 选项来输出pod的标签

[root@master ~]# kubectl get pod --show-labels
NAME        READY   STATUS    RESTARTS   AGE     LABELS
nginx       1/1     Running   0          4m40s   run=nginx,unhealthy=true
nginx-pod   1/1     Running   0          4d1h    run=nginx-pod

1
2
3
4
5
6
7

删除标签

#通过kubectl label pod nginx 标签名称- 来移除指定标签 

[root@master ~]# kubectl label pod nginx unhealthy-
pod/nginx labeled
[root@master ~]# kubectl get pod --show-labels
NAME        READY   STATUS    RESTARTS   AGE    LABELS
nginx       1/1     Running   0          6m1s   run=nginx
nginx-pod   1/1     Running   0          4d1h   run=nginx-pod


1
2
3
4
5
6
7
8
9
10

4.2 节点调度

nodeSelector选项指定该pod将会被部署到带有指定标签的node节点.

​ 部署以上的yaml文件,pod并不会被创建,因为nodeSelector指定了pod需要调度到带有标签app=app的node节点上.所以当前我们的集群中的两个节点没有该标签,所以pod的状态会处于Pending状态

[root@master ~]# kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
nginx-pod-name   0/1     Pending   0          59s

[root@master ~]# kubectl get node --show-labels=true
NAME     STATUS   ROLES                  AGE     VERSION   LABELS
master   Ready    control-plane,master   4d21h   v1.23.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=,node.kubernetes.io/exclude-from-external-load-balancers=
node1    Ready    <none>                 4d19h   v1.23.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2    Ready    <none>                 4d19h   v1.23.1   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
1
2
3
4
5
6
7
8
9

接下来,我在node1节点上打上这个标签

[root@master ~]# kubectl label node node1 app=app
node/node1 labeled
[root@master ~]# kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
nginx-pod-name   1/1     Running   0          8m29s

1
2
3
4
5
6

​ 可以看到处于Pending状态的pod,已经Running了.这就是我们通过label实现pod的调度

1. 污点

污点 Taints

​ 在前面的章节,我们了解到pod可以通过nodeSelector选项,将pod调度到指定的node节点上.接下来,我们实验室一个效果.在集群中直接创建10个nginx pod

[root@master ~]# kubectl get pod -owide
NAME             READY   STATUS    RESTARTS   AGE     IP               NODE    NOMINATED NODE   READINESS GATES
nginx            1/1     Running   0          3m3s    172.16.104.9     node2   <none>           <none>
nginx-pod-name   1/1     Running   0          115m    172.16.166.130   node1   <none>           <none>
nginx1           1/1     Running   0          2m57s   172.16.104.10    node2   <none>           <none>
nginx2           1/1     Running   0          2m53s   172.16.104.11    node2   <none>           <none>
nginx3           1/1     Running   0          2m50s   172.16.104.12    node2   <none>           <none>
nginx4           1/1     Running   0          2m47s   172.16.104.13    node2   <none>           <none>
nginx5           1/1     Running   0          2m44s   172.16.166.131   node1   <none>           <none>
nginx6           1/1     Running   0          2m40s   172.16.104.14    node2   <none>           <none>
nginx7           1/1     Running   0          2m36s   172.16.104.15    node2   <none>           <none>
nginx8           1/1     Running   0          2m33s   172.16.104.16    node2   <none>           <none>
nginx9           1/1     Running   0          2m30s   172.16.166.132   node1   <none>           <none>
1
2
3
4
5
6
7
8
9
10
11
12
13

​ 我们发现,所有的pod都被调度到了node1节点与node2节点上了,那么为什么master节点没有被调度到呢?这就是接下来我们要聊到的节点的污点机制

​ 通俗的来说就是设置了污点的node节点,将不会被调度pod.除非pod有被设置容忍 Tolerations,

我们先查看node1node2污点设置,看到是none

[root@master ~]# kubectl describe node node1 | grep Taints
Taints:             <none>
[root@master ~]# kubectl describe node node2 | grep Taints
Taints:             <none>
1
2
3
4

两个node节点都没有任何污点,在看一下master节点

[root@master ~]# kubectl describe node master | grep Taints
Taints:             node-role.kubernetes.io/master:NoSchedule
1
2

看到master节点上Taints被设置了为NoSchedule.接下来我们做一个实验.

​ 首先删除这10个nginx的pod,

[root@master ~]# kubectl get pod -n default | grep nginx | awk '{print $1}' | xargs kubectl delete pod -n default
pod "nginx" deleted
pod "nginx-pod-name" deleted
pod "nginx1" deleted
pod "nginx2" deleted
pod "nginx3" deleted
pod "nginx4" deleted
pod "nginx5" deleted
pod "nginx6" deleted
pod "nginx7" deleted
pod "nginx8" deleted
pod "nginx9" deleted
[root@master ~]# kubectl get pod
No resources found in default namespace.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

然后将master节点上的污点移除.然后将node1与node2节点都标记污点

#移除master的污点
[root@master ~]# kubectl taint node master node-role.kubernetes.io/master:NoSchedule-
node/master untainted
[root@master ~]# kubectl describe node master | grep Taints
Taints:             <none>

#对node1节点进行标记污点
[root@master ~]# kubectl taint node node1 key1=value1:NoSchedule
node/node1 tainted

#对node2节点进行标记污点
[root@master ~]# kubectl taint node node2 key1=value1:NoSchedule
node/node2 tainted
1
2
3
4
5
6
7
8
9
10
11
12
13

接下来我们在连续创建10个pod

[root@master ~]# kubectl get pod -owide
NAME      READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES
nginx10   1/1     Running   0          6s    172.16.219.85   master   <none>           <none>
nginx2    1/1     Running   0          49s   172.16.219.77   master   <none>           <none>
nginx3    1/1     Running   0          46s   172.16.219.78   master   <none>           <none>
nginx4    1/1     Running   0          41s   172.16.219.79   master   <none>           <none>
nginx5    1/1     Running   0          36s   172.16.219.80   master   <none>           <none>
nginx6    1/1     Running   0          33s   172.16.219.81   master   <none>           <none>
nginx7    1/1     Running   0          16s   172.16.219.82   master   <none>           <none>
nginx8    1/1     Running   0          13s   172.16.219.83   master   <none>           <none>
nginx9    1/1     Running   0          10s   172.16.219.84   master   <none>           <none>
1
2
3
4
5
6
7
8
9
10
11

可以看到所有的pod都已经在master节点上创建了

污点的语法构成

key=value:effect
1

effect一共有以下三个值

#NoSchedule意为不能被调度
kubectl taint node node1 key1=value1:NoSchedule
#NoExecute意为不能被调度,且会驱逐节点上已有的pod
kubectl taint node node1 key1=value1:NoExecute
#PreferNoSchedule意为尽量不被调度
kubectl taint node node1 key1=value1:PreferNoSchedule

1
2
3
4
5
6
7
2. 容忍

容忍 Tolerations

污点是标记的node节点,那么容忍就是对pod的标记.简单的来说就是pod如果标记了容忍那么即使node节点被标记了污点,pod依然会被调度到该节点上

​ 我们来做一个示例,示例的目的是将node2节点设置为污点,然后我们将pod通过nodeSelector调度到node2节点上.看一下是否会成功.

首先我们将master节点恢复污点,node1与node2节点移除污点

[root@master ~]# kubectl describe node master | grep Taints
Taints:             node-role.kubernetes.io/master:NoSchedule
[root@master ~]# kubectl describe node node1 | grep Taints
Taints:             key1=value1:NoSchedule
[root@master ~]# kubectl describe node node2 | grep Taints
Taints:             key1=value1:NoSchedule
[root@master ~]#
1
2
3
4
5
6
7

可以看到三个节点都已经有了污点,然后部署以下的yaml

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  labels:
    run: nginx
    app: nginxtest
  name: nginx-tolerations
spec:
  nodeSelector:
   app: app
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: nginx-tolerations
  #此处为容忍设置,意为容忍key1中存在NoSchedule
  tolerations:
  - key: "key1"
    operator: "Exists"
    effect: "NoSchedule"
    
    
 #可以看到依然被部署到了node1节点上   
[root@master ~]# kubectl get pod -owide
NAME                READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
nginx-tolerations   1/1     Running   0          6s    172.16.166.139   node1   <none>           <none>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

​ 这就是pod的容忍的能力.operator有二个值

  tolerations:
  - key: "key1"
    operator: "Exists"
    effect: "NoSchedule"
1
2
3
4
  • Equal,意如其名就是effect值应该相等
  • Exists,就是存在,effect值存在即可

多个污点的匹配原则

可以给一个节点添加多个污点,也可以给一个 Pod 添加多个容忍度设置。

Kubernetes 处理多个污点和容忍度的过程就像一个过滤器:从一个节点的所有污点开始遍历, 过滤掉那些 Pod 中存在与之相匹配的容忍度的污点。余下未被过滤的污点的 effect 值决定了 Pod 是否会被分配到该节点,特别是以下情况:

  • 如果未被过滤的污点中存在至少一个 effect 值为 NoSchedule 的污点, 则 Kubernetes 不会将 Pod 分配到该节点。
  • 如果未被过滤的污点中不存在 effect 值为 NoSchedule 的污点, 但是存在 effect 值为 PreferNoSchedule 的污点, 则 Kubernetes 会 尝试 不将 Pod 分配到该节点。
  • 如果未被过滤的污点中存在至少一个 effect 值为 NoExecute 的污点, 则 Kubernetes 不会将 Pod 分配到该节点(如果 Pod 还未在节点上运行), 或者将 Pod 从该节点驱逐(如果 Pod 已经在节点上运行)。

例如,假设您给一个节点添加了如下污点

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule
1
2
3

假定有一个 Pod,它有两个容忍度:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
1
2
3
4
5
6
7
8
9

上述 Pod 不会被分配到 node1 节点,因为其没有容忍度和第三个污点相匹配。

但是如果在给节点添加上述污点之前,该 Pod 已经在上述节点运行, 那么它还可以继续运行在该节点上,因为第三个污点是三个污点中唯一不能被这个 Pod 容忍的

4.3 镜像拉取

imagePullPolicy定义了镜像的拉取策略,该选项有以下3个值

  1. Always

    每次都拉取镜像,该参数为默认参数

  2. Never

    从不拉取镜像,只使用本地镜像

  3. IfNotPresent

    如果本地不存在该镜像,就从网络拉取镜像,我们实战中经常用到该选项

4.4 环境变量

​ 通过env选项,我们可以注入环境变量,我们可以通过环境变量来区分是测试环境还是生产环境.在此示例中,我们注入了ASPNetCore使用的ASPNETCORE_ENVIRONMENT环境变量值为Production,还设置了时区为亚洲/上海

 env:
  - name: ASPNETCORE_ENVIRONMENT
    value: Production
  - name: TZ
    value: Asia/Shanghai
1
2
3
4
5

4.5 健康检查

    ...
    livenessProbe:
     httpGet:
        path: /
        port: 80
     initialDelaySeconds: 10
     periodSeconds: 3
     timeoutSeconds: 1
     successThreshold: 1
     failureThreshold: 3
    readinessProbe:
     httpGet:
        path: /
        port: 80
     initialDelaySeconds: 10
     periodSeconds: 3 
     timeoutSeconds: 1
     successThreshold: 1
     failureThreshold: 3
     ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

​ 健康检查用于检测我们的应用是否在正常工作,是保障业务可用性的一种机制.如果检测的状态不符合预期,则将应用实例排除,不在正在业务流量.

Kubernetes中的健康检查使用存活探测liveness probes)和就绪探测readiness probes)来实现,这两种机制是Kubernetes中实现自愈能力的重要手段.基于这两种探测机制,可以实现以下需求:

  • 异常实例自动剔除,并重启新实例
  • 多种类型探针检测,保证异常pod不接入流量
  • 不停机部署,更安全的滚动升级
1. 存活探测

liveness probe,存活探测.用来判断一个 Pod 是否处于存活状态,如果一个 Pod 被探测到不处于存活状态,则由上层判断机制来处理,如果上层配置重启策略为 restart always 的话,Pod 就会被重启。

2. 就绪探测

readiness probe,就绪探测.用来判断一个 Pod 是否处于就绪状态,是否能对外提供相应服务了。当Pod处于就绪状态时,负载均衡器才会将流量打到这个 Pod,否则将把流量从这个 Pod 上面摘除。比如我们的一个服务在启动时,需要从数据库缓存大量数据,所以就会启动的比较慢,那么这个时候则需要等待程序完全启动以后,在接入请求流量

​ 以上两种类型的探测的选项一致,那么我来看下这几个选项的意思

  1. initialDelaySeconds:延迟探测,单位秒,意为pod启动后,延迟几秒进行探测
  2. periodSeconds:探测周期,单位秒,意为每隔多少秒探测一次
  3. timeoutSeconds:超时时间,单位秒
  4. successThreshold:健康阈值
  5. failureThreshold:不健康阈值
3. 探针

​ 本示例的yaml文件中配置的选两项是:pod启动后,10秒探测一次,每隔3秒探测一次,如果失败次数超过3则会触发restartPolicy重启策略

​ 一共有三种类型的探针,本实例使用的是httpGet探针,

  1. httpGet

    Kubernetes对指定的容器**路径(path)端口(port)**执行一个HTTP Get请求,可以看到每3秒nginx就会输出请求日志与

     kubectl logs nginx-pod-name
    ...
    ...
    192.168.1.196 - - [25/Jan/2022:18:04:49 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    192.168.1.196 - - [25/Jan/2022:18:04:52 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    192.168.1.196 - - [25/Jan/2022:18:04:52 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    192.168.1.196 - - [25/Jan/2022:18:04:55 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    192.168.1.196 - - [25/Jan/2022:18:04:55 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    192.168.1.196 - - [25/Jan/2022:18:04:58 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    192.168.1.196 - - [25/Jan/2022:18:04:58 +0800] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.22" "-"
    ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    liveness probe使用该类型探针

    1. 状态码:200-300,视为正常.
    2. 状态码4XX-5XX,视为错误,pod会被重启.

    readiness probe使用该类型探针

    1. 状态码:200-300,视为正常.
    2. 状态码4XX-5XX,pod会被标记不健康,Kubernetes不在调度流量请求此pod.
  2. tcpSocket

tcpSocket探针与httpGet非常相似,对指定的容器的端口执行一个TCP检查,如果端口能被打开则表示探测成功,否则表示失败,使用方式也很简单

...
...
#4.5 健康检查    
    livenessProbe:
     tcpSocket:
        port: 80
     initialDelaySeconds: 10
     periodSeconds: 3
     timeoutSeconds: 1
     successThreshold: 1
     failureThreshold: 3
    readinessProbe:
     tcpSocket:
        port: 80
     initialDelaySeconds: 10
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. exec

​ 该类型的探针通过在目标容器中执行由用户自定义的命令来判断容器的监控状态,若命令状态返回值为0则表示“成功”通过检测,其他值则均为“失败”状态。

​ 该命令使用方式也比较简单,具体格式如下:

...
...
#4.5 健康检查    
    livenessProbe:
     exec:
         command: ["/bin/sh" ,"-c","echo from-livenessProbe > livenessProbe.txt"]
     initialDelaySeconds: 10
     periodSeconds: 3
     timeoutSeconds: 1
     successThreshold: 1
     failureThreshold: 3
    readinessProbe:
     exec:
         command: ["/bin/sh" ,"-c","echo from-readinessProbe > readinessProbe.txt"]
     initialDelaySeconds: 10
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

​ 我们部署修改后的yaml文件后,进入到pod内部来查看当前目录是否存在这两个文件

[root@master ~]# kubectl exec -it nginx-pod-name bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-pod-name:/# ls
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  livenessProbe.txt  media  mnt  opt  proc  readinessProbe.txt  root  run  sbin  srv  sys  tmp  usr  var
1
2
3
4

可以看到当前目录已经生成了livenessProbe.txtreadinessProbe.txt两个文件.

​ 探针每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

​ 至此三种类型探针已经介绍完毕,这三种类型的探针,同时只能使用一种,不能同时使用.在实际应用中大多使用httpGet方式.

4.6 资源限制

Kubernetes对于资源限制只能限制两种资源类型cpu内存,对于网络与存储目前是无法限制的.

    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 256Mi
1
2
3
4
5
6
7
  1. requests,是资源需求,即pod最少需要的使用量.

    ​ 本示例中的yaml配置的是:最少需要内存128mb,cpu为100m.

  2. limits,是资源限制,pod运行期间,最大的使用量.

    ​ 本示例中的yaml配置的是:pod运行期间,最大只能使用内存为256mb,cpu为500m

​ 这个cpu的资源我们该怎么理解?1个cpu等于1000m,假如node节点上cpu是4核,这么整个cpu资源就为4000m.如果requests的资源需求在所有的node节点上无法被满足,那么这个pod就会被Pending,直到有node节点的资源满足才会被调度.

​ 为什么要资源限制?试想一下,如果不对pod进行资源限制,那么集群资源相对紧张的时候,每个pod会争抢内存与cpu, 当node节点资源不够的时候,可能会造成pod的漂移,即可能会被Kubernetes终止占用资源最大的pod,然后调度到认为合适的node节点上.当然也肯能会处于Pending状态

4.7 重启策略

restartPolicy来指定容器的重启策略,该参数有以下三个选项

  1. Always:默认值,只要退出就重启
  2. OnFailure:失败退出时(exit code 不为 0)才重启
  3. Never: 永远不重启

​ kubelet 会按指数回退方式计算重启的延迟(10s、20s、40s、...),其最长延迟为 5 分钟。 一旦某容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启回退计时器执行 重置操作

4.8 生命周期

​ pod被认为是相对临时性(而不是长期存在)的实体。 Pod 会被创建,并赋予一个唯一的 ID, 并被调度到节点,并在终止(根据重启策略)或删除之前一直运行在该节点。

​ 如果一个node节点挂掉了,那么该节点 的pod也被计划在给定超时期限结束后被删除.Pod 自身不具有自愈能力。如果 pod 被调度到某node节点,而该节点之后失效,pod会被删除;类似地,pod无法在因节点资源 耗尽或者节点维护而被驱逐期间继续存活。Kubernetes使用一种高级抽象 来管理这些相对而言可随时丢弃的pod实例,称作 控制器

​ pod状态一共有以下五种状态

  • Pending: Kubernetes 已经创建并确认该 Pod,可能两种情况: 1. Pod 还未完成调度(例如没有合适的节点);2. 正在从镜像仓库拉取镜像
  • Running: 该 Pod 已经被绑定到一个节点,并且该 Pod 所有的容器都已经成功创建,其中至少有一个容器正在运行,或者正在启动/重启
  • Succeeded:Pod 中的所有容器都已经成功终止,并且不会再被重启
  • Failed:Pod 中的所有容器都已经终止,至少一个容器终止于失败状态:容器的进程退出码不是 0,或者被系统 kill
  • Unknown: 因为某些未知原因,不能确定 Pod 的状态,通常的原因是 master 与 Pod 所在节点之间的通信故障

pod-status-trans

4.9 Init容器

init 容器是一种特殊的容器,它会在pod内应用容器启动之前运行,用于执行一些初始化操作.该容器与普通的容器非常像,但是除了以下2点:

  • init 容器总是最先运行
  • 运行完成后才会于运行下一个容器

​ 如果init 容器出现错误,那么Kubernetes会不断重启该容器,直到成功为止.如果restartPolicy重启策略Never,则整个pod的状态会被设置成失败.

init 容器不支持livenessProbereadinessProbe,如果有多个init 容器,Kubernetes会依次按照顺序逐个执行,每一个必须执行成功,下一个才能执行.

​ 下面我们看一个init 容器示例,该示例是使用init 容器ningx的启示页面更改成百度.

...
...
#4.2 节点调度  
  nodeSelector:
   app: app
   
#4.9 init容器
  initContainers:
  - name: downloand-index-page
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ["wget","-O","/wwwroot/index.html","https://www.baidu.com"]
    #本实例用到了存储卷,该功能我们将在第六章介绍
    volumeMounts:
    - name: wwwroot
      mountPath: "/wwwroot"
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: nginx-container-name
    volumeMounts:
     - name: wwwroot
       mountPath: /usr/share/nginx/html
    ports:
    - containerPort: 80
...
...
  restartPolicy: Always
  volumes:
  - name: wwwroot
    emptyDir: {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

​ 首先我们挂载了一个wwwroot目录到init容器,然后通过wget下载了百度的首页,保存到了wwwroot目录,名为index.html.然后启动nginx容器,nginx容器上同样挂载了wwwroot目录,映射到了容器内部nginx的默认目录内,即**/usr/share/nginx/html**,这样nginx的index.html就被替换了.

​ pod成功启动以后,我使用curl命令来访问pod的IP地址,可以看到首页已经被替换成百度了

image-20220126205511002

第四章 存储卷

Kubernetes支持很多种类型的存储卷,在此章节中,我主要讲解emptyDir,hostPath,持久卷这三种类型.还有其他类型的存储卷如:

  1. **SAN(存储区域网络)😗*iSCSI,FB
  2. **NAS(网络附加存储)😗*NFS,CIFS
  3. 分布式存储: cephfs,Glusterfs,ceph(rbd)
  4. **云存储:**Azure Disk,Amazon EBS

1. emptyDir

2. hostPath

​ 此类型的存储方式,类似在创建容器时,我们使用的**-v xx/xx**这样挂载目录,意思是在将宿主机的目录映射到容器内部目录,如果目录不存在则会自动创建目录,当pod被删除时,emptyDir中存储的数据与目录也会被一起删除

3. 持久卷

第五章 Secret与ConfigMap

1. Secret

​ 在真实业务场景下,有一些敏感信息,不能使用明文,Secret就是用来解决该场景的组件.

1. 查看

[root@master ~]# kubectl get  secret
NAME                  TYPE                                  DATA   AGE
default-token-kctt5   kubernetes.io/service-account-token   3      8dxxxxxxxxxx kubectl get sc[root@master ~]# kubectl get  secretNAME                  TYPE                                  DATA   AGEdefault-token-kctt5   kubernetes.io/service-account-token   3      8d

#描述指定的secret
[root@master ~]# kubectl describe secret default-token-kctt5
Name:         default-token-kctt5
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: b7ebc16d-ad92-4240-99d2-236abfbe1eb5

Type:  kubernetes.io/service-account-token

Data
====
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IjQ0bjlCSDZiOGM0ejR3akpoZDlkV0pZZU5iRWtQY0RkVnB5b3ctRnVrdzAifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4ta2N0dDUiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImI3ZWJjMTZkLWFkOTItNDI0MC05OWQyLTIzNmFiZmJlMWViNSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.tNWsCzQawHVKqpu4Bg7uuGr5jN3njtPGHdPIfBGWGgU5MWBbP4bwDWiuKga2PGqsl3x_e3OVKngqE3RpNFmwD2u3RBb22w_NsBM6wdzTATbXB1oYf3OaYNQOgrFCyeuNqV1WqI276BPMzElbQNdFlz3_2roi0MJ8GK28MHP1eOIVcAB81v5b4G3vEI0jji4RpSYl1s0uBrALSZdfiecuRXbdKvm_v-jBVy1krLhKpZIUt_Qtc9CePPu_Wlnynmcn6CcFB8hIlclNs2MfRXnG_Q3BEjJTzeVDwXEg5WI07sxaBxuVUhIIkODDks_sURWiVhj_14BpOjXYeZhbUC5CnQ
ca.crt:     1099 bytes
[root@master ~]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

2. 创建

​ 创建secret有两种方式,

  • 使用**kubectl create secret generic 名称 --from-literal=key名称=base64值 **方式
#我们创建一个sqlserver的连接字符串
#直接利用echo生成base64字符串

#生成base64字符串
[root@master ~]#  echo -n 'server=.;db=users;uid=sa;pwd=123456;' | base64
c2VydmVyPS47ZGI9dXNlcnM7dWlkPXNhO3B3ZD0xMjM0NTY7

#创建secret
[root@master ~]# kubectl create secret generic db-connection --from-literal=connection=c2VydmVyPS47ZGI9dXNlcnM7dWlkPXNhO3B3ZD0xMjM0NTY7
secret/db-connection created

#查看该secret
[root@master ~]# kubectl get secret
NAME                  TYPE                                  DATA   AGE
db-connection         Opaque                                1      58s
default-token-kctt5   kubernetes.io/service-account-token   3      8d

[root@master ~]# kubectl describe secret default-token-kctt5
Name:         default-token-kctt5
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: b7ebc16d-ad92-4240-99d2-236abfbe1eb5

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1099 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IjQ0bjlCSDZiOGM0ejR3akpoZDlkV0pZZU5iRWtQY0RkVnB5b3ctRnVrdzAifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4ta2N0dDUiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImI3ZWJjMTZkLWFkOTItNDI0MC05OWQyLTIzNmFiZmJlMWViNSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.tNWsCzQawHVKqpu4Bg7uuGr5jN3njtPGHdPIfBGWGgU5MWBbP4bwDWiuKga2PGqsl3x_e3OVKngqE3RpNFmwD2u3RBb22w_NsBM6wdzTATbXB1oYf3OaYNQOgrFCyeuNqV1WqI276BPMzElbQNdFlz3_2roi0MJ8GK28MHP1eOIVcAB81v5b4G3vEI0jji4RpSYl1s0uBrALSZdfiecuRXbdKvm_v-jBVy1krLhKpZIUt_Qtc9CePPu_Wlnynmcn6CcFB8hIlclNs2MfRXnG_Q3BEjJTzeVDwXEg5WI07sxaBxuVUhIIkODDks_sURWiVhj_14BpOjXYeZhbUC5CnQ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  • 使用声明式的yam文件
#将该内容保存为secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-connection2
type: Opaque
data:
  connection: c2VydmVyPS47ZGI9dXNlcnM7dWlkPXNhO3B3ZD0xMjM0NTY7
1
2
3
4
5
6
7
8
[root@master ~]# kubectl apply -f secret.yaml
secret/db-connection created
1
2

3. 删除

[root@master ~]# kubectl delete secret default-token-kctt5
secret "default-token-kctt5" deleted
[root@master ~]#
1
2
3

4. 使用

​ 使用secret的方式有两种

  • 以环境变量的方式注入
  • 以挂载卷的形式注入

​ 下面我们将使用变量注入的方式来使用该变量

#将该内容保存为get_secret.yaml
apiVersion: v1
kind: Pod
metadata:
  name: get-db-connection
spec:
  containers:
  - name: get-db-connection
    image: nginx
    imagePullPolicy: IfNotPresent
    env:
      - name: DB_CONNECTION
        valueFrom:
          secretKeyRef:
            #这里为secret名称
            name: db-connection
            #这里为key
            key: connection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master ~]# kubectl apply -f get_secret.yaml
pod/get-db-connection created

[root@master ~]# kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
get-db-connection   1/1     Running   0          4s

#进入到该Pod内部,并查看环境变量
[root@master ~]# kubectl exec -it get-db-connection bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

#可以看到该环境变量已经注入,并且已经从base64解析
[root@get-db-connection:/# echo $DB_CONNECTION
server=.;db=users;uid=sa;pwd=123456;
[root@get-db-connection:/#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

​ 我们来使用挂载卷的方式来使用该secret

# 将内容保存到get_secret_volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: get-secret-volume
spec:
  containers:
  - name: get-secret-volume
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: dbstr
      #挂载到根目录下的db
      mountPath: "/db"
  volumes:
  - name: dbstr
    secret:
      secretName: db-connection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master ~]# kubectl apply -f get_secret_volume.yaml
pod/get-secret-volume created

[root@master ~]# kubectl exec -it get-secret-volume bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

#可以看到db目录已经生成了
root@get-secret-volume:/# ls
bin  boot  db  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

#读取该目录
root@get-secret-volume:/# cat /db/connection
server=.;db=users;uid=sa;pwd=123456;
1
2
3
4
5
6
7
8
9
10
11
12
13

2. configmap

configmap简称cm,通常用于来注入配置文件,使用key=value键值对的像是来配置数据.

1. 查看

[root@master ~]# kubectl get configmap
NAME               DATA   AGE
kube-root-ca.crt   1      8d

#描述指定的configmap
[root@master ~]# kubectl describe configmap kube-root-ca.crt
Name:         kube-root-ca.crt
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/description:
                Contains a CA bundle that can be used to verify the kube-apiserver when using internal endpoints such as the internal service IP or kubern...

Data
====
ca.crt:
----
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIyMDEyMDEzMDQ0NVoXDTMyMDExODEzMDQ0NVowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALnF
vVmqOx3PYFZU4iQiNX9Izs2SMf043UT5EOKvulWyLN81DiHxXaGkNfXdx2f4Msj6
NhAVlf9NKWxcJsjqdzVkve9J72HW2qBeQvK4YdNilBioVGlMX/gd/HdPFn70p/Gw
cdGi5feO16kf2oqlD2PlCtCXqifKX8rq/EWKR95tDuhWVYzS0uw0y5A48SC72S2U
9xuDOP2oCbpnIl2nwdgeo9UZlLklIIZTrAEVLinQgHExXH1z7jJoXcFuLUIhcNsv
EOWkOwc4lXLwWuW7nJoxo9x3Caw+SJFBPfp32PjdsH4lU7FI+KnYHMLoRKv57RiE
LtmTJtoxw2kaKNN22asCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFHtus55PiONH7Z4zoR3kppjLZc2pMBUGA1UdEQQO
MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAH/CQItVAhUM25xaWZAa
T2tt5qTAbN7PqxnGSRm5b5j17j33V3FUCLx6jBW1PLxA+AP/JGCcu+V4yns6I6Mh
A6HqiFzIAaE4dA8wEICD1/fmlloz92OxltB5OrIKaUxtw3GKFywnOeipe+rM56AU
CcnH8+/pWbRxBm9H1+q14wK7Jvk3irM+pwfhblUaxYpXFA2/fyp6nwBPZo818BP4
sNG3zP+7aoCDPG7844Rgyrq6Dfju+T5GSGgiTNaHt6Y10+iYXU47AOj0jFYqPUhT
V1C/NZKQUe5QpI361PLqo7VZCHufP9Y45Pk91+jt6GOuz4qwyZeFGvwSNJCviraB
ApY=
-----END CERTIFICATE-----


BinaryData
====

Events:  <none>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

2. 创建

configmap创建与使用的方式与secret很相似.

  • 使用**kubectl create configmap 名称 --from-literal=key=value **方式
[root@master ~]# kubectl create configmap appsetting --from-literal=db.conneciton='server=.;db=users;uid=sa;pwd=123456;' --from-literal=redis.connection='localhost:6379'
configmap/appsetting created

[root@master ~]# kubectl get cm
NAME               DATA   AGE
appsetting         2      7s
kube-root-ca.crt   1      8d

[root@master ~]# kubectl describe cm appsetting
Name:         appsetting
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
redis.connection:
----
localhost:6379
db.conneciton:
----
server=.;db=users;uid=sa;pwd=123456;

BinaryData
====

Events:  <none>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • 通过yaml的方式进行创建
#将以下内容保存为cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: appsetting
data:
  db.conneciton: 'server=.;db=users;uid=sa;pwd=123456;'
  redis.connection: 'localhost:6379'
1
2
3
4
5
6
7
8
[root@master ~]# kubectl create -f cm.yaml
configmap/appsetting created
1
2

3. 删除

[root@master ~]# kubectl delete cm appsetting
configmap "appsetting" deleted
1
2

4. 使用

​ 使用configmap的方式有两种

  • 以环境变量的方式注入

  • 以挂载卷的形式注入

下面我们将使用变量注入的方式来使用该变量

#将该内容保存为get_configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: get-configmap
spec:
  containers:
  - name: get-configmap
    image: nginx
    imagePullPolicy: IfNotPresent
    env:
      - name: DB_CONNECTION
        valueFrom:
          configMapKeyRef:
            #这里为configmap名称
            name: appsetting
            #这里为key
            key: db.conneciton
      - name: REDIS_CONNECTION
        valueFrom:
          configMapKeyRef:
           #这里为configmap名称
            name: appsetting
            #这里为key
            key: redis.connection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@master ~]# kubectl apply -f get_configmap.yaml
pod/get-configmap created

[root@master ~]# kubectl get pod
NAME            READY   STATUS    RESTARTS   AGE
get-configmap   1/1     Running   0          2s

#进入到该Pod内部,并查看环境变量
[root@master ~]# kubectl exec -it get-configmap bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

#可以看到该环境变量已经注入
root@get-configmap:/# echo $DB_CONNECTION
server=.;db=users;uid=sa;pwd=123456;

root@get-configmap:/# echo $REDIS_CONNECTION
localhost:6379
root@get-configmap:/#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

​ 我们来使用挂载卷的方式来使用该configmap

# 将内容保存到get_config_volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: get-config-volume
spec:
  containers:
  - name: get-config-volume
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: config-volume
      #挂载到根目录下的configmap
      mountPath: "/configmap"
  volumes:
  - name: config-volume
    configMap:
      name: appsetting
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master ~]# kubectl apply -f get_config_volume.yaml
pod/get-config-volume created

[root@master ~]# kubectl exec -it get-config-volume bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

#可以看到configmap目录已经生成了
root@get-config-volume:/# ls
bin        dev                   etc   lib64  opt   run   sys  var
boot       docker-entrypoint.d   home  media  proc  sbin  tmp
configmap  docker-entrypoint.sh  lib   mnt    root  srv   usr
root@get-config-volume:/#

#读取该目录
root@get-config-volume:/# cat /configmap/db.conneciton
server=.;db=users;uid=sa;pwd=123456;

root@get-config-volume:/# cat /configmap/redis.connection
localhost:6379
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

第六章 控制器

Kubernetes中有一组不同的控制器,来确保集群当前的状态保持预期,控制器主要是用来控制Pod的状态和行为,Kubernetes一共有6种不同的类型的控制器,适用于不同场景的需求.

1. Deployment

Deployment简称deploy, 顾名思义,是用于部署应用的对象。它使Kubernetes最常用的一个对象,它为Pod的创建提供了一种声明式的定义方法

适合无状态的服务部署,什么是无状态的?就是应用程序内部没有存储数据,应用程序可以随意启动与关闭.

​ 比如我们告诉Deployment,我们需要3个pod,那么Deployment控制器就会始终保持3个pod,如果其中的1个pod挂掉了,那么Deployment控制器则会马上重新生成一个pod,保证环境里有3个pod,那么如果挂掉的pod又恢复了,则Deployment控制器则会删除其中的一个pod.

​ 创建Deployment控制器有两种方式,第一种直接使用kubectl create deploy 名称创建,我们来创建一个具有4个副本(pod)的控制器

#kubernetes1.18版本以后使用以下命令
[root@master ~]# kubectl create deploy nginx-deploy --image=nginx --port=80 --replicas=4
deployment.apps/nginx-deploy created
#查看已经部署的Deployment控制器
[root@master ~]# kubectl get deploy
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   4/4     4            4           30s
#Kubernetes1.18版本以前使用以下命令
[root@master ~]# kubectl run nginx-deploy --image=nginx --port=80 --replicas=4
1
2
3
4
5
6
7
8
9

可以看到一个Deployment控制器已经被创建,并且READY中显示为4,说明4个副本(pod)已经全部就绪.我们来查看一下副本(pod)的具体分布

[root@master ~]# kubectl get pod -owide | grep nginx-deploy
nginx-deploy-84bf7bc885-hxpnv   1/1     Running   0             3m43s   172.16.166.138   node1   <none>           <none>
nginx-deploy-84bf7bc885-pm6ml   1/1     Running   0             3m43s   172.16.104.58    node2   <none>           <none>
nginx-deploy-84bf7bc885-tnwxf   1/1     Running   0             3m43s   172.16.166.139   node1   <none>           <none>
nginx-deploy-84bf7bc885-wm22j   1/1     Running   0             3m43s   172.16.104.57    node2   <none>           <none>
1
2
3
4
5

可以看到每个node节点上启动了2个pod.接下来,我删除其中名为nginx-deploy-84bf7bc885-wm22j的pod,看看会发生什么

[root@master ~]# kubectl delete pod nginx-deploy-84bf7bc885-wm22j
pod "nginx-deploy-84bf7bc885-wm22j" deleted
[root@master ~]# kubectl get pod -owide | grep nginx-deploy
nginx-deploy-84bf7bc885-hfbw4   1/1     Running   0             26s     172.16.104.59    node2   <none>           <none>
nginx-deploy-84bf7bc885-hxpnv   1/1     Running   0             6m18s   172.16.166.138   node1   <none>           <none>
nginx-deploy-84bf7bc885-pm6ml   1/1     Running   0             6m18s   172.16.104.58    node2   <none>           <none>
nginx-deploy-84bf7bc885-tnwxf   1/1     Running   0             6m18s   172.16.166.139   node1   <none>           <none>
1
2
3
4
5
6
7

可以看到,nginx-deploy-84bf7bc885-wm22j虽然被删除了,但是又重新启动了一个,这就是Deployment控制器的特性之一,下面,我们在Hyper-V中将node2节点直接关机,再来查看pod会发生什么变化

[root@master ~]# kubectl get node
NAME     STATUS     ROLES                  AGE   VERSION
master   Ready      control-plane,master   34d   v1.23.1
node1    Ready      <none>                 33d   v1.23.1
node2    NotReady   <none>                 33d   v1.23.1

[root@master ~]# kubectl get pod -owide | grep nginx-deploy
nginx-deploy-84bf7bc885-4kn9w   0/1     ContainerCreating   0             2m59s   <none>           node1   <none>           <none>
nginx-deploy-84bf7bc885-5w66q   1/1     Running             0             14m     172.16.166.140   node1   <none>           <none>
nginx-deploy-84bf7bc885-76j6h   1/1     Terminating         0             14m     172.16.104.60    node2   <none>           <none>
nginx-deploy-84bf7bc885-fhfx8   1/1     Running             0             14m     172.16.166.141   node1   <none>           <none>
nginx-deploy-84bf7bc885-jwk6c   0/1     ContainerCreating   0             2m59s   <none>           node1   <none>           <none>
nginx-deploy-84bf7bc885-r94fg   0/1     Terminating         0             14m     <none>           node2   <none>           <none>
1
2
3
4
5
6
7
8
9
10
11
12
13

可以看到node2节点上的pod开始处于Terminating状态,而node1节点上正在创建新的pod,并且node1节点pod的数量为4

​ 对于已经部署的Deployment控制器,我们可以通过kubectl describe 名称来查看具体信息

[root@master ~]# kubectl describe deploy nginx-deploy
Name:                   nginx-deploy
Namespace:              default
CreationTimestamp:      Wed, 23 Feb 2022 09:19:17 -0500
Labels:                 app=nginx-deploy
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=nginx-deploy
Replicas:               4 desired | 4 updated | 4 total | 1 available | 3 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx-deploy
  Containers:
   nginx:
    Image:        nginx
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      False   MinimumReplicasUnavailable
  Progressing    True    ReplicaSetUpdated
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deploy-84bf7bc885 (4/4 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  15s   deployment-controller  Scaled up replica set nginx-deploy-84bf7bc885 to 4
[root@master ~]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

​ 删除Deployment控制器使用kubectl delete deploy 名称

[root@master ~]# kubectl delete deploy nginx-deploy
deployment.apps "nginx-deploy" deleted
1
2

​ 另外一种创建Deployment控制的方式就是使用声明式的yaml文件进行创建

#该文件定义了一个Deployment对象
apiVersion: apps/v1
kind: Deployment
metadata:
  #指定该控制器的名称
  name: nginx-deploy
spec:
  #指定pod数量为4
  replicas: 4
  selector:
   #指定该控制器需要匹配的pod的标签
   #即需要与下template中metadata里的labels的值一模一样
   matchLabels:
    app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      #镜像为nginx
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@master ~]# kubectl apply -f deploy.yaml
deployment.apps/nginx-deploy created
[root@master ~]# kubectl get deploy
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   4/4     4            4           7s
1
2
3
4
5

我们可以通过kubectl edit deploy 名称的方式直接编辑已经被创建的控制器,

#比如将该控制器的副本(pod)数更改成2,更改replicas值就可以
[root@master ~]# kubectl edit deploy nginx-deploy
...
...
spec:
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
...
...
[root@master ~]# kubectl get pod | grep nginx-deploy
nginx-deploy-66857ff745-4qgcv   1/1     Running   0             8m43s
nginx-deploy-66857ff745-xktrn   1/1     Running   0             8m43s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

我们亦可以直接伸缩正在运行中的Deployment控制器

#使用以下命令对副本伸缩到4个
[root@master ~]# kubectl scale deploy nginx-deploy --replicas=4
deployment.apps/nginx-deploy scaled
1
2
3

​ 我们对Deployment控制器进行镜像的更新与回滚,通过使用命令**kubectl set image deploy/名称 容器名称=镜像 <--record>**命令,来进行镜像更新

#由于我们示例中的nginx镜像版本为最新的,所以我们将镜像更改成为比较旧的版本,比如:1.19.9

[root@master ~]# kubectl set image deploy/nginx-deploy nginx=nginx:1.19.9 --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deploy image updated
[root@master ~]# kubectl get pod  | grep nginx-deploy
nginx-deploy-54f8496db5-6hw5f   0/1     ContainerCreating   0             65s
nginx-deploy-54f8496db5-c8tcn   0/1     Terminating         0             62m
nginx-deploy-54f8496db5-httwj   0/1     ContainerCreating   0             65s
nginx-deploy-66857ff745-8pw9v   1/1     Running             0             58m
nginx-deploy-66857ff745-bb7hw   1/1     Running             0             58m
nginx-deploy-66857ff745-qw78c   1/1     Running             0             58m

[root@master ~]# kubectl get pod  | grep nginx-deploy
nginx-deploy-54f8496db5-6hw5f   1/1     Running             0             17m
nginx-deploy-54f8496db5-httwj   0/1     ContainerCreating   0             17m
nginx-deploy-54f8496db5-qxsnh   1/1     Running             0             4m58s
nginx-deploy-54f8496db5-t5n4q   0/1     ContainerCreating   0             4m56s
nginx-deploy-66857ff745-bb7hw   1/1     Running             0             75m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

可以看到其中的一个副本(pod),处于Terminating状态,正在被终结,另外有2个正在创建中.这说明Deployment控制器并没有将全部的副本(pod)同时进行更新,而是终结了1个副本(pod),这样另外Running的副本(pod)依然可以接受流量处理请求,这样每次更新一个副本(pod),这就是Deployment控制器的滚动升级的特性.

我们再次将镜像更新到最新版本

[root@master ~]# kubectl set image deploy/nginx-deploy nginx=nginx --record
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deploy image updated
[root@master ~]#  kubectl get pod  | grep nginx-deploy
nginx-deploy-66857ff745-kz7t9   1/1     Running   0             3m47s
nginx-deploy-66857ff745-vj6wq   1/1     Running   0             3m48s
nginx-deploy-66857ff745-x5s2j   1/1     Running   0             3m48s
nginx-deploy-66857ff745-zpqt4   1/1     Running   0             3m47s
1
2
3
4
5
6
7
8

--reord参数的意义是记录每次更新信息,这样,我们可以通过kubectl rollout history deploy/名称来查看所有的镜像变更历史记录

[root@master ~]# kubectl rollout history deploy/nginx-deploy
deployment.apps/nginx-deploy
REVISION  CHANGE-CAUSE
2         kubectl set image deploy/nginx-deploy nginx=nginx:1.19.9 --record=true
3         kubectl set image deploy/nginx-deploy nginx=nginx --record=true
1
2
3
4
5

我们可以通过命令kubectl rollout undo deploy/名称 --to-revision=版本号 进行回滚

#我再次切换到1.19.9版本
[root@master ~]# kubectl rollout undo deploy/nginx-deploy --to-revision=2
deployment.apps/nginx-deploy rolled back
[root@master ~]# kubectl get pod  | grep nginx-deploy
nginx-deploy-54f8496db5-66f94   1/1     Running   0             12s
nginx-deploy-54f8496db5-hrw8b   1/1     Running   0             10s
nginx-deploy-54f8496db5-vn2s8   1/1     Running   0             10s
nginx-deploy-54f8496db5-zjq85   1/1     Running   0             12s
1
2
3
4
5
6
7
8

控制器的镜像更新受制于更新策略

[root@master ~]# kubectl describe deploy nginx-deploy
Name:                   nginx-deploy
Namespace:              default
CreationTimestamp:      Thu, 24 Feb 2022 06:44:10 -0500
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 4
                        kubernetes.io/change-cause: kubectl set image deploy/nginx-deploy nginx=nginx:1.19.9 --record=true
Selector:               app=nginx
Replicas:               4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
#此处定义更新策略
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.19.9
    Port:         80/TCP
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

max unavailable:升级过程中最多有多少个副本(pod)处于无法提供服务的状态, 该值为百分比或者整型值,该值默认为25%,通过计算max unavailable=4x0.25=1,即每次终结1个副本(pod)

max surge:升级过程中最多可以比原先设置多出的副本(pod)数量, 该值为百分比或者整型值.这个值指定为25%时,也就是说,新旧副本(pod)的总量不能超过125%。简单来讲,就是在滚动升级时,会先启动25%的新的副本(pod)。然后开始杀掉旧的副本(pod),每当一个旧的副本(pod)被杀掉,一个新的副本(pod)的会被启动,始终保持总量不超过125%,直至更新完成.

2. StatefulSet

​ 适合有状态的服务部署,

3. DaemonSet

image-20220128164006465

DaemonSet简称(ds)会在所有的节点上或者某些节点都运行一个pod的副本.当有node节点加入时,也会在该节点上增加一个pod.反之如果node节点被移除,则pod也会消失.

**DaemonSet **控制器的一些典型用法:

  • 在每个节点上运行集群守护进程
  • 在每个节点上运行监控守护进程
  • 在每个节点上运行日志收集守护进程

下面我们就使用该控制器,在每一个node节点上部署一个nginx镜像

apiVersion: apps/v1             
kind: DaemonSet                 
metadata:                       
  name: busybox
spec:                        
  selector:
   #指定该控制器需要匹配的pod的标签
   #即需要与下template中metadata里的labels的值一模一样
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
      - name: busybox
        image: busybox
        imagePullPolicy: IfNotPresent
        #busybox镜像是无法常驻的,所以使用sleep命令,让进程停住36000秒
        command: 
        - sleep
        - "36000"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@master ~]# kubectl get pod -owide
NAME                            READY   STATUS    RESTARTS      AGE   IP               NODE    NOMINATED NODE   READINESS GATES
busybox-89bmx                   1/1     Running   0             9s    172.16.166.186   node1   <none>           <none>
busybox-jkjl9                   1/1     Running   0             9s    172.16.104.43    node2   <none>           <none>
1
2
3
4

可以看到,我们在yaml文件中没有使用任何关于数量的参数,然后在每个节点上依然创建了pod

#删除
[root@master ~]# kubectl delete ds busybox
daemonset.apps "busybox" deleted
1
2
3

DaemonSet控制器依然受限于污点,有污点的node节点,是无法进行创建pod的.同样的,我们也可以增加nodeSelector来指定Pod部署到某些节点上

apiVersion: apps/v1             
kind: DaemonSet                 
metadata:                       
  name: busybox
spec:                        
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      #只在具有标签disktype=ssd的node节点部署
      nodeSelector:
       disktype: ssd
      containers:
      - name: busybox
        image: busybox
        imagePullPolicy: IfNotPresent
        command: 
        - sleep
        - "36000"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

4. Job

Job就是任务,通常用在批处理一些自动化脚本或者预处理一些数据,执行一次性的任务,他保证一个或者多个pod成功结束.

​ 我们使用busybox镜像来输出一个hello job字符串

apiVersion: batch/v1
kind: Job
metadata:
  name: job-hello
spec:
  template:
    metadata:
      name: job-hello
    spec:
      containers:
      - name: job-hello
        image: busybox
        command: ["echo", "hello job!"]
      restartPolicy: Never
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master ~]# kubectl get pod
NAME                READY   STATUS      RESTARTS      AGE
job-hello-64jfl     0/1     Completed   0             91s

[root@master ~]# kubectl logs job-hello-64jfl
hello job!
1
2
3
4
5
6

​ 可以看到pod完成以后,状态为Completed,查看pod日志,已经输出hello job !

1. 重启策略

Job的重启策略只支持NeverOnFailure,不支持Always,如果pod执行失败,则会收到 restartPolicy的影响

具体如下

  • Never,集群会不断的生成新的pod,但不会让它一直开下去,默认参数 spec.backoffLimit: 6 会进行阻止

  • OnFailure,集群不会生成新的pod,会不断的重启该pod.

2. 并行度

parallelism选项为并行度,可以理解为,该pod同时运行指定副本数量,我们来试一下将该选项改为5,在看一下pod的数量

apiVersion: batch/v1
kind: Job
metadata:
  name: job-hello
spec:
  # 并行度选项
  parallelism: 5
  template:
    metadata:
      name: job-hello
    spec:
      containers:
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14

执行以上文件,然后在看结果

[root@master ~]# kubectl get pod
NAME              READY   STATUS      RESTARTS   AGE
job-hello-jd8lt   0/1     Completed   0          79s
job-hello-m5d8c   0/1     Completed   0          79s
job-hello-mp4bs   0/1     Completed   0          79s
job-hello-npvvc   0/1     Completed   0          79s
job-hello-w42gv   0/1     Completed   0          79s
1
2
3
4
5
6
7

可以看到更改parallelism选项后,pod同时出现了5个副本

5. CronJob

CronJob周期性的调度执行Job,接下来我们将job章节中的yaml稍微改一下

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cronjob-hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cronjob-hello
            image: busybox
            command: ["echo","cronjob hello!"]
          restartPolicy: OnFailure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

我们执行以上yaml文件,等待几分钟来查看一下

[root@master ~]# kubectl get pod
NAME                           READY   STATUS              RESTARTS   AGE
cronjob-hello-27389276-chn4f   0/1     Completed           0          3m5s
cronjob-hello-27389277-qgbtf   0/1     Completed           0          2m5s
cronjob-hello-27389278-cvz8b   0/1     Completed           0          65s
cronjob-hello-27389279-sxrbh   0/1     ContainerCreating   0          5s
1
2
3
4
5
6

可以看到每一分钟集群都会执行一个Job

CronJob最小只支持以分钟为最小单位调度,

# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6) (周日到周一;在某些系统上,7 也是星期日)
# * * * * *
1
2
3
4
5
6

第七章 自动伸缩

​ 在Kubernetes中自动伸缩通常有三中类型

  1. CA(Cluster Autoscaler):node节点级别的弹入弹出,通常云厂商提供了该能力
  2. HPA(Horizontal Pod Autoscaler):pod个数的自动扩容与缩容.本文主要介绍该该特性
  3. VPA(Vertical Pod Autoscaler):pod配置(request和Limites限额)自动扩融与缩容,该组件目前不是很成熟,应用的比较少

Horizontal Pod Autoscaler简称HPA,是Kubernetes另一个高级特性之一,该特性能够根据某些特定指标自动对pod进行动态伸缩.HPA可以监控pod的CPU使用率,来动态伸缩DeploymentStatefulSet控制器中的pod的数量,但是无法用于DaemonSet控制器

image-20220225205535640

​ 在真实的业务场景中,我们可以将核心业务配置上HPA,以便应对高峰期的流量请求.

1. metric-server

HPA依赖于metric-server组件来获得核心指标,所以需要安装该组件,metric-server从每个节点上收集各个资源指标,每个node节点上kubelet组件已经内置了cadvisor组件

image-20220316095910878

下载地址: https://github.com/kubernetes-sigs/metrics-server/releases,下载最新版本的yaml文件

image-20220225200816047

下载yaml文件后修改以下内容

...
...
- args:
 - --cert-dir=/tmp
 - --secure-port=4443
 #1. 增加该参数,意为不用验证证书
 - --kubelet-insecure-tls
 - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
 - --kubelet-use-node-status-port
 - --metric-resolution=15s
#2. 将k8s.gcr.io修改成registry.cn-hangzhou.aliyuncs.com/google_containers
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server:v0.6.1
imagePullPolicy: IfNotPresent
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

执行该文件

[root@master ~]# kubectl apply -f metrics.yaml
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created

#可以看到metrics-server的pod正在创建
[root@master ~]# kubectl get pod -n kube-system
NAME                                       READY   STATUS              RESTARTS       AGE
calico-kube-controllers-85b5b5888d-26w6v   1/1     Running             11 (25d ago)   35d
calico-node-2n6dg                          1/1     Running             10 (45h ago)   35d
calico-node-bwf72                          1/1     Running             11 (25d ago)   35d
calico-node-m4gmc                          1/1     Running             9 (25d ago)    35d
coredns-6d8c4cb4d-4dqgc                    1/1     Running             0              45h
coredns-6d8c4cb4d-86j99                    1/1     Running             11 (25d ago)   35d
etcd-master                                1/1     Running             11 (25d ago)   35d
kube-apiserver-master                      1/1     Running             12 (25d ago)   35d
kube-controller-manager-master             1/1     Running             11 (25d ago)   35d
kube-proxy-8brfp                           1/1     Running             9 (25d ago)    35d
kube-proxy-qvqcf                           1/1     Running             11 (25d ago)   35d
kube-proxy-vx4ch                           1/1     Running             10 (45h ago)   35d
kube-scheduler-master                      1/1     Running             11 (25d ago)   35d
metrics-server-7999786998-dhv2t            0/1     ContainerCreating   0              22s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

安装成功以后,我们使用两个命令来查看资源使用率最高的node节点与pode节点

#该命令查看资源使用率最高的node节点
[root@master ~]# kubectl top node
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
master   95m          9%     1260Mi          75%
node1    46m          4%     879Mi           52%
node2    45m          4%     813Mi           48%

#该命令查看资源使用率最高的pod
[root@master ~]# kubectl top pod
NAME                            CPU(cores)   MEMORY(bytes)
nginx-deploy-66857ff745-vdw7x   0m           2Mi
1
2
3
4
5
6
7
8
9
10
11

可以通过命令kubectl get hpa来查看当前是否配有HPA

[root@master ~]# kubectl get hpa
No resources found in default namespace.
1
2

2. 示例

为了模拟自动伸缩效果,我们先部署一个Deployment控制器,副本数为1

#创建Deployment控制器
apiVersion: apps/v1
kind: Deployment
metadata:
  #指定该控制器的名称
  name: nginx
spec:
  #指定pod数量为1
  replicas: 1
  selector:
   matchLabels:
    app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      #镜像为nginx
      - image: nginx
        name: nginx
        imagePullPolicy: IfNotPresent
        resources:
         requests:
          #该值为必须
          cpu: 200m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

部署以上文件

[root@master ~]# kubectl apply -f nginx.yaml
deployment.apps/nginx created
[root@master ~]# kubectl get deploy
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           65s
1
2
3
4
5

接下来我们部署一个HPA

#创建HPA
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    #需要与刚刚部署的Deployment控制器的名称一样
    name: nginx
  #定义最少pod数量
  minReplicas: 1
  #定义最大pod数量
  maxReplicas: 3
  #当整体资源使用率超过50%则进行扩容,该指标会监视Deployment中的requests的CPU资源使用率
  targetCPUUtilizationPercentage: 50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

部署以上文件

[root@master ~]# kubectl apply -f hpa.yaml
horizontalpodautoscaler.autoscaling/nginx-hpa created

[root@master ~]# kubectl get hpa
NAME        REFERENCE          TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
nginx-hpa   Deployment/nginx   <unknown>/50%   1         3         1          31s
1
2
3
4
5
6

接下里我们进入模拟环节,我们重新打开一个终端,原有终端链接不要断开,连接master节点,进入pod内部,并执行以下命令

[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS      AGE
get-configmap            1/1     Running   2 (25d ago)   26d
nginx-54986548c9-t8k69   1/1     Running   0             5m31s
[root@master ~]# kubectl exec  -it  nginx-54986548c9-t8k69 bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
#执行以下命令
root@nginx-54986548c9-t8k69:/# cat /dev/zero > /dev/null

1
2
3
4
5
6
7
8
9

然后切换到原有终端查看pod使用率,如果没有数据,请等待一段时间

[root@master ~]# kubectl top pod
NAME                     CPU(cores)   MEMORY(bytes)
nginx-85b98978db-h9bss   688m         3Mi
1
2
3

可以看到nginx的pod的CPU使用率已经越来越高,等待一段时间以后,再次查看pod数量

[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS      AGE
nginx-54986548c9-b69ht   1/1     Running   0             46s
nginx-54986548c9-t8k69   1/1     Running   0             2m38s
nginx-54986548c9-zn9gb   1/1     Running   0             46s
1
2
3
4
5

可以看到,nginx的pod数量已经扩容到了3个,这刚刚是我们部署HPA的最大副本数.

接下来,我们将刚刚那个pod杀死

[root@master ~]# kubectl delete pod nginx-54986548c9-t8k69
pod "nginx-54986548c9-t8k69" deleted

1
2
3

等待大概5分钟左右,再次查看pod数量,发现pod数量已经开始逐渐减少

[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS      AGE
nginx-54986548c9-b69ht   1/1     Running   0             7m56s
nginx-54986548c9-zn9gb   1/1     Running   0             7m56s
[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS      AGE
get-configmap            1/1     Running   2 (25d ago)   27d
nginx-54986548c9-b69ht   1/1     Running   0             8m45s
1
2
3
4
5
6
7
8

另外一种方式,我们可以直接使用以下命令来创建HPA,具体参数可以查看kubectl autoscale --help

kubectl autoscale deploy nginx --min=1 --max=3 --cpu-percent=50 --name=nginx-hpa
1

第八章 Service

​ 前面的所有示例中,我们从来没有讨论过pod是如何被外接访问的,接下来就说一下pod被外接访问的规则.

Service简称(svc)是Kubernetes一个非常重要的概念,作用就是作为一个代理服务器将pod内部的服务发布出去.这样请求端就只需记住相应的svc服务名称即可,而无需关心背后的pod的真实IP地址是什么在哪个node节点.

​ 通常情况下,集群内部pod之间相互访问都是访问对方的svc,整体请求流程如下

image-20220308174817538

Service会将请求流量依次转发给后端的pod,这就起到了一个负载均衡的功能.所以整体来说Service起到了服务发现与负载均衡的作用

1. 查看

​ 使用命令kubectl get svc -n 命名空间来查看svc

[root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   46d
1
2
3

可以看到default命名空间下存在kubernetes的svc

查看该svc具体的详细信息

[root@master ~]# kubectl describe svc kubernetes
Name:              kubernetes
Namespace:         default
Labels:            component=apiserver
                   provider=kubernetes
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.0.1
IPs:               10.96.0.1
Port:              https  443/TCP
TargetPort:        6443/TCP
Endpoints:         192.168.137.46:6443
Session Affinity:  None
Events:            <none>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

2. 创建

​ 创建Service使用以下yaml

apiVersion: v1
kind: Service
metadata:
 name: svc-nginx
spec:
 type: NodePort
 selector:
  app: nginx
 ports:
  - protocol: TCP
    port: 80
    nodePort: 30066
    targetPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13

我首先利用这个文件来创建一个svc

[root@master ~]# kubectl apply -f nginx-service.yaml
service/svc-nginx created
[root@master ~]# kubectl get svc -owide
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        46d     <none>
svc-nginx    NodePort    10.110.327.150   <none>        80:30066/TCP   4s      app=nginx
[root@master ~]#
1
2
3
4
5
6
7

可以看到svc创建成功了,所以svc的创建并没有依赖任何控制器或者pod.接下来,我们在创建一个nginxpod

使用以下文件

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx-service-demo
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: nginx-service-demo
    ports:
    - containerPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@master ~]# kubectl apply -f nginx-service-pod.yaml
pod/nginx-service-demo created
[root@master ~]# kubectl get pod
NAME                 READY   STATUS    RESTARTS   AGE
nginx-service-demo   1/1     Running   0          3s
[root@master ~]#
1
2
3
4
5
6

接下来我们进入nginx-service-demo这个pod,更改index.html的内容为from server

[root@master ~]# kubectl exec -it nginx-service-demo bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-service-demo:/# cat > usr/share/nginx/html/index.html << EOF
from server
EOF
root@nginx-service-demo:/# cat usr/share/nginx/html/index.html
from server
1
2
3
4
5
6
7

然后我们在临时创建一个nginx pod,名为nginx-client

[root@master ~]#  kubectl run nginx-client --image=nginx      --image-pull-policy=IfNotPresent
pod/nginx-client created
[root@master ~]# kubectl get pod
NAME                 READY   STATUS    RESTARTS   AGE
nginx-client         1/1     Running   0          10s
nginx-service-demo   1/1     Running   0          19m
1
2
3
4
5
6

可以看到现在存在两个nginx的pod,一个nginx-client,一个nginx-service-demo,现在集群的内部如下:

image-20220308210608315

现在我们进入到nginx-client内部,使用命令curl svc-nginx

[root@master ~]# kubectl exec -it nginx-client bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-client:/# curl svc-nginx
from server
root@nginx-client:/#
1
2
3
4
5

​ 大家发现什么了吗?该命令返回了from server,正式我更改的nginx-service-demoindex.html文件!

然后我们在创建一个nginx-service-demo2的pod

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
  name: nginx-service-demo2
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: nginx-service-demo2
    ports:
    - containerPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13

现在的集群内部情况如下

[root@master ~]# kubectl get pod -owide
NAME                  READY   STATUS    RESTARTS   AGE    IP               NODE    NOMINATED NODE   READINESS GATES
nginx-client          1/1     Running   0          168m   172.16.104.20    node2   <none>           <none>
nginx-service-demo    1/1     Running   0          3h6m   172.16.104.9     node2   <none>           <none>
nginx-service-demo2   1/1     Running   0          1s     172.16.166.150   node1   <none>           <none>
1
2
3
4
5

进入到nginx-service-demo2内部,将index.html内容更改成了from server2

[root@master ~]# kubectl exec -it pod/nginx-service-demo2 bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-service-demo2:/# cat > usr/share/nginx/html/index.html << EOF
from server2
EOF
root@nginx-service-demo2:/# cat usr/share/nginx/html/index.html
from server2
1
2
3
4
5
6
7

现在集群内容情况如下

image-20220308211533372

我们回到client内部,在多次使用curl svc-nginx看一下效果

[root@master ~]# kubectl exec -it nginx-client bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-client:/# curl svc-nginx
from server
root@nginx-client:/# curl svc-nginx
from server2
root@nginx-client:/#
1
2
3
4
5
6
7

可以看到,第一次返回from server,第二次返回from server2.这就是svc的负载均衡与服务发现的能力.那么svc如何具有服务发现的能力呢?

svc通过节点selector属性来绑定具有相同标签的pod,也就是绑定具有app: nginx标签的pod

#svc
...
...
 selector:
  app: nginx
...
...
#pod
...
...
metadata:
  labels:
    app: nginx
...
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

2. type

svc的类型一种有三种类型

1. NodePort

...
...
spec:
 type: NodePort
...
...
1
2
3
4
5
6

该类型的svc会在每个node节点都开上都开启nodePort端口号,访问集群中任意的节点该端口号,都可以访问该服务

[root@master ~]# kubectl get pod -o wide
NAME                  READY   STATUS    RESTARTS   AGE    IP               NODE    NOMINATED NODE   READINESS GATES
nginx-client          1/1     Running   0          145m   172.16.104.20    node2   <none>           <none>
nginx-service-demo    1/1     Running   0          164m   172.16.104.9     node2   <none>           <none>
nginx-service-demo2   1/1     Running   0          126m   172.16.166.150   node1   <none>           <none>
#我们先将nginx-service-demo2这个pod删除
[root@master ~]# kubectl delete -f nginx-service-demo2.yaml
pod "nginx-service-demo2" deleted
#可以看到现在只有一个nginx-service-demo的pod了
[root@master ~]# kubectl get pod -owide
NAME                 READY   STATUS    RESTARTS   AGE    IP              NODE    NOMINATED NODE   READINESS GATES
nginx-client         1/1     Running   0          148m   172.16.104.20   node2   <none>           <none>
nginx-service-demo   1/1     Running   0          167m   172.16.104.9    node2   <none>           <none>
1
2
3
4
5
6
7
8
9
10
11
12
13

然后我们通过浏览器访问http://node1:30066http://node2:30066看一下效果

image-20220308232218770

image-20220308232230201

可以看到都能访问到这个pod

2. ClusterIP

​ 默认值,是Kubernetes集群给给Service的自动分配的虚拟IP,该IP只能在集群内部访问.这个虚拟IP(VIP)通常情况下是没有办法ping通的,因为这个VIP不是一个真实的实体网络来响应

​ 通常情况我们不会直接使用这个VIP地址,而是使用对方ServiceName服务名称请求访问.比如下图

image-20220310213012570

3. LoadBalancer

​ 该类型的svc通常是一些云服务厂商会提供该接口,会为该类型的svc提供一个公网的IP.从来将我们的服务公布在互联网上.如下图是我司的Kong网关,为EXTERNAL-IP提供了一个公网IP.

image-20220304172819324

​ 通常我们不会直接使用该公网IP,而是会在网关前面加上一个SLB即负载均衡器,比如下图

image-20220308233219611

3. ports

​ 在yaml文件中,我们看到ports节点下有三个端口

 ...
 ...
 ports:
  - protocol: TCP
    port: 80
    nodePort: 30066
    targetPort: 80
1
2
3
4
5
6
7

那么这三个端口都是用来做什么的呢?

  1. targetPort:就是后端容器的端口,比如我们nginx使用的就是80端口
  2. nodePort:就是这个pod所在node节点上的端口,端口范围:30000-32767,如果不指定该参数,则会随机使用一个端口
  3. port:集群中服务互相访问的端口.

如下图

image-20220308213137590

集群内部svc相互访问受限于namespace,处于相同namespace的服务可以直接使用svc进行通讯,不同namespace的服务,需要指定namespace.格式如下:

svc-nginx.default

也就是在svc名称后面增加一个**.namespace**;

5. Ingress Controller

IngressKubernetes的一个对象,通过该对象,我们可以实现访问集群内部的统一入口Ingress具有反向代理和负载均能的能力,可以简单的比喻为就像是nginx的反向代理

​ 请求示例

image-20220310214141556

​ 目前Kubernetes支持和维护AWSopen in new windowGCEopen in new windowNginx Ingressopen in new window 控制器

1. 安装

​ 我们在集群中安装Nginx Inregss,可以在官方网站中,寻找yaml文件:Installation Guide - NGINX Ingress Controller (kubernetes.github.io)open in new window,在githubkubernetes/ingress-nginx: NGINX Ingress Controller for Kubernetes (github.com)open in new window,查看各版本与Kubernetes版本兼容的情况

#查看集群版本
[root@master ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.3", GitCommit:"816c97ab8cff8a1c72eccca1026f7820e93e0d25", GitTreeState:"clean", BuildDate:"2022-01-25T21:25:17Z", GoVersion:"go1.17.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:34:54Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
1
2
3
4

​ 因为部署文件中包含镜像源k8s.gcr.io国内无法访问,所以我们需要将部署文件保存到本地,然后替换镜像源

k8s.gcr.io替换成registry.aliyuncs.com/google_containers

第九章 监控

​ 在早期的Kubernetes(v1.11以前)的版本内置了heapster组件,不过该组件已经被废弃,内置监控则由metics-server负责.

image-20220316112355955

​ 在第七章 自动伸缩章节中,我们在集群中部署了一个metics-server组件,metics-server负责去kubelet上抽取数据.metics-server也有局限性,仅提供NodePodCPU和内存使用情况,其他的Custom Metrics(自定义指标),则由社区与其他组件来完成.

1. Prometheus

image-20220316133301377

Prometheus是开源的监控与度量组件,2016年加入了CNCF云原生计算基金会,2018年进入毕业状态.以下是官方给出的架构图

image-20220316202844119

​ 为了研发同学便于理解,用一句话概括就是Prometheus内置了一个时序数据库,targets为被监控的目标,Prometheus会定时的去targets拉取数据,然后存储到内部.然后我们通过Grafana进行数据可视化,自定义监控指标,当达标阈值后,通过Alertmanager进行报警.

2. 安装

​ 在Kubernetes集群部署Prometheus有两种方案,prometheus-operatorkube-prometheus

prometheus-operator提供Prometheus和相关监控组件的 Kubernetesopen in new window 原生部署和管理。该项目的目的是简化和自动化基于 Prometheus的Kubernetes集群的监控部署

kube-prometheus提供了一个完成整的集群监控示例与配置,并且内置了针对集群的alert与record rule,内置了grafana看板.可一键部署Prometheus的所有组件.接下来我们就使用kube-prometheus来在集群中部署Prometheus.

kube-prometheus版本兼容性

image-20220316204642440

**1. ** 克隆kube-prometheus

[root@master ~]# git clone https://github.com/prometheus-operator/kube-prometheus.git
Cloning into 'kube-prometheus'...
remote: Enumerating objects: 15559, done.
remote: Counting objects: 100% (245/245), done.
remote: Compressing objects: 100% (124/124), done.
remote: Total 15559 (delta 157), reused 159 (delta 108), pack-reused 15314R
Receiving objects: 100% (15559/15559), 7.79 MiB | 2.30 MiB/s, done.
Resolving deltas: 100% (9915/9915), done.
1
2
3
4
5
6
7
8

2. ** 进入到manifests**目录,所有安装文件都在该目录下

├── manifests
│   ├── alertmanager-alertmanager.yaml
│   ├── alertmanager-podDisruptionBudget.yaml
│   ├── alertmanager-prometheusRule.yaml
│   ├── alertmanager-secret.yaml
│   ├── alertmanager-serviceAccount.yaml
│   ├── alertmanager-serviceMonitor.yaml
│   ├── alertmanager-service.yaml
│   ├── blackboxExporter-clusterRoleBinding.yaml
│   ├── blackboxExporter-clusterRole.yaml
│   ├── blackboxExporter-configuration.yaml
│   ├── blackboxExporter-deployment.yaml
│   ├── blackboxExporter-serviceAccount.yaml
│   ├── blackboxExporter-serviceMonitor.yaml
│   ├── blackboxExporter-service.yaml
│   ├── grafana-config.yaml
│   ├── grafana-dashboardDatasources.yaml
│   ├── grafana-dashboardDefinitions.yaml
│   ├── grafana-dashboardSources.yaml
│   ├── grafana-deployment.yaml
│   ├── grafana-prometheusRule.yaml
│   ├── grafana-serviceAccount.yaml
│   ├── grafana-serviceMonitor.yaml
│   ├── grafana-service.yaml
│   ├── kubePrometheus-prometheusRule.yaml
│   ├── kubernetesControlPlane-prometheusRule.yaml
│   ├── kubernetesControlPlane-serviceMonitorApiserver.yaml
│   ├── kubernetesControlPlane-serviceMonitorCoreDNS.yaml
│   ├── kubernetesControlPlane-serviceMonitorKubeControllerManager.yaml
│   ├── kubernetesControlPlane-serviceMonitorKubelet.yaml
│   ├── kubernetesControlPlane-serviceMonitorKubeScheduler.yaml
│   ├── kubeStateMetrics-clusterRoleBinding.yaml
│   ├── kubeStateMetrics-clusterRole.yaml
│   ├── kubeStateMetrics-deployment.yaml
│   ├── kubeStateMetrics-prometheusRule.yaml
│   ├── kubeStateMetrics-serviceAccount.yaml
│   ├── kubeStateMetrics-serviceMonitor.yaml
│   ├── kubeStateMetrics-service.yaml
│   ├── nodeExporter-clusterRoleBinding.yaml
│   ├── nodeExporter-clusterRole.yaml
│   ├── nodeExporter-daemonset.yaml
│   ├── nodeExporter-prometheusRule.yaml
│   ├── nodeExporter-serviceAccount.yaml
│   ├── nodeExporter-serviceMonitor.yaml
│   ├── nodeExporter-service.yaml
│   ├── prometheusAdapter-apiService.yaml
│   ├── prometheusAdapter-clusterRoleAggregatedMetricsReader.yaml
│   ├── prometheusAdapter-clusterRoleBindingDelegator.yaml
│   ├── prometheusAdapter-clusterRoleBinding.yaml
│   ├── prometheusAdapter-clusterRoleServerResources.yaml
│   ├── prometheusAdapter-clusterRole.yaml
│   ├── prometheusAdapter-configMap.yaml
│   ├── prometheusAdapter-deployment.yaml
│   ├── prometheusAdapter-podDisruptionBudget.yaml
│   ├── prometheusAdapter-roleBindingAuthReader.yaml
│   ├── prometheusAdapter-serviceAccount.yaml
│   ├── prometheusAdapter-serviceMonitor.yaml
│   ├── prometheusAdapter-service.yaml
│   ├── prometheus-clusterRoleBinding.yaml
│   ├── prometheus-clusterRole.yaml
│   ├── prometheusOperator-clusterRoleBinding.yaml
│   ├── prometheusOperator-clusterRole.yaml
│   ├── prometheusOperator-deployment.yaml
│   ├── prometheusOperator-prometheusRule.yaml
│   ├── prometheusOperator-serviceAccount.yaml
│   ├── prometheusOperator-serviceMonitor.yaml
│   ├── prometheusOperator-service.yaml
│   ├── prometheus-podDisruptionBudget.yaml
│   ├── prometheus-prometheusRule.yaml
│   ├── prometheus-prometheus.yaml
│   ├── prometheus-roleBindingConfig.yaml
│   ├── prometheus-roleBindingSpecificNamespaces.yaml
│   ├── prometheus-roleConfig.yaml
│   ├── prometheus-roleSpecificNamespaces.yaml
│   ├── prometheus-serviceAccount.yaml
│   ├── prometheus-serviceMonitor.yaml
│   ├── prometheus-service.yaml
│   └── setup
│       ├── 0alertmanagerConfigCustomResourceDefinition.yaml
│       ├── 0alertmanagerCustomResourceDefinition.yaml
│       ├── 0podmonitorCustomResourceDefinition.yaml
│       ├── 0probeCustomResourceDefinition.yaml
│       ├── 0prometheusCustomResourceDefinition.yaml
│       ├── 0prometheusruleCustomResourceDefinition.yaml
│       ├── 0servicemonitorCustomResourceDefinition.yaml
│       ├── 0thanosrulerCustomResourceDefinition.yaml
│       └── namespace.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

3. 首选创建命名空间

[root@master kube-prometheus-main]# kubectl apply -f manifests/setup/namespace.yaml
namespace/monitoring created
1
2

4. 部署安装文件

[root@master kube-prometheus-main]# kubectl apply --server-side -f manifests/setup
customresourcedefinition.apiextensions.k8s.io/alertmanagerconfigs.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/alertmanagers.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/podmonitors.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/probes.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/prometheuses.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/prometheusrules.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/servicemonitors.monitoring.coreos.com serverside-applied
customresourcedefinition.apiextensions.k8s.io/thanosrulers.monitoring.coreos.com serverside-applied
namespace/monitoring serverside-applied
1
2
3
4
5
6
7
8
9
10

5. 因为国内无法直接访问镜像源k8s.gcr.io,在每个node节点上需要手动拉取相关镜像,并会用tag进行更改标签

​ 检查mainfests目录下的两个deployment文件:kubeStateMetrics-deployment.yamlprometheusAdapter-deployment.yaml的相关镜像版本号与镜像拉取策略是否为imagePullPolicy: IfNotPresent

通常来说以下三个镜像需要懂手动从DockerHub进行拉取,如果有其他镜像下载不了的,也可以使用此方法

docker pull selina5288/prometheus-adapter:v0.9.1
docker image tag selina5288/prometheus-adapter:v0.9.1 k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1
docker rmi selina5288/prometheus-adapter:v0.9.1
1
2
3
docker pull bitnami/kube-state-metrics:2.4.2
docker image tag bitnami/kube-state-metrics:2.4.2 k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.4.2
docker rmi bitnami/kube-state-metrics:2.4.2
1
2
3
docker pull serialt/prometheus-config-reloader:v0.55.0
docker image tag serialt/prometheus-config-reloader:v0.55.0  quay.io/prometheus-operator/prometheus-config-reloader:v0.55.0
docker rmi serialt/prometheus-config-reloader:v0.55.0
1
2
3

然后执行部署命令

[root@master kube-prometheus-main]# kubectl apply -f manifests/
1

6. 暴漏服务

​ 可以看到所有的svc均为ClusterIP,这样我们没办法访问,所以我将三个核心服务的dashboard暴漏相关端口

[root@master ~]# kubectl get svc -n monitoring
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
alertmanager-main       ClusterIP   10.103.80.87     <none>        9093/TCP,8080/TCP            21h
alertmanager-operated   ClusterIP   None             <none>        9093/TCP,9094/TCP,9094/UDP   21h
blackbox-exporter       ClusterIP   10.105.194.181   <none>        9115/TCP,19115/TCP           21h
grafana                 ClusterIP   10.104.135.57    <none>        3000/TCP                     21h
kube-state-metrics      ClusterIP   None             <none>        8443/TCP,9443/TCP            21h
node-exporter           ClusterIP   None             <none>        9100/TCP                     21h
prometheus-adapter      ClusterIP   10.110.154.125   <none>        443/TCP                      21h
prometheus-k8s          ClusterIP   10.111.81.199    <none>        9090/TCP,8080/TCP            21h
prometheus-operated     ClusterIP   None             <none>        9090/TCP                     21h
prometheus-operator     ClusterIP   None             <none>        8443/TCP                     21h
1
2
3
4
5
6
7
8
9
10
11
12

使用kubectl edit svc命令修改三个svc,将类型更改为NodePort

  1. kubectl edit svc/prometheus-k8s -n monitoring
  2. kubectl edit svc/grafana -n monitoring
  3. kubectl edit svc/alertmanager-main -n monitoring
[root@master ~]# kubectl get svc -n monitoring | grep NodePort
alertmanager-main       NodePort    10.103.80.87     <none>        9093:32603/TCP,8080:31578/TCP   21h
grafana                 NodePort    10.104.135.57    <none>        3000:31849/TCP                  21h
prometheus-k8s          NodePort    10.111.81.199    <none>        9090:30211/TCP,8080:30359/TCP   21h
1
2
3
4

访问node节点的相关端口,可以看到Prometheus面板已经成功访问

image-20220317204215416

我们直接使用命令来查看一下每个pod的cpu使用情况

sum(rate(container_cpu_usage_seconds_total{image!="", pod!=""}[1m] )) by (pod)
1

image-20220317204528221

接下来,我们使用使用Grafana来渲染监控数据,访问端口http://node1:31849

先测试一下Data Source是否成功

image-20220317205307616

然后查看相关的看板

image-20220317205444949

我们看一下Kubernetes / Compute Resources / Pod看板

image-20220317205622971

其他看板各位同学可以自行研究.

3. 卸载

​ 如果想要全部卸载,使用命令

kubectl delete --ignore-not-found=true -f manifests/ -f manifests/setup
1

第十章 可视化管理

1. Kubernetes Dashboard

Kubernetes Dashboard是Kubernetes集群的Web UI,用户可以通过Dashboard进行管理集群内所有资源对象,例如查看资源对象的运行情况,部署新的资源对象,伸缩Deployment中的Pod数量等等一系列操作。官方库:https://github.com/kubernetes/dashboard

可以直接使用以下命令进行安装

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.1/aio/deploy/recommended.yaml
1

如果出现解析域名失败,也可以下载文件后,使用基于本地文件安装,dashboard部署在命名空间kubernetes-dashboard

[root@master ~]# kubectl apply -f dashboard.yaml
namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created
[root@master ~]# kubectl get pod -n kubernetes-dashboard
NAME                                         READY   STATUS    RESTARTS   AGE
dashboard-metrics-scraper-799d786dbf-z4bwl   1/1     Running   0          14m
kubernetes-dashboard-fb8648fd9-thvtj         1/1     Running   0          14m
[root@master ~]# kubectl get svc -n kubernetes-dashboard
NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
dashboard-metrics-scraper   ClusterIP   10.96.39.194    <none>        8000/TCP   21m
kubernetes-dashboard        ClusterIP   10.103.29.152   <none>        443/TCP    21m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

可以看到kubernetes-dashboardsvc使用的类型是ClusterIP,所以我们无法通过外部访问,所以我们需要编辑该svc

[root@master ~]# kubectl edit service kubernetes-dashboard -n kubernetes-dashboard
# 定位到33行,将ClusterIP更改成NodePort
     30   selector:
     31     k8s-app: kubernetes-dashboard
     32   sessionAffinity: None
     33   type: NodePort
     34 status:
     35   loadBalancer: {}
1
2
3
4
5
6
7
8

创建Service AccountClusterRoleBinding

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master ~]# kubectl apply -f sa.yaml
serviceaccount/admin-user created
clusterrolebinding.rbac.authorization.k8s.io/admin-user created
1
2
3

使用以下命令生成访问集群的token

[root@master ~]# kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')
Name:         admin-user-token-dmlhh
Namespace:    kubernetes-dashboard
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: admin-user
              kubernetes.io/service-account.uid: 9a2bdbff-bbf1-44ba-abd1-acd270010f65

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1099 bytes
namespace:  20 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IjQ0bjlCSDZiOGM0ejR3akpoZDlkV0pZZU5iRWtQY0RkVnB5b3ctRnVrdzAifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWRtbGhoIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI5YTJiZGJmZi1iYmYxLTQ0YmEtYWJkMS1hY2QyNzAwMTBmNjUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.GpFdmNAJOnm5nS3ZDZ8Cjprv8BAATR4_MAMxu0vLqyb-TGnwHZGkFRaOX4EgTP9U_hkjkZ4MtcA51qFaMlC18okOZBPFA58dcWmQ47hdPQuQuNpS2cKQ7E_5It7B4tkTMiu0K4gbCyZI0U82IpHgggAD6nP3ZM0xC7AZ93hZtKGSN5-Anq9l6or-DCYW5a6N6cBjoDEkTh-pAveoydp0vTJX_IbUuh7AIdW29gxJiDke3t11_GPZYLEfiqwnSFk5W_t4Tqqns7r7d2NKk6MxcFswQp2IFCu8lXgXIdDLXtl5Fwjmho_NcBs0hUNCgzdfwuMCIDRtL-pwFcU-xD6daA
1
2
3
4
5
6
7
8
9
10
11
12
13
14

访问https://node1:31811/#/login,这里需要使用https放问,并把上面生成的token输入

image-20220313233221795

这时可以看到整个集群的状况

image-20220313233259131

2. Rancher

image-20220314194903774

Rancher是笔者比较喜欢的一款用来管理Kubernetes的工具,官网https://www.rancher.cn/products.官方对Rancher的定义

提供Kubernetes即服务(Kubernetes-as-a-Service)

Rancher开源,且有两个版本,社区办与企业版,中小团队使用社区办已经足够.

Rancher安装也非常简单

[root@cd ~]# docker run -d --restart=always  --privileged -p 80:80 -p 443:443 rancher/rancher
1
Last Updated:
Contributors: 刘岩