kubernetes 基础干货

古人已用三冬足,年少今开万卷余。这篇文章主要讲述kubernetes 基础干货相关的知识,希望能为你提供帮助。


1. 应用程序生命周期

#创建应用
kubectl create deployment web --image=nginx

#使用service 将pod暴露出去
kubectlexpose deploymentweb --port=80--type=NodePort--target-port=80 --name=web

#查看
kubectlget pods,svc

#访问应用
http://NodeIP:Port#端口随机生成,通过get svc获取

2. YAML 文件创建资源对象
kubectlcreate创建一个资源
kubectlapply从一个文件创建或者更新资源

标签重要性: 通过标签关联不同的资源

#查看service 关联的pod ip
kubectlget endpoints

apiVersion API版本
kind 资源类型
metadata 资源元数据
spec 资源规格
replicas 副本(实例)数量
selector 标签选择器,与下面metadata.labels 保持一致
template pod模板
spec pod 的规格
containers 容器配置
3. Deployment 介绍
Deployment 是最常用的k8s 工作负载控制器,是k8s 的一个抽象概念,用于更高级层次对象,部署和管理pod。

主要功能:
管理Pod 与ReplicaSet
具有上线部署、副本设定、滚动升级、回滚等功能
提供声明式更新,例如只更新一个新的image
应用场景: 网站API微服务

deployment 应用生命周期管理流程
应用程序——部署——升级——回滚——下线
当执行apply 部署应用时,deployment 向RS pod 扩容副本数量


回滚
kubectl rollouthistorydeployment/web查看历史版本
kubectlrollout undodeployment/web回滚上一个版本
kubectlrollout undodeployment/web--to-revision=2 回滚历史指定版本
#回滚是重新部署某一次部署时的状态,即当时版本所有配置

4. Pod对象
Pod 是kubernetes 创建和管理的最小单元,一个pod 由一个容器或多个容器组成,这些容器共享存储、网络

POD的特点
一个pod 可以理解为一个应用实例,提供服务
Pod中容器始终部署一个Node上
Pod 中容器共享网络,存储资源
kubernetes直接管理pod,而不是容器

pod的主要用法:
运行单个容器:最常见的用法,在这种情况下,可以将pod看做是单个容器的抽象封装
运行多个容器: 封装多个紧密耦合且需要共享资源的应用程序

管理命令
创建pod:
kubectlapply -f pod.yaml
或者使用命令: kubectl runnginx --image=nginx

查看pod:
kubectl get pods
kubectl describe pod < pod名称> -nnamesapce

查看日志:
kubectl logs < pod名称> -n namesapces
kubectl logs < pod名称> -nnamespaces

进入终端
kubectl exec < pod名称> -nnamespaces bash/sh

删除pod
kubectl delete pod < pod名称>

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

健康检查有以下两种类型:
* livenessProbe(存活检查): 如果检查失败了,将杀死容器,根据pod的 restartPolicy 来操作
* readinessprobe(就绪检查): 如果检查失败,kubernetes会把pod 从service endpoints 剔除

支持以下三种检查方法:
httpGet: 发送HTTP请求,返回200-400范围状态码为成功
exec: 执行shell 命令返回状态码是0为成功
tcpSocket: 发起TCP Socket建立成功

环境变量
变量值得几种定义方式
* 自定义变量值
* 变量值从Pod 属性获取
* 变量值从Secret、ConfigMap 获取

kubernetes 调度
kubernetes 是基于list-watch机制控制的架构,实现组件间交互的解耦。
其他组件监控自己负责的资源,当这些资源发生变化时,kube-apiserver 会通知这些组件,这个过程类似发布与订阅

master: apiservercontroller-managerscheduler
node:kube-proxykubeletdocker

创建一个pod的流程
1. kubelet 向apiserver发送一个创建pod 的请求
2. apiserver 接收到并向etcd 写入存储,写入成功后返回一个提示
3. scheduler 向apiserver查询未分配的pod资源,通过自身调度算法选择一个合适node 进行绑定(给这个pod打一个标记,标记分配到node1)
4. kubelet 向apiserver 查询分配到自己节点的pod,调用docker api(/var/run/docker.sock) 创建容器
5. kubelet 获取docker 创建容器的状态,并汇报给apiserver,apiserver 更新状态到etcd 存储
6. kubectl get pods 就能查看到pod状态

资源限制对调度的影响
apiVersion: v1
kind: Pod
metadata:
labels:
run: resources
name: resources
spec:
containers:
- image: nginx
name:web
resources:
#最小资源
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 1000Mi
#pod会根据Request 的值去查找有足够资源的Node来调度此Pod
limits 是最大可用资源,一般是requests的20% 左右,不太超出太多
limits 不能小于requests
reqeusts 只是一个预留机制,不是pod配置写多少,宿主机就会占多少资源
k8s 根据reqeusts来统计每个节点预分配资源,来判断下一个pod 能不能分配到这个节点

nodeSelector & nodeAffinity
nodeSelector: 用于将pod调度到匹配Label的Node上,如果没有匹配的标签会调度失败。
作用: 约束pod到特定节点运行
完全匹配节点标签
应用场景:
专用节点: 根据业务线将node分组管理
配置特殊硬件: 部分node 配有SSD硬盘 GPU

第一步 给节点打一个标签
kubectl label nodek8s-node1disktype=ssd

第二步 pod的yaml修改
root@k8s-master-01:~# cat resources1.yaml
apiVersion: v1
kind: Pod
metadata:
name: resources-ssh
spec:
nodeSelector:
disktype: "ssd"
containers:
- image: nginx
name:web
kubectlapply -f resources1.yaml

#然后查看是否跑到特定的节点上
kubectlget pod -o wide

nodeAffinity
节点亲和性类似于nodeSelector,可以根据节点上的标签来约束pod可以调度哪些节点
相比nodeSelector:
匹配有更多的逻辑组合,不只是字符串的完全相等,支持的操作
符有: In、Notln、Exists、DoesNotExist、Gt、Lt
调度氛围软策略和硬策略,而不是硬性要求
硬(required): 必须满足
软(preferred): 尝试满足,但不保证

# 硬性限制
cat resources1.yaml
apiVersion: v1
kind: Pod
metadata:
name: nodeaffinity-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: type### key的值
operator: In
values:
- gpu### values的值
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent


#软性限制
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1# 权重值,1-100,权重越大,调度到匹配节点的概率越大
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent

Taint(污点)与Tolerations(污点容忍)
Taints: 避免Pod调度到特定Node 上
Tolerations: 允许pod调度到持有Taints的Node上

应用场景:
专用节点: 根据业务线将Node分组管理,希望在默认情况下不调度该节点,只有配置了污点容忍才允许分配
配备特殊硬件: 部分Node 配有SSD硬盘、GPU,希望在默认情况下不调度该节点,只有配置看了污点容忍才允许配置
基于Taint的驱逐

Taint污点
格式
kubectltaintnode [node] key=value:[effect]

# 例子
kubectltaint node k8s-node1 gpu=yes:NoSchedule

其中[effect]可取值:
NoSchedule: 一定不能被调度
PreferNoSchedule: 尽量不要调度,非必须配置容忍
NoExecute: 不仅不会调整,还会驱逐Node上已有的Pod

# 污点容忍是可以直接放宽条件写的,例如calico 不分key 与value,符合带有污点的是NoSchedule
tolerations:
- effect: NoSchedule
opeartor: Exists

nodeName
nodeName 指定节点名称,不经过调度器,并且给定节点上运行的kubelet 进程尝试执行该Pod,因此,如果nodeName在PodSpec中指定了,则它优先于上面的节点选择方法。
例子:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01

service 介绍
将运行在一组pod上的应用程序公开为网络服务的抽象方法。
使用kubernetes,你无需修改应用程序即可使用不熟悉的服务发现机制,kubernetes 为pods提供自己的IP地址,并为一组Pod 提供相同的DNS 名,并且可以在它们之间进行负载均衡。

使用Pod IP就面临着ip 变化的情况,因为Pod 是非永久性资源,如果使用deployment 来运行你的应用程序,则它可以动态创建和销毁Pod,这样的话就会导致ip 随时变化。

# server 资源
kubernetes Service 是一种抽象的定义,Service 是不可变的,除非删除,这样的话可以有效的解决Pod IP变动的话问题,也可以有效的解耦这种关系。 即使有多个Pod,那么只需要连接一个可用的Service 即可解决负载问题。

定义Service
apiVersion: v1
kind: Service
metadata:
name: my-service#Service 的名字
spec:
selector:
app: MyApp# 指定关联Pod的标签
ports:
- protocol: TCP#协议
port: 80# service 端口
targetPort: 9376# 容器端口(应用程序监听端口)
type: ClusterIP#服务类型

Service 三种常用类型
##ClusterIP
ClusterIP 集群内部使用,只能在k8s 集群内

## NodePort
NodePort对外暴露应用,通过每个节点上的IP和静态端口(NodePort)暴露服务。NodePort 服务会路由到自动创建的ClusterIP 服务。通过请求< 节点IP> :< 节点端口> ,可以从集群的外部访问一个NodePort服务,端口范围30000-32767 在apiserver 里面可以修改端口范围。

NodePort 会在每台Node 上监听端口接收用户流量,

###LoadBalancer
LoadBalancer 对外暴露应用,适用公有云

Pod 对外暴露: 集群之内的其他应用

Service 代理模式 ipvs
在ipvs 模式下,kube-proxy 监视kubernetes 服务和端点,调用netlink 接口相应地创建IPVS规则,并定期将IPVS规则与kubernetes 服务和端点同步,该控制循环可确保IPVS状态与所需状态匹配。访问服务时,IPVS将流量定向到后端Pod之一。
IPVS 代理模式基于类似于iptables模式的netfilter 挂钩函数,但是使用哈希表作为基础数据结构,并且在内核空间中工作。这意味着,与iptables 模式下的kube-proxy 相比,IPVS 模式下的kube-proxy 重定向通信延迟要短,并且在同步代理规则时具有更好的性能。与其他代理模式相比,IPVS模式还支持更高的网络流量吞吐量。

IPVS 提供了更多选项来平衡后端Pod 的流量,这些是:
rr: 轮替
lc: 最少链接
dn: 目标地址哈希
sh: 源地址哈希
sed: 最短预期延迟
nq:从不排队

【kubernetes 基础干货】
iptables
kube-proxy 会监视kubernetes控制节点对Service 对象和Endpoints 对象的添加和移除。对每个Service,它会配置iptables 规则,从而捕获到达该Service 的clusterIP 和端口的请求,进而将请求重定向到Service的一组后端中的某个Pod上面。对于Endpoints对象,它也会配置iptables 规则,这个规则会选择一个后端组合。


DNS
coresDNS: 是一个DNS 服务器,kubernetes 默认采用,以Pod 部署在集群中,CoreDNS 服务监视kubernetes API,为每一个Service 创建DNS 记录用于域名解析

ClusterIPA 记录格式< service-name> .< namespace-name> .svc.cluster.local

Ingress
ingress 公开了从集群外部到集群内服务的HTTP与HTTPS路由规则集合,而具体实现流量路由是有Ingress Controller 负责。
官网文档
https://kubernetes.io/zh/docs/concepts/services-networking/ingress/

需要先部署ingress-controlle 组件
https://github.com/kubernetes/ingress-nginx

创建ingress-nginx 规则
cat ingress-web.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
rules:
- host: web.xingxing.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web# 关联service 的名称
port:
number: 80

## ingress 配置https

Ingress Controller怎么工作的?
Ingress Controller 通过与kubernetes API 交互,动态的去感知集群中Ingress 规则变化,然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段Nginx 配置,应用到管理的Nginx 服务,然后热加载生效。 以此来达到Nginx 负载均衡器配置与动态更新的问题

网络存储卷 NFS
NFS卷: 提供对NFS挂载支持,可以自动将NFS共享路径挂载到Pod 中
# 安装nfs 插件yuminstallnfs-utils-y

ConfigMap
ConfigMap 是一种API 对象,用来将非机密性的数据保存到键值对中。使用时,Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。
ConfigMap 将你的环境配置信息和容器镜像解耦,便于应用配置的修改

例子
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# 类属性键;每一个键都映射到一个简单的值
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"

# 类文件键
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true

#以环境变量方式使用的ConfigMap 数据不会被自动更新。更新这些数据需要重新启动Pod

apiVersion: v1
kind: Pod
metadata:
name: configmap-demo-pod
spec:
containers:
- name: demo
image: alpine
command: ["sleep", "3600"]
env:
# 定义环境变量
- name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的
valueFrom:
configMapKeyRef:
name: game-demo# 这个值来自 ConfigMap
key: player_initial_lives # 需要取值的键
- name: UI_PROPERTIES_FILE_NAME
valueFrom:
configMapKeyRef:
name: game-demo
key: ui_properties_file_name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
# 你可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中
- name: config
configMap:
# 提供你想要挂载的 ConfigMap 的名字
name: game-demo
# 来自 ConfigMap 的一组键,将被创建为文件
items:
- key: "game.properties"
path: "game.properties"
- key: "user-interface.properties"
path: "user-interface.properties"

Secret
Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和SSH秘钥。
kubernetes Secret 默认情况下存储为base64-编码的、非加密的字符串。默认情况下,能够访问API 的任何人,或者能够访问kubernetes 下层数据存储(etcd)的任何人都可以明文方式读取这些数据。为了能够安全地使用Secret,可以配合使用RBAC 规则来限制。

kubernetes 安全框架
k8s 安全控制框架主要由下面3个阶段进行控制,每一个阶段都支持插件方式,通过API Server 配置来启用插件。

1. Authentication(鉴权)
2. Authorization(授权)
3. Admission Control(准入控制)
客户端要访问k8s集群API Server,一般需要证书、Token 或者用户名+密码;如果Pod 访问,需要ServiceAccount
#k8s APIserver 提供三种客户端身份认证
https 证书认证: 基于CA证书签名的数字证书认证(kubeconfig)
http Token 认证: 通过一个Token 来识别用户(serviceaccount)
http Base认证: 用户名+密码的方式认证(已弃用)

RBAC(Role-Based Access Control,基于角色的访问控制:负责完成授权(Authorization))工作。
RBAC根据API 请求属性,决定允许还是拒绝。
比较常见的授权维度:
user: 用户
group: 用户分组
资源,例如pod、deployment
资源操作方法: getlistcreate updatepatch watch delete
命名空间
API 组

准入控制(Admission Control )
Admission Control 实际上市一个准入控制器插件列表,发送到API Server 的请求都需要经过这个列表中的每个准入控制器插件的检查,检查不通过,则拒绝请求。

启用准入控制
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

关闭准入控制
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...

查看插件哪些是默认启用的
kube-apiserver -h | grep enable-admission-plugins

基于角色的权限访问控制: RBAC
RBAC (Role-Based Access Control, 基于角色的访问控制)
是k8s 默认授权策略,并且是动态配置策略(修改即时生效)。

#主体(subject)
User 用户
Group 用户组
SeriveAccount 服务账号

#角色
Role 授权特定命名空间的访问权限
ClusterRole: 授权所有命名空间的访问权限

# 角色绑定
RoleBinding 将角色绑定到主体(subject) # 指定的命名空间中执行授权
ClusterRoleBinding 将集群角色绑定到主体 #在集群范围执行授权

etcd 集群备份恢复
#单实例备份
## 备份
ETCDCTL_API=3 etcdctl snapshot savesnap.db --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt--cert=/etc/kubernetes/pki/etcd/server.crt--key=/etc/kubernetes/pki/etcd/server.key

## 恢复
#1. 先暂停kube-apiserver和etcd容器
mv/etc/kubernetes/manifests//etc/kubernetes/manifests.bak
mv /var/lib/etcd//var/lib/etcd.bak
#2. 恢复
ETCDCTL_API=3 etcdctl snapshotrestore snap.db--data-dir=/var/lib/etcd

#3. 启动kube-apiserver 和etcd容器
mv /etc/kubernetes/manifests.bak/etc/kubernetes/manifests


####集群的备份,备份文件使用一个节点上的,
--endpoints=IP地址是主机自己的IP地址

#备份
ETCDCTL_API=3 etcdctl snapshot savesnap.db --endpoints=https://$IP:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt--cert=/etc/kubernetes/pki/etcd/server.crt--key=/etc/kubernetes/pki/etcd/server.key

#集群恢复
1. 先暂停kube-apiserver 和etcd
###二进制
systemctl stop kube-apiserver
systemctl stop etcd

#kubeadm 与单集群一样

###在每个master 节点上进行恢复
先将刚在master 节点上备份的数据拷贝到其他节点上。确保恢复数据的一致性
--name k8s-master-01(etcd 配置文件写的名称,每个节点都不一样)
--initial-advertise-peer-urls=https://10.39.60.175:2380(在那个节点恢复写哪个节点IP)
--data-dir=/var/lib/etcd # etcd 数据读取的目录,需要和配置文件一致

ETCDCTL_API=3 etcdctl snapshotrestore snap.db--namek8s-master-01--initial-cluster="k8s-master-01=https://10.39.60.175:2380,k8s-master-03=https://10.39.60.157:2380,k8s-master-02=https://10.39.60.154:2380" --initial-cluster-token=etcd-cluster--initial-advertise-peer-urls=https://10.39.60.175:2380--data-dir=/var/lib/etcd


### 启动kube-apiserver 和etcd容器
mv /etc/kubernetes/manifests.bak/etc/kubernetes/manifests##kubeadm 方式

### 二进制方式
systemctlstartetcd
systemctlstart kube-apiserver




    推荐阅读