k8s 使用手册
简介
docker compose 虽然能通过 yml 运行多个容器并实现容器间的通讯问题,但它处理多主机部署容器、跨主机网络通信等问题就比较吃力了,这点恰巧是 Kubernetes(简称 k8s) 所能解决的。
如果项目需要多机器节点的微服务架构,并且采用 Docker image(镜像)进行容器化部署,那么 Kubernetes 可以帮助我们屏蔽掉集群的复杂性。k8s 具有以下特性:服务发现和负载均衡、存储编排、自动部署和回滚、资源分配、自我修复等。
以下是 k8s 中的一些基本概念:
Master
Kubernetes 将集群中的机器划分为一个 Master(集群控制节点,也可以配置多个 Master) 和一些 工作节点 Worker。在 Master 上运行着集群管理相关的一组进程 kube-apiserver 、 kube-controller-manager 和 kube-scheduler,这些进程实现了整个集群的资源管理、Pod (一组业务相关的容器)调度、弹性伸缩、安全控制、系统监控和纠错等管理功能,并且都是自动完成的。
Worker
Worker(有时也叫 Node) 作为集群中的工作节点,运行真正的应用程序,一台计算机(物理机、私有云、公有云等)就相当于一个 Worker; Worker 中运行的 Pod(一组业务相关的容器) 是 k8s 的最小管理单元。在 Worker 上运行着 Kubernetes 的 kubelet、kube-proxy 服务进程,这些服务进程负责 Pod 的创建、启动、监控、重启、销毁,以及实现软件模式的负载均衡器。
Service
在 Kubernetes 中,Service 是分布式集群架构的核心,一个 Service 对象拥有如下关键特征:
- 唯一服务名 ( Namespace 命名空间)
- 一个虚拟 IP 和端口号 (虚拟 ip 由 k8s 自动生成)
- 提供某种远程服务能力
- 被映射到一组容器应用上
Pod
Pod 可理解为一组与业务相关的容器;每个 Pod 的标签(Label)和 Service 的标签选择器对应,Service 与 Pod 就关联上了。
Pod 是 k8s 所管理的最小单位。
Pod 运行在 Worker 节点中,每个 Pod 中都运行着一个特殊的 Pause 容器和多个业务容器,业务容器共享 Pause 容器的网络栈和 Volume 挂载卷;并不是每个 Pod 和它里面运行的容器都能被映射到一个 Service 上,只有提供服务(无论是对内还是对外)的那组 Pod 才会被映射为一个服务。
服务扩容和服务升级
关于服务扩容和服务升级,只需为需要扩容的 Service 关联的 Pod 创建一个 RC(Replication Controller,适用于旧版本 k8s)或 Deployment ,服务扩容以至服务升级等令人头疼的问题都迎刃而解。
RC 定义文件中包括以下 3 个关键信息:
- 目标 Pod 的定义
- 目标 Pod 需要运行的副本数量(Replicas)
- 要监控的目标 Pod 的标签
安装
提示
注意文档演示安装 k8s 1.28 版本,亲测可用,如需安装其他版本,自行在对应位置替换
前置条件
每个节点需具备容器运行时(Container Runtime),手动给每台机器安装上 Docker 即可。
每个节点不可以有重复的主机名、MAC 地址或 product_uuid(查看指令:sudo cat /sys/class/dmi/id/product_uuid)。参考地址
# 1.设置主机名。修改如下文件。
sudo vi /etc/hosts
sudo vi /etc/hostname
# 2.禁用 SELinux(一个安全系统)
# 查看 /etc/selinux/config ,SELINUX=disabled 为禁用;
# Ubuntu 默认未启用SELinux,因此不用管他。如果不确定是否启用,可用如下指令测试
# 修改 SELinux 配置后,只能重启系统才会永久生效;sudo setenforce 0 可临时放开权限
sudo sestatus
# 3.关闭交换分区( kubelet 检测到交换内存时无法启动;可使用 free -m 查看,Swap 部分必须为 0)
swapoff -a &&
sed -ri 's/.*swap.*/#&/' /etc/fstab
# 4.允许 iptables 检查桥接流量
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
# 5.修改 MAC 地址,编辑如下.yml配置文件中的macaddress属性,可能不为00-installer-config.yaml,自行查看
vim /etc/netplan/00-installer-config.yaml
# network:
# ethernets:
# ens32:
# macaddress: 00:11:22:33:44:55
netplan apply
systemctl restart systemd-networkd
# 准备好上面的前置条件后,有些配置重启才会生效,需重启服务器。
reboot now# 1.设置主机名
hostnamectl set-hostname new_name
# 2.禁用 SELinux(一个安全系统),记得配置后查看一下 /etc/selinux/config 内容
sudo setenforce 0 &&
sudo sed -i 's/^SELINUX=disabled$/SELINUX=permissive/' /etc/selinux/config
# 3.关闭交换分区( kubelet 检测到交换内存时无法启动;可使用 free -m 查看,Swap 部分必须为 0)
swapoff -a &&
sed -ri 's/.*swap.*/#&/' /etc/fstab
# 4.允许 iptables 检查桥接流量
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
# 5.修改 MAC 地址,配置文件通常位于/etc/sysconfig/network-scripts/目录下,文件名格式为ifcfg-<接口名>,添加 MACADDR 字段。
sudo vim /etc/sysconfig/network-scripts/ifcfg-eth0
# DEVICE=eth0
# BOOTPROTO=dhcp
# ONBOOT=yes
# MACADDR=00:11:22:33:44:55
sudo systemctl restart network
# 准备好上面的前置条件后,有些配置重启才会生效,需重启服务器。
reboot now- 保证每个节点能相互通信。
- 每台机器至少 2 CPU 核 2GB 内存。
准备
第一步:需要给每台机器安装上 kubeadm、kubectl、kubelet;这三个组件说明如下:
组件 说明 kubeadm 使用 kubeadm 简化了集群的部署过程,用于初始化和管理 Kubernetes 集群 kubectl kubectl 用于与 Kubernetes 集群进行交互 kubelet 负责管理节点上的容器和 Pod Ubuntu# 设置 k8s 镜像源(该源支持 v1.24 - v1.29 版本,示例为 1.28 版本,其他版本在对应位置替换即可) apt-get update && apt-get install -y apt-transport-https curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/deb/ /" | tee /etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install -y kubelet kubeadm kubectlCentOS# 设置 k8s 镜像源(该源支持 v1.24 - v1.29 版本,示例为 1.28 版本,其他版本在对应位置替换即可) cat <<EOF | tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/rpm/ enabled=1 gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.28/rpm/repodata/repomd.xml.key EOF setenforce 0 # 安装kubelet、kubeadm、kubectl yum install -y kubelet kubeadm kubectl systemctl enable kubelet && systemctl start kubelet其他需要核对的相关配置
# 1. Docker 可能默认关闭了 CRI(容器运行时接口),需要手动打开。 vim /etc/containerd/config.toml # 找到 disabled_plugins = ["cri"] 这一行并注释它,再重启容器服务 systemctl restart containerd systemctl enable containerd # 2. k8s默认配置项中会指定镜像地址,导致下载失败,需手动修改 containerd config default > /etc/containerd/config.toml # 生成 containerd 默认配置文件 vim /etc/containerd/config.toml # 修改仓库地址 # 在 sandbox_image = "registry.k8s.io/pause:xxx" 配置项中 # 将 registry.k8s.io 修改为 registry.aliyuncs.com/google_containers systemctl daemon-reload systemctl restart containerd.service第二步:按需使用 Docker 下载组件工具镜像,组件工具说明如下:
组件 安装位置 说明 kube-proxy 安装在 Worker 节点上,若 Master 需要提供服务也可以安装 维护工作节点之间的通信,支持服务发现和负载均衡 kube-controller-manager 仅安装在 Master 一个守护进程,资源对象的自动化控制中心,确保集群的期望状态和实际状态一致 kube-scheduler 仅安装在 Master 负责将新创建的 Pod 分配到合适的节点上 kube-apiserver 仅安装在 Master Kubernetes 的前端接口,处理所有的 REST API 请求,与 etcd 交互存储集群状态信息 coredns 通常安装在以 Pod 形式运行的某些节点上,可以是 Master 或 Worker 集群内的 DNS 服务器,将服务名称解析为集群内部的 ip 地址 etcd 通常安装在 Master,也可以同时安装在多个节点上(通常是独立的 etcd 集群) 一个数据库,存储和复制 Kubernetes 集群的所有状态数据 pause Worker 节点,若 Master 需要提供服务也可以安装 是 Worker 中其他容器的“父”容器,确保 Pod 中的其他容器共享同一网络命名空间 # 查看当前版本所需工具镜像列表 kubeadm config images list # 下载组件工具镜像 kubeadm config images pull # 如果出现问题,也可以自己手动拉取 docker pull registry.aliyuncs.com/google_containers/kube-proxy:v1.28.11 docker pull registry.aliyuncs.com/google_containers/kube-controller-manager:v1.28.11 docker pull registry.aliyuncs.com/google_containers/kube-scheduler:v1.28.11 docker pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.11 docker pull registry.aliyuncs.com/google_containers/coredns:1.10.1 docker pull registry.aliyuncs.com/google_containers/etcd:3.5.12-0 docker pull registry.aliyuncs.com/google_containers/pause:3.9
搭建 k8s 集群
初始化主节点
使用 kubeadm 可以快速初始化一个主节点,通过初始化面板提示,可以非常简单的使用一条指令,在当前主节点中添加任意个主节点和工作节点,指令如下:
# 1. 每台机器(包括 Master)添加 Master 域名映射(例如 Master ip 为 192.168.0.60) echo "192.168.0.60 my-cluster-endpoint" >> /etc/hosts echo "192.168.0.60 my-master" >> /etc/hosts sudo systemctl restart systemd-networkd # 2. 使用 kubeadm 初始化主节点(只在192.168.0.60这台主节点上执行);最后两项配置用于指定服务间的网段和Pod中容器的网段 # 注意:网段都是内网网段,而且服务器内网网段、服务网段、容器网段不能重叠 # apiserver-advertise-address 一定要是Master节点的IP kubeadm init \ --apiserver-advertise-address 192.168.0.60 \ --control-plane-endpoint my-cluster-endpoint \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.28.11 \ --service-cidr 10.0.0.0/16 \ --pod-network-cidr 172.16.0.0/16 \ --ignore-preflight-errors Swap # 3. 执行初始化主节点命令后,会得到如下提示,需根据提示完成以下步骤: #Your Kubernetes control-plane has initialized successfully! # # 3.1. 需要运行如下指令,生成配置文件 #To start using your cluster, you need to run the following as a regular user: # mkdir -p $HOME/.kube # sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config # sudo chown $(id -u):$(id -g) $HOME/.kube/config # #Alternatively, if you are the root user, you can run: # # export KUBECONFIG=/etc/kubernetes/admin.conf # # 3.2. 需要部署网络插件,以使 Pod 之间可以互相通信,详见下节 【部署 Pod 网络插件】 #You should now deploy a pod network to the cluster. #Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: # https://kubernetes.io/docs/concepts/cluster-administration/addons/ # # 3.3. 可以使用下面指令,加入任意个 Master 或 Worker 节点。 # token有效期默认一天,使用 kubeadm token create --print-join-command 指令可以重新获取token #You can now join any number of control-plane nodes by copying certificate authorities #and service account keys on each node and then running the following as root: # # kubeadm join my-cluster-endpoint:6443 --token 3478p8.gouutf7clkrsux3p \ # --discovery-token-ca-cert-hash sha256:ede8032a95797f87569564c48c3f22e2b3bd99a1eaa0a7c06140724203ed6353 \ # --control-plane # #Then you can join any number of worker nodes by running the following on each as root: # #kubeadm join my-cluster-endpoint:6443 --token 3478p8.gouutf7clkrsux3p \ # --discovery-token-ca-cert-hash sha256:ede8032a95797f87569564c48c3f22e2b3bd99a1eaa0a7c06140724203ed6353# 初始化需要一定时间,需等待相关 Pod 成功运行后才能使用 k8s 指令 # 在还未成功初始化期间,如使用 kubectl 指令可能出现连接被拒绝 # 查看各个 Pod 运行状况可使用如下指令,-A 是指所有命名空间下的 Pod kubectl get pod -A # 查看节点信息 kubectl get nodes部署 Pod 网络插件
在上节使用 k8s 初始化集群中,提示配置 Pod 网络,官网给出了多种网络插件,我们使用其中的 Calico。
cd /usr/local/my-calico/ # 1. 创建Calico;可以参考官网使用较新版本,示例为 3.28 版本 kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml # 2. 下载配置文件 curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/custom-resources.yaml # 注意,.yaml 中 Pod 网段默认是 192.168.0.0/16; # 此网段对应 kubeadm init 参数中的 --pod-network-cidr 值,若不相同,需打开配置手动修改 cat custom-resources.yaml | grep -n "192.168.0.0" sed -i 's/"192.168.0.0\/16"/"172.16.0.0\/16"/' ./custom-resources.yaml # 3. 先观察第1步是否运行成功,若是运行状态则运行 kubectl create ... kubectl get pods -n tigera-operator kubectl create -f custom-resources.yaml部署 k8s 的 UI 可视化平台
UI 界面有很多,这里随便介绍几个。
dashboard是官方提供的可视化界面,感觉不太好用,安装非常简单:
# Add kubernetes-dashboard repository helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/ # Deploy a Helm Release named "kubernetes-dashboard" using the kubernetes-dashboard chart helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboardKubeSphere为 k8s 提供了优美的 UI 操作界面,由青云团队开发,比 k8s 官方的 dashboard 功能更加强大,界面更加优美,推荐使用。
Kuboard也是一款不错的 k8s 的 UI 管理工具,若有兴趣可以了解一下,之前尝试了一下用他的Kuboard-Spray安装 k8s 集群,这里简要记录一下:
取一台服务器安装即可(需已经安装 docker)。
dockerdocker run -d \ --privileged \ --restart=unless-stopped \ --name=kuboard-spray \ -p 80:80/tcp \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/local/docker/k8s/kuboard-spray/data:/data \ eipwork/kuboard-spray:latest-amd64 # 如果抓不到这个镜像,可以尝试一下这个备用地址: # swr.cn-east-2.myhuaweicloud.com/kuboard/kuboard-spray:latest-amd64docker composeservices: kuboard-spray: image: eipwork/kuboard-spray:latest-amd64 container_name: kuboard-spray privileged: true restart: unless-stopped ports: - "80:80" volumes: - /var/run/docker.sock:/var/run/docker.sock - /usr/local/docker/k8s/kuboard-spray/data:/data安装成功后,浏览器打开地址 http://这台机器的 IP,输入用户名 admin,默认密码 Kuboard123,即可登录 Kuboard-Spray 界面。
卸载
sudo kubeadm reset &&
sudo rm -rf /etc/cni/net.d &&
sudo rm -rf /var/lib/cni/ &&
sudo rm -rf /var/lib/kubelet/* &&
sudo rm -rf /etc/kubernetes/ &&
sudo systemctl restart docker &&
sudo systemctl restart containerd核心实战
Namespace
名称空间用于隔离资源,Kubernetes 集群在启动后会创建一个名为 default 的 Namespace,如果不特别指明 Namespace,则用户创建的 Pod、RC、Service 都将被系统创建到这个默认的名为 default 的 Namespace 中。
# 查看所有 Namespace
kubectl get namespaces
# 查看指定Namespace下的Pod,如不指定,默认查看default名称空间的Pod
kubectl get pods --namespace=dev
# 增、删 Namespace
kubectl create/delete ns my-namespacePod
包含一组与业务相关的容器,Pod 是 kubernetes 管理的最小单位。
创建 Pod ,Pod 默认会被 k8s 自动分配到某个节点运行。在同一 Pod 中的容器,容器内直接使用 127.0.0.1 :端口即可访问其他容器;默认情况下新建的 Pod 是不能通过外部访问的(如浏览器),只能在 Service 中定义,详见 Service 部分。
一般情况下我们不会像下面这样一个一个的创建 Pod,而是使用 Deployment 批量编排,详见下节。
apiVersion: v1
kind: Pod
metadata:
labels:
run: myapp
name: myapp
# namespace: default
spec:
containers:
- image: nginx
name: nginx01
- image: tomcat:8.5.68
name: tomcat01# 指令方式创建一个Pod
kubectl run mynginx --image=nginx
# yml 方式创建一个 Pod
kubectl apply -f pod.yml
# 查看指定 Namespace下的 Pod,如不指定,默认查看 default 名称空间的 Pod
kubectl get pods --namespace=dev
# 查看 Pod 运行状态
kubectl describe pod Pod名字
# 查看 Pod 的运行日志
kubectl logs Pod名字
# 以交互的方式进入 Pod
kubectl exec -it Pod名字 -- /bin/bash
# 删除 Pod
kubectl delete pod Pod名字
# yml 方式删除 Pod
kubectl delete -f pod.yml
# 查看所有 Pod 被分配的 iP 信息
kubectl get pod -owide
# 查看 Pod 运行状态
kubectl get pod -w
kubectl get pod -ADeployment
使用 Deployment,k8s 可以自动将多个 Pod 副本分发到工作节点上,副本会有自己的 ip。
可以把它看作旧版 RC(ReplicationController) 的升级版,能更方便的处理服务扩容和服务升级,他还能使 Pod 具有自愈、滚动更新、版本回退等能力。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-dep
name: my-dep
spec:
replicas: 3
selector:
matchLabels:
app: my-dep
template:
metadata:
labels:
app: my-dep
spec:
containers:
- image: nginx:1.16.0
name: my-nginx# 指令方式新建并启动一个 deployment
# 和 kubectl run mynginx --image=nginx非常相似,区别如下:
# 使用 kubectl delete pod 删除,会发现 deployment 方式会立马运行一个新的 Pod ,这就是自愈的体现
kubectl create deployment my-dep --image=nginx
kubectl create deployment my-dep --image=nginx --replicas=3 # 多副本,自动分配到Worker节点,每个副本有独立的iP
# yml 方式创建一个 deployment
kubectl apply -f xxx.yml
# 查看 Pod 配置信息(yml方式查看)
kubectl get deploy my-dep -oyml
# 指令方式删除 deployment
kubectl delete deployment my-dep
# Deployment 的牛逼用法,一行指令做到动态扩缩容、滚动更新、版本回退
# 指令方式 -- Pod 扩容/缩容,只需要改变 replicas 的值即可做到一键扩缩容功能
kubectl scale -n default deployment my-dep --replicas=5
# yml方式 -- Pod 扩容/缩容,下面指令会自动返回 deployment 的 yml 配置,编辑 replicas 属性保存即可
kubectl edit deployment my-dep
# 滚动更新(不会立马停掉所有旧版本,而是每启动成功一个新版本,就停一个旧版本,保证服务的高可用)
# --record 用于记录本次更新,可用于查询历史记录
kubectl set image deployment/my-dep my-nginx=nginx:1.16.1 --record
# 查看版本历史记录
kubectl rollout history deployment/my-dep
# 回滚到上一个版本
kubectl rollout undo deployment/my-dep
# 回滚到指定版本; --to-revision 的属性值通过查询版本历史记录获取,历史记录是一个递增的列表
kubectl rollout undo deployment/my-dep --to-revision=1工作负载
除了 Deployment,k8s 还有 StatefulSet 、DaemonSet 、Job 等类型资源,他们用于管理不同类型的 Pod,都称为工作负载。
| 工作负载 | 适用场景 |
|---|---|
| Deployment | Deployment 是在集群上运行应用最常见的, 适合在集群上管理无状态应用工作负载,如部署 Java 微服务程序,它相对独立,不依赖系统其他资源,且可以随时替换。 |
| StatefulSet | 部署有状态的应用,比如数据库,提供存储、网络等功能 |
| DaemonSet | 守护型应用部署,比如日志收集组件,在每台机器上运行一份 |
| Job/CronJob | 定时任务部署,比如垃圾清理组件,在指定时间部署运行 |
具体使用参考官网
Service
Pod 副本在不同节点上有不同的 IP,而且默认只能在内网访问,Service 可以将这些 Pod 副本提供的服务统一暴露在一个 IP 下,并且支持外部访问;Service 相当于实现了 Pod 的服务发现和负载均衡。
当应用扩缩容、宕机等,Service 会通过服务发现机制,自动排除掉不可用的节点,因此不需要任何人为操作。
apiVersion: v1
kind: Service
metadata:
labels:
app: my-dep
name: my-dep
spec:
selector:
app: my-dep
ports:
- port: 8000
protocol: TCP
targetPort: 80# 指令方式 -- 暴露 Pod 的 80 端口到 Service 的 8000 端口
# --type=ClusterIP 是默认值,这种类型下只能节点内部访问
# --type=NodePort 可以同时支持节点内部和外部访问(如浏览器,需要机器有外网IP,
# 开放的8000端口只能内部访问,同时还会在每个节点开放同一个供外部访问的端口,
# 这个端口在 30000-32767 之间)
kubectl expose deployment my-dep --port=8000 --target-port=80 --type=ClusterIP
# yml方式 -- 暴露 Pod 的 80 端口到 Service 的 8000 端口
kubectl apply -f xxx.yml
# 查看已经创建的 Service
kubectl get service
# 删除 Service
kubectl delete svc my-depIngress
提示
Ingress 目前已停止更新,可使用 Gateway API替代,详情参考官网。
k8s 集群中会存在多个 Service,他们分别管理自己的 Pod 网络,比如订单服务、用户服务等,相应的会存在多个对外暴露的端口。
Ingress 相当于一个网关的角色,通过配置,使客户端使用不同的域名将请求转发到不同的 Service 中,它内部其实就是一个 Nginx。
在使用 Ingress 资源之前需要安装 Ingress 控制器,控制器有多种,如ingress-nginx 安装。
假设有两个名称分别为 my-order(8080 端口) 和 my-user(8081 端口) 的 Service,可通过下面配置实现请求转发:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress-test
# 限流功能,每秒放行一个请求,异常状态码 503
annotations:
nginx.ingress.kubernetes.io/limit-rps: "1"
spec:
ingressClassName: nginx
rules:
- host: "order.zjx.com"
http:
paths:
- pathType: Prefix # 前缀匹配,path 也可以写正则表达式
path: "/"
backend:
service:
name: my-order
port:
number: 8080
- host: "user.zjx.com"
http:
paths:
- pathType: Exact # 精确匹配,只允许指定 path
path: "/nginx"
backend:
service:
name: my-user
port:
number: 8081# 新建 Ingress
kubectl apply -f xxx.yml
# 获取 Ingress 列表
kubectl get ing
# 编辑 Ingress ,会返回yml配置,修改后保存退出即可生效
kubectl edit ing my-ingress-test存储卷
Pod 在运行期间会可能保存一些文件数据,若最开始它在节点 A 上运行,如果发生扩缩容、故障转移等, A 节点上保存的文件,在其他节点上就不能访问了。
k8s 利用存储卷,以保证 Pod 能正确的访问到相应文件。
存储卷的存储类型很多,比如 NFS、CephFS、Glusterfs 等,这里以 NFS 为例。
NFS (Network File System) 可以在每个节点中指定一个目录,在任意一个节点目录中写入,会自动同步到其他节点。
安装 NFS
所有机器都要安装
Ubuntuapt-get install nfs-kernel-serverCentOSyum install -y nfs-utils配置 NFS
NFS 分为服务端和客户端,可以在主节点中开启 NFS 服务端,并指定一个开放的目录,其他节点同步这个目录的数据。完成配置后,在任意节点操作目录,都会自动同步到其他节点。
主节点mkdir -p /nfs/data sudo chown nobody:nogroup /nfs/data # 将 /nfs/data/ 目录暴露给其他节点 echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports systemctl enable rpcbind --now systemctl enable nfs-kernel-server --now # Ubuntu systemctl enable nfs-server --now # CentOS # 使配置生效 exportfs -r从节点# 查看主节点可以同步的目录列表 showmount -e 192.168.0.60 mkdir -p /nfs/data sudo chown nobody:nogroup /nfs/data # 挂载 nfs 服务器上的共享目录到本机路径 mount -t nfs 192.168.0.60:/nfs/data /nfs/dataPod 绑定 NFS
Pod 内部也相当于一台 Linux,有自己的文件系统,可以将内部文件目录挂载到 NFS 服务端,实现文件同步,保证每个 Pod 共享文件,示例配置如下:
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 # 指定要挂载的 Pod 内部目录 volumeMounts: - name: my-nginx-file mountPath: /usr/share/nginx/html volumes: - name: my-nginx-file # NFS 服务端同步的目录,注意要保证服务端存在这个目录 nfs: server: 192.168.0.60 path: /nfs/data/nginx-demo
PV & PVC
上节 Pod 绑定 NFS 存储卷中有一些缺点,比如不能限制文件容量(Pod 会无限制的写入数据)、Pod 删除但文件依然存在等。使用 PV(持久卷,Persistent Volume) 和 PVC (持久卷申明,Persistent Volume Claim) 可以自定义 Pod 文件存储容量。
PV 可以定义实际文件保存位置以及分配的空间大小,PVC 用于定义 Pod 期望的空间大小;可以提前定义多个 PV 构成 PV 池,k8s 可以根据 PVC 申明的大小自动找到并引用合适的 PV。
手动定义 PV 是一件无聊的事情,使用默认存储类型可通过 PVC 自动创建合适的 PV;本节内容理解一下即可。
以下示例中定义了两个 PV,一个 PVC,且 Pod 和 PVC 绑定:
# 定义 PV ,注意目录需要真实存在
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-10m
spec:
capacity:
storage: 10M
# 存储卷访问模式
accessModes:
- ReadWriteMany
storageClassName: my-nfs
# 使用 NFS 存储
nfs:
path: /nfs/data/01
server: 192.168.0.60
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02-1gi
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
storageClassName: my-nfs
nfs:
path: /nfs/data/02
server: 192.168.0.60
---
# 定义 PVC,申请大小为200M,k8s 会自动选择上面 1G 的 PV
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 200Mi
storageClassName: my-nfs
---
# 创建Pod绑定PVC
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-nginx-deploy-pvc
name: my-nginx-deploy-pvc
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx-deploy-pvc
template:
metadata:
labels:
app: my-nginx-deploy-pvc
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
persistentVolumeClaim:
claimName: my-nginx-pvc# 查看 PV 或 PVC
kubectl get pv/pvc默认存储类型
存储类(StorageClass)描述存储了存储的不同策略,当一个 PVC 没有指定 storageClassName 时,会使用默认存储类型,并且可以根据 PVC 指定的大小自动分配存储空间,不需要手动创建 PV。
以下是使用 NFS 作为默认存储类型的示例(注意有两处 NFS 服务端 IP 需要写成自己的):
## 创建了一个存储类
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "true" ## 删除pv的时候,pv的内容是否要备份
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2
# resources:
# limits:
# cpu: 10m
# requests:
# cpu: 10m
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 192.168.0.60 ## 指定自己nfs服务器地址
- name: NFS_PATH
value: /nfs/data ## nfs服务器共享的目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.0.60 ## 指定自己nfs服务器地址
path: /nfs/data
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.iokubectl apply -f my-defult-storage-class.yml
kubectl get pod -A
# 查看是否生效
kubectl get scConfigMap
通过存储卷可以实现挂载目录,但有时候我们只想挂载某些文件(如配置文件),ConfigMap 可以统一管理这些文件,并且可以自动更新。
创建 ConfigMap
# 将指定文件保存到 ConfigMap 中(实际存入了 etcd 数据库),保存后可以删除原文件 kubectl create cm my-redis-conf --from-file=./redis.conf # 获取文件列表 kubectl get cm # 查看创建的配置 kubectl get cm my-redis-conf -oyml # 修改文件,会自动应用到 Pod kubectl edit cm my-redis-conf # cm 修改配置会同步到Pod内部文件,但程序可能不会应用已修改配置,可以手动应用或者重启使之生效 kubectl exec -it Pod名字 -- /bin/bash给 Pod 绑定 ConfigMap
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: /redis-master name: my-config - mountPath: xxxx name: xxxx volumes: - name: my-config configMap: name: my-redis-conf items: - key: redis.conf path: redis.conf - name: xxxx
Secret
Secret 用来保存敏感信息,如密码、OAuth 令牌和 SSH 密钥等。 将这些信息放在 secret 中比放在 Pod 的定义或者容器镜像中更加安全和灵活。
创建 secret
# 创建 secret ;以 docker-registry 为例,保存一个账户信息,每次拉取就不需要输入密码 kubectl create secret docker-registry my-docker-info \ --docker-server=192.168.0.60:5000 \ --docker-username=zjx \ --docker-password=123456 \ --docker-email=986379932@qq.com # 获取 secret 列表 kubectl get secret # 查看 secret 信息,可以发现 data 部分是一个 md5 加密串 kubectl get secret my-docker-info -oyml给 Pod 绑定 secret
apiVersion: v1
kind: Pod
metadata:
name: private-nginx
spec:
containers:
- name: private-nginx
image: 192.168.0.60:5000/zjx/nginx
imagePullSecrets:
- name: my-docker-info
