#markdown 笔记下载:
wget  https://install-data.oss-cn-hongkong.aliyuncs.com/%E7%AC%94%E8%AE%B0/kubernetes%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.md 

1.搭建kubernetes集群
Kubernetes 组件介绍
  • 一个 kubernetes 集群主要由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。

  • 控制节点(master):集群的控制平面,负责集群的决策。

    • API Server:集群操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制,同时协调各组件的工作。

    • Scheduler:负责集群资源调度,按照预定的调度策略将 Pod 调度到相应的 node 节点上。

    • ControllerManager:负责维护集群的状态,比如程序部署安排、故障检测、自动扩展和滚动更新等。

    • Etcd:负责存储集群中各种资源对象的信息。

  • 工作节点(node):集群的数据平面,负责为容器提供运行环境。

    • Kubelet:负责维护容器的生命周期,即通过控制 Docker ,来创建、更新、销毁容器。

    • KubeProxy:负责提供集群内部的服务发现和负载均衡。

    • Docker:负责节点上容器的各种操作。

  • 部署网络组件的作用?
    实现容器跨主机网络通信

  • 容器接口

CNI 容器网络接口
CRI 容器运行时接口 (对接容器接口)
CSI 容器存储接口

  • k8s弃用docker前,与弃用后

k8s -> docker-shim/cri -> docker (有dockershim,默认能兼容docker)

k8s -> cri -> docker(支持cri) (后期需要docker去兼容对接 CRI)

1.1.环境准备
master 192.168.3.124
node1  192.168.3.125
node2  192.168.3.126

##### 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

#关闭selinux
sed -i 's/enforcing/disabled/' /etc/selinux/config  # 永久
setenforce 0  # 临时

#关闭swap
swapoff -a  # 临时
sed -ri 's/.*swap.*/#&/' /etc/fstab    # 永久,sed中&,经常用来在原文本下增加字符串,其中&就相当于要被替换".*swap.*
swapoff -a && swapon -a  #刷新swp
(在初始化得时候如果常出现swp报错请重启服务器,然后再执行一遍关闭swp得命令)

#修改主机名,修改hosts
vim  /etc/hosts
192.168.3.124  master
192.168.3.125  node01
192.168.3.126  node02

#将桥接ipv4流量传递到iptables的链上
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system # 生效

#时间同步
yum install -y ntpdate
timedatectl set-timezone Asia/Shanghai

在kubernetes中service有两种代理模型,一种是基于iptables,另一种是基于ipvs的。ipvs的性能要高于iptables的,但是如果要使用它,需要手动载入ipvs模块。
修改内核参数:
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf 

1)加载ipvs:
[root@k8s-master ~] mkdir /opt/ipvs && cd /opt/ipvs
[root@master ipvs] yum -y install ipset ipvsadm sysstat conntrack libseccomp
[root@master ipvs] vim ipvs.sh
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_sh
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- nf_conntrack_ipv4

#执行脚本
[root@master ipvs] chmod +x ipvs.sh
[root@master ipvs] bash ipvs.sh

2)让脚本开机自启:
[root@master ipvs]# lsmod |grep ip_vs

[root@master ipvs]# echo "bash /opt/ipvs/ipvs.sh" >> /etc/rc.local
[root@master ipvs]# chmod +x /etc/rc.local
1.2.安装docker(每台都执行)
yum -y install yum-utils device-mapper-persistent-data lvm2

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

yum-config-manager --enable docker-ce-edge

yum install -y docker-ce-19.03.9-3.el7 docker-ce-cli-19.03.9-3.el7 containerd.io  docker-compose

systemctl enable docker
systemctl start docker

#配置镜像加速(阿里云镜像站)
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF

1.3.安装kubeadm和kubelet(每台都执行)
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

yum -y install  kubelet-1.23.0-0.x86_64  kubeadm-1.23.0-0.x86_64 kubectl-1.23.0-0.x86_64

systemctl enable kubelet
1.4.使用kubeadm部署master
kubeadm init --kubernetes-version=1.23.0 --apiserver-advertise-address=192.168.1.211  --service-cidr=10.64.0.0/24 --pod-network-cidr=10.244.0.0/16   --image-repository registry.aliyuncs.com/google_containers

#注:主机名中不能带有_(下划线),不然初始化会报错

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf  $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

查看node信息
kubectl get nodes
1.5.让node加入集群
#当主节点启动完k8s之后会生成加入节点的命令,视自己服务器而定
kubeadm join 172.16.8.124:6443 --token xxxxx \
    --discovery-token-ca-cert-hash xxxxxx

#默认token有效期为24小时,当过期之后,该token就不可用了。这时就需要重新创建token,操作如下:
kubeadm token create --print-join-command

#查看节点是否已加入
kubectl get nodes
1.6.集群组件报错
1.6.1.所有节点显示 NotReady (是因为还没安装网络组件)

[外链图片转存中…(img-NzrNASwg-1661304119398)]

#查看组件健康情况
kubectl get  cs

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UmqPvvvp-1661304119400)(https://oss.linuxtxc.com/images/image-20220407170643950.png)]

  • 解决方法
vim  /etc/kubernetes/manifests/kube-controller-manager.yaml (注释掉 - --port=0vim  /etc/kubernetes/manifests/kube-scheduler.yaml          (注释掉 - --port=0)
systemctl start kubelet 

1.7.安装pod网络插件
#主节点执行
#kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

#安装calico
# wget https://docs.projectcalico.org/v3.8/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml

wget  https://docs.projectcalico.org/manifests/calico.yaml
kubectl apply -f  calico.yaml

#修改部分 (#取消注释)
- name: CALICO_IPV4POOL_CIDR   
  value: "10.244.0.0/16"    #地址为初始化集群时的“--pod-network-cidr”的设置IP


查看pods信息
kubectl get pods -n kube-system
1.8.安装私有镜像仓库harbor
#安装docker-compose
#可以通过修改URL中的版本,可以自定义您的需要的版本
curl -L https://get.daocloud.io/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

为安装脚本添加执行权限
chmod +x /usr/local/bin/docker-compose

# 安装Compose命令补全工具
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose

#重启docker
systemctl daemon-reload
systemctl restart docker

#安装harbor
wget https://storage.googleapis.com/harbor-releases/release-1.8.0/harbor-offline-installer-v1.8.0.tgz
tar xvf harbor-offline-installer-v1.8.0.tgz
cd harbor/
vim harbor.yml (修改默认端口)
./prepare
./install.sh

#登录harbor (没有证书,需要添加信任)
(1)docker login http://192.168.3.124:8080 (docker登录harbor)
(docker无法连接harbor时,请在/etc/docker/daemon.json 中添加{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
   "insecure-registries": ["harbor的主机ip:端口"]
}
(如果还不行请./install.sh)

#创建连接镜像仓库的secret
kubectl create secret docker-registry harbor-key-secret --docker-server=harbor.linuxtxc.com:9090 --docker-username=admin --docker-password=Harbor123 
1.9.安装coredns
#官方模板:https://github.com/coredns/deployment/blob/master/kubernetes/coredns.yaml.sed
#复制内容模板内容
[root@k8s_master yaml]# vim coredns.yaml

#要修改的内容
CLUSTER_DNS_IP: 10.0.0.2   #对应kubelet的DNS
CLUSTER_DOMAIN: cluster.local
REVERSE_CIDRS: in-addr.arpa ip6.arpa
STUBDOMAINS: 无
UPSTREAMNAMESERVER: /etc/resolv.conf
# 修改镜像为1.7.1
coredns/coredns:1.7.1

#修改后的coredns.yaml
wget https://oss.linuxtxc.com/deploy/yaml/coredns.yaml
kubectl apply -f coredns.yaml
1.10.重置kubernetes集群
(master,node都执行) 
rm -rf  ./kube
rm -rf /etc/cni/net.d/*


(master上执行 )
kubeadm  reset
kubeadm init --kubernetes-version=1.23.0 --apiserver-advertise-address=192.168.1.211  --service-cidr=10.64.0.0/24 --pod-network-cidr=10.244.0.0/16   --image-repository registry.aliyuncs.com/google_containers

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

#出现pod无法起来时
#vim  /etc/kubernetes/manifests/kube-controller-manager.yaml (注释掉 - --port=0)
#vim  /etc/kubernetes/manifests/kube-scheduler.yaml          (注释掉 - --port=0)
#systemctl restart kubelet 

(node 上执行)
kubeadm reset
kubeadm join 192.168.3.124:6443 --token f62e1g.exfdrr8bg2fxpdmu \
    --discovery-token-ca-cert-hash sha256:7e4762cccce2e8306b6635cbc2091eae34bbf33577276413521a343c49fc026a
    
    
#安装1.24之后的版本时
rm -rf /etc/containerd/config.toml 

containerd config default |   tee /etc/containerd/config.toml

sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml

 sed -i 's#sandbox_image = "k8s.gcr.io/pause:3.6"#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"#g' /etc/containerd/config.toml
 
systemctl restart containerd
1.11.切换容器引擎为Containerd
#启动模块(默认已经启用)
modprobe overlay
modprobe br_netfilter

#设置必需的 sysctl 参数
cat > /etc/sysctl.d/cri-containerd.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system

#安装containerd (已经安装docker后,默认会有containerd)
#yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
#yum install -y containerd.io
#mkdir -p /etc/containerd

#生成默认的配置文件
containerd config default > /etc/containerd/config.toml
  • 修改配置文件

​ • pause镜像设置过阿里云镜像仓库地址

​ • cgroups驱动设置为systemd

​ • 拉取Docker Hub镜像配置加速地址设置为阿里云镜像仓库地址


sed -i 's#SystemdCgroup = false#SystemdCgroup = true#g' /etc/containerd/config.toml

 sed -i 's#sandbox_image = "k8s.gcr.io/pause:3.6"#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"#g' /etc/containerd/config.toml

vim /etc/containerd/config.toml        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]  #修改

#重启containerd
systemctl restart containerd
  • 配置kubelet使用containerd
vi /etc/sysconfig/kubelet 
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd

systemctl restart kubelet

#验证
kubectl get node -o wide

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOkslN3w-1661304119401)(https://oss.linuxtxc.com/images/image-20220531181711832.png)]

  • 管理容器工具

containerd提供了ctr命令行工具管理容器,但功能比较简单,所以一般会用crictl工具检查和调试容器。

项目地址:https://github.com/kubernetes-sigs/cri-tools/

设置crictl连接containerd:

vi /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
  • docker与crictl命令对照表:
*镜像相关功能**Docker**Containerd*
显示本地镜像列表docker imagescrictl images
下载镜像docker pullcrictl pull
上传镜像docker push无,例如buildk
删除本地镜像docker rmicrictl rmi
查看镜像详情docker inspect IMAGE-IDcrictl inspecti IMAGE-ID
*容器相关功能**Docker**Containerd*
显示容器列表docker pscrictl ps
创建容器docker createcrictl create
启动容器docker startcrictl start
停止容器docker stopcrictl stop
删除容器docker rmcrictl rm
查看容器详情docker inspectcrictl inspect
附加容器docker attachcrictl attach
执行命令docker execcrictl exec
查看日志docker logscrictl logs
查看容器资源docker statuscrictl status
*POD* *相关功能**Docker**Containerd*
显示 POD 列表crictl pods
查看 POD 详情crictl inspectp
运行 PODcrictl run
停止 PODcrictl stop
1.12.编写dockerfile
  • 使用alpine 构建 nginx镜像
FROM alpine:latest
RUN  sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories    #更换下载地址
RUN  apk update && apk --no-cache add  gcc libc-dev make openssl-dev pcre-dev zlib-dev linux-headers curl gnupg libxslt-dev gd-dev geoip-dev pcre wget zlib 
RUN  mkdir /opt/apps
ADD  nginx-1.19.6.tar.gz  /usr/local/src/ 

RUN cd /usr/local/src/nginx-1.19.6 && ./configure  --prefix=/opt/apps/nginx --with-http_stub_status_module --with-http_geoip_module --with-http_ssl_module --with-http_gzip_static_module  --with-http_v2_module --with-stream --with-stream_ssl_module --user=nginx --group=nginx && make && make install 
ENV PATH $PATH:/opt/apps/nginx/sbin
COPY nginx.conf /opt/apps/nginx/conf/nginx.conf 
ADD index.html /opt/apps/nginx/html/index.html
RUN echo 'if (\$request_filename ~* .*\.(jspx|jsp|z|gz|bz2|7z|zip|rar|tar|tar.gz|tar.bz2|tgz)$) { \
    return 403; \
    break;  \
}' > /opt/apps/nginx/conf/filter.forbid
RUN addgroup  -g 2019 -S nginx && adduser  -s /sbin/nologin -S -D  -u 2019 -G nginx nginx
RUN  mkdir /opt/apps/nginx/conf/vhosts && mkdir -p /opt/logs/nginx && chown -R nginx:nginx /opt/apps/nginx
WORKDIR  /opt/apps/nginx
EXPOSE 80 443 
CMD ["nginx", "-g", "daemon off;"]

2.kubernetes 资源管理
2.1.kubectl create
kubectl create  namespace  dev    #创建一个namespace    

kubectl create service clusterip ngx-dep --tcp=80:80  #命令行创建service

kubectl create -f  ns-dev.yaml    #根据yaml文件创建资源

kubectl create deployment  deploy-nginx  --image=nginx:1.17.2 --replicas=3  -n dev
#暴露端口
kubectl expose deployment deploy-nginx   --port=80 --target-port=80 --type=NodePort --name=nginx-service -n dev

#命令行创建yaml模板(–dry-run:不执行命令,打印资源文件,-o yaml,将资源输出为YAML格式)
kubectl create deployment  deploy-nginx  --image=nginx:1.17.2 --replicas=3  -n dev --dry-run=client -o yaml > deploy-nginx.yaml

#命令行创建service
kubectl create service clusterip ngx-dep --clusterip="none" --tcp=80:80 --dry-run=client -o yaml

kubectl create service nodeport nginx-service --tcp=80:80  --dry-run=client -o yaml
2.2.kubectl api-resources
kubectl api-resources   #查看k8s所有的资源以及对应的apiversion

kubectl api-versions    #查看k8s所有 版本号
2.3.kubectl explain
kubectl explain 资源类型(pod,deployment)   # 查看某种资源可以配置的一级配置

kubectl explain 资源类型.属性       #查看资源的子属性
2.4.kubectl label

版本类标签(release):stable(稳定版)、canary(金丝雀版本,可以将其称之为测试版中的测试版)、beta(测试版);
环境类标签(environment):dev(开发)、qa(测试)、production(生产)、op(运维);
应用类(app):ui、as、pc、sc;
架构类(tier):frontend(前端)、backend(后端)、cache(缓存);
分区标签(partition):customerA(客户A)、customerB(客户B);
品控级别(Track):daily(每天)、weekly(每周)。

kucectl label pod  nginx-pod version=1.0 -n web    #为pod打上标签

kubectl label pod  --show-labels -n web            #显示pod的标签

kucectl label pod  nginx-pod version=1.0  --overwrite -n web     #覆盖原来的标签
2.5.kubectl describe
kubectl describe  pod nginx-pod  -n web   #查看pod 的详细信息

kubectl describe service nginx-svc -n web  #查看service的 详细信息

kubectl describe node 192.168.3.125     #查看node的详细信息  
2.6.kubectl exec
#进入pod容器中
kubectl exec -it  nginx-deployment-798444d598-87hlk  -n web /bin/sh
2.7.kubectl get
kubectl get cs   #获取集群健康状态

kubectl get csr  #查看申请加入kubernetes集群的token信息

kubectl get endpoints -n web #获取service对应的所有pod的访问地址

kubectl get pod -n web -o wide -w    #查看namespace web下的pod,--all-namespaces:查看所有namespace下的pod;-w:动态查看;-o wide:查看更多信息

kubectl get pod  --show-labels  (显示资源标签)

kubectl get pod  -l app=nginx  (-l,根据标签过滤资源)
2.8.kubectl edit
kubectl edit cm kubeadm-config -n kube-system   #cm,configMap;编辑configMap中的配置
2.9.kubectl logs
kubectl logs nginx-deployment-798444d598-87hlk  -n web   #查看pod日志

kubectl logs -f  nginx-deployment-798444d598-87hlk -n web  #实时查看日志

kubectl log  nginx-deployment-798444d598-87hlk -c <container_name> -n web  #查看pod中单个容器的日志
kubectl logs -l app=frontend  -n web    #返回全部标记为 app=frontend 的 pod 的合并日志
2.10.kubectl delete
kubectl delete -f  deployment-nginx.yaml   #删除该yaml中创建的资源

kubectl delete pod nginx-deployment-798444d598-87hlk -n web  #删除pod资源

#有部分 Terminating状态的pod无法删除,可以使用 --grace-period=0 --force强制删除
kubectl delete  pod calico-kube-controllers-6b77fff45-m6w4n -n kube-system --grace-period=0 --force
2.11.kubectl autoscale
kubectl autoscale deployment deployment-nginx --cpu-percent=60 --min=2 --max=10     #使用 Deployment “deployment-nginx”设定,使用默认的自动伸缩策略,指定目标CPU使用率,使其Pod数量在2到10之间。
2.12.kubectl patch
#使用(patch)补丁修改、更新资源的字段。
kubectl patch pod rc-nginx-2-kpiqt -p '{"metadata":{"labels":{"app":"nginx-3"}}}'  #修改资源配置
2.13.kubectl replace
kubectl replace -f rc-nginx.yaml   #根据yaml文件更新修改后配置资源,会停掉原来的资源,重新创建
2.14.kubectl scale
kubectl scale rc redis --replicas=3 -n web    #操作pod 的扩容和缩容

kubectl scale --replicas=2 -f redis-slave-deployment.yaml   
2.15.kubectl rollout
status:显示当前升级状态
history:显示升级历史记录
pause:暂停版本升级过程
resume:继续已经暂停的版本升级过程
restart:重启版本升级过程
undo:回滚到上一级版本 (可以使用--to-revision,指定版本)
kubectl apply -f pc-deployment.yml --record  (--record,记录操作记录,便于回滚)
   

更新镜像:
(1)通过kubectl set 命令去更改,nginx为容器名
kubectl set image  deploy  nginx-deployment nginx=nginx:1.15.2 -n app --record  && kubectl  rollout  pause deployment  nginx-deployment -n app   #相当于灰度发布

(2)通过修改deployment文件去更改
kubectl apply -f nginx-deployment.yml --record  && kubectl  rollout  pause deployment    nginx-deployment -n app 

#继续更新过程
kubectl rollout resume deployment nginx-deployment -n app

#查看历史版本
kubectl  rollout history deployment nginx-deployment -n app 

#版本回滚到v1
kubectl rollout undo deploy nginx-deployment --to-revision=1 -n app
2.16.kubectl taint
 kubectl taint node k8s-node1 tag=webapps:PreferNoSchedule  #设置污点
 
 kubectl taint node k8s-node1 tag:PreferNoSchedule-         #去除污点
 
 kubectl taint node k8s-node1 tag-                          #去除所有污点
 
 kubectl describe nodes k8s-node1 | grep Taints            #查看节点上的污点
 
#将node标记为不可调度的状态,这样就不会让新创建的pod在此node上运行。
kubectl cordon 192.168.1.48
 
#恢复node为可调度的状态。
kubectl uncordon 192.168.1.48

#可以让node在维护期间排除节点。drain本意排水,意思是将出问题的node下的pod转移到其它node下运行,并且不接收新的pod。
kubectl drain 192.168.1.48 --ignore-daemonsets --delete-local-data

#drain的参数
--force
当一些pod不是经 ReplicationController, ReplicaSet, Job, DaemonSet 或者 StatefulSet 管理的时候
就需要用--force来强制执行 (例如:kube-proxy)

--ignore-daemonsets
无视DaemonSet管理下的Pod
 
--delete-local-data
如果有mount local volumn的pod,会强制杀掉该pod并把料清除掉
2.17.kubectl run
kubectl run nginx --image=nginx:1.17.1 -n web   #命令使用镜像创建容器
2.18.kubectl apply
#如果资源不存在,就创建,相当于kubectl create。
#如果资源存在,就更新,相当于kubectl patch。
kubectl apply -f nginx-pod.yaml -n web --record  (--record,记录操作记录,便于回滚)
2.19.kubectl top
kubectl top  pod/node  -n dev   #查看资源使用情况(需要安装metrics-server)

#-l,标签选择,--sort-by,根据参数排序
kubectl top pod  -l name=cpu-utillzer --sort-by="cpu” 	-A
2.20.kubectl expose
#将tomcat-deployment 服务的8080 端口 暴露给 tomcat-service 的service (也相当于创建service)
kubectl expose deployment tomcat-deployment   --port=8080 --target-port=8080 --type=ClusterIP --name=tomcat-service -n app
2.21.kubectl cp
#将pod里的文件拷贝到主机
#kubectl cp -c 容器名 pod名:文件绝对路径 文件目标位置 -n namespace名
kubectl cp     nginx-deployment-84b859f76c-crdrl:tmp/helm-v3.5.4-linux-amd64.tar.gz  /tmp/helm-v3.5.4-linux-amd64.tar.gz -n app

#将主机文件拷贝到pod
#kubectl cp 主机文件路径  -c 容器 pod名:容器内绝对路径 -n namespace名
kubectl cp   tomcat-java-demo-master.zip  nginx-deployment-84b859f76c-crdrl:tmp  -n app   (如果pod中有多个容器,需要指定进入的容器名)

注:pod后的目录绝对路径不用写"/"
2.22.kubectl certificate
#kubectl certificate用来修改证书资源,可选approve/deny同意与拒绝审批。



2.23.kubectl config
#config命令,生成集群信息,集群用户和用户权限并把这些内容写入kubectl读取的配置文件
# 设置集群参数
[root@k8s-master admin]# kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/cert/ca.pem --embed-certs=true --server=${KUBE_APISERVER} --kubeconfig=kubectl.kubeconfig
#设置客户端认证参数
[root@k8s-master admin]# kubectl config set-credentials admin --client-certificate=admin.pem --client-key=admin-key.pem --embed-certs=true --kubeconfig=kubectl.kubeconfig
#设置上下文参数,包含集群名称和访问集群的用户名字
[root@k8s-master admin]# kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=kubectl.kubeconfig
#使用默认上下文
[root@k8s-master admin]# kubectl config use-context kubernetes --kubeconfig=kubectl.kubeconfig

2.24.docker 命令
#查看容器运行的进程
docker top 容器ID/容器名

#删除所有未被容器使用的镜像
docker image prune -a

#删除所有停止运行的容器
docker container prune

#删除所有未被挂载的卷
docker volume prune


#删除所有未使用的网络
docker network prune

#用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及dangling镜像(即无tag的镜像),-a,清理的更加彻底
docker system prune -a



#查看Docker的磁盘使用情况,-v,显示空间使用的详细信息
docker system df -v

3.kubernetes 的Pod 详解

pod:k8s 最小控制单元,其中包含一个pause容器(又叫infra容器,基础容器),多个业务 容器,提供服务,pod中容器共享网络,存储资源
pause容器作用:可以根据它判断pod的健康状态,同时在pause容器上设置IP,可以让同一个pod中的所有容器共用

  • 创建一个pod工作流程:

    • kubectl向apiserver发送一个创建pod的请求,apiserver会将数据放到etcd存储

    • scheduler收到未绑定pod资源,通过自身调度算法选择一个合适的node进行绑定,然后响应给apiserver

    • kubelet收到分配到自己节点上pod,调用docker api创建容器,然后将容器状态响应给apiserver

3.1.Pod 所有参数配置
apiVersion: v1
kind: Namespace
metadata:
   name: dev
---
#所有常用配置参数
apiVersion: v1     #必选,版本号,例如v1
kind: Pod         #必选,资源类型,例如 Pod
metadata:         #必选,元数据
  name: string     #必选,Pod名称
  namespace: string  #Pod所属的命名空间,默认为"default"
  labels:           #自定义标签列表
    - name: string                 
spec:  #必选,Pod中容器的详细定义
  containers:  #必选,Pod中容器列表
  - name: string   #必选,容器名称
    image: string  #必选,容器的镜像名称
    imagePullPolicy: [ Always|Never|IfNotPresent ]  #获取镜像的策略 
    command: [string]   #容器的启动命令列表,如不指定,使用打包时使用的启动命令
    args: [string]      #容器的启动命令参数列表
    workingDir: string  #容器的工作目录
    volumeMounts:       #挂载到容器内部的存储卷配置
    - name: string      #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
      mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
      readOnly: boolean #是否为只读模式
    ports: #需要暴露的端口库号列表
    - name: string        #端口的名称
      containerPort: int  #容器需要监听的端口号
      hostPort: int       #容器所在主机需要监听的端口号,默认与Container相同
      protocol: string    #端口协议,支持TCP和UDP,默认TCP
    env:   #容器运行前需设置的环境变量列表
    - name: string  #环境变量名称
      value: string #环境变量的值
    resources: #资源限制和请求的设置
      limits:  #资源限制的设置
        cpu: string     #Cpu的限制,单位为core数
        memory: string  #内存限制,单位可以为Mib/Gib
      requests: #资源请求的设置
        cpu: string    #Cpu请求,容器启动的初始可用数量
        memory: string #内存请求,容器启动的初始可用数量
    lifecycle: #生命周期钩子
		postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
		preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
    livenessProbe:  #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
      exec:         #对Pod容器内检查方式设置为exec方式
        command: [string]  #exec方式需要制定的命令或脚本
      httpGet:       #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
        path: string
        port: number
        host: string
        scheme: string
        HttpHeaders:
        - name: string
          value: string
      tcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式
         port: number
       initialDelaySeconds: 0       #容器启动完成后首次探测的时间,单位为秒
       timeoutSeconds: 0          #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
       periodSeconds: 0           #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
       successThreshold: 0
       failureThreshold: 0
       securityContext:
         privileged: false
  restartPolicy: [Always | Never | OnFailure]  #Pod的重启策略
  nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
  nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
  imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
  - name: string
  hostNetwork: false   #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
  volumes:   #在该pod上定义共享存储卷列表
  - name: string    #共享存储卷名称 (volumes类型有很多种)
    emptyDir: {}       #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
    hostPath: string   #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
      path: string                #Pod所在宿主机的目录,将被用于同期中mount的目录
    secret:          #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
      scretname: string  
      items:     
      - key: string
        path: string
    configMap:         #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
      name: string
      items:
      - key: string
        path: string
        
3.1.1.环境变量ENV
  • 创建容器时,为容器设置环境变量

    • 应用场景:

      • 容器内应用程序希望获取Pod信息

      • 容器内应用程序希望通过用户定义的变量改变默认行为

      变量值几种定义方式:

      • 自定义变量值

      • 变量值从Pod属性获取

      • 变量值从Secret、ConfigMap获取

apiVersion: v1
kind: Pod
metadata:
  name: pod-envars
spec:
  containers:
    - name: test-container
      image: busybox
      command: [ "sh", "-c","sleep 3600h"]
      env:
        #变量值从Pod属性获取
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        #自定义变量值
        - name: PASSWORD
          value: qwer123
3.2.Pod资源限制
  • limits 限制容器最高使用多少资源,requests 容器使用的最小资源申请(对于k8s来说是预留,主要用作k8s对node资源一个规划和分配)。
  • requests一般小于limits的20%-30%。
  • limits建议不能超出宿主机配置,否则没意义了,至少要低于宿主机配置的20%
  • requests必须小于或等于limits。
  • k8s会根据requests的值去查找能满足该值的node进行调度,如果不满足,pod处于未分配创建。
  • requests决定了一个节点分配的pod数量,所以不要设置太大,否则会造成node资源浪费,即跑的pod少,实际负载很低。
  • kubectl describe node k8s-node2 查看节点资源分配情况。
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: app
  labels:
    app: nginx
spec:
  containers:
    - name: nginx01 # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent # 设置镜像拉取策略,Alawys;IfNotPresent;Never
      ports: # 端口设置
        - containerPort: 80 # 容器要监听的端口 (0~65536)
          protocol: TCP # 端口协议
      resources: # 资源配额
        limits: # 限制资源的上限
          cpu: "1" # CPU限制,单位是core数,0.1=100m
          memory: "10Gi" # 内存限制
        requests: # 限制资源的下限
          cpu: "0.5" # CPU限制,单位是core数, 0.5=500m
          memory: "1Gi" # 内存限制
  • imagePullPolicy:用于设置镜像拉取的策略,kubernetes支持配置三种拉取策略:

    • Always:总是从远程仓库拉取镜像(一直远程下载)。
    • IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就用本地,本地没有就使用远程下载)。
    • Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错(一直使用本地,没有就报错)。
3.3.Pod 生命周期

我们一般将Pod对象从创建到终止的这段时间范围称为Pod的生命周期,它主要包含下面的过程:

    • Pod创建过程。
    • 运行初始化容器(init container)过程,执行一些主容器的前置工作,执行完就结束。

    • 应用场景:

      • 环境检查:例如确保应用容器依赖的服务启动后再启动应用容器

      • 初始化配置:例如给应用容器准备配置文件

    • 运行主容器(main container):
      • 容器启动后钩子(post start)。
      • 容器的存活性探测(liveness probe)、就绪性探测(readiness probe)。
      • 容器终止前钩子(pre stop)。
    • Pod终止过程。
3.3.1.钩子函数
  • 钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。

  • kubernetes在主容器启动之后和停止之前提供了两个钩子函数:

    • post start:容器创建之后执行,如果失败会重启容器。
    • pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。
  • 钩子处理器支持使用下面的三种方式定义动作:

    • ① exec命令:在容器内执行一次命令。
……
  lifecycle:
     postStart: 
        exec:
           command:
             - cat
             - /tmp/healthy
……

​ ② tcpSocket:在当前容器尝试访问指定的socket。

…… 
   lifecycle:
      postStart:
         tcpSocket:
            port: 8080
……

​ ③ httpGet:在当前容器中向某url发起HTTP请求。

…… 
   lifecycle:
      postStart:
         httpGet:
            path: / #URI地址
            port: 80 #端口号
            host: 192.168.109.100 #主机地址  
            scheme: HTTP #支持的协议,http或者https
……

以exec方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-hook-exec
  namespace: app
  labels:
    app: nginx 
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
      resources:
        limits:
          cpu: "2"
          memory: "10Gi"
        requests:
          cpu: "1"
          memory: "10Mi"
      lifecycle: # 生命周期配置
        postStart: # 容器创建之后执行,如果失败会重启容器
          exec: # 在容器启动的时候,执行一条命令,修改掉Nginx的首页内容
            command: ["/bin/sh","-c","echo postStart ... > /usr/share/nginx/html/index.html"]
        preStop: # 容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作
          exec: # 在容器停止之前停止Nginx的服务
            command: ["/usr/sbin/nginx","-s","quit"]
3.3.2.容器探测

容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。

    • liveness probes:存活性探测,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器。
    • readiness probes:就绪性探测,用于检测应用实例是否可以接受请求,如果不能,k8s不会转发流量,并将容器从service中剔除。
  • 上面两种探针目前均支持三种探测方式:

    • ① exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常。
----------------------------
readinessProbe:
      exec:
        command:
        - cat
        - /etc/hosts
      initialDelaySeconds: 5
      timeoutSeconds: 2
      successThreshold: 3
      failureThreshold: 2
      periodSeconds: 5
-----------------------------
    • ② tcpSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常。
……
   livenessProbe:
     tcpSocket:
       port: 8080
     failureThreshold: 5           #检测失败5次表示未就绪
     initialDelaySeconds: 60       #初始化时间
     periodSeconds: 10             #检测间隔
     successThreshold: 1           #检查成功为1次表示就绪,存活和启动探测的这个值必须是 1
     timeoutSeconds: 5             #检测超时5秒表示未就绪 
……
    • ③ httpGet:调用容器内web应用的URL,如果返回的状态码在200和399之前,则认为程序正常,否则不正常。
------------------------ 
 livenessProbe:         #健康检查方式:[readinessProbe,livenessProbe,StartupProbe]
   httpGet:                      #请求方式
     path: /health               #请求路径
     port: 8080                  #请求端口
     scheme: HTTP                ##请求协议
   failureThreshold: 5           #检测失败5次表示未就绪
   initialDelaySeconds: 60       #初始化时间
   periodSeconds: 10             #检测间隔
   successThreshold: 1           #检查成功为1次表示就绪,存活和启动探测的这个值必须是 1
   timeoutSeconds: 5             #检测超时5秒表示未就绪
----------------------------
  • startupProbe : 启动探测,k8s1.16版本后新加的探测方式,用于判断容器内应用程序是否已经启动,如果配置了startuprobe,就会先禁用其他的探测,直到它成功为止。
----------------------------------
startupProbe:             #健康检查方式:[readinessProbe,livenessProbe,StartupProbe]
  httpGet:                        #请求方式
    path: /                       #请求路径
    port: 8080                    #请求端口
    scheme: HTTP                  #请求协议
  failureThreshold: 3             #检测失败3次表示未就绪
  periodSeconds: 10               #检测间隔
  successThreshold: 1             #检查成功为1次表示就绪,存活和启动探测的这个值必须是 1
  timeoutSeconds: 1               #检测超时1秒表示未就绪
----------------------------------

initialDelaySeconds # 容器启动后等待多少秒执行第一次探测。

timeoutSeconds # 探测超时时间。默认1秒,最小1秒。

periodSeconds # 执行探测的频率。默认是10秒,最小1秒。

failureThreshold # 连续探测失败多少次才被认定为失败。默认是3。最小值是1。

successThreshold # 连续探测成功多少次才被认定为成功。默认是1,存活和启动探测的这个值必须是 1。

  • 重启策略:

    • Always:容器失效时,自动重启该容器,默认值。

    • OnFailure:容器终止运行且退出码不为0时重启。

    • Never:不论状态如何,都不重启该容器。

  • readinessProbe+ livenessProbe混合案例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: probe-deployment
  namespace: app
spec:
  replicas: 2
  revisionHistoryLimit: 10  #可保留的版本数
  minReadySeconds: 5        #在等待设置的时间后才进行升级
  strategy:  #策略 
    type: RollingUpdate 
    rollingUpdate:  #滚动更新 
      maxSurge: 30%  #最大额外可以存在的副本数,可以为百分比,也可以为整数; 默认25%
      maxUnavailable: 30% #最大不可用状态的 ;Pod 的最大值,可以为百分比,也可以为整数,默认25% 
  selector:
    matchLabels:
      project: dev
      app: nginx
  template:
    metadata:
      labels:
        project: dev
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.3
          resources: # 资源配额
            limits: # 限制资源的上限
              cpu: "1" # CPU限制,单位是core数,0.1=100m
              memory: "100Mi" # 内存限制
            requests: # 限制资源的下限
              cpu: "0.5" # CPU限制,单位是core数, 0.5=500m
              memory: "50Mi" # 内存限制
          livenessProbe: #存活性检查,重启容器
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5   ##初始化时间,多少秒后开始第一次检测
            timeoutSeconds: 1        #检测超时1秒表示未就绪,默认1秒,最小1秒
            periodSeconds: 10        #执行探测的频率,默认是10秒,最小1秒
            failureThreshold: 5      #检测失败5次表示未就绪,默认是3
            successThreshold: 1      #检查成功为1次表示就绪,默认是1,存活和启动探测的这个值必须是 1
          readinessProbe:   #就绪性探测,从service中剔除容器
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5   ##初始化时间,多少秒后开始第一次检测
            timeoutSeconds: 1        #检测超时1秒表示未就绪,默认1秒,最小1秒
            periodSeconds: 10        #执行探测的频率,默认是10秒,最小1秒
            failureThreshold: 5      #检测失败5次表示未就绪,默认是3
            successThreshold: 3      #检查成功为2次表示就绪,默认是1

4.kubernetes 中Pod调度
  • kubernetes提供了四大类调度方式。
    • 自动调度:运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。

    • 定向调度:NodeName、NodeSelector。

    • 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity。

    • 污点(容忍)调度:Taints、Toleration。

4.1.定向调度
  • 定向调度,指的是利用在Pod上声明的nodeName或nodeSelector,以此将Pod调度到期望的Node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过Pod运行失败而已。

nodeName:用于强制约束将Pod调度到指定的name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。

  • 创建一个pod-nodename.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: web
  labels:
    app: nginx
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeName: k8s-node1                #指定调度到k8s-node1节点上

nodeSelector:用于将Pod调度到添加了指定标签的Node节点上,它是通过kubernetes的label-selector机制实现的,换言之,在Pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将Pod调度到目标节点,该匹配规则是强制约束。

#给node节点添加标签
kubectl label node k8s-node1 node=web
kubectl label node k8s-node2 node=app
  • 创建pod-nodeselector.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeSelector:
    node: app        #指定调度到具有node=app的Node节点上
4.2.亲和性调度
  • 虽然定向调度的两种方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用的Node列表也不行,这就限制了它的使用场景。

  • 基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。它在nodeSelector的基础之上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使得调度更加灵活。

  • Affinity主要分为三类:

    • nodeAffinity(node亲和性):以Node为目标,解决Pod可以调度到哪些Node的问题。

    • podAffinity(pod亲和性):以Pod为目标,解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。

    • podAntiAffinity(pod反亲和性):以Pod为目标,解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。

4.2.1.node亲和性(nodeAffinity)
  • 查看nodeAffinity的可选配置项:
pod.spec.affinity.nodeAffinity
  requiredDuringSchedulingIgnoredDuringExecution  #Node节点必须满足指定的所有规则才可以,相当于硬限制
    nodeSelectorTerms         #节点选择列表
      matchFields             #按节点字段列出的节点选择器要求列表  
      matchExpressions   #按节点标签列出的节点选择器要求列表(推荐)
        key             #键
        values          #值
        operator #      关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
---
  preferredDuringSchedulingIgnoredDuringExecution    #优先调度到满足指定的规则的Node,相当于软限制 (倾向)     
    preference    #一个节点选择器项,与相应的权重相关联
      matchFields  #按节点字段列出的节点选择器要求列表
      matchExpressions   #按节点标签列出的节点选择器要求列表(推荐)
        key              #键
        values           #值
        operator     #关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt  
    weight           #倾向权重,在范围1-100。
---
- matchExpressions:
	- key: nodeenv # 匹配存在标签的key为nodeenv的节点
	  operator: Exists   
	- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
	  operator: In    
      values: ["xxx","yyy"]
    - key: nodeenv # 匹配标签的key为nodeenv,且value大于"xxx"的节点
      operator: Gt   
      values: "xxx"
  • 下面演示preferredDuringSchedulingIgnoredDuringExecution(软限制):
    • 创建pod-nodeaffinity-preferred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-preferred
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
      preferredDuringSchedulingIgnoredDuringExecution: # 先匹配node标签中有nodeenv=app或者nodeenv=xyy的node,优先调度到满足指定的规则的Node,相当于软限制 (倾向)
        - preference: # 一个节点选择器项,与相应的权重相关联
            matchExpressions:
              - key: nodeenv
                operator: In
                values:
                  - "app"
                  - "xyy"
          weight: 50
  • 下面演示preferredDuringSchedulingIgnoredDuringExecution(硬限制):

    • 创建pod-nodeaffinity-preferred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    nodeAffinity: # node亲和性配置
      requiredDuringSchedulingIgnoredDuringExecution: #先匹配node标签中有nodeenv=app或者nodeenv=xyy的node,如果没有,则会调度失败,相当于硬规则,类似于定向调度
        nodeSelectorTerms: # 节点选择列表
          - matchExpressions:
              - key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是"xxx"或"yyy"的节点
                operator: In
                values:
                  - "app"
                  - "yyy"

nodeAffinity的注意事项:

  • 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都满足,Pod才能运行在指定的Node上。

  • 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。

  • 如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功。

  • 如果一个Pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的nodeAffinity的要求,则系统将忽略此变化。

4.2.2.Pod亲和性(PodAffinity)
  • PodAffinity的可选配置项:
pod.spec.affinity.podAffinity
  requiredDuringSchedulingIgnoredDuringExecution   #硬限制
    namespaces                                    #指定参照pod的namespace
    topologyKey                                   #指定调度作用域
    labelSelector                              # 标签选择器
      matchExpressions                #按节点标签列出的节点选择器要求列表(推荐)
        key                           #键
        values                         #值
        operator                     #关系符 支持In, NotIn, Exists, DoesNotExist.
      matchLabels                 #指多个matchExpressions映射的内容  
  preferredDuringSchedulingIgnoredDuringExecution  #软限制    
    podAffinityTerm                              #选项
      namespaces                                 #指定参照pod的namespace
      topologyKey                                 #指定调度作用域
      labelSelector                                # 标签选择器
         matchExpressions 
            key                                     # 键  
            values                                   # 值  
            operator
         matchLabels 
    weight                           # 倾向权重,在范围1-100

#topologyKey用于指定调度的作用域,例如:
#如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围。
#如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分。
  • 创建Pod过程:
    • 创建pod-podaffinity-requred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-requred
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-web
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    podAffinity: # Pod亲和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
        - labelSelector:
            matchExpressions: # 该Pod必须和拥有标签app=nginx或者app=test的Pod在同一个Node上。
              - key: app
                operator: In
                values:
                  - "nginx"
                  - "test"
          topologyKey: kubernetes.io/hostname
  • 创建Pod过程:
    • 创建pod-podaffinity- prefer.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-prefer
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx01
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-app
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    podAffinity: # Pod亲和性
      preferredDuringSchedulingIgnoredDuringExecution:  #软限制
        -  weight: 50
           podAffinityTerm:
             labelSelector:
               matchExpressions:  #先匹配拥有标签app=yyy或者app=test的Pod,如果有,则将pod放在匹配pod同一个Node上,如果没有则让Scheduler根据算法自动调度
                 - key: app
                   operator: In
                   values:
                     - "yyy"
                     - "test"
             topologyKey: kubernetes.io/hostname
4.2.3.Pod反亲和性(podAntiAffinity)
  • 主要实现以运行的Pod为参照,让新创建的Pod和参照的Pod不在一个区域的功能。
apiVersion: v1
kind: Pod
metadata:
  name: pod-podantiaffinity-requred
  namespace: app
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 亲和性配置
    podAntiAffinity: # Pod反亲和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values:
                  - "nginx"
          topologyKey: kubernetes.io/hostname
4.3.污点和容忍
4.3.1.污点(Taints)
  • Node被设置了污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。

  • 污点的格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持如下三个选项:

    • PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可以调度。

    • NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但是不会影响当前Node上已经存在的Pod。

    • NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已经存在的Pod驱逐。

#设置污点
kubectl taint node xxx key=value:effect

#去除污点
kubectl taint node xxx key:effect-

#去除所有污点
kubectl taint node xxx key-

#查询所有节点的污点
wget -O jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x ./jq
cp jq /usr/bin
kubectl get nodes -o json | jq '.items[].spec'

#查看所有节点上的污点
kubectl describe node  | grep Taint

#给k8s-node1节点设置污点(PreferNoSchedule)
kubectl taint node k8s-node1 tag=test:PreferNoSchedule

#为k8s-node1取消污点(PreferNoSchedule),并设置污点(NoSchedule)
kubectl taint node k8s-node1 tag:PreferNoSchedule-
kubectl taint node k8s-node1 tag=test:NoSchedule
4.3.2.容忍

污点是拒绝,容忍就是忽略,Node通过污点拒绝Pod调度上去,Pod通过容忍忽略拒绝

  • 容忍的详细配置:
kubectl explain  pod.spec.tolerations  
.........
tolerations
- key       # 对应着要容忍的污点的键,空意味着匹配所有的键
  value     # 对应着要容忍的污点的值
  operator  # key-value的运算符,支持Equal(默认)和Exists
  effect    # 对应污点的effect,空意味着匹配所有影响
  tolerationSeconds   # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间

当operator为Equal的时候,如果Node节点有多个Taint,那么Pod每个Taint都需要容忍才能部署上去。

当operator为Exists的时候,有如下的三种写法:

  • 容忍指定的污点,污点带有指定的effect:

  • 容忍指定的污点,不考虑具体的effect:

  • 容忍一切污点(慎用):

tolerations: # 容忍
  - key: "tag" # 要容忍的污点的key
    operator: Exists # 操作符
    effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同  
tolerations: # 容忍
  - key: "tag" # 要容忍的污点的key
    operator: Exists # 操作符
tolerations: # 容忍
  - operator: Exists # 操作符
  • 创建pod-toleration.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  tolerations: # 容忍
    - key: "tag" # 要容忍的污点的key
      operator: Equal # 操作符
      value: "test" # 要容忍的污点的value
      effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同
  • 案例:
#给两个节点都打上污点
kubectl taint node 192.168.3.125 tag=test:NoSchedule
kubectl taint node 192.168.3.125 tag=deploy:NoSchedule

vim  pod-toleration-test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  tolerations: # 容忍
    - key: "tag" # 要容忍的污点的key
      operator: Equal # 操作符
      value: "test" # 要容忍的污点的value
      effect: NoSchedule # 添加容忍的规则,这里必须和标记的污点规则相同
    - key: "tag" # 要容忍的污点的key
      operator: Equal # 操作符
      value: "deploy" # 要容忍的污点的value
      effect: NoExecute # 添加容忍的规则,这里必须和标记的污点规则相同 

5.kubernetes的Pod控制器
5.1.Deployment控制器
  • Deployment控制器并不直接管理Pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。

  • Deployment的主要功能如下:

    • 支持ReplicaSet的所有功能。

    • 支持发布的停止、继续。

    • 支持版本滚动更新和版本回退。

  • 创建nginx-deployment.yaml文件,在spec下添加更新策略

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: app
spec:
  replicas: 3
  revisionHistoryLimit: 10  #可保留的版本数
  minReadySeconds: 5        #在等待设置的时间后才进行升级
  strategy:  #策略 
    type: RollingUpdate 
    rollingUpdate:  #滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod 
      maxSurge: 30%  #最大额外可以存在的副本数,可以为百分比,也可以为整数; 默认25%
      maxUnavailable: 30% #最大不可用状态的 ;Pod 的最大值,可以为百分比,也可以为整数,默认25% 
  selector:
    matchLabels:
      app: nginx
    #matchExpressions: # Expressions匹配规则 
    #  - {key: app, operator: In, values: [nginx-pod]}
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          imagePullpolicy: ifnotpresent
          ports:
            - containerPort: 80
  • 镜像更新
(1)kubectl set image deployment nginx-deployment nginx=nginx:1.17.2 -n app
(2)或者修改 nginx-deployment.yaml 中的image,再使用kubectl apply -f nginx-deployment.yaml --recard 更新资源
(3)kubectl edit deployment nginx-deployment -n app ,直接使用命令修改deployment
#查看升级过程
kubectl get pod -n app -w

[外链图片转存中…(img-iQ00ZQtU-1661304119403)]

  • 查看rs,发现原来的rs依旧存在,只是Pod的数量变为0,而后又产生了一个rs,Pod的数量变为3
kubectl get rs -n app

[外链图片转存中…(img-jz48YXWe-1661304119403)]

  • 查看历史版本
kubectl  rollout history deployment -n app 

[外链图片转存中…(img-MDRJTHoh-1661304119403)]

  • 版本回滚
kubectl rollout undo deploy nginx-deployment --to-revision=1 -n app
  • 金丝雀发布(在一批新的Pod资源创建完成后立即暂停更新过程)
(1)kubectl set image  deploy  nginx-deployment nginx=nginx:1.17.3 -n app   && kubectl  rollout  pause deployment  nginx-deployment -n app

(2)kubectl apply -f nginx-deployment.yml --record  && kubectl  rollout  pause deployment    nginx-deployment -n app
5.2.Horizontal Pod Autoscaler(HPA)

HPA可以获取每个Pod的利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。

5.2.1.安装metrics-server(用来收集集群中的资源使用情况。)
#wget https://github.com/kubernetes-sigs/metrics-server/archive/v0.3.6.tar.gz
wget  https://oss.linuxtxc.com/metrics-server.zip
#解压
unzip metrics-server.zip

#进入metrics-server/deploy/1.8+/目录
cd  metrics-server/deploy/1.8+

#修改metrics-server-deployment.yaml文件
vim metrics-server-deployment.yaml

按图中添加下面选项
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6 
args:
  - --kubelet-insecure-tls 
  - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjmCT9by-1661304119404)(https://oss.linuxtxc.com/images/image-20220312164723469.png)]

  • 安装安装metrics-server(v0.4.1)
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.4.1/components.yaml

修改components.yaml
修改部分:
- --kubelet-insecure-tls
image: registry.cn-shanghai.aliyuncs.com/xuweiwei-kubernetes/metrics-server:v0.4.1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dg9HwlM-1661304119404)(https://oss.linuxtxc.com/images/image-20220312183606595.png)]

  • 查看metrics-server生成的Pod:
kubectl get pod -n kube-system

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C994J9iH-1661304119405)(https://oss.linuxtxc.com/images/image-20220312165123704.png)]

5.2.2.错误排查
  • 查看资源使用情况报错

[外链图片转存中…(img-FxRlHXi9-1661304119405)]

  • 排查错误
systemctl status kube-apiserver -l  #或者 journalctl -u kube-apiserver

[外链图片转存中…(img-lmUg8rKc-1661304119406)]

  • metric-server安装要满足2个条件

    • 在master节点要能访问metrics server pod ip(kubeadm部署默认已经满足该条件,二进制部署需注意要在master节点也部署node组件)

    • apiserver启用聚合层支持(kubeadm默认已经启用,二进制部署需自己启用)

因为我是二进制安装的,所以在master上安装node组件就可以了

kubectl top pod -n app

[外链图片转存中…(img-WRtEclKp-1661304119406)]

5.2.3.部署HPA案例
  • 使用之前创建的nginx-deployment,使用kubectl edit 将副本数改为 1

[外链图片转存中…(img-V05Wvr6K-1661304119406)]

  • 创建 nginx-service.yaml
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service
  namespace: app
spec:
  selector: # 标签选择器,用于确定当前Service代理那些Pod
    app: nginx-deployment
  type: NodePort 
  ports: # 
    - port: 80 # Service端口
      protocol: TCP 
      targetPort : 80
      nodePort: 30081
  • 创建nginx-hpa.yaml
apiVersion: autoscaling/v1 # 版本号
kind: HorizontalPodAutoscaler # 类型
metadata: # 元数据
  name: nginx-hpa # deployment的名称
  namespace: app  #命名类型
spec:
  minReplicas: 1 # 最小Pod数量
  maxReplicas: 10 # 最大Pod数量
  targetCPUUtilizationPercentage: 3 # CPU使用率指标
  scaleTargetRef:  # 指定要控制的Nginx的信息
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment
#使用命令创建HPA控制器
kubectl autoscale deployment nginx-hpa --cpu-percent=3 --min=1 --max=10
  • targets为unknown 的原因

[外链图片转存中…(img-AV50oZq4-1661304119407)]

  • 需要安装metrics-server

  • HPA对应的pod,需要设置request资源

#使用命令行设置pod 资源限制
kubectl set resources deployment/nginx-deployment --limits=cpu=800m
  • 再次查看:

[外链图片转存中…(img-OGNv12s4-1661304119407)]

  • 使用ab 命令访问测试

[外链图片转存中…(img-IqdmfnAw-1661304119408)]

  • 查看 HPA状态

[外链图片转存中…(img-lPPLUKzc-1661304119408)]

5.3.DaemonSet
  • DaemonSet类型的控制器可以保证集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景

  • DaemonSet控制器的特点:

    • 每向集群中添加一个节点的时候,指定的Pod副本也将添加到该节点上。
    • 当节点从集群中移除的时候,Pod也会被垃圾回收。
  • DaemonSet的资源清单:

apiVersion: apps/v1 # 版本号
kind: DaemonSet # 类型
metadata: # 元数据
  name: # 名称
  namespace: #命名空间
  labels: #标签
    controller: daemonset
spec: # 详情描述
  revisionHistoryLimit: 3 # 保留历史版本
  updateStrategy: # 更新策略
    type: RollingUpdate # 滚动更新策略
    rollingUpdate: # 滚动更新
       maxSurge: 30%  #最大额外可以存在的副本数,可以为百分比,也可以为整数; 默认25%
      maxUnavailable: 1 # 最大不可用状态的Pod的最大值,可用为百分比,也可以为整数,默认25%
  selector: # 选择器,通过它指定该控制器管理那些Pod
    matchLabels: # Labels匹配规则
      app: nginx-pod
    matchExpressions: # Expressions匹配规则
      - key: app
        operator: In
        values:
          - nginx-pod
  template: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板
     metadata:
       labels:
         app: nginx-pod
     spec:
       containers:
         - name: nginx
           image: nginx:1.17.1
           ports:
             - containerPort: 80
  • 案例
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: kube-system
  labels:
    k8s-app: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      containers:
      - image: prom/node-exporter
        name: node-exporter
        ports:
        - containerPort: 9100
          protocol: TCP
          name: http
5.4.Job
  • Job主要用于负责批量处理短暂的一次性任务。J可以保证指定数量的Pod执行完成。

  • Job的特点:

    • 当Job创建的Pod执行成功结束时,Job将记录成功结束的Pod数量。
    • 当成功结束的Pod达到指定的数量时,Job将完成执行。
  • Job的资源清单:

apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
  name:  # 名称
  namespace:  #命名空间
  labels: # 标签
    controller: job
spec: # 详情描述
  completions: 1 # 指定Job需要成功运行Pod的总次数,默认为1
  parallelism: 1 # 指定Job在任一时刻应该并发运行Pod的数量,默认为1
  activeDeadlineSeconds: 30 # 指定Job可以运行的时间期限,超过时间还没结束,系统将会尝试进行终止
  backoffLimit: 6 # 指定Job失败后进行重试的次数,默认为6
  manualSelector: true # 是否可以使用selector选择器选择Pod,默认为false
  selector: # 选择器,通过它指定该控制器管理那些Pod
    matchLabels: # Labels匹配规则
      app: counter-pod
    matchExpressions: # Expressions匹配规则
      - key: app
        operator: In
        values:
          - counter-pod
  template: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板
     metadata:
       labels:
         app: counter-pod
     spec:
       restartPolicy: Never # 重启策略只能设置为Never或OnFailure
       containers:
         - name: counter
           image: busybox:1.30
           command: ["/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done"]
  • Job案例
apiVersion: batch/v1 # 版本号
kind: Job # 类型
metadata: # 元数据
  name: pc-job # 名称
  namespace: dev #命名空间
spec: # 详情描述
  manualSelector: true # 是否可以使用selector选择器选择Pod,默认为false
  parallelism: 1   ##指定job需要成功允许pods的次数。默认值:1
  completions: 1   #指定job 在任一时刻应该并发运行pod 的数量。默认值:1
  selector: # 选择器,通过它指定该控制器管理那些Pod
    matchLabels: # Labels匹配规则
      app: counter-pod
  template: # 模板,当副本数量不足时,会根据下面的模板创建Pod模板
    metadata:
      labels:
        app: counter-pod
    spec:
      restartPolicy: Never # 重启策略只能设置为Never或OnFailure
      containers:
        - name: counter
          image: busybox:1.30
          command: [ "/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 3;done" ]
#查看job运行情况
kubectl get job -n dev -w
5.5.CronJob(CJ)
  • CronJob控制器以Job控制器为其管控对象,并借助它管理Pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似Linux操作系统的周期性任务作业计划的方式控制器运行时间点及重复运行的方式,换言之,CronJob可以在特定的时间点反复去执行Job任务。

  • 应用场景

    • 数据备份

    • 清理日志

  • CronJob的资源清单:

apiVersion: batch/v1beta1 # 版本号
kind: CronJob # 类型
metadata: # 元数据
  name:  # 名称
  namespace:  #命名空间
  labels:
    controller: cronjob
spec: # 详情描述
  schedule:                  #cron格式的作业调度运行时间点,用于控制任务任务时间执行
  concurrencyPolicy:            #并发执行策略
  failedJobsHistoryLimit:       #为失败的任务执行保留的历史记录数,默认为1
  successfulJobsHistoryLimit:   #为成功的任务执行保留的历史记录数,默认为3
  jobTemplate:        #job控制器模板,用于为cronjob控制器生成job对象,下面其实就是job的定义
    metadata: {}
    spec:
      completions: 1     #指定Job需要成功运行Pod的总次数,默认为1
      parallelism: 1     #指定Job在任一时刻应该并发运行Pod的数量,默认为1
      activeDeadlineSeconds: 30   #指定Job可以运行的时间期限,超过时间还没结束,系统将会尝试进行终止
      backoffLimit: 6             #指定Job失败后进行重试的次数,默认为6
      template:               #模板,当副本数量不足时,会根据下面的模板创建Pod模板
        spec:
          restartPolicy: Never     #重启策略只能设置为Never或OnFailure
          containers:
            - name: counter
              image: busybox:1.30
              command: [ "/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done" ]
  • schedule:cron表达式,用于指定任务的执行时间。

    • */1 * * * *:表示分 时 日 月 周。

    • 分钟的值从0到59。

    • 小时的值从0到23。

    • 日的值从1到31。

    • 月的值从1到12。

    • 星期的值从0到6,0表示星期日。

    • 多个时间可以用逗号隔开,范围可以用连字符给出:* 可以作为通配符,/表示每…

  • concurrencyPolicy:并发执行策略

    • Allow:运行Job并发运行(默认)。

    • Forbid:禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行。

    • Replace:替换,取消当前正在运行的作业并使用新作业替换它。

  • 案例

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: pc-cronjob
  namespace: dev
  labels:
    controller: cronjob
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    metadata:
    spec:
      template:
         spec:
           restartPolicy: OnFailure
           containers:
             - name: timing
               image: busybox:latest
               command: ["echo","hello k8s cronjob!"]
#查看cronjob 任务pod
kubectl get pod -n dev

[外链图片转存中…(img-mrbtpcCr-1661304119409)]

5.6.StatefulSet(有状态服务)
  • 无状态应用:

    • 认为Pod都是一样的。
    • 没有顺序要求。
    • 不用考虑在哪个Node节点上运行。
    • 随意进行伸缩和扩展。
  • 有状态应用:

    • 有顺序的要求。
    • 认为每个Pod都是不一样的。
    • pod有序的部署,扩容,删除和停止
    • pod分配有个稳定的且唯一的网络标识
    • pod分配有个独享的存储
  • StatefulSet是Kubernetes提供的管理有状态应用的负载管理控制器。

  • StatefulSet部署需要HeadLinessService(无头服务)。

为什么需要HeadLinessService(无头服务)?

  • 在用Deployment时,每一个Pod名称是没有顺序的,是随机字符串,因此是Pod名称是无序的,但是在StatefulSet中要求必须是有序 ,每一个Pod不能被随意取代,Pod重建后pod名称还是一样的。

  • 而Pod IP是变化的,所以是以Pod名称来识别。Pod名称是Pod唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个Pod一个唯一的名称 。

  • StatefulSet常用来部署RabbitMQ集群、Zookeeper集群、MySQL集群、Eureka集群等。

  • 案例模板

apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
  type: ClusterIP
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pc-statefulset
  namespace: dev
spec:
  replicas: 3
  serviceName: service-headliness
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          ports:
            - containerPort: 80
  • Deployment和StatefulSet的区别:Deployment没有唯一标识而StatefulSet有唯一标识。

  • StatefulSet的唯一标识是根据主机名+一定规则生成的。

  • StatefulSet的唯一标识是statefulset名称-编号(主机名).headless Service名称.命名空间.svc.cluster.local

  • pod独享存储:statefulset的存储卷使用volumeClaimTemplate 创建,称为卷申请模板,当VolumeClaimTemplate 创建 一个PersistentVolume时,同时也会为每个pod分配并创建一个编号的PVC,每个PVC绑定

5.6.1.StatefulSet的金丝雀发布
  • StatefulSet支持两种更新策略:OnDelete和RollingUpdate(默认),其中OnDelete表示删除之后才更新,RollingUpdate表示滚动更新
apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
  type: ClusterIP
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pc-statefulset
  namespace: dev
spec:
  replicas: 3
  type: RollingUpdate
  updateStrategy:
    rollingUpdate:
    partition: 2   #表示从第2个分区开始更新,默认是0
  serviceName: service-headliness  #headliness Service名称
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          ports:
            - containerPort: 80			  			

6.k8s 的service服务
  • 在kubernetes中,Pod是应用程序的载体,我们可以通过Pod的IP来访问应用程序,但是Pod的IP地址不是固定的,这就意味着不方便直接采用Pod的IP对服务进行访问。

  • 为了解决这个问题,kubernetes提供了为一组pod提供负载均衡,并且提供一个统一的入口地址,通过访问Service的入口地址就能访问到后面的Pod服务。

  • Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行了一个kube-proxy的服务进程。当创建Service的时候会通过API Server向etcd写入创建的Service的信息,而kube-proxy会基于监听的机制发现这种Service的变化,然后它会将最新的Service信息转换为对应的访问规则。

# 10.97.97.97:80 是service提供的访问入口
# 当访问这个入口的时候,可以发现后面有三个pod的服务在等待调用,
# kube-proxy会基于rr(轮询)的策略,将请求分发到其中一个pod上去
# 这个规则会同时在集群内的所有节点上都生成,所以在任何一个节点上访问都可以。
[root@k8s-node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
 -> RemoteAddress:Port  Forward Weight ActiveConn InActConn
 TCP 10.97.97.97:80 rr
  -> 10.244.1.39:80   Masq  1  0  0
  -> 10.244.1.40:80   Masq  1  0  0
  -> 10.244.2.33:80   Masq  1  0  0
  • kube-proxy支持的两种工作模式:

    • iptables模式:

      • iptables模式下,kube-proxy为Service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod的IP上。

      • 该模式下kube-proxy不承担四层负载均衡器的角色,只负责创建iptables规则。该模式的优点在于较userspace模式效率更高,但是不能提供灵活的LB策略,当后端Pod不可用的时候无法进行重试。

    • ipvs模式:

      • ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高,除此之外,ipvs支持更多的LB算法。
  • 开启ipvs(必须安装ipvs内核模块,否则会降级为iptables):

kubectl edit cm kube-proxy -n kube-system

[外链图片转存中…(img-XPWQ527A-1661304119409)]

#重新构建所有节点的kube-proxy pod
kubectl delete pod -l k8s-app=kube-proxy -n kube-system

##查看ipvs模块是否开启成功
ipvsadm -Ln

##二进制安装修改ipvs模式(配置文件路径根据实际安装目录为准)
# vi kube-proxy-config.yml
mode: ipvs
ipvs:
  scheduler: "rr"
  
#重启kube-proxy服务
systemctl restart kube-proxy
6.1. Service类型
  • Service的资源清单:
apiVersion: v1 # 版本
kind: Service # 类型
metadata: # 元数据
  name: # 资源名称
  namespace: # 命名空间
spec:
  selector: # 标签选择器,用于确定当前Service代理那些Pod
    app: nginx
  type: NodePort # Service的类型,指定Service的访问方式
  clusterIP:  #虚拟服务的IP地址,不写会默认生成
  sessionAffinity: # session亲和性,支持ClientIP、None两个选项,默认值为None,ClientIP,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上
  ports: # 端口信息
    - port: 8080 # Service端口
      protocol: TCP # 协议
      targetPort : # Pod端口
      nodePort:  # 主机端口
6.1.1.Endpoint(service关联pod一种资源)
  • Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有Pod的访问地址,它是根据service配置文件中的selector描述产生的。

  • 一个service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换言之,service和Pod之间的联系是通过Endpoints实现的。

#查看service 的详细信息
kubectl describe svc nginx-service -n app

[外链图片转存中…(img-mYwbfsAP-1661304119410)]

  • 查看endpoint
 kubectl get  endpoints -n web

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FjLxLwfB-1661304119410)(https://oss.linuxtxc.com/images/image-20220608151612784.png)]

spec.type的说明:

  • ClusterIP:默认值,它是kubernetes系统自动分配的虚拟IP,只能在集群内部访问。

  • NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务。

  • LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境的支持。

  • ExternalName:把集群外部的服务引入集群内部,直接使用。

6.1.2.ClusterIP类型service
  • 只允许集群内部访问
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service
  namespace: app
spec:
  selector: # 标签选择器,用于确定当前Service代理那些Pod
    app: nginx
  clusterIP: 10.0.0.133 # service的IP地址,如果不写,默认会生成一个
  type: ClusterIP
  ports: # 
    - port: 80 # Service端口
      protocol: TCP 
      targetPort : 80
6.1.3.HeadLiness类型的Service
  • 这类Service不会分配Cluster IP,如果想要访问Service,只能通过Service的域名进行查询。

  • 创建service-headliness.yaml文件,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
  type: ClusterIP
  ports:
  - name: http
    port: 80 # Service的端口
    targetPort: 80 # Pod的端口
  ports:
  - name: https
    port: 443 # Service的端口
    targetPort: 443 # Pod的端口
6.2.4.NodePort类型的Service
  • NodePort的工作原理就是将Service的端口映射到Node的一个端口上,然后就可以通过NodeIP:NodePort来访问Service了。

  • 创建service-nodeport.yaml文件,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # Service类型为NodePort
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
      nodePort: 30002 # 指定绑定的node的端口(默认取值范围是30000~32767),如果不指定,会默认分配
  • 查看Service:
kubectl get svc service-nodeport -n dev -o wide
6.1.5.LoadBalancer类型的Service
  • LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer需要在集群的外部再配置一个负载均衡设备,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。
  • 创建service-LoadBalancer.yaml,内容如下
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: web
spec:
  selector:
    app: web-deployment
  type: LoadBalancer # Service类型为NodeBalancer
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
6.1.6. ExternalName类型的Service
  • ExternalName类型的Service用于引入集群外部的服务,它通过externalName属性指定一个服务的地址,然后在集群内部访问此Service就可以访问到外部的服务了。

  • 创建service-externalname.yaml文件,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: test-external
  namespace: app
spec:
  type: ExternalName
  externalName: 192.168.2.214
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80 #目标端口
6.1.7.k8s 接入外部数据库
  • 集群内部访问外部数据库或者中间件一般采用endpoints与service关联方式映射。

  • 创建endpoints-mysql.yaml文件,内容如下:

apiVersion: v1
kind: Endpoints
metadata:
  name: mysql-214
  namespace: app
subsets:
  - addresses:
    - ip: 192.168.2.214
    ports:
    - port: 16303
  • 创建 service-mysql.yaml文件,内容如下:
apiVersion: v1 
kind: Service 
metadata: 
  name: mysql-214   #要与endpoints 的名称一样
  namespace: app
spec:
  type: ClusterIP
  ports: 
    - port: 16303 # Service端口
      protocol: TCP 
      targetPort : 16303
6.1.8.部署案例
  • 创建nginx-deployment.yaml 文件
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: app
spec:
  replicas: 1
  strategy:  #策略 
    type: RollingUpdate 
    rollingUpdate:  #滚动更新 
      maxSurge: 30%  #最大额外可以存在的副本数,可以为百分比,也可以为整数; 默认25%
      maxUnavailable: 30% #最大不可用状态的 ;Pod 的最大值,可以为百分比,也可以为整数,默认25% 
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.3
          ports:
            - containerPort: 80
    • 创建 nginx-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: app
spec:
  selector:
    app: nginx
  type: NodePort # Service类型为NodePort
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
      nodePort: 30082 # 指定绑定的node的端口(默认取值范围是30000~32767),如果不指定,会默认分配
  • 测试访问
#查看service
kubectl get svc service-nodeport -n app -o wide
或者
kubectl describe service service-nodeport -n app

[外链图片转存中…(img-EQU6fcWv-1661304119411)]

6.2.ingress 资源
  • Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点:

    • NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显。
    • LoadBalancer的缺点是每个Service都需要一个LB,浪费,麻烦,并且需要kubernetes之外的设备的支持。
  • 基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。

6.2.1安装ingress需要的环境
#wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml

#项目地址
#https://kubernetes.github.io/ingress-nginx/deploy

#修改镜像地址
image: k8s.gcr.io/ingress-nginx/controller:v1.1.1@sha256:d8196e3bc1e72547c5dec66d6556c0ff92a23f6d0919b206be170bc90d5f9185
修改成:
image: registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.1.1

image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660
修改成:
image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1


[root@k8s_master ~]# wget https://oss.linuxtxc.com/ingress-nginx.zip
[root@k8s_master ~]# unzip ingress-nginx.zip
[root@k8s_master ~]# kubectl apply -f  deploy.yaml

#命令行创建ingress
kubectl create ingress ingress-web --rule=host/path=web-service:80 --dry-run=client -o yaml > ingress-web.yaml

[外链图片转存中…(img-59Ko2fZg-1661304119411)]

6.2.2.Http代理
  • 为服务创建 service,yaml文件内容如下:
apiVersion: v1 
kind: Service 
metadata: 
  name: nginx-service
  namespace: app
spec:
  selector: # 标签选择器,用于确定当前Service代理那些Pod
    app: nginx
  type: ClusterIP
  ports: # 
  - name: http 
    port: 80 # Service端口
    protocol: TCP 
    targetPort : 80
  - name: https
    port: 443 # Service端口
    protocol: TCP 
    targetPort : 443
---

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: app
spec:
  selector:
    app: tomcat
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
  • 创建ingress-http.yaml文件,内容如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-http
  namespace: app
spec:
  rules:
  - host: nginx.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80  #service端口
  - host: tomcat.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
  • 最新版ingress-controller配置yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  namespace: app
spec:
  ingressClassName: nginx
  rules:
  - host: "nginx.linuxtxc.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  • 查看ingress端口:
kubectl get ing ingress-http -n app

[外链图片转存中…(img-SNo6sbDZ-1661304119411)]

#查看访问ingress端口
kubectl get service -n ingress-nginx

[外链图片转存中…(img-6MpHi8Tj-1661304119411)]

  • 访问URL:http://tomcat.linuxtxc.com:32443(可以在本地hosts文件中添加解析访问)

[外链图片转存中…(img-Av2lNlXs-1661304119412)]

6.2.3.修改ingress访问端口
#查看ingress-controller 的pod部署在哪个节点上
kubectl get pod -n ingress-nginx -o wide

[外链图片转存中…(img-QggOcHjV-1661304119413)]

  • 修改ingress配置,并将上面nginx.linuxtxc.com 域名指向node01 公网IP
kubectl edit deployment.apps/ingress-nginx-controller -n ingress-nginx

#添加 --http-port=83

[外链图片转存中…(img-VawnYfVi-1661304119413)]

#删除旧的ingress-nginx-controller pod ,让其自动重新创建
kubectl delete pod ingress-nginx-controller-6bc4ff465c-pmjdr -n ingress-nginx
  • 访问url:http://nginx.linuxtxc.com:83

[外链图片转存中…(img-acAWq4WG-1661304119413)]

6.2.4.Https代理
为ingress 配置https:
 (1)openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/k8s/https/linuxtxc.com.key -x509 -out /opt/k8s/https/linuxtxc.com.crt -subj /C=CN/ST=BJ/L=BJ/O=DEVOPS/CN=linuxtxc.com  -days 3650
 
 (2)kubectl create secret tls tls-linuxtxc.com --key=/opt/k8s/https/linuxtxc.com.key --cert=/opt/k8s/https/linuxtxc.com.crt -n app
 
 req     产生证书签发申请命令
-newkey  生成新私钥
rsa:4096  生成秘钥位数
-nodes   表示私钥不加密
-sha256  使用SHA-2哈希算法
-keyout  将新创建的私钥写入的文件名
-x509   签发X.509格式证书命令。X.509是最通用的一种签名证书格式。
-out 指定要写入的输出文件名
-subj    指定用户信息
-days    有效期
  • 创建ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
  name: ingress-https
  namespace: app
spec:
  tls:
  - hosts:
    - nginx.linuxtxc.com
    - tomcat.linxtxc.com
    secretName: tls-linuxtxc.com    ## 指定秘钥
  rules:
  - host: nginx.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.linuxtxc.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
  • 最新版ingress-controller的yaml配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  tls:
  - hosts:
    - nginx.linuxtxc.com
    secretName: tls-linuxtxc.com    ## 指定秘钥
  ingressClassName: nginx
  rules:
  - host: "nginx.linuxtxc.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  • 查看ingress https访问端口
kubectl get service -n ingress-nginx

[外链图片转存中…(img-SAekgFFk-1661304119414)]

  • 修改https 访问端口
kubectl edit deployment.apps/ingress-nginx-controller -n ingress-nginx

[外链图片转存中…(img-pYQx5mfT-1661304119414)]

  • 删除旧的ingress-controller 的pod,让其重新生成
 kubectl delete pod ingress-nginx-controller-764766d96f-mtpmd -n ingress-nginx
  • 重新使用修改的端口访问

[外链图片转存中…(img-8h4xQDNx-1661304119414)]

6.2.5.ingress个性化配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-connect-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-send-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-read-timeout: 600s
    nginx.ingress.kubernetes.io/proxy-body-size: 8m           #客户端请求正文的最大允许大小
    nginx.ingress.kubernetes.io/proxy-buffering: "on"         #打开代理缓冲
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"     #代理缓冲区数
    nginx.ingress.kubernetes.io/proxy-buffer-size: "8k"       #代理缓冲区大小
    nginx.ingress.kubernetes.io/proxy-max-temp-file-size: "1024m"    #代理最大临时文件大小
    #nginx.ingress.kubernetes.io/proxy-http-version: "1.0"     #代理http版本
    #nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"  #连接代理的标头
    #nginx.ingress.kubernetes.io/enable-access-log: "false"   #启用访问日志
    #nginx.ingress.kubernetes.io/enable-rewrite-log: "true"    #启用日志重写
    #nginx.ingress.kubernetes.io/x-forwarded-prefix: "/"   #X-Forwarded-Prefix标头,匹配/之后对接下来的进行操作
    #nginx.ingress.kubernetes.io/rewrite-target: https://www.baidu.com  #重定向
    #nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"   #后端连接协议
    #nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com #永久重定向(301)
    #nginx.ingress.kubernetes.io/whitelist-source-range: 220.167.31.8i #允许什么ip访问
    nginx.ingress.kubernetes.io/configuration-snippet: |   #自定义配置
      if ($http_user_agent ~* '(iphone)') {
          rewrite  ^/(.*) https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md break;
      }
spec:
  tls:
  - hosts:
    - nginx.linuxtxc.com
    secretName: tls-linuxtxc.com    ## 指定秘钥
  ingressClassName: nginx
  rules:
  - host: "nginx.linuxtxc.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
6.2.6.ingress-nginx 高可用方案
  • 通过DaemonSet+NodeSelector实现将副本在特定节点上运行,然后通过公网负载均衡器提供对外访问
#将ingress-controller 的yaml文件中的Deployment改为 DaemonSet,replicas,副本数根据情况修改
vim  deploy.yaml

#给节点打上标签
kubectl label  node  k8s-node01  ingress-nginx=true

[外链图片转存中…(img-MJxGR1Ns-1661304119414)]

6.3.NetworkPolicy(网络策略)
  • 是一种关于 Pod 间及与其他网络端点间所允许的通信规则的规范。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:           #networkpolicy规约中包含了在一个命名空间中定义特定网络策略所需的所有信息
  podSelector:   #每个networkpolicy都包含一个podselector,对有其标签的pod(role:db),没有的话就对应所有命名空间下所有的pod
    matchLabels:
      role: db    #标签
  policyTypes:   #每个networkpolicy都包含一个policytype列表,包含ingress和egress
 - Ingress    #定义所选择的pod入站规则,如果此字段为空则不允许任何入站流量   
 - Egress      #定义所选择的pod出战规则,如果此字段为空则不允许任何出站流量   
  ingress:  
 - from:
    - ipBlock:  #针对特定的ip cidr范围设置策略,如果设置此字段,则不可以设置其他字段
        cidr: 172.17.0.0/16   #cidr是ip范围,允许这个范围内的ip访问networkpolicy的podselector匹配的pod集合
        except:                #except是ip范围,不允许这个范围内的ip访问networkpolicy的podselector匹配的pod集合
        - 172.17.1.0/24
    - namespaceSelector:  #这将选择特定的命名空间,应将所有的pod用做输入源或者输出目的地
        matchLabels:
          project: myproject
    - podSelector:    #这将与在networkpolicy相同的命名空间中选择带有特定标签的pod(role:frontend),应将其允许作为入口源或者出口目的地
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
 - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

7.k8s的数据存储
  • Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里面的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命周期不和Pod中的单个容器的生命周期有关,当容器终止或者重启的时候,Volume中的数据也不会丢失。

  • kubernetes的Volume支持多种类型,比较常见的有下面的几个:

    • 简单存储:EmptyDir、HostPath、NFS。
    • 高级存储:PV、PVC。
    • 配置存储:ConfigMap、Secret。
7.1.EmptyDir
  • 它是最基础的volume类型,一个EmptyDir就是Host 上的一个空目录,当pod被分配到 Node时创建,它的初始内容为空,当pod 被销毁时,EmptyDir中的数据也会永久删除

  • EmptyDir的用途如下:

    • 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
    • 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
  • 创建volume-emptydir.yaml,内容如下:(一个容器从一个容器中获取数据的目录(多容器共享目录)),可以在/var/lib/kubelet 下查看到,这是k8s的工作目录

apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
        - name: logs-volume
          mountPath: /var/log/nginx
    - name: busybox
      image: busybox:1.30
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
      volumeMounts:  #将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
        - name: logs-volume
          mountPath: /logs
  volumes: # 声明volume,name为logs-volume,类型为emptyDir
    - name: logs-volume
      emptyDir: {}
  • 查看pod
kubectl get pod volume-emptydir -n dev -o wide

[外链图片转存中…(img-lMJaoyew-1661304119415)]

  • 访问pod中的 nginx
 curl 172.16.67.8

[外链图片转存中…(img-YqOrMlGM-1661304119415)]

  • 查看指定容器的标准输出:
kubectl logs -f volume-emptydir -n dev -c busybox

[外链图片转存中…(img-eqpGiRet-1661304119416)]

7.2.HostPath
  • HostPath就是将Node主机中的一个实际目录挂载到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依旧可以保存在Node主机上。

  • 创建volume-hostpath.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: volume-hostpath
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
        - name: logs-volume
          mountPath: /var/log/nginx
    - name: busybox
      image: busybox:1.30
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
      volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
        - name: logs-volume
          mountPath: /logs
  volumes: # 声明volume,name为logs-volume,类型为hostPath
    - name: logs-volume
      hostPath:
        path: /opt/logs/nginx
        type: DirectoryOrCreate # 目录存在就使用,不存在就先创建再使用

type的值的说明:

  • DirectoryOrCreate:目录存在就使用,不存在就先创建后使用。

  • Directory:目录必须存在。

  • FileOrCreate:文件存在就使用,不存在就先创建后使用。

  • File:文件必须存在。

  • Socket:unix套接字必须存在。

  • CharDevice:字符设备必须存在。

  • BlockDevice:块设备必须存在。

查看pod

kubectl get pod volume-hostpath -n dev -o wide

[外链图片转存中…(img-9x45CL1W-1661304119416)]

  • 访问Pod中的Nginx:
curl  172.16.34.7

[外链图片转存中…(img-ptcCxa0v-1661304119416)]

  • 去node节点找到hostPath映射的目录(我是在node01上,如果在此目录中创建文件,到容器中也是可以看到的。)

[外链图片转存中…(img-74gu3fK0-1661304119416)]

7.3.NFS
  • HostPath虽然可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到别的Node节点上,又会出现问题,此时需要准备单独的网络存储系统,比较常用的是NFS

  • NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样,无论Pod在节点上怎么转移,只要Node和NFS的对接没有问题,数据就可以成功访问。

7.3.1.安装NFS服务
  • 下载软件包
yum install -y nfs-utils rpcbind
  • 准备一个共享目录:
mkdir -pv /opt/data
  • vim /etc/exports,将共享目录以读写权限暴露给172.16.8.0/24网段中的所有主机:
/opt/data 172.16.8.0/24(rw,no_root_squash)

#加载配置
exportfs -r

#启动nfs服务
systemctl start  rpcbind
systemctl enable rpcbind
systemctl start  nfs-server
systemctl enable nfs-server
  • K8s node上安装nfs-utils ,但并不启动服务
yum -y install nfs-utils

#查看NFS挂载情况(-d:仅显示已被NFS客户端加载的目录; -e:显示NFS服务器上所有的共享目录。)
showmount -e  172.16.8.217

[外链图片转存中…(img-5v3I8ulr-1661304119417)]

  • 挂载到nfs上 (这步可以不用操作)
mount -t  nfs 172.16.8.217:/opt/data /mnt
7.3.2.pod挂载案例
  • 创建volume-nfs.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: volume-nfs
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
    volumeMounts: # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
    volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
    - name: logs-volume
      mountPath: /logs
  volumes: # 声明volume
  - name: logs-volume
    nfs:
      server: 172.16.8.217 # NFS服务器地址
      path: /opt/data/nfs # 共享文件路径
  • 在NFS服务器上查看 文件共享情况

[外链图片转存中…(img-GPhclCX6-1661304119417)]

7.3.3.NFS+StorageClass实现动态存储
  • nfs-provisioner是一个自动配置卷程序,它使用现有的和已配置的 NFS 服务器来支持通过持久卷声明动态配置
 wget https://oss.linuxtxc.com/nfs-provisoner.zip
  • 下载的nfs-provisoner.yaml,内容如下(storageclass的namespace可以根据自己创建的服务更改):
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - 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: kube-system
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: kube-system
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: kube-system
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: kube-system
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: kube-system
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: quay.io/external_storage/nfs-client-provisioner:latest
          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.1.211 
            - name: NFS_PATH
              value: /opt/data/nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.1.211
            path: /opt/data/nfs

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storageclass
  namespace: default
  #annotations:
  #  storageclass.kubernetes.io/is-kube-system-class: "true"   #设置为默认的sc
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
reclaimPolicy: Retain # 默认为delete
parameters:
  archiveOnDelete: "true"   #启用归档,当删除pvc时,后端数据会归档保留
  • kubectl apply -f nfs-provisoner.yaml 后,查看pod情况

[外链图片转存中…(img-vOlZKyPq-1661304119417)]

  • 创建statefulset文件 statefulset-mysql.yaml,可直接挂载storageclass,内容如下:
apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  labels:
    statefulset: mysql
spec:
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    statefulset: mysql
  type: ClusterIP
  clusterIP: None

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-mysql
spec:
  replicas: 2
  selector:
    matchLabels:
      statefulset: mysql
  serviceName: mysql-headless
  template:
    metadata:
      labels:
        statefulset: mysql
    spec:
      containers:
      - name: mysql01
        image: mysql:8.0
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: root123456
        ports:
          - containerPort: 3306
        volumeMounts:
        - name: nfs-mysql-data
          mountPath: /var/lib/mysql
          subPath: mysql
  volumeClaimTemplates:
    - metadata:
        name: nfs-mysql-data
        annotations:
          volume.beta.kubernetes.io/storage-class: "nfs-storageclass"
          gcp-auto-backup: "yes"
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 5Gi
  • 查看statefulset创建情况,它的每个副本都是独立的,并且使用独立的存储

[外链图片转存中…(img-jI4itccv-1661304119418)]

#pod 与存储都会以名称+序号,顺序创建排列
kubectl get pvc 

[外链图片转存中…(img-C0ZWlQSC-1661304119418)]

  • 测试mysql-headless是否可以正常使用(headless服务的域名格式为:“主机名.headless service名.namespace名.svc.cluster.local”)

[外链图片转存中…(img-hIuHwUlQ-1661304119418)]

  • 使用 nfs-storageclass创建 PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs-pvc01
  annotations:
    volume.beta.kubernetes.io/storage-class: "nfs-storageclass"
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
7.4.PV与PVC
7.4.1.PV(Persistent Volume)
  • 是持久化卷的意思,是对底层的共享存储的一种抽象。它和底层具体的共享存储技术有关,并通过插件完成和共享存储的对接。(它没有namespace区别的划分)
pv的关键配置参数说明:
存储类型:底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置有所不同。
存储能力(capacity):目前只支持存储空间的设置(storage=1Gi),不过未来可能会加入IOPS、吞吐量等指标的配置。
访问模式(accessModes):
  用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
    ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载。
    ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。
    ReadWriteMany(RWX):读写权限,可以被多个节点挂载。
  需要注意的是,底层不同的存储类型可能支持的访问模式不同。
回收策略( persistentVolumeReclaimPolicy):
  当PV不再被使用之后,对其的处理方式,目前支持三种策略:
    Retain(保留):保留数据,需要管理员手动清理数据。
    Recycle(回收):清除PV中的数据,效果相当于rm -rf /volume/*。
    Delete(删除):和PV相连的后端存储完成volume的删除操作,常见于云服务器厂商的存储服务。
  需要注意的是,底层不同的存储类型可能支持的回收策略不同。
存储类别(storageClassName):PV可以通过storageClassName参数指定一个存储类别。
  具有特定类型的PV只能和请求了该类别的PVC进行绑定。
  未设定类别的PV只能和不请求任何类别的PVC进行绑定。
状态(status):一个PV的生命周期,可能会处于4种不同的阶段。
  Available(可用):表示可用状态,还未被任何PVC绑定。
  Bound(已绑定):表示PV已经被PVC绑定。
  Released(已释放):表示PVC被删除,但是资源还没有被集群重新释放。
  Failed(失败):表示该PV的自动回收失败。
  • 创建pv-nfs.yaml,内容如下(使用的之前创建的NFS服务):
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  nfs: # 存储类型吗,和底层正则的存储对应
    path: /opt/data/pv1
    server: 172.16.8.217
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 1Gi
  accessModes: # 访问模式
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain # 回收策略

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存储类型吗,和底层正则的存储对应
    path: /opt/data/pv2   ## 共享文件路径
    server: 172.16.8.217
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 2Gi
  accessModes: # 访问模式
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain # 回收策略
  
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  nfs: # 存储类型吗,和底层正则的存储对应
    path: /opt/data/pv3
    server: 172.16.8.217
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 3Gi
  accessModes: # 访问模式
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain # 回收策略
  • 查看PV
kubectl get  pv

[外链图片转存中…(img-FqPnHqie-1661304119419)]

7.4.2.PVC(Persistent Volume Claim)
  • 是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息,下面是PVC的资源清单文件:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
  namespace: dev
spec:
  accessModes:  #访问模式
    - ReadWriteMany 
  selector: # 采用标签对PV选择
  storageClassName: # 存储类别
  resources: # 请求空间
    requests:
      storage: 5Gi

PVC的关键配置参数说明:

  • 访客模式(accessModes):用于描述用户应用对存储资源的访问权限。

  • 用于描述用户应用对存储资源的访问权限:

    • 选择条件(selector):通过Label Selector的设置,可使PVC对于系统中已存在的PV进行筛选。
    • 存储类别(storageClassName):PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出。
    • 资源请求(resources):描述对存储资源的请求。
  • 创建pvc-test.yaml,内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
  namespace: dev
spec:
  accessModes: # 访客模式
    - ReadWriteMany
  resources: # 请求空间
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
  namespace: dev
spec:
  accessModes: # 访客模式
    - ReadWriteMany
  resources: # 请求空间
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
  namespace: dev
spec:
  accessModes: # 访客模式
    - ReadWriteMany
  resources: # 请求空间
    requests:
      storage: 5Gi
  • 查看PVC
kubectl get  pvc -n dev -o wide
#pvc3未绑定,是因为没有满足需求的pv

[外链图片转存中…(img-lGv2OYK1-1661304119419)]

  • 查看PV
kubectl get pv

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fv3WVtZ9-1661304119419)(https://oss.linuxtxc.com/images/image-20220404131629691.png)]

7.4.3.创建pod使用PVC
  • 创建pvc-pod.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod1
  namespace: dev
spec:
  containers:
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
    volumeMounts:
    - name: volume
      mountPath: /root/
  volumes:
    - name: volume
      persistentVolumeClaim:
        claimName: pvc1
        readOnly: false

---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
  namespace: dev
spec:
  containers:
    - name: busybox
      image: busybox:1.30
      command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
      volumeMounts:
        - name: volume
          mountPath: /root/
  volumes:
    - name: volume
      persistentVolumeClaim:
        claimName: pvc2
        readOnly: false
  • 查看NFS服务器上共享的的目录

[外链图片转存中…(img-K56xEOMj-1661304119419)]

  • 资源释放:

    • 用户删除PVC来释放PV。
    • 当存储资源使用完毕后,用户可以删除PVC,和该PVC绑定的PV将会标记为“已释放”,但是还不能立刻和其他的PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能再次使用。
  • 资源回收:

    • kubernetes根据PV设置的回收策略进行资源的回收。
    • 对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。

创建PVC后一直绑定不了PV的原因

  • ①PVC的空间申请大小比PV的空间要大。

  • ②PVC的storageClassName和PV的storageClassName不一致。

  • ③PVC的accessModes和PV的accessModes不一致。

7.4.4.使用glusterfs 创建 pv,并挂载使用
#glusterfs分布式存储部署
wget https://oss.linuxtxc.com/%E7%AC%94%E8%AE%B0/%E5%88%86%E5%B8%83%E5%BC%8F%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9Fglusterfs%E5%AE%89%E8%A3%85%E6%96%87%E6%A1%A3.md

#kubernetes 所有节点都安装 glusterfs客户端
yum install -y glusterfs glusterfs-fuse
  • 创建glusterfs的endpoints
apiVersion: v1
kind: Namespace
metadata:
  name: app

---
apiVersion: v1
kind: Endpoints
metadata:
  name: glusterfs-cluster
  namespace: app
subsets:
- addresses:
  - ip: 172.16.8.212
  - ip: 172.16.8.213
  - ip: 172.16.8.214
  ports:
  - port: 49152
    protocol: TCP
  • 创建pv使用glusterfs
apiVersion: v1
kind: PersistentVolume
metadata:
  name: glusterfs-pv01
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  glusterfs:
    endpoints: glusterfs-cluster
    path: app-data  #创建的存储卷
    readOnly: false

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: glusterfs-pv02
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteMany
  glusterfs:
    endpoints: glusterfs-cluster
    path: app-data  #创建的存储卷
    readOnly: false
  • 创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: glusterfs-pvc01
  namespace: app
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
  • 创建使用Glusterfs卷的应用
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: app
spec:
  replicas: 2
  selector:
    matchLabels:
      name: nginx
  template:
    metadata:
      labels:
        name: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginxglusterfs
              mountPath: "/usr/share/nginx/html"
      volumes:
      - name: nginxglusterfs
        persistentVolumeClaim:
          claimName: glusterfs-pvc
  • 查看容器是否成功挂载
kubectl  exec -it  deployment-glu-pv-9fb5fb64-kl48h -n app  mount  | grep app-data

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPkwVWu8-1661304119420)(https://oss.linuxtxc.com/images/image-20220409100607509.png)]

7.5.configmap
  • ConfigMap是一种API对象,用来将非加密数据保存到键值对中。可以用作环境变量、命令行参数或者存储卷中的配置文件。
  • ConfigMap可以将环境变量配置信息和容器镜像解耦,便于应用配置的修改。如果需要存储加密信息时可以使用Secret对象。
7.5.1.使用命令行创建
(1)使用目录创建
kubectl create configmap configmap名 --from-file=/path (--from-file,指定在目录下的所有文件都会被用在ConfigMap里面创建一个键值对,键的名字就是文件名,值就是文件的内容)

(2)使用文件创建
kubectl create configmap configmap名 --from-file=./file1 --from-file=./file2 (--from-file,可以使用多次,同样键为文件名,值为文件中的内容)

(3)通过key-value字符串创建
kubectl create configmap configmap名 --from-literal=key1=123 --from-literal=key2=234

(4)通过env文件创建
kubectl create configmap configmap名 --from-env-file=env.txt 
其中,env.txt的文件格式为:
key1=***
key2=***
  • 使用ConfigMap有以下几个限制条件:
    • ConfigMap必须在pod之前创建

    • configmap受namespace的限制,只能相同namespace的pod才可以引用

7.5.2.通过Volume 方式挂载
  • 将ConfigMap中的内容挂载为容器内部的文件或目录

  • 创建 configmap-nginx.yaml,内容如下:

apiVersion: v1
kind: ConfigMap
metadata: 
  name: configmap-nginx
data:
  user: admin    
  passwd: aa123456
  stream.conf: |   
    stream.conf {
      server {
        listen 1550;
        proxy_pass  192.168.3.125:16303;
    }
    
     }
 #data为3,表示有三个可调用数据
 kubectl get cm   

[外链图片转存中…(img-89ZchsWP-1661304119420)]

  • 将configmap中的配置通过volume挂载
apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    env: 
    - name: user
      valueFrom:
        configMapKeyRef:
          name: configmap-nginx
          key: user
    - name: passwd 
      valueFrom:
        configMapKeyRef:
          name: configmap-nginx
          key: passwd
    volumeMounts: 
    - name: config              #要挂载的volumes名
      mountPath: /etc/nginx/vhosts   #没有则会自动创建,目录下有文件则会覆盖掉
      readOnly: true
  volumes:
  - name: config
    configMap:
      name: configmap-nginx    #指的是要从哪个configmap里读取数据
      items:
      - key: stream.conf 
        path: stream.conf  #挂载到容器后的文件名
kubectl exec -it configmap-pod  /bin/sh

[外链图片转存中…(img-1fL3paKe-1661304119420)]

7.5.3.通过SubPath 使原目录文件不被覆盖
  • 创建 configmap-php.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata: 
  name: configmap-php
  namespace: web
data:
  index.php: | 
    <?php
     echo phpversion();
    ?>
kubectl get cm -n web

[外链图片转存中…(img-stXbYuOE-1661304119421)]

  • 创建 volume-configmap-php.yaml,内容如下
apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap-php
  namespace: web
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    volumeMounts: 
    - name: config
      mountPath: /usr/share/nginx/html/index.php   
      subPath: index.php   #与configmap中的key相同
  volumes:
  - name: config
    configMap:
      name: configmap-php    #指的是要从哪个configmap里读取数据
 kubectl exec -it pod-configmap-php -n web /bin/sh

[外链图片转存中…(img-q5aQUa7s-1661304119421)]

  • 只覆盖指定的文件
apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap
  namespace: app
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      command: [ "/bin/sh", "-c", "sleep 3600" ]
      volumeMounts:
      - name: config
        mountPath: /etc/nginx/nginx.conf
        subPath: nginx.conf #subPath:要覆盖文件的相对路径
  volumes:
    - name: config
      configMap:
        name: configmap-nginx    #configMap的名称
        items:        #用于只挂载configmap中特定的key
         - key: nginx.conf   #ConfigMap中key的名称
           path: nginx.conf # 此处的path相当于 mv nginx.conf nginx.conf
7.6.Secret
  • 跟configMap非常类似的对象,它主要是用于存储敏感信息,例如密码,密钥,证书等等。

  • Secret有三种类型:

    • Opaque
    • kubernetes.io/dockerconfigjson
    • ServiceAccount
7.6.1.Opaque
  • base64 编码格式的 Secret,用来存储密码、密钥等;但数据也可以通过base64 –decode解码得到原始数据,所有加密性很弱。
#使用命令行 创建secret
 (1)kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
 (2)kubectl create secret generic db-user-pass --from-literal=username=admin --from-literal=password=1f2d1e2e67df
 
#手动创建base64加密 
echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64

#解密
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
  • 创建 secret-opaque.yaml,内容如下:
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
  namespace: dev
type: Opaque
data:
  username: YWRtaW4=    #修改为base64加密后的
  password: MWYyZDFlMmU2N2Rm
 kubectl get secret mysecret -n dev -o yaml

[外链图片转存中…(img-UasVF9Hi-1661304119421)]

  • 也可以将一个非 base64 编码的字符串直接放入 Secret 中, 当创建或更新该 Secret 时,此字段将被编码。
apiVersion: v1
kind: Secret
metadata:
  name: web-secret
  namespace: web
type: Opaque
stringData:
  config.yaml: |
    apiUrl: "https://my.api.com/api/v1"
    username: admin
    password: 123456 
  • 创建pod-secret.yaml,将secert 挂载到 pod中,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      volumeMounts:
        - name: config
          mountPath: /secret/config
          readOnly: true
  volumes:
    - name: config
      secret:
        secretName: mysecret
kubectl exec -it pod-secret -n dev /bin/sh

[外链图片转存中…(img-QGSv2UFJ-1661304119422)]

  • 只挂载Secret中特定的key
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret-01
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    volumeMounts:
    - name: secret
      mountPath: /opt
      readOnly: true
  volumes:
  - name: secret
    secret:
      secretName: mysecret
      items:
      - key: username
        path: cert/username    #username 将存储在/opt/cert/username 下
kubectl exec -it pod-secret-01 -n dev /bin/sh

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dBfCRTTG-1661304119422)(https://oss.linuxtxc.com/images/image-20220406155616789.png)]

  • 将Secret设置为环境变量
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: redis
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
7.6.2.kubernetes.io/dockerconfigjson
  • 用来存储私有docker registry的认证信息。
kubectl create secret docker-registry harbor-key-secret --docker-server=harbor.linuxtxc.com:9090 --docker-username=admin --docker-password=Harbor123
  • 在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey
apiVersion: v1
kind: Pod
metadata:
  name: java-demo
spec:
  containers:
    - name: tomcat
      image: harbor.linuxtxc.com:9090/txcrepo/tomcat-demo:v1
  imagePullSecrets:
    - name: harbor-key-secret
7.6.3.ServiceAccount
  • 用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中。
  • 每个namespace下有一个名为default的默认的ServiceAccount对象,这个ServiceAccount里有一个名为Tokens的可以作为Volume一样被Mount到Pod里的Secret,当Pod启动时这个Secret会被自动Mount到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权过程。
#创建一个sa 名称为admin
kubectl create  serviceaccount admin -n dev
  • 如果一个Pod在定义时没有指定spec.serviceAccountName属性,则会默认赋值为default,可进行如下指定
apiVersion: v1
kind: Pod
metadata:
  name: sa-demo
  namespace: dev
  labels:
    app: myapp
    release: canary
spec:
  containers:
  - name: myapp
    image: nginx:1.17.2
    ports:
    - name: nginx-port
      containerPort: 80
  serviceAccountName: admin  #此处指令为指定sa的名称
7.7.GlusterFS+Heketi实现volume动态扩容
  • SC:是StorageClass的缩写,表示存储类;这种资源主要用来对pv资源的自动供给提供接口;所谓自动供给是指用户无需手动创建pv,而是在创建pvc时对应pv会由persistentVolume-controller自动创建并完成pv和pvc的绑定;使用sc资源的前提是对应后端存储必须支持restfull类型接口的管理接口,并且pvc必须指定对应存储类名称来引用SC;简单讲SC资源就是用来为后端存储提供自动创建pv并关联对应pvc的接口;

  • Heketi是一个提供RESTful API管理GlusterFS卷的框架,便于管理员对GlusterFS进行操作:

#yum下载
#wget https://mirrors.aliyun.com/centos/7/storage/x86_64/gluster-9/Packages/h/heketi-9.0.0-1.el7.x86_64.rpm
wget  https://oss.linuxtxc.com/deploy/rpm/heketi-9.0.0-1.el7.x86_64.rpm

#wget https://mirrors.aliyun.com/centos/7/storage/x86_64/gluster-9/Packages/h/heketi-client-9.0.0-1.el7.x86_64.rpm
wget https://oss.linuxtxc.com/deploy/rpm/heketi-client-9.0.0-1.el7.x86_64.rpm
7.7.1.安装 heketi
#安装rpm包,我只在master上安装
yum -y install heketi-9.0.0-1.el7.x86_64.rpm
yum -y install heketi-client-9.0.0-1.el7.x86_64.rpm
  • 修改service文件
vim /usr/lib/systemd/system/heketi.service
......................
 [Unit]
Description=Heketi Server

[Service]
Type=simple
WorkingDirectory=/var/lib/heketi
EnvironmentFile=/etc/heketi/heketi.json
User=root
ExecStart=/usr/bin/heketi --config=/etc/heketi/heketi.json
Restart=on-failure
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target
  • 修改heketi配置文件
vim /etc/heketi/heketi.json
.............................
#修改端口,防止端口冲突
  "port": "18080",
......
#允许认证
  "use_auth": true,
......
#admin用户的key改为adminkey
      "key": "adminkey"
#普通用户的key改为userkey  
      "key": "userkey"
......
#修改执行插件为ssh,并配置ssh的所需证书,注意要能对集群中的机器免密ssh登陆,使用ssh-copy-id把pub key拷到每台glusterfs服务器上
    "executor": "ssh",
    "sshexec": {
      "keyfile": "/etc/heketi/heketi_key",
      "user": "root",
      "port": "22",
      "fstab": "/etc/fstab"
    },
......
# 定义heketi数据库文件位置
    "db": "/var/lib/heketi/heketi.db"
......
#调整日志输出级别
    "loglevel" : "warning"
  • 配置ssh密钥
[root@k8s-master heketi]# ssh-keygen -f /etc/heketi/heketi_key -t rsa -N ''

[root@k8s-master heketi]# ssh-copy-id -i /etc/heketi/heketi_key.pub 172.16.8.212
[root@k8s-master heketi]# ssh-copy-id -i /etc/heketi/heketi_key.pub 172.16.8.213
[root@k8s-master heketi]# ssh-copy-id -i /etc/heketi/heketi_key.pub 172.16.8.214
  • 验证登录
ssh -i /etc/heketi/heketi_key  root@172.16.8.212

[外链图片转存中…(img-B4qK5REz-1661304119422)]

  • 启动heketi
systemctl  start  heketi
systemctl enable  heketi 
  • 查看heketi服务状态
systemctl status heketi

[外链图片转存中…(img-vLHQBskO-1661304119423)]

7.7.2.heketi添加glusterfs
  • Heketi添加cluster
 heketi-cli --user admin --server http://172.16.8.124:18080 --secret adminkey --json  cluster create
.................................
{"id":"7a263332ed6bc432f7fa81e65e021542","nodes":[],"volumes":[],"block":true,"file":true,"blockvolumes":[]}

[外链图片转存中…(img-7IHWxH3j-1661304119423)]

  • 将所有glusterfs节点作为node添加到cluster
[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"   node add --cluster "7a263332ed6bc432f7fa81e65e021542"  --management-host-name  172.16.8.212  --storage-host-name 172.16.8.212 --zone 1

[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"   node add --cluster "7a263332ed6bc432f7fa81e65e021542"  --management-host-name  172.16.8.213  --storage-host-name 172.16.8.213 --zone 1

[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"   node add --cluster "7a263332ed6bc432f7fa81e65e021542"  --management-host-name  172.16.8.214  --storage-host-name 172.16.8.214 --zone 1

注:要保存每个glusterfs节点ID

[外链图片转存中…(img-iULtyomF-1661304119423)]

  • 添加device,volume是基于device创建的,目前heketi仅支持使用裸分区或裸磁盘(未格式化)添加为device,不支持文件系统,所以每个glusterfs节点上要有未格式化的磁盘
[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"    --json device add --name="/dev/sdb" --node "3aee8b6287717c18c72fbcc547e8bff4"

[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"    --json device add --name="/dev/sdb" --node "833b827e2d72c73a597c7d82a5ebdbc5"

[root@k8s-master heketi]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"    --json device add --name="/dev/sdb" --node "968afeb52b8924cac7ffac6db3963e03"

[外链图片转存中…(img-LQaeOtxE-1661304119423)]

  • 创建一个大小为2G,副本为2的volume
[root@k8s-master dev]# heketi-cli --server http://172.16.8.124:18080   --user "admin" --secret "adminkey"  volume create --size 2 --replica 2

#通过下面信息,可以看到Heketi创建了名为vol_15249e32eb410cabe764e4a06e254a7e的数据卷。

[外链图片转存中…(img-v15dVI0p-1661304119424)]

7.7.3.创建资源使用volume
  • 创建storageclass-glusterfs.yaml,内容如下:
apiVersion: v1
kind: Secret
metadata:
  name: heketi-secret
  namespace: app
data:
  # echo -n "mypassword" | base64 / base64 encoded "password"
  key: bXlwYXNzd29yZA==
type: kubernetes.io/glusterfs

---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: glusterfs
provisioner: kubernetes.io/glusterfs
allowVolumeExpansion: true
parameters:
  resturl: "http://172.16.8.124:18080"
  clusterid: "7a263332ed6bc432f7fa81e65e021542"
  restauthenabled: "true"
  restuser: "admin"
  #secretNamespace: "default"
  #secretName: "heketi-secret"
  restuserkey: "adminkey"
  gidMin: "40000"
  gidMax: "50000"
  volumetype: "replicate:2"
reclaimPolicy: Recycle    #默认为delete(删除),当前设置的策略为Recycle(保留)
kubectl get storageclass -n app

[外链图片转存中…(img-CmPbwmlM-1661304119424)]

  • 创建glusterfs-pvc.yaml内容如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: glusterfs-pvc
  namespace: app
  annotations:
    volume.beta.kubernetes.io/storage-class: "glusterfs"
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 2Gi     
 kubectl get pvc -n app
 #有绑定的volume才算创建成功,同时PV也会自动创建

[外链图片转存中…(img-KNM06uyc-1661304119424)]

  • 创建 mysql-deployment.yaml,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: app
spec:
  selector:
    matchLabels:
      name: mysql
  template:
    metadata:
      labels:
        name: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: root123456
        ports:
          - containerPort: 3306
        volumeMounts:
        - name: glusterfs-mysql-data
          mountPath: "/var/lib/mysql"
      volumes:
        - name: glusterfs-mysql-data
          persistentVolumeClaim:
            claimName: glusterfs-pvc
            readOnly: false
  • 查看volume挂载情况
kubectl exec -it mysql-5b94895868-n6xld -n app mount | grep 172

[外链图片转存中…(img-qO9jwwFP-1661304119425)]

  • 查看glusterfs集群状态
gluster  volume  status

[外链图片转存中…(img-1lCcq1bj-1661304119425)]

7.7.4.动态修改PVC
  • 查看当前PVC容量

[外链图片转存中…(img-zWZwGY84-1661304119425)]

#修改yaml配置
vim glusterfs-pvc.yaml

kubectl apply -f glusterfs-pvc.yaml
  • 再次查看 PVC
 kubectl get pvc -n app

[外链图片转存中…(img-ENIxeSl7-1661304119426)]

7.8.ceph+storageclass

7.8.1.rbd + storageclass
  • 实现动态存储 (可自动创建PV)

  • 在ceph集群中操作

#创建pool池
ceph osd pool create kube  32 32

#查看所有池
ceph osd lspools

#创建用户kubernetes,pool为刚创建的池
ceph auth get-or-create client.kube mon 'allow r' osd 'allow class-read object_prefix rbd_children, allow rwx pool=kube' -o /etc/ceph/ceph.client.kube.keyring

#删除用户
#ceph auth del client.kube

#获取用户的key,并使用base64转换,
ceph auth get-key client.admin | base64
ceph auth get-key client.kube | base64

#查看用户秘钥
#ceph auth get-key client.admin
#查询用户权限
#ceph auth get client.admin
#查看全部用户
#ceph auth list

#将文件传到k8s master节点上 
(创建pod时,kubelet需要使用rbd命令去检测和挂载pv对应的ceph image,所以要在所有的worker节点安装ceph客户端ceph-common。将ceph的ceph.client.admin.keyring和ceph.conf文件拷贝到master的/etc/ceph目录下,我是将ceph安装在k8s集群中的,所以省略这步操作)
scp -rp /etc/ceph/{ceph.client.kubernetes.keyring,ceph.client.admin.keyring,ceph.conf}  root@192.168.1.211:/etc/ceph/
  • gcr.io提供的kube-controller-manager容器镜像未打包ceph-common组件,缺少了rbd命令,因此无法通过rbd命令为pod创建rbd image,我们需要在kubernetes集群中安装rbd-provisioner
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: rbd-provisioner
rules:
  - 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"]
  - apiGroups: [""]
    resources: ["services"]
    resourceNames: ["kube-dns", "coredns"]
    verbs: ["list", "get"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
 
---
 
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: rbd-provisioner
subjects:
  - kind: ServiceAccount
    name: rbd-provisioner
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: rbd-provisioner
  apiGroup: rbac.authorization.k8s.io
 
---
 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: rbd-provisioner
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["endpoints"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
 
---
 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rbd-provisioner
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: rbd-provisioner
subjects:
- kind: ServiceAccount
  name: rbd-provisioner
  namespace: kube-system
 
---
 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rbd-provisioner
  namespace: kube-system
  labels:
    app: rbd-provisioner
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rbd-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: rbd-provisioner
    spec:
      nodeSelector:
        app: rbd-provisioner
      containers:
      - name: rbd-provisioner
        image: "quay.io/external_storage/rbd-provisioner:latest"
        volumeMounts:
        - name: ceph-conf
          mountPath: /etc/ceph
        env:
        - name: PROVISIONER_NAME
          value: ceph.com/rbd
      serviceAccount: rbd-provisioner
      volumes:
      - name: ceph-conf
        hostPath:
          path: /etc/ceph
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      - key: "node-role.kubernetes.io/control-plane"
        operator: "Exists"
        effect: "NoSchedule"
 
---
 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: rbd-provisioner
  namespace: kube-system
  • 查看rbd-provisioner pod启动情况

[外链图片转存中…(img-hdE9FcA7-1661304119426)]

  • 创建secret
#在k8s集群中创建secret(key为上面转换的获得的值)
kubectl create secret generic ceph-secret-admin --from-literal=key='QVFETkVLeGk4LzBLQUJBQXlmcGVhSzc2cCtPb1M5Und4WjBrU0E9PQ==' --type=kubernetes.io/rbd -n kube-system

kubectl create secret generic ceph-secret-kubernetes --from-literal=key='QVFBMnRxNWlNTEYzRWhBQWl4dHg0OVRIRkhKbUJ2QWQyb2FQWkE9PQ==' --type=kubernetes.io/rbd 

## 在k8s集群每个节点安装(下载ceph的源)
yum -y install ceph-common
  • 创建 yaml/ceph-storageclass.yaml,内容如下:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: ceph-storageclass-rbd
   annotations:
     storageclass.beta.kubernetes.io/is-default-class: "true"
provisioner: ceph.com/rbd
allowVolumeExpansion: true   #是否支持扩展
parameters:
    monitors: 192.168.1.211:6789,192.168.1.15:6789,192.168.1.49:6789
    adminId: admin
    adminSecretName: ceph-secret-admin
    adminSecretNamespace: "kube-system"
    pool: kube_pool
    userId: kubernetes
    userSecretName: ceph-secret-kubernetes
    #userSecretNamespace: "kube-system"
    fsType: ext4
    imageFormat: "2"
    imageFeatures: "layering"
reclaimPolicy: Retain  #默认为delete(删除),还有当前设置的策略为Retain(保留)
  • statefulset挂载storageclass
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-mysql
spec:
  replicas: 2
  selector:
    matchLabels:
      statefulset: mysql
  serviceName: mysql-headless
  template:
    metadata:
      labels:
        statefulset: mysql
    spec:
      containers:
      - name: mysql01
        image: mysql:8.0
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: root123456
        ports:
          - containerPort: 3306
        volumeMounts:
        - name: ceph-mysql-data
          mountPath: /var/lib/mysql
          subPath: mysql
  volumeClaimTemplates:
    - metadata:
        name: ceph-mysql-data
        annotations:
          volume.beta.kubernetes.io/storage-class: "ceph-storageclass-rbd"
          gcp-auto-backup: "yes"
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 5Gi
  • 使用ceph-storageclass-rbd创建 PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: ceph-pvc01
  annotations:
    volume.beta.kubernetes.io/storage-class: "ceph-storageclass-rbd"
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
7.8.2.cephfs + storageclass
  • 在ceph集群中操作
#创建pool
ceph osd pool create cephfs_data 32
ceph osd pool create cephfs_metadata 32

#创建cephfs存储
ceph fs new cephfs cephfs_metadata  cephfs_data

##删除cephfs
#ceph fs rm cephfs  --yes-i-really-mean-it
#删除pool
#ceph osd pool delete cephfs-data cephfs-data --yes-i-really-really-mean-it
#ceph osd pool delete cephfs-data cephfs-data --yes-i-really-really-mean-it

#查看admin用户的key
ceph auth get-key client.admin
  • 在k8s集群中创建secret
kubectl create secret generic ceph-admin --type="kubernetes.io/rbd" --from-literal=key=AQDNEKxi8/0KABAAyfpeaK76p+OoS9RwxZ0kSA==  --namespace=kube-system
  • 创建cephfs-storageclass.yaml,内容如下:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: cephfs-storageclass
  annotations:
     storageclass.beta.kubernetes.io/is-default-class: "true"
provisioner: ceph.com/cephfs
parameters:
  monitors: 192.168.1.211:6789,192.168.1.15:6789,192.168.1.49:6789
  adminId: admin
  adminSecretName: cephfs-secret-admin
  adminSecretNamespace: kube-system
  claimRoot: /volumes/kubernetes
reclaimPolicy: Retain  #默认为delete(删除),还有当前设置的策略为Retain(保留)
  • 创建cephfs-pvc01,内容如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: cephfs-pvc01
spec:
  accessModes:     
  - ReadWriteOnce
  storageClassName: cephfs-storageclass
  resources:
    requests:
      storage: 1Gi

8.k8s的安全认证
8.1.概念描述

● API Server是访问和管理资源对象的唯一入口。任何一个请求访问API Server,都要经过下面的三个流程:
○ ① Authentication(认证):身份鉴别,只有正确的账号才能通过认证。
○ ② Authorization(授权):判断用户是否有权限对访问的资源执行特定的动作。
○ ③ Admission Control(注入控制):用于补充授权机制以实现更加精细的访问控制功能。

UserAccount:用于限制用户账号对 kubernetes集群资源访问的权限
ServiceAccount:用于为Pod的服务进程在访问kubernetes时提供身份标识。

API Server目前支持的几种授权策略
● AlwaysDeny:表示拒绝所有请求,一般用于测试。
● AlwaysAllow:允许接收所有的请求,相当于集群不需要授权流程(kubernetes默认的策略)。
● ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制。
● Webhook:通过调用外部REST服务对用户进行授权。
● Node:是一种专用模式,用于对kubelet发出的请求进行访问控制。
● RBAC:基于角色的访问控制(kubeadm安装方式下的默认选项)。

● RBAC(Role Based Access Control) : 基于角色的访问控制,主要是在描述一件事情:给哪些对象授权了哪些权限。
● RBAC涉及到了下面几个概念:
○ 对象:User、Groups、ServiceAccount。
○ 角色:代表着一组定义在资源上的可操作的动作(权限)的集合。
○ 绑定:将定义好的角色和用户绑定在一起。

● RBAC引入了4个顶级资源对象:
○ Role、ClusterRole:角色,用于指定一组权限。
○ RoleBinding、ClusterRoleBinding:角色绑定,用于将角色(权限的集合)赋予给对象。

8.2.Role、ClusterRole
  • Role的资源清单文件:
# Role只能对命名空间的资源进行授权,需要指定namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: authorization-role
  namespace: dev
rules:
  - apiGroups: ["","apps"]       # 支持的API组列表,""空字符串,表示核心API群
    resources: ["pods",]      # 支持的资源对象列表
    verbs: ["get","watch","list"]
  • ClusterRole的资源清单文件:
# ClusterRole可以授予整个集群范围内资源访问权限、与非资源类型进行授权
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: authorization-clusterrole
rules:
  - apiGroups: ["","apps"]      # 支持的API组列表,""空字符串,表示核心API群
    resources: ["pods","deployment"]       # 支持的资源对象列表
    verbs: ["get","watch","list"]
rules中的参数说明:
apiGroups:
  #支持的API组列表。
  "","apps","autoscaling","batch"。""表示核心组,如pods,namespace、pod、pv、pvc等
resources:
  #支持的资源对象列表。 "services","endpoints","pods","secrets","configmaps","crontabs","deployments","jobs","nodes","rolebindings","clusterroles","daemonsets","replicasets","statefulsets","horizontalpodautoscalers","replicationcontrollers","cronjobs"。
verbs:
  #对资源对象的操作方法列表。
  "get", "list", "watch", "create", "update", "patch", "delete", "exec"。
  • 查看资源的组
kubectl  api-resources | grep deployment
#下图中的app 便为资源的组

[外链图片转存中…(img-Rbam8ZoT-1661304119426)]

8.3.RoleBinding、ClusterRoleBinding
  • 角色绑定用来把一个角色绑定到一个目标对象上,绑定目标可以是User、Group或者ServiceAccount。

  • RoleBinding的资源清单文件:

# RoleBinding可以将同一namespace中的subject对象绑定到某个Role下,则此Subject具有该Role定义的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: authorization-role-binding
  namespace: dev
subjects:
  - kind: User
    name: test
    apiGroup: rbac.authorization.k8s.io  
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: authorization-role
  • ClusterRoleBinding的资源清单文件:
# ClusterRoleBinding在整个集群级别和所有namespaces将特定的subject与ClusterRole绑定,授予权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: authorization-clusterrole-binding
subjects:
  - kind: User
    name: test
    apiGroup: rbac.authorization.k8s.io
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: authorization-clusterrole
  • RoleBinding引用ClusterRole进行授权
    • RoleBinding可以引用ClusterRole,对属于同一命名空间内ClusterRole定义的资源主体进行授权。
    • 一种很常用的做法是,集群管理员为集群范围预定义好一组规则(ClusterRole),然后在多个命名空间中重复使用这些ClusterRole。这样可以大幅度提高授权管理工作效率,也使得各个命名空间下的基础性授权规则和使用体验保持一致。
# 虽然authorization-clusterrole是一个集群角色,但是因为使用了RoleBinding
# 所以test用户只能读取dev命名空间中的资源
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: authorization-clusterrole-binding
  namespace: dev
subjects:
  - kind: User
    name: test
    apiGroup: rbac.authorization.k8s.io
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: authorization-clusterrole
8.4.引用集群默认的clusterole
kubectl get clusterrole  | grep admin
#admin 具有单个namespace的所有权限
#cluster-admin 具有对整个k8s集群的操作权限

#检查serviceaccount 是否绑定权限成功
 kubectl auth can-i --as=system:serviceaccount:app-team1:cicd-token get pods -n app-team1

[外链图片转存中…(img-4POijAiM-1661304119427)]

8.5.RBAC案例
  • 创建一个只能管理dev命名空间下Pods资源的账号。
#创建用户的私钥
cd /etc/kubernetes/pki
umask 077;openssl genrsa -out devuser.key 2048

#创建证书签署请求(O:组名 CN:用户名)
 openssl req -new -key devuser.key -out devuser.csr -subj "/CN=devuser/O=devgroup"

#签署证书(我们使用的是kubeadm安装的集群,CA相关证书位于/etc/kubernetes/pki/ 目录下
openssl x509 -req -in devuser.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out devuser.crt -days 3650

#设置集群、用户、上下文信息(不设置--kubeconfig,如--kubeconfig=devuser.config,则会默认放在家目录下的./kube 中)
kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/devuser.crt  --server=https://172.16.8.124:6443 --kubeconfig=devuser.config

kubectl config set-credentials devuser --embed-certs=true --client-certificate=devuser.crt --client-key=devuser.key  --kubeconfig=devuser.config

kubectl config set-context devuser@kubernetes --cluster=kubernetes --user=devuser --kubeconfig=devuser.config

#切换账号到devuser
kubectl config use-context devuser@kubernetes 
#查看(当前还没有权限执行当前操作,还需要对用户授权)
kubectl get pods -n dev

[外链图片转存中…(img-6faEbMJa-1661304119427)]

  • 如果要使用linux的普通用户管理集群
[root@k8s_master ~]# useradd devuser
[root@k8s_master ~]# mkdir /home/devuser/.kube
[root@k8s_master ~]# cp .kube/config /home/devuser/.kube/
[root@k8s_master ~]# chown  -R devuser:devuser  /home/devuser/
  • 查看管理账号,并切换
#输出合并后的kubeconfig的内容,格式为 YAML,密文内容不会显示(--kubeconfig,可以指定只显示某个账户的kubeconfig内容,不指定则显示.kube/config中的账号)
kubectl  config view --kubeconfig=devuser.config

#切回admin账户
kubectl config use-context kubernetes-admin@kubernetes
  • 创建Role和RoleBinding,为devuser授权

    • 创建dev-role.yaml文件,内容如下:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: dev-role
  namespace: dev
rules:
  - apiGroups: ["","apps"] # 支持的API组列表,""空字符串,表示核心API群,如nodes,pod,service等,可通过kubectl api-resources 查看
    resources: ["pods","deployment"] # 支持的资源对象列表
    verbs: ["get","watch","list"]

---

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: authorization-role-binding 
  namespace: dev
subjects:
  - kind: User
    name: devuser
    apiGroup: rbac.authorization.k8s.io 
roleRef:
  kind: Role 
  name: dev-role
  apiGroup: rbac.authorization.k8s.io
  • 也可以直接引用k8s集群自带的 ClusterRole
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: authorization-role-binding 
  namespace: dev
subjects:
  - kind: User
    name: devuser
    apiGroup: rbac.authorization.k8s.io 
roleRef:
  kind: ClusterRole 
  name: admin
  apiGroup: rbac.authorization.k8s.io
#切换成 devuser,再次查看
kubectl config use-context devuser@kubernetes  --kubeconfig=devuser.config

kubectl get pods -n dev

#上面这是将kubeconfig信息放在 ~/.kube/config中的情况下,也可以指定文件进行操作,如(将文件传到k8s节点依然可以使用下面方式操作):
kubectl get pods -n dev --kubeconfig=devuser.config

[外链图片转存中…(img-cKFeXW0L-1661304119427)]

8.6.准入控制
  • 通过了前面的认证和授权之后,还需要经过准入控制通过之后,API Server才会处理这个请求。

  • 准入控制是一个可配置的控制器列表,可以通过在API Server上通过命令行设置选择执行哪些注入控制器。

vim /etc/kubernetes/manifests/kube-apiserver.yaml
..............
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds

[外链图片转存中…(img-xHJvbaT8-1661304119427)]

  • 只有当所有的注入控制器都检查通过之后,API Server才会执行该请求,否则返回拒绝。

  • 当前可配置的Admission Control(准入控制)
    ○ AlwaysAdmit:允许所有请求。

    ○ AlwaysDeny:禁止所有请求,一般用于测试。

    ○ AlwaysPullImages:在启动容器之前总去下载镜像。

    ○ DenyExecOnPrivileged:它会拦截所有想在Privileged Container上执行命令的请求。

    ○ ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。

    ○ Service Account:实现ServiceAccount实现了自动化。

    ○ SecurityContextDeny:这个插件将使用SecurityContext的Pod中的定义全部失效。

    ○ ResourceQuota:用于资源配额管理目的,观察所有请求,确保在namespace上的配额不会超标。

    ○ LimitRanger:用于资源限制管理,作用于namespace上,确保对Pod进行资源限制。

    ○ InitialResources:为未设置资源请求与限制的Pod,根据其镜像的历史资源的使用情况进行设置。

    ○ NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒 绝。当删除一个namespace时,系统将会删除该namespace中所有对象。

    ○ DefaultStorageClass:为了实现共享存储的动态供应,为未指定StorageClass或PV的PVC尝试匹配默认StorageClass,尽可能减少用户在申请PVC时所需了解的后端存储细节。

    ○ DefaultTolerationSeconds:这个插件为那些没有设置forgiveness tolerations并具有notready:NoExecute和unreachable:NoExecute两种taints的Pod设置默认的“容忍”时间,为5min。

    ○ PodSecurityPolicy:这个插件用于在创建或修改Pod时决定是否根据Pod的security context和可用的 PodSecurityPolicy对Pod的安全策略进行控制


9.k8s+prometheus+grafana实现集群监控
  • Prometheus组件与架构
    • Prometheus Server:收集指标和存储时间序列数据,并提供查询接口
    • ClientLibrary:客户端库
    • Push Gateway:短期存储指标数据,主要用于临时性的任务
    • Exporters:采集已有的第三方服务监控指标并暴露metrics
    • Alertmanager:告警
    • Web UI:简单的web控制台
  • 监控插件
    • Node exporter —> 用于监控linux
    • Mysql server exporter —> 用于监控Mysql
    • cAdvisor —> 用于监控Docker
    • JMX exporter —>用于监控JAVA
#监控插件官方地址
https://prometheus.io/docs/instrumenting/exporters/

#下载yaml文件
wget https://oss.linuxtxc.com/k8s-prometheus-grafana.zip
#解压
unzip  k8s-prometheus-grafana.zip

#将storageclass修改成自己的
sed -i 's/managed-nfs-storage/nfs-storageclass/g'  ./prometheus/*.yaml
sed -i 's/managed-nfs-storage/nfs-storageclass/g'  ./grafana/*.yaml
sed -i 's/managed-nfs-storage/nfs-storageclass/g'  ./*.yaml

#创建命名空间
kubectl create ns ops

#创建资源
cd k8s-prometheus-grafana/
kubectl apply -f prometheus/
kubectl apply -f grafana/
kubectl apply -f kube-state-metrics.yaml


#使用docker安装grafana与Prometheus
#Prometheus
docker run -d --name=prometheus -p 9090:9090 prom/prometheus
#grafana
docker run -d --name=grafana -p 3000:3000 grafana/grafana
  • 访问prometheus(http://125.124.145.22:30090/targets)

[外链图片转存中…(img-0OZDp9mQ-1661304119428)]

  • 访问node-exporter(http://125.124.145.22:30090/metrics)

[外链图片转存中…(img-7gFOcnAv-1661304119428)]

  • 访问Grafana(http://125.124.145.22:30030/login),默认用户密码均为admin

[外链图片转存中…(img-d2G5YXc7-1661304119428)]

  • 添加数据源,以下使用的代理模式(推荐),仅限k8s内部访问

[外链图片转存中…(img-dBTX5Lt9-1661304119428)]

    • 也可以使用直接访问模式

[外链图片转存中…(img-qf06wyxW-1661304119429)]

  • 导入面板:Dashboards->Import(导入面板,可以直接输入模板编号315在线导入,node_exporter可以使用9276,或者下载好对应的json模板文件本地导入)
ls k8s-prometheus-grafana/dashboard
---
jnaK8S工作节点监控-2021.json  jnaK8S集群资源监控-2021.json  jnaK8S资源对象状态监控-2021.json

[外链图片转存中…(img-pDqF9d9C-1661304119429)]

  • 查看展示效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3PRGwMv-1661304119429)(https://oss.linuxtxc.com/images/image-20220315171537239.png)]

  • Prometheus基本使用:查询数据

    • 支持条件查询,操作符,并内建了大量内置函数,供我们针对监控数据的各种维度进行查询
  • 数据模型

    • Prometheus将所有数据存储为时间序列;
    • 具有相同度量名称以及标签属于同一指标;
    • 每个时间序列都由度量标准名称和一组键值对(称为标签)唯一标识;
    • 指标格式
      • {
  • 监控思路

监控指标具体实现举例
Pod性能cAdvisorCPU,内存,网络等
Node性能node-exporterCPU,内存,网络等
K8s资源对象kube-state-metricsPod,Deployment,Service等
  • kubelet内置监控指标接口:https://127.0.0.1:10250/metrics

  • kubelet内置的cadvisor指标接口:https://127.0.0.1:10250/metrics/cadvisor

  • 基于k8s服务发现

    • 不是所有的pod都会被监控,如果这个pod想要被监控想要声明让Prometheus监控
    metadata:
      annotations:
        prometheus.io/scrape: "true"
    
  • Prometheus告警

cd  k8s-prometheus-grafana/dashboard
vim alertmanager-configmap.yaml
  • 配置文件内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
  name: alertmanager-config
  namespace: ops
data:
  alertmanager.yml: |
    global:
      resolve_timeout: 5m
      smtp_smarthost: 'smtp.163.com:25'   #邮箱官网
      smtp_from: 'linuxtxc@163.com'    #发送告警的邮箱
      smtp_auth_username: 'linuxtxc@163.com'    
      smtp_auth_password: 'QCVUMVDBEIZTBBQW'   #邮箱安全码

    route:
      group_interval: 1m
      group_wait: 10s
      receiver: default-receiver
      repeat_interval: 1m

    receivers:
    - name: default-receiver
      email_configs:
      - to: "413848871@qq.com"         #接受邮箱
  • 告警规则
kubectl apply -f   k8s-prometheus-grafana/prometheus/prometheus-rules.yaml

#配置钉钉告警
- 创建钉钉群->群设置->智能群助手->添加机器人->自定义->添加
- 获取Webhook地址,填写到dingding-webhook-deployment.yaml,然后apply
- kubectl apply  -f alertmanager-dingding-configmap.yaml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPgfqdps-1661304119429)(https://oss.linuxtxc.com/images/image-20220718143041204.png)]


10.k8s集群管理平台部署
10.1.kuboard安装
#下载
#wget https://oss.linuxtxc.com/deploy/yaml/kuboard-v3.yaml
wget https://addons.kuboard.cn/kuboard/kuboard-v3.yaml

kubectl apply -f kuboard-v3.yaml

访问 Kuboard

  • 在浏览器中打开链接 http://172.16.8.124:30080
  • 输入初始用户名和密码,并登录
    • 用户名: admin
    • 密码: Kuboard123

[外链图片转存中…(img-fzdnBaDi-1661304119430)]

10.2.kubesphere安装
#wget https://github.com/kubesphere/ks-installer/releases/download/v3.2.1/kubesphere-installer.yaml
#wget https://github.com/kubesphere/ks-installer/releases/download/v3.2.1/cluster-configuration.yaml

wget https://oss.linuxtxc.com/deploy/yaml/kubesphere/kubesphere-installer.yaml

wget https://oss.linuxtxc.com/deploy/yaml/kubesphere/cluster-configuration.yaml

kubectl apply -f  kubesphere-installer.yaml
kubectl apply -f  cluster-configuration.yaml

#查看安装情况
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4d2F1KcP-1661304119430)(https://oss.linuxtxc.com/images/image-20220413150044896.png)]

  • 解决方法:
#yaml文件中的storageClass默认为空,需要在两处添加,我的storageClass为"glusterfs"
vim  cluster-configuration.yaml

[外链图片转存中…(img-e3zlXW5z-1661304119430)]

#再次查看安装情况(安装成功后会有如下提示)
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l app=ks-install -o jsonpath='{.items[0].metadata.name}') -f

[外链图片转存中…(img-8pQnMezw-1661304119431)]

  • 访问 curl:http://172.16.8.124:30880/

[外链图片转存中…(img-CxipnNlD-1661304119431)]

10.3.部署dashboard
wget https://oss.linuxtxc.com/deploy/yaml/kubernetes-dashboard.yaml
kubectl apply -f  kubernetes-dashboard.yaml

#查看Nodeport访问端口
kubectl get service -n kubernetes-dashboard

[外链图片转存中…(img-TLlqLHOw-1661304119432)]

  • https://nodeIP:30081

[外链图片转存中…(img-8q0IS3mW-1661304119432)]

#创建用户
kubectl create serviceaccount dashboard-admin -n kube-system

#用户授权
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin

#获取token
kubectl describe secrets -n  kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
  • 输入token后

[外链图片转存中…(img-67sZYud8-1661304119432)]

11.helm包管理
  • Helm是一个Kubernetes的包管理工具,就像Linux下的包管理器,如yum/apt-get等,可以方便的将之前打包好的yaml文件部署到kubernetes上

  • Helm有三个重要概念:

    • Helm:一个命令行客户端工具,主要是用于Kubernetes应用chart的创建,打包,发布和管理。
    • Chart:应用描述,一系列用于描述k8s资源相关文件的集合
    • Release:基于Chart的部署实体,一个chart被Helm运行后将会生成对应的release,并在k8s中创建出真实运行的资源对象
  • chart常用文件

    • charts/ (可选项)是一个目录,存放一些调用的charts
    • Chart.yaml (必需项)定义一些基础信息。例如作者、版本等
    • templates/ (必需项)是应用需要的yaml文件模板
    • requirements.yaml (可选项)同charts一样的。
    • values.yaml (必需项)默认配置值。例如定义的一些变量等。
1.1.helm安装
#GitHub下载地址
#https://github.com/helm/helm/releases
wget https://get.helm.sh/helm-v3.5.4-linux-amd64.tar.gz
tar -xf helm-v3.5.4-linux-amd64.tar.gz
mv linux-amd64/helm  /usr/local/bin/

#查看版本
helm version
1.2.helm常用命令
creat         创建一个chart并指定名字
package       将chart目录打包到chart存档文件中
dependency    管理chart 依赖
get    下载一个release,可用子命令:all,hooks,manifest,notes,values
history       获取release历史
install       安装一个chart
list          列出release
package       将chart目录打包到chart存档文件中
pull     从远程仓库下载chart并解压到本地 #helm pull stable/mysql --untar
repo     添加,列出,移除,更新和索引chart仓库,可用子命令:add,index,list,remove,update
rollback      从之前版本回滚
search        根据关键字搜索chart,可用子命令;hub,repo
show          查看chart详细信息,可用子命令:all,chart,readme,values
status        显示已命名版本的状态
template      本地呈现模板
uninstall     卸载一个release
upgrade       更新一个release
version       查看helm客户端版本
1.3.自建chart
#创建chart
helm create mychart

#会在当前目录下创建一个目录
cd /root/mychart
  • 修改模板文件,(可以将自带的模板文件删除)
cd  /root/mychart/templates

vim deployment.yaml (会引用values.yaml 文件中的值)
----------
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: {{ .Values.labels.app }}
  name: nginx-deployment
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Values.matchLabels.app }}
  template:
    metadata:
      labels:
        app: {{ .Values.labels.app }}
    spec:
      containers:
      - image: {{  .Values.containers.image }}
        name: alpine-nginx
        imagePullPolicy: {{ .Values.containers.imagePullPolicy }}
      volumes:
        - name: data
          nfs:
            server: 192.168.1.211 # NFS服务器地址
            path: {{ .Values.nfs.path }} # 共享文件路径
  • 修改values.yaml (自定义,注:文件不需要加 “-”)
nfs:
  server: 192.168.1.211 # NFS服务器地址
  path: /opt/data/nfs/nginx

volumeMounts:
  mountPath: /opt/apps/nginx/conf/vhosts

metadata:
  labels:
    app: web-deployment
  name: web-deployment

matchLabels:
  app: web-deployment

containers:
  image: alpine-nginx:1.19.6
  imagePullPolicy: IfNotPresent

labels:
  app: web-deployment

#service port
service:
  port: 80
  targetport: 80
  type: NodePort
  name: nginx-service

#service 标签选择器
selector:
  app: web-deployment
  • 使用自建的chart部署服务
helm install web-nginx /root/mychart 
  • 更改变量值
#--description,添加描述信息,有利于版本回滚
helm install web-nginx /root/mychart --set service.port=8080  --set service.targetport=80 --set labels.app=tomcat --description="nginx-1.17.3"

#--values,-f,使用yaml文件更改变量值(文件格式与values.yaml相同)
helm install web-nginx /root/mychart  -f service-values.yaml

#--dry-run,将配置打印出来
helm install web-nginx /root/mychart  -f service-values.yaml --dry-run

#版本更新
helm upgrade web-nginx /root/mychart -f  deploy-values.yaml

#查看历史版本
helm history web-nginx

#回滚到2版本
helm rollback web 2
1.4.helm安装harbor
#添加harbor的helm仓库
helm repo add harbor https://helm.goharbor.io

#下载最新的harbor包
helm pull harbor/harbor

kubectl create namespace harbor

#进入目录
mv harbor  /opt
cd /opt/harbor

#使用域名证书创建secret
kubectl create secret tls tls-harbor.linuxtxc.com --key=harbor.linuxtxc.com.key --cert=harbor.linuxtxc.com.pem -n harbor
  • 编辑values.yaml文件
expose:
  type: ingress
  tls:
    auto:
      commonName: ""
    secret:
      secretName: "tls-harbor.linuxtxc.com" #添加使用证书创建的secret
......
externalURL: https://harbor.linuxtxc.com   #添加自己的域名
......
persistence:
  enabled: true
  resourcePolicy: "keep"
  persistentVolumeClaim:
    registry:
      existingClaim: ""
      storageClass: "nfs-storageclass"    #添加自己的storageclass
      ......
    chartmuseum:
      existingClaim: ""
      storageClass: "nfs-storageclass"   #添加自己的storageclass
      ......
    jobservice:
      existingClaim: "" 
      storageClass: "nfs-storageclass"  #添加自己的storageclass
      ......
    database:
      existingClaim: ""
      storageClass: "nfs-storageclass"  #添加自己的storageclass
      ......
    redis:
      existingClaim: ""
      storageClass: "nfs-storageclass"  #添加自己的storageclass
      ......
    trivy:
      existingClaim: ""
      storageClass: "nfs-storageclass"   #添加自己的storageclass
  • 查看资源创建情况(创建会需要几分钟时间)

[外链图片转存中…(img-8fVGmx2o-1661304119433)]

  • 安装harbor
 helm install harbor harbor/harbor -f values.yaml -n harbor
  • 添加harbor为可信任仓库
vim /etc/docker/daemon.json 
......
"insecure-registries": ["harbor.linuxtxc.com:93"]

##重启docker
systemctl restart docker
12. Jenkins+gitlab实现CICD

12.1.安装gitlab
mkdir gitlab
cd gitlab
docker run -d \
  --name gitlab \
  -p 8043:443 \
  -p 99:80 \
  -p 2222:22 \
  -v $PWD/config:/etc/gitlab \
  -v $PWD/logs:/var/log/gitlab \
  -v $PWD/data:/var/opt/gitlab \
  -v /etc/localtime:/etc/localtime \
  --restart=always \
  lizhenliang/gitlab-ce-zh:latest
  • 初次会先设置管理员密码 ,然后登陆,默认管理员用户名root,密码就是刚设置的。
12.2.安装Jenkins
  • 创建Jenkins.yaml ,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      name: jenkins 
  template:
    metadata:
      name: jenkins
      labels:
        name: jenkins
    spec:
      serviceAccountName: jenkins
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts
          ports:
            - containerPort: 8080
            - containerPort: 50000
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
      securityContext:
        fsGroup: 1000
      volumes:
      - name: jenkins-home
        persistentVolumeClaim:
          claimName: jenkins
          
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins
spec:
  storageClassName: "nfs-storageclass"
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

---
apiVersion: v1
kind: Service
metadata:
  name: jenkins
spec:
  selector:
    name: jenkins
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 8080
      protocol: TCP
      nodePort: 30008
    - name: agent
      port: 50000
      protocol: TCP
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: jenkins
rules:
- apiGroups: [""]
  resources: ["pods","events"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get","list","watch"]
- apiGroups: [""]
  resources: ["secrets","events"]
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: jenkins
subjects:
- kind: ServiceAccount
  name: jenkins
  • 查看/var/jenkins_home/secrets/initialAdminPassword目录下的凭证
kubectl logs jenkins-fd79f7c9d-b85nj

[外链图片转存中…(img-hGFLgd4q-1661304119433)]

  • 修改插件源
#进入持久化存储目录
cd  /opt/data/nfs/default-jenkins-pvc-812aa60a-6244-4ad8-bbf0-42894eaa2200/updates

#修改插件源
sed -i 's/http:\/\/updates.jenkins-ci.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json && \
sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json

#重启Jenkins
kubectl  delete pod  jenkins-fd79f7c9d-b85nj
  • 安装插件,管理Jenkins->系统配置 ->管理插件 -> 分别搜索Git Parameter / Git / Pipeline / kubernetes / Config File Provider

[外链图片转存中…(img-dWn40iVD-1661304119434)]

12.3.jenkins 主从架构
12.3.1.jenkins slave 节点配置
  • 需要安装kubernetes插件(上面已安装)
  • 当触发Jenkins任务时,Jenkins会调用kubernetes API创建 Slave Pod,Pod启动后会连接Jenkins,接受任务并处理
  • 点击节点管理—>Configure Clouds -> Add a new cloud -> Kubernetes地址处填写 https://kubernetes.default.svc.cluster.local (kubernetes网络地址)-> jenkins地址( http://jenkins.default.svc.cluster.local)

[外链图片转存中…(img-bbZ75dAO-1661304119434)]

[外链图片转存中…(img-XTglM4R6-1661304119434)]

12.3.2.Jenkins Pipeline
  • Jenkins Pipeline 语法

    • Stages:阶段,它是Pipeline中主要的组成部分,Jenkins将会按照Stages中的描述的顺序从上往下的执行,一个Pipeline可以划分为若干个Stage,每个Stage代表一组操作,比如Build,Test,Deploy
    • Steps:步骤,Steps是最基本的操作单元,可以是打印一句话,也可以是构建一个docker镜像,由各类Jenkins插件提供,比如命令“sh”,“mvn”,就相当于我们平时shell终端执行mvn命令一样
  • 执行一个pipeline

    • 点参数化构建过程,选择选项参数

    [外链图片转存中…(img-oZcGY0a3-1661304119435)]

    • 自定义参数(多个选项,需要换行写)

    [外链图片转存中…(img-AHgyHIQm-1661304119435)]

    • 再次点击添加参数,选择字符参数

    [外链图片转存中…(img-ZVOPTdI7-1661304119435)]

    • 保存并点击构建,便有了一个可选择参数的变量和一个有默认值的变量(不指定便默认)

[外链图片转存中…(img-e5uwFO7G-1661304119435)]

  • 使用pipeline语法生成配置

    • 点击流水线语法

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vQIiLf7l-1661304119436)(https://oss.linuxtxc.com/images/image-20220701172734222.png)]

    • 示例步骤点击 checkout: Check out from version control —>写上git仓库地址—>创建git仓库凭证—>再点击生成流水线脚本

    [外链图片转存中…(img-9lH831uA-1661304119436)]

    • 动态获取gitlab仓库分支,也可以添加其他参数化构建,如选项参数(在流水线语法中点击Declarative Directive Generator—> 选择parameters:Parameters—>选择GIt参数—>填写参数—>生成代码片段)

    [外链图片转存中…(img-QSNGRv6V-1661304119436)]

  • Pipeline脚本如下(stages与parameters同级)

pipeline {
    agent {
        kubernetes {
            label "jenkins-slave"
            yaml '''
apiVersion: v1
kind: Pod
metadata:
  name: jenkins-slave
spec:
  containers:
  - name: shell
    image: "harbor.linuxtxc.com:9090/txcrepo/jenkins-slave-jdk:1.8"
    command:
    - sleep
    args:
    - infinity
'''
            defaultContainer 'shell'
        }
    }
    
    parameters {
  gitParameter branch: '', branchFilter: '.*', defaultValue: 'origin/master', description: '请选择要发布的版本', name: 'branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
}
    
    
    stages {
        stage('Main') {
            steps {
              sh  "echo ${namespace}"
            }
        }
        
    stage('拉取代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "${Branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: 'dc627461-ff7c-4a04-8c13-38fa1f4f08ea', url: 'http://218.0.48.118:99/web-ops/web.git']]])
            }
        }
    
    
    }
    
    
}
  • 将凭据用户名密码生成变量(避免用明文展示)

    [外链图片转存中…(img-gX9l9P7F-1661304119437)]

    • 生成脚本片段如下
    withCredentials([usernamePassword(credentialsId: '36c1732b-6502-45b5-9d5b-05d691dfbbbc', passwordVariable: 'password', usernameVariable: 'username')]) {
        // some block
    }
    
  • 将镜像部署到k8s平台的思路

    • 将部署项目yaml文件提交到项目代码仓库里,在slave容器中使用kubectl apply 部署,由于kubectl 使用kubeconfig 配置文件连接k8s集群,还需要通过 Config File Provider 插件将kubeconfig配置文件存储到Jenkins,然后再挂载到slave容器中,这样就有权限部署了(kubectl apply -f deploy.yaml --kubeconfig=config)
    • 注:为提高安全性,kubeconfig文件应分配权限
    • 将kubeconfig配置文件存储到jenkins中
    cat .kube/config
    
    • 点击系统管理—>Managed files—>Add a new Config—>Custom file (自定义文件)—>再直接点击下一步—>更改名字,再将kubeconfig文件写入

    [外链图片转存中…(img-jtfB3Zb2-1661304119437)]

    • 生成pipeline脚本片段(需要将fileid 部分 修改成刚刚创建的k8s 凭据)

    [外链图片转存中…(img-ShngXmkB-1661304119437)]

  • 以下是将deploy.yaml与Jenkinsfile,都可以放在gitlab仓库中

    • 在gitlab中创建Jenkinsfile,再配置pipeline脚本

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1KHSBAuS-1661304119438)(https://oss.linuxtxc.com/images/image-20220706164816410.png)]

[外链图片转存中…(img-OFPTuMYt-1661304119438)]

  • 一个完整的pipeline脚本
// 公共
def registry = "harbor.linuxtxc.com:9090"
// 项目
def project = "txcrepo"
def app_name = "java-demo3"
// BUILD_NUMBER,自带变量,逐次递增
def image_name = "${registry}/${project}/${app_name}:${BUILD_NUMBER}"
def git_address = "http://218.0.48.118:99/web-ops/web.git"
// 认证
def secret_name = "harbor-key-secret"
def harbor_auth = "36c1732b-6502-45b5-9d5b-05d691dfbbbc"
def git_auth = "3467ad1b-6fdc-4a4a-a758-19b229a362fa"
def k8s_auth = "3f9a430c-81ee-484c-95e1-9c07a6360c53"

pipeline {
  agent {
    kubernetes {
        label "jenkins-slave"
        yaml """
kind: Pod
metadata:
  name: jenkins-slave
spec:
  containers:
  - name: jnlp
    image: "${registry}/zhan/jenkins_slave-jdk:1.8"
    imagePullPolicy: IfNotPresent
    volumeMounts:
      - name: docker-cmd
        mountPath: /usr/bin/docker
      - name: docker-sock
        mountPath: /var/run/docker.sock
      - name: maven-cache
        mountPath: /root/.m2
  volumes:
    - name: docker-cmd
      hostPath:
        path: /usr/bin/docker
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock
    - name: maven-cache
      hostPath:
        path: /tmp/m2
"""
        }
      
      }
    parameters {    
        gitParameter branch: '', branchFilter: '.*', defaultValue: 'master', description: '选择发布的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
        choice (choices: ['1', '3', '5', '7'], description: '副本数', name: 'Replicas')
        choice (choices: ['dev','test','prod','default'], description: '命名空间', name: 'Namespace')
    }
    stages {
        stage('拉取代码'){
            steps {
                checkout([$class: 'GitSCM', 
                branches: [[name: "${params.Branch}"]], 
                doGenerateSubmoduleConfigurations: false, 
                extensions: [], submoduleCfg: [], 
                userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]
                ])
            }
        }

        stage('代码编译'){
           steps {
             sh """
                mvn clean package -Dmaven.test.skip=true
                """ 
           }
        }

        stage('构建镜像'){
           steps {
                withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
                sh """
                  unzip target/*.war -d target/ROOT  
                  echo '
                    FROM lizhenliang/tomcat
                    LABEL maitainer lizhenliang
                    ADD target/ROOT /usr/local/tomcat/webapps/ROOT
                  ' > Dockerfile
                  docker build -t ${image_name} .
                  docker login -u ${username} -p '${password}' ${registry}
                  docker push ${image_name}
                """
                }
           } 
        }
        stage('部署到K8S平台'){
          steps {
              configFileProvider([configFile(fileId: "${k8s_auth}", targetLocation: "admin.kubeconfig")]){
                sh """
                  sed -i 's#IMAGE_NAME#${image_name}#' deploy.yaml
                  sed -i 's#SECRET_NAME#${secret_name}#' deploy.yaml
                  sed -i 's#REPLICAS#${Replicas}#' deploy.yaml
                  kubectl apply -f deploy.yaml -n ${Namespace} --kubeconfig=admin.kubeconfig
                """
              }
          }
        }
    }
}


13.k8s日志收集,ELK部署
  • 应用程序日志记录体现方式分为两类:

    • 标准输出:输出到控制台,使用kubectl logs可以看到,日志保存在/var/lib/docker/containers/<容器ID>/xxx-json.log
      • 针对标准输出的日志收集:以DaemonSet 方式在每个Node上部署一个日志收集程序,采集/var/lib/docker/containers/目录下所有的容器日志
    • 日志文件:写到容器文件系统的文件中
      • 针对容器日志文件的日志收集:在pod中增加一个容器运行日志采集器,使用emptyDir共享目录让日志采集器读取到日志文件
  • 日志平台解决方案 ELK

    • Elasticsearch:数据存储,搜索,分析
    • Logstash:采集日志,格式化,过滤,最后将数据推送到Elasticsearch
    • Kibana:数据可视化
  • 在k8s中部署服务

wget https://oss.linuxtxc.com/kubernetes-elk.zip
cd  kubernetes-elk/
#需要将storageclass改为创建的
sed -i  's/managed-nfs-storage/nfs-storageclass/g'  ./*.yaml 
kubectl apply -f elasticsearch.yaml
kubectl apply -f kibana.yaml
kubectl apply -f filebeat-kubernetes.yaml
  • 访问kibana,添加elasticsearch索引(http://125.124.145.22:30601/)

[外链图片转存中…(img-6bheF8EE-1661304119438)]

  • 点击创建索引模式

[外链图片转存中…(img-DvenPmjK-1661304119439)]

  • 正则匹配索引名称(我这里是之前创建过)

[外链图片转存中…(img-duaYd5Q6-1661304119439)]

  • 时间字段选择时间戳

[外链图片转存中…(img-SpyNDpjp-1661304119439)]

  • 创建成功后,搜索查看日志,点击kibana->Discover

[外链图片转存中…(img-TkWqJvB5-1661304119439)]

  • 通过搜索查询日志

[外链图片转存中…(img-wJja3JzS-1661304119440)]

  • 创建一个能收集日志文件的应用,app-log-logfile.yaml 内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-log-logfile
spec:
  replicas: 1
  selector:
    matchLabels:
      project: microservice
      app: nginx-logfile
  template:
    metadata:
      labels:
        project: microservice
        app: nginx-logfile
    spec:
      imagePullSecrets:
      - name: harbor-key-secret
      containers:
      # 应用容器
      - name: nginx 
        image: harbor.linuxtxc.com:9090/txcrepo/alpine-nginx:1.19.6
        # 将数据卷挂载到日志目录
        volumeMounts:
        - name: nginx-logs 
          mountPath: /opt/logs/nginx
      # 日志采集器容器
      - name: filebeat
        image: elastic/filebeat:7.9.2 
        args: [
          "-c", "/etc/filebeat.yml",
          "-e",
        ]
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
          limits:
            memory: 500Mi
        securityContext:
          runAsUser: 0
        volumeMounts:
        # 挂载filebeat配置文件
        - name: filebeat-config
          mountPath: /etc/filebeat.yml
          subPath: filebeat.yml
        # 将数据卷挂载到日志目录
        - name: nginx-logs 
          mountPath: /opt/logs/nginx
      # 数据卷共享日志目录
      volumes:
      - name: nginx-logs
        emptyDir: {}
      - name: filebeat-config
        configMap:
          name: filebeat-nginx-config
---
apiVersion: v1
kind: Service
metadata:
  name: app-log-logfile
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    project: microservice
    app: nginx-logfile
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-nginx-config
  
data:
  # 配置文件保存在ConfigMap
  filebeat.yml: |-
    filebeat.inputs:
      - type: log
        paths:
          - /opt/logs/nginx/*.log
        # tags: ["access"]
        fields_under_root: true
        fields:
          project: microservice
          app: nginx

    setup.ilm.enabled: false
    setup.template.name: "nginx-access"
    setup.template.pattern: "nginx-access-*"

    output.elasticsearch:
      hosts: ['elasticsearch.ops:9200']
      index: "nginx-access-%{+yyyy.MM.dd}"
  • 查看访问日志如下

[外链图片转存中…(img-Kdp3Wi5f-1661304119440)]


Logo

开源、云原生的融合云平台

更多推荐