由于没有资源,也不想开虚拟机,所以只停留在理论学习。
K8s 学习
对我的理解,Docker 就是箱子,服务器里面的 Docker 就是将他们做成集装箱,K8s 可以将这些集装箱集中在自己的船上,一条船可以搭上数百个集装箱。是一个分布式的系统框架。
注意下面的 YAML 语法,不能用 Tab!
理解特性
K8s 可以实现负载均衡与服务发现,会通过轮询分担网络流量。可以互相测每个集群的健康度,有问题会自动切换。
K8s 可以管理每个集群分配存储空间;
K8s 可以实现自动部署或回滚,可以回滚容器到指定的版本;
K8s 的 Pod 里面的容器,他可以自动分配或自己指定每个容器的 CPU 和内存;
K8s 可以实现自我修复,如果某个应用部署到其中一个集群中,但这个集群突然故障了,K8s可以在另外的集群重新拉起这个应用。
架构
一般是集群模式。Master + Worker 模式,Master 是领导,Worker 是员工。n Master + n Worker Node,n 要大于或等于1
如果只有一个领导的话,领导寄了公司就会乱套;如果有多个领导,领导会成立一个董事会,如果A领导挂掉了,董事会的各位领导会选举一名新的领导,少数服从多数,大家都同意 B 领导可以当主领导才能当主领导。
Master 可以当 Worker 部署应用,但一般不用于跑业务。
组件架构
架构图,Cluster 是集群,我们当成集团吧。整个模式可以理解为中介模式,类似 Zabbix Proxy。
Node :代表服务器,工厂。
- kubelet:服务器管理 Agent。监控当前节点的所有情况,控制器。
- kube-proxy:控制网络访问的出入,相当于路由器。
Control Plane :控制中心,就是上面的 Master Node,集团的总部,负责安排工作给 Node 干!
- c-m:Control manager,决策者,决定干什么。
- etcd:数据库或资料库,用来存储核心。
- api:接口,秘书,工人想联系 c-m 得经过我的 API。
- sched(scheduler):调度者,负责决定这个项目由哪个工人去干,看这些应用在哪个服务器运行比较优越。
- cloud provider manger:云决策者
sched 决定哪个节点跑,这些基础数据都会存到 etcd;Node 无法直接联系到 C-M 和 sched,数据交互都得经过 api。所有部件都得经过 api,不可以单独找 c-m。
运作架构
每台服务器都要安装 Docker 或容器运行环境。kubelet 负责每个应用的启停销毁,探测每个应用的状态。
k-proxy 控制每个应用容器之间的访问,发现其他节点的应用。
kubectl 是命令行模式,我们都是用这个跟 k8s 集群进行交互,用户可以让 K8s 部署容器应用,也是会发到 api。
Docker 的 容器 == K8s 的 Pod,但Pod可以包括多个容器
部署
Worker
- 部署kubelet、kubectl(但一般不用装在worker,用户用来交互的)、kubeadm(用来快速搭建集群的工具),Docker
- kubeadm join:加入集群,会部署 kube-proxy。
Master
- 部署kubelet、kubectl、kubeadm,Docker
- kubeadm init:初始化主节点,将该节点变成主节点,会自动部署 scheduler、kube-proxy、api、etcd、c-m;
步骤
所有步骤都要装 docker 本体,都略过,记得配置加速。
安装 kubeadm
- 要 RHEL 或 Debian 的操作系统,服务器配置至少要 2H2G 以上。
设置防火墙放行规则,主机名和MAC地址不可重复,允许 iptables 检查桥接流量。
cat <<EOF | tee /etc/modules-load.d/k8s.conf br_netfilter EOF cat <<EOF | tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system
- 设置主机名(hostnamectl set-hostname xxxx)
- 永久关闭 SELlinux,关闭 Swap。
安装 kubelet、kubeadm、kubectl
# 此操作会覆盖 /etc/yum.repos.d/kubernetes.repo 中现存的所有配置
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet
初始化主节点
可以提前拉取 master 节点必备的docker 镜像,kube-apiserver/kube-proxy/kube-controller-manager/coredns/etcd/pause 这些镜像。
主节点的 /etc/hosts 要提前编写好次节点的 IP 和 域名映射,次节点也要
127.0.0.2 cluster
127.0.0.3 node1
127.0.0.4 node2
然后运行主节点初始
kubeadm init \
--apiserver-advertise-address=主节点IP \
--control-plane-endpoint=主节点的主机名 \
--image-respsitory k8s的镜像远程地址 \
--kubernetes-version v1.20.9 \
--service-cidr= 10.96.0.0/16 \
--pod-network-cidr=192.168.0.0/16
所有网络范围不重叠,后面四个参数可以不用写
初始化完毕后,下面有 kubeadm join
的命令,可以让其他从从节点加入到主节点中,从而建立成董事会(Master 集群);再下面是加入工作节点。
kubectl get nodes
可以查看主节点的状态。
部署 calico 插件
部署网络组件 calico
curl https://docs.projectcalico.org/manifests/calico.yaml -O
kubectl apply -f calico.yaml
相关命令
#查看集群所有节点,相当于查看ansible有哪些在线资产,只有 master 才能执行
kubectl get nodes
#根据配置文件,给集群创造资源
kubectl apply -f xxx.yaml
#查看集群部署了哪些应用?相当于 docker ps -a
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
- RESTARTS 是自我修复,自动重启次数
- AGE 最后启动时间
- NAMESPACE 命名空间
#如果worker节点不幸重启了,可能要手动开启 systemctl start kubelet 和 docker
加入 Worker 节点
记录主节点初始化完毕的命令,给需要当 Worker 的主机执行,加入过程也要初始化组件,所以 kubectl get pod -A
可以看初始化进展。完毕后,kubectl get nodes
能看到子节点已经 Ready 了!记得关掉主节点的防火墙哦
令牌机制
上面说的初始化命令,其实是只有24小时时效的,如果后面要加的话新节点,执行这条命令
kubeadm token create --print-join-command
部署 Dashboard
部署可视化界面,方便管理
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml
kubectl get all -n kubernetes-dashboard
# 放开访问端口
kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
把 type:ClusterIP 改成 NodePort
# 找到仪表盘的外网端口,会看到 443:XXXXX/TCP,XXXXX就是外网端口
kubectl get svc -A | grep kubernetes-dashboard
访问了,要获取令牌,具体步骤看
【云原生Java架构师的第一课K8s+Docker+KubeSphere+DevOps】 【精准空降到 11:20】 https://www.bilibili.com/video/BV13Q4y1C7hS/?p=40&share_source=copy_web&vd_source=e61c16155fc4a4d0e1d899421ac8157f&t=680
实战
资源创建方式
我们可以用命令行或 YAML 的方式建立资源,YAML 可以理解成 ansible 的playbook 或 docker 的 dockerfile
Namespace
命名空间,用来隔离资源,不隔离网络。如何理解呢,就一个生产环境,上线实际业务的应用我们就划分成生产区;在测试环境,就划分成 测试区。我们就当应用分类就行了。
Namespace | node1 | node2 | node3 |
---|---|---|---|
prod | 应用A | 应用A-2 | 应用a-3 |
dev | APP-DEV-1 | APP-DEV-2 | APP-DEV-3 |
命令行模式
kubectl get ns
: 获取集群的命名空间;kubectl delete ns xxx
:删除 xxx 的命名空间,但是要谨慎删除,会把 xxx 名下的资源都会被删除掉;kubectl create ns xxxx
:创建 xxxx 的命名空间;kubectl get pod -n xxx
:查看 xxx 命名空间中的 Pod
YAML 模式,Kind 代表模式
apiVersion: v1
kind: Namespace
metadata:
name: xxxx
创建完毕后,也要 kubectl apply -f xxx.yaml
才能生效;kubectl delete -f xxxx.yaml
会删除掉 xxxx 的命名空间
Pod
Pod 是一组容器, Pod 是K8S的最小单位。Pod 封装了 Docker 容器,允许多个容器一起运行在 Pod;
在 kubectl get pod -A
的 READY
, 1/2
中的 1
代表 Pod 中有多少个应用在运行中,2
代表这 Pod 总共有多少个应用。
注意,我们只能在集群内访问 Pod 的 IP,如果要暴露公网,就需要用 Service 组件来暴露公网。我们 Pod 能实现互相访问是靠 calico
单容器
命令行模式,创建一个 nginx 容器,K8S会给容器分配一个新IP。
kubectl run mynginx --image=nginx
# 查看default名称空间的 Pod,owide参数可以显示完整一点的信息
kubectl get pod (-owide)
# 查看Pod nginx 的创建进度或问题
kubectl describe pod mynginx
## 我们主要看 Events,可以查看容器创造过程,定位问题。
# 删除 Pod
kubectl delete pod mynginx
#可以查看 Pod 的运行日志,跟 docker 的logs 差不多
kubectl logs mynginx
# 进入 mynginx 的控制台
kubectl exec -it mynginx -- /bin/bash
> root@mynginx:/#
YAML 模式,这是单 Pod 的创建方式
apiVersion: v1
kind: Pod
metadta:
label:
run: mynginx
name: mynginx
# namespace: xxx 在XXX区创建
spec:
containers:
- image: nginx
name: myningx
多容器
如果我要在 YAML 模式,建立一个多容器的 Pod 的话。他们共享网络空间与存储。
apiVersion: v1
kind: Pod
metadta:
label:
run: myapp
name: myapp
# namespace: xxx 在XXX区创建
spec:
containers:
- image: nginx
name: myningx
- image: tomcat:8.5.68
name: tomcat
如果我们要访问不同容器,我们访问 80 端口就是访问 nginx;我们访问 8080 就是 tomcat 的容器;只不过我们访问的 IP 是 K8s 分配的 IP 。
在这个 YAML 文件中,如果我们创建两个 nginx 容器,会出现 80 端口冲突。
Deployment
控制 Pod,让 Pod 拥有多副本,自愈、故障转移与扩缩容等能力。
跟传统部署 Pod 相比,我们用 deployment 部署 pod,不怕服务器或容器宕机,自动拉起新的 pod。死了会自动在其他机器中拉起。
创建
# 创建 mytomcat 的部署
kubectl create deployment my-dep --image=tomcat:x.x.x
# 删除上面的部署
kubectl delete deploy my-dep
# 多副本部署
# 创建 mytomcat 的部署
kubectl create deployment my-dep --image=tomcat:x.x.x --replicas=3
# 查看 deploy 状态
kubectl get deploy
扩缩容
kubectl scale deploy/my-dep --replicas=2
,这个 2
是缩容和扩容的数值
自愈
通过 Hello 心跳包观察每个 Pod 的情况。
如果应用因为内存泄露或其他宕机,会自动重启;但如果本身应用有问题,自愈能力会不断的重启,需要检查容器本身时候有问题。
其中给一个 Worker 挂掉了,5分钟(这是阈值)后自动在其他 Worker 重新拉起 Worker 中已经 Deploy 的 Pod,可以理解成故障转移。
滚动更新/回退
滚动更新是先启动 V2 版本的 Pod,如果启动成功了,会关掉 V1 的 Pod,可以保证流量不中断。如果 V2 刚开始启动失败的话,不会影响原本 V1 的 Pod。在这个过程是起一个杀一个。
kubectl set image deployment/my-dep nginx=nginx:1.16.1 --record
# kubectl set image <资源类型> <资源名称> <容器名称>=<新镜像名称>
kubectl rollout status deployment/my-dep
如果我们要回退之前的版本的话
# 查看 Pod 的历史记录
kubectl rollout history deployment/my-dep
# 回滚,回退到第一次
kubectl rollout undo deploy/my-dep --to-revision=1
其他工作负载模式
deployment 也是有局限性的,适用于无状态的应用;其他工作模式还有 Statefulset、Daemonset、job/cronjob 这些内容。
- Deployment: 无状态应用部署比如微服务,提供多副本等功能。
- StatefulSet: 有状态应用部署,比如redis,提供稳定的存储、网络等功能。死了会挂载到专属的存储中。
- DaemonSet:守护型应用部署,比如日志收集组件,在每个机器都运行一份
- Job/CronJob:定时任务部署,比如垃圾清理组件,可以在指定时间运行
Service
服务,用来做服务发现功能的,与负载均衡。可以理解为 Service 是一个网站服务的入口,这个入口会自动分发到 Pod 里的内部端口。我们访问的网站的话,流量会负载到每个 Pod 的服务中。
在这个 Service 内,是以 Label 标签进行分组,Service 会根据这个 deploy 的 label 形成一组映射组。
可以用 IP 与 域名访问,建立好的 Service 的内部域名形式是 label名.命名空间.svc
# 暴露映射端口,外网访问的是 8000 端口,入口则映射到 80 端口中
kubectl expose deploy my-dep --port:8000 --target-port=80
# 我暴露的这 IP,只能在集群内访问
kubectl expose deploy my-dep --port:8000 --target-port=80 --type=ClusterIP
# 我暴露的这 IP,可以在集群外访问(公网),但是 NodePort 的范围在 30000-32767 之间
kubectl expose deploy my-dep --port:8000 --target-port=80 --type=NodePort
# 查看 Pod 标签
kubectl get pod --show-labels
# 访问 service
curl my-dep.default.svc
# 查看 service 状态
kubectl get svc
# 删除 service
kubectl delete svc my-dep
YAML 的话是这样
apiVersion: v1
kind: Service
metadta:
label:
app: my-dep
name: my-dep
spec:
selector:
app: my-dep
ports:
- port:8000
protocol: TCP
targetPort: 80
在负载均衡中,一台 Pod 中断后,流量不会经过异常 Pod 的 IP,会负载到其他 Pod 上。至于怎么发现中断的,源自于服务发现。
Ingress
入口,作为集群对外服务的唯一入口,是总网关,访问对应的 service 的服务端口,相当于 Ingress → Service → Pod。就是叠一层网络入口。
Ingress 基于 Nginx,用 Nginx 控制流量出入。所有流量先访问 ingress 层,找到映射关系后会转发到对应 service,如果没找到,会返回 404.
注意的是,Ingress 不是全局负载均衡, Service 是对 Pod 负载均衡,Ingress 只是把 Service 映射对应的服务出去而已。在外网也需要对 Ingress 另外配置负载均衡才行。
# 查看 ingress 资源
kubectl get ing
# 编辑 ingress 的某个资源配置文件
kubectl edit ing <资源名> -n
域名映射
在外部访问某个域名,域名的请求会交给对应的 service 进行处理。
apiVersion: v1
kind: Ingress
metadata:
name: ingress-host-bar
spec:
ingressClassName: nginx
rules:
- host: "hello.my.com"
http:
paths:
- pathType: Prefix #前缀匹配模式
path: "/"
backend:
service:
name: hello-server #这里是名字
port:
number: 8000 # 这里的意义是,访问 hello.my.com 的时候,流量会转到 hello-server 这个 service 处理
- host: "demo.my.com"
http:
paths:
- pathType: Prefix #前缀匹配模式
path: "/nginx" # 把请求转给下面的服务,如果下面的服务一定要能处理这个路径
backend:
service:
name: nginx-demo #这里是名字
port:
number: 8000 # 这里的意义是,访问 demo.my.com/nginx 的时候,流量会转到 nginx-demo 这个 service 处理
路径重写
这段暂时不重要,有需要我将会查 Rewrite - Ingress-Nginx Controller (kubernetes.github.io)
流量限制
使用 nginx.ingress.kubernetes.io/limit-rps
进行限流
apiVersion: v1
kind: Ingress
metadata:
name: ingress-limit-rate
annotations:
nginx.ingress.kubernetes.io/limit-rps: "1" #1秒放一个
spec:
ingressClassName: nginx
rules:
- host: "ha.my.com"
http:
paths:
- pathType: Exact #精确匹配,访问 ha.my.com 生效
path: "/"
backend:
service:
name: hello-server #这里是名字
port:
number: 8000
存储
在上面的搭建中,如果发生了节点故障,在其他 Worker 的主机就算重新拉起新的容器,也会因为另外的 Worker 没有原 Worker 的数据,导致数据缺失或不对等。
使用 NFS 作为存储
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
# 配置生效
exportfs -r
# 查看nfs情况
exportfs
从节点
showmount -e 主节点 IP
# 建立目录准备挂载
mkdir -p /nfs/data
mount -t nfs 主节点:/nfs/data /nfs/data
# 写入测试
echo "hello world" > /nfs/data/text.txt
用原生方式进行挂载
apiVersion: v1
kind: Deployment
metadata:
labels:
app: nginx-pv
name: nginx-pv
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pv
spec:
containers:
- image: nginx
name: nginx
volomeMounts:
- name: html # 这个是挂载名,要对应下面的实际挂载的名
mountPath: /usr/share/nginx/html
volumes:
- name: html
nfs:
server: 主节点或NFS服务器的 IP
path: /nfs/data/nginx-pv # 最终nginx容器的 mountPath 会映射到这里的 path 目录中
PV 与 PVC
PV: 持久卷(Persistent Volume),将应用需要持久化的数据保存到指定位置
PVC:持久卷申明(Persistent Volume Claim),申明需要使用的持久卷规格
PV 就是我们提前规划好,哪些卷定了多少的空间,每个节点都部署好 PV 卷后,将会形成静态供应的 PV 池;而 PVC 相当于申请书,我想要 1G 的空间。假设现在的 PV 池有 1G
、20G
和1M
的 PV ,系统将会找匹配相近条件的 PV 池,对 1G 的 PV 卷进行绑定,饭量多少选哪个盒饭。
如果 Pod 删掉,PVC 也会跟着删,最后那 1G
的 PV 卷的数据也会回收掉。
静态供应是需要提前划分,动态供应可以按需自动创建一个 PVC 要求的空间卷。
创建 PV 池
mkdir -p /nfs/data/01
mkdir -p /nfs/data/02
mkdir -p /nfs/data/03
创建 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-10m
spec:
capacity:
storage: 10M
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/01
server: nfs服务器IP
--- # 这个是分隔符,也可以理解为把多个 yaml 串在一起
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02-1G
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/02
server: nfs服务器IP
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv03-3Gi
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
storageClassName: nfs
nfs:
path: /nfs/data/01
server: nfs服务器IP
我们 kubectl get persistentvolome
可以查看持久存储;
kubectk get pv
可以查看持久卷状态;
如果看到 STATUS 是 Available
的话,是表示这些卷是可用状态;Bound
表示绑定状态,可以看右侧的 Claim;Released
表示被释放了
创建 PVC 与绑定
意思是写一份申请书
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
storageClassName: nfs
kubectl get pvc
查看 PVC 的状态
创建 Pod 的时候,绑定我们刚创建的 PVC。
apiVersion: v1
kind: Deployment
metadata:
labels:
app: nginx-pv
name: nginx-pv
spec:
replicas: 2
selector:
matchLabels:
app: nginx-pv
spec:
containers:
- image: nginx
name: nginx
volomeMounts:
- name: html # 这个是挂载名,要对应下面的实际挂载的名
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolomeClaim:
claimName: nginx-pvc
ConfigMap
可以抽取应用的配置文件,实现自动更新。也就是说不用传统
发表评论