k8s的安装教程可以看上一篇 https://developer.aliyun.com/article/806212
1.资源
k8s中所有的对象可以称为k8s的资源,之前安装k8s时就已经体验过k8s的资源了
k8s中资源的创建方式:
- 命令行
- yaml 如安装calico时使用的yaml
2. NameSpace
命名空间,主要用来隔离资源,默认不隔离网络
2.1 使用命令行操作名称空间:
# 查看名称空间
kubectl get ns/namespace
# 创建名称空间
kubectl create ns [名称空间名]
# 删除名称空间 会删除该名称空间下的所有资源
kubectl delete ns [名称空间名]
同样的,可以解析下之前用过的命令
# -A是获取所有资源,如果不加-A 会默认使用default名称空间 也可以指定名称空间
kubectl get pod -A
# 指定名称空间
kubectl get pod -n [名称空间名]
2.2 使用yaml操作名称空间
创建如下yaml配置文件 hello.yaml
创建时注意格式,注释需要去掉
apiVersion: v1 #版本号
kind: Namespace #资源类型 指定为namespace
metadata: #元数据
name: hello #命名空间名称
# 创建名称空间资源
kubectl apply -f hello.yaml
# 删除名称空间资源
kubectl delete -f hello.yaml
k8s中使用配置文件创建的资源都可以通过配置文件删除
3. pod
pod是k8s中运行的最小单位,也是运行的一组容器
pod相对于k8s来说,与docker中的容器概念类似
如下图所示,解释pod的概念
docker是k8s运行的基础环境,
pod是k8s的最小单位,但是一个pod中可以有多个容器在运行,不同的pod是相互隔离的
3.1 pod基本操作命令
3.1.1 命令行方式操作pod
# 创建pod
kubectl run [pod名] --image=[镜像名]
example:
kubectl run mynginx --image=nginx
# 查看pod创建日志
kubectl describe pod [pod名]
# 删除pod 可以在后面加上-n [namespace名]
kubectl delete pod [pod名]
3.1.2 YAML方式操作pod
创建如下内容yaml pod.yaml
apiVersion: v1
kind: Pod #资源类型
metadata:
labels:
run: mynginx
name: mynginx # pod名称
spec:
containers:
- image: nginx #镜像
name: mynginx
# 创建pod
kubectl apply -f pod.yaml
# 删除pod
kubectl delete -f pod.yaml
3.1.3 dashboard操作pod
界面化操作,略
3.1.4 pod的一些常用命令
pod的命令与docker 容器的相关命令类似
# 查看pod运行日志
kubectl logs [-f] [pod名]
# 查看pod详细信息 -owide查看详细信息 包括pod的ip
kubectl get pod [-n namespace] -owide
# 进入pod
kubectl exec -it [pod名] -- /bin/bash
3.1.5 多容器pod
创建如下内容的yaml, multicontainers.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: myapp
name: myapp
spec:
containers:
- image: nginx
name: nginx
- image: tomcat:8.5.68
name: tomcat
# 创建pod
kubectl apply -f multicontainers.yaml
一个pod中不能启动两个相同的容器,端口冲突导致启动失败
4. Deployment
通过Deployment使pod拥有多副本,自愈,扩缩容的能力
# 原来的创建pod
kubectl run mynginx --image=nginx
# 通过Deployment创建pod
kubectl create deployment my_deployment_nginx --image=nginx
通过deployment创建的pod,再删除(宕机)后会自动重新创建一个pod,这就是k8s的自愈能力
# 删除deployment
kubectl delete deploy my_deployment_nginx
4.1 多副本
deployment可以启动多个pod,称之为副本
# 创建多副本 replicas副本数
kubectl create deployment my_tomcat_deployment --image=tomcat:8.5.68 --replicas=3
dashboard同样可以操作
4.2 扩缩容
通过deployment可以对pod进行扩缩容,增加/减少pod的数量
4.2.1 命令行扩缩容
# 扩缩容 指定副本数即可
kubectl scale deployment/[deployment名] --replicas=5
4.2.2 yaml扩缩容
# 使用edit命令
kubectl edit deploy [deployment名]
# 修改replicas属性 保存后即可
4.2.3 dashboard扩缩容
4.3 自愈&故障转移
自愈: pod发生故障后,k8s重启该pod后正常,称为自愈
故障转移: pod所在机器可能发生故障(断电等特殊情况),k8s在其他节点重新拉起故障节点的pod,称为故障转移
# 测试自愈 在某个pod所在节点 停止容器
docker stop [容器名]
# 可以看到k8s会自动重启该pod
# 测试故障转移,直接将某个pod所在节点关机
# 可以看到5min后,k8s会重启拉起一个pod
4.4 滚动更新
滚动更新: 在修改deployment的镜像时(升级),会先新启一个新版本的pod,当新版本的pod启动后,再停止老版本的pod,依次对老版本的pod进行此操作,最终达到更新镜像的效果
# 滚动更新 镜像名可以通过deployment的yaml中寻找,就是- image下的name属性
kubectl set image deploy/[deployment名] [镜像名]=[新镜像名] --record
4.5 版本回退
一个deployment经过多次更新后,会产生多个版本,可以通过版本回退回退到某一个版本
# 查看历史版本 加--revision 可以看到某一个版本的详情
kubectl rollout history deployment/[deployment名]
# 回退到某一个版本 不加--revision默认会回滚到上一个版本
kubectl rollout undo deployment/[deployment名] --revision=[通过查看历史看到的版本号]
# 查看回滚后的deployment的yaml配置文件
kubectl get deploy/[deployment名] -oyaml | grep [查找项]
版本回退的过程也是一次滚动更新
4.6 其他工作负载
deployment是其中一种工作负载
deployment是无状态的,针对于微服务这种业务数据都存在与中间件中的应用
除此之外,还有其他的工作负载
- 有状态工作负载: StatefulSet 提供稳定的存储,网络等功能
- 守护应用工作负载: DaemonSet 比如日志收集组件,每个机器运行一份
- 定时应用工作负载: Job/CronJob 比如垃圾清理组件,定时运行
5. Service
Service:是将多个pod对外暴露成一个服务,可以实现对pod的服务发现和负载均衡
举例:
有三个微服务pod,每个pod有自己的IP,前端调用的时候无法自动的负载,需要后台来暴露出唯一的一个ip,通过service就可以做到,而且当某一个pod挂了之后,Service就可以将之后的流量打入其他两个pod中,实现服务发现的功能
# 暴露服务 --port为暴露端口 --target-port为目标端口即原pod的端口
kubectl expose deploy [deployment名] --port=8000 --target-port=80
# 查看服务 可以获取暴露服务的ip
kubectl get service
此时可以使用服务ip:服务port访问该Service
同时在集群容器内部,可以使用 域名:port 访问
域名规则: Service名.NameSpace名.svc
这种集群内部可以访问的方式 就是ClusterIP方式 即集群ip方式
这个时候返回来看之前的 k8s-dashboard的搭建,当时修改了type为 NodePort,即节点端口方式
5.1 服务发现
Service会对pod进行动态的发现,当某个pod离线后,就不会将流量继续负载给该pod,当扩容一个pod后,Service也会自动将该pod纳入负载的范围内,根据一定的策略将流量分发至该pod
5.2 端口暴露方式
Service主要就是端口的暴露,分为两种方式
5.2.1 ClusterIP
# ClusterIP模式也是默认的 即不加--type就是ClusterIP
kubectl expose deploy [deployment名] --port=[暴露端口] --target_port=[pod服务端口] --type=ClusterIP
以集群IP的方式暴露端口,此时在集群内部可以通过
- 集群内部ip:port的方式访问
- 也可以通过域名:端口的方式访问
5.2.2 NodePort
以当前机器节点的方式暴露端口
kubectl expose deploy [deployment名] --port=[暴露端口] --target_port=[pod服务端口] --type=NodePort
# 此时查询service
kubectl get svc
可以看到除了集群ip port之外,又多一个端口 这就是当前服务器的端口
此时可以通过服务器ip+此端口即可访问(注意放行安全组)
k8s生成的NodePort node范围为 30000- 32767之前,可以在安全组内直接对此范围内的端口放行
6. Ingress
Ingress是k8s-Service的入口,相当于统一网关,底层是Nginx
通过Ingress将不同的请求分发至不同的Service
官网: https://kubernetes.github.io/ingress-nginx/deploy/
6.1 安装
k8s默认没有安装Ingress,需要手动安装
# 下载ingress-nginx
# 修改ingress-nginx的镜像源 改为阿里云
vi deploy.yaml
registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0
# 安装
kubectl apply -f deploy.yaml
# 查看安装服务
kubectl get pod,svc -n ingress-nginx
# 放行svc端口
6.2 使用
安装后,查看服务就可以看到ingress-nginx 命名空间下的服务,可以通过此服务暴露的端口来访问
Ingress也是一个Service
注意: Ingress会暴露两个端口
80的为http请求端口
443的为https请求端口
6.2.1 测试
测试的目的是通过Ingress来访问不同的Service
架构如下:
6.2.1.1 准备工作
6.2.1.1.1 创建deploy/Service
建立两个deploy,并将其暴露成Service
建立如下yaml, test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-server
spec:
replicas: 2
selector:
matchLabels:
app: hello-server
template:
metadata:
labels:
app: hello-server
spec:
containers:
- name: hello-server
image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server
ports:
- containerPort: 9000
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-demo
name: nginx-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- image: nginx
name: nginx
---
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx-demo
name: nginx-demo
spec:
selector:
app: nginx-demo
ports:
- port: 8000
protocol: TCP
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
labels:
app: hello-server
name: hello-server
spec:
selector:
app: hello-server
ports:
- port: 8000
protocol: TCP
targetPort: 9000
里面分别创建了两个 Deployment 并暴露成Service,启动两个Deployment的镜像就是简单的hello-server和nginx
访问hello-server返回一个hello字符串
访问nginx返回nginx的首页
# 创建如上资源
kubectl apply -f test.yaml
6.2.1.1.2 创建Ingress
建立如下yaml ingress.yaml,
rules配置的很类似gateway的配置
host就是请求的域名,根据域名来转发到不同的Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-host-bar
spec:
ingressClassName: nginx
rules:
- host: "hello.zy.com"
http:
paths:
- pathType: Prefix
path: "/" # 前缀是/后的所有请求会被转发至service
backend:
service:
name: hello-server # 转发至的service
port:
number: 8000 # service端口
- host: "demo.zy.com" # 多个规则
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: nginx-demo
port:
number: 8000
# 创建ing
kubectl apply -f ingress.yaml
6.2.1.2 使用域名测试
便于测试,我们就直接修改windows hosts文件,不使用公网ip解析
将任意k8s节点ip配置至hosts文件
[ip] hello.zy.com
[ip] demo.zy.com
浏览器访问 hello.zy.com 转发到 hello-server的service处理
浏览器访问 nginx.zy.com 转发到 nginx-demo的service处理
6.2.2 重写路径
上述Ingress的配置文件中配置的是/后的所有请求会被转发到对应的Service,我们也可以对转发的路径进行重写,类似于GateWay/Nginx重写路径的方式
官网文档: https://kubernetes.github.io/ingress-nginx/examples/rewrite/
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2 # 第二个参数
name: rewrite
namespace: default
spec:
ingressClassName: nginx
rules:
- host: rewrite.bar.com
http:
paths:
- path: /something(/|$)(.*) # 通配符
pathType: Prefix
backend:
service:
name: http-svc
port:
number: 80
6.2.3 流量控制
https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#rate-limiting
参考文档增加如下annotations参数即可
# rps不能大于1 否则返回503 默认503 可以修改
nginx.ingress.kubernetes.io/limit-rps: "1"
6.3 总结
通过ingress来回顾一下k8s安装初始化主节点时的命令
kubeadm init \
--apiserver-advertise-address=172.16.0.208 \
--control-plane-endpoint=cluster-endpoint \
--image-repository registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images \
--kubernetes-version v1.20.9 \
--service-cidr=10.96.0.0/16 \
--pod-network-cidr=192.168.0.0/16
其中最后两个参数
service-cidr
pod-network-cidr
此时就可以明白啥意思
- service-cidr 为service层的网络范围
- pod-network-cidr 为pod的网络范围
而此时也可以建立起k8s的网络模型
7. 存储
docker有将容器内的目录挂载到宿主机的能力,同样的k8s也可以挂载,但是不能像docker一样直接挂载到物理机的某个目录
原因是 我们的pod/deploy存在故障转移的能力,如果将某个pod的文件挂载到固定的机器下,当我们的pod发生了故障转移之后,再另外一个节点上重新启动了一个pod时,就会发生找不到之前的数据的问题,如果我们的pod是一个存储类的中间件 如mysql时,找不到之前的数据将是灾难的
针对如上的情况,k8s将存储抽象出来,作为一个单独的存储层,存储层使用分布式的文件存储系统来实现,如NFS
NFS会把各个节点的文件进行同步
通过分布式文件存储系统的特性,使我们的pod即时在别的节点重启也可以获取到之前挂载在分布式文件存储系统的数据.
k8s对存储是开放的,可以使用多种分布式文件存储系统.
7.1 NFS搭建
我们对k8s的三台服务器搭建nfs
# 所有节点安装nfs工具包
yum install -y nfs-utils
# 主节点初始化目录
# 暴露nfs/data目录,此目录可以自定义
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
# 创建上述暴露目录
mkdir -p /nfs/data
# 启动
systemctl enable rpcbind --now
systemctl enable nfs-server --now
# 使nfs配置生效
exportfs -r
此时主节点已经初始化完毕,有一个/nfs/data的目录暴露出来
# 从节点查看主目录 ip为主节点IP 此处可以使用局域网ip 前提处于一个vpc下
showmount -e 172.31.0.4
# 从节点创建目录
mkdir -p /nfs/data
# 从节点目录挂载 ip为主节点ip 目录为上述新建目录
mount -t nfs 172.31.0.4:/nfs/data /nfs/data
# 测试 在任意一节点新增/修改文件,查看另外两个节点是否同步
echo "hello nfs server" > /nfs/data/test.txt
7.2 deployment挂载目录
yaml配置如下
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-pv-demo
name: nginx-pv-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pv-demo
template:
metadata:
labels:
app: nginx-pv-demo
spec:
containers:
- image: nginx
name: nginx
volumeMounts: # 挂载
- name: html # 挂载名称
mountPath: /usr/share/nginx/html # 容器内要挂载的目录
volumes: # 挂载配置
- name: html # 对应上面配置的name
nfs: # 此处可以改为其他fs
server: 172.31.0.4 # nfs ip
path: /nfs/data/nginx-pv # nfs路径,需要先在nfs上创建出对应的路径,否则会挂载失败
kubectl apply -f [xx.yml]
这样就将多个pod中的文件挂载到nfs上,只要nfs上的文件修改,每个节点的文件都会随之修改,并且容器内部的文件也会随之修改(类似Docker).
7.3 挂载问题
上面的挂载存在两个问题:
- pod删除,nfs里的文件不会随之删除
- 挂载时,无法指定某个pod挂载目录的可使用空间
为了解决上面的问题,有两种方案:
- PV&PVC
7.3.1 PV&PVC
PV(Persistent Volume): 持久卷 存储数据
PVC(Persistent Volume Claim): 持久卷申明 说明持久卷的规格(大小)
PV和PVC都是k8s的资源类型
概述: PV就是数据存储的地方,PVC就是描述PV大小的说明,一个pod创建的时候可以指定自己的PVC,通过PVC再绑定一个PV,这样就达到上面pod的挂载问题
7.3.1.1 PV的使用
PV分为静态分配和动态分配两种
7.3.2.1 静态分配
7.3.2.1.1 PV池
提前建立一个PV池,分配好各种规格的PV,当使用PVC绑定PV的时候会根据PVC中指定的大小来自适应(向上取大)获取合适大小的PV.
如下yaml,创建pv池 pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-10m #pv名字
spec:
capacity:
storage: 10M # pv的大小
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/01 #nfs的文件路径
server: 172.31.0.4 #nfs的ip
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02-1gi
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/02
server: 172.31.0.4
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv03-3gi
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/03
server: 172.31.0.4
# 创建如上三个pv
kubectl apply -f pv.yml
# 查看pv池
kubectl get pv
7.3.2.1.2 PVC创建
如下yaml,创建PVC pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-pvc # 名字
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi # 需要的大小
storageClassName: nfs # 与pv中的保持一致
# 创建PVC
kubectl apply -f pvc.yml
# 此时k8s就会选择最合适大小的pv来与PVC绑定,如上文中PVC的大小 200M 那么就会选择 pv02-1gi 这个PV来绑定
kubectl get pvc
7.3.2.1.3 创建pod绑定PVC
如下yaml, pod-pvc.yml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy-pvc
name: nginx-deploy-pvc
spec:
replicas: 2
selector:
matchLabels:
app: nginx-deploy-pvc
template:
metadata:
labels:
app: nginx-deploy-pvc
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: nginx-pvc # 挂载PVC
# 创建使用PVC挂载的pod
kubectl apply -f pod-pvc.yml
7.3.2.2 动态分配
动态分配就是动态的增加PV并将其分配给pod使用
7.4 ConfigMap
配置集: 专为配置文件挂载使用的资源类型
类似Docker中的-v 参数,但是将挂载的配置文件抽象成了一种资源类型
底层保存数据使用的是k8s的键值数据库: ETCD
# 创建redis的配置文件
echo appendonyl yes > redis.conf
# 创建配置集
kubectl create cm redis-conf --from-file=redis.conf
创建pod,cm.yml
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
command:
- redis-server
- "/redis-master/redis.conf" #指的是redis容器内部的位置
ports:
- containerPort: 6379
volumeMounts:
- mountPath: /data
name: data
- mountPath: /redis-master
name: config # 对应下面volumes中的cm配置
volumes:
- name: data
emptyDir: {}
- name: config # 配置项
configMap:
name: redis-conf # cm的名称
items:
- key: redis.conf # 文件名
path: redis.conf # 挂载到redis中的文件
7.5 Secret
类似于ConfigMap,但是是针对密钥,令牌等东西存储的
举例: 创建pod拉取镜像时可能需要私有镜像仓库的密钥,此时通过Secret就可以做到一次配置,多次使用的效果
##命令格式
kubectl create secret docker-registry [secret名] \
--docker-server=<你的镜像仓库服务器> \
--docker-username=<你的用户名> \
--docker-password=<你的密码> \
--docker-email=<你的邮箱地址>
apiVersion: v1
kind: Pod
metadata:
name: private-nginx
spec:
containers:
- name: private-nginx
image: zy/mynginx:v1.0
imagePullSecrets:
- name: zy-docker # 创建的secret名称
8 总结
通过上面的学习,简单的了解了k8s中的大部分资源类型及使用方式,可以通过命令行的方式来操作k8s底层的各种东西
熟悉了k8s的网络模型,知道k8s集群中服务流量的分发规则.
学无止境!