k8s 2019总结

2019-09-04
k8s 相当于就是对 Docker的一层封装,在上面扩展了一些功能。
最好的就是扩容,缩容,设置资源,。。。

1、*组件介绍

点击打开:k8s 结构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
MESOS  APACHE  分布式资源管理框架   2019-5  Twitter  》 Kubernetes

Docker Swarm 2019-07 阿里云宣布 Docker Swarm 剔除

Kubernetes Google 10年容器化基础架构 borg GO 语言 Borg
特点:
轻量级:消耗资源小
开源
弹性伸缩
负载均衡:IPVS

适合人群:软件工程师 测试工程师 运维工程师 软件架构师 项目经理


介绍说明: 前世今生 KUbernetes 框架 KUbernetes关键字含义

基础概念: 什么是 Pod 控制器类型 K8S 网络通讯模式

Kubernetes: 构建 K8S 集群

资源清单:资源 掌握资源清单的语法 编写 Pod 掌握 Pod 的生命周期***

Pod 控制器:掌握各种控制器的特点以及使用定义方式

服务发现:掌握 SVC 原理及其构建方式

存储:掌握多种存储类型的特点 并且能够在不同环境中选择合适的存储方案(有自己的简介)

调度器:掌握调度器原理 能够根据要求把Pod 定义到想要的节点运行

安全:集群的认证 鉴权 访问控制 原理及其流程

HELM:Linux yum 掌握 HELM 原理 HELM 模板自定义 HELM 部署一些常用插件

运维:修改Kubeadm 达到证书可用期限为 10年 能够构建高可用的 Kubernetes 集群



服务分类
有状态服务:DBMS
无状态服务:LVS APACHE

高可用集群副本数据最好是 >= 3 奇数个


APISERVER:所有服务访问统一入口
CrontrollerManager:维持副本期望数目
Scheduler::负责介绍任务,选择合适的节点进行分配任务
ETCD:键值对数据库 储存K8S集群所有重要信息(持久化)
Kubelet:直接跟容器引擎交互实现容器的生命周期管理
Kube-proxy:负责写入规则至 IPTABLES、IPVS 实现服务映射访问的
COREDNS:可以为集群中的SVC创建一个域名IP的对应关系解析
DASHBOARD:给 K8S 集群提供一个 B/S 结构访问体系
INGRESS CONTROLLER:官方只能实现四层代理,INGRESS 可以实现七层代理
FEDERATION:提供一个可以跨集群中心多K8S统一管理功能
PROMETHEUS:提供K8S集群的监控能力
ELK:提供 K8S 集群日志统一分析介入平台

2、基础概念

*pod 概念

pod 是 k8s 最小运行单元。一个 pod 可以有多个容器,多个容器共享 pod的网络,挂载。
所以,一个 pod 不能有端口冲突的容器,否则 pod不能正常启动。

pod 有两种:1、自主式Pod。2、控制器管理的Pod

pod 分类:

1
2
3
4
5
+ ReplicationController & ReplicaSet & Deployment
> HPA (HorizontalPodAutoScale)
+ StatefullSet
+ DaemonSet
+ Job, Cronjob

1、ReplicationController < ReplicaSet < Deployment

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、HPA:根据 cpu 自动扩容,缩容
在这里插入图片描述
在这里插入图片描述

3、StatefulSet:有序(先启动 mysql,再启动 java)
在这里插入图片描述
4、运行一个副本节点
在这里插入图片描述
5、定时执行、批处理。(感觉没什么用)
在这里插入图片描述

网络通讯方式

在这里插入图片描述

在这里插入图片描述

1、同一 pod 多个容器通讯:同一个pod 内的容器,都是共享同一个网络,所以是 lo 本地回环网络。
2、各pod之间通讯,使用扁平化网络
3、pod 与 service 之间,通过防火墙规则配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

flannel

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

命令式 VS 声明式

1、命令式(how):给钱 -> 买东西(路应该怎么走,左拐,右拐,。。。)
2、声明式(what):给钱 -> 买东西(无需关心路怎么走)

在这里插入图片描述

“命令式”有时也被称作“指令式”,好像有一个是台湾翻译,我不确定是哪一个了。“命令式”强调的是how,如果你是在写命令式的程序,那么你将step-by-step的告诉计算机如何完成一项工作,大多数的程序都是这样的。

在命令式场景下,计算机是不具备“智能”的,只是很机械的完成你交代的事情,至于结果如何,要看你的水平。就像上图,煎饼好吃不好吃、会不会糊锅,要看厨师对原料和火候的掌握。

在这里插入图片描述
“声明式”有时也被成为“描述式”或者“申明式”,为告诉计算机你想要什么,“声明”你想要的what,由计算机自己去设计执行路径,需要计算机或者是“运行时”具备一定的“智能”。在这种情况下,计算机显然不会实现所有你想要的what,用专业术语说就是“非图灵完备”,但是针对特定的任务,“声明式”要远比“命令式”方便,其实大多数声明式语言都是针对特定任务的领域专用语言,即DSL。当然,随着深度学习驱动的自动编程技术的兴起,我们有可能在任意领域使用“声明式”完成任务,这是后话,暂且不提。

最常见的声明式语言就是SQL—— 告诉计算机你想要的结果集,SQL语言的运行时,即数据库,帮你设计获取这个结果集的执行路径,并返回结果集。众所周知,使用SQL语言获取数据,要比自行编写处理过程去获取数据容易的多。
在这里插入图片描述

3、*集群安装

系统初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 设置系统主机名以及 Host 文件的相互解析
hostnamectl set-hostname k8s-master01

# 安装依赖包
yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget vim net-tools git

# 设置防火墙为 Iptables 并设置空规则
systemctl stop firewalld && systemctl disable firewalld

yum -y install iptables-services && systemctl start iptables && systemctl enable iptables && iptables -F && service iptables save

# 关闭 SELINUX
swapoff -a && sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

# 调整内核参数,对于 K8S
cat > kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv4.tcp_tw_recycle=0
vm.swappiness=0 # 禁止使用 swap 空间,只有当系统 OOM 时才允许使用它
vm.overcommit_memory=1 # 不检查物理内存是否够用
vm.panic_on_oom=0 # 开启 OOM
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1
net.netfilter.nf_conntrack_max=2310720
EOF

cp kubernetes.conf /etc/sysctl.d/kubernetes.conf

sysctl -p /etc/sysctl.d/kubernetes.conf

# 调整系统时区
timedatectl set-timezone Asia/Shanghai ; # 设置系统时区为 中国/上海
timedatectl set-local-rtc 0 ; # 将当前的 UTC 时间写入硬件时钟
systemctl restart rsyslog ; # 重启依赖于系统时间的服务
systemctl restart crond

# 关闭系统不需要服务
systemctl stop postfix && systemctl disable postfix

# 设置 rsyslogd 和 systemd journald
mkdir /var/log/journal # 持久化保存日志的目录
mkdir /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/99-prophet.conf <<EOF
[Journal]
# 持久化保存到磁盘
Storage=persistent

# 压缩历史日志
Compress=yes

SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000

# 最大占用空间 10G
SystemMaxUse=10G

# 单日志文件最大 200M
SystemMaxFileSize=200M

# 日志保存时间 2 周
MaxRetentionSec=2week

# 不将日志转发到
syslog ForwardToSyslog=no
EOF

systemctl restart systemd-journald


# 升级系统内核为 4.44
# CentOS 7.x 系统自带的 3.10.x 内核存在一些 Bugs,导致运行的 Docker、Kubernetes 不稳定,
# 例如: rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

# 安装完成后检查/boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装 一次!
yum --enablerepo=elrepo-kernel install -y kernel-lt

# 设置开机从新内核启动
grub2-set-default 'CentOS Linux (4.4.189-1.el7.elrepo.x86_64) 7 (Core)'

Kubeadm 部署安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# kube-proxy开启ipvs的前置条件
modprobe br_netfilter

cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF

chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4

# 安装 Docker 软件
yum install -y yum-utils device-mapper-persistent-data lvm2

yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

yum update -y && yum install -y docker-ce

## 创建 /etc/docker 目录
mkdir /etc/docker

# 配置 daemon
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
}
}
EOF

mkdir -p /etc/systemd/system/docker.service.d

# 重启docker服务
systemctl daemon-reload && systemctl restart docker && systemctl enable docker

# 安装 Kubeadm (主从配置)
cat > /etc/yum.repos.d/kubernetes.repo << 'EOF'
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

yum -y install kubeadm-1.15.1 kubectl-1.15.1 kubelet-1.15.1

systemctl enable kubelet.service


# 初始化主节点
kubeadm config print init-defaults > kubeadm-config.yaml
localAPIEndpoint:
advertiseAddress: 192.168.66.10
kubernetesVersion: v1.15.1
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: 10.96.0.0/12
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
featureGates:
SupportIPVSProxyMode: true
mode: ipvs
kubeadm init --config=kubeadm-config.yaml --experimental-upload-certs | tee kubeadm-init.log

# 加入主节点以及其余工作节点
执行安装日志中的加入命令即可

# 部署网络
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

4、资源清单:yml 文件

2021-04-09 09:40:21
在这里插入图片描述

k8s 中的资源种类

1、什么是资源?
k8s 中所有的内容都抽象为资源,资源实例化之后,叫做对象。

2、k8s 中存在哪些资源?
在这里插入图片描述
在这里插入图片描述

yml 常用字段

1、必须存在的属性
在这里插入图片描述

2、主要对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、额外的参数项
在这里插入图片描述

*示例:yml 配置

资源清单格式

1
2
3
4
5
6
7
8
9
10
# 可以使用 kubectl api-versions 获取当前 k8s 版本上所有的 apiVersion 版本信息( 每个版本可能不同 ) 
apiVersion: group/apiversion # 如果没有给定 group 名称,那么默认为 core。
kind: #资源类别
metadata: #资源元数据
name
namespace
lables
annotations # 主要目的是方便用户阅读查找
spec: # 期望的状态(disired state)
status:# 当前状态,本字段有 Kubernetes 自身维护,用户不能去定义

资源清单的常用命令

获取apiversion

1
2
3
4
5
6
7
[root@k8s-master01 ~]# kubectl api-versions
admissionregistration.k8s.io/v1beta1
apiextensions.k8s.io/v1beta1
apiregistration.k8s.io/v1
apiregistration.k8s.io/v1beta1
apps/v1
......(以下省略)

获取资源的apiVersion

1
2
3
4
5
6
7
8
[root@k8s-master01 ~]# kubectl explain pod
KIND: Pod
VERSION: v1
.....(以下省略)

[root@k8s-master01 ~]# kubectl explain Ingress
KIND: Ingress
VERSION: extensions/v1beta1

获取字段设置帮助文档

1
2
3
4
5
6
7
8
9
10
11
12
[root@k8s-master01 ~]# kubectl explain pod
KIND: Pod
VERSION: v1

DESCRIPTION:
Pod is a collection of containers that can run on a host.
This resource is created by clients and scheduled onto hosts.

FIELDS:
apiVersion <string>
........
........

字段配置格式

1
2
3
4
5
6
7
8
apiVersion <string>        #表示字符串类型
metadata <Object> #表示需要嵌套多层字段
labels <map[string]string> #表示由k:v组成的映射
finalizers <[]string> #表示字串列表
ownerReferences <[]Object> #表示对象列表
hostPID <boolean> #布尔类型
priority <integer> #整型
name <string> -required- #如果类型后面接 -required-,表示为必填字段

通过yml文件创建pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
spec:
containers:
- name: myapp-1
image: hub.panfeng.com/library/myapp:v1
- name: busybox-1
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"


# kubectl get pod xx.xx.xx -o yaml
# 使用 -o 参数 加 yaml,可以将资源的配置以 yaml的格式输出出来,也可以使用json,输出为json格式

*pod 生命周期图示

在这里插入图片描述

init 容器

、作为初始化。在真正的容器启动之前启动。启动成功之后,完成工作就销毁。
、只有上一个 init容器工作完成,才能进行下一个 init容器启动。
、真正的 pod处于运行时,init容器是不存在的,真正的容器是存在的。
、一般 init容器用于初始化,和检查。

init 容器:概念

2021-04-12 09:14:45 攀峰说:作为初始化使用,暂时感觉没什么用。
在这里插入图片描述

init 容器:作用

在这里插入图片描述

init容器:特殊说明

在这里插入图片描述
在这里插入图片描述

示例:init 容器模版

循环,init容器一直循环,直到有 service(myservice,mydb),才退出循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# pod
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# service
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377

容器探针

在这里插入图片描述

探测方式:readinessProbe,livenessProbe

在这里插入图片描述

示例:就绪检测:readinessProbe

检测探针 - 就绪检测
1、httpget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: wangyanglinux/myapp:v1
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet: # 探测方式 get
port: 80 # 探测端口 80
path: /index222.html # 看看容器根路径 / 下面有没有 index222.html 文件
initialDelaySeconds: 1 # 初始化,延迟 1秒
periodSeconds: 3 # 3秒一次,发送请求

示例:存活检测:livenessProbe

检测探针 - 存活检测

1、livenessProbe-exec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: hub.panfeng.com/library/busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3

2、livenessProbe-httpget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: hub.panfeng.com/library/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 10

3、livenessProbe-tcp

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
spec:
containers:
- name: nginx
image: hub.panfeng.com/library/myapp:v1
livenessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
tcpSocket:
port: 80

示例:启动/退出、postStart/preStop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle: # 容器生命周期
postStart: # 启动后
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop: # 停止前
exec:
command: ["/bin/sh", "-c", "echo Hello from the poststop handler > /usr/share/message"]

pod hook(钩子)

在这里插入图片描述

*pod 重启策略

在这里插入图片描述

*pod phase(生命周期)

1、概念
在这里插入图片描述

2、pod phase 存在的可能性
在这里插入图片描述

*示例:pod 状态

  • Pod 中只有一个容器并且正在运行,容器成功退出
    1
    2
    3
    4
    5
    + 记录事件完成
    + 如果 restartPolicy 为:
    + Always:重启容器;Pod phase 仍为 Running
    + OnFailure:Pod phase 变成 Succeeded
    + Never:Pod phase 变成 Succeeded
  • Pod 中只有一个容器并且正在运行。容器退出失败
    1
    2
    3
    4
    5
    + 记录失败事件
    + 如果 restartPolicy 为:
    + Always:重启容器;Pod phase 仍为 Running
    + OnFailure:重启容器;Pod phase 仍为 Running
    + Never:Pod phase 变成 Failed
  • Pod 中有两个容器并且正在运行。容器1退出失败
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    + 记录失败事件
    + 如果 restartPolicy 为:
    + Always:重启容器;Pod phase 仍为 Running
    + OnFailure:重启容器;Pod phase 仍为 Running
    + Never:不重启容器;Pod phase 仍为 Running
    + 如果有容器1没有处于运行状态,并且容器2退出:
    + 记录失败事件
    + 如果 restartPolicy 为:
    + Always:重启容器; Pod phase 仍为 Running
    + OnFailure:重启容器; Pod phase 仍为 Running
    + Never:Pod phase 变成 Failed
  • Pod 中只有一个容器并处于运行状态。容器运行时内存超出限制
    1
    2
    3
    4
    5
    6
    + 容器以失败状态终止
    + 记录 OOM 事件
    + 如果 restartPolicy 为 :
    + Always:重启容器;Pod phase 仍为 Running
    + OnFailure:重启容器;Pod phase 仍为 Running
    + Never: 记录失败事件;Pod phase 仍为 Failed
  • Pod 正在运行,磁盘故障
    1
    2
    3
    + 杀掉所有容器。 记录适当事件
    + Pod phase 变成 Failed
    + 如果使用控制器来运行,Pod 将在别处重建
  • Pod 正在运行,其节点被分段
    1
    2
    3
    + 节点控制器等待直到超时
    + 节点控制器将 Pod phase 设置为 Failed
    + 如果是用控制器来运行,Pod 将在别处重建

5、资源控制器

2021-04-12 10:50:47

*Pod 的分类

  • 自主式 Pod:Pod 退出了,此类型的 Pod 不会被创建
  • 控制器管理的 Pod:在控制器的生命周期里,始终要维持 Pod 的副本数目
1
2
申明式编程(Deployment)  apply(优)	create
命令式(rs) create(优) apply

简介

什么是控制器?

Kubernetes 中内建了很多 controller(控制器),这些相当于一个状态机,用来控制 Pod 的具体状态和行为

控制器类型

  • ReplicationController(RC) 和 ReplicaSet(RS)
  • Deployment
  • DaemonSet
  • StateFulSet
  • Job/CronJob
  • Horizontal Pod Autoscaling(HPA)

*rc/rs,ReplicationController/ReplicaSet

ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收;

在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController 。ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector;

*Deployment

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 ReplicationController 来方便的管理应用。

典型的应用场景包括;

  • 定义 Deployment 来创建 Pod 和 ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续 Deployment

DaemonSet

DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。
当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

使用 DaemonSet 的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterd 、 ceph
  • 在每个 Node 上运行日志收集 daemon,例如 fluentd 、 logstash
  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond

*StatefulSet

StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序

StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
  • 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有 Cluster IP的Service)来实现
  • 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到 N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
  • 有序收缩,有序删除(即从N-1到0)

Job/CronJob

Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束。

Cron Job 管理基于时间的 Job,即:

  • 在给定时间点只运行一次
  • 周期性地在给定时间点运行

使用前提条件:当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)。对于先前版本的集群,版本 < 1.8,启动 API Server时,通过传递选项 --runtime-config=batch/v2alpha1=true 可以开启 batch/v2alpha1 API

典型的用法如下所示:

  • 在给定的时间点调度 Job 运行
  • 创建周期性运行的 Job,例如:数据库备份、发送邮件

*HPA,Horizontal Pod Autoscaling

应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?

这就有赖于Horizontal Pod Autoscaling(HPA)了,顾名思义,使Pod水平自动缩放(根据 CPU,内存,。。。自动扩容,缩容)

*rc/rs/Deployment 关系

RC (ReplicationController )主要的作用就是用来确保容器应用的副本数始终保持在用户定义的副本数。
即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收。

Kubernetes 官方建议使用 RS(ReplicaSet ) 替代 RC (ReplicationController ) 进行部署,RS 跟 RC 没有本质的不同,只是名字不一样,并且 RS 支持集合式的 selector(选择器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80

*rs/Deployment 关系

1、会建立一个新的 rs,并在 rs 上创建 3 个 pod。
2、等新的 rs 创建创建,老的 rs 上的 pod 再停止使用。
3、如果要回滚,只需要切换到老的 rs 上面即可。
在这里插入图片描述

*Deployment

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义(declarative)方法,用来替代以前的 ReplicationController 来方便的管理应用。

典型的应用场景包括:

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment

示例:部署 Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

# 1、创建
# kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record
## --record参数可以记录命令,我们可以很方便的查看每次 revision 的变化

# 2、扩容
# kubectl scale deployment nginx-deployment --replicas 10

# 3、如果集群支持 horizontal pod autoscaling 的话,还可以为Deployment设置自动扩展
# kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80

# 4、更新镜像也比较简单
# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

# 5、回滚
# kubectl rollout undo deployment/nginx-deployment

更新 Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 修改 nginx pod 镜像: nginx:1.7.9 ===> nginx:1.9.1
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
》deployment "nginx-deployment" image updated

# 可以使用 edit 命令来编辑 Deployment
kubectl edit deployment/nginx-deployment
》deployment "nginx-deployment" edited

# 查看 rollout 的状态
kubectl rollout status deployment/nginx-deployment
》Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
》deployment "nginx-deployment" successfully rolled out

# 查看历史 RS
kubectl get rs
》NAME DESIRED CURRENT READY AGE
》nginx-deployment-1564180365 3 3 0 6s
》nginx-deployment-2035384211 0 0 0 36s

Deployment 更新策略

Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。
默认的,它会确保至少有比期望的 Pod数量少一个是 up状态(最多一个不可用)

Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。
默认的,它会确保最多比期望的 Pod数量多一个的 Pod 是 up 的(最多1个 surge )

未来的 Kuberentes 版本中,将从1-1变成25%-25%

1
kubectl describe deployments

Rollover(多个rollout并行)

假如您创建了一个有5个 niginx:1.7.9 replica的 Deployment,但是当还只有3个 nginx:1.7.9 的 replica 创建出来的时候您就开始更新含有5个 nginx:1.9.1 replica 的 Deployment。

在这种情况下,Deployment 会立即杀掉已创建的3个 nginx:1.7.9 的 Pod,并开始创建 nginx:1.9.1 的 Pod。它不会等到所有的5个 nginx:1.7.9 的 Pod 都创建完成后才开始改变航道

回退 Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
kubectl set image deployment/nginx-deployment nginx=nginx:1.91

kubectl rollout status deployments nginx-deployment

kubectl get pods

kubectl rollout history deployment/nginx-deployment

kubectl rollout undo deployment/nginx-deployment

kubectl rollout undo deployment/nginx-deployment --to-revision=2 ## 可以使用 --revision参数指定 某个历史版本

kubectl rollout pause deployment/nginx-deployment ## 暂停 deployment 的更新

您可以用 kubectl rollout status 命令查看 Deployment 是否完成。如果 rollout 成功完成, kubectl rollout status 将返回一个 0值的 Exit Code

1
2
3
4
5
6
kubectl rollout status deploy/nginx
》Waiting for rollout to finish: 2 of 3 updated replicas are available...
》deployment "nginx" successfully rolled out

echo $?
》0

回滚历史记录限制

您可以通过设置 .spec.revisonHistoryLimit 项来指定 deployment 最多保留多少 revision 历史记录。
默认的会保留所有的 revision;如果将该项设置为0,Deployment 就不允许回退了。

什么是 DaemonSet

1、DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。
2、当有 Node 加入集群时,也会为他们新增一个 Pod 。
3、当有 Node 从集群移除时,这些 Pod 也会被回收。
4、删除 DaemonSet 将会删除它创建的所有 Pod。

使用 DaemonSet 的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterd 、 ceph
  • 在每个 Node 上运行日志收集 daemon,例如 fluentd 、 logstash
  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
containers:
- name: daemonset-example
image: wangyanglinux/myapp:v1

Job/CronJob

Job

Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束

特殊说明

  • spec.template 格式同Pod
  • RestartPolicy仅支持 Never 或 OnFailure
  • 单个Pod时,默认Pod成功运行后Job即结束
  • .spec.completions 标志Job结束需要成功运行的Pod个数,默认为1
  • .spec.parallelism 标志并行运行的Pod的个数,默认为1
  • spec.activeDeadlineSeconds 标志失败Pod的重试最大时间,超过这个时间不会继续重试

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
metadata:
name: pi
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never

CronJob Spec

  • spec.template格式同Pod
  • RestartPolicy仅支持Never或OnFailure
  • 单个Pod时,默认Pod成功运行后Job即结束
  • .spec.completions 标志Job结束需要成功运行的Pod个数,默认为1
  • .spec.parallelism 标志并行运行的Pod的个数,默认为1
  • spec.activeDeadlineSeconds 标志失败Pod的重试最大时间,超过这个时间不会继续重试

CronJob

Cron Job 管理基于时间的 Job,即:

  • 在给定时间点只运行一次
  • 周期性地在给定时间点运行

注意:创建 Job 操作应该是 幂等的

使用条件:当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)

典型的用法如下所示:

  • 在给定的时间点调度 Job 运行
  • 创建周期性运行的 Job,例如:数据库备份、发送邮件

CronJob Spec

  • .spec.schedule :调度,必需字段,指定任务运行周期,格式同 Cron
  • .spec.jobTemplate :Job 模板,必需字段,指定需要运行的任务,格式同 Job
  • .spec.startingDeadlineSeconds :启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限
  • .spec.concurrencyPolicy :并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。
    只允许指定下面策略中的一种:
    • Allow (默认):允许并发运行 Job
    • Forbid :禁止并发运行,如果前一个还没有完成,则直接跳过下一个
    • Replace :取消当前正在运行的 Job,用一个新的来替换
      注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。
  • .spec.suspend :挂起,该字段也是可选的。如果设置为 true ,后续所有执行都会被挂起。它对已经开始
    执行的 Job 不起作用。默认值为 false 。
  • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit :历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为 3 和 1 。设置限制的值为 0 ,相关类型的 Job 完成后将不会被保留。

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kubectl get cronjob
》NAME SCHEDULE SUSPEND ACTIVE LAST-SCHEDULE
》hello */1 * * * * False 0 <none>

kubectl get jobs
》NAME DESIRED SUCCESSFUL AGE
》hello-1202039034 1 1 49s

pods=$(kubectl get pods --selector=job-name=hello-1202039034 --output=jsonpath={.items..metadata.name})

kubectl logs $pods
》Mon Aug 29 21:34:09 UTC 2016
》Hello from the Kubernetes cluster

# 注意,删除 cronjob 的时候不会自动删除 job,这些 job 可以用 kubectl delete job 来删除
kubectl delete cronjob hello
》cronjob "hello" deleted

6、Service

2021-04-13 07:56:06

*Service 的概念

Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略,通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector。

在这里插入图片描述

Service能够提供负载均衡的能力,但是在使用上有以下限制:

  • 只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的

Service 的类型

Service 在 k8s 中有以下四种类型

  • ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样外部就可以通过 : NodePort 来访问该服务
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort
  • ExternalName:(Headless Cluser IP 无头服务)把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持

在这里插入图片描述

VIP 和 Service 代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。
kube-proxy 负责为 Service 实现了一种VIP(虚拟 virtual IP)的形式,而不是 ExternalName 的形式。

在 Kubernetes v1.0 版本,代理完全在 userspace。
在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。
从 Kubernetes v1.2 起,默认就是iptables 代理。
在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理

在 Kubernetes 1.14 版本开始默认使用 ipvs 代理

在 Kubernetes v1.0 版本, Service 是 “4层”(TCP/UDP over IP)概念。
在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务

!为何不使用 round-robin DNS?

代理模式的分类

1、userspace 代理模式
2、iptables 代理模式
3、ipvs 代理模式

Ⅰ、userspace 代理模式
在这里插入图片描述

Ⅱ、iptables 代理模式
在这里插入图片描述

Ⅲ、ipvs 代理模式

这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建 ipvs(IP Virtual Server) 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod

与 iptables 类似,ipvs(IP 虚拟服务器) 于 netfilter(网络过滤器) 的 hook(钩子) 功能,但使用哈希表作为底层数据结构并在内核空间中工作。
这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。

此外,ipvs 为负载均衡算法提供了更多选项,例如:

  • rr :轮询调度
  • lc :最小连接数
  • dh :目标哈希
  • sh :源哈希
  • sed :最短期望延迟
  • nq : 不排队调度

在这里插入图片描述

*ClusterIP

clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端口,进而把数据转发给对应的 pod 的地址和端口

在这里插入图片描述

为了实现图上的功能,主要需要以下几个组件的协同工作:

  • apiserver 用户通过kubectl命令向 apiserver发送创建 service的命令,apiserver接收到请求后将数据存储到 etcd中
  • kube-proxy kubernetes的每个节点中都有一个叫做 kube-porxy的进程,这个进程负责感知 service、pod的变化,并将变化的信息写入本地的 iptables规则中
  • iptables 使用 NAT等技术将 virtual IP的流量转至 endpoint中

》》》示例:ClusterIP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# myapp-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: stabel
template:
metadata:
labels:
app: myapp
release: stabel
env: test
spec:
containers:
- name: myapp
image: wangyanglinux/myapp:v2
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80

---

# myapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: ClusterIP
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80

Headless Service:无头服务

Headless:不在意的;无头脑的;无领导者的;无头的

有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP) 的值为 “None” 来创建 Headless Service 。

这类 Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master mainfests]# vim myapp-svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
clusterIP: "None"
ports:
- port: 80
targetPort: 80

[root@k8s-master mainfests]# dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10

*NodePort

nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@master manifests]# vim myapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: NodePort
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80

》》》查询流程

1
iptables -t nat -nvL KUBE-NODEPORTS

*LoadBalancer

2021-04-13 08:43:09

负载到不同的 node上。

loadBalancer 和 nodePort 其实是同一种方式。区别在于 loadBalancer 比 nodePort 多了一步,就是可以调用 cloud provider 去创建 LB 来向节点导流。

在这里插入图片描述

ExternalName

这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.panfeng.com )。

ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。

相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务

1
2
3
4
5
6
7
8
kind: Service
apiVersion: v1
metadata:
name: my-service-1
namespace: default
spec:
type: ExternalName
externalName: hub.panfeng.com

当查询主机 my-service.defalut.svc.cluster.local ( SVC_NAME.NAMESPACE.svc.cluster.local )时,集群的 DNS 服务将返回一个值 my.database.example.com 的 CNAME 记录。

访问这个服务的工作方式和其他的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发

*Ingress-Nginx

资料信息

Ingress-Nginx github 地址
Ingress-Nginx 官方网站

在这里插入图片描述
在这里插入图片描述

部署 Ingress-Nginx

1
2
3
kubectl apply -f mandatory.yaml

kubectl apply -f service-nodeport.yaml

*Ingress http代理

》》》deployment、Service、Ingress Yaml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-dm
spec:
replicas: 2
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
name: nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
spec:
rules:
- host: www1.panfeng.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80

Ingress https代理

  1. 创建证书,以及 cert 存储方式
    1
    2
    3
    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"

    kubectl create secret tls tls-secret --key tls.key --cert tls.crt
  2. deployment、Service、Ingress Yaml 文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
    name: nginx-test
    spec:
    tls:
    - hosts:
    - foo.bar.com
    secretName: tls-secret
    rules:
    - host: foo.bar.com
    http:
    paths:
    - path: /
    backend:
    serviceName: nginx-svc
    servicePort: 80

Nginx 进行BasicAuth

访问时,需要认证。(输入账户名,密码)

1
2
3
4
5
yum -y install httpd

htpasswd -c auth foo

kubectl create secret generic basic-auth --from-file=auth
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-with-auth
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
rules:
- host: foo2.bar.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80

Nginx 进行重写

  • nginx.ingress.kubernetes.io/rewrite-target:必须重定向流量的目标URI 【串】
  • nginx.ingress.kubernetes.io/ssl-redirect:指示位置部分是否仅可访问SSL(当Ingress包含证书时默认为True) 【布尔】
  • nginx.ingress.kubernetes.io/force-ssl-redirect:即使Ingress未启用TLS,也强制重定向到HTTPS 【布尔】
  • nginx.ingress.kubernetes.io/app-root:定义Controller必须重定向的应用程序根,如果它在’/‘上下文中 【串】
  • nginx.ingress.kubernetes.io/use-regex:指示Ingress上定义的路径是否使用正则表达式 【布尔】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://foo.bar.com:31795/hostname.html
spec:
rules:
- host: foo10.bar.com
http:
paths:
- path: /
backend:
serviceName: nginx-svc
servicePort: 80

7、储存

2021-04-14 11:27:22

configMap

在这里插入图片描述

configMap 描述信息

ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。

ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制大对象

ConfigMap 的创建

使用目录创建

Ⅰ、使用目录创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ls docs/user-guide/configmap/kubectl/
》game.properties
》ui.properties

cat docs/user-guide/configmap/kubectl/game.properties
》enemies=aliens
》lives=3
》enemies.cheat=true
》enemies.cheat.level=noGoodRotten
》secret.code.passphrase=UUDDLRLRBABAS
》secret.code.allowed=true
》secret.code.lives=30

cat docs/user-guide/configmap/kubectl/ui.properties
》color.good=purple
》color.bad=yellow
》allow.textmode=true
》how.nice.to.look=fairlyNice

kubectl create configmap game-config --from-file=docs/user-guide/configmap/kubectl
# —from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容

使用文件创建

Ⅱ、使用文件创建

1
2
3
4
# 使用文字值创建,利用 —from-literal 参数传递配置信息,该参数可以使用多次
kubectl create configmap special-config --from-literal=special.how=very --from- literal=special.type=charm

kubectl get configmaps special-config -o yaml

pod使用ConfigMap

替代环境变量

Ⅰ、使用 ConfigMap 来替代环境变量

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm
1
2
3
4
5
6
7
8

apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
namespace: default
data:
log_level: INFO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: hub.panfeng.com/library/myapp:v1
command: [ "/bin/sh", "-c", "env" ]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
- name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.type
envFrom:
- configMapRef:
name: env-config
restartPolicy: Never

设置命令行参数

Ⅱ、用 ConfigMap 设置命令行参数

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: hub.panfeng.com/library/myapp:v1
command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
- name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.type
restartPolicy: Never

数据卷地址

Ⅲ、通过数据卷插件使用ConfigMap

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm

在数据卷里面使用这个 ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文
件名,键值就是文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: hub.panfeng.com/library/myapp:v1
command: [ "/bin/sh", "-c", "cat /etc/config/special.how" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: special-config
restartPolicy: Never

ConfigMap 的热更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: v1
kind: ConfigMap
metadata:
name: log-config
namespace: default
data:
log_level: INFO
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: hub.panfeng.com/library/myapp:v1
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: log-config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kubectl exec $(kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2) cat /etc/config/log_level
》INFO


# 修改 ConfigMap
kubectl edit configmap log-config

# 修改 log_level=DEBUG 等待大概 10 秒钟时间,再次查看环境变量的值
kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` cat /tmp/log_level
》DEBUG

# ConfigMap 更新后滚动更新 Pod
# 更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发滚动更新
kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20190411" }}}}}'

这个例子里我们在 .spec.template.metadata.annotations 中添加 version/config ,每次通过修改 version/config 来触发滚动更新

!!! 更新 ConfigMap 后:

  • 使用该 ConfigMap 挂载的 Env 不会同步更新
  • 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新

Secret

Secret 存在意义

Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec中。

Secret 可以以 Volume 或者环境变量的方式使用。

Secret 有三种类型:

  • Service Account :用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中
  • Opaque :base64编码格式的Secret,用来存储密码、密钥等
  • kubernetes.io/dockerconfigjson :用来存储私有 docker registry 的认证信息

Service Account

Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中

1
2
3
4
5
6
7
8
9
10
11
kubectl run nginx --image nginx
》deployment "nginx" created

kubectl get pods
》NAME READY STATUS RESTARTS AGE
》nginx-3137573019-md1u2 1/1 Running 0 13s

kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount
》ca.crt
》namespace
》token

Opaque Secret

Opaque Secret:不透明的秘密

Ⅰ、创建说明
Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式:

1
2
3
4
5
echo -n "admin" | base64
》YWRtaW4=

echo -n "1f2d1e2e67df" | base64
》MWYyZDFlMmU2N2Rm

secrets.yml

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm

Ⅱ、使用方式
1、将 Secret 挂载到 Volume 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
labels:
name: seret-test
name: seret-test
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
containers:
- image: hub.panfeng.com/library/myapp:v1
name: db
volumeMounts:
- name: secrets
mountPath: "
readOnly: true

2、将 Secret 导出到环境变量中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: pod-deployment
spec:
replicas: 2
template:
metadata:
labels:
app: pod-deployment
spec:
containers:
- name: pod-1
image: hub.panfeng.com/library/myapp:v1
ports:
- containerPort: 80
env:
- name: TEST_USER
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: TEST_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password

*dockerConfigJson

使用 Kuberctl 创建 docker registry 认证的 secret

1
2
kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER -- docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
》secret "myregistrykey" created.

在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: foo
image: roc/awangyang:v1
imagePullSecrets:
- name: myregistrykey

volume

*背景

Kubernetes 中的卷有明确的寿命都与封装它的 Pod 相同。所以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。
当然,当 Pod 不再存在时,卷也将不复存在。更重要的是,Kubernetes支持多种类型的卷,Pod 可以同时使用任意数量的卷

卷的类型

Kubernetes 支持以下类型的卷:

1
2
3
4
5
6
7
+ awsElasticBlockStore azureDisk azureFile cephfs csi downwardAPI emptyDir

+ fc flocker gcePersistentDisk gitRepo glusterfs hostPath iscsi local nfs

+ persistentVolumeClaim projected portworxVolume quobyte rbd scaleIO secret

+ storageos vsphereVolume

*emptyDir

在这里插入图片描述

当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。
正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。
当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除

emptyDir 的用法有:

  • 暂存空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • Web服务器容器提供数据时,保存内容管理器容器提取的文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}

*hostPath

hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中

hostPath 的用途如下:

  • 运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath
  • 在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath
  • 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在

除了所需的 path 属性之外,用户还可以为 hostPath 卷指定 type
在这里插入图片描述

使用这种卷类型是请注意,因为:

  • 由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同
  • 当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
  • 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath 卷
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /data # 宿主机目录地址
type: Directory # 这个字段是可选的

Persistent Volume

概念

PersistentVolume (PV)

是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。

PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。

此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统

PersistentVolumeClaim (PVC)

是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。

Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)

静态 pv

集群管理员创建一些 PV。它们带有可供群集用户使用的实际存储的细节。它们存在于 Kubernetes API 中,可用于消费

动态 pv

当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。
此配置基于 StorageClasses :PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。
声明该类为 “” 可以有效地禁用其动态配置

要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器]。
例如,通过确保 DefaultStorageClass 位于 API server 组件的 –admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作

绑定 pv

master 中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定在一起。
如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。

一旦 PV 和 PVC 绑定后, PersistentVolumeClaim 绑定是排他性的,不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射

持久化卷声明的保护

PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失

当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。
PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用

持久化卷类型

PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:

  • GCEPersistentDisk AWSElasticBlockStore AzureFile AzureDisk FC (Fibre Channel)
  • FlexVolume Flocker NFS iSCSI RBD (Ceph Block Device) CephFS
  • Cinder (OpenStack block storage) Glusterfs VsphereVolume Quobyte Volumes
  • HostPath VMware Photon Portworx Volumes ScaleIO Volumes StorageOS

持久卷演示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2

PV 访问模式

PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。
如下表所示,供应商具有不同的功能,每个PV 的访问模式都将被设置为该卷支持的特定模式。
例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。
每个 PV 都有一套自己的用来描述特定功能的访问模式

  • ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany——该卷可以被多个节点以只读模式挂载
  • ReadWriteMany——该卷可以被多个节点以读/写模式挂载

在命令行中,访问模式缩写为:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany

在这里插入图片描述

回收策略

  • Retain(保留)——手动回收
  • Recycle(回收)——基本擦除( rm -rf /thevolume/* )
  • Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除

当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略

状态

卷可以处于以下的某种状态:

  • Available(可用)——一块空闲资源还没有被任何声明绑定
  • Bound(已绑定)——卷已经被声明绑定
  • Released(已释放)——声明被删除,但是资源还未被集群重新声明
  • Failed(失败)——该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称

示例:NFS

持久化演示说明 - NFS

在这里插入图片描述

Ⅰ、安装 NFS 服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
yum install -y nfs-common nfs-utils rpcbind

mkdir /nfsdata

chmod 666 /nfsdata

chown nfsnobody /nfsdata

cat /etc/exports /nfsdata *(rw,no_root_squash,no_all_squash,sync)

systemctl start rpcbind

systemctl start nfs

Ⅱ、部署 PV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /data/nfs
server: 10.66.66.10

Ⅲ、创建服务并使用 PVC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs"
resources:
requests:
storage: 1Gi

*关于 StatefulSet

》陶攀峰:为什么有状态,就是例如:mysql 先启动,再启动 java。先关 java,再关 mysql。
》所以,有状态 StatefulSet,就是为了保证顺序而设计的。

  • 匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2
  • StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
  • StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:$(service name).$(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
  • 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Pod name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
  • 删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

Statefulset的启停顺序:

  • 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
  • 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
  • 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。

StatefulSet使用场景:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
  • 稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
  • 有序部署,有序扩展,基于 init containers 来实现。
  • 有序收缩。

8、集群调度

2021-04-15 15:47:08

调度说明

简介

Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。

听起来非常简单,但有很多要考虑的问题:

  • 公平:如何保证每个节点都能被分配资源
  • 资源高效利用:集群所有资源最大化被使用
  • 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
  • 灵活:允许用户根据自己的需求控制调度的逻辑

Sheduler 是作为单独的程序运行的,启动之后会一直坚挺 API Server,获取 PodSpec.NodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上

调度过程

调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为 predicate
然后对通过的节点按照优先级排序,这个是 priority
最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误

Predicate 有一系列的算法可以使用:

  • PodFitsResources :节点上剩余的资源是否大于 pod 请求的资源
  • PodFitsHost :如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
  • PodFitsHostPorts :节点上已经使用的 port 是否和 pod 申请的 port 冲突
  • PodSelectorMatches :过滤掉和 pod 指定的 label 不匹配的节点
  • NoDiskConflict :已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读

如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态,不断重试调度,直到有节点满足条件。
经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程: 按照优先级大小对节点排序

优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。
这些优先级选项包括:

  • LeastRequestedPriority :通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点
  • BalancedResourceAllocation :节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用
  • ImageLocalityPriority :倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高

通过算法对所有的优先级项目和权重进行计算,得出最终的结果

自定义调度器

除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过 spec:schedulername 参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。

比如下面的 pod 选择 my-scheduler 进行调度,而不是默认的default-scheduler :

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: annotation-second-scheduler
labels:
name: multischeduler-example
spec:
schedulername: my-scheduler
containers:
- name: pod-with-second-annotation-container
image: gcr.io/google_containers/pause:2.0

调度亲和性

2021-04-15 16:06:56

*节点亲和性:软/硬策略

pod.spec.nodeAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    apiVersion: v1
    kind: Pod
    metadata:
    name: affinity
    labels:
    app: node-affinity-pod
    spec:
    containers:
    - name: with-node-affinity
    image: hub.panfeng.com/library/myapp:v1
    affinity:
    nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
    preference:
    matchExpressions:
    - key: source
    operator: In
    values:
    - qikqiak
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    apiVersion: v1
    kind: Pod
    metadata:
    name: affinity
    labels:
    app: node-affinity-pod
    spec:
    containers:
    - name: with-node-affinity
    image: hub.panfeng.com/library/myapp:v1
    affinity:
    nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchExpressions:
    - key: kubernetes.io/hostname
    operator: NotIn
    values:
    - k8s-node02
  • 软策略+硬策略
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    apiVersion: v1
    kind: Pod
    metadata:
    name: affinity
    labels:
    app: node-affinity-pod
    spec:
    containers:
    - name: with-node-affinity
    image: hub.panfeng.com/library/myapp:v1
    affinity:
    nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchExpressions:
    - key: kubernetes.io/hostname
    operator: NotIn
    values:
    - k8s-node02
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
    preference:
    matchExpressions:
    - key: source
    operator: In
    values:
    - qikqiak

键值运算关系

  • In:label 的值在某个列表中
  • NotIn:label 的值不在某个列表中
  • Gt:label 的值大于某个值
  • Lt:label 的值小于某个值
  • Exists:某个 label 存在
  • DoesNotExist:某个 label 不存在

*Pod 亲和性:软/硬策略

pod.spec.affinity.podAffinity/podAntiAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略
  • 软策略+硬策略
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    apiVersion: v1
    kind: Pod
    metadata:
    name: pod-3
    labels:
    app: pod-3
    spec:
    containers:
    - name: pod-3
    image: hub.panfeng.com/library/myapp:v1
    affinity:
    podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
    matchExpressions:
    - key: app
    operator: In
    values:
    - pod-1
    topologyKey: kubernetes.io/hostname
    podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
    podAffinityTerm:
    labelSelector:
    matchExpressions:
    - key: app
    operator: In
    values:
    - pod-2
    topologyKey: kubernetes.io/hostname

亲和性/反亲和性调度策略比较如下:
在这里插入图片描述

*污点

2021-04-15 16:30:09

Taint 和 Toleration

Taint:腐坏;污染;污点;感染;难闻的气味
Toleration:宽容,忍受,默认;耐受性

节点亲和性,是 pod 的一种属性(偏好或硬性要求),它使 pod 被吸引到一类特定的节点。Taint 则相反,它使节点 能够 排斥 一类特定的 pod

Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。
每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。
如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上

污点(Taint)

Ⅰ、 污点 ( Taint ) 的组成
使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去

每个污点的组成,例如:key=value:effect
effect:影响;效果;作用

每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。
当前 taint effect 支持如下三个选项:

  • NoSchedule :表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
  • PreferNoSchedule :表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上
  • NoExecute :表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去

Ⅱ、污点的设置、查看和去除

1
2
3
4
5
6
7
8
# 设置污点
kubectl taint nodes node1 key1=value1:NoSchedule

# 节点说明中,查找 Taints 字段
kubectl describe pod pod-name

# 去除污点(减号 -)
kubectl taint nodes node1 key1:NoSchedule-

容忍(Tolerations)

设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。
但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思:是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上

pod.spec.tolerations

1
2
3
4
5
6
7
8
9
10
11
12
13
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"

其中

  • key, vaule, effect 要与 Node 上设置的 taint 保持一致
  • operator 的值为 Exists 将会忽略 value 值
  • tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Pod 上继续保留运行的时间

Ⅰ、当不指定 key 值时,表示容忍所有的污点 key:

1
2
tolerations:
- operator: "Exists"

Ⅱ、当不指定 effect 值时,表示容忍所有的污点作用

1
2
3
tolerations:
- key: "key"
operator: "Exists"

Ⅲ、有多个 Master 存在时,防止资源浪费,可以如下设置

1
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule

*固定节点

2021-04-15 16:40:58

指定调度节点
Ⅰ、Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 7
template:
metadata:
labels:
app: myweb
spec:
nodeName: k8s-node01
containers:
- name: myweb
image: hub.panfeng.com/library/myapp:v1
ports:
- containerPort: 80

Ⅱ、Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 2
template:
metadata:
labels:
app: myweb
spec:
nodeSelector:
type: backEndNode1
containers:
- name: myweb
image: harbor/tomcat:8.5-jre8
ports:
- containerPort: 80

9、安全

2021-05-14 16:55:03

机制说明

Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server 是集群内部各个组件通信的中介,也是外部控制的入口。
所以 Kubernetes 的安全机制基本就是围绕保护 API Server 来设计的。
Kubernetes 使用了认证(Authentication)、鉴权(Authorization)、准入控制(AdmissionControl)三步来保证API Server的安全

在这里插入图片描述

Authentication

认证流程

在这里插入图片描述

认证方式

  • HTTP Token 认证:通过一个 Token 来识别合法用户
    HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 - Token 来表达客户的一种方式。
    Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token

  • HTTP Base 认证:通过 用户名+密码 的方式认证
    用户名+:+密码 用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 Heather Authorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码

  • 最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式

Ⅰ、HTTPS 证书认证:

在这里插入图片描述

Ⅱ、需要认证的节点

在这里插入图片描述

两种类型

  • Kubenetes 组件对 API Server 的访问:kubectl、Controller Manager、Scheduler、kubelet、kubeproxy
  • Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)

安全性说明

  • Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用 API Server 的非安全端口访问, --insecure-bind-address=127.0.0.1
  • kubectl、kubelet、kube-proxy 访问 API Server 就都需要证书进行 HTTPS 双向认证

证书颁发

  • 手动签发:通过 k8s 集群的跟 ca 进行签发 HTTPS 证书
  • 自动签发:kubelet 首次访问 API Server 时,使用 token 做认证,通过后,Controller Manager 会为kubelet 生成一个证书,以后的访问都是用证书做认证了

Ⅲ、kubeconfig

kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群 context 信息(集群名称、用户名)。
Kubenetes 组件通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群

Ⅳ、ServiceAccount

Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了。
Kubenetes使用了Service Account解决Pod 访问API Server的认证问题

Ⅴ、Secret 与 SA 的关系

Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-accounttoken, 另一种是用于保存用户自定义保密信息的 Opaque。
ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace
、token是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证
、ca.crt,根证书。用于Client端验证API Server发送的证书
、namespace, 标识这个service-account-token的作用域名空间

1
kubectl get secret --all-namespaces kubectl describe secret default-token-5gm9r --namespace=kube-system

默认情况下,每个 namespace 都会有一个 ServiceAccount,如果 Pod 在创建时没有指定 ServiceAccount,就会使用 Pod 所属的 namespace 的 ServiceAccount

授权

授权策略

上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。
API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “–authorization-mode” 设置)

  • AlwaysDeny:表示拒绝所有的请求,一般用于测试
  • AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略
  • ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
  • Webbook:通过调用外部 REST 服务对用户进行授权
  • RBAC(Role-Based Access Control):基于角色的访问控制,现行默认规则

RBAC 授权模式

RBAC(Role-Based Access Control)基于角色的访问控制,在 Kubernetes 1.5 中引入,现行版本成为默认标准。
相对其它访问控制方式,拥有以下优势:

  • 对集群中的资源和非资源均拥有完整的覆盖

  • 整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作

  • 可以在运行时进行调整,无需重启 API Server

  • Ⅰ、RBAC 的 API 资源对象说明
    RBAC 引入了 4 个新的顶级资源对象:Role、ClusterRole、RoleBinding、ClusterRoleBinding,4 种对象类型均可以通过 kubectl 与 API 操作
    在这里插入图片描述

需要注意的是 Kubenetes 并不会提供用户管理,那么 User、Group、ServiceAccount 指定的用户又是从哪里来的呢?
Kubenetes 组件(kubectl、kube-proxy)或是其他自定义的用户在向 CA 申请证书时,需要提供一个证书请求文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"CN":"admin",
"hosts":[

],
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"HangZhou",
"L":"XS",
"O":"system:masters",
"OU":"System"
}
]
}

API Server会把客户端证书的 CN 字段作为User,把 names.O 字段作为Group

kubelet 使用 TLS Bootstaping 认证时,API Server 可以使用 Bootstrap Tokens 或者 Token authentication file 验证 =token,无论哪一种,Kubenetes 都会为 token 绑定一个默认的 User 和 Group

Pod使用 ServiceAccount 认证时,service-account-token 中的 JWT 会保存 User 信息

有了用户信息,再创建一对角色/角色绑定(集群角色/集群角色绑定)资源对象,就可以完成权限绑定了

Role and ClusterRole

在 RBAC API 中,Role 表示一组规则权限,权限只会增加(累加权限),不存在一个资源一开始就有很多权限而通过 RBAC 对其进行减少的操作;
Role 可以定义在一个 namespace 中,如果想要跨 namespace 则可以创建 ClusterRole

1
2
3
4
5
6
7
8
9
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]

ClusterRole 具有与 Role 相同的权限角色控制能力,不同的是 ClusterRole 是集群级别的,ClusterRole 可以用于:

  • 集群级别的资源控制( 例如 node 访问权限 )
  • 非资源型 endpoints( 例如 /healthz 访问 )
  • 所有命名空间资源控制(例如 pods )
1
2
3
4
5
6
7
8
9
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]

RoleBinding and ClusterRoleBinding

RoloBinding 可以将角色中定义的权限授予用户或用户组,RoleBinding 包含一组权限列表(subjects),权限列表中包含有不同形式的待授予权限资源类型(users, groups, or service accounts);

RoloBinding 同样包含对被Bind 的 Role 引用;RoleBinding 适用于某个命名空间内授权,而 ClusterRoleBinding 适用于集群范围内的授权。

将 default 命名空间的 pod-reader Role 授予 jane 用户,此后 jane 用户在 default 命名空间中将具有 pod- reader 的权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

RoleBinding 同样可以引用 ClusterRole 来对当前 namespace 内用户、用户组或 ServiceAccount 进行授权,这种操作允许集群管理员在整个集群内定义一些通用的 ClusterRole,然后在不同的 namespace 中使用RoleBinding 来引用

例如,以下 RoleBinding 引用了一个 ClusterRole,这个 ClusterRole 具有整个集群内对 secrets 的访问权限;
但是其授权用户 dave 只2能访问 development 空间中的 secrets(因为 RoleBinding 定义在 development 命名空间)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# This role binding allows "dave" to read secrets in the "development" namespace.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-secrets
namespace: development # This only grants permissions within the "development" namespace.
subjects:
- kind: User
name: dave
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

使用 ClusterRoleBinding 可以对整个集群中的所有命名空间资源权限进行授权;以下 ClusterRoleBinding 样例展示了授权 manager 组内所有用户在全部命名空间中对 secrets 进行访问

1
2
3
4
5
6
7
8
9
10
11
12
13
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

to Resources

Kubernetes 集群内一些资源一般以其名称字符串来表示,这些字符串一般会在 API 的 URL 地址中出现;
同时某些资源也会包含子资源,例如 logs 资源就属于 pods 的子资源,API 中 URL 样例如:GET /api/v1/namespaces/{namespace}/pods/{name}/log

如果要在 RBAC 授权模型中控制这些子资源的访问权限,可以通过 / 分隔符来实现,以下是一个定义 pods 资资源 logs 访问权限的 Role 定义样例

1
2
3
4
5
6
7
8
9
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: default
name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]

to Subjects

RoleBinding 和 ClusterRoleBinding 可以将 Role 绑定到 Subjects;Subjects 可以是 groups、users 或者 service accounts

Subjects 中 Users 使用字符串表示,它可以是一个普通的名字字符串,如 “alice”;也可以是 email 格式的邮箱地址,如 “1801957499@qq.com”;甚至是一组字符串形式的数字 ID 。
但是 Users 的前缀 system: 是系统保留的,集群管理员应该确保普通用户不会使用这个前缀格式。

Groups 书写格式与 Users 相同,都为一个字符串,并且没有特定的格式要求;同样 system: 前缀为系统保留

实践:创建一个用户只能管理 dev 空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"CN":"devuser",
"hosts":[

],
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"CN",
"ST":"BeiJing",
"L":"BeiJing",
"O":"k8s",
"OU":"System"
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 下载证书生成工具
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl

wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64

mv cfssljson_linux-amd64 /usr/local/bin/cfssljson

wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo

cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes /root/devuser-csr.json | cfssljson-bare devuser

# 设置集群参数
export KUBE_APISERVER="https://172.20.0.113:6443"
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=devuser.kubeconfig

# 设置客户端认证参数
kubectl config set-credentials devuser \
--client-certificate=/etc/kubernetes/ssl/devuser.pem \
--client-key=/etc/kubernetes/ssl/devuser-key.pem \
--embed-certs=true \
--kubeconfig=devuser.kubeconfig

# 设置上下文参数
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=devuser \
--namespace=dev \
--kubeconfig=devuser.kubeconfig

# 设置默认上下文
kubectl config use-context kubernetes --kubeconfig=devuser.kubeconfig

cp -f ./devuser.kubeconfig /root/.kube/config

kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev

准入控制

准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount

官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的 1.14 的推荐列表是:

  • NamespaceLifecycle
  • LimitRanger
  • ServiceAccount
  • DefaultStorageClass
  • DefaultTolerationSeconds
  • Mutat ingAdmissionWebhook
  • ValidatingAdmissionWebhook
  • ResourceQuota

列举几个插件的功能:

  • NamespaceLifecycle: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除namespace 时,连带删除它的所有资源对象。
  • LimitRanger:确保请求的资源不会超过资源所在 Namespace 的 LimitRange 的限制。
  • ServiceAccount: 实现了自动化添加 ServiceAccount。
  • ResourceQuota:确保请求的资源不会超过资源的 ResourceQuota 限制。

10、Helm

2021-05-14 17:40:07

Helm 是什么

在没使用 helm 之前,向 kubernetes 部署应用,我们要依次部署 deployment、svc 等,步骤较繁琐,况且随着很多项目微服务化,复杂的应用在容器中部署以及管理显得较为复杂。
helm 通过打包的方式,支持发布的版本管理和控制,很大程度上简化了 Kubernetes 应用的部署和管理。

Helm 本质就是让 K8s 的应用管理(Deployment,Service 等 ) 可配置,能动态生成。通过动态生成 K8s 资源清单文件(deployment.yaml,service.yaml)。
然后调用 Kubectl 自动执行 K8s 资源部署。

Helm 是官方提供的类似于 YUM 的包管理器,是部署环境的流程封装。

Helm 有两个重要的概念:

  • chart:是创建一个应用的信息集合,包括各种 Kubernetes 对象的配置模板、参数定义、依赖关系、文档说明等。chart 是应用部署的自包含逻辑单元。可以将 chart 想象成 apt、yum 中的软件安装包
  • release:是 chart 的运行实例,代表了一个正在运行的应用。当 chart 被安装到 Kubernetes 集群,就生成一个 release。chart 能够多次安装到同一个集群,每次安装都是一个 release

Helm 包含两个组件:Helm 客户端和 Tiller 服务器,如下图所示
在这里插入图片描述

Helm 客户端负责 chart 和 release 的创建和管理以及和 Tiller 的交互。
Tiller 服务器运行在 Kubernetes 集群中,它会处理 Helm 客户端的请求,与 Kubernetes API Server 交互。

Helm 部署

越来越多的公司和团队开始使用 Helm 这个 Kubernetes 的包管理器,我们也将使用 Helm 安装 Kubernetes 的常用 组件。 Helm 由客户端命 helm 令行工具和服务端 tiller 组成,Helm 的安装十分简单。
下载 helm 命令行工具到 master 节点 node1 的 /usr/local/bin 下,这里下载的 2.13.1 版本:

1
2
3
4
5
ntpdate ntp1.aliyun.com
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz
tar -zxvf helm-v2.13.1-linux-amd64.tar.gz
cd linux-amd64/
cp helm /usr/local/bin/

为了安装服务端 tiller,还需要在这台机器上配置好 kubectl 工具和 kubeconfig 文件,确保 kubectl 工具可以在这台机器上访问 apiserver 且正常使用。
这里的 node1 节点以及配置好了 kubectl。

因为 Kubernetes APIServer 开启了 RBAC 访问控制,所以需要创建 tiller 使用的 service account: tiller 并分配合适的角色给它。
详细内容可以查看helm文档中的 Role-based Access Control。 这里简单起见直接分配 cluster- admin 这个集群内置的 ClusterRole 给它。
创建 rbac-config.yaml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
1
2
3
4
5
kubectl create -f rbac-config.yaml
》serviceaccount/tiller created
》clusterrolebinding.rbac.authorization.k8s.io/tiller created

helm init --service-account tiller --skip-refresh

tiller

tiller 默认被部署在 k8s 集群中的 kube-system 这个 namespace 下

1
2
3
4
5
6
7
8
kubectl get pod -n kube-system -l app=helm
》NAME READY STATUS RESTARTS AGE
》tiller-deploy-c4fd4cd68-dwkhv 1/1 Running 0 83s


helm version
》Client: &version.Version{SemVer:"v2.13.1", GitCommit:"618447cbf203d147601b4b9bd7f8c37a5d39fbb4", GitTreeState:"clean"}
》Server: &version.Version{SemVer:"v2.13.1", GitCommit:"618447cbf203d147601b4b9bd7f8c37a5d39fbb4", GitTreeState:"clean"}

自定义模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# 创建文件夹
mkdir ./hello-world
cd ./hello-world

# 创建自描述文件 Chart.yaml , 这个文件必须有 name 和 version 定义
cat > ./Chart.yaml << 'EOF'
name: hello-world
version: 1.0.0
EOF

# 创建模板文件, 用于生成 Kubernetes 资源清单(manifests)
mkdir ./templates
cat > ./templates/deployment.yaml << 'EOF'
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 1
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello-world
image: hub.atguigu.com/library/myapp:v1
ports:
- containerPort: 80
protocol: TCP
EOF


cat > ./templates/service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: hello-world
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app: hello-world
EOF

# 使用命令 helm install RELATIVE_PATH_TO_CHART 创建一次Release
helm install .

# 列出已经部署的 Release
helm ls

# 查询一个特定的 Release 的状态
helm status RELEASE_NAME

# 移除所有与这个 Release 相关的 Kubernetes 资源
helm delete cautious-shrimp

# 版本回滚 helm rollback RELEASE_NAME REVISION_NUMBER
helm rollback cautious-shrimp 1

# 使用 helm delete --purge RELEASE_NAME 移除所有与指定 Release 相关的 Kubernetes 资源和所有这个 Release 的记录
helm delete --purge cautious-shrimp
helm ls --deleted




# 配置体现在配置文件 values.yaml
cat > ./values.yaml << 'EOF'
image:
repository: gcr.io/google-samples/node-hello
tag: '1.0'
EOF

# 这个文件中定义的值,在模板文件中可以通过 .VAlues对象访问到
cat > ./templates/deployment.yaml << 'EOF'
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 1
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello-world
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
ports:
- containerPort: 8080
protocol: TCP
EOF


# 在 values.yaml 中的值可以被部署 release 时用到的参数 --values YAML_FILE_PATH 或 --set key1=value1, key2=value2 覆盖掉
helm install --set image.tag='latest' .

# 升级版本
helm upgrade -f values.yaml test .



# debug
# 使用模板动态生成K8s资源清单,非常需要能提前预览生成的结果。
# 使用--dry-run --debug 选项来打印出生成的清单文件内容,而不执行部署
helm install . --dry-run --debug --set image.tag=latest

部署 dashboard

kubernetes-dashboard.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cat > kubernetes-dashboard.yaml << 'EOF'
image:
repository: k8s.gcr.io/kubernetes-dashboard-amd64
tag: v1.10.1
ingress:
enabled: true
hosts:
- k8s.frognew.com
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
tls:
- secretName: frognew-com-tls-secret
hosts:
- k8s.frognew.com
rbac:
clusterAdminRole: true
EOF



helm install stable/kubernetes-dashboard -n kubernetes-dashboard --namespace kube-system -f kubernetes-dashboard.yaml

kubectl -n kube-system get secret | grep kubernetes-dashboard-token
》省略输出

# 修改 ClusterIP 为 NodePort
kubectl edit svc kubernetes-dashboard -n kube-system

部署 metrics-server

Heapster GitHub 中已经声明 DEPRECATED 废弃了。
从 Kubernetes 1.12 开始将从 Kubernetes 各种安装脚本中移除。
Kubernetes 推荐使用 metrics-server。

metrics-server.yaml

1
2
3
4
args:
- --logtostderr
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP
1
2
3
4
5
6
7
8
9
10
11
12
13
helm install stable/metrics-server -n metrics-server --namespace kube-system -f metrics-server.yaml

# 使用下面的命令可以获取到关于集群节点基本的指标信息:
kubectl top node##
》NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
》node1 650m 32% 1276Mi 73%
》node2 73m 3% 527Mi 30%


kubectl top pod --all-namespaces
》NAMESPACE NAME CPU(cores) MEMORY(bytes)
》ingress-nginxnginx-ingress-controller-6f5687c58d-jdxzk 3m 142Mi
》ingress-nginx nginx-ingress-controller-6f5687c58d-lxj5q 5m 146Mi

部署Prometheus

Prometheus github 地址

组件说明

  • 1.MetricServer:是kubernetes集群资源使用情况的聚合器,收集数据给kubernetes集群内使用,如kubectl,hpa,scheduler等。
  • 2.PrometheusOperator:是一个系统监测和警报工具箱,用来存储监控数据。
  • 3.NodeExporter:用于各node的关键度量指标状态数据。
  • 4.KubeStateMetrics:收集kubernetes集群内资源对象数 据,制定告警规则。
  • 5.Prometheus:采用pull方式收集apiserver,scheduler,controller-manager,kubelet组件数 据,通过http协议传输。
  • 6.Grafana:是可视化数据统计和监控平台。

构建记录

1
2
git clone https://github.com/coreos/kube-prometheus.git
cd /root/kube-prometheus/manifests
  • 修改 grafana-service.yaml 文件,使用 nodepode 方式访问 grafana:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    vim grafana-service.yaml

    apiVersion: v1
    kind: Service
    metadata:
    name: grafana
    namespace: monitoring
    spec:
    type: NodePort #添加内容
    ports:
    - name: http
    port: 3000
    targetPort: http
    nodePort: 30100 #添加内容
    selector:
    app: grafana
  • 修改 prometheus-service.yaml,改为 nodepode

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    vim prometheus-service.yaml

    apiVersion: v1
    kind: Service
    metadata:

    name: prometheus-k8s
    namespace: monitoring
    labels:
    prometheus: k8s
    spec:
    type: NodePort
    ports:
    - name: web
    port: 9090
    targetPort: web
    nodePort: 30200
    selector:
    app: prometheus
    prometheus: k8s
  • 修改 alertmanager-service.yaml,改为 nodepode

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    vim alertmanager-service.yaml

    apiVersion: v1
    kind: Service
    metadata:
    name: alertmanager-main
    namespace: monitoring
    labels:
    alertmanager: main
    spec:
    type: NodePort
    ports:
    - name: web
    port: 9093
    targetPort: web
    nodePort: 30300
    selector:
    alertmanager: main
    app: alertmanager

HPA

Horizontal Pod Autoscaling 可以根据 CPU 利用率自动伸缩一个 Replication Controller、Deployment 或者 Replica Set 中的 Pod 数量

1
2
3
4
5
6
7
8
9
kubectl run php-apache --image=gcr.io/google_containers/hpa-example --requests=cpu=200m --expose --port=80

# 创建 HPA 控制器
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

# 增加负载,查看负载节点数目
kubectl run -i --tty load-generator --image=busybox /bin/sh

while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done

资源限制 - pod

Kubernetes 对资源的限制实际上是通过 cgroup 来控制的,cgroup 是容器的一组用来控制内核如何运行进程的相关属性集合。针对内存、CPU 和各种设备都有对应的 cgroup

默认情况下,Pod 运行没有 CPU 和内存的限额。 这意味着系统中的任何 Pod 将能够像执行该 Pod 所在的节点一样,消耗足够多的 CPU 和内存 。
一般会针对某些应用的 pod 资源进行资源限制,这个资源限制是通过resources 的 requests 和 limits 来实现

requests 要分分配的资源,limits 为最高请求的资源值。可以简单理解为初始值和最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spec:
containers:
- image: xxxx
imagePullPolicy: Always
name: auth
ports:
- containerPort: 8080
protocol: TCP
resources:
limits:
cpu: "4"
memory: 2Gi
requests:
cpu: 250m
memory: 250Mi

资源限制 - namespace

Ⅰ、计算资源配额

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
namespace: spark-cluster
spec:
hard:
pods: "20"
requests.cpu: "20"
requests.memory: 100Gi
limits.cpu: "40"
limits.memory: 200Gi

Ⅱ、配置对象数量配额限制

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: spark-cluster
spec:
hard:
configmaps: "10"
persistentvolumeclaims: "4"
replicationcontrollers: "20"
secrets: "10"
services: "10"
services.loadbalancers: "2"

Ⅲ、配置 CPU 和 内存 LimitRange
、default 即 limit 的值
、defaultRequest 即 request 的值

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default:
memory: 50Gi
cpu: 5
defaultRequest:
memory: 1Gi
cpu: 1
type: Container

访问 prometheus

  • prometheus 对应的 nodeport 端口为 30200,访问 http://MasterIP:30200
    在这里插入图片描述

  • 通过访问http://MasterIP:30200/target 可以看到 prometheus 已经成功连接上了 k8s 的 apiserver
    在这里插入图片描述

  • 查看 service-discovery
    在这里插入图片描述

  • Prometheus 自己的指标
    在这里插入图片描述

  • prometheus 的 WEB 界面上提供了基本的查询 K8S 集群中每个 POD 的 CPU 使用情况,查询条件如下:
    sum by (pod_name)( rate(container_cpu_usage_seconds_total{image!="", pod_name!=""}[1m] ) )
    在这里插入图片描述

上述的查询有出现数据,说明 node-exporter 往 prometheus 中写入数据正常,接下来我们就可以部署 grafana 组件,实现更友好的 webui 展示数据了

访问 grafana

  • 查看 grafana 服务暴露的端口号:

    1
    2
    kubectl get service -n monitoring | grep grafana
    》grafana NodePort 10.107.56.143 <none> 3000:30100/TCP 20h
  • 如上可以看到 grafana 的端口号是 30100,浏览器访问 http://MasterIP:30100 用户名密码默认 admin/admin
    在这里插入图片描述

  • 修改密码并登陆
    在这里插入图片描述

  • 添加数据源 grafana 默认已经添加了 Prometheus 数据源,grafana 支持多种时序数据源,每种数据源都有各自的查询编辑器
    在这里插入图片描述

  • Prometheus 数据源的相关参数:
    在这里插入图片描述

  • 目前官方支持了如下几种数据源:
    在这里插入图片描述

部署 EFK

Elasticsearch + Filebeat + Kibana

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 添加 Google incubator 仓库
helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator

# 部署 Elasticsearch
kubectl create namespace efk
helm fetch incubator/elasticsearch
helm install --name els1 --namespace=efk -f values.yaml incubator/elasticsearch
kubectl run cirror-$RANDOM --rm -it --image=cirros -- /bin/sh curl Elasticsearch:Port/_cat/nodes

# 部署 Fluentd
helm fetch stable/fluentd-elasticsearch
vim values.yaml # 更改其中 Elasticsearch 访问地址
helm install --name flu1 --namespace=efk -f values.yaml stable/fluentd-elasticsearch

# 部署 kibana
helm fetch stable/kibana --version 0.14.8
helm install --name kib1 --namespace=efk -f values.yaml stable/kibana --version 0.14.8

11、修改证书时间

2021-05-14 18:43

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 1、go 环境部署
wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz
tar -zxvf go1.12.1.linux-amd64.tar.gz -C /usr/local
vi /etc/profile
export PATH=$PATH:/usr/local/go/bin
source /etc/profile

# 2、下载源码
cd /data
git clone https://github.com/kubernetes/kubernetes.git
git checkout -b remotes/origin/release-1.15.1 v1.15.1

# 3、修改 Kubeadm 源码包更新证书策略
vim staging/src/k8s.io/client-go/util/cert/cert.go # kubeadm 1.14 版本之前
vim cmd/kubeadm/app/util/pkiutil/pki_helpers.go # kubeadm 1.14 至今
》const duration365d = time.Hour * 24 * 365
》NotAfter: time.Now().Add(duration365d).UTC(),
make WHAT=cmd/kubeadm GOFLAGS=-v
cp _output/bin/kubeadm /root/kubeadm-new

# 4、更新 kubeadm(将 kubeadm 进行替换)
cp /usr/bin/kubeadm /usr/bin/kubeadm.old
cp /root/kubeadm-new /usr/bin/kubeadm
chmod a+x /usr/bin/kubeadm

# 5、更新各节点证书至 Master 节点
cp -r /etc/kubernetes/pki /etc/kubernetes/pki.old
cd /etc/kubernetes/pki
kubeadm alpha certs renew all --config=/root/kubeadm-config.yaml
openssl x509 -in apiserver.crt -text -noout | grep Not

# 6、HA集群其余 mater 节点证书更新
#!/bin/bash

masterNode="192.168.66.20 192.168.66.21"
#for host in ${masterNode}; do
# scp /etc/kubernetes/pki/{ca.crt,ca.key,sa.key,sa.pub,front-proxy-ca.crt,front-proxy-ca.key} "${USER}"@$host:/etc/kubernetes/pki/
# scp /etc/kubernetes/pki/etcd/{ca.crt,ca.key} "root"@$host:/etc/kubernetes/pki/etcd
# scp /etc/kubernetes/admin.conf "root"@$host:/etc/kubernetes/
#done

for host in ${CONTROL_PLANE_IPS}; do
scp /etc/kubernetes/pki/{ca.crt,ca.key,sa.key,sa.pub,front-proxy-ca.crt,front-proxy-ca.key} "${USER}"@$host:/root/pki/
scp /etc/kubernetes/pki/etcd/{ca.crt,ca.key} "root"@$host:/root/etcd
scp /etc/kubernetes/admin.conf "root"@$host:/root/kubernetes/
done

12、k8s 高可用集群搭建

系统初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 设置系统主机名以及 Host 文件的相互解析
hostnamectl set-hostname k8s-master01

# 安装依赖包
yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget vim net-tools git

# 设置防火墙为 Iptables 并设置空规则
systemctl stop firewalld
systemctl disable firewalld

yum -y install iptables-services
systemctl start iptables
systemctl enable iptables
iptables -F
service iptables save

# 关闭 SELINUX
swapoff -a && sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

# 调整内核参数,对于 K8S
cat > kubernetes.conf << 'EOF'
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv4.tcp_tw_recycle=0
vm.swappiness=0 # 禁止使用 swap 空间,只有当系统 OOM 时才允许使用它
vm.overcommit_memory=1 # 不检查物理内存是否够用
vm.panic_on_oom=0 # 开启 OOM
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1
net.netfilter.nf_conntrack_max=2310720
EOF
cp kubernetes.conf /etc/sysctl.d/kubernetes.conf
sysctl -p /etc/sysctl.d/kubernetes.conf

# 调整系统时区
timedatectl set-timezone Asia/Shanghai # 设置系统时区为 中国/上海
timedatectl set-local-rtc 0 # 将当前的 UTC 时间写入硬件时钟
systemctl restart rsyslog && systemctl restart crond # 重启依赖于系统时间的服务

# 关闭系统不需要服务
systemctl stop postfix && systemctl disable postfix

# 设置 rsyslogd 和 systemd journald
mkdir /var/log/journal # 持久化保存日志的目录
mkdir /etc/systemd/journald.conf.d
cat > /etc/systemd/journald.conf.d/99-prophet.conf << 'EOF'
[Journal]

# 持久化保存到磁盘
Storage=persistent

# 压缩历史日志
Compress=yes

SyncIntervalSec=5m
RateLimitInterval=30s
RateLimitBurst=1000

# 最大占用空间 10G
SystemMaxUse=10G

# 单日志文件最大 200M
SystemMaxFileSize=200M

# 日志保存时间 2 周
MaxRetentionSec=2week

# 不将日志转发到syslog
ForwardToSyslog=no

EOF

systemctl restart systemd-journald


# 升级系统内核为 4.44
CentOS 7.x 系统自带的 3.10.x 内核存在一些 Bugs,导致运行的 Docker、Kubernetes 不稳定。

rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm

# 安装完成后检查 /boot/grub2/grub.cfg 中对应内核 menuentry 中是否包含 initrd16 配置,如果没有,再安装 一次!
yum --enablerepo=elrepo-kernel install -y kernel-lt

# 设置开机从新内核启动
grub2-set-default "CentOS Linux (4.4.182-1.el7.elrepo.x86_64) 7 (Core)"

# 重启后安装内核源文件
yum --enablerepo=elrepo-kernel install kernel-lt-devel-$(uname -r) kernel-lt-headers-$(uname -r)


# 关闭 NUMA
cp /etc/default/grub{,.bak}
vim /etc/default/grub # 在 GRUB_CMDLINE_LINUX 一行添加 `numa=off` 参数,如下所示:

diff /etc/default/grub.bak /etc/default/grub
6c6
< GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rhgb quiet"
---
> GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=centos/root rhgb quiet numa=off"

cp /boot/grub2/grub.cfg{,.bak}
grub2-mkconfig -o /boot/grub2/grub.cfg

Kubeadm

2021-05-17 10:39:07

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# kube-proxy开启ipvs的前置条件
modprobe br_netfilter
cat > /etc/sysconfig/modules/ipvs.modules << 'EOF'
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules
bash /etc/sysconfig/modules/ipvs.modules
lsmod | grep -e ip_vs -e nf_conntrack_ipv4



# 安装 Docker
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y update && yum -y install docker-ce
mkdir /etc/docker # 创建 /etc/docker 目录
# 配置守护进程
cat > /etc/docker/daemon.json << 'EOF'
{
"exec-opts":[
"native.cgroupdriver=systemd"
],
"log-driver":"json-file",
"log-opts":{
"max-size":"100m"
}
}
EOF
mkdir -p /etc/systemd/system/docker.service.d
systemctl daemon-reload # 让守护进程重新加载
systemctl restart docker # 重启docker服务
systemctl enable docker # 开机自启动



# 在主节点启动 Haproxy 与 Keepalived 容器
# 导入脚本 > 运行 > 查看可用节点



# 安装 Kubeadm (主从配置)
cat > /etc/yum.repos.d/kubernetes.repo << 'EOF'
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
yum -y install kubeadm-1.15.1 kubectl-1.15.1 kubelet-1.15.1
systemctl enable kubelet.service



# 初始化主节点
kubeadm config print init-defaults > kubeadm-config.yaml
kubeadm init --config=kubeadm-config.yaml --experimental-upload-certs | tee kubeadm-init.log



# 加入主节点以及其余工作节点
# 执行安装日志中的加入命令即可



# Etcd 集群状态查看
kubectl -n kube-system exec etcd-k8s-master01 -- etcdctl \
--endpoints=https://192.168.92.10:2379 \
--ca-file=/etc/kubernetes/pki/etcd/ca.crt \
--cert-file=/etc/kubernetes/pki/etcd/server.crt \
--key-file=/etc/kubernetes/pki/etcd/server.key cluster-health

kubectl get endpoints kube-controller-manager --namespace=kube-system -o yaml
kubectl get endpoints kube-scheduler --namespace=kube-system -o yaml



# 部署网络
kubectl apply -f kube-flannel.yml

在这里插入图片描述