k8s 的介绍

1 应用部署方式的演变

1.1 应用部署方式的演变

  • 在部署应用程序的方式上,主要经历了三个时代:
  • ① 传统部署:
    • 互联网早期,会直接将应用部署在物理机上。
    • 优点:简单,不需要其他的技术参与。
    • 缺点:不能为应用程序定义资源的使用边界,很难合理的分配计算机资源,而且程序之间容易产生影响。
  • ② 虚拟化部署:
    • 可以在一台物理机上运行多个虚拟机,每个虚拟机都是独立的一个环境。
    • 优点:程序环境不会相互产生影响,提供了一定程序上的安全性。
    • 缺点:增加了操作系统,浪费了部分资源。
  • ③ 容器化部署:

    • 和虚拟化类似,但是共享了操作系统。
    • 优点:
      • ① 可以保证每个容器拥有自己的文件系统、CPU 、内存和进程空间等。
      • ② 运行应用程序所需要的资源都被容器包装,并和底层基础架构解耦。
      • ③ 容器化的应用程序可以跨云服务商、跨 Linux 操作系统发行版进行部署。

        1.2 容器化部署方式产生的问题及解决方案

  • 容器化部署方式带来了很多的便利,但是也会带来一些问题,比如:

    • 一旦容器故障停机了,怎么让另外一个容器立刻启动去替补停机的容器。
    • 当并发访问量变大的时候,怎么做到横向扩展容器数量。
    • ……
  • 这些容器管理的问题统称为 容器编排问题 ,为了解决这些容器编排问题,就产生了一些容器编排的软件:

    • Swarm:Docker 自己的容器编排工具。
    • Mesos:Apache 的一个资源统一管控的工具,需要和 Marathon 结合。
    • Kubernetes:Google 开源的容器编排工具。

      2 kubernetes 简介

  • Kubernetes,是一个全新的基于容器技术的分布式架构领先方案,是 Google 严格保密十几年的秘密武器— Borg 系统的一个开源版本,于 2014 年 9 月发布第一个版本,2015 年 7 月发布第一个正式版本。

  • Kubernetes 的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。它的目的就是实现资源管理的自动化,主要提供了如下的功能:

    • 自我修复:一旦某一个容器崩溃,能够在1秒左右迅速启动新的容器。
    • 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整。
    • 服务发现:服务可以通过自动发现的形式找到它所依赖的服务。
    • 负载均衡:如果一个服务启动了多个容器,能够自动实现请求的负载均衡。
    • 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本。
    • 存储编排:可以根据容器自身的需求自动创建存储卷。
    • ……

      3 kubernetes 组件

      3.1 Kubernetes 组件介绍

  • 一个 kubernetes 集群主要由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。

  • 控制节点(master):集群的控制平面,负责集群的决策。
    • API Server:集群操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制。
    • Scheduler:负责集群资源调度,按照预定的调度策略将 Pod 调度到相应的 node 节点上。
    • ControllerManager:负责维护集群的状态,比如程序部署安排、故障检测、自动扩展和滚动更新等。
    • Etcd:负责存储集群中各种资源对象的信息。
  • 工作节点(node):集群的数据平面,负责为容器提供运行环境。
    • Kubelet:负责维护容器的生命周期,即通过控制 Docker ,来创建、更新、销毁容器。
    • KubeProxy:负责提供集群内部的服务发现和负载均衡。
    • Docker:负责节点上容器的各种操作。

云原生-第一部分 - 图3

3.2 kubernetes 组件调用关系的应用示例

  • 以部署一个 Nginx 服务来说明 Kubernetes 系统各个组件调用关系:
  • ① 首先需要明确,一旦 Kubernetes 环境启动之后,master 和 node 都会将自身的信息存储到etcd数据库中。
  • ② 一个Nginx服务的安装请求首先会被发送到 master 节点上的 API Server 组件。
  • ③ API Server 组件会调用 Scheduler 组件来决定到底应该把这个服务安装到那个 node 节点上。此时,它会从 etcd 中读取各个 node 节点的信息,然后按照一定的算法进行选择,并将结果告知 API Server 。
  • ④ API Server 调用 Controller-Manager 去调用 Node 节点安装 Nginx 服务。
  • ⑤ Kubelet 接收到指令后,会通知 Docker ,然后由 Docker 来启动一个 Nginx 的 Pod 。Pod 是 Kubernetes 的最小操作单元,容器必须跑在 Pod 中。
  • ⑥ 一个 Nginx 服务就运行了,如果需要访问 Nginx ,就需要通过 kube-proxy 来对 Pod 产生访问的代理,这样,外界用户就可以访问集群中的 Nginx 服务了。

    4 kubernetes 概念

  • Master:集群控制节点,每个集群要求至少有一个 Master 节点来负责集群的管控。

  • Node:工作负载节点,由 Master 分配容器到这些 Node 工作节点上,然后 Node 节点上的 Docker 负责容器的运行。
  • Pod:Kubernetes 的最小控制单元,容器都是运行在 Pod 中的,一个 Pod 中可以有一个或多个容器。
  • Controller:控制器,通过它来实现对 Pod 的管理,比如启动 Pod 、停止 Pod 、伸缩 Pod 的数量等等。
  • Service:Pod 对外服务的统一入口,其下面可以维护同一类的多个 Pod 。
  • Label:标签,用于对 Pod 进行分类,同一类 Pod 会拥有相同的标签。
  • NameSpace:命名空间,用来隔离 Pod 的运行环境。

    2.k8s集群环境的搭建

    1 环境规划

    1.1 集群类型

  • Kubernetes集群大致分为两类:一主多从和多主多从。

  • 一主多从:一个Master节点和多台Node节点,搭建简单,但是有单机故障风险,适合用于测试环境。
  • 多主多从:多台Master和多台Node节点,搭建麻烦,安全性高,适合用于生产环境。

云原生-第一部分 - 图4
为了测试方便,本次搭建的是一主多从类型的集群。

1.2 安装方式

  • kubernetes有多种部署方式,目前主流的方式有kubeadm、minikube、二进制包。
  • ① minikube:一个用于快速搭建单节点的kubernetes工具。
  • ② kubeadm:一个用于快速搭建kubernetes集群的工具。
  • ③ 二进制包:从官网上下载每个组件的二进制包,依次去安装,此方式对于理解kubernetes组件更加有效。
  • 我们需要安装kubernetes的集群环境,但是又不想过于麻烦,所以选择kubeadm方式。

    1.3 主机规划

    | 角色 | IP地址 | 操作系统 | 配置 | | —- | —- | —- | —- | | Master | 192.168.18.100 | CentOS7.8+,基础设施服务器 | 2核CPU,2G内存,50G硬盘 | | Node1 | 192.168.18.101 | CentOS7.8+,基础设施服务器 | 2核CPU,2G内存,50G硬盘 | | Node2 | 192.168.18.102 | CentOS7.8+,基础设施服务器 | 2核CPU,2G内存,50G硬盘 |

2 环境搭建

2.1 前言

  • 本次环境搭建需要三台CentOS服务器(一主二从),然后在每台服务器中分别安装Docker(18.06.3)、kubeadm(1.18.0)、kubectl(1.18.0)和kubelet(1.18.0)。

没有特殊说明,就是三台机器都需要执行。

2.2 环境初始化

2.2.1 检查操作系统的版本

  • 检查操作系统的版本(要求操作系统的版本至少在7.5以上):

cat /etc/redhat-release
云原生-第一部分 - 图5

2.2.2 关闭防火墙和禁止防火墙开机启动

  • 关闭防火墙:

    1. systemctl stop firewalld
  • 禁止防火墙开机启动:

    1. systemctl disable firewalld

    云原生-第一部分 - 图6

    2.2.3 设置主机名

  • 设置主机名:

hostnamectl set-hostname

  • 设置192.168.18.100的主机名:

hostnamectl set-hostname k8s-master

  • 设置192.168.18.101的主机名:

hostnamectl set-hostname k8s-node1

  • 设置192.168.18.102的主机名:

hostnamectl set-hostname k8s-node2

2.2.4 主机名解析

  • 为了方便后面集群节点间的直接调用,需要配置一下主机名解析,企业中推荐使用内部的DNS服务器。

cat >> /etc/hosts << EOF
192.168.18.100 k8s-master
192.168.18.101 k8s-node1
192.168.18.102 k8s-node2
EOF

2.2.5 时间同步

  • kubernetes要求集群中的节点时间必须精确一致,所以在每个节点上添加时间同步:

yum install ntpdate -y
ntpdate time.windows.com

2.2.6 关闭selinux

  • 查看selinux是否开启:

getenforce

  • 永久关闭selinux,需要重启:

sed -i ‘s/enforcing/disabled/‘ /etc/selinux/config

  • 临时关闭selinux,重启之后,无效:

setenforce 0

2.2.7 关闭swap分区

  • 永久关闭swap分区,需要重启:

sed -ri ‘s/.swap./#&/‘ /etc/fstab

  • 临时关闭swap分区,重启之后,无效::

swapoff -a

2.2.8 将桥接的IPv4流量传递到iptables的链

  • 在每个节点上将桥接的IPv4流量传递到iptables的链:

    1. cat > /etc/sysctl.d/k8s.conf << EOF
    2. net.bridge.bridge-nf-call-ip6tables = 1
    3. net.bridge.bridge-nf-call-iptables = 1
    4. net.ipv4.ip_forward = 1
    5. vm.swappiness = 0
    6. EOF
    7. # 加载br_netfilter模块
    8. modprobe br_netfilter
    9. # 查看是否加载
    10. lsmod | grep br_netfilter
    11. # 生效
    12. sysctl --system

    2.2.9 开启ipvs

  • 在kubernetes中service有两种代理模型,一种是基于iptables,另一种是基于ipvs的。ipvs的性能要高于iptables的,但是如果要使用它,需要手动载入ipvs模块。

  • 在每个节点安装ipset和ipvsadm:

    1. yum -y install ipset ipvsadm
  • 在所有节点执行如下脚本:

    1. cat > /etc/sysconfig/modules/ipvs.modules <<EOF
    2. #!/bin/bash
    3. modprobe -- ip_vs
    4. modprobe -- ip_vs_rr
    5. modprobe -- ip_vs_wrr
    6. modprobe -- ip_vs_sh
    7. modprobe -- nf_conntrack_ipv4
    8. EOF
  • 授权、运行、检查是否加载:

    1. chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
  • 检查是否加载:

    1. lsmod | grep -e ipvs -e nf_conntrack_ipv4

    2.2.10 重启三台机器

  • 重启三台Linux机器:

reboot

2.3 每个节点安装Docker、kubeadm、kubelet和kubectl

2.3.1 安装Docker

  • 安装Docker:

    1. wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
    2. yum -y install docker-ce-18.06.3.ce-3.el7
    3. systemctl enable docker && systemctl start docker
    4. docker version
  • 设置Docker镜像加速器:

    1. sudo mkdir -p /etc/docker
    2. sudo tee /etc/docker/daemon.json <<-'EOF'
    3. {
    4. "exec-opts": ["native.cgroupdriver=systemd"],
    5. "registry-mirrors": ["https://du3ia00u.mirror.aliyuncs.com"],
    6. "live-restore": true,
    7. "log-driver":"json-file",
    8. "log-opts": {"max-size":"500m", "max-file":"3"},
    9. "storage-driver": "overlay2"
    10. }
    11. EOF
    12. sudo systemctl daemon-reload
    13. sudo systemctl restart docker

    2.3.2 添加阿里云的YUM软件源

  • 由于kubernetes的镜像源在国外,非常慢,这里切换成国内的阿里云镜像源:

    1. cat > /etc/yum.repos.d/kubernetes.repo << EOF
    2. [kubernetes]
    3. name=Kubernetes
    4. baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
    5. enabled=1
    6. gpgcheck=0
    7. repo_gpgcheck=0
    8. gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
    9. EOF

    2.3.3 安装kubeadm、kubelet和kubectl

  • 由于版本更新频繁,这里指定版本号部署:

    1. yum install -y kubelet-1.18.0 kubeadm-1.18.0 kubectl-1.18.0
  • 为了实现Docker使用的cgroup drvier和kubelet使用的cgroup drver一致,建议修改”/etc/sysconfig/kubelet”文件的内容:

    1. vim /etc/sysconfig/kubelet
    2. # 修改
    3. KUBELET_EXTRA_ARGS="--cgroup-driver=systemd"
    4. KUBE_PROXY_MODE="ipvs"
  • 设置为开机自启动即可,由于没有生成配置文件,集群初始化后自动启动:

    1. systemctl enable kubelet

    2.4 查看k8s所需镜像

  • 查看k8s所需镜像:

kubeadm config images list
云原生-第一部分 - 图7

2.5 部署k8s的Master节点

  • 部署k8s的Master节点(192.168.18.100):

    1. # 由于默认拉取镜像地址k8s.gcr.io国内无法访问,这里需要指定阿里云镜像仓库地址
    2. kubeadm init \
    3. --apiserver-advertise-address=192.168.18.100 \
    4. --image-repository registry.aliyuncs.com/google_containers \
    5. --kubernetes-version v1.18.0 \
    6. --service-cidr=10.96.0.0/12 \
    7. --pod-network-cidr=10.244.0.0/16

    云原生-第一部分 - 图8
    云原生-第一部分 - 图9

  • 根据提示消息,在Master节点上使用kubectl工具:

    1. mkdir -p $HOME/.kube
    2. sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    3. sudo chown $(id -u):$(id -g) $HOME/.kube/config

    2.6 部署k8s的Node节点

  • 根据提示,在192.168.18.101和192.168.18.102上添加如下的命令:

    1. kubeadm join 192.168.18.100:6443 --token jv039y.bh8yetcpo6zeqfyj \
    2. --discovery-token-ca-cert-hash sha256:3c81e535fd4f8ff1752617d7a2d56c3b23779cf9545e530828c0ff6b507e0e26

    云原生-第一部分 - 图10

  • 默认的token有效期为24小时,当过期之后,该token就不能用了,这时可以使用如下的命令创建token:

    1. kubeadm token create --print-join-command
    2. # 生成一个永不过期的token
    3. kubeadm token create --ttl 0 --print-join-command

    2.7 部署CNI网络插件

  • 根据提示,在Master节点上使用kubectl工具查看节点状态:

kubectl get nodes
云原生-第一部分 - 图11

  • kubernetes支持多种网络插件,比如flannel、calico、canal等,任选一种即可,本次选择flannel,如果网络不行,可以使用本人提供的📎kube-flannel.yml,当然,你也可以安装calico,请点这里📎calico.yaml,推荐安装calico。

下面操作只需要在master节点上执行就可以了,插件使用的是DaemonSet控制器,他会在每个节点上都运行

  • 在Master节点上获取flannel配置文件(可能会失败,如果失败,请下载到本地,然后安装):

    1. wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    2. #当我们使用本地flannel.yml文件的时候 修改文件中的 quay.io仓库为quay-mirror.qiniu.com 这是个国内镜像
  • 使用配置文件启动flannel:

    1. kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    2. #如果使用的是下载好的flannel.yml文件,用如下的方式
    3. kubectl apply -f /lujing/flannel.yml
  • 查看部署CNI网络插件进度:

kubectl get pods -n kube-system
云原生-第一部分 - 图12

  • 再次在Master节点使用kubectl工具查看节点状态:

kubectl get nodes
云原生-第一部分 - 图13

  • 查看集群健康状况:

kubectl get cs
云原生-第一部分 - 图14
kubectl cluster-info
云原生-第一部分 - 图15

3 服务部署

3.1 前言

  • 在Kubernetes集群中部署一个Nginx程序,测试下集群是否正常工作。

    3.2 步骤

    如下操作都在master节点操作就可以了。

  • 部署Nginx:

    1. kubectl create deployment nginx --image=nginx:1.14-alpine
  • 暴露端口:NodePort的意思就是,可以让集群之外的浏览器可以访问他

    1. kubectl expose deployment nginx --port=80 --type=NodePort
  • 查看服务状态:

    1. kubectl get pods,svc
    2. #或者:
    3. kubectl get pod #查看pod
    4. kubectl get svc #查看服务 svc是service的简写,我们可以直接写service也可以

    pod是k8s的最小单元,我们的程序在容器里,容器在pod里,下图中30871是暴露给外界的端口号。然后我们用master服务器的ip+这个端口来访问,注意我们不仅master节点可以访问,而是master节点和两个node节点都可以访问
    云原生-第一部分 - 图16
    云原生-第一部分 - 图17

    4 kubernetes中kubectl命令自动补全

    1. yum install -y bash-completion
    2. source /usr/share/bash-completion/bash_completion
    3. source <(kubectl completion bash)
    4. echo source <(kubectl completion bash)” >> ~/.bashrc
    5. vim /root/.bashrc
    6. source /usr/share/bash-completion/bash_completion
    7. source <(kubectl completion bash)

    k8s的资源管理

    1 资源管理介绍

  • 在Kubernetes中,所有的内容都抽象为资源(就像java中一切皆对象,k8s中一切皆资源),用户需要通过操作资源来管理Kubernetes。比如pod是资源,pod控制器是资源,service也是资源

  • Kubernetes的本质就是一个集群系统,用户可以在集群中部署各种服务。所谓的部署服务,其实就是在Kubernetes集群中运行一个个的容器,并将指定的程序跑在容器中,比如前面我们讲讲nginx跑到了一个容器里边。
  • Kubernetes的最小管理单元是Pod而不是容器,所以只能将容器放在Pod中,而Kubernetes一般也不会直接管理Pod,而是通过Pod控制器来管理Pod的。
  • Pod提供服务之后,就需要考虑如何访问Pod中的服务,Kubernetes提供了Service资源实现这个功能。
  • 当然,如果Pod中程序的数据需要持久化,Kubernetes还提供了各种存储系统。

下图中的 Pod Controller就是pod控制器,当然不止这六种。
云原生-第一部分 - 图18
上图中 pod Controller就是为了产生各种各样的pod,然后pod里边运行容器,容器里边运行程序,如果程序需要数据持久化,就会用到下边的数据卷,然后如果这个pod想让被外部访问,就要通过service代理,外部访问service就能访问到pod了。
volume下边的就是各种存储系统。
学习kubernets的核心,就是学习如何对集群中的Pod、Pod控制器、Service、存储等各种资源进行操作。

2 YAML语法介绍

2.1 YAML语法介绍

  • YAML是一个类似于XML、JSON的标记性语言。它强调的是以“数据”为中心,并不是以标记语言为重点。因而YAML本身的定义比较简单,号称是“一种人性化的数据格式语言”。 ```xml 15
    beijign

对于xml来说,上述我们写了很多,但是有价值的数据只有15 和beijign两个,其他的都是辅助作用。,并且有浪费空间的闭合标间,所以它是以标记为重点的语言,信息密度低,使用复杂。

  1. 同样的如果用yml
  2. ```yaml
  3. heima:
  4. age: 15
  5. address:beijign
  6. #可见他比xml要简洁的多。而且他除了属性名和属性数据外,基本上没有无用的标签在里边。因此它是以数据为中心。

spring boot 就推荐用yml为配置文件。

  • YAML的语法比较简单,主要有下面的几个:
    • 大小写敏感。
    • 使用缩进表示层级关系。
    • 缩进不允许使用tab,只允许空格(低版本yaml的限制)。
    • 缩进的空格数不重要,只要相同层级的元素左对齐即可。
    • ‘#’表示注释。
  • YAML支持以下几种数据类型:
    • 常量:单个的、不能再分的值,yml中管他叫常量。
    • 对象:键值对的集合,又称为映射/哈希/字典。
    • 数组:一组按次序排列的值,又称为序列/列表。

我们可以用下边的网站验证我们的yml是否正确,他就是一个yml和json相互转换的网站:

2.2 YAML语法示例

2.2.1 YAML常量

  1. #常量,就是指的是一个简单的值,字符串、布尔值、整数、浮点数、NUll、时间、日期
  2. # 布尔类型
  3. c1: true
  4. # 整型
  5. c2: 123456
  6. # 浮点类型
  7. c3: 3.14
  8. # null类型
  9. c4: ~ # 使用~表示null
  10. # 日期类型
  11. c5: 2019-11-11 # 日期类型必须使用ISO 8601格式,即yyyy-MM-dd
  12. # 时间类型
  13. c6: 2019-11-11T15:02:31+08.00 # 时间类型使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区
  14. # 字符串类型
  15. c7: haha # 简单写法,直接写值,如果字符串中间有特殊符号,必须使用双引号或单引号包裹
  16. c8: line1
  17. line2 # 字符串过多的情况可以折成多行,每一行都会转换成一个空格

2.2.2 对象

  1. # 对象
  2. # 形式一(推荐):
  3. xudaxian:
  4. name: 许大仙
  5. age: 16
  6. # 形式二(了解):
  7. xuxian: { name: 许仙, age: 18 }

2.2.3 数组

  1. # 数组
  2. # 形式一(推荐):
  3. address:
  4. - 江苏
  5. - 北京
  6. # 形式二(了解):
  7. address: [江苏,上海]

3 资源管理方式

3.1 资源管理方式

k8s提供了如下三种资源管理的方式:

  • 命令式对象管理:直接使用命令去操作kubernetes的资源,就是把参数和配置写到命令行里边,一起执行。

    1. kubectl run nginx-pod --image=nginx:1.17.1 --port=80
    2. #这个命令就是运行一个nginx的pod --image就是指定容器镜像, --port就是指定暴露的端口
  • 命令式对象配置:通过命令配置和配置文件去操作kubernetes的资源。

    1. kubectl create/patch -f nginx-pod.yaml
    2. #create/patch 这里就是说明要做一个什么样的事,做这件事需要的参数在nginx-pod.yaml这个文件中。
  • 声明式对象配置:通过apply命令和配置文件去操作kubernetes的资源。

    1. kubectl apply -f nginx-pod.yaml
    2. #apply命令只用于创建和更新资源,是无法完全替代第二种方式的。

    | 类型 | 操作 | 适用场景 | 优点 | 缺点 | | —- | —- | —- | —- | —- | | 命令式对象管理 | 对象 | 测试 | 简单 | 只能操作活动对象(比如查看),无法审计、跟踪 | | 命令式对象配置 | 文件 | 开发 | 可以审计、跟踪 | 项目大的时候,配置文件多,操作麻烦 | | 声明式对象配置 | 目录 | 开发 | 支持目录操作 | 意外情况下难以调试 |

3.2 命令式对象管理

3.2.1 kubectl命令

  • kubectl是kubernetes集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装和部署。
  • kubectl命令的语法如下:

kubectl [command] [type] [name] [flags]

  • command:指定要对资源执行的操作,比如create、get、delete。
  • type:指定资源的类型,比如deployment、pod、service。
  • name:指定资源的名称,名称大小写敏感。
  • flags:指定额外的可选参数。

一般来说,command和type是必须的,name和flags都是可选的。

  • 示例:查看所有的pod

kubectl get pods

  • 示例:查看某个pod

kubectl get pod pod_name
image-20220309231241197.png

  • 示例:查看某个pod,以yaml格式展示结果

kubectl get pod pod_name -o yaml
kubectl get pod pod_name -o json #用json的方式展示信息

注意这两种展示方式,会展示出来非常的信息。

  • 如果想查看更详细的信息,可以用如下命令:

kubectl get pod pod_name -o wide:
输出如下图所示:
image-20220309231215317.png
可以看到更多的信息

3.2.2 操作(command)

  • kubernetes允许对资源进行多种操作,可以通过—help查看详细的操作命令:

kubectl —help
云原生-第一部分 - 图21

  • 经常使用的操作如下所示:
  • ① 基本命令: | 命令 | 翻译 | 命令作用 | | —- | —- | —- | | create | 创建 | 创建一个资源 | | edit | 编辑 | 编辑一个资源 | | get | 获取 | 获取一个资源 | | patch | 更新 | 更新一个资源 | | delete | 删除 | 删除一个资源 | | explain | 解释 | 展示资源文档 |

  • ② 运行和调试: | 命令 | 翻译 | 命令作用 | | —- | —- | —- | | run | 运行 | 在集群中运行一个指定的镜像 | | expose | 暴露 | 暴露资源为Service | | describe | 描述 | 显示资源内部信息 | | logs | 日志 | 输出容器在Pod中的日志 | | attach | 缠绕 | 进入运行中的容器 | | exec | 执行 | 执行容器中的一个命令 | | cp | 复制 | 在Pod内外复制文件 | | rollout | 首次展示 | 管理资源的发布 | | scale | 规模 | 扩(缩)容Pod的数量 | | autoscale | 自动调整 | 自动调整Pod的数量 |

对于describe命令来说,他可以查看多非常多的资源信息:
image-20220309232024802.png
以及容器在启动的过程中经历的一个例程,如下图所示。
image-20220309232115633.png
我们会经常使用describe这个命令查看容器的运行的状态,尤其是容器启动时问题的时候

  • ③ 高级命令: | 命令 | 翻译 | 命令作用 | | —- | —- | —- | | apply | 应用 | 通过文件对资源进行配置 | | label | 标签 | 更新资源上的标签 |

  • ④ 其他命令: | 命令 | 翻译 | 命令作用 | | —- | —- | —- | | cluster-info | 集群信息 | 显示集群信息 | | version | 版本 | 显示当前Client和Server的版本 |

3.2.3 资源类型(type)

  • kubernetes中所有的内容都抽象为资源,可以通过下面的命令进行查看:

kubectl api-resources
云原生-第一部分 - 图24

  • 经常使用的资源如下所示:
  • ① 集群级别资源: | 资源名称 | 缩写 | 资源作用 | | —- | —- | —- | | nodes | no | 集群组成部分 | | namespaces | ns | 隔离Pod |

  • ② Pod资源: | 资源名称 | 缩写 | 资源作用 | | —- | —- | —- | | Pods | po | 装载容器 |

  • ③ Pod资源控制器: | 资源名称 | 缩写 | 资源作用 | | —- | —- | —- | | replicationcontrollers | rc | 控制Pod资源 | | replicasets | rs | 控制Pod资源 | | deployments | deploy | 控制Pod资源 | | daemonsets | ds | 控制Pod资源 | | jobs | | 控制Pod资源 | | cronjobs | cj | 控制Pod资源 | | horizontalpodautoscalers | hpa | 控制Pod资源 | | statefulsets | sts | 控制Pod资源 |

  • ④ 服务发现资源: | 资源名称 | 缩写 | 资源作用 | | —- | —- | —- | | services | svc | 统一Pod对外接口 | | ingress | ing | 统一Pod对外接口 |

  • ⑤ 存储资源: | 资源名称 | 缩写 | 资源作用 | | —- | —- | —- | | volumeattachments | | 存储 | | persistentvolumes | pv | 存储 | | persistentvolumeclaims | pvc | 存储 |

  • ⑥ 配置资源: | 资源名称 | 缩写 | 资源作用 | | —- | —- | —- | | configmaps | cm | 配置 | | secrets | | 配置 |

3.2.4 应用示例

  • 示例:创建一个namespace

kubectl create namespace dev
#上班的 namespace可以简写为ns
云原生-第一部分 - 图25

  • 示例:获取namespace

kubectl get namespace
kubectl get ns
云原生-第一部分 - 图26

  • 示例:在刚才创建的namespace下创建并运行一个Nginx的Pod

kubectl run nginx —image=nginx:1.17.1 -n dev
# 这里 -n dev就是指定名称空间
云原生-第一部分 - 图27

  • 示例:查看名为dev的namespace下的所有Pod,如果不加-n,默认就是default的namespace

kubectl get pods -n dev
云原生-第一部分 - 图28

  • 示例:删除指定namespace下的指定Pod

kubectl delete pod nginx -n dev
云原生-第一部分 - 图29

  • 示例:删除指定的namespace,这个资源里边的其他资源也会被删除,覆巢之下无完卵

kubectl delete namespace dev
云原生-第一部分 - 图30

3.3 命令式对象配置

3.3.1 概述

  • 命令式对象配置:通过命令配置和配置文件去操作kubernetes的资源。这里的重点不是编辑yaml文件,而是使用这种方式

    3.3.2 应用示例

  • 示例:

  • ① 创建一个nginxpod.yaml,内容如下: ```yaml apiVersion: v1 kind: Namespace #类型为名称空间 metadata: name: dev #名称的名称

上边就会创建出来一个 叫做dev的namespace


apiVersion: v1 kind: Pod #类型为pod metadata: name: nginxpod #pod的名字 namespace: dev #所属的名称空间 spec: containers:

  • name: nginx-containers #pod中容器的名字

    1. image: nginx:1.17.1 #容器的镜像

    ```

  • ② 执行create命令,创建资源:

kubectl create -f nginxpod.yaml
云原生-第一部分 - 图31

  • ③ 执行get命令,查看资源:

kubectl get -f nginxpod.yaml

云原生-第一部分 - 图32
注意我们上边的pod加不加s都是一个意思。

  • ④ 执行delete命令,删除资源:

kubectl delete -f nginxpod.yaml #这种方式就把这个yaml创建的namespace和pod都删了
云原生-第一部分 - 图33

3.3.3 总结

  • 命令式对象配置的方式操作资源,可以简单的认为:命令+yaml配置文件(里面是命令需要的各种参数)。

    3.4 声明式对象配置

    3.4.1 概述

  • 声明式对象配置:通过apply命令和配置文件去操作kubernetes的资源。

  • 声明式对象配置和命令式对象配置类似,只不过它只有一个apply命令。
  • apply相当于create和patch。

    3.4.2 应用示例

  • 示例:

    1. kubectl apply -f nginxpod.yaml
    2. #首次执行他会创建资源,如果我们再次执行一下:
    3. kubectl apply -f nginxpod.yaml
    4. #会发现它会提示是否修改了资源

    云原生-第一部分 - 图34
    如果我们把nginx的版本调整一下,比如把17.2改成17.1 ,然后再执行这个命令就会发现如下提示:
    image-20220311175107358.png
    他提示我们的pod重新配置过了。

    3.4.3 总结

  • 声明式对象配置就是使用apply描述一个资源的最终状态(在yaml中定义状态)。

  • 使用apply操作资源:

    • 如果资源不存在,就创建,相当于kubectl create。
    • 如果资源存在,就更新,相当于kubectl patch。

      3.4.4.扩展知识

      node节点是默认是不能执行kubectl命令的,因为执行kubectl这个命令是需要配置的,如果我们想在node节点上执行这个命令
      ,需要将master上的.kube文件复制到node节点上,即在master节点上回字形如下命令:
      1. scp -r HOME/.kube node1:HOME/
      2. #scp可以夸主机复制,-r 此选项递归复制目录及其内容

      3.5 使用方式推荐

      注意如下的只是推荐,实际怎么做可以随自己喜好
  • 创建和更新资源使用声明式对象配置:kubectl apply -f xxx.yaml。

  • 删除资源使用命令式对象配置:kubectl delete -f xxx.yaml。
  • 查询资源使用命令式对象管理:kubectl get(describe) 资源名称。

    3.6 扩展:kubectl可以在Node上运行

  • kubectl的运行需要进行配置,它的配置文件是$HOME/.kube,如果想要在Node节点上运行此命令,需要将Master节点的.kube文件夹复制到Node节点上,即在Master节点上执行下面的操作:

scp -r $HOME/.kube k8s-node1:$HOME

4 如何快速的编写yaml文件

4.1 使用kubectl create命令生成yaml文件

  • 此种方式适用于没有真正部署资源。
  • 使用kubectl create命令生成yaml文件:

kubectl create deployment nginx —image=nginx:1.17.1 —dry-run=client -n dev -o yaml

  • 如果yaml文件太长,可以写入到指定的文件中。

kubectl create deployment nginx —image=nginx:1.17.1 —dry-run=client -n dev -o yaml > test.yaml
云原生-第一部分 - 图36

4.2 使用kubectl get命令导出yaml文件(此种方式已经不建议使用)

  • 此种方式适合于资源已经部署,动态的导出yaml文件。
  • 创建一个Deployment:

kubectl create deployment nginx —image=nginx:1.17.1 -n dev

  • 使用kubectl get命令导出yaml文件:

kubectl get deployment nginx -n dev -o yaml —export > test2.yaml
云原生-第一部分 - 图37
此种方式会在未来版本中删除,因此不再建议使用。

5.k8s的实战入门

1 前言

  • 介绍如何在kubernetes集群中部署一个Nginx服务,并且能够对其访问。

    2 Namespace

    2.1 概述

  • Namespace是kubernetes系统中一种非常重要的资源,它的主要作用是用来实现多套系统的资源隔离或者多租户的资源隔离

  • 默认情况下,kubernetes集群中的所有Pod都是可以相互访问的。但是在实际中,可能不想让两个Pod之间进行互相的访问,那么此时就可以将两个Pod划分到不同的Namespace下。kubernetes通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的“组”,以方便不同的组的资源进行隔离使用和管理。
  • 可以通过kubernetes的授权机制,将不同的Namespace交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等等,来实现租户可用资源的管理。

云原生-第一部分 - 图38

  • kubernetes在集群启动之后,会默认创建几个namespace。
    1. kubectl get namespace
    2. #如下是k8s启动后默认创建的四个namespace
    3. #NAME STATUS AGE
    4. #default Active 67d 所有未指定namespace的对象都会被分配在default命名空间
    5. #kube-node-lease Active 67d 集群节点之间的心跳维护,v1.13开始引入
    6. #kube-public Active 67d 这个命名空间下的资源可以被所有人访问(包括未认证用户)
    7. #kube-system Active 67d 所有由k8s系统创建的资源都处于这个命名空间

云原生-第一部分 - 图39

  • default:所有未指定的Namespace的对象都会被分配在default命名空间。
  • kube-node-lease:集群节点之间的心跳维护,v1.13开始引入。
  • kube-public:此命名空间的资源可以被所有人访问(包括未认证用户)。
  • kube-system:所有由kubernetes系统创建的资源都处于这个名称空间。 可以通过kubectl get pod -n kube-system来查看

我们k8s本身的组件,也是以pod方式运行的,都在kube-system这个名称空间下。

2.2 应用示例

  • 示例:查看所有的命名空间 ```shell kubectl get namespace

    或者

    kubectl get ns

查看具体命名空间

kubectl get ns defalut

  1. STATUS表示当前名称空间的状态,AGE标识这个名称空间工作了多久了<br />![](https://cdn.nlark.com/yuque/0/2020/png/513185/1609137101603-e9ccc847-2be3-4f20-b237-da67964ae72c.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_28%2Ctext_6K645aSn5LuZ%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10#crop=0&crop=0&crop=1&crop=1&from=url&id=LDshu&margin=%5Bobject%20Object%5D&originHeight=260&originWidth=996&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
  2. - 可以通过describe命令查看名称空间的详细信息:
  3. ```shell
  4. kubectl describe ns default

image-20220311195741907.png
注意 STATUS中 active:表示正在运行的名称空间,Terminating标识正在删除的命名空间,这是因为我们当前的命名空间下可能会有很多的资源,我们删除这个名称空间的时候,这些资源也要被删除,删除这些资源肯定是需要时间的。
然后 resource quota表示针对namespace做的资源限制
LimitRange resource表示针对namespace中的每个组件做的资源限制

  • 示例:查看指定的命名空间

kubectl get namespace default
kubectl get ns default
云原生-第一部分 - 图41

  • 示例:指定命名空间的输出格式

kubectl get ns default -o wide
kubectl get ns default -o json
kubectl get ns default -o yaml
云原生-第一部分 - 图42

  • 示例:查看命名空间的详情

kubectl describe namespace default
kubectl describe ns default
云原生-第一部分 - 图43

  • 示例:创建命名空间

kubectl create namespace dev
kubectl create ns dev
云原生-第一部分 - 图44

  • 示例:删除命名空间

kubectl delete ns dev
云原生-第一部分 - 图45

  • 示例:命令式对象配置
  • ① 新建ns-dev.yaml:

apiVersion: v1
kind: Namespace
metadata:
name: dev

  • ② 通过命令式对象配置进行创建和删除:

kubectl create -f ns-dev.yaml
kubectl delete -f ns-dev.yaml

3 Pod

3.1 概述

  • Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
  • Pod可以认为是容器的封装,一个Pod中可以存在一个或多个容器

云原生-第一部分 - 图46
上图中,最后一个显示Pause的表示的是根容器,然后再往上的 user ContainerN就是表示用户容器

  • kubernetes在集群启动之后,集群中的各个组件也是以Pod方式运行的,可以通过下面的命令查看:

kubectl get pods -n kube-system
云原生-第一部分 - 图47
例如上图中的,kube-apiserver-k8s-master就是我们的访问入口
kube-scheduler-k8s-master就是我们的调度组件,比如把容器安装到哪个节点
kube-controller-manager-k8s-master 应该就是具体执行我们的pod的。
etcd-k8s-master:就是我们存储信息的。
kube-flannel- 是用来管理网络的,他在每个节点都运行了以后,可以看到他后边ds接的应该就是md5
kube-proxy-
是做访问代理的,也是每个节点都部署了一个。

3.2 语法及应用示例

语法:创建并运行Pod

  • kubernetes没有单独运行pod的命令,都是通过pod控制器来实现的。

    1. # 命令格式:kubectl run (Pod控制器的名称) [参数]
    2. # --image 指定Pod的镜像
    3. # --port 指定端口
    4. # --namespace 指定namespace
    5. # kubectl run nginx --image=nginx:1.17.1 --port=80 --namespace=dev 这里的run后边的nginx就是pod控制器的名称
  • 示例:在名称为dev的namespace下创建一个Nginx的Pod

kubectl run nginx —image=nginx:1.17.1 —port=80 —namespace=dev
云原生-第一部分 - 图48

  • 语法: 查询所有Pod的基本信息

kubectl get pods [-n 命名空间的名称]

  • 示例:查询名称为dev的namespace下的所有Pod的基本信息

kubectl get pods -n dev
云原生-第一部分 - 图49

  • 语法:查看Pod的详细信息

kubectl describe pod pod的名称 [-n 命名空间名称]

  • 示例:查看名称为dev的namespace下的Pod的名称为nginx的详细信息 ```shell kubectl describe pods nginx-f89759699-vsx59 -n dev

    注意我们这里要指定 -n dev 标识从哪个名称空间查找,否则他会默认去default这个名称

    这样可以看到更详细的关于这个pod的信息

这里上边的 nginx-f89759699-vsx59 这个名字不用写全,也可以查询到,他会把所有匹配的pod的详细信息都输出出来。List也就是

  1. ![image-20220311224653674.png](https://cdn.nlark.com/yuque/0/2022/png/22845779/1648290657615-7c83e959-e2ec-4f0c-bf63-199025bfc5c1.png#clientId=ua5da38af-75eb-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=zYXHl&margin=%5Bobject%20Object%5D&name=image-20220311224653674.png&originHeight=750&originWidth=1123&originalType=binary&ratio=1&rotation=0&showTitle=false&size=835574&status=done&style=none&taskId=u4da6766e-02fc-42e6-b7a4-745d1491593&title=)<br />describe对于我们来说,最重要的是下边的events这部分,我们经常用这部分的内容来排查容器启动时候的错误,我们可以用 | grep Events:直接抓取这部分内容:<br />![image-20220311225222869.png](https://cdn.nlark.com/yuque/0/2022/png/22845779/1648290686951-543f70d1-ba18-4d2a-a27f-4c3190cd5e95.png#clientId=ua5da38af-75eb-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=ude4e7722&margin=%5Bobject%20Object%5D&name=image-20220311225222869.png&originHeight=426&originWidth=1854&originalType=binary&ratio=1&rotation=0&showTitle=false&size=520908&status=done&style=none&taskId=u77111a41-c328-4cab-84e4-d5c193bf350&title=)
  2. - 语法:Pod的访问
  3. ```shell
  4. # 获取Pod的IP
  5. kubectl get pods [-n dev] -o wide
  6. # 通过curl访问
  7. curl ip:端口
  • 示例:访问Nginx的Pod

    1. kubectl get pods -n dev -o wide
    2. curl 10.244.2.7:80

    下图中,
    NAME:标识pod的名称
    READ:当前pod里边有几个容器,容器里边有几个正在运行的,根容器不会计算在内。
    RESTARS:表示这个pod重启的次数。
    AGE:标识pod运行的时间
    IP:标识这个pod的IP地址,注意pod的IP地址会随着pod的重新创建而变化,也就是说重启这个pod后,他的IP可能就变了
    NODE:表示这个pod被调度到了那个节点上运行了
    云原生-第一部分 - 图50

  • 语法:删除指定的Pod

kubectl delete pod pod的名称 [-n 命名空间]

  • 示例:删除Nginx的Pod

    1. kubectl delete pod nginx -n dev

    image-20220311230331645.png
    我们删除了一个pod以后,发现他又自动创建了一个新的pod, 通过 AGE就能看出来,也就是他删除的时候又重新创建了一个。
    这是我们的pod控制器在生效。

  • 示例:命令式对象配置

  • ① 新建pod-nginx.yaml: ```yaml apiVersion: v1 kind: Pod metadata: name: nginx namespace: dev spec: containers:
  • image: nginx:1.17.1 imagePullPolicy: IfNotPresent name: pod ports:
  • name: nginx-port containerPort: 80

    1. protocol: TCP

    ```

  • ② 执行创建和删除命令:

kubectl create -f pod-nginx.yaml
kubectl delete -f pod-nginx.yaml

4 Label

4.1 概述

  • Label是kubernetes的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
  • Label的特点:
    • 一个Label会以key/value键值对的形式附加到各种对象上,如添加到Node、Pod、Service等,大多数资源都可以添加标签。
    • 一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去。
    • Label通常在资源对象定义时确定,当然也可以在对象创建后动态的添加或删除。
  • 可以通过Label实现资源的多纬度分组,以便灵活、方便地进行资源分配、调度、配置和部署等管理工作。
  • 所谓的标签就是标识选择机制,先添加标签,再根据标签进行选择和区分

一些常用的Label标签示例如下:

  • 版本标签:“version”:”release”,”version”:”stable”。。。
  • 环境标签:“environment”:”dev”,“environment”:”test”,“environment”:”pro”。。。
  • 架构标签:“tier”:”frontend”,”tier”:”backend”。。。
  • 标签定义完毕之后,还要考虑到标签的选择,这就要用到Label Selector,即:
    • Label用于给某个资源对象定义标识。
    • Label Selector用于查询和筛选拥有某些标签的资源对象。
  • 当前有两种Label Selector:
    • 基于等式的Label Selector。
      • name=salve有包含Label中的key=“name”并且value=“slave”的对象(比如上述的 environment:dev 在我们这里就是 environment=dev)。
      • env!=production:选择所有包含Label中的key=“env”并且value!=“production”的对象。
    • 基于集合的Label Selector。
      • name in (master,slave):选择所有包含Label中的key=“name”并且value=“master”或value=“slave”的对象。
      • name not in (master,slave):选择所有包含Label中的key=“name”并且value!=“master”和value!=“slave”的对象。
  • 标签的选择条件可以使用多个,此时将多个Label Selector进行组合,使用逗号(,)进行分隔即可。

    • name=salve,env!=production。
    • name not in (master,slave),env!=production。

      4.2 语法及应用示例

  • 语法:为资源打标签

    1. kubectl label pod xxx key=value [-n 命名空间]
    2. #pod是指给pod资源打标签
    3. #xxx表示某个pod的名字
    4. # key=value就是标签

    image-20220316172758953.png

  • 示例:为Nginx的Pod打上标签

kubectl label pod nginx version=1.0 -n dev
云原生-第一部分 - 图53

  • 语法:更新资源的标签

    1. kubectl label pod xxx key=value [-n 命名空间] --overwrite
    2. #上述--overwrite就是强制覆盖以后标签
    3. #由此我们也知道,同一个key的标签只能有一个 比如version=1.0 和version=2.0只能有一个

    image-20220316173227947.png

  • 示例:为Nginx的Pod更新标签

kubectl label pod nginx version=2.0 -n dev —overwrite
云原生-第一部分 - 图55

  • 语法:查看标签

kubectl get pod xxx [-n 命名空间] —show-labels

  • 示例:显示Nginx的Pod的标签

kubectl get pod nginx -n dev —show-labels
云原生-第一部分 - 图56

  • 语法:筛选标签

    1. kubectl get pod -l key=value [-n 命名空间] --show-labels
    2. #这里 key=value就是要筛选的标签
    3. # -l 就是要筛选的标签
    4. #key=value这里,我们还可以写 不等于 key!=value

    image-20220316173803548.png

  • 示例:筛选版本号是2.0的在名称为dev的namespace下的Pod

kubectl get pod -l version=2.0 -n dev —show-labels
云原生-第一部分 - 图58

  • 语法:删除标签

kubectl label pod xxx key- [-n 命名空间]
#注意 key-是连在一起的

  • 示例:删除名称为dev的namespace下的Nginx的Pod上的标签

kubectl label pod nginx version- -n dev
云原生-第一部分 - 图59
添加和筛选标签,是我们平时用的最多的。

  • 示例:命令式对象配置
  • ① 新建pod-nginx.yaml: ```yaml apiVersion: v1 kind: Pod metadata: name: nginx namespace: dev labels: #此处就是在资源定义的时候,就打上了标签 version: “3.0” #标签的key和value env: “test”
    spec: containers:
  • image: nginx:1.17.1 imagePullPolicy: IfNotPresent name: pod ports:
  • name: nginx-port containerPort: 80

    1. protocol: TCP

    ```

  • ② 执行创建和删除命令:

kubectl create -f pod-nginx.yaml
kubectl delete -f pod-nginx.yaml

5 Deployment(pod控制器)

5.1 概述

  • 在kubernetes中,Pod是最小的控制单元,但是kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。
  • Pod控制器用于Pod的管理,确保Pod资源符合预期的状态,当Pod的资源出现故障的时候,会尝试进行重启或重建Pod。

什么叫符合预期的状态:
比如,我们需要三个部署nginx的pod来支持我们的服务,当运行一段时间后,有一个pod挂了,此时就不符合我们的预期了,这回收pod控制器(Deployment)就会尝试重启这个pod,如果重启失败,就会干掉这个pod来创建一个新的pod,使nginx的pod始终保持三个,让她一直处于符合我们预期的状态。
这也就是为啥之前我们直接删pod是删不掉的。(注意在1.18之前是这样,1.18之后,直接用kubectl run命令创建pod是不会创建deployment的,所以可以删掉的)

  • 在kubernetes中Pod控制器的种类有很多,每一种pod控制器都有适合自己的场景,本章节只介绍一种:Deployment。
  • 从下图中可以发现,pod和pod控制器是通过标签的方式来联系的,看下图的红字。所以我们创建的pod默认都是带有一个标签的。

云原生-第一部分 - 图60

5.2 语法及应用示例

  1. #命令格式:kubectl run deployment名称 [参数]
  2. # --image 指定pod的镜像
  3. # --port 指定端口
  4. # --replicas 指定创建pod的数量
  5. # --namespace 指定名称空间
  6. kubectl run nginx --image=nginx:1.17.1 --port=80 --replicas=3 -n dev
  7. #上述中 nginx就是我们要创建的deployment的名字

特别注意:在v1.18版之后,kubectl run nginx —image=nginx —replicas=2 —port=80,会反馈Flag —replicas has been deprecated, has no effect and will be removed in the future,并且只会创建一个Nginx容器实例,不会又deployment。因此这个版本不建议我们这样创建deployment和pod了,建议我们使用下边的deployment的命令创建。
而且上述的这种方式,已经不会创建deployment了,他只会创建一个pod,这样我们这个pod直接删除也会删掉,不会再重新出来一个新的了。
image-20220316183634796.png
可以看到,我们用上述的命令创建完,是看不到depyment资源的。只有pod

  • 语法:创建指定名称的deployement

    1. kubectl create deployment xxx [-n 命名空间]
    2. #注意这个命令是不能直接使用的,必须要跟上 --image这个参数
    3. kubectl create deploy xxx [-n 命名空间]
    4. #注意这个命令是不能直接使用的,必须要跟上 --image这个参数
  • 示例:在名称为test的命名空间下创建名为nginx的deployment,这里就指定了 image

kubectl create deployment nginx —image=nginx:1.17.1 -n test
云原生-第一部分 - 图62

  • 语法:根据指定的deplyment创建Pod

kubectl scale deployment xxx [—replicas=正整数] [-n 命名空间]
#这里的xxx是我们的deployment 也就是pod控制器的名称

  • 示例:在名称为test的命名空间下根据名为nginx的deployment创建4个Pod

kubectl scale deployment nginx —replicas=4 -n dev
云原生-第一部分 - 图63
image-20220316184436628.png
上图中我们可以看出来,pod都是以deployment的名字开头的。

  • 语法:命令式对象配置
  • ① 创建一个deploy-nginx.yaml,内容如下: ```yaml apiVersion: apps/v1 #版本 这里应该是固定写法 kind: Deployment #类型为Deployment metadata:
    name: nginx #Deployment的名字 namespace: dev #Deployment所属的命名空间 spec: replicas: 3 #创建3个pod selector: #控制器要选择的标签 被控制的pod上就会有这个标签 matchLabels:
    run: nginx template: #pod模板 metadata: labels: run: nginx #pod的标签 spec: containers: #pod的相关信息
  • image: nginx:1.17.1 name: nginx ports:
  • containerPort: 80

    1. protocol: TCP

    ```

  • ② 执行创建和删除命令:

kubectl create -f deploy-nginx.yaml
kubectl delete -f deploy-nginx.yaml

  • 语法:查看创建的Pod

kubectl get pods [-n 命名空间]

  • 示例:查看名称为dev的namespace下通过deployment创建的3个Pod

kubectl get pods -n dev
云原生-第一部分 - 图65

  • 语法:查看deployment的信息

kubectl get deployment [-n 命名空间]
image-20220316184854955.png
kubectl get deploy [-n 命名空间]

  • 示例:查看名称为dev的namespace下的deployment

kubectl get deployment -n dev
云原生-第一部分 - 图67

  • 语法:查看deployment的详细信息

kubectl describe deployment xxx [-n 命名空间]
kubectl describe deploy xxx [-n 命名空间]

  • 示例:查看名为dev的namespace下的名为nginx的deployment的详细信息

kubectl describe deployment nginx -n dev
云原生-第一部分 - 图68

  • 语法:删除deployment

kubectl delete deployment xxx [-n 命名空间]
#注意这个控制器下的pod也会对应着被删除
kubectl delete deploy xxx [-n 命名空间]

  • 示例:删除名为dev的namespace下的名为nginx的deployment

kubectl delete deployment nginx -n dev
云原生-第一部分 - 图69

6 Service

6.1 概述

  • 我们已经能够利用Deployment来创建一组Pod来提供具有高可用性的服务,虽然每个Pod都会分配一个单独的Pod的IP地址,但是却存在如下的问题:
    • Pod的IP会随着Pod的重建产生变化。
    • Pod的IP仅仅是集群内部可见的虚拟的IP,外部无法访问。

云原生-第一部分 - 图70

  • 这样对于访问这个服务带来了难度,因此,kubernetes设计了Service来解决这个问题。
  • Service可以看做是一组同类的Pod对外的访问接口,借助Service,应用可以方便的实现服务发现和负载均衡。
  • service也是通过标签选择的机制来链接pod
  • service在它整个生命周期中IP地址都不会发生变化
  • service是通过deploy来寻找pod的

云原生-第一部分 - 图71

6.2 语法及应用示例

6.2.1 创建集群内部可访问的Service

  • 语法:暴露Service

暴露Service
#kubectl expose deployment xxx —name=service的名称 —type=ClusterIP —port=暴露的端口,指定service的端口 —target-port=指向集群中的Pod的端口 [-n 命名空间]

上述命令可以发现,这里是通过指定deployment来创建service的,因为pod都是通过deploy来管理的。所以这样写能更有表达性
#type的类型有很多种,这里用ClusterIP 表示的是集群IP,如果不指定,他默认也是ClusterIP

会产生一个CLUSTER-IP,这个就是service的IP,在Service的生命周期内,这个地址是不会变化的

注意:type= ClusterIP这个类型的IP只能在集群内部访问,就是部署集群的机器上,外部是不能访问的。

  • 示例:暴露名为test的namespace下的名为nginx的deployment,并设置服务名为svc-nginx1

kubectl expose deployment nginx —name=svc-nginx1 —type=ClusterIP —port=80 —target-port=80 -n test
云原生-第一部分 - 图72

  • 语法:查看Service

kubectl get service [-n 命名空间] [-o wide]
image-20220321005512256.png

  • 示例:查看名为test的命名空间的所有Service

kubectl get service -n test
云原生-第一部分 - 图74

6.2.2 创建集群外部可访问的Service

  • 语法:暴露Service

上述创建的Service是不可以被外部访问的,因为他的type类型为ClusterIP,想让她被外部节点访问,type的类型应改为NodePort
kubectl expose deployment xxx —name=服务名 —type=NodePort —port=暴露的端口 —target-port=指向集群中的Pod的端口 [-n 命名空间]
# 当指定type的类型为NodePort的时候,会产生一个外部也可以访问的Service

  • 示例:暴露名为test的namespace下的名为nginx的deployment,并设置服务名为svc-nginx2

kubectl expose deploy nginx —name=svc-nginx2 —type=NodePort —port=80 —target-port=80 -n test
云原生-第一部分 - 图75

  • 语法:查看Service

kubectl get service [-n 命名空间] [-o wide]

image-20220321010843864.png
注意,我们可以看到PORT的部分不在只有一个80,而是 80:31961 ,这个意思是,当我们通过主机的IP+31961端口进行访问的时候,他就会被转换到 10.100.31.194:80上,也就实现了外部访问service,如下us哦是 192.168.14.100就是主机的IP
image-20220321011034665.png

  • 示例:查看名为test的命名空间的所有Service

kubectl get service -n test
云原生-第一部分 - 图78

6.2.3 删除服务

  • 语法:删除服务

kubectl delete service xxx [-n 命名空间]

  • 示例:删除服务

kubectl delete service/svc svc-nginx1 -n test
云原生-第一部分 - 图79

6.2.4 对象配置方式

  • 示例:对象配置方式
  • ① 新建svc-nginx.yaml,内容如下:

    1. apiVersion: v1
    2. kind: Service
    3. metadata:
    4. name: svc-nginx
    5. namespace: dev
    6. spec:
    7. clusterIP: 10.109.179.231
    8. ports:
    9. - port: 80
    10. protocol: TCP
    11. targetPort: 80
    12. selector: #这里是选择的哪个标签
    13. run: nginx
    14. type: ClusterIP #这里写上后,上边的clusterIP可写可不写,写就是自己指定IP,不写就是k8s随机一个IP
    15. #上述我们也可以指定type的类型为 NodePort,
  • ② 执行创建和删除命令:

kubectl create -f svc-nginx.yaml
kubectl delete -f svc-nginx.yaml
详细介绍Pod资源的各种配置(YAML)和原理。

k8s的Pod详解

这里会讲解pod资源的各种配置(yaml)和原理

1 Pod的介绍

1.1 Pod的结构

云原生-第一部分 - 图80

  • 每个Pod中都包含一个或者多个容器,这些容器可以分为两类:
  • ① 用户程序所在的容器,数量可多可少。
  • ② Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:

    • 可以以它为依据,评估整个Pod的健康状况。就是以根容器的钱康状态为准,查看整个容器是死是活
    • 可以在根容器上设置IP地址,其它容器都共享此IP(Pod的IP),以实现Pod内部的网络通信,也就是说同一个pod内部的容器,都可以通过这个ip加上自己的各个容器的端口号来访问ip:port(这里是Pod内部的通讯,Pod之间的通讯采用虚拟二层网络技术来实现,我们当前环境使用的是Flannel)。

      1.2 Pod定义

  • 下面是Pod的资源清单,也就是pod的yml配置:

  • 其实我们通过 kubectl get pod nginx1 -o yaml 这种方式获取到的详细信息,和pod的yaml配置就很相像了

image-20220321021213879.png

  1. apiVersion: v1 #必选,版本号,例如v1
  2. kind: Pod   #必选,资源类型,例如 Pod
  3. metadata:   #必选,元数据
  4. name: string #必选,Pod名称
  5. namespace: string #Pod所属的命名空间,默认为"default"
  6. labels:    #自定义标签列表
  7. - name: string  
  8. spec: #必选,Pod中容器的详细定义,这事最重要的的一个属性,我们可以看到下边所有的配置都是他的
  9. containers: #必选,Pod中容器列表
  10. - name: string #必选,容器名称
  11. image: string #必选,容器的镜像名称
  12. imagePullPolicy: [ Always|Never|IfNotPresent ] #获取镜像的策略
  13. command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令
  14. args: [string] #容器的启动命令参数列表
  15. workingDir: string #容器的工作目录
  16. volumeMounts: #挂载到容器内部的存储卷配置
  17. - name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
  18. mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
  19. readOnly: boolean #是否为只读模式
  20. ports: #需要暴露的端口库号列表
  21. - name: string #端口的名称
  22. containerPort: int #容器需要监听的端口号
  23. hostPort: int #容器所在主机需要监听的端口号,默认与Container相同
  24. protocol: string #端口协议,支持TCP和UDP,默认TCP
  25. env: #容器运行前需设置的环境变量列表
  26. - name: string #环境变量名称
  27. value: string #环境变量的值
  28. resources: #资源限制和请求的设置
  29. limits: #资源限制的设置
  30. cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
  31. memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
  32. requests: #资源请求的设置
  33. cpu: string #Cpu请求,容器启动的初始可用数量
  34. memory: string #内存请求,容器启动的初始可用数量
  35. lifecycle: #生命周期钩子
  36. postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
  37. preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
  38. livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
  39. exec:   #对Pod容器内检查方式设置为exec方式
  40. command: [string] #exec方式需要制定的命令或脚本
  41. httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
  42. path: string
  43. port: number
  44. host: string
  45. scheme: string
  46. HttpHeaders:
  47. - name: string
  48. value: string
  49. tcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式
  50. port: number
  51. initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒
  52. timeoutSeconds: 0    #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
  53. periodSeconds: 0    #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
  54. successThreshold: 0
  55. failureThreshold: 0
  56. securityContext:
  57. privileged: false
  58. restartPolicy: [Always | Never | OnFailure] #Pod的重启策略
  59. nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
  60. nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
  61. imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
  62. - name: string
  63. hostNetwork: false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
  64. volumes: #在该pod上定义共享存储卷列表
  65. - name: string #共享存储卷名称 (volumes类型有很多种)
  66. emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
  67. hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
  68. path: string    #Pod所在宿主机的目录,将被用于同期中mount的目录
  69. secret:    #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
  70. scretname: string
  71. items:
  72. - key: string
  73. path: string
  74. configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
  75. name: string
  76. items:
  77. - key: string
  78. path: string
  • 语法:查看每种资源的可配置项

    1. # 查看某种资源可以配置的一级配置,就是yaml文件最外一层的标签,二级资源自然就是yaml的第二级的标签
    2. #比如metadata是第一级资源,那么metadata的第二级资源就是metadata.name
    3. kubectl explain 资源类型
    4. # 查看属性的子属性
    5. kubectl explain 资源类型.属性
    6. #查看子属性的子属性
    7. kubectl explain 资源类型.属性.属性
  • 示例:查看资源类型为pod的可配置项

kubectl explain pod
云原生-第一部分 - 图82

  • 示例:查看资源类型为Pod的metadata的属性的可配置项

kubectl explain pod.metadata
云原生-第一部分 - 图83
在kubernetes中基本所有资源的一级属性都是一样的,主要包含5个部分:

  • apiVersion :版本,有kubernetes内部定义,版本号必须用kubectl api-versions查询。每个资源都有指定的类型,通过kubectl explain 资源名(比如pod) 就可以查到了
  • kind :类型,有kubernetes内部定义,类型必须用kubectl api-resources查询。 每个资源的名字也是固定的,比如pod,同样可以通过kubectl explain 资源名查看到
  • metadata :元数据,主要是资源标识和说明,常用的有name、namespace、labels等。
  • spec :描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述。
  • status :状态信息,里面的内容不需要定义,由kubernetes自动生成。

    在上面的属性中,spec是接下来研究的重点,继续看下它的常见子属性:

    • containers <[]Object>:容器列表,用于定义容器的详细信息。他是一个数组的格式可以看到,可以定义每个容器的相信信息,比如使用的镜像,端口号
    • nodeName :根据nodeName的值将Pod调度到指定的Node节点上。这里如果新制定了。那就是定死部署到那个节点上。
    • nodeSelector :根据NodeSelector中定义的信息选择该Pod调度到包含这些Label的Node上。
    • hostNetwork :是否使用主机网络模式,默认为false,如果是false就是k8s分配的一个IP。如果设置为true,表示使用宿主机网络,宿主机网络,也就是指的,这个pod的ip地址和我们的宿主机(linux系统)是一个IP地址,就是通过ifconfig可以看到的那个主机IP ,使用宿主机网络的问题就是,加入我们有两个pod都运行nginx,那么这两个pod是不能使用同一个端口的(80端口),因此我们很少使用这种方式。
    • volumes <[]Object> :存储卷,用于定义Pod上面挂载的存储信息。
    • restartPolicy :重启策略,表示Pod在遇到故障的时候的处理策略。

      2 Pod的配置

      2.1 概述

    • 本小节主要来研究pod.spec.containers属性,这也是Pod配置中最为关键的一项配置。

    • 示例:查看pod.spec.containers的可选配置项

      1. kubectl explain pod.spec.containers
      2. # 返回的重要属性 这事上述命令返回的内容
      3. KIND: Pod
      4. VERSION: v1
      5. RESOURCE: containers <[]Object> # 数组,代表可以有多个容器FIELDS:
      6. FIELDS:
      7. name <string> # 容器名称
      8. image <string> # 容器需要的镜像地址
      9. imagePullPolicy <string> # 镜像拉取策略 ,就是镜像使用本地还是远程仓库的
      10. command <[]string> # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
      11. args <[]string> # 容器的启动命令需要的参数列表
      12. env <[]Object> # 容器环境变量的配置
      13. ports <[]Object> # 容器需要暴露的端口号列表
      14. resources <Object> # 资源限制和资源请求的设置

      2.2 基本配置

    • 创建pod-base.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-base
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers:
      10. - name: nginx # 容器名称
      11. image: nginx:1.17.1 # 容器需要的镜像地址
      12. - name: busybox # 容器名称
      13. image: busybox:1.30 # 容器需要的镜像地址
    • 上面定义了一个比较简单的Pod的配置,里面有两个容器:

      • nginx:用的是1.17.1版本的nginx镜像创建(nginx是一个轻量级的web容器)。
      • busybox:用的是1.30版本的busybox镜像创建(busybox是一个小巧的linux命令集合)。他经常被用作测试工具,因为比较小
    • 创建Pod:

    kubectl apply -f pod-base.yaml

    • 查看Pod状况:

    kubectl get pod -n dev
    这里注意,要是1.18版本的k8s以后,它是不会重启的,因为这种只创建pod的方式,是没有deploy控制器的
    云原生-第一部分 - 图84

    • 通过describe查看内部的详情:

    此时已经运行起来了一个基本的Pod,虽然它暂时有问题
    kubectl describe pod pod-base -n dev
    云原生-第一部分 - 图85

    2.3 镜像拉取策略

    • 创建pod-imagepullpolicy.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-imagepullpolicy # 注意这里name的值必须要是用小写字母,使用大写字母会报错
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers:
      10. - name: nginx # 容器名称
      11. image: nginx:1.17.1 # 容器需要的镜像地址
      12. imagePullPolicy: Always # 用于设置镜像的拉取策略
      13. - name: busybox # 容器名称
      14. image: busybox:1.30 # 容器需要的镜像地址
    • imagePullPolicy:用于设置镜像拉取的策略,kubernetes支持配置三种拉取策略:

      • Always:总是从远程仓库拉取镜像(一直远程下载)。
      • IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就用本地,本地没有就使用远程下载)。
      • Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错(一直使用本地,没有就报错)。

    我们可以通过 kubectl explain pod.xx的方式来查询imagePullPolicy的值有哪些,以及怎么写:
    image-20220322034750773.png
    imagePullPolicy默认值说明:

    • 如果镜像tag为具体的版本号,默认策略是IfNotPresent。
    • 如果镜像tag为latest(最终版本),默认策略是Always。
    • 创建Pod:

    kubectl apply -f pod-imagepullpolicy.yaml

    • 查看Pod详情:

    kubectl describe pod pod-imagepullpolicy -n dev
    云原生-第一部分 - 图87

    2.4 启动命令

    在前面的案例中,一直有一个问题没有解决,就是busybox容器一直没有成功运行,那么到底是什么原因导致这个容器的故障的呢?
    原来busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command的配置。

    • 创建pod-command.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-command
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers:
      10. - name: nginx # 容器名称
      11. image: nginx:1.17.1 # 容器需要的镜像地址
      12. imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      13. - name: busybox # 容器名称
      14. image: busybox:1.30 # 容器需要的镜像地址
      15. command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done;"] #这里就是一个启动shell进程的命令,这个进程中有一个while死循环。这里就是启动这个容器后,执行这个命令

      command:用于在Pod中的容器初始化完毕之后执行一个命令。
      这里稍微解释下command中的命令的意思:

    • “/bin/sh”,”-c”:使用sh执行命令。

    • touch /tmp/hello.txt:创建一个/tmp/hello.txt的文件。
    • while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done:每隔3秒,向文件写入当前时间
    • 创建Pod:

    kubectl apply -f pod-command.yaml

    • 查看Pod状态:

    kubectl get pod pod-command -n dev
    云原生-第一部分 - 图88

    • 进入Pod中的busybox容器,查看文件内容:

    在容器中执行命令
    # kubectl exec -it pod的名称 -n 命名空间 -c 容器名称 /bin/sh
    #上述的容器名称要和yaml中的 name指定的容器名字一样
    kubectl exec -it pod-command -n dev -c busybox /bin/sh
    云原生-第一部分 - 图89
    特别说明:通过上面发现command已经可以完成启动命令和传递参数的功能,为什么还要提供一个args选项,用于传递参数?其实和Docker有点关系,kubernetes中的command和args两个参数其实是为了实现覆盖Dockerfile中的ENTRYPOINT的功能:

    • 如果command和args均没有写,那么用Dockerfile的配置。
    • 如果command写了,但是args没有写,那么Dockerfile默认的配置会被忽略,执行注入的command。
    • 如果command没有写,但是args写了,那么Dockerfile中配置的ENTRYPOINT命令会被执行,使用当前args的参数。
    • 如果command和args都写了,那么Dockerfile中的配置会被忽略,执行command并追加上args参数。

      2.5 环境变量

    • 创建pod-evn.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-env
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers:
      10. - name: nginx # 容器名称
      11. image: nginx:1.17.1 # 容器需要的镜像地址
      12. imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      13. - name: busybox # 容器名称
      14. image: busybox:1.30 # 容器需要的镜像地址
      15. command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done;"]
      16. env: #设置环境变量列表,
      17. - name: "username" #这个是key
      18. value: "admin" #这个是value
      19. - name: "password"
      20. value: "123456"

      env:环境变量,用于在Pod中的容器设置环境变量。就是我们linux中的那种环境变量

    • 创建Pod:

    kubectl create -f pod-env.yaml
    云原生-第一部分 - 图90

    • 进入容器,输出环境变量:

    kubectl exec -it pod-env -n dev -c busybox -it /bin/sh
    云原生-第一部分 - 图91
    此种方式不推荐,推荐将这些配置单独存储在配置文件中,后面介绍。

    2.6 端口设置

    • 查看ports支持的子选项:

    kubectl explain pod.spec.containers.ports
    云原生-第一部分 - 图92

    1. KIND: Pod
    2. VERSION: v1
    3. RESOURCE: ports <[]Object>
    4. FIELDS: #这里实收ports这个属性有如下五个子配置项可以配置
    5. name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的
    6. containerPort <integer> # 容器要监听的端口(0<x<65536),就是容器暴露的端口,pod的ip+容器的这个端口,就能访问到这个容器
    7. hostPort <integer> # 容器要在主机上公开的端口,就是通过主机的ip+这个端口可以访问到我们的这个容器,如果设置,主机上只能运行容器的一个副本(一般省略),因为主机的某个端口号只能被一个容器占据,所以也就只能运行容器的一个副本,否则端口冲突,因此我们一般都不会设置这个
    8. hostIP <string> # 要将外部端口绑定到的主机IP(一般省略),这个和hostPort一起使用的,因此他也被省略一般
    9. protocol <string> # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”
    • 创建pod-ports.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-ports
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers:
      10. - name: nginx # 容器名称
      11. image: nginx:1.17.1 # 容器需要的镜像地址
      12. imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      13. ports:
      14. - name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
      15. containerPort: 80 # 容器要监听的端口 (0~65536) 这个是最重要的
      16. protocol: TCP # 端口协议
    • 创建Pod:

    kubectl create -f pod-ports.yaml
    云原生-第一部分 - 图93
    访问Pod中的容器中的程序使用的是PodIp:containerPort。
    通过describe命令来就可以看到我们容器的的端口和pod的IP
    image-20220323032112183.png

    2.7 资源配额

    • 容器中的程序要运行,肯定会占用一定的资源,比如CPU和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量的资源,导致其他的容器无法运行。针对这种情况,kubernetes提供了对内存和CPU的资源进行配额的机制,这种机制主要通过resources选项实现,它有两个子选项:
      • limits:用于限制运行的容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启。
      • requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
    • 可以通过上面的两个选项设置资源的上下限。
    • 创建pod-resoures.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-resoures
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers:
      10. - name: nginx # 容器名称
      11. image: nginx:1.17.1 # 容器需要的镜像地址
      12. imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      13. ports: # 端口设置
      14. - name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
      15. containerPort: 80 # 容器要监听的端口 (0~65536)
      16. protocol: TCP # 端口协议
      17. resources: # 资源配额,目前只能限制CPU和内存的使用
      18. limits: # 限制资源的上限
      19. cpu: "2" # CPU限制,单位是core数
      20. memory: "10Gi" # 内存限制
      21. requests: # 限制资源的下限
      22. cpu: "1" # CPU限制,单位是core数
      23. memory: "10Mi" # 内存限制

      cpu:core数,可以为整数或小数。
      memory:内存大小,可以使用Gi、Mi、G、M等形式。

    • 创建Pod:

    kubectl create -f pod-resource.yaml
    云原生-第一部分 - 图95

    • 查看发现Pod运行正常:

    kubectl get pod pod-resoures -n dev
    云原生-第一部分 - 图96

    • 接下来,停止Pod:

      1. kubectl delete -f pod-resource.yaml
      2. 编辑Pod,修改resources.requests.memory的值为10Gi
      3. apiVersion: v1
      4. kind: Pod
      5. metadata:
      6. name: pod-resoures
      7. namespace: dev
      8. labels:
      9. user: xudaxian
      10. spec:
      11. containers:
      12. - name: nginx # 容器名称
      13. image: nginx:1.17.1 # 容器需要的镜像地址
      14. imagePullPolicy: IfNotPresent # 设置镜像拉取策略
      15. ports: # 端口设置
      16. - name: nginx-port # 端口名称,如果执行,必须保证name在Pod中是唯一的
      17. containerPort: 80 # 容器要监听的端口 (0~65536)
      18. protocol: TCP # 端口协议
      19. resources: # 资源配额
      20. limits: # 限制资源的上限
      21. cpu: "2" # CPU限制,单位是core数
      22. memory: "10Gi" # 内存限制
      23. requests: # 限制资源的下限
      24. cpu: "1" # CPU限制,单位是core数
      25. memory: "10Gi" # 内存限制
    • 再次启动Pod:

    kubectl create -f pod-resource.yaml

    • 查看Pod状态,发现Pod启动失败:

    kubectl get pod pod-resoures -n dev -o wide
    云原生-第一部分 - 图97

    • 查看Pod详情会发现,如下提示:

    kubectl describe pod pod-resoures -n dev
    云原生-第一部分 - 图98

    3 Pod的生命周期

    3.1 概述

    • 我们一般将Pod对象从创建到终止的这段时间范围称为Pod的生命周期,它主要包含下面的过程:
      • Pod创建过程。
      • 运行初始化容器(init container)过程。 这里初始化容器整个是一个名词,它是容器的一种,这个容器叫“初始化容器”
      • 运行主容器(main container): 这里主容器就是指的用户容器
        • 容器启动后 钩子(post start)函数执行过程、容器终止前钩子(pre stop)函数执行过程。
        • 容器的存活性探测(liveness probe)的钩子函数性过程、就绪性探测(readiness probe)的钩子函数性过程。
      • Pod终止过程。就是pod停止的一个过程

    云原生-第一部分 - 图99
    上图中 init container 就是我们的初始化容器 执行的过程,初始化容器的数量可多可少,可有可无的,没有啥限制,他一定在主容器之前运行
    post start 直到 pre stop都是我们的主容器运行的过程了。
    post start就是主容器启动后钩子,pre stop就是容器终止前钩子,我们像这些钩子函数传递参数,让这些钩子函数在特定的生命周期点执行相应的命令
    liveness prode 是存活性探测。
    readiness probe 是就绪型探测
    这两个探测就是探测服务是否正常运行的。

    • 在整个生命周期中,Pod会出现5种状态(相位),分别如下:

      • 挂起(Pending):API Server已经创建了Pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中。
      • 运行中(Running):Pod已经被调度到某节点,并且所有容器都已经被kubelet创建完成。
      • 成功(Succeeded):Pod中的所有容器都已经成功终止并且不会被重启。
      • 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态。
      • 未知(Unknown):API Server无法正常获取到Pod对象的状态信息,通常由于网络通信失败所导致。

        3.2 创建和终止

        3.2.1 Pod的创建过程

        云原生-第一部分 - 图100
    • ① 用户通过kubectl或其他的api客户端提交需要创建的Pod信息给API Server。

    • ② API Server开始生成Pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端。
    • ③ API Server开始反映etcd中的Pod对象的变化,其它组件使用watch机制来跟踪检查API Server上的变动。
    • ④ Scheduler发现有新的Pod对象要创建,开始为Pod分配主机并将结果信息更新至API Server。
    • ⑤ Node节点上的kubelet发现有Pod调度过来,尝试调度Docker启动容器,并将结果回送至API Server。
    • ⑥ API Server将接收到的Pod状态信息存入到etcd中。

      3.2.2 Pod的终止过程

    • ① 用户向API Server发送删除Pod对象的命令。

    • ② API Server中的Pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),宽限期就是给你时间做最后的处理,Pod被视为dead。
    • ③ 将Pod标记为terminating状态。这一步是直接着第一步的,②只是一个说明过程
    • ④ kubelete在监控到Pod对象转为terminating状态的同时启动Pod关闭过程。这里也是watch监听的
    • ⑤ 端点控制器监控到Pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除。这一步就是从service列表中把pod移除掉
    • ⑥ 如果当前Pod对象定义了preStop钩子处理器,则在其标记为terminating后会以同步的方式启动执行。
    • ⑦ Pod对象中的容器进程收到停止信号。
    • ⑧ 宽限期结束后,如果Pod中还存在运行的进程,那么Pod对象会收到立即终止的信号。
    • ⑨ kubectl请求API Server将此Pod资源的宽限期设置为0从而完成删除操作,此时Pod对于用户已经不可用了。

      3.3 初始化容器

    • 初始化容器是在Pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:

      • ① 初始化容器必须运行完成直至结束,如果某个初始化容器运行失败,那么kubernetes需要重启它直至成功完成。
      • ② 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行。
    • 初始化容器有很多的应用场景,下面列出的是最常见的几个:
      • 提供主容器镜像中不具备的工具程序或自定义代码。
      • 初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至其依赖的条件得到满足。
    • 接下来做一个案例,模拟下面这个需求:
      • 假设要以主容器来运行Nginx,但是要求在运行Nginx之前要能够连接上MySQL和Redis所在的服务器。
      • 为了简化测试,事先规定好MySQL和Redis所在的IP地址分别为192.168.18.103和192.168.18.104(注意,这两个IP都不能ping通,因为环境中没有这两个IP)。
    • 创建pod-initcontainer.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-initcontainer
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. resources:
      18. limits:
      19. cpu: "2"
      20. memory: "10Gi"
      21. requests:
      22. cpu: "1"
      23. memory: "10Mi"
      24. initContainers: # 初始化容器配置,它和containers是平级的,可以看到他这个只是名字多了一个init前缀
      25. - name: test-mysql
      26. image: busybox:1.30 #用了这个命令工具镜像,在这个镜像中执行如下command命令的测试。
      27. command: ["sh","-c","until ping 192.168.18.103 -c 1;do echo waiting for mysql ...;sleep 2;done;"]
      28. securityContext:
      29. privileged: true # 使用特权模式运行容器
      30. - name: test-redis
      31. image: busybox:1.30
      32. command: ["sh","-c","until ping 192.168.18.104 -c 1;do echo waiting for redis ...;sleep 2;done;"]
    • 创建Pod:

    kubectl create -f pod-initcontainer.yaml
    云原生-第一部分 - 图101

    • 查看Pod状态:

    kubectl describe pod pod-initcontainer -n dev
    云原生-第一部分 - 图102

    • 动态查看Pod:

    kubectl get pod pod-initcontainer -n dev -w
    #-w 的意思就是动态监听
    云原生-第一部分 - 图103

    • 接下来,新开一个shell,为当前服务器(192.168.18.100)新增两个IP,观察Pod的变化:

    ifconfig ens33:1 192.168.18.103 netmask 255.255.255.0 up
    ifconfig ens33:2 192.168.18.104 netmask 255.255.255.0 up
    云原生-第一部分 - 图104

    3.4 钩子函数

    • 钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。
    • kubernetes在主容器启动之后和停止之前提供了两个钩子函数:
      • post start:容器启动之后立即执行,如果这个钩子执行成功,容器正常启动,如果失败则会有一定的重启策略来重启容器。
      • pre stop:容器终止之前执行,执行完成之后容器将成功终止,在这个钩子函数完成之前会阻塞删除容器的操作。
    • 钩子处理器支持使用下面的三种方式定义动作:

      • ① exec命令:在容器内执行一次命令。这种用的最多

        1. lifecycle: #无论是容器启前钩子,还是终止后钩子,都在这个lifecycle标签下
        2. postStart: # 表示容器启动的钩子
        3. exec: #表示我们exec这个命令
        4. command: #命令的参数,他是一个数组,拼起来就是一个命令
        5. - cat
        6. - /tmp/healthy
        7. #cat /tmp/healthy 如果这个命令执行成功,则钩子成功执行,否则执行失败
      • ② tcpSocket:在当前容器尝试访问指定的socket。这种用的最少

        1. lifecycle: #和上边一样
        2. postStart:
        3. tcpSocket:
        4. port: 8080
      • ③ httpGet:在当前容器中向某url发起HTTP请求。这种用的一般

        1. lifecycle:
        2. postStart:
        3. httpGet:
        4. path: / #URI地址,就是我们的资源地址,比如/user/list
        5. port: 80 #端口号
        6. host: 192.168.109.100 #主机地址
        7. scheme: HTTP #支持的协议,http或者https
        8. #上述连起来就是:http://192.168.109.100:80/user/list
    • 接下来,以exec方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-hook-exec
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. resources:
      18. limits:
      19. cpu: "2"
      20. memory: "10Gi"
      21. requests:
      22. cpu: "1"
      23. memory: "10Mi"
      24. lifecycle: # 生命周期配置
      25. postStart: # 容器创建之后执行,如果失败会重启容器
      26. exec: # 在容器启动的时候,执行一条命令,修改掉Nginx的首页内容,同时注意这里没有把命令写成数组的形式
      27. command: ["/bin/sh","-c","echo postStart ... > /usr/share/nginx/html/index.html"]
      28. preStop: # 容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
      29. exec: # 在容器停止之前停止Nginx的服务
      30. command: ["/usr/sbin/nginx","-s","quit"]
    • 创建Pod:

    kubectl create -f pod-hook-exec.yaml
    云原生-第一部分 - 图105

    • 查看Pod:

    kubectl get pod pod-hook-exec -n dev -o wide
    云原生-第一部分 - 图106

    • 访问Pod:

    curl 10.244.1.11
    云原生-第一部分 - 图107

    3.5 容器探测

    3.5.1 概述

    • 容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,k8s就会认为该实例是一个问题实例,那么kubernetes就会把该问题实例“摘除”,不让它承担业务流量。
    • 就是加入我们的一个服务通过4个pod和它对应的service进行服务,如果某个pod意外停止了。此时如果service不知道这个pod已经不能提供服务了,还把请求分给他,就会出现问题,这就是我们要做探测的目的
    • kubernetes提供了两种探针来实现容器探测,分别是:
      • liveness probes:存活性探测,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启这个状态异常的容器。
      • readiness probes:就绪性探测,用于检测应用实例是否可以接受请求,如果不能,k8s不会转发流量。比如一个容器虽然启动成功了,但是正在做初始化配置,这个时候不能处理请求,此时k8s不会把请求转给他。

    livenessProbe:存活性探测,决定是否重启容器。
    readinessProbe:就绪性探测,决定是否将请求转发给容器。

    k8s在1.16版本之后新增了startupProbe探针,用于判断容器内应用程序是否已经启动。如果配置了startupProbe探针,就会先禁止其他的探针,直到startupProbe探针成功为止,一旦成功将不再进行探测。

    • 上面两种探针目前均支持三种探测方式:

      • ① exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常。

        1. ……
        2. livenessProbe: #如果是就绪型探针 这里改成readinessProbe就可以了
        3. exec:
        4. command:
        5. - cat
        6. - /tmp/healthy
        7. ……
      • ② tcpSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常。

        1. ……
        2. livenessProbe:
        3. tcpSocket:
        4. port: 8080
        5. ……
      • ③ httpGet:调用容器内web应用的URL,如果返回的状态码在200和399之前,则认为程序正常,否则不正常。

        1. ……
        2. livenessProbe:
        3. httpGet:
        4. path: / #URI地址
        5. port: 80 #端口号
        6. host: 127.0.0.1 #主机地址
        7. scheme: HTTP #支持的协议,http或者https
        8. ……

        3.5.2 exec方式

    • 创建pod-liveness-exec.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-liveness-exec
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. livenessProbe: # 存活性探针
      18. exec:
      19. command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令,必须失败,因为根本没有这个文件
    • 创建Pod:

    kubectl create -f pod-liveness-exec.yaml
    云原生-第一部分 - 图108

    • 查看Pod详情:

    kubectl describe pod pod-liveness-exec -n dev
    云原生-第一部分 - 图109

    • 观察上面的信息就会发现nginx容器启动之后就进行了健康检查。
    • 检查失败之后,容器被kill掉,然后尝试进行重启,这是重启策略的作用。
    • 稍等一会之后,再观察Pod的信息,就会看到RESTARTS不再是0,而是一直增长。
    • 查看Pod信息:

    kubectl get pod pod-liveness-exec -n dev
    云原生-第一部分 - 图110

    3.5.3 tcpSocket方式

    • 创建pod-liveness-tcpsocket.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-liveness-tcpsocket
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. livenessProbe: # 存活性探针
      18. tcpSocket:
      19. port: 8080 # 尝试访问8080端口,必须失败,因为Pod内部只有一个Nginx容器,而且只是监听了80端口
    • 创建Pod:

    kubectl create -f pod-liveness-tcpsocket.yaml
    云原生-第一部分 - 图111

    • 查看Pod详情:

    kubectl describe pod pod-liveness-tcpsocket -n dev
    云原生-第一部分 - 图112
    观察上面的信息,发现尝试访问8080端口,但是失败了
    稍等一会之后,再观察Pod的信息,就会看到RESTARTS不再是0,而是一直增长。

    • 查看Pod信息:

    kubectl get pod pod-liveness-tcpsocket -n dev
    云原生-第一部分 - 图113

    3.5.4 httpGet方式

    • 创建pod-liveness-httpget.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-liveness-httpget
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. livenessProbe: # 存活性探针
      18. httpGet: # 其实就是访问http://127.0.0.1:80/hello
      19. port: 80 # 端口号
      20. scheme: HTTP # 支持的协议,HTTP或HTTPS
      21. path: /hello # URI地址
      22. host: 127.0.0.1 # 主机地址
    • 创建Pod:

    kubectl create -f pod-liveness-httpget.yaml
    云原生-第一部分 - 图114

    • 查看Pod详情:

    kubectl describe pod pod-liveness-httpget -n dev
    云原生-第一部分 - 图115

    • 查看Pod信息:

    kubectl get pod pod-liveness-httpget -n dev
    云原生-第一部分 - 图116

    3.5.5 容器探测的补充

    • 上面已经使用了livenessProbe演示了三种探测方式,但是查看livenessProbe的子属性,会发现除了这三种方式,还有一些其他的配置。
      1. kubectl explain pod.spec.containers.livenessProbe
      云原生-第一部分 - 图117
      FIELDS:
      exec
      tcpSocket
      httpGet
      initialDelaySeconds # 容器启动后等待多少秒执行第一次探测
      timeoutSeconds # 探测超时时间。默认1秒,最小1秒
      periodSeconds # 执行探测的频率。默认是10秒,最小1秒
      failureThreshold # 连续探测失败多少次才被认定为失败。默认是3。最小值是1
      successThreshold # 连续探测成功多少次才被认定为成功。默认是1

    上述参数中 initialDelaySeconds和timeoutSeconds用的频率还算可以,但是剩下的那三个,我们很少回去调整

    3.6 重启策略

    • 在容器探测中,一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由Pod的重启策略决定的,Pod的重启策略有3种,分别如下:
      • Always:容器失效时,自动重启该容器,默认值。
      • OnFailure:容器终止运行且容器的退出码不为0时重启。
      • Never:不论状态如何,都不重启该容器。
    • 重启策略适用于Pod对象中的所有容器,首次需要重启的容器,将在其需要的时候立即进行重启,随后再次重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大的延迟时长。这样可以防止资源都浪费在重启成功概率很低的重启上
    • 创建pod-restart-policy.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-restart-policy
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. livenessProbe: # 存活性探测
      18. httpGet:
      19. port: 80
      20. path: /hello
      21. host: 127.0.0.1
      22. scheme: HTTP
      23. restartPolicy: Never # 重启策略,注意它是pod的一个子选项,不是容器的
    • 创建Pod:

    kubectl create -f pod-restart-policy.yaml
    云原生-第一部分 - 图118

    • 查看Pod详情,发现nginx容器启动失败:

    kubectl describe pod pod-restart-policy -n dev
    云原生-第一部分 - 图119
    多等一会,观察Pod的重试次数,发现一直是0,并未重启。

    • 查看Pod:

    kubectl get pod pod-restart-policy -n dev
    云原生-第一部分 - 图120

    4 Pod的调度

    4.1 概述

    • 在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式。
      • 自动调度:运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。
      • 定向调度:NodeName(根据node节点的名称)、NodeSelector(根据node上的标签来选择(env:test))。
      • 亲和性调度:NodeAffinity(node的亲和性)、PodAffinity(pod的亲和性)、PodAntiAffinity(pod的反亲和性)。他使用标签来实现
      • 污点(容忍)调度:Taints、Toleration。

    污点-容忍调度就是说,node说我有一个污点,你别来,pod说,没事我不怕,我能容忍我要去

    4.2 定向调度

    4.2.1 概述

    • 定向调度,指的是利用在Pod上声明的nodeName或nodeSelector,以此将Pod调度到期望的Node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过Pod运行失败而已。

      4.2.2 nodeName

    • nodeName用于强制约束将Pod调度到指定的name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。

    • node的名字可以通过kubectl get nodes 来获取
      image.png
    • 创建一个pod-nodename.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-nodename
      5. namespace: dev
      6. labels:
      7. user: xudaxian
      8. spec:
      9. containers: # 容器配置
      10. - name: nginx
      11. image: nginx:1.17.1
      12. imagePullPolicy: IfNotPresent
      13. ports:
      14. - name: nginx-port
      15. containerPort: 80
      16. protocol: TCP
      17. nodeName: k8s-node1 # 指定调度到k8s-node1节点上
    • 创建Pod:

    kubectl create -f pod-nodename.yaml
    云原生-第一部分 - 图122

    • 查看Pod:

    kubectl get pod pod-nodename -n dev -o wide
    云原生-第一部分 - 图123

    4.2.3 nodeSelector

    • nodeSelector用于将Pod调度到添加了指定标签的Node节点上,它是通过kubernetes的label-selector机制实现的,换言之,在Pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将Pod调度到目标节点,该匹配规则是强制约束。
    • 首先给node节点添加标签:

    kubectl label node k8s-node1 nodeevn=pro
    kubectl label node k8s-node2 nodeenv=test
    云原生-第一部分 - 图124

    • 创建pod-nodeselector.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: pod-nodeselector
      5. namespace: dev
      6. spec:
      7. containers: # 容器配置
      8. - name: nginx
      9. image: nginx:1.17.1
      10. imagePullPolicy: IfNotPresent
      11. ports:
      12. - name: nginx-port
      13. containerPort: 80
      14. protocol: TCP
      15. nodeSelector:
      16. nodeenv: pro # 指定调度到具有nodeenv=pro的Node节点上 这里 key=nodeenv,value=pro
    • 创建Pod:

    kubectl create -f pod-nodeselector.yaml
    云原生-第一部分 - 图125

    • 查看Pod:

    kubectl get pod pod-nodeselector -n dev -o wide
    云原生-第一部分 - 图126

    4.3 亲和性调度

    4.3.1 概述

    • 虽然定向调度的两种方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用的Node列表也不行,这就限制了它的使用场景。
    • 基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。它在nodeSelector的基础之上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使得调度更加灵活。
    • Affinity主要分为三类:
      • nodeAffinity(node亲和性):以Node为目标,解决Pod可以调度到那些Node的问题。
      • podAffinity(pod亲和性):以Pod为目标,解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。
      • podAntiAffinity(pod反亲和性):以Pod为目标,解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。

    关于亲和性和反亲和性的使用场景的说明:

    • 亲和性:如果两个应用频繁交互,那么就有必要利用亲和性让两个应用尽可能的靠近,这样可以较少因网络通信而带来的性能损耗。
    • 反亲和性:当应用采用多副本部署的时候,那么就有必要利用反亲和性让各个应用实例打散分布在各个Node上,这样可以提高服务的高可用性。

      4.3.2 nodeAffinity

    • 查看nodeAffinity的可选配置项:

    pod.spec.affinity.nodeAffinity
    requiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的所有规则才可以,相当于硬限制
    nodeSelectorTerms 节点选择列表
    matchFields 按节点字段列出的节点选择器要求列表
    matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
    key 键
    values 值
    operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
    preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
    preference 一个节点选择器项,与相应的权重相关联
    matchFields 按节点字段列出的节点选择器要求列表
    matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
    key 键
    values 值
    operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
    weight 倾向权重,在范围1-100。
    关系符的使用说明:
    - matchExpressions:
    - key: nodeenv # 匹配存在标签的key为nodeenv的节点
    operator: Exists
    - key: nodeenv # 匹配标签的key为nodeenv,且value是”xxx”或”yyy”的节点
    operator: In
    values: [“xxx”,”yyy”]
    - key: nodeenv # 匹配标签的key为nodeenv,且value大于”xxx”的节点
    operator: Gt
    values: “xxx”

    • 下面演示requiredDuringSchedulingIgnoredDuringExecution:
      • 创建pod-nodeaffinity-required.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-nodeaffinity-required
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
    requiredDuringSchedulingIgnoredDuringExecution: # Node节点必须满足指定的所有规则才可以,相当于硬规则,类似于定向调度
    nodeSelectorTerms: # 节点选择列表
    - matchExpressions:
    - key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是”xxx”或”yyy”的节点
    operator: In
    values:
    - “xxx”
    - “yyy”

    • 创建Pod:

    kubectl create -f pod-nodeaffinity-required.yaml
    云原生-第一部分 - 图127

    • 查看Pod状态(运行失败):

    kubectl get pod pod-nodeaffinity-required -n dev -o wide
    云原生-第一部分 - 图128

    • 查看Pod详情(发现调度失败,提示node选择失败):

    kubectl describe pod pod-nodeaffinity-required -n dev
    云原生-第一部分 - 图129

    • 删除Pod:

    kubectl delete -f pod-nodeaffinity-required.yaml
    云原生-第一部分 - 图130

    • 修改pod-nodeaffinity-required.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-nodeaffinity-required
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
    requiredDuringSchedulingIgnoredDuringExecution: # Node节点必须满足指定的所有规则才可以,相当于硬规则,类似于定向调度
    nodeSelectorTerms: # 节点选择列表
    - matchExpressions:
    - key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是”xxx”或”yyy”的节点
    operator: In
    values:
    - “pro”
    - “yyy”

    • 再次创建Pod:

    kubectl create -f pod-nodeaffinity-required.yaml
    云原生-第一部分 - 图131

    • 再次查看Pod:

    kubectl get pod pod-nodeaffinity-required -n dev -o wide
    云原生-第一部分 - 图132

    • 下面演示preferredDuringSchedulingIgnoredDuringExecution:
      • 创建pod-nodeaffinity-preferred.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-nodeaffinity-preferred
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
    preferredDuringSchedulingIgnoredDuringExecution: # 优先调度到满足指定的规则的Node,相当于软限制 (倾向)
    - preference: # 一个节点选择器项,与相应的权重相关联
    matchExpressions:
    - key: nodeenv
    operator: In
    values:
    - “xxx”
    - “yyy”
    weight: 1

    • 创建Pod:

    kubectl create -f pod-nodeaffinity-preferred.yaml
    云原生-第一部分 - 图133

    • 查看Pod:

    kubectl get pod pod-nodeaffinity-preferred -n dev -o wide
    云原生-第一部分 - 图134
    nodeAffinity的注意事项:

    • 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都满足,Pod才能运行在指定的Node上。
    • 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
    • 如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功。
    • 如果一个Pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的nodeAffinity的要求,则系统将忽略此变化。

      4.3.3 podAffinity

    • podAffinity主要实现以运行的Pod为参照,实现让新创建的Pod和参照的Pod在一个区域的功能。

    • PodAffinity的可选配置项:

    pod.spec.affinity.podAffinity
    requiredDuringSchedulingIgnoredDuringExecution 硬限制
    namespaces 指定参照pod的namespace
    topologyKey 指定调度作用域
    labelSelector 标签选择器
    matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
    key 键
    values 值
    operator 关系符 支持In, NotIn, Exists, DoesNotExist.
    matchLabels 指多个matchExpressions映射的内容
    preferredDuringSchedulingIgnoredDuringExecution 软限制
    podAffinityTerm 选项
    namespaces
    topologyKey
    labelSelector
    matchExpressions
    key 键
    values 值
    operator
    matchLabels
    weight 倾向权重,在范围1-1
    topologyKey用于指定调度的作用域,例如:

    • 如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围。
    • 如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分。
    • 演示requiredDuringSchedulingIgnoredDuringExecution。
    • 创建参照Pod过程:
      • 创建pod-podaffinity-target.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-podaffinity-target
    namespace: dev
    labels:
    podenv: pro # 设置标签
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    nodeName: k8s-node1 # 将目标pod定向调度到k8s-node1

    • 创建参照Pod:

    kubectl create -f pod-podaffinity-target.yaml
    云原生-第一部分 - 图135

    • 查看参照Pod:

    kubectl get pod pod-podaffinity-target -n dev -o wide
    云原生-第一部分 - 图136

    • 创建Pod过程:
      • 创建pod-podaffinity-requred.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-podaffinity-requred
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    affinity: # 亲和性配置
    podAffinity: # Pod亲和性
    requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
    - labelSelector:
    matchExpressions: # 该Pod必须和拥有标签podenv=xxx或者podenv=yyy的Pod在同一个Node上,显然没有这样的Pod
    - key: podenv
    operator: In
    values:
    - “xxx”
    - “yyy”
    topologyKey: kubernetes.io/hostname

    • 创建Pod:

    kubectl create -f pod-podaffinity-requred.yaml
    云原生-第一部分 - 图137

    • 查看Pod状态,发现没有运行:

    kubectl get pod pod-podaffinity-requred -n dev
    云原生-第一部分 - 图138

    • 查看Pod详情:

    kubectl get pod pod-podaffinity-requred -n dev
    云原生-第一部分 - 图139

    • 删除Pod:

    kubectl delete -f pod-podaffinity-requred.yaml
    云原生-第一部分 - 图140

    • 修改pod-podaffinity-requred.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-podaffinity-requred
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    affinity: # 亲和性配置
    podAffinity: # Pod亲和性
    requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
    - labelSelector:
    matchExpressions: # 该Pod必须和拥有标签podenv=xxx或者podenv=yyy的Pod在同一个Node上,显然没有这样的Pod
    - key: podenv
    operator: In
    values:
    - “pro”
    - “yyy”
    topologyKey: kubernetes.io/hostname

    • 再次创建Pod:

    kubectl create -f pod-podaffinity-requred.yaml
    云原生-第一部分 - 图141

    • 再次查看Pod:

    kubectl get pod pod-podaffinity-requred -n dev -o wide
    云原生-第一部分 - 图142

    4.3.4 podAntiAffinity

    • podAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod和参照的Pod不在一个区域的功能。
    • 其配置方式和podAffinity一样,此处不做详细解释。
    • 使用上个案例中的目标Pod:

    kubectl get pod -n dev -o wide
    云原生-第一部分 - 图143

    • 创建pod-podantiaffinity-requred.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-podantiaffinity-requred
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    affinity: # 亲和性配置
    podAntiAffinity: # Pod反亲和性
    requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
    - labelSelector:
    matchExpressions:
    - key: podenv
    operator: In
    values:
    - “pro”
    topologyKey: kubernetes.io/hostname

    • 创建Pod:

    kubectl create -f pod-podantiaffinity-requred.yaml
    云原生-第一部分 - 图144

    • 查看Pod:

    kubectl get pod -n dev -o wide
    云原生-第一部分 - 图145

    4.4 污点和容忍

    4.4.1 污点(Taints)

    • 前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否运行Pod调度过来。
    • Node被设置了污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
    • 污点的格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
      • PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可以调度。
      • NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但是不会影响当前Node上已经存在的Pod。
      • NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已经存在的Pod驱逐。

    云原生-第一部分 - 图146

    • 语法:
      • 设置污点:

    kubectl taint node xxx key=value:effect

    • 去除污点:

    kubectl taint node xxx key:effect-

    • 去除所有污点:

    kubectl taint node xxx key-

    • 查询所有节点的污点:

    wget -O jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
    chmod +x ./jq
    cp jq /usr/bin

    • 列出所有节点的污点方式一:

    kubectl get nodes -o json | jq ‘.items[].spec’

    • 列出所有节点的污点方式二:

    kubectl get nodes -o json | jq ‘.items[].spec.taints’

    • 查看指定节点上的污点:

    kubectl describe node 节点名称

    • 接下来,演示污点效果:
      • ① 准备节点k8s-node1(为了演示效果更加明显,暂时停止k8s-node2节点)。
      • ② 为k8s-node1节点设置一个污点:tag=xudaxian:PreferNoSchedule,然后创建Pod1(Pod1可以)。
      • ③ 修改k8s-node1节点的污点为:tag=xudaxian:NoSchedule,然后创建Pod2(Pod1可以正常运行,Pod2失败)。
      • ④ 修改k8s-node1节点的污点为:tag=xudaxian:NoExecute,然后创建Pod3(Pod1、Pod2、Pod3失败)。
    • 为k8s-node1设置污点(PreferNoSchedule):

    kubectl taint node k8s-node1 tag=xudaxian:PreferNoSchedule
    云原生-第一部分 - 图147

    • 创建Pod1:

    kubectl run pod1 —image=nginx:1.17.1 -n dev
    云原生-第一部分 - 图148

    • 查看Pod:

    kubectl get pod pod1 -n dev -o wide
    云原生-第一部分 - 图149

    • 为k8s-node1取消污点(PreferNoSchedule),并设置污点(NoSchedule):

    kubectl taint node k8s-node1 tag:PreferNoSchedule-
    kubectl taint node k8s-node1 tag=xudaxian:NoSchedule
    云原生-第一部分 - 图150

    • 创建Pod2:

    kubectl run pod2 —image=nginx:1.17.1 -n dev
    云原生-第一部分 - 图151

    • 查看Pod:

    kubectl get pod pod1 -n dev -o wide
    kubectl get pod pod2 -n dev -o wide
    云原生-第一部分 - 图152

    • 为k8s-node1取消污点(NoSchedule),并设置污点(NoExecute):

    kubectl taint node k8s-node1 tag:NoSchedule-
    kubectl taint node k8s-node1 tag=xudaxian:NoExecute
    云原生-第一部分 - 图153

    • 创建Pod3:

    kubectl run pod3 —image=nginx:1.17.1 -n dev
    云原生-第一部分 - 图154

    • 查看Pod:

    kubectl get pod pod1 -n dev -o wide
    kubectl get pod pod2 -n dev -o wide
    kubectl get pod pod3 -n dev -o wide
    云原生-第一部分 - 图155
    使用kubeadm搭建的集群,默认就会给Master节点添加一个污点标记,所以Pod就不会调度到Master节点上。

    4.4.2 容忍(Toleration)

    • 上面介绍了污点的作用,我们可以在Node上添加污点用来拒绝Pod调度上来,但是如果就是想让一个Pod调度到一个有污点的Node上去,这时候应该怎么做?这就需要使用到容忍。

    污点就是拒绝,容忍就是忽略,Node通过污点拒绝Pod调度上去,Pod通过容忍忽略拒绝。

    • 容忍的详细配置:

    kubectl explain pod.spec.tolerations
    ……
    FIELDS:
    key # 对应着要容忍的污点的键,空意味着匹配所有的键
    value # 对应着要容忍的污点的值
    operator # key-value的运算符,支持Equal和Exists(默认)
    effect # 对应污点的effect,空意味着匹配所有影响
    tolerationSeconds # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间
    当operator为Equal的时候,如果Node节点有多个Taint,那么Pod每个Taint都需要容忍才能部署上去。
    当operator为Exists的时候,有如下的三种写法:

    • 容忍指定的污点,污点带有指定的effect:
    • 容忍指定的污点,不考虑具体的effect:
    • 容忍一切污点(慎用):

      tolerations: # 容忍
      - key: “tag” # 要容忍的污点的key
      operator: Exists # 操作符
      effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
      tolerations: # 容忍
      - key: “tag” # 要容忍的污点的key
      operator: Exists # 操作符
      tolerations: # 容忍
      - operator: Exists # 操作符

    • 在上面的污点中,已经给k8s-node1打上了NoExecute的污点,此时Pod是调度不上去的,此时可以通过在Pod中添加容忍,将Pod调度上去。

    • 创建pod-toleration.yaml文件,内容如下:

    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-toleration
    namespace: dev
    spec:
    containers: # 容器配置
    - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - name: nginx-port
    containerPort: 80
    protocol: TCP
    tolerations: # 容忍
    - key: “tag” # 要容忍的污点的key
    operator: Equal # 操作符
    value: “xudaxian” # 要容忍的污点的value
    effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同

    • 创建Pod:

    kubectl create -f pod-toleration.yaml
    云原生-第一部分 - 图156

    • 查看Pod:

    kubectl get pod pod-toleration -n dev -o wide
    云原生-第一部分 - 图157

    5 临时容器

    5.1 概述

    • 临时容器是一种特殊的容器,该容器可以在现有的Pod中临时运行,以便完成我们发起的操作,比如故障排查。我们应该使用临时容器来检查服务,而不是用临时容器来构建应用程序。
    • Pod是kubernetes集群进行管理的最小单元,由于Pod是一次性且可以替换的,因此Pod一旦被创建,就无法将容器加入到Pod中。而且,我们通常使用Deployment来删除并替换Pod。但是,有的时候我们需要检查现有Pod的状态,比如对难以复现的故障进行排查。在这些场景中,可以在现有Pod中运行临时容器来检查其状态并运行任意命令。

      5.2 什么是临时容器?

    • 临时容器和其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启,因此不适合用来构建应用程序。临时容器使用和常规容器相同的ContainerSpec来描述,但是许多字段是不兼容或者不允许的。

      • 临时容器没有端口配置,因此像ports、livenessProbe、readinessProbe这样的字段是没有的。
      • Pod的资源分配是不可变的,因此resources这样的配置临时容器也是没有的。
      • ……
    • 临时容器是使用ephemeralcontainers来进行创建的,而不是直接添加到pod.spec中,所以是无法使用kubectl edit来添加一个临时容器。
    • 和常规容器一样,将临时容器添加到Pod后,不能更改或删除临时容器。

      5.3 临时容器的用途

    • 当由于容器奔溃或容器镜像不包含调试工具而导致kubectl exec无用的时候,临时容器对于交互式故障排查非常有用。

    • 比如,像distroless 镜像允许用户部署最小的容器镜像,从而减少攻击面并减少故障和漏洞的暴露。由于distroless 镜像不包含Shell或任何的调试工具,因此很难单独使用kubectl exec命令进行故障排查。
    • 使用临时容器的时候,启用进程名字空间共享 很有帮助,可以查看其他容器中的进程。

      5.4 临时容器的配置

    • 目前来说,临时容器默认是关闭的。

    • 查看临时容器是否开启:

    kubelet -h | grep EphemeralContainers
    云原生-第一部分 - 图158

    • 在每个节点(不管Master节点还是Node节点)修改kubectl的参数:

    注意:kubectl的启动文件的路径是/usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
    vim /etc/sysconfig/kubelet

    1. # 修改增加--feature-gates EphemeralContainers=true
    2. KUBELET_EXTRA_ARGS="--cgroup-driver=systemd --feature-gates EphemeralContainers=true"
    3. KUBE_PROXY_MODE="ipvs"
    4. vim /var/lib/kubelet/config.yaml
    5. apiVersion: kubelet.config.k8s.io/v1beta1
    6. authentication:
    7. anonymous:
    8. enabled: falsevim
    9. webhook:
    10. cacheTTL: 0s
    11. enabled: true
    12. x509:
    13. clientCAFile: /etc/kubernetes/pki/ca.crt
    14. authorization:
    15. mode: Webhook
    16. webhook:
    17. cacheAuthorizedTTL: 0s
    18. cacheUnauthorizedTTL: 0s
    19. clusterDNS:
    20. - 10.96.0.10
    21. clusterDomain: cluster.local
    22. cpuManagerReconcilePeriod: 0s
    23. evictionPressureTransitionPeriod: 0s
    24. fileCheckFrequency: 0s
    25. healthzBindAddress: 127.0.0.1
    26. healthzPort: 10248
    27. httpCheckFrequency: 0s
    28. imageMinimumGCAge: 0s
    29. kind: KubeletConfiguration
    30. nodeStatusReportFrequency: 0s
    31. nodeStatusUpdateFrequency: 0s
    32. rotateCertificates: true
    33. runtimeRequestTimeout: 0s
    34. staticPodPath: /etc/kubernetes/manifests
    35. streamingConnectionIdleTimeout: 0s
    36. syncFrequency: 0s
    37. volumeStatsAggPeriod: 0s
    38. # 修改部分
    39. featureGates:
    40. EphemeralContainers: true
    • 加载配置文件重启kubelet:

    systemctl daemon-reload
    systemctl stop kubelet
    systemctl start kubelet

    • 在Master节点修改kube-apiserver.yaml和kube-scheduler.yaml:

    vim /etc/kubernetes/manifests/kube-apiserver.yaml

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. annotations:
    5. kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.49.100:6443
    6. creationTimestamp: null
    7. labels:
    8. component: kube-apiserver
    9. tier: control-plane
    10. name: kube-apiserver
    11. namespace: kube-system
    12. spec:
    13. containers:
    14. - command:
    15. - kube-apiserver
    16. - --advertise-address=192.168.49.100
    17. - --allow-privileged=true
    18. - --authorization-mode=Node,RBAC
    19. - --client-ca-file=/etc/kubernetes/pki/ca.crt
    20. - --enable-admission-plugins=NodeRestriction
    21. - --enable-bootstrap-token-auth=true
    22. - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    23. - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    24. - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
    25. - --etcd-servers=https://127.0.0.1:2379
    26. - --insecure-port=0
    27. - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
    28. - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
    29. - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
    30. - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
    31. - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
    32. - --requestheader-allowed-names=front-proxy-client
    33. - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    34. - --requestheader-extra-headers-prefix=X-Remote-Extra-
    35. - --requestheader-group-headers=X-Remote-Group
    36. - --requestheader-username-headers=X-Remote-User
    37. - --secure-port=6443
    38. - --service-account-issuer=https://kubernetes.default.svc.cluster.local
    39. - --service-account-key-file=/etc/kubernetes/pki/sa.pub
    40. - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
    41. - --service-cluster-ip-range=10.96.0.0/12
    42. - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    43. - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    44. # 修改部分
    45. - --feature-gates=EphemeralContainers=true

    vim /etc/kubernetes/manifests/kube-scheduler.yaml

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. creationTimestamp: null
    5. labels:
    6. component: kube-scheduler
    7. tier: control-plane
    8. name: kube-scheduler
    9. namespace: kube-system
    10. spec:
    11. containers:
    12. - command:
    13. - kube-scheduler
    14. - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
    15. - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
    16. - --bind-address=127.0.0.1
    17. - --kubeconfig=/etc/kubernetes/scheduler.conf
    18. - --leader-elect=true
    19. # 修改部分
    20. - --feature-gates=EphemeralContainers=true

    5.5 使用临时容器在线debug

    • 创建一个nginx.yaml文件,内容如下:

      1. apiVersion: v1
      2. kind: Pod
      3. metadata:
      4. name: nginx
      5. spec:
      6. shareProcessNamespace: true # 这个配置非常重要,一定要配置
      7. containers:
      8. - name: nginx
      9. image: nginx:1.17.1
    • 创建Pod:

    kubectl apply -f nginx.yaml

    • 创建ec.json文件,内容如下(注意:name是Pod的名称):

      1. {
      2. "apiVersion": "v1",
      3. "kind": "EphemeralContainers",
      4. "metadata": {
      5. "name": "nginx"
      6. },
      7. "ephemeralContainers": [{
      8. "command": [
      9. "sh"
      10. ],
      11. "image": "busybox",
      12. "imagePullPolicy": "IfNotPresent",
      13. "name": "debugger",
      14. "stdin": true,
      15. "tty": true,
      16. "terminationMessagePolicy": "File"
      17. }]
      18. }
    • 使用下面的命令更新已经运行的容器:

    kubectl replace —raw /api/v1/namespaces/default/pods/nginx/ephemeralcontainers -f ec.json
    云原生-第一部分 - 图159

    • 使用如下的命令查看新创建的临时容器的状态:

    kubectl describe pod nginx
    云原生-第一部分 - 图160

    • 可以使用如下的命令连接临时容器:

    kubectl exec -it nginx -c debugger — sh
    kubectl attach -it nginx -c debugger
    云原生-第一部分 - 图161

    6 服务质量Qos

    6.1 概述

    • kubernetes创建Pod的时候就会指定QoS。
    • QoS分为如下的三类:

      • ① Guaranteed。
      • ② Burstable。
      • ③ BestEffort。

        6.2 Qos之Guaranteed

        6.2.1 概述

    • 对于 QoS 类为 Guaranteed 的 Pod:

      • Pod 中的每个容器,包含初始化容器,必须指定内存请求和内存限制,并且两者要相等。
      • Pod 中的每个容器,包含初始化容器,必须指定 CPU 请求和 CPU 限制,并且两者要相等。

        6.2.2 应用示例

    • 创建命名空间:

    kubectl create namespace qos-example

    • 创建qos-demo.yaml文件,内容如下:

    vim qos-demo.yaml

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: qos-demo
    5. namespace: qos-example
    6. spec:
    7. containers:
    8. - name: qos-demo-ctr
    9. image: nginx
    10. resources:
    11. limits:
    12. memory: "200Mi"
    13. cpu: "700m"
    14. requests:
    15. memory: "200Mi"
    16. cpu: "700m"
    • 创建Pod:

    kubectl create -f qos-demo.yaml

    • 查看Pod详情:

    kubectl get pod qos-demo -n qos-example -o yaml
    云原生-第一部分 - 图162

    • 删除Pod:

    kubectl delete -f qos-demo.yaml

    6.3 Qos之Burstable

    6.3.1 概述

    • 如果满足下面条件,将会指定 Pod 的 QoS 类为 Burstable:

      • Pod 不符合 Guaranteed QoS 类的标准。
      • Pod 中至少一个容器具有内存或 CPU 请求,但是值不相等。

        6.3.2 应用示例

    • 创建qos-demo-2.yaml文件,内容如下:

    vim qos-demo-2.yaml

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: qos-demo-2
    5. namespace: qos-example
    6. spec:
    7. containers:
    8. - name: qos-demo-2-ctr
    9. image: nginx
    10. resources:
    11. limits:
    12. memory: "200Mi"
    13. requests:
    14. memory: "100Mi"
    • 创建Pod:

    kubectl create -f qos-demo-2.yaml

    • 查看Pod详情:

    kubectl get pod qos-demo-2 -n qos-example -o yaml
    云原生-第一部分 - 图163

    • 删除Pod:

    kubectl delete -f qos-demo2.yaml

    6.4 Qos之BestEffort

    6.4.1 概述

    • 对于 QoS 类为 BestEffort 的 Pod,Pod 中的容器必须没有设置内存和 CPU 限制或请求。

      6.4.2 应用示例

    • 创建qos-demo-3.yaml文件,内容如下:

    vim qos-demo-3.yaml

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: qos-demo-3
    5. namespace: qos-example
    6. spec:
    7. containers:
    8. - name: qos-demo-3-ctr
    9. image: nginx
    • 创建Pod:

    kubectl create -f qos-demo-3.yaml

    • 查看Pod详情:

    kubectl get pod qos-demo-3 -n qos-example -o yaml
    云原生-第一部分 - 图164

    • 删除Pod:

    kubectl delete -f qos-demo3.yaml

    6.5 Qos的应用

    • 一旦出现OOM,kubernetes为了保证服务的可用,会先删除QoS为BestEffort的Pod,然后删除QoS为Burstable的Pod,最后删除QoS为Guaranteed 的Pod。

    主要介绍各种Pod控制器的详细使用。