diff --git a/kubernetes-MD/CICD_Harbor_K8S实现微服务项目持续集成和发布.md b/kubernetes-MD/CICD_Harbor_K8S实现微服务项目持续集成和发布.md new file mode 100644 index 0000000..1fc5ff6 --- /dev/null +++ b/kubernetes-MD/CICD_Harbor_K8S实现微服务项目持续集成和发布.md @@ -0,0 +1,387 @@ +

CI/CD+Harbor+K8S实现微服务项目持续集成和发布

+ + + +------ + +## 一:项目描述 + +#### 1.环境介绍 + +| 服务器 | IP | 角色 | 配置 | +| :--------: | :------------: | :--------------------------------: | :-----: | +| jenkins | 192.168.18.210 | 持续构建项目并发布 | 1Cpu+3G | +| gitlab | 192.168.18.200 | (https://www.xingdian.com)版本库 | 2Cpu+8G | +| harbor | 192.168.18.230 | 镜像仓库 | 1Cpu+3G | +| NFS | 192.168.18.230 | 给k8s提供持久化存储 | 1Cpu+3G | +| k8s-master | 192.168.18.160 | k8s管理节点 | 2Cpu+4G | +| k8s-node-1 | 192.168.18.161 | k8s项目节点 | 1Cpu+3G | +| k8s-node-2 | 192.168.18.162 | k8s项目节点 | 1Cpu+3G | +| k8s-node-3 | 192.168.18.163 | k8s项目节点 | 1Cpu+3G | + +#### 2.jenkins部署 + +```shell +1.上传jdk +[root@jenkins ~]# tar xzf jdk-8u191-linux-x64.tar.gz -C /usr/local/ +[root@jenkins ~]# cd /usr/local/ +[root@jenkins local]# mv jdk1.8.0_191/ java +2.安装tomcat +[root@jenkins ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.42/bin/apache-tomcat-8.5.42.tar.gz +[root@jenkins ~]# tar xzf apache-tomcat-8.5.42.tar.gz -C /usr/local/ +[root@jenkins ~]# cd /usr/local/ +[root@jenkins local]# mv apache-tomcat-8.5.42/ tomcat +3.安装maven +[root@jenkins ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz +[root@jenkins ~]# tar xzf apache-maven-3.5.4-bin.tar.gz -C /usr/local/java +[root@jenkins ~]# cd /usr/local/java +[root@jenkins java]# mv apache-maven-3.5.4/ maven +设置变量: +[root@jenkins-server ~]# vim /etc/profile +JAVA_HOME=/usr/local/java +MAVEN_HOME=/usr/local/java/maven +PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin +export PATH JAVA_HOME MAVEN_HOME +[root@jenkins-server ~]# source /etc/profile +验证: +[root@jenkins-server ~]# java -version +java version "1.8.0_191" +Java(TM) SE Runtime Environment (build 1.8.0_191-b12) +Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode) +[root@jenkins-server ~]# mvn -v +Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00) +Maven home: /usr/local/java/maven +Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /usr/local/java/jre +Default locale: en_US, platform encoding: UTF-8 +OS name: "linux", version: "3.10.0-693.el7.x86_64", arch: "amd64", family: "unix" +下载jenkins的安装包: +安装jenkins:2.332.3 ----通过官网直接下载war包。 +官网:http://updates.jenkins-ci.org/download/war/ +[root@jenkins-server ~]# wget https://get.jenkins.io/war-stable/2.332.3/jenkins.war +部署jenkins +[root@jenkins-server ~]# cd /usr/local/tomcat/webapps/ +[root@jenkins-server webapps]# rm -rf * +[root@jenkins-server webapps]# cp /root/jenkins.war . +[root@jenkins-server webapps]# ./bin/startup.sh +Using CATALINA_BASE: /usr/local/tomcat +Using CATALINA_HOME: /usr/local/tomcat +Using CATALINA_TMPDIR: /usr/local/tomcat/temp +Using JRE_HOME: /usr/local/java +Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar +Tomcat started. +``` + +![](C:\Users\A\Pictures\QQ浏览器截图\image-20220612205950281.png) + +#### 3.gitlab部署 + +部署文件地址: https://docs.qq.com/doc/DQ0hScnRCbVN6QW1F + +![](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612205933958.png) + +#### 4.harbor部署 + +部署链接地址:https://docs.qq.com/doc/DQ0l1bUtFdFNQSmdR + +![image-20220612210023644](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612210023644.png) + +## 二:项目使用 + +​ 在实现部署之前在各个服务至上进行配置,以下是各个配置详情。 + +#### 1.jenkins配置 + +插件安装: + +​ Maven Integration + +​ Generic Webhook Trigger + +​ Deploy to container + +​ Git + +​ Publish Over SSH + +![image-20220612210702847](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612210702847.png) + +配置JDK+MAVEN+GIT环境 + +​ Dashboard--->Global Tool Configuration + +![image-20220612210956961](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612210956961.png) + +注意:需要在服务器上安装git + +```shell +[root@jenkins ~]# yum -y install git +[root@jenkins ~]# git config --global user.email "xingdianvip@gmail.com" +[root@jenkins ~]# git config --global user.name "xingdian" +[root@jenkins ~]# git config --global http.sslVerify "false" +``` + +例如: + +![image-20220612211207274](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612211207274.png) + +jenkisn节点安装docker(因为需要构建镜像到harbor) + +```shell +[root@jenkins ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo +[root@jenkins ~]# yum -y install docker-ce +[root@jenkins ~]# systemctl start docker && docker enable docker +``` + +配置http访问,创建daemon.json指定harbor地址 + +```shell +[root@jenkins ~]# cat /etc/docker/daemon.json +{ + "insecure-registries":["192.168.18.230:80"] +} +[root@jenkins ~]# systemctl daemon-reload && systemctl restart docker +``` + +注意:另外一种修改docker.service文件添加--insecure-registry,在这里不生效 + +额外配置,在企业中每个微服务项目都是独立的,但是此项目源码具有关联性,故需要执行一下操作 + +将所有的项目源码上传到jenkis服务器,在项目目录下执行以下命令,此过程需要耐心等待 + +如果想加快速度,百度搜索mvn的国内仓库地址(略) + +```shell +[root@jenkins tensquare_parent]# mvn install +``` + +创建maven项目: + +![image-20220612213835015](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612213835015.png) + +注意:地址来自下面的gitlab部署 + +创建凭据 (再此添加kubernetes 集群master节点) + +![image-20220612214908915](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612214908915.png) + +![image-20220612214940777](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612214940777.png) + +![image-20220612215011081](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612215011081.png) + +![image-20220612215054577](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612215054577.png) + +配置ssh remote hosts + +![image-20220612215202282](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612215202282.png) + +取消gitlab配置 + +![image-20220612215240632](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612215240632.png) + +配置webhook + +![image-20220612213909544](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612213909544.png) + +![image-20220612213933315](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612213933315.png) + +脚本如下: + +```shell +# Jenkins机器:编译完成后,build生成一个新版本的镜像,push到远程docker仓库 + +# Variables +# 注意下面路径里的maven-docker就是jenkins项目的名称,必须一致 +JENKINS_WAR_HOME='/root/.jenkins/workspace/eureka/target' +DOCKERFILE='/root/.jenkins/workspace/eureka/' +# 自己创建下面目录,主要镜像构建 +DOCKERFILE_HOME='/root/jenkins/docker-file/eureka_jar' +HARBOR_IP='192.168.18.230' +REPOSITORIES='xingdian/eureka' +HARBOR_USER='admin' +HARBOR_USER_PASSWD='Harbor12345' +HARBOR_USER_EMAIL='xingdianvip@gmail.com' + +# Copy the newest war to docker-file directory. +if [ -f /root/jenkins/docker-file/eureka_jar/eureka.jar ];then + rm -rf eureka.jar + \cp -f ${JENKINS_WAR_HOME}/tensquare_eureka_server-1.0-SNAPSHOT.jar ${DOCKERFILE_HOME}/eureka.jar +else + \cp -f ${JENKINS_WAR_HOME}/tensquare_eureka_server-1.0-SNAPSHOT.jar ${DOCKERFILE_HOME}/eureka.jar +fi +# Delete image early version. +docker login ${HARBOR_IP}:80 -u ${HARBOR_USER} -p ${HARBOR_USER_PASSWD} +IMAGE_ID=`sudo docker images | grep ${REPOSITORIES} | awk '{print $3}'` +if [ -n "${IMAGE_ID}" ];then + sudo docker rmi ${IMAGE_ID} +fi + +# Build image. +cd ${DOCKERFILE_HOME} +if [ -f jdk-8u211-linux-x64.tar.gz ];then + echo "jdk ok!!!!!" +else +# 此地址需要自己准备 + wget ftp://192.168.18.234/share/jdk-8u211-linux-x64.tar.gz +fi +if [ -f Dockerfile ];then + rm -rf Dockerfile + cp -f ${DOCKERFILE}Dockerfile ${DOCKERFILE_HOME} + echo "Dockerfile is ok!!" +else + cp -f ${DOCKERFILE}Dockerfile ${DOCKERFILE_HOME} +fi + +TAG=`date +%Y%m%d-%H%M%S` +sudo docker build -t ${HARBOR_IP}:80/${REPOSITORIES}:${TAG} . + +# Push to the harbor registry. +sudo docker push ${HARBOR_IP}:80/${REPOSITORIES}:${TAG} +``` + +#### 2.gitlab配置 + +创建Groups和Project (Menu --- > Groups Menu --- > Project ) + +![image-20220612212255399](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612212255399.png) + +![image-20220612212337337](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612212337337.png) + +取消main分支保护 + +![image-20220612212601128](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612212601128.png) + +添加本地公钥(实现项目推送) + +![image-20220612212702850](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612212702850.png) + +将项目空仓库下载,拷贝到本地空仓库目录下,然后推送给gitlab + +```shell +上传项目的服务器需要安装以下内容: +[root@xingdian ~]# yum -y install git +[root@xingdian ~]# git config --global user.email "xingdianvip@gmail.com" +[root@xingdian ~]# git config --global user.name "xingdian" +[root@xingdian ~]# git config --global http.sslVerify "false" + +[root@xingdian ~]# git clone https://www.xingdian.com/diandian/diandian.git +[root@xingdian ~]# cd diandian +[root@xingdian diandian]# 将项目源码拷贝到此 +[root@xingdian diandian]# git add . +[root@xingdian diandian]# git commit -m "diandian" +[root@xingdian diandian]# git push -u origin main +``` + +开启允许本地网络 + +![image-20220612213514193](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612213514193.png) + +添加webhook + +gitlab: + +![image-20220612213705137](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612213705137.png) + +#### 3.harbor配置 + +​ 可以先手动构建,验证部署过程是否有问题,然后再进行自动化构建,自动化构建需要在gitlab上修改源代码,提交后,会自动触发 + +基础镜像配置(完成基础镜像配置后再构建) + +```shell +[root@harbor centos]# cat Dockerfile +FROM daocloud.io/centos:7 +MAINTAINER "xingdian" +ENV container docker +RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs +RUN yum -y update; yum clean all; \ +(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \ +rm -f /lib/systemd/system/multi-user.target.wants/*;\ +rm -f /etc/systemd/system/*.wants/*;\ +rm -f /lib/systemd/system/local-fs.target.wants/*; \ +rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ +rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ +rm -f /lib/systemd/system/basic.target.wants/*;\ +rm -f /lib/systemd/system/anaconda.target.wants/*; +VOLUME [ "/sys/fs/cgroup" ] +CMD ["/usr/sbin/init"] + +[root@harbor centos]# docker build -t xingdian:latest . + +[root@harbor centos]# docker tag xingdian 192.168.18.230/xingdian/centos:latest + +[root@harbor ~]# cat /etc/docker/daemon.json +{ + "insecure-registries":["192.168.18.230:80"] +} +[root@harbor ~]# systemctl daemon-reload && systemctl restart docker + +[root@harbor centos]# docker login 192.168.18.230 + +[root@harbor centos]# docker push 192.168.18.230/xingdian/centos:latest +``` + +可以手动构建或者自动化构建,查看构建最终的镜像 + +![image-20220612214752493](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612214752493.png) + +#### 4.发布到kubernetes集群 + +在master节点创建持续发布脚本 + +``` +[root@master ~]# cat eureka.sh +#!/bin/bash +HARBOR_IP='192.168.18.230' +HARBOR_USER='admin' +HARBOR_USER_PASSWD='Harbor12345' +/usr/bin/yum -y install git +if [ -d eureka-yaml ];then + rm -rf eureka-yaml + /usr/bin/git clone https://www.xingdian.com/xingdian/eureka-yaml.git +else + /usr/bin/git clone https://www.xingdian.com/xingdian/eureka-yaml.git +fi +cd eureka-yaml +docker login ${HARBOR_IP}:80 -u ${HARBOR_USER} -p ${HARBOR_USER_PASSWD} +tags=`curl -X GET "http://192.168.18.230/api/v2.0/projects/xingdian/repositories/eureka/artifacts?page=1&page_size=10&with_tag=true&with_label=false&with_scan_overview=false&with_signature=false&with_immutable_status=false" -H "accept: application/json" | jq | grep name ` +tagss=`echo $tags | awk -F "\"" '{print $4}'` +sed -i "s#eureka_image#192.168.18.230:80/xingdian/eureka:${tagss}#" eureka.yaml +kubectl get pod | grep eureka +if [ $? -eq 0 ];then + kubectl delete -f eureka.yaml + kubectl create -f eureka.yaml +else + kubectl create -f eureka.yaml +fi +``` + +![image-20220612233953314](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220612233953314.png) + +所有节点docker修改http方式 + +```shell +[root@harbor ~]# cat /etc/docker/daemon.json +{ + "insecure-registries":["192.168.18.230:80"] +} +[root@harbor ~]# systemctl daemon-reload && systemctl restart docker +``` + +jenkins构建发布 + +![image-20220613002321701](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220613002321701.png) + +gitlab修改代码提交,自动触发jenkins构建 + +![image-20220613002244405](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220613002244405.png) + +浏览器访问构建的项目 + +![image-20220613002408320](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220613002408320.png) + +同理其余jar包部署,最终结果: + +![image-20220622130840731](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220622130840731.png) + + + diff --git a/kubernetes-MD/Kubernetes存储类StorageClass.md b/kubernetes-MD/Kubernetes存储类StorageClass.md new file mode 100644 index 0000000..7fb5391 --- /dev/null +++ b/kubernetes-MD/Kubernetes存储类StorageClass.md @@ -0,0 +1,235 @@ +

Kubernetes存储类StorageClass

+ + + +------ + +## 一:StorageClass + +​ StorageClass 为管理员提供了描述存储 "类" 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 "配置文件" + +#### 1.StorageClass 资源 + +​ 每个 StorageClass 都包含 `provisioner`、`parameters` 和 `reclaimPolicy` 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到 + +​ StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新 + +#### 2.创建Storageclass + +```shell +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: example-nfs //名称 +provisioner: example.com/external-nfs +parameters: + server: nfs-server.example.com + path: /share + readOnly: "false" + +server:NFS 服务器的主机名或 IP 地址 +path:NFS 服务器导出的路径 +readOnly:是否将存储挂载为只读的标志(默认为 false) +``` + +注意: + +provisioner参数值: + +```yaml +NFS example.com/external-nfs +Glusterfs kubernetes.io/glusterfs +AWS EBS kubernetes.io/aws-ebs +...... +``` + +AWS EBS: + +```shell +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: slow +provisioner: kubernetes.io/aws-ebs +parameters: + type: io1 + iopsPerGB: "10" //这里需要输入一个字符串,即 "10",而不是 10 + fsType: ext4 + +type:io1,gp2,sc1,st1。详细信息参见 AWS 文档。默认值:gp2 +iopsPerGB:只适用于 io1 卷。每 GiB 每秒 I/O 操作。AWS卷插件将其与请求卷的大小相乘以计算IOPS的容量,并将其限制在 20000 IOPS +fsType:受 Kubernetes 支持的文件类型。默认值:"ext4" +``` + +Glusterfs: + +```shell +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: slow +provisioner: kubernetes.io/glusterfs +parameters: + resturl: "http://127.0.0.1:8081" + clusterid: "630372ccdc720a92c681fb928f27b53f" + restauthenabled: "true" + restuser: "admin" + secretNamespace: "default" + secretName: "heketi-secret" + gidMin: "40000" + gidMax: "50000" + volumetype: "replicate:3" + +resturl:制备 gluster 卷的需求的Gluster REST服务/Heketi服务url,通用格式应该是 IPaddress:Port +restauthenabled:Gluster REST 服务身份验证布尔值,用于启用对 REST 服务器的身份验证 +restuser:在 Gluster 可信池中有权创建卷的 Gluster REST服务/Heketi 用户 +restuserkey:服务器进行身份验证。 此参数已弃用,取而代之的是 secretNamespace + secretName +secretNamespace,secretName:Secret 实例的标识,包含与 Gluster REST 服务交互时使用的用户密码; + 这些参数是可选的,secretNamespace 和 secretName 都省略时使用空密码,以这种方式创建: +kubectl create secret generic heketi-secret \ + --type="kubernetes.io/glusterfs" --from-literal=key='opensesame' \ + --namespace=default +clusterid:630372ccdc720a92c681fb928f27b53f 是集群的 ID,当制备卷时, Heketi 将会使用这个文件 +gidMin,gidMax:StorageClass GID 范围的最小值和最大值,这是 gidMin 和 gidMax 的默认值 +volumetype:卷的类型及其参数可以用这个可选值进行配置 + 'Replica volume': volumetype: replicate:3 其中 '3' 是 replica 数量 + 'Disperse/EC volume': volumetype: disperse:4:2 其中 '4' 是数据,'2' 是冗余数量 + 'Distribute volume': volumetype: none +``` + +#### 3.使用 + +创建storageclass文件: + +```shell +[root@master class]# cat storageclass +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: example-nfs +provisioner: example.com/external-nfs +parameters: + server: 10.0.0.230 + path: /kubernetes-3 + readOnly: "false" +``` + +创建: + +```shell +[root@master class]# kubectl create -f storageclass +storageclass.storage.k8s.io/example-nfs created +``` + +查看: + +```shell +[root@master class]# kubectl get storageclass +NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE +example-nfs example.com/external-nfs Delete Immediate false 9s + +RECLAIMPOLICY:回收策略 Delete +VOLUMEBINDINGMODE:默认情况下, Immediate 模式表示一旦创建了PersistentVolumeClaim 也就完成了卷绑定和动态制备 +``` + +创建pv的yaml文件: + +```shell +[root@master class]# cat pv.yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: xingdian-1 +spec: + capacity: + storage: 10Gi + volumeMode: Filesystem + accessModes: + - ReadWriteOnce + storageClassName: example-nfs + nfs: + path: /kubernetes-3 + server: 10.0.0.230 +``` + +创建: + +```shell +[root@master class]# kubectl create -f pv.yaml +``` + +查看pv: + +```shell +[root@master class]# kubectl get pv +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE +xingdian-1 10Gi RWO Retain Available example-nfs 3s +``` + +创建应用使用: + +```shell +[root@master class]# cat nginx.yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: web +spec: + selector: + matchLabels: + app: nginx + serviceName: "nginx" + replicas: 1 + template: + metadata: + labels: + app: nginx + spec: + terminationGracePeriodSeconds: 10 + containers: + - name: nginx + image: 10.0.0.230/xingdian/nginx:v1 + ports: + - containerPort: 80 + name: web + volumeMounts: + - name: www + mountPath: /usr/share/nginx/html + volumeClaimTemplates: + - metadata: + name: www + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: "example-nfs" + resources: + requests: + storage: 10Gi +``` + +创建: + +```shell +[root@master class]# kubectl create -f nginx.yaml +statefulset.apps/web created +``` + +查看: + +```shell +[root@master class]# kubectl get statefulset +NAME READY AGE +web 1/1 9s +[root@master class]# kubectl get pod +NAME READY STATUS RESTARTS AGE +web-0 1/1 Running 0 13s +``` + +验证pv: + +```shell +[root@master class]# kubectl get pv +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE +xingdian-1 10Gi RWO Retain Bound default/www-web-0 example-nfs 52s +``` + +![image-20220526224804444](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/image-20220526224804444-16535764908601.png) diff --git a/kubernetes-MD/kubernetes-RBAC.md b/kubernetes-MD/kubernetes-RBAC.md new file mode 100644 index 0000000..e038a13 --- /dev/null +++ b/kubernetes-MD/kubernetes-RBAC.md @@ -0,0 +1,744 @@ +

kubernetes-RBAC

+ + + +------ + +## 一:RBAC详解 + +#### 1.RBAC基于角色的访问控制 + +​ Service Account为服务提供了一种方便的认证机制,但它不关心授权的问题。可以配合RBAC来为Service Account鉴权,在Kubernetes中,授权有ABAC(基于属性的访问控制)、RBAC(基于角色的访问控制)、Webhook、Node、AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式 + +​ 从1.6版起,Kubernetes 默认启用RBAC访问控制策略 + +​ 从1.8开始,RBAC已作为稳定的功能 + +#### 2.授权步骤 + +​ 定义角色:在定义角色时会指定此角色对于资源的访问控制的规则 + +​ 绑定角色:将主体与角色进行绑定,对用户进行访问授权 + +​ 基于角色的访问控制使用"rbac.authorization.k8s.io" API Group实现授权决策允许管理员通过Kubernetes API动态配置策略 + +​ 定义Role、ClusterRole、RoleBinding或ClusterRoleBinding + +#### 3.启用RBAC + +​ 要启用RBAC,使用--authorization-mode=RBAC启动API Server + +```shell +[root@master ~]# cat /etc/kubernetes/manifests/kube-apiserver.yaml +apiVersion: v1 +kind: Pod +metadata: + annotations: + kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.18.160:6443 + creationTimestamp: null + labels: + component: kube-apiserver + tier: control-plane + name: kube-apiserver + namespace: kube-system +spec: + containers: + - command: + - kube-apiserver + - --advertise-address=192.168.18.160 + - --allow-privileged=true + - --authorization-mode=Node,RBAC +``` + +#### 4.Role与ClusterRole + +​ 一个角色包含了一套表示一组权限的规则。 权限以纯粹的累加形式累积(没有"否定"的规则) + +###### Role: + +​ 角色可以由命名空间内的Role对象定义 + +​ 一个Role对象只能用于授予对某一单一命名空间中资源的访问权限 + +###### ClusterRole: + +​ 整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现 + +###### Role示例: + +​ 描述"default"命名空间中的一个Role对象的定义,用于授予对pod的读访问权限 + +```shell +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + namespace: default + name: pod-reader +rules: +- apiGroups: [""]       # 空字符串""表明使用core API group + resources: ["pods"] + verbs: ["get", "watch", "list"] +``` + +注意: + +​ watch操作语义的含义如下:读取状态并从任意版本开始 + +​ 在任何资源版本开始 watch;首选可用的最新资源版本,但不是必需的。允许任何起始资源版本。由于分区或过时的缓存,watch 可能从客户端之前观察到的更旧的资源版本开始,特别是在高可用性配置中。不能容忍这种明显倒带的客户不应该用这种语义启动 watch。 为了建立初始状态,watch从起始资源版本中存在的所有资源实例的合成 “添加” 事件开始。 以下所有监视事件都针对在watch开始的资源版本之后发生的所有更改 + +**读取状态并从最新版本开始** + +​ 从最近的资源版本开始 **watch**, 它必须是一致的(详细说明:通过仲裁读取从 etcd 提供服务)。 为了建立初始状态,**watch** 从起始资源版本中存在的所有资源实例的合成 “添加” 事件开始。 以下所有监视事件都针对在 **watch** 开始的资源版本之后发生的所有更改 + +**从指定版本开始** + +​ 以确切的资源版本开始 **watch**。监视事件适用于提供的资源版本之后的所有更改。 与 “Get State and Start at Most Recent” 和 “Get State and Start at Any” 不同, **watch** 不会以所提供资源版本的合成 “添加” 事件启动。 由于客户端提供了资源版本,因此假定客户端已经具有起始资源版本的初始状态 + +###### ClusterRole示例: + +​ ClusterRole定义可用于授予用户对某一特定命名空间,或者所有命名空间中的secret(取决于其绑定方式)的读访问权限 + +```shell +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + # ClusterRole是集群范围对象,所以这里不需要定义"namespace"字段 + name: secret-reader +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] +``` + +#### 5.RoleBinding与ClusterRoleBinding + +​ 角色绑定将一个角色中定义的各种权限授予一个或者一组用户 + +###### 角色绑定包含: + +​ 一组相关主体, 即:subject + +​ 包括: + +​ 用户--User + +​ 用户组--Group + +​ 服务账户--Service Account + +​ 对被授予角色的引用 + +###### RoleBinding: + +​ 在命名空间中可以通过RoleBinding对象授予权限 + +​ RoleBinding可以引用在同一命名空间内定义的Role对象 + +###### ClusterRoleBinding: + +​ 集群范围的权限授予则通过ClusterRoleBinding对象完成 + +###### RoleBinding示例: + +​ 定义的RoleBinding对象在"default"命名空间中将"pod-reader"角色授予用户"jane"。 这一授权将允许用户"jane"从"default"命名空间中读取pod;以下角色绑定定义将允许用户"jane"从"default"命名空间中读取pod: + +```shell +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: read-pods + namespace: default +subjects: +- kind: User          #赋予用户jane pod-reader角色权限 + name: jane + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: Role + name: pod-reader      #引用上面定义的role + apiGroup: rbac.authorization.k8s.io +``` + +​ RoleBinding对象也可以引用一个ClusterRole对象 + +​ 用于在RoleBinding所在的命名空间内授予用户对所引用的ClusterRole中定义的命名空间资源的访问权限 + +​ 这一点允许管理员在整个集群范围内首先定义一组通用的角色,然后再在不同的命名空间中复用这些角色 + +​ 例如,尽管下面示例中的RoleBinding引用的是一个ClusterRole对象,但是用户"dave"(即角色绑定主体)还是只能读取"development" 命名空间中的secret(即RoleBinding所在的命名空间) + +​ 以下角色绑定允许用户 "dave" 读取 "development" 命名空间中的secret: + +```shell +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: read-secrets + namespace: development       # 这里表明仅授权读取"development"命名空间中的资源。 +subjects: +- kind: User + name: dave + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: ClusterRole + name: secret-reader      #引用上面定义的clusterRole 名称(clusterRole没有指定命名空间,默认可以应用所有,但是在rolebinding时,指定了命名空间,所以只能读取本命名空间的文件) + apiGroup: rbac.authorization.k8s.io +``` + +​ ClusterRoleBinding在集群级别和所有命名空间中授予权限 + +​ 以下 'ClusterRoleBinding' 对象允许在用户组 "manager" 中的任何用户都可以读取集群中任何命名空间中的secret + +```shell +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: read-secrets-global +subjects: +- kind: Group + name: manager + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: ClusterRole + name: secret-reader + apiGroup: rbac.authorization.k8s.io +``` + +#### 6.role对资源的引用 + +​ 大多数资源由代表其名字的字符串表示,如 "pods",就像它们出现在相关API endpoint的URL中一样。然而,有一些Kubernetes API还包含了 "子资源" + +​ 比如pod的logs: + +​ pod logs endpoint的URL格式为: + +​ GET /api/v1/namespaces/{namespace}/pods/{name}/log + +​ 这种情况下,"pods" 是命名空间资源,而 "log" 是pods的子资源 + +​ 为了在RBAC角色中表示出这一点,需使用斜线来划分 资源 与 子资源 + +例子: + +​ 如果需要角色绑定主体读取pods以及pod log,需要定义以下角色 + +```shell +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + namespace: default + name: pod-and-pod-logs-reader +rules: +- apiGroups: [""] + resources: ["pods", "pods/log"]    #表示授予读取pods下log的权限 + verbs: ["get", "list"] +``` + +###### resourceNames: + +​ 通过 resourceNames 列表,角色可以针对不同种类的请求根据资源名引用资源实例。当指定了resourceNames 列表时,不同动作种类的请求的权限,如使用 "get"、"delete"、"update" 以及 "patch" 等动词的请求,将被限定到资源列表中所包含的资源实例上 + +例子: + +​ 如果需要限定一个角色绑定主体只能 "get" 或者 "update" 一个configmap时,可以定义以下角色 + +```shell +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + namespace: default + name: configmap-updater +rules: +- apiGroups: [""] + resources: ["configmap"] + resourceNames: ["my-configmap"] + verbs: ["update", "get"] +``` + +注意: + +​ 如果设置了resourceNames,则请求所使用的动词不能是list、watch、create或者deletecollection,由于资源名不会出现在create、list、watch和deletecollection等API请求的URL中,所以这些请求动词不会被设置了resourceNames 的规则所允许,因为规则中的resourceNames部分不会匹配这些请求 + +###### 角色定义的例子: + +​ 在以下示例中,仅截取展示了rules部分的定义: + +允许读取core API Group中定义的资源 "pods" + +```shell +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] +``` + +允许读写在 "extensions" 和 "apps" API Group中定义的 "deployments" + +```shell +rules: +- apiGroups: [""] + resources: ["deployments"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +``` + +允许读取 "pods" 以及读写 "jobs" + +```shell +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] +- apiGroups: ["batch", "extensions"] + resources: ["jobs"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] +``` + +允许读取一个名为 "my-config" 的 ConfigMap 实例 + +```shell +rules: +- apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["my-config"] + verbs: ["get"] +``` + +允许读取core API Group中的"nodes"资源,Node是集群级别资源,所以此ClusterRole定义需要与一个ClusterRoleBinding绑定才能有效 + +```shell +rules: +- apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list", "watch"] +``` + +允许对非资源endpoint "/healthz" 及其所有子路径的 "GET" 和 "POST" 请求 + +```shell +rules: +- nonResourceURLs: ["/healthz", "/healthz/*"]   # 在非资源URL中,'*'代表后缀通配符 + verbs: ["get", "post"] + +注意: + 此ClusterRole定义需要与一个ClusterRoleBinding绑定才能有效 +``` + +#### 7.对角色绑定主体(Subject)的引用 + +​ RoleBinding或者ClusterRoleBinding将角色绑定到角色绑定主体(Subject)。 角色绑定主体(kind指定)可以是用户组(Group)、用户(User)或者服务账户(Service Accounts) + +###### 用户: + +​ 由字符串表示 + +​ 纯粹的用户名,例如 "alice" + +​ 电子邮件风格的名字,如 "bob@example.com" + +​ 用字符串表示的数字id + +用户的产生: + +​ 由Kubernetes管理员配置认证模块以产生所需格式的用户名。对于用户名,RBAC授权系统不要求任何特定的格式 + +注意: + +​ 前缀system:是 为Kubernetes系统使用而保留的,所以管理员应该确保用户名不会意外地包含这个前缀 + +用户组的产生: + +​ Kubernetes中的用户组信息由授权模块提供。用户组与用户一样由字符串表示。Kubernetes对用户组 字符串没有格式要求,但前缀system:同样是被系统保留的 + +服务账户: + +​ 服务账户(serviceAccount)拥有包含 system:serviceaccount:前缀的用户名,并属于拥有system:serviceaccounts:前缀的用户组 + +###### 角色绑定例子: + +​ 以下示例中,仅截取展示了RoleBinding的subjects字段 + +一个名为"alice@example.com"的用户 + +```shell +subjects: +- kind: User + name: "alice@example.com" + apiGroup: rbac.authorization.k8s.io +``` + +一个名为"frontend-admins"的用户组 + +```shell +subjects: +- kind: Group + name: "frontend-admins" + apiGroup: rbac.authorization.k8s.io +``` + +kube-system命名空间中的默认服务账户 + +```shell +subjects: +- kind: ServiceAccount + name: default + namespace: kube-system +``` + +名为"qa"命名空间中的所有服务账户 + +```shell +subjects: +- kind: Group + name: system:serviceaccounts:qa + apiGroup: rbac.authorization.k8s.io +``` + +在集群中的所有服务账户 + +```shell +subjects: +- kind: Group + name: system:serviceaccounts + apiGroup: rbac.authorization.k8s.io +``` + +所有认证过的用户(version 1.5+) + +```shell +subjects: +- kind: Group + name: system:authenticated + apiGroup: rbac.authorization.k8s.io +``` + +所有未认证的用户(version 1.5+) + +```shell +subjects: +- kind: Group + name: system:unauthenticated + apiGroup: rbac.authorization.k8s.io +``` + +所有用户(version 1.5+) + +```shell +subjects: +- kind: Group + name: system:authenticated + apiGroup: rbac.authorization.k8s.io +- kind: Group + name: system:unauthenticated + apiGroup: rbac.authorization.k8s.io +``` + +#### 8.默认角色与默认角色绑定 + +​ API Server会创建一组默认的ClusterRole和ClusterRoleBinding对象。 这些默认对象中有许多包含system:前缀,表明这些资源由Kubernetes基础组件"拥有"。 对这些资源的修改可能导致非功能性集群(non-functional cluster) + +​ 比如:system:node ClusterRole对象。 这个角色定义了kubelets的权限。如果这个角色被修改,可能会导致kubelets无法正常工作 + +​ 所有默认的ClusterRole和ClusterRoleBinding对象都会被标记为kubernetes.io/bootstrapping=rbac-defaults + +​ 每次启动时,API Server都会更新默认ClusterRole所缺乏的各种权限,并更新默认ClusterRoleBinding所缺乏的各个角色绑定主体。 这种自动更新机制允许集群修复一些意外的修改。由于权限和角色绑定主体在新的Kubernetes释出版本中可能变化,这也能够保证角色和角色绑定始终保持是最新的 + +​ 如果需要禁用自动更新,请将默认ClusterRole以及ClusterRoleBinding的rbac.authorization.kubernetes.io/autoupdate 设置成为false。 请注意,缺乏默认权限和角色绑定主体可能会导致非功能性集群问题 + +​ 自Kubernetes 1.6+起,当集群RBAC授权器(RBAC Authorizer)处于开启状态时,可以启用自动更新功能 + +###### 命令行工具: + +​ 有两个kubectl命令可以用于在命名空间内或者整个集群内授予角色 + +```shell +[root@master ~]# kubectl create rolebinding +``` + +​ 在某一特定命名空间内授予Role或者ClusterRole + +示例如下: + +​ 在名为"acme"的命名空间中将admin ClusterRole授予用户"bob" + +```shell +[root@master ~]# kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme +``` + +​ 在名为"acme"的命名空间中将view ClusterRole授予服务账户"myapp" + +```shell +[root@master ~]# kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme +``` + +在整个集群中授予ClusterRole,包括所有命名空间,示例如下: + +​ 在整个集群范围内将cluster-admin ClusterRole授予用户"root" + +```shell + [root@master ~]# kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root +``` + +​ 在整个集群范围内将system:node ClusterRole授予用户"kubelet" + +```shell +[root@master ~]# kubectl create clusterrolebinding kubelet-node-binding --clusterrole=system:node --user=kubelet +``` + +​ 在整个集群范围内将view ClusterRole授予命名空间"acme"内的服务账户"myapp" + +```shell +[root@master ~]# kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp +``` + +#### 9.服务账户(Service Account)权限 + +​ 默认的RBAC策略将授予控制平面组件(control-plane component)、节点(node)和控制器(controller)一组范围受限的权限, 但对于"kube-system"命名空间以外的服务账户,则不授予任何权限(超出授予所有认证用户的发现权限) + +从最安全到最不安全可以排序以下方法: + +​ 对某一特定应用程序的服务账户授予角色(最佳实践) + +​ 要求应用程序在其pod规范(pod spec)中指定serviceAccountName字段,并且要创建相应服务账户(例如通过API、应用程序清单或者命令kubectl create serviceaccount等) + +###### 案例: + +​ 在"my-namespace"命名空间中授予服务账户"my-sa"只读权限 + +```shell +[root@master ~]# kubectl create rolebinding my-sa-view \ + --clusterrole=view \ + --serviceaccount=my-namespace:my-sa \ + --namespace=my-namespace +``` + +​ 换成yaml文件大概如下 + +```shell +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: my-sa-view + namespace: my-namespace +subjects: +- kind: ServiceAccount + name: my-sa + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: ClusterRole + name: view #这里view为clusterrole名称,其中berbs需要给view + apiGroup: rbac.authorization.k8s.io +``` + +在某一命名空间中授予"default"服务账号角色 + +​ 如果一个应用程序没有在其pod规范中指定serviceAccountName,它将默认使用"default"服务账号 + +​ 注意:授予"default"服务账号的权限将可用于命名空间内任何没有指定serviceAccountName的pod + +例子: + +​ 下面的例子将在"my-namespace"命名空间内授予"default"服务账号只读权限 + +```shell +[root@master ~]# kubectl create rolebinding default-view \ + --clusterrole=view \ + --serviceaccount=my-namespace:default \ + --namespace=my-namespace +``` + +​ 目前,许多加载项(addon)作为"kube-system"命名空的"default"服务帐户运行。 要允许这些加载项使用超级用户访问权限,请将cluster-admin权限授予"kube-system"命名空间中的"default"服务帐户。 注意:启用上述操作意味着"kube-system"命名空间将包含允许超级用户访问API的秘钥 + +```shell +[root@master ~]# kubectl create clusterrolebinding add-on-cluster-admin \ +--clusterrole=cluster-admin \ +--serviceaccount=kube-system:default +``` + +为命名空间中所有的服务账号授予角色 + +​ 如果希望命名空间内的所有应用程序都拥有同一个角色,无论它们使用什么服务账户,可以为该命名空间的服务账户用户组授予角色 + +例子: + +​ 下面的例子将授予"my-namespace"命名空间中的所有服务账户只读权限 + +```shell +[root@master ~]# kubectl create rolebinding serviceaccounts-view \ +--clusterrole=view \ +--group=system:serviceaccounts:my-namespace \ +--namespace=my-namespace +``` + +## 二:RBAC应用 + +​ 创建k8s账号与RBAC授权使用 + +#### 创建账号 + +​ 创建私钥 + +```shell +[root@master ~]# (umask 077; openssl genrsa -out xingdian.key 2048) +``` + +​ 用此私钥创建一个csr(证书签名请求)文件 + +```shell +[root@master ~]# openssl req -new -key xingdian.key -out xingdian.csr -subj "/CN=xingdian" +[root@master ~]# openssl x509 -req -in xingdian.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out xingdian.crt -days 365 +``` + +​ 看证书内容 + +```shell +[root@master ~]# openssl x509 -in xingdian.crt -text -noout +[root@master ~]# kubectl config set-credentials xingdian --client-certificate=./xingdian.crt --client-key=./xingdian.key --embed-certs=true +``` + +​ 设置上下文(设置为k8s集群的用户) + +```shell +[root@master ~]# kubectl config set-context xingdian@kubernetes --cluster=kubernetes --user=xingdian +``` + +​ 查看当前的工作上下文 + +```shell +[root@master ~]# kubectl config view +``` + +​ 切换用户(切换上下文) + +```shell +[root@master ~]# kubectl config use-context xingdian@kubernetes +``` + +​ 验证是否已经切换到了新的上下文 + +```shell +[root@master ~]# kubectl config current-context +``` + +​ 测试(还未赋予权限) + +```shell +[root@master ~]# kubectl get pod +No resources found. +Error from server (Forbidden): pods is forbidden: User "wing" cannot list pods in the namespace "default" +``` + +#### 授权 + +​ K8S授权请求是http的请求 + +​ 对象URL格式: + +​ /apis/[GROUP]/[VERSION]/namespace/[NAMESPACE_NAME]/[KIND]/[OBJECT_ID] + +​ k8s授权方式分为: + +​ serviceaccount和自己签证ca证书的账号,及签证ca的用户组(group)上(授权给这个组的权限) + +​ role: + +​ 允许的操作,如get,list等 + +​ 允许操作的对象,如pod,svc等 + +​ rolebinding: + +​ 将哪个用户绑定到哪个role或clusterrole上 + +​ clusterrole:(集群角色) + +​ clusterrolebinding:(绑定到集群) + +​ 如果使用rolebinding绑定到role上,表示绑定的用户只能用于当前namespace的权限 + +###### 创建一个角色: + +​ 切回管理帐号先 + +```shell +[root@master ~]# kubectl config use-context kubernetes-admin@kubernetes +[root@master ~]# kubectl create role myrole --verb=get,list,watch --resource=pod,svc +``` + +​ 绑定用户xingdian,绑定role为myrole + +```shell +[root@master ~]# kubectl create rolebinding myrole-binding --role=myrole --user=xingdian +``` + +​ 切换用户 + +```shell +[root@master ~]# kubectl config use-context xingdian@kubernetes +Switched to context "xingdian@kubernetes". +``` + +​ 查看权限 + +​ 只授权了default名称空间pod和svc的get,list,watch权限 + +```shell +[root@master ~]# kubectl get pod +NAME READY STATUS RESTARTS AGE +nginx-pod 0/1 ImagePullBackOff 0 1h + +[root@master ~]# kubectl get pod -n kube-system #无权访问kube-system +No resources found. +Error from server (Forbidden): pods is forbidden: User "wing" cannot list pods in the namespace "kube-system" + +[root@master ~]# kubectl delete pod nginx-pod #无删除权限 +Error from server (Forbidden): pods "nginx-pod" is forbidden: User "wing" cannot delete pods in the namespace "default" +``` + +###### 创建clusterrole: + +​ 可以访问全部的namespace + +```shell +[root@master ~]# kubectl create clusterrole mycluster-role --verb=get,list,watch --resource=pod,svc +``` + +​ 删除xingdian账号之前绑定的rolebinding + +```shell +[root@master ~]# kubectl delete rolebinding myrole-binding +``` + +​ 使用clusterrolebinding绑定clusterrole + +```shell +[root@master ~]# kubectl create clusterrolebinding my-cluster-rolebinding --clusterrole=mycluster-role --user=xingdian +``` + +​ 切换账号 + +```shell +[root@master ~]# kubectl config use-context xingdian@kubernetes +``` + +​ 查看权限 查看kube-system空间的pod + +```shell +# kubectl get pod -n kube-system +NAME READY STATUS RESTARTS AGE +coredns-78fcdf6894-67h9h 1/1 Running 1 11h +coredns-78fcdf6894-lzxmz 1/1 Running 1 11h +etcd-k8s-m 1/1 Running 2 11h +...... +``` + + + + + + + + + + + + + diff --git a/kubernetes-MD/kubernetes持久化存储.md b/kubernetes-MD/kubernetes持久化存储.md new file mode 100644 index 0000000..2e47417 --- /dev/null +++ b/kubernetes-MD/kubernetes持久化存储.md @@ -0,0 +1,343 @@ +

kubernetes持久化存储

+ + + +------ + +## 一:简介 + +​ 存储的管理是一个与计算实例的管理完全不同的问题。PV子系统为用户 和管理员提供了一组 API,将存储如何供应的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。 + +​ 持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应,或者使用存储类(Storage Class)来动态供应。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样,也是使用 卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。 + +​ 持久卷申领(PersistentVolumeClaim,PVC)表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式。 + +​ 尽管 PersistentVolumeClaim 允许用户消耗抽象的存储资源,常见的情况是针对不同的问题用户需要的是具有不同属性(如,性能)的 PersistentVolume 卷。 集群管理员需要能够提供不同性质的 PersistentVolume,并且这些 PV 卷之间的差别不仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户;为了满足这类需求,就有了存储类(StorageClass) 资源。 + +​ PV 卷是集群中的资源。PVC 申领是对这些资源的请求,也被用来执行对资源的申领检查。 + +#### 1.PV卷供应的方式 + +静态供应: + +​ 集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息,并且对集群 用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。 + +动态供应: + +​ 如果管理员所创建的所有静态 PV 卷都无法与用户的 PVC 匹配, 集群可以尝试为该 PVC 申领动态供应一个存储卷。 这一供应操作是基于 StorageClass 来实现的:PVC 申领必须请求某个 存储类,同时集群管理员必须 已经创建并配置了该类,这样动态供应卷的动作才会发生。 如果 PVC 申领指定存储类为 "",则相当于为自身禁止使用动态供应的卷。 + +## 二:基于NFS的持久化存储 + +#### 1.部署NFS + +```shell +安装部署nfs服务,并创建nfsdata作为共享目录。 +[root@master ~]# yum -y install nfs-utils +[root@master ~]# systemctl start nfs +[root@master ~]# mkdir /nfsdata +[root@master ~]# vim /etc/exports +/nfsdata *(rw,no_root_squash,no_all_squash,sync) +[root@master ~]# exportfs -r +修改配置文件重启nfs +[root@master ~]# vim /etc/sysconfig/nfs +RPCNFSDARGS="-N 2 -N 3" +RPCNFSDARGS="-N 4" +[root@master ~]# systemctl restart nfs +所有节点需要安装ntf-utils,目的是识别NFS文件系统 +[root@node-1 ~]# yum -y install nfs-utils +``` + +#### 2.创建PV+PVC+nginx应用 + +```shell +[root@master ~]# cat pv.yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: nfs-pv +spec: + capacity: + storage: 1000Mi + accessModes: + - ReadWriteMany + nfs: + server: 10.0.0.110 + path: /nfsdata +[root@master ~]# cat pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: nfs-pvc +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 90Mi + +--- +apiVersion: v1 +kind: Pod +metadata: + name: nginx-volume-pvc + +spec: + containers: + - name: nginx + image: 10.0.0.144/library/nginx:1.18 + ports: + - containerPort: 80 + volumeMounts: + - name: html-pv + mountPath: /usr/share/nginx/html + volumes: + - name: html-pv + persistentVolumeClaim: + claimName: nfs-pvc +[root@master ~]# kubectl create -f pv.yaml +[root@master ~]# kubectl create -f pvc.yaml +``` + +#### 3.查看创建好的PV和PVC + +```shell +[root@master ~]# kubectl get pv +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE +nfs-pv 1000Mi RWX Retain Bound default/nfs-pvc 6m37s +[root@master ~]# kubectl get pvc +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE +nfs-pvc Bound nfs-pv 1000Mi RWX 6m39s +``` + +#### 4.查看创建好的应用 + +```shell +[root@master ~]# kubectl get pod +NAME READY STATUS RESTARTS AGE +nginx-volume-pvc 1/1 Running 0 8m47s +``` + +#### 5.验证存储卷 + +```shell +[root@master nfsdata]# echo "hello world" > index.html +[root@master nfsdata]# ls +index.html +[root@master nfsdata]# kubectl get pod -o wide +NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES +nginx-volume-pvc 1/1 Running 0 9m52s 10.244.3.9 node-3 +[root@master nfsdata]# curl 10.244.3.9 +hello world +``` + +## 三:使用过程 + +#### 1.绑定 + +​ 用户创建一个带有特定存储容量和特定访问模式需求的 PVC 对象; 在动态供应场景下,这个 PVC 对象可能已经创建完毕。 主控节点中的控制回路监测新的 PVC 对象,寻找与之匹配的 PV 卷(如果可能的话), 并将二者绑定到一起。 如果为了新的 PVC 申领动态供应了 PV 卷,则控制回路总是将该 PV 卷绑定到这一 PVC 申领。 否则,用户总是能够获得他们所请求的资源,只是所获得的 PV 卷可能会超出所请求的配置。 一旦绑定关系建立,则 PersistentVolumeClaim 绑定就是排他性的,无论该 PVC 申领是 如何与 PV 卷建立的绑定关系。 PVC 申领与 PV 卷之间的绑定是一种一对一的映射,实现上使用 ClaimRef 来记述 PV 卷 与 PVC 申领间的双向绑定关系。 + +#### 2.使用 + +​ Pod 将 PVC 申领当做存储卷来使用。集群会检视 PVC 申领,找到所绑定的卷,并 为 Pod 挂载该卷。对于支持多种访问模式的卷,用户要在 Pod 中以卷的形式使用申领时指定期望的访问模式 + +​ 一旦用户有了申领对象并且该申领已经被绑定,则所绑定的 PV 卷在用户仍然需要它期间 一直属于该用户。用户通过在 Pod 的volumes块中包含persistentVolumeClaim节区来调度 Pod,访问所申领的 PV 卷 + +#### 3.回收 + +​ 当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除,从而允许 该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群,当其被 从申领中释放时如何处理该数据卷。 目前,数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除) + +#### 4.保留 + +​ 回收策略 Retain使得用户可以手动回收资源。当 PVC对象 被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放"。 由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷: + +​ 删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在 + +​ 根据情况,手动清除所关联的存储资产上的数据。手动删除所关联的存储资产;如果你希望重用该存储资产,可以基于存储资产的 定义创建新的 PersistentVolume 卷对象 + +#### 5.删除 + +​ 对于支持 Delete回收策略的卷插件,删除动作会将 PV对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 动态供应的卷会继承其 StorageClass 中设置的回收策略,该策略默认 为Delete。管理员需要根据用户的期望来配置 StorageClass;否则 PV 卷被创建之后必须要被编辑或者修补 + +## 四:PV参数解释 + +#### 1.持久卷 + +​ 每个 PV 对象都包含spec部分和status部分,分别对应卷的规约和状态 + +```shell +apiVersion: v1 +kind: PersistentVolume +metadata: + name: pv0003 +spec: + capacity: + storage: 5Gi + volumeMode: Filesystem + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Recycle + storageClassName: slow + mountOptions: + - hard + - nfsvers=4.1 + nfs: + path: /tmp + server: 172.17.0.2 +``` + +#### 2.容量 + +​ 一般而言,每个 PV 卷都有确定的存储容量。 容量属性是使用 PV 对象的capacity属性来设置的。目前,存储大小是可以设置和请求的唯一资源。 未来可能会包含 IOPS、吞吐量等属性 + +#### 3.卷模式 + +​ 针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统)和Block(块)。 volumeMode是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是Filesystem + +​ volumeMode属性设置为Filesystem的卷会被 Pod 挂载(Mount) 到某个目录。 如果卷的存储来自某块设备而该设备目前为空,Kuberneretes 会在第一次挂载卷之前在设备上创建文件系统 + +​ 你可以将volumeMode设置为Block,以便将卷作为原始块设备来使用。 这类卷以块设备的方式交给 Pod 使用,其上没有任何文件系统。 这种模式对于为 Pod 提供一种使用最快可能方式来访问卷而言很有帮助,Pod 和 卷之间不存在文件系统层。另外,Pod 中运行的应用必须知道如何处理原始块设备 + +#### 4.访问模式 + +​ PersistentVolume 卷可以用资源提供者所支持的任何方式挂载到宿主系统上。 如下表所示,提供者(驱动)的能力不同,每个 PV 卷的访问模式都会设置为 对应卷所支持的模式值。 例如,NFS 可以支持多个读写客户,但是某个特定的 NFS PV 卷可能在服务器 上以只读的方式导出。每个 PV 卷都会获得自身的访问模式集合,描述的是特定 PV 卷的能力 + +访问模式有: + +​ ReadWriteOnce -- 卷可以被一个节点以读写方式挂载 + +​ ReadOnlyMany -- 卷可以被多个节点以只读方式挂载 + +​ ReadWriteMany -- 卷可以被多个节点以读写方式挂载 + +在命令行接口(CLI)中,访问模式也使用以下缩写形式: + +​ RWO - ReadWriteOnce + +​ ROX - ReadOnlyMany + +​ RWX - ReadWriteMany + +注意: + +​ 每个卷只能同一时刻只能以一种访问模式挂载,即使该卷能够支持 多种访问模式 + +#### 5.类 + +​ 每个 PV 可以属于某个类(Class),通过将其storageClassName属性设置为某个 StorageClass 的名称来指定。 特定类的 PV 卷只能绑定到请求该类存储卷的 PVC 申领。 未设置storageClassName的 PV 卷没有类设定,只能绑定到那些没有指定特定 存储类的 PVC 申领 + +#### 6.回收策略 + +​ Retain -- 手动回收 + +​ Recycle -- 基本擦除 (rm -rf /thevolume/*) + +​ Delete -- 诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除 + +​ 目前,仅 NFS 和 HostPath 支持回收。 AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除(Delete) + +#### 7.节点亲和性 + +​ 每个 PV 卷可以通过设置 节点亲和性 来定义一些约束,进而限制从哪些节点上可以访问此卷。 使用这些卷的 Pod 只会被调度到节点亲和性规则所选择的节点上执行 + +#### 8.阶段 + +​ Available(可用)-- 卷是一个空闲资源,尚未绑定到任何申领 + +​ Bound(已绑定)-- 该卷已经绑定到某申领 + +​ Released(已释放)-- 绑定的申领已被删除,资源尚未被集群回收 + +​ Failed(失败)-- 卷的自动回收操作失败 + +## 五:PVC参数解释 + +#### 1.PersistentVolumeClaims + +​ 每个 PVC 对象都有spec和status部分,分别对应申领的规约和状态 + +```shell +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: myclaim +spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} +``` + +#### 2.访问模式 + +​ ReadWriteOnce -- 卷可以被一个节点以读写方式挂载 + +​ ReadOnlyMany -- 卷可以被多个节点以只读方式挂载 + +​ ReadWriteMany -- 卷可以被多个节点以读写方式挂载 + +#### 3.卷模式 + +​ 针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):Filesystem(文件系统)和Block(块)。 volumeMode是一个可选的 API 参数。 如果该参数被省略,默认的卷模式是Filesystem + +#### 4.资源 + +​ 申领和 Pod 一样,也可以请求特定数量的资源。在这个上下文中,请求的资源是存储。 卷和申领都使用相同的资源模型 + +#### 5.选择算符 + +​ 申领可以设置标签选择算符 来进一步过滤卷集合。只有标签与选择算符相匹配的卷能够绑定到申领上。 选择算符包含两个字段: + +​ matchLabels - 卷必须包含带有此值的标签 + +​ matchExpressions - 通过设定键(key)、值列表和操作符(operator) 来构造的需求 + +​ 合法的操作符有 In、NotIn、Exists 和 DoesNotExist + +注意: + +​ 来自matchLabels和matchExpressions的所有需求都按逻辑与的方式组合在一起, 这些需求都必须被满足才被视为匹配 + +#### 6.类 + +​ 申领可以通过为storageClassName属性设置 StorageClass 的名称来请求特定的存储类。 只有所请求的类的 PV 卷,即storageClassName值与 PVC 设置相同的 PV 卷, 才能绑定到 PVC申领 + +​ PVC 申领不必一定要请求某个类。如果 PVC 的 `storageClassName` 属性值设置为 `""`, 则被视为要请求的是没有设置存储类的 PV 卷,因此这一 PVC 申领只能绑定到未设置 存储类的 PV 卷(未设置注解或者注解值为 `""` 的 PersistentVolume(PV)对象在系统中不会被删除,因为这样做可能会引起数据丢失。 未设置 `storageClassName` 的 PVC 与此大不相同,也会被集群作不同处理,具体筛查方式取决于`DefaultStorageClass` 准入控制器插件是否被启用 + +​ 如果准入控制器插件被启用,则管理员可以设置一个默认的 StorageClass。 所有未设置 storageClassName 的 PVC 都只能绑定到隶属于默认存储类的 PV 卷。 设置默认 StorageClass 的工作是通过将对应 StorageClass 对象的注解 storageclass.kubernetes.io/is-default-class 赋值为 true 来完成的。 如果管理员未设置默认存储类,集群对 PVC 创建的处理方式与未启用准入控制器插件 时相同。如果设定的默认存储类不止一个,准入控制插件会禁止所有创建 PVC 操作 + +​ 如果准入控制器插件被关闭,则不存在默认 StorageClass 的说法。 所有未设置 `storageClassName` 的 PVC 都只能绑定到未设置存储类的 PV 卷。 在这种情况下,未设置 `storageClassName` 的 PVC 与 `storageClassName` 设置未 `""` 的 PVC 的处理方式相同 + +#### 7.使用申领作为卷 + +​ Pod 将申领作为卷来使用,并藉此访问存储资源。 申领必须位于使用它的 Pod 所在的同一名字空间内。 集群在 Pod 的名字空间中查找申领,并使用它来获得申领所使用的 PV 卷。 之后,卷会被挂载到宿主上并挂载到 Pod 中 + +```shell +apiVersion: v1 +kind: Pod +metadata: + name: mypod +spec: + containers: + - name: myfrontend + image: nginx + volumeMounts: + - mountPath: "/var/www/html" + name: mypd + volumes: + - name: mypd + persistentVolumeClaim: + claimName: myclaim +``` + + + + +