1. 基本概念和架构

1.1 基本介绍

Kubernetes,简称K8S。一款开源、用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes 提供了应用部署、规划、更新、维护的一种机制。

传统的应用部署方式是通过插件或脚本来安装应用。这样做的缺点是应用的运行、配置、管理、所有生存周期将与当前操作系统绑定,这样做并不利于应用的升级更新/回滚等操作,当然也可以通过创建虚拟机的方式来实现某些功能,但是虚拟机非常重,并不利于可移植性。

新的方式是通过部署容器方式实现,每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。相对于虚拟机,容器能快速部署,由于容器与底层设施、机器文件系统解耦的,所以它能在不同云、不同版本操作系统间进行迁移。

容器占用资源少、部署快,每个应用可以被打包成一个容器镜像,每个应用与容器间成一对一关系也使容器有更大优势,使用容器可以在 build 或 release 的阶段,为应用创建容器镜像,因为每个应用不需要与其余的应用堆栈组合,也不依赖于生产环境基础结构,这使得从研发到测试、生产能提供一致环境。类似地,容器比虚拟机轻量、更 “透明”,这更便于监控和管理。

Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便对应用请求进行负载均衡。

在 Kubernetes 中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。

※ 发展经历

经典案例:

Infrastructure as a Service(阿里云)

Platform as a service(新浪云)

Software as a Service(Office 365)


集群资源管理器:

Apache MESOS(最新版本可以管理Kubernetes, 但增加软件层可能会增加故障节点)

  • Apache开源协议,分布式资源管理框架。
  • 2019年5月,Twitter弃用 MESOS,开始使用Kubernetes。

Docker Swarm(功能较少, 缺乏滚动更新+回滚等功能)

  • 2019年7月,阿里云宣布删除 Docker Swarm 选项。

Kubernetes(Google)

  • Google拥有10年容器化基础架构经验,Google拥有强大的内部的容器管理系统——Borg。
  • 采用GO语言,借鉴Borg的思想,开发了Kubernetes。
  • 特点:
    • 轻量级:消耗资源小
    • 开源
    • 弹性伸缩
    • 负载均衡:IPVS框架

  • K8S是谷歌在2014年开源的容器化集群管理系统。

  • 使用K8S进行容器化应用部署。

  • 使用K8S利于应用扩展。

  • K8S目标:让部署容器化应用更加简介、高效。

1.2 功能和架构

1.2.1 概述

Kubernetes 是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务。通过Kubernetes 能够进行应用的自动化部署和扩缩容。在 Kubernetes 中,会将组成应用的容器组合成一个逻辑单元以更易管理和发现。Kubernetes 积累了作为 Google 生产环境运行工作负载15年的经验,并吸收了来自于社区的最佳想法与实践。

1.2.2 功能

  • 自动装箱

    基于容器对应用运行环境的资源配置要求自动部署应用容器。

  • 自我修复(自愈能力)

    当容器失败时,会对容器进行重启。

    当所部署的 Node 节点有问题时,会对容器进行重新部署、重新调度。

    当容器未通过监控检查时,会关闭此容器。直到容器正常运行时,才会对外提供服务。

  • 水平扩展

    通过简单的命令、用户UI界面或基于CPU等资源的使用情况,对应用容器进行规模扩大或规模剪裁。

  • 服务发现

    用户无需使用额外的服务发现机制,就能基于 Kubernetes 自身能力实现服务发现和负载均衡。

  • 滚动更新

    可以根据应用的变化,对应用容器运行的应用,进行一次性或批量式更新。

  • 版本回退

    可以根据应用部署情况,对应用容器运行的应用,进行历史版本即时回退。

  • 密钥和配置管理

    在不需要重新构建镜像的情况下,可以部署和更新密钥和应用配置,类似热部署。

  • 存储编排

    启动实现存储系统挂载及应用,特别对有状态应用实现数据持久化非常重要。

    存储系统可以来自于本地目录、网络存储(NFS、Gluster、Ceph等)、公共云存储服务。

  • 批处理

    提供一次性任务,定时任务;满足批量数据处理和分析场景。

1.2.3 K8S集群架构组件

  • Master(主控节点)
    • API Server:集群的统一入口,以RESTful方式,交给etcd存储
    • Scheduler:节点调度,选择node节点应用部署
    • controller-manager:处理集群中常规后台任务,一个资源对应一个控制器
    • etcd:存储系统,用于保存集群相关数据
  • Node(工作节点)
    • Kubelet:Master派到Node节点代表,管理本机容器
    • Kube-Proxy:提供网络代理、负载均衡等操作

结构

① Master Node

K8S集群控制节点,对集群进行调度管理,接受集群外用户去集群操作请求;Master Node 由 API Server、Scheduler、ClusterState Strore(ETCD数据库)和 Controller ManagerServer所组成。

② Worker Node

集群工作节点,运行用户业务应用容器;

Worker Node 包含 kubelet、kube proxy 和 ContainerRuntime;

WorkNode

1.2.4 核心概念

① Pod
  • 最小部署单元
  • 一组容器的集合
  • 共享网络
  • 生命周期短暂
② Controller
  • 确保预期的Pod副本数量

  • 应用部署状态

    • 无状态应用部署

    • 有状态应用部署 (依赖存储 / IP唯一)

  • 确保所有的Node运行同一个Pod

  • 一次性任务、定时任务

③ Service
  • 定义一组Pod访问规则

访问规则

2. 搭建集群

2.1 搭建K8S环境平台规划

2.1.1 单Master集群

单Master集群

2.1.2 多Master集群

多Master集群

2.2 服务器硬件配置要求

2.2.1 测试环境

Master:CPU 2核、内存4GB、磁盘20GB

Node:CPU 4核、内存8GB、磁盘40GB

2.2.2 生产环境

Master:CPU 8核+、内存16GB+、磁盘100GB+

Node:CPU 16核+、内存64GB+、磁盘500GB+

2.3 搭建K8S集群部署方式

目前生产部署 Kubernetes 集群主要有两种方式:

  • Kubeadm
  • 二进制包
    • 从Github再在发行版的二进制包,手动部署每个组件,组成 Kubernetes 集群。
    • Kubeadm 降低部署门槛,但屏蔽了很多细节,遇到问题很难排查。如果想更容易控制,推荐使用二进制包部署 Kubernetes 集群,虽然手动部署麻烦点,期间可以学习很多工作原理,也利于后期维护。

2.4 Kubeadm部署方式

Kubeadm 是官方社区推出的一个用于快速部署 Kubernetes 集群的工具,这个工具能通过两条指令完成一个 Kubernetes 集群的部署:

  1. 创建一个Master节点:kubeadm init
  2. 将Node节点加入到当前集群中 kubeadm join <Master 节点IP和端口>

2.4.1 安装要求

在开始之前,部署 Kubernetes 集群及其需要满足以下几个条件:

  • 一台/多台机器,操作系统 CentOS7.x-86_x64
  • 硬件配置:内存2GB+,CPU 2+、硬盘30GB+
  • 集群中所有机器之间网络互通
  • 可以访问外网,需要拉取镜像
  • 禁止 swap 分区

2.4.2 最终目标

  1. 在所有节点上安装Docker和Kubeadm
  2. 部署 Kubernetes Master
  3. 部署容器网络插件
  4. 部署 Kubernetes Node,将节点加入Kubernetes 集群中
  5. 部署Dashboard Web 页面,可视化查看Kubernetes 资源

2.4.3 准备环境

① 虚拟机准备
角色IP
K8S-Master192.168.1.136
K8S-Node1192.168.1.135
K8S-Node2192.168.1.134
② 系统初始化

1、关闭防火墙

$ systemctl stop firewalld		# 临时
$ systemctl disable firewalld 	# 永久

2、关闭 selinux

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

3、关闭 swap

$ swapoff -a		# 临时
$ vim /etc/fstab	# 永久(删除swap相关行 /mnt/swap swap swap defaults 0 0 这一行或者注释掉这一行)
$ free -m				# 确认swap已经关闭(若swap行都显示 0 则表示关闭成功)

4、根据规划设置主机名

$ hostnamectl set-hostname <hostname>

5、在master添加hosts

$ cat >> /etc/hosts << EOF
192.168.1.136 k8smaster
192.168.1.135 k8snode1
192.168.1.134 k8snode2
EOF

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

$ cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system		# 生效

7、时间同步

$ yum install ntpdate -y
$ ntpdate time.windows.com

2.4.4 所有节点安装Docker/Kubeadm/Kubelet

Kubernetes 默认CRI(容器运行时)为Docker,因此先安装Docker。

① 安装Docker
$ wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
$ yum -y install docker-ce-18.06.1.ce-3.el7
$ systemctl enable docker && systemctl start docker
$ docker --version
Docker version 18.06.1-ce, build e68fc7a
$ cat > /etc/docker/daemon.json << EOF
{
	"registry-mirrors":["https://b9pmyelo.mirror.aliyuncs.com"]
}
EOF
② 添加阿里云YUM软件源
$ cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
③ 安装 Kubeadm、Kubelet、Kubectl

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

$ yum install -y kubelet-1.18.0 kubeadm-1.18.0 kubectl-1.18.0
$ systemctl enable kubelet

2.4.5 部署 Kubernetes Master

在Master中执行命令:

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

由于默认拉区镜像地址k8s.gcr.io国内无法访问,这里指定阿里云镜像仓库地址。

更换镜像源

使用kubectl工具(省略权限修饰符):

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

查看当前已加入节点:

kubectl get nodes

此时,各个节点状态为Not Ready。

2.4.6 加入 Kubernetes Node

在Node执行命令,向集群添加新节点,执行在kubeadm init输出的kubeadm join命令:

kubeadm join 192.168.1.136:6443 --token 3o69uv.pugf6vy6lq44wjrs \
    --discovery-token-ca-cert-hash sha256:3150067383e9af1021776982a4100823564e7ab0a604fe59137409159042b9ec

默认token有效期为24小时,当过期后,该token就不可用了。这时就需要重新创建token,操作如下:

kubeadm token create --print-join-command

2.4.7 部署CNI网络插件

wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

默认镜像地址无法访问,sed命令修改为docker hub 镜像仓库。(连接不上可查询IP并修改/etc/hosts文件)

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

kubectl get pods -n kube-system

部署CN网络插件

查看当前已加入节点:

kubectl get nodes

此时,各个节点状态为Ready。
Ready

2.4.8 测试 Kubernetes 集群

在Kubernetes集群中创建一个pod,验证是否正常运行:

kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get pod,svc

访问地址:http://Node_IP:Port

端口号

访问成功

3. Kubernetes集群命令行工具kubectl

3.1 概述

kubectl是Kubernetes集群的命令行工具没通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署

3.2 kubectl命令的语法

kubectl [command] [TYPE] [NAME] [flags]
  • command:指定要对资源执行的操作,例如create、get、describe、delete等

  • TYPE:指定资源类型,资源类型是大小写敏感的,开发者能够以单数、复数、缩略形式。

    kubectl get pod pod1
    kubectl get pods pod1
    kubectl get po pod1
    
  • NAME:指定资源的名称,名称也大小写敏感。如果省略名称,则会显示所有的资源。例如:

    kubectl get pods
    kubectl get nodes
    
  • flags:指定可选的参数。例如:可用-s或者-server参数指定 Kubernetes API server 的地址、端口。

3.3 kubectl --help 获取更多信息

所有帮助信息:

kubectl --help

获取更多信息

具体查看某个操作帮助信息:

kubectl [?] --help

3.4 kubectl 子命令使用分类

3.4.1 基础命令

命令解释
create通过文件或标准输入创建资源
expose将一个资源公开为一个新的Service
run在集群中运行一个特定的镜像
set在对象上设置特定的功能
get显示一个/多个资源
explain文档参考资料
edit使用默认的编辑器编辑一个资源
delete通过文件名、标准输入、资源名称、标签选择器来删除资源

3.4.2 部署和集群管理命令

① 部署命令
命令解释
rollout管理资源的发布
rolling-update给指定的复制控制器滚动更新
scale扩容/缩容Pod数量,Deployment、ReplicaSet、RC、Job
autoscale创建一个自动选择扩容/缩容并设置Pod数量
② 集群管理命令
命令解释
certificate修改证书资源
cluster-info显示集群信息
top显示资源(CPU/Memory/Storage)使用。需要Heapster运行。
cordon标记节点不可调度
uncordon标记节点可调度
drain驱逐节点上的应用,准备下线维护
taint修改节点taint标记

3.4.3 故障和调试命令

命令解释
describe显示特定资源或资源组的详细信息
logs在一个Pod中打印一个容器日志,如果Pod只有一个容器,容器名称是可选的
attach附加到一个运行的容器
exec执行命令到容器
port-forward转发一个/多个本地端口到一个Pod
proxy运行一个proxy到Kubernetes API Server
cp拷贝文件/目录到容器中
auth检查授权

3.4.5 其他命令

① 高级命令
命令解释
apply通过文件名/标准输入对资源应用配置
patch使用补丁修改、更新资源的字段
replace通过文件名/标准输入替换一个资源
convert不同的API版本之间转换配置文件
② 设置命令
命令解释
label更新资源上的标签
annotate更新资源上的注释
completion用于实现kubectl工具自动补全
③ 其他命令
命令解释
api-versions打印受支持的API版本
config修改kubeconfig文件(用于访问API,比如配置认证信息)
help所有命令帮助
plugin运行一个命令行插件
version打印客户端和服务版本信息

※ 常用命令举例

kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get pod,svc
kubectl get pods
kubectl get nodes
kubectl cs
kubectl apply -f [yaml文件本地/网络路径]

4. 资源编排:YAML

4.1 介绍

K8S集群中对资源管理和资源对象编排部署都可以通过声明样式(YAML)文件来解决,也就是可以把需要对资源对象操作编辑到YAML格式文件中,我们把这种文件叫做资源清单文件,通过kubectl命令直接使用资源清单文件就可以实现对大量的资源对象进行编排部署了。

4.2 编写格式

4.2.1 YAML介绍

YAML:仍是一种标记语言。为了强调这种语言以数据作为中心,而不是以标记语言作为重点。

YAML是一个可读性高,用来表达数据序列的格式。

4.2.2 YAML基本语法

  • 使用空格作为缩进,通过缩进表示层级关系
  • 缩进空格数目不重要,只要相同层级的元素左侧对齐即可
  • 一般缩进不允许使用Tab键,只允许使用空格
  • 一般开头缩进两个空格,字符 (冒号/逗号) 后缩进一个空格
  • 使用---表示新的yaml文件开始
  • 使用#标识注释,从这个字符一直到行尾,都会被解释器忽略

4.2.3 YAML文件组成

  • 控制器定义
  • 被控制对象
# 控制器定义
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
# 被控制对象
    template: 		# 开始定义规则
      metadata:
        labels:
          app: nginx
      spec:
       containers:
       - name: nginx
         image: nginx:latest
         ports:
         - containerPort: 80

4.2.4 YAML常用字段含义

字段含义
apiVersionAPI版本
kind资源类型
metadata资源元数据
spec资源规格
replicas副本数量
selector标签选择器
templatePod模板
metadataPod元数据
specPod规格
containers容器配置

4.2.5 快速编写YAML文件

  • 使用 kubectl create 命令生成 YAML文件
  • 使用 kubectl get 导出 YAML文件
① 使用kubectl create命令生成文件

举例:

产生yaml格式:

kubectl create deployment web --image=nginx -o yaml --dry-run

输出至具体文件:

kubectl create deployment web --image=nginx -o yaml --dry-run > m1.yaml
② 使用 kubectl get 导出 YAML文件

举例:

从已部署资源中导出yaml,并输出至文件:

kubectl get deploy nginx -o=yaml --export > m2.yaml

5. 核心概念

5.1 Pod

5.1.1 Pod 概述

Pod是K8S系统中可以创建、管理的最小单元,是资源对象模型中由用户创建/部署的最小资源对象模型,也是在K8S上运行容器化应用的资源对象,其他资源对象都是用来支撑/扩展Pod对象功能的,比如Controller对象是用来管控Pod对象的,Service/Ingress资源对象是用来暴露Pod引用对象的,Persistent Volumn 资源对象是用来为Pod提供存储等等,K8S不会直接处理容器,而是Pod。Pod是由一个/多个container组成。

Pod是 Kubernetes 的最重要概念,每一个Pod都有一个特殊的被称为 “根容器” 的 Pause容器。Pause容器对应的镜像属于 Kubernetes 平台的一部分,除了Pause容器,每个Pod还包含一个/多个紧密相关的用户业务容器。

Pod

Pod-Pause


特点总结:

  • 最小部署单元
  • 包含多个容器(一组容器的集合)
  • 一个Pod中的容器共享网络命名空间
  • Pod是短暂的

存在意义:

  • 创建容器使用Docker,一个Docker对应一个容器,一个容器有进程 (查看进程ps -ef),一个容器运行一个应用程序(单进程+守护进程)
  • Pod是多进程设计,运行多个应用程序(一个Pod有多个容器,一个容器里运行一个应用程序)
  • Pod的存在为了 亲密性应用 而产生
    • 两个应用之间进行交互
    • 网络之间调用 (127.0.0.1 / Socket)
    • 两个应用需要频繁调用

5.1.2 Pod 实现机制

Pod 两大实现机制:

  • 共享网络
  • 共享存储
① 共享网络

容器本身之间相互隔离

  • namespace
  • group

前提条件:容器在同一个namespace中


实现机制:

通过Pause容器,把其他业务容器加入Pause容器,让所有容器在同一个namespace中,实现网络共享。

  • 首先,创建Pause容器(独立IP、MAC、Port、namespace)
  • 其次,分别创建业务容器并加入Pause容器,使其处于同一namespace中
② 共享存储

Pod持久化操作:

  • 日志数据
  • 业务数据

引入数据卷概念Volumn,使用数据卷进行持久化存储。

数据卷


示例YAML:
示例yaml

5.1.3 Pod 镜像拉取策略

Pod镜像拉取策略 imagePullPolicy:

  • IfNotPresent:镜像在宿主机上不存在时才拉取(默认)

  • Always:每次创建Pod都会重新拉取一次镜像

  • Never:Pod永远不会主动拉取这个镜像

示例:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
   - name: nginx
     image: nginx:1.14
     imagePullPolicy: Always

# Pod镜像拉取策略-imagePullPolicy
# IfNotPresent:默认值,镜像在宿主机上不存在时才拉取
# Always:每次创建Pod都会重新拉取一次镜像
# Never:Pod永远不会主动拉取这个镜像

5.1.4 Pod 资源限制

资源限制

示例:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
   - name: db
     image: mysql
     env:
     - name: MYSQL_ROOT_PASSWORD
       value: "password"
      
     resources:
       requests:		# 资源请求(调度)
         memory: "64Mi"
         cpu: "250m"
      
      limits:		# 资源限制(最大)
        memory: "128Mi"
        cpu: "500m"

Pod和Container的资源请求和限制:

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory

CPU资源举例解释:设 1c=1000m,则0.25c=250m、0.5=500m


5.1.5 Pod 重启机制

Pod重启机制 restartPolicy:

  • Always:当容器终止退出后,总是重启容器(默认)

  • OnFailure:当容器异常退出(退出状态码非0)时,才重启容器

  • Never:当容器终止退出,从不重启容器

    • 批量一次性任务正常结束

示例:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: busybox
    image: busybox:1.28.4
    args: 
    - /bin/sh
    - -c
    - sleep 36000
  restartPolicy: Never

# Pod重启机制-restartPolicy
# Always:当容器终止退出后,总是重启容器,默认策略
# OnFailure:当容器异常退出(退出状态码非0)时,才重启容器
# Never:当容器终止退出,从不重启容器

5.1.6 Pod 健康检查

应用层面健康检查:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: liveness
    image: busybox
    args: 
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy
    livenessProbe: 
      exec:
       command:
       - cat
       - /temp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

健康检查策略:

  • livenessProbe(存活检查)

    如果检查失败,将杀死容器,根据Pod的restartPolicy来操作。

  • readinessPribe(就绪检查)

    如果检查失败,Kubernetes会把Pod从service endpoints中剔除。

Porbe支持的检查方法:

  • httpGet

    发送HTTP请求,返回200~400范围状态码为成功。

  • exec

    执行shell命令返回状态码是0为成功。

    • touch创建文件后,通过echo $?查看状态码为0;删除文件后,通过echo $?查看状态码为1。
  • tcpSocket

    发起TCP Socket建立成功

5.1.7 Pod 调度策略

Pod调度策略

① 创建Pod流程

创建Pod流程

master节点:

  • createpod – apiserver – etcd

  • scheduler – apiserver – etcd – 调度算法, 将Pod调度到Node节点

node节点:

  • kubelet – apiserver – 读取etcd拿到分配给当前节点pod – docker创建容器
② Pod调度

影响调度的属性:

  • Pod资源限制

    根据request找到足够node节点进行调度。

  • 节点选择器nodeSelector标签

    (对节点创建标签:kubectl label [node_name] env_role=dev

    apiVersion: v1
    kind: Pod
    metadata: 
      name: pod-example
    spec: 
      nodeSelector:
        env_role: dev
      containers:
      - name: nginx
        image: nginx:1.15
    

    示例

  • 节点亲和性nodeAffinity标签

    与nodeSelector基本一样,根据节点上标签的约束来决定Pod调度到哪些节点上,但功能更加强大。

    • 硬亲和性(requiredDuringSchedulingIgnoreDuringExecution):约束条件必须满足
    • 软亲和性(preferredDuringSchedulingIgnoreDuringExecution):尝试满足,不保证绝对满足
    apiVersion: v1
    kind: Pod
    metadata: 
      name: with-node-affinity
    spec: 
      affinity:
        nodeAffinity: 
        requiredDuringSchedulingIgnoreDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: evn_role
              operator: In
              values:
              - dev
              - test
        preferredDuringSchedulingIgnoreDuringExecution:
        - weight: 1
          preference:
           matchExpressions: 
            - key: group
              operator: In
              values:
              - otherprod
      containers:
      - name: webdemo
        image: nginx
    

    支持常用操作符operator:In、NotIn(反亲和性)、Exists、Gt、Lt、DoesNotExists(反亲和性)

  • 污点&污点容忍

    • 基本介绍:

      • nodeSelector、nodeAffinity:Pod调度到节点上,Pod属性,调度时实现。
      • Taint污点:节点不做普通分配调度,是节点属性
    • 场景:

      • 专用节点(IP范围)
      • 配置特定硬件节点
      • 基于Taint驱逐
    • 具体演示:

      • 查看节点污点情况:kubectl describe node [node_name] | grep Taint

        污点值(taint_value)有三个:

        • NoSchedule:一定不被调度
        • PreferNoSchedule:尽量不被调度
        • NoExecute:不会调度,并且驱逐已有Pod进其它Node
      • 为节点添加污点 (key, value自定义):kubectl taint node [node_name] key=value:[taint_value]

      • 为节点删除污点 (key, value自定义):kubectl taint node [node_name] key=value-

    • 污点容忍tolerations : 可能被调度 (部分YAML) :

      spec: 
        tolerations:
        - key: "key"
          operator: "Equal"
          value: "value"
          effect: "NoSchedule"
        containers:
        - name: webdemo
          image: nginx
      

5.2 Controller

5.2.1 Controller概述

  • 在集群上,管理和运行容器的对象

5.2.2 Pod和Controller关系

  • Pod通过Controller实现应用的运维

    (如:伸缩、滚动升级 等)

  • Pod与Controller之间通过 label标签 建立关系

Pod与Controller关系

5.2.3 Deployment控制器应用场景

  • 部署无状态应用
  • 管理Pod和ReplicaSet
  • 部署、滚动升级 等功能

应用场景:Web服务、微服务

5.2.4 Deployment:控制器部署无状态应用

  • 导出yaml文件:kubectl create deployment web --image=nginx --dry-run -o yaml > web.yaml

导出yaml文件

  • 使用yaml部署应用:kubectl apply -f web.yaml;并查看状态:kubectl get pods

部署应用

  • 对外发布(暴露对外端口号):kubectl expose deployment [app_name] --port=80 --type=NodePort --target-port=80 --name=[app_name]

对外发布

5.2.5 应用升级回滚

① 应用升级

升级应用至某指定版本:

kubectl set image deployment web [the_name]=[image_name]:[version_number]

# 举例
kubectl set image deployment web nginx=nginx:1.15

查看升级状态:

查看升级状态

② 应用回滚

查看应用升级历史版本:

查看历史版本


回滚至上一个版本:

kubectl rollout undo deployment web

查看回滚状态:

kubectl rollout status deployment web

查看回滚状态


回滚到指定版本(eg. 1):

kubectl rollout undo deployment web --to-revision=1

回滚指定版本

5.2.6 应用弹性伸缩

提供更多服务:

kubectl scale deployment web --replicas=10

提供更多服务

查看新增9个副本:

新增9个副本

5.2.7 StatefulSet:部署有状态应用

※ 无状态 & 有状态:
  • 无状态
    • 认为Pod都是一样的
    • 没有顺序要求
    • 不用考虑在哪个Node运行
    • 随意进行伸缩、扩展
  • 有状态
    • 考虑以上所有因素
    • 让每个Pod独立,保持Pod的启动顺序、唯一性
      • 唯一的网络标识符、持久存储
      • 有序(如:MySQL主从)
    • 无头Service
      • ClusterIP:none
※ 部署有状态应用

YAML示例:

apiVersion: v1
kind: Service
metadata: 
  name: nginx
  labels: 
   app: nginx
spec: 
  ports: 
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
   
---

apiVersion: apps/v1
kind: StatefulSet
metadata: 
  name: nginx-statefulset
  namespace: default
spec: 
  serviceName: nginx
  replicas: 3
  selector: 
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

部署有状态应用

查看Pod,有3个Pod,每个都是唯一名称:
查看Pod

查看创建的无头Service:
无头Service


Deployment 和 StatefulSet区别:有身份(唯一标识):

  • 根据主机名(每个Pod有唯一主机名) + 按照一定规则生成域名
    • 每个Pod有唯一主机名
    • 生成唯一域名:
      • 主机格式.service名称.名称空间.svc.cluster.local
      • 举例:nginx-statfulset-0.nginx.default.svc.cluster.local

5.2.8 DeamonSet:部署守护进程

部署守护进程DeamonSet

  • 在每个node节点上运行同一个Pod,新加入的node也同样运行这个Pod
  • 举例:每个node节点安装数据采集工具

YAML示例:

apiVersion: apps/v1
kind: DaemonSet
metadata: 
  name: ds-test
  labels: 
    app: filebeat
spec: 
  selector:
    matchLabels: 
      app: filebeat
  template: 
    metadata:
      labels:
        app: filebeat
    spec:
      containers:
      - name: logs
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts: 
        - name: varlog
          mountPath: /tmp/log
      volumes:
      - name: varlog
        hostPath: 
          path: /var/log

创建成功:

创建成功

创建成功

进入其中一个Pod (kubectl exec -it [pod_name] bash),查看日志文件:

查看日志文件

5.2.9 Job:部署一次任务

YAML示例:

apiVersion: batch/v1
kind: Job
metadata: 
  name: pi
spec: 
  template: 
    spec: 
      containers:
      - name: pi
        image: perl
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4   # 失败后尝试重试次数

创建成功 (kubectl create -f pi.yaml):

创建成功

计算完成:

计算完成

通过日志(kubectl logs pi-gxztq),查看计算结果:

日志查看计算结果

通过删除K8S中缓存的yaml文件,移除一次性任务:

移除一次性任务

5.2.10 CronJob:部署定时任务

YAML示例:

apiVersion: batch/v1beta1
kind: CronJob
metadata: 
  name: hello
spec: 
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args: 
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

创建成功:

创建成功

已成功运行一次:

运行一次

通过日志,查看运行结果:

通过日志查看结果

每隔一段时间执行一次,出现一个Pod (Complated):
间隔出现

删除K8S缓存yaml配置,移除定时任务:

移除任务

5.3 Service

5.3.1 存在意义

  • Pod的IP不固定,Service可防止Pod失联(服务发现)

    服务发现

  • 定义一组Pod访问策略(负载均衡)

    负载均衡

5.3.2 Pod和Service关系

根据 label 和 selector 标签建立关联

建立关联

通过Service实现Pod的负载均衡,每个Service拥有一个 虚拟IP(Virtual IP),借助Service访问一组Pod通过虚拟IP访问。

负载均衡

5.3.3 常用Service类型

  • ClusterIP:集群内部访问

  • NodePort:对外访问应用使用

  • LoadBalancer:对外访问应用使用 / 用于公有云

  • ExternalName:将服务映射到 DNS 名称,而非典型的选择器(需追加属性externalName)

    type: ExternalName
    externalName: [external_name]
    
① ClusterIP

使用kubectl expose deployment web --port=80 --target-port=80 --dry-run -o yaml > service.yaml导出web的svc(Service)配置,并进行修改:

ClusterIP

使用命令kubectl apply -f service.yaml执行(若已自动生成svc, 可手动删除替换),创建Service:

Service

从其他节点内部访问curl [ip_address]

节点访问

② NodePort

更改名称并更改类型为NodePort:

NodePort

创建成功,检查类型:

检查类型

内部访问成功:

内部访问成功

外部访问成功:
外部访问成功

③ LoadBalancer

Node在内网进行部署,外网一般不能访问到:

  • 找到一台可以进行外网访问机器,安装nginx,反向代理
    • 手动把可以访问到的节点添加到nginx里
  • LoadBalancer:公有云 → 负载均衡、控制器

5.4 配置管理

5.4.1 Secret

作用:加密数据存在etcd中,让Pod容器以挂载 变量/Volume(数据卷) 方式进行访问

场景:凭证

  • base64编码 (echo -n [target_str] | base64)

  1. 创建Secrete加密数据(用户名密码已用base64加密, 命令kubectl create -f secret.yaml

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      username: YWRtaW4=
      password: MWYyZDFlMmU2N2Rm
    

    创建secret

    创建secret

  2. 以变量形式挂载到Pod容器中

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec: 
      containers:
      - name: nginx
        image: nginx
        env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: SECRET_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password
    

    变量形式

    变量形式

  3. 以Volume(数据卷)形式挂载到Pod容器中

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec: 
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
          readOnly: true
      volumes: 
      - name: foo
        secret: 
          secretName: mysecret
    

    Volume

    Volume

5.4.2 ConfigMap

作用:存储不加密数据到etcd中,让Pod以变量或Volume数据卷形式挂载到容器中

场景:配置文件 (IP / 端口号 / 用户名)


  1. 创建配置文件(如:Redis配置文件)

    redis.host=127.0.0.1
    redis.port=6379
    redis.password=123456
    
  2. 创建configMap (命令:kubectl create configmap redis-config --from-file=redis.properties)

    创建

    查看所有kubectl get configmapkubectl get cm,查看详细信息kubectl describe cm redis-config

    查看所有

  3. 以Volume挂载到Pod容器(cm.yaml)

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec: 
      containers:
      - name: busybox
        image: busybox
        command: [ "/bin/sh","-c","cat /etc/config/redis.properties" ]
        volumeMounts:
        - name: config-volume
          mountPath: "/etc/config"
      volumes: 
      - name: config-volume
        configMap: 
          name: redis-config
      restartPolicy: Never
    

    挂载

    挂载

  4. 以变量形式挂载到Pod容器(myconfig.yaml)

    ① 创建yaml,声明变量信息 configmap 创建 kubectl apply -f myconfig.yaml

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: myconfig
      namespace: default
    data:
      special.level: info
      special.type: hello
    

    变量形式挂载

    ② 以变量挂载(config-var.yaml)

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec: 
      containers:
      - name: busybox
        image: busybox
        command: [ "/bin/sh","-c","echo $(LEVEL) $(TYPE)" ]
        env:
        - name: LEVEL
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.level
        - name: TYPE
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.type
      restartPolicy: Never
    

    创建

    通过日志查看输出:

    查看输出

5.5. 集群安全机制

5.5.1 概述

  • 访问K8S集群资源(Pod/Service/Controller)时,需要经过三个步骤完成具体操作
    • 第一步:认证
    • 第二步:鉴权(授权)
    • 第三步:准入控制
  • 进行访问时,过程中需要经过 API Server 做统一协调。
    • 访问过程中,需要证书 / token / 用户名+密码。
    • 访问Pod需要serviceAccount。
① 认证:传输安全
  • 传输安全:对外不暴露8080端口,只能内部访问,对外使用端口6443
  • 认证:客户端身份认证常用方式:
    • https证书认证:基于CA证书
    • http token:通过token识别用户
    • http基本认证:用户名+密码认证
② 鉴权 (授权)
  • 基于RBAC鉴权操作
  • 基于角色访问控制
③ 准入控制
  • 进入准入控制器的列表,如果列表中有请求内容就通过,如果没有就拒绝

5.5.2 RBAC介绍

基于角色的访问控制

  • 角色
    • role:特定命名空间访问权限(查看命名空间:kubectl get ns
    • clusterRole:所有命名空间控制
  • 角色绑定
    • roleBingding:角色绑定到主体
    • clusterRoleBingding:集群的角色绑定到主体
  • 主体:
    • user:用户
    • group:用户组
    • serviceAccount:服务账号(Pod访问)

RBAC

5.5.3 RBAC鉴权

  1. 创建命名空间

    RBAC鉴权

  2. 在新创建的命名空间创建一个Pod

    创建Pod

  3. 创建角色(rbac-role.yaml)

    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      namespace: roledemo
      name: pod-reader
    rules: 
    - apiGroups: [""] # "" indicates the core API group
      resources: ["pods"]
      verbs: ["get", "watch", "list"]
    

    创建角色

    查看角色:

    查看角色

  4. 创建角色绑定(rbac-rolebinding.yaml)

    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      namespace: roledemo
      name: read-pods
    subjects: 
    - kind: User
      name: lucy  # Name is case sensive
      apiGroup: rbac.authorization.k8s.io
    roleRef: 
      kind: Role  # This must be Role or ClusterRole
      name: pod-reader  # This must match the name of the Role or ClusterRole you wish to bind to
      apiGroup: rbac.authorization.k8s.io
    

    绑定

    绑定

  5. 使用证书识别身份(二进制搭建集群)

    cat > lucy-csr.json << EOF
    {
      "CN": "lucy",
      "host": [],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
          "C": "CN",
          "L": "BeiJing",
          "ST": "BeiJing"
        }
      ]
    }
    EOF
    
    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes lucy-csr.json | cfssljson -bare lucy
    
    kubectl config set-cluster kubernetes \
      --certificate-authority=ca.pem \
      --embed-certs=true \
      --server=https://192.168.1.1.36:6443 \
      --kubeconfig=lucy-kubeconfig
    
    kubectl config set-certificate lucy \
      --client-key=lucy-key.pem \
      --client.certificate=lucy.pem \
      --embed-certs=true \
      --kubeconfig=lucy-kubeconfig
    
    kubectl config set-context lucy \
      --cluster=kubernetes \
      --user=lucy \
      --kubeconfig=lucy-kubeconfig
      
    kubectl config use-context default --kubeconfig=lucy-kubeconfig
    

5.6 Ingress

5.6.1 概述

  • 把端口号对外暴露,通过IP+端口号进行访问

    • 使用Service中NodePort实现
  • NodePort缺陷

    • 在每个节点上都会启动端口,在访问时通过任何节点IP+暴露端口号进行访问
    • 意味着每个端口只能使用一次,一个端口对应一个项目
    • 实际访问中都是使用域名,根据不同域名跳转到不同端口服务中
  • Ingress和Pod关系

    • pod和ingress通过service关联
    • ingress作为统一入口,由service关联一组pod
  • Ingress工作流程

    Ingress

  • 使用Ingress

    • 部署Ingress Controller (官方维护nginx控制器→实现部署)
    • 创建Ingress规则

Ingress

5.6.2 对外暴露应用

  1. 创建nginx-pod应用,对外暴露端口使用NodePort

    创建

    创建

  2. 部署 Ingress Controller

    apiVersion: v1
    kind: Namespace
    metadata:
      name: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    
    ---
    
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: nginx-configuration
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    
    ---
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: tcp-services
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    
    ---
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: udp-services
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: nginx-ingress-serviceaccount
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRole
    metadata:
      name: nginx-ingress-clusterrole
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    rules:
      - apiGroups:
          - ""
        resources:
          - configmaps
          - endpoints
          - nodes
          - pods
          - secrets
        verbs:
          - list
          - watch
      - apiGroups:
          - ""
        resources:
          - nodes
        verbs:
          - get
      - apiGroups:
          - ""
        resources:
          - services
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - ""
        resources:
          - events
        verbs:
          - create
          - patch
      - apiGroups:
          - "extensions"
          - "networking.k8s.io"
        resources:
          - ingresses
        verbs:
          - get
          - list
          - watch
      - apiGroups:
          - "extensions"
          - "networking.k8s.io"
        resources:
          - ingresses/status
        verbs:
          - update
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: Role
    metadata:
      name: nginx-ingress-role
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    rules:
      - apiGroups:
          - ""
        resources:
          - configmaps
          - pods
          - secrets
          - namespaces
        verbs:
          - get
      - apiGroups:
          - ""
        resources:
          - configmaps
        resourceNames:
          # Defaults to "<election-id>-<ingress-class>"
          # Here: "<ingress-controller-leader>-<nginx>"
          # This has to be adapted if you change either parameter
          # when launching the nginx-ingress-controller.
          - "ingress-controller-leader-nginx"
        verbs:
          - get
          - update
      - apiGroups:
          - ""
        resources:
          - configmaps
        verbs:
          - create
      - apiGroups:
          - ""
        resources:
          - endpoints
        verbs:
          - get
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: RoleBinding
    metadata:
      name: nginx-ingress-role-nisa-binding
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: nginx-ingress-role
    subjects:
      - kind: ServiceAccount
        name: nginx-ingress-serviceaccount
        namespace: ingress-nginx
    
    ---
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:
      name: nginx-ingress-clusterrole-nisa-binding
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: nginx-ingress-clusterrole
    subjects:
      - kind: ServiceAccount
        name: nginx-ingress-serviceaccount
        namespace: ingress-nginx
    
    ---
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-ingress-controller
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    spec:
      replicas: 1
      selector:
        matchLabels:
          app.kubernetes.io/name: ingress-nginx
          app.kubernetes.io/part-of: ingress-nginx
      template:
        metadata:
          labels:
            app.kubernetes.io/name: ingress-nginx
            app.kubernetes.io/part-of: ingress-nginx
          annotations:
            prometheus.io/port: "10254"
            prometheus.io/scrape: "true"
        spec:
          hostNetwork: true
          # wait up to five minutes for the drain of connections
          terminationGracePeriodSeconds: 300
          serviceAccountName: nginx-ingress-serviceaccount
          nodeSelector:
            kubernetes.io/os: linux
          containers:
            - name: nginx-ingress-controller
              image: lizhenliang/nginx-ingress-controller:0.30.0
              args:
                - /nginx-ingress-controller
                - --configmap=$(POD_NAMESPACE)/nginx-configuration
                - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
                - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
                - --publish-service=$(POD_NAMESPACE)/ingress-nginx
                - --annotations-prefix=nginx.ingress.kubernetes.io
              securityContext:
                allowPrivilegeEscalation: true
                capabilities:
                  drop:
                    - ALL
                  add:
                    - NET_BIND_SERVICE
                # www-data -> 101
                runAsUser: 101
              env:
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
                - name: https
                  containerPort: 443
                  protocol: TCP
              livenessProbe:
                failureThreshold: 3
                httpGet:
                  path: /healthz
                  port: 10254
                  scheme: HTTP
                initialDelaySeconds: 10
                periodSeconds: 10
                successThreshold: 1
                timeoutSeconds: 10
              readinessProbe:
                failureThreshold: 3
                httpGet:
                  path: /healthz
                  port: 10254
                  scheme: HTTP
                periodSeconds: 10
                successThreshold: 1
                timeoutSeconds: 10
              lifecycle:
                preStop:
                  exec:
                    command:
                      - /wait-shutdown
    
    ---
    
    apiVersion: v1
    kind: LimitRange
    metadata:
      name: ingress-nginx
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    spec:
      limits:
      - min:
          memory: 90Mi
          cpu: 100m
        type: Container
    

    创建

    创建

  3. 创建ingress规则

    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
      name: example-ingress
    spec:
      rules:
      - host: example.ingredemo.com
        http:
          paths:
          - path: /
            backend:
              serviceName: web
              servicePort: 80
    

    创建

    查看

    创建

  4. 在windows系统host文件(C:\Windows\System32\drivers\etc\hosts)中添加域名访问规则后,并访问

    hosts

    访问

5.7 Helm

5.7.1 引入

此前部署应用的过程:

  • 编写yaml文件
    • Deployment
    • Service
    • Ingress

如果使用此前的方式 部署 单一应用/少数服务的应用 比较合适。

部署微服务项目,可能会有几十个服务,每个服务都有一套yaml文件,需要维护大量yaml文件,版本管理不方便。


使用helm可以解决哪些问题?

  • 使用helm可将这些yaml文件作为一个整体管理
  • 实现yaml文件高效复用
  • 实现应用级别的版本管理

5.7.2 概述

Helm介绍:

Helm是个Kubernetes的包管理工具,就想Linux下的包管理器,如yum/apt等,可以很方便的将之前打包好的yaml文件部署到kubernetes上。

Helm3个重要概念:

  1. Helm:一个命令行客户端工具,主要用于Kubernetes应用chart的创建、打包、发布、管理。
  2. Chart:应用描述,一系列用于描述ks资源相关文件的集合。可以理解为yaml的集合。
  3. Release:基于Chart的部署实体,一个chart被Helm运行后将会生成对应的一个release;将在k8s中创建出真实运行的资源对象。可实现应用级别的版本管理。

Helm v3 (2019) 版本变化:架构变化

  • 删除Tiller (此前版本通过Tiller操作集群)
  • release可以在不同的命名空间重用
  • 将chart推送到docker仓库中

V3版本架构

5.7.3 安装和配置仓库

① helm安装
  1. 下载 Helm 压缩文件,上传到Linux系统中。
  2. 解压helmy压缩文件,将解压后的helm目录复制到/usr/bin目录下。

安装+配置仓库

② 配置helm仓库
  1. 添加仓库 helm repo add [repo_name] [repo_address]

    helm repo add stable http://mirror.azune.cn/kubernetes/charts/
    helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
    helm repo update
    
  2. 查看仓库 helm repo list

    查看仓库

  3. 更新仓库地址 helm repo update

    更新仓库地址

  4. 删除仓库 helm repo remove [repo_name]

5.7.4 快速部署应用

  1. 使用命令搜索应用: helm search repo [key_word]

    搜索应用

  2. 根据搜索内容选择安装:helm install [name] [app_name]

    根据搜索内容安装

    查看安装后状态:helm listhelm status [name]

    查看安装后状态


使其可从外部访问:修改Service的yaml文件,type改为NodePort(kubectl edit svc ui-weave-scope

NodePort

NodePort

NodePort

5.7.5 自定义chart部署

自定义Chart完成部署:

  • 使用命令创建chart:helm create chart [chart_name]

    自定义部署

    自定义部署

    • Chart.yaml:当前chart属性配置信息
    • templates:编写yaml文件放到这个目录
    • values.yaml:yaml文件可以使用全局变量
  • 在templates文件夹创建两个yaml文件

    • deployment.yaml(kubectl create deployment web1 --image=nginx --dry-run -o yaml > deployment.yaml
    • service.yaml(先创建再删除/根据模板修改:kubectl expose deployment web1 --port=80 --target-port=80 --type=NodePort --dry-run -o yaml > service.yaml
  • 安装mychart

    安装mychart

  • 应用升级:helm upgrade [chart_name] [chart_dir]

5.7.6 chart模板使用

实现yaml高效复用:

  • 通过传递参数,动态渲染模板,yaml内容动态传入参数生成
  • 在 chart 有 values.yaml 文件,定义 yaml 文件全局变量
  • yaml文件大体上有这些地方不同
    • image
    • tag
    • label
    • port
    • replicas

  • 在values.yaml定义变量和值

定义变量和值

  • 在具体yaml文件,获取使用定义变量值(格式:{{ .Values.[var_name]}};动态生成名称:{{ .Release.Name}}

    • deployment.yaml

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: {{ .Release.Name}}-dep
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: {{ .Values.label}}
        strategy: {}
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: {{ .Values.label}}
          spec:
            containers:
            - image: {{ .Values.image}}
              name: nginx
              resources: {}
      status: {}
      
    • service.yaml:

      apiVersion: v1
      kind: Service
      metadata:
        name: {{ .Release.Name}}-svc
      spec:
        ports:
        - port: {{ .Values.port}}
          protocol: TCP
          targetPort: 80
        selector:
          app: {{ .Values.label}}
        type: NodePort
      status:
        loadBalancer: {}
      

使用helm install --dry-run web2 mychart/命令查看生成yaml,验证结果,直接安装也可验证:

直接安装验证

5.8 持久化存储

5.8.1 nfs网络存储

数据卷 empty dir,为本地存储。pod重启后,数据不存在,需对数据持久化存储。

nfs 网络存储

pod重启数据,仍存在。


  1. 找一台服务器作为nfs服务端,安装nfs(yum install -y nfs-utils

  2. nfs服务端设置挂载路径:

    vim /etc/exports
    # 写入信息:
     *(rw,no_root_squash)
    
  3. nfs服务端创建挂载路径:

    mkdir /data/nfs
    
  4. 在k8s集群node节点安装nfs(yum install -y nfs-utils

  5. 在nfs服务端启动nfs服务

  6. 在ks集群部署应用使用nfs网络持久存储

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-dep1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - image: nginx
            name: nginx
            volumeMounts:
            - name: wwwroot
              mountPath: /usr/share/nginx/html
            ports:
            - containerPort: 80
          volumes: 
          - name: wwwroot
            nfs:
              server: 192.168.1.139	# nfs server ip
              path: /data/nfs
    
  7. 在nfs服务器目录创建文件后,进入容器 寻找/usr/share/nginx/html目录内主页信息,查看文件是否读取。

  8. 创建Service-NodePort,在浏览器查看主页信息是否发生变化。

5.8.2 PV和PVC

PV

持久化存储,对存储资源进行抽象,对外提供一个可以调用的地方。

PVC

用于调用,不需要关心内部实现细节。


实现流程

  1. 应用部署

  2. 定义PVC(绑定PV)

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-dep1
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx
            volumeMounts:
            - name: wwwroot
              mountPath: /usr/share/nginx/html
            ports:
            - containerPort: 80
          volumes:
          - name: wwwroot
            persistentVolumeClaim:
              claimName: my-pvc
    
    ---
    
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-pvc
    spec:
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 5Gi
    
  3. 定义PV(数据存储服务器IP、路径;定义存储容量storage、匹配模式accessModes-RW)

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: my-pv
    spec:
      capacity:
        storage: 5Gi
      accessModes:
        - ReadWriteMany
      nfs:
        path: /k8s/nfs
        server: 192.168.1.139
    

6. 集群监控平台系统

6.1 监控指标和方案

6.1.1 监控指标

  • 集群监控
    • 节点资源利用率
    • 节点数
    • 运行pods
  • Pod监控
    • 容器指标
    • 应用程序

6.1.2 监控平台搭建方案

组件: prometheus + Grafana

  • prometheus

    • 开源
    • 监控、报警
    • 内置数据库
    • 以HTTP协议周期性抓取被监控组件状态
    • 无需复杂的集成过程,使用http接口接入即可
  • Grafana:

    • 开源

    • 数据分析、可视化工具

    • 支持多种数据源

架构

6.2 搭建监控平台

  • 部署守护进程 node.exporter.yaml(kubectl create -f node-exporter.yaml):

    ---
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-exporter
      namespace: kube-system
      labels:
        k8s-app: node-exporter
    spec:
      selector:
        matchLabels:
          k8s-app: node-exporter
      template:
        metadata:
          labels:
            k8s-app: node-exporter
        spec:
          containers:
          - image: prom/node-exporter
            name: node-exporter
            ports:
            - containerPort: 9100
              protocol: TCP
              name: http
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        k8s-app: node-exporter
      name: node-exporter
      namespace: kube-system
    spec:
      ports:
      - name: http
        port: 9100
        nodePort: 31672
        protocol: TCP
      type: NodePort
      selector:
        k8s-app: node-exporter
    
  • 部署 prometheus:

    • rbac-setup.yaml(kubectl create -f rbac-setup.yaml):

      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRole
      metadata:
        name: prometheus
      rules:
      - apiGroups: [""]
        resources:
        - nodes
        - nodes/proxy
        - services
        - endpoints
        - pods
        verbs: ["get", "list", "watch"]
      - apiGroups:
        - extensions
        resources:
        - ingresses
        verbs: ["get", "list", "watch"]
      - nonResourceURLs: ["/metrics"]
        verbs: ["get"]
      ---
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: prometheus
        namespace: kube-system
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRoleBinding
      metadata:
        name: prometheus
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: ClusterRole
        name: prometheus
      subjects:
      - kind: ServiceAccount
        name: prometheus
        namespace: kube-system
      
    • configMap.yaml(kubectl create -f configMap.yaml):

      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: prometheus-config
        namespace: kube-system
      data:
        prometheus.yml: |
          global:
            scrape_interval:     15s
            evaluation_interval: 15s
          scrape_configs:
      
          - job_name: 'kubernetes-apiservers'
            kubernetes_sd_configs:
            - role: endpoints
            scheme: https
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            relabel_configs:
            - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
              action: keep
              regex: default;kubernetes;https
      
          - job_name: 'kubernetes-nodes'
            kubernetes_sd_configs:
            - role: node
            scheme: https
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            relabel_configs:
            - action: labelmap
              regex: __meta_kubernetes_node_label_(.+)
            - target_label: __address__
              replacement: kubernetes.default.svc:443
            - source_labels: [__meta_kubernetes_node_name]
              regex: (.+)
              target_label: __metrics_path__
              replacement: /api/v1/nodes/${1}/proxy/metrics
      
          - job_name: 'kubernetes-cadvisor'
            kubernetes_sd_configs:
            - role: node
            scheme: https
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            relabel_configs:
            - action: labelmap
              regex: __meta_kubernetes_node_label_(.+)
            - target_label: __address__
              replacement: kubernetes.default.svc:443
            - source_labels: [__meta_kubernetes_node_name]
              regex: (.+)
              target_label: __metrics_path__
              replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
      
          - job_name: 'kubernetes-service-endpoints'
            kubernetes_sd_configs:
            - role: endpoints
            relabel_configs:
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
              action: keep
              regex: true
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
              action: replace
              target_label: __scheme__
              regex: (https?)
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
              action: replace
              target_label: __metrics_path__
              regex: (.+)
            - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
              action: replace
              target_label: __address__
              regex: ([^:]+)(?::\d+)?;(\d+)
              replacement: $1:$2
            - action: labelmap
              regex: __meta_kubernetes_service_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              action: replace
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_service_name]
              action: replace
              target_label: kubernetes_name
      
          - job_name: 'kubernetes-services'
            kubernetes_sd_configs:
            - role: service
            metrics_path: /probe
            params:
              module: [http_2xx]
            relabel_configs:
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
              action: keep
              regex: true
            - source_labels: [__address__]
              target_label: __param_target
            - target_label: __address__
              replacement: blackbox-exporter.example.com:9115
            - source_labels: [__param_target]
              target_label: instance
            - action: labelmap
              regex: __meta_kubernetes_service_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_service_name]
              target_label: kubernetes_name
      
          - job_name: 'kubernetes-ingresses'
            kubernetes_sd_configs:
            - role: ingress
            relabel_configs:
            - source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe]
              action: keep
              regex: true
            - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path]
              regex: (.+);(.+);(.+)
              replacement: ${1}://${2}${3}
              target_label: __param_target
            - target_label: __address__
              replacement: blackbox-exporter.example.com:9115
            - source_labels: [__param_target]
              target_label: instance
            - action: labelmap
              regex: __meta_kubernetes_ingress_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_ingress_name]
              target_label: kubernetes_name
      
          - job_name: 'kubernetes-pods'
            kubernetes_sd_configs:
            - role: pod
            relabel_configs:
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
              action: keep
              regex: true
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
              action: replace
              target_label: __metrics_path__
              regex: (.+)
            - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
              action: replace
              regex: ([^:]+)(?::\d+)?;(\d+)
              replacement: $1:$2
              target_label: __address__
            - action: labelmap
              regex: __meta_kubernetes_pod_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              action: replace
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_pod_name]
              action: replace
              target_label: kubernetes_pod_name
      
    • prometheus.deploy.yaml(kubectl create -f prometheus.deploy.yaml):

      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          name: prometheus-deployment
        name: prometheus
        namespace: kube-system
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: prometheus
        template:
          metadata:
            labels:
              app: prometheus
          spec:
            containers:
            - image: prom/prometheus:v2.0.0
              name: prometheus
              command:
              - "/bin/prometheus"
              args:
              - "--config.file=/etc/prometheus/prometheus.yml"
              - "--storage.tsdb.path=/prometheus"
              - "--storage.tsdb.retention=24h"
              ports:
              - containerPort: 9090
                protocol: TCP
              volumeMounts:
              - mountPath: "/prometheus"
                name: data
              - mountPath: "/etc/prometheus"
                name: config-volume
              resources:
                requests:
                  cpu: 100m
                  memory: 100Mi
                limits:
                  cpu: 500m
                  memory: 2500Mi
            serviceAccountName: prometheus    
            volumes:
            - name: data
              emptyDir: {}
            - name: config-volume
              configMap:
                name: prometheus-config   
      
    • prometheus.svc.yaml(kubectl create -f prometheus.svc.yaml):

      ---
      kind: Service
      apiVersion: v1
      metadata:
        labels:
          app: prometheus
        name: prometheus
        namespace: kube-system
      spec:
        type: NodePort
        ports:
        - port: 9090
          targetPort: 9090
          nodePort: 30003
        selector:
          app: prometheus
      

    查看

  • 部署 Grafana

    • grafana-deploy.yaml(kubectl create -f grafana-deploy.yaml

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: grafana-core
        namespace: kube-system
        labels:
          app: grafana
          component: core
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: grafana
            component: core
        template:
          metadata:
            labels:
              app: grafana
              component: core
          spec:
            containers:
            - image: grafana/grafana:4.2.0
              name: grafana-core
              imagePullPolicy: IfNotPresent
              # env:
              resources:
                # keep request = limit to keep this container in guaranteed class
                limits:
                  cpu: 100m
                  memory: 100Mi
                requests:
                  cpu: 100m
                  memory: 100Mi
              env:
                # The following env variables set up basic auth twith the default admin user and admin password.
                - name: GF_AUTH_BASIC_ENABLED
                  value: "true"
                - name: GF_AUTH_ANONYMOUS_ENABLED
                  value: "false"
                # - name: GF_AUTH_ANONYMOUS_ORG_ROLE
                #   value: Admin
                # does not really work, because of template variables in exported dashboards:
                # - name: GF_DASHBOARDS_JSON_ENABLED
                #   value: "true"
              readinessProbe:
                httpGet:
                  path: /login
                  port: 3000
                # initialDelaySeconds: 30
                # timeoutSeconds: 1
              volumeMounts:
              - name: grafana-persistent-storage
                mountPath: /var
            volumes:
            - name: grafana-persistent-storage
              emptyDir: {}
      
    • grafana-ing.yaml(kubectl create -f grafana-ing.yaml

      apiVersion: extensions/v1beta1
      kind: Ingress
      metadata:
         name: grafana
         namespace: kube-system
      spec:
         rules:
         - host: k8s.grafana
           http:
             paths:
             - path: /
               backend:
                serviceName: grafana
                servicePort: 3000
      
      
    • grafana-svc.yaml(kubectl create -f grafana-svc.yaml

      apiVersion: v1
      kind: Service
      metadata:
        name: grafana
        namespace: kube-system
        labels:
          app: grafana
          component: core
      spec:
        type: NodePort
        ports:
          - port: 3000
        selector:
          app: grafana
          component: core
      
      
  • 打开Grafana,配置数据源,导入显示模板。

    • 通过查看端口号访问:查看端口号

    • 默认用户名和密码为admin:

      默认用户名密码

    • 配置数据源,使用 Prometheus:

      配置数据源

      配置时 http://CLUSTER-IP:Port需填入:prometheus的CLUSTER-IP与Port:

      查看端口号
      填写信息

    • 设置显示模板:

      设置显示模板

      显示模板

      设置模板

      设置模板

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐