上传文件至 kubernetes-MD

This commit is contained in:
tu 2024-06-07 15:26:40 +08:00
parent 70b25278e9
commit a58a495acd
5 changed files with 1997 additions and 0 deletions

View File

@ -0,0 +1,387 @@
<h1><center>CI/CD+Harbor+K8S实现微服务项目持续集成和发布</center></h1>
著作:行癫 <盗版必究>
------
## 一:项目描述
#### 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.
```
![](https://xingdian-image.oss-cn-beijing.aliyuncs.com/xingdian-image/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" <xingdianvip@gmail.com>
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)

View File

@ -0,0 +1,235 @@
<h1><center>Kubernetes存储类StorageClass</center></h1>
著作:行癫 <盗版必究>
------
## 一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"
serverNFS 服务器的主机名或 IP 地址
pathNFS 服务器导出的路径
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
typeio1gp2sc1st1。详细信息参见 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
restauthenabledGluster REST 服务身份验证布尔值,用于启用对 REST 服务器的身份验证
restuser在 Gluster 可信池中有权创建卷的 Gluster REST服务/Heketi 用户
restuserkey服务器进行身份验证。 此参数已弃用,取而代之的是 secretNamespace + secretName
secretNamespacesecretNameSecret 实例的标识,包含与 Gluster REST 服务交互时使用的用户密码;
这些参数是可选的secretNamespace 和 secretName 都省略时使用空密码,以这种方式创建:
kubectl create secret generic heketi-secret \
--type="kubernetes.io/glusterfs" --from-literal=key='opensesame' \
--namespace=default
clusterid630372ccdc720a92c681fb928f27b53f 是集群的 ID当制备卷时 Heketi 将会使用这个文件
gidMingidMaxStorageClass 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)

View File

@ -0,0 +1,744 @@
<h1><center>kubernetes-RBAC</center></h1>
著作:行癫 <盗版必究>
------
## 一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的getlistwatch权限
```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
......
```

View File

@ -0,0 +1,343 @@
<h1><center>kubernetes持久化存储</center></h1>
著作:行癫 <盗版必究>
------
## 一:简介
存储的管理是一个与计算实例的管理完全不同的问题。PV子系统为用户 和管理员提供了一组 API将存储如何供应的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源PersistentVolume 和 PersistentVolumeClaim。
持久卷PersistentVolumePV是集群中的一块存储可以由管理员事先供应或者使用存储类Storage Class来动态供应。 持久卷是集群资源就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样,也是使用 卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领PersistentVolumeClaimPVC表达的是用户对存储的请求。概念上与 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 <none> <none>
[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 支持两种卷模式volumeModesFilesystem文件系统和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 支持两种卷模式volumeModesFilesystem文件系统和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 卷(未设置注解或者注解值为 `""` 的 PersistentVolumePV对象在系统中不会被删除因为这样做可能会引起数据丢失。 未设置 `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
```

View File

@ -0,0 +1,288 @@
<h1><center>kubernetes调度粘性</center></h1>
著作:行癫 <盗版必究>
------
## 一:调度粘性
#### 1.三种调度粘性
NodeSelector(定向调度)
NodeAffinity(Node亲和性)
PodAffinity(Pod亲和性)
通常情况下使用的都是k8s默认的调度调度方式但是在有些情况下我们需要将pod运行在具有特点的标签的node上才能都运行这个时候pod的调度策略就不能使用k8s默认的调度策略了这个时候就需要指定调度策略告诉k8s需要将pod调度到那些node上。
#### 2.nodeSelector
常规情况下会直接使用nodeSelector这种调度策略。labels标签 是k8s里面用来编标记资源的一种常用的方式我们可以给node标记特殊的标签然后nodeSelector会将pod调度到带有指定labels的node上的提供简单的pod部署限制pod选择一个或多个node的label部署
给node添加label
```shell
kubectl label nodes <node-name> <label-key>=<label-value>
```
pod添加nodeSelector机制
```shell
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
```
部署pod
```shell
[root@master ~]# kubectl create -f test.yaml
pod/nginx created
```
查看结果:
```shell
[root@master ~]# kubectl get pod -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
default nginx 0/1 ContainerCreating 0 37s <none> node-1 <none>
```
从上面的执行结果可以看出pod 通过默认的 default-scheduler 调度器到了node-1节点上。不过这种调度方式属于强制性的。如果node02上的资源不足那么pod的状态将会一直是pending状态。
#### 3.亲和性和反亲和性调度
k8s的默认调度流程实际上是经过了两个阶段predicates判断,priorities优先选择 。使用默认的调度流程的话k8s会将pod调度到资源充裕的节点上使用nodeselector的调度方法又会将pod调度具有指定标签的节点上。然后在实际生产环境中我们需要将pod调度到具有些label的一组node才能满足实际需求这个时候就需要nodeAffinity、podAffinity以及 podAntiAffinity(pod 反亲和性)
亲和性可以分为具体可以细分为硬和软两种亲和性:
软亲和性:如果调度的时候,没有满足要求,也可以继续调度,即能满足最好,不能也无所谓
硬亲和性是指调度的时候必须满足特定的要求如果不满足那么pod将不会被调度到当前node
requiredDuringSchedulingIgnoredDuringExecution #硬性强制
preferredDuringSchedulingIgnoredDuringExecution #软性配置
#### 4.nodeAffinity 节点亲和性
节点亲和性主要是用来控制 pod 能部署在哪些节点上以及不能部署在哪些节点上的它可以进行一些简单的逻辑组合了不只是简单的相等匹配preferredDuringSchedulingIgnoredDuringExecution
强调优先满足制定规则调度器会尝试调度pod到Node上但并不强求相当于软限制。多个优先级规则还可以设置权重值以定义执行的先后顺序
nodeAffinity控制 pod 的调度:
```shell
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
containers:
- name: with-node-affinity
image: nginx
```
设置label
```shell
[root@master ~]# kubectl label nodes node-2 disk-type=ssd
node/node-2 labeled
```
创建pod并查看运行结果
```shell
[root@master yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
with-node-affinity 0/1 ContainerCreating 0 4m <none> node-2 <none> <none>
```
NodeAffinity规则设置的注意事项如下
如果同时定义了nodeSelector和nodeAffinityname必须两个条件都得到满足pod才能最终运行在指定的node上
如果nodeAffinity指定了多个nodeSelectorTerms那么其中一个能够匹配成功即可
如果在nodeSelectorTerms中有多个matchExpressions则一个节点必须满足所有matchExpressions才能运行该pod
matchExpressions : 匹配表达式,这个标签可以指定一段例如pod中定义的key为zoneoperator为In(包含那些)values为 foo和bar就是在node节点中包含foo和bar的标签中调度
kubernetes提供的操作符有下面的几种
Inlabel 的值在某个标签中
NotInlabel 的值不在某个标签中
Gtlabel 的值大于某个值
Ltlabel 的值小于某个值
Exists某个 label 存在
DoesNotExist某个 label 不存在
#### 5.podAffinity pod亲和性
Pod的亲和性主要用来解决pod可以和哪些pod部署在同一个集群里面即拓扑域由node组成的集群里面而pod的反亲和性是为了解决pod不能和哪些pod部署在一起的问题二者都是为了解决pod之间部署问题。需要注意的是Pod 间亲和与反亲和需要大量的处理这可能会显著减慢大规模集群中的调度不建议在具有几百个节点的集群中使用而且Pod 反亲和需要对节点进行一致的标记即集群中的每个节点必须具有适当的标签能够匹配topologyKey。如果某些或所有节点缺少指定的topologyKey标签可能会导致意外行为
Pod亲和性场景我们的k8s集群的节点分布在不同的区域或者不同的机房当服务A和服务B要求部署在同一个区域或者同一机房的时候我们就需要亲和性调度了
labelSelector : 选择跟那组Pod亲和
namespaces : 选择哪个命名空间
topologyKey : 指定节点上的哪个键
pod亲和性调度需要各个相关的pod对象运行于"同一位置" 而反亲和性调度则要求他们不能运行于"同一位置"
这里指定“同一位置” 是通过 topologyKey 来定义的topologyKey 对应的值是 node 上的一个标签名称比如各别节点zone=A标签各别节点有zone=B标签pod affinity topologyKey定义为zone那么调度pod的时候就会围绕着A拓扑B拓扑来调度而相同拓扑下的node就为“同一位置”如果基于各个节点kubernetes.io/hostname标签作为评判标准那么很明显“同一位置”意味着同一节点不同节点既为不同位置
pod亲和性
```shell
apiVersion: v1
kind: Pod
metadata:
name: pod-first
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: daocloud.io/library/nginx:latest
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second
labels:
app: db
tier: db
spec:
containers:
- name: busybox
image: daocloud.io/library/busybox
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: kubernetes.io/hostname
```
查看结果:
```shell
[root@master yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first 1/1 Running 0 10m 10.244.1.6 node-1 <none> <none>
pod-second 1/1 Running 0 10m 10.244.1.7 node-1 <none> <none>
```
pod反亲和性
Pod反亲和性场景当应用服务A和数据库服务B要求尽量不要在同一台节点上的时候
```shell
apiVersion: v1
kind: Pod
metadata:
name: pod-first-1
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: daocloud.io/library/nginx:latest
---
apiVersion: v1
kind: Pod
metadata:
name: pod-second-2
labels:
app: backend
tier: db
spec:
containers:
- name: busybox
image: daocloud.io/library/busybox:latest
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 3600"]
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: ["myapp"]}
topologyKey: kubernetes.io/hostname
```
查看结果:
```shell
[root@master yaml]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-first-1 1/1 Running 0 7m28s 10.244.1.8 node-1 <none> <none>
pod-second-2 1/1 Running 0 7m28s 10.244.2.6 node-2 <none> <none>
```