ingress
理论
回顾暴露端口

假如没有ingress,自己来做https方案:

Nginx-Ingress

Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方网站:https://kubernetes.github.io/ingress-nginx/
Ingress可以解决什么问题
1.动态配置服务:如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指向我们新的k8s服务. 而如果用了Ingress, 只需要配置好这个服务, 当服务启动时, 会自动注册到Ingress的中, 不需要额外的操作.
2.减少不必要的端口暴露
Ingress 工作原理
1.ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化,
2.然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置,
3.再写到nginx-ingress-control的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,
4.然后reload一下使配置生效。以此达到域名分配置和动态更新的问题。
ingress和pod的关系

pod和ingress通过service关联,ingress作为统一入口,由service关联一组pod
ingress工作流程

通过ingress管理不同业务的域名,从而分发流量到不同的service
使用ingress

操作:通过ingress对外暴露应用,通过域名访问
创建nginx应用,对外暴露端口使用NodePort
kubectl create deployment web --image=nginxkubectl expose deployment web --port=80 --target-port=80 --type=NodePort

部署ingress controller,多加一层ingress
参考链接:
https://www.cnblogs.com/hailun1987/p/14162278.html
https://segmentfault.com/a/1190000040618813
直接部署
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.0/deploy/static/provider/baremetal/deploy.yaml
sed -i 's@k8s.gcr.io/ingress-nginx/controller:v1.0.0\(.*\)@willdockerhub/ingress-nginx-controller:v1.0.0@' deploy.yaml
sed -i 's@k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0\(.*\)$@hzde0128/kube-webhook-certgen:v1.0@' deploy.yaml
kubectl apply -f deploy.yaml
部署文件介绍:
namespace.yaml
创建一个独立的命名空间 ingress-nginx
configmap.yaml
ConfigMap是存储通用的配置变量的,类似于配置文件,使用户可以将分布式系统中用于不同模块的环境变量统一到一个对象中管理;而它与配置文件的区别在于它是存在集群的“环境”中的,并且支持K8S集群中所有通用的操作调用方式。从数据角度来看,ConfigMap的类型只是键值组,用于存储被Pod或者其他资源对象(如RC)访问的信息。这与secret的设计理念有异曲同工之妙,主要区别在于ConfigMap通常不用于存储敏感信息,而只存储简单的文本信息。
ConfigMap可以保存环境变量的属性,也可以保存配置文件。创建pod时,对configmap进行绑定,pod内的应用可以直接引用ConfigMap的配置。相当于configmap为应用/运行环境封装配置。
pod使用ConfigMap,通常用于:设置环境变量的值、设置命令行参数、创建配置文件。
default-backend.yaml
如果外界访问的域名不存在的话,则默认转发到default-http-backend这个Service,其会直接返回404:
rbac.yaml
负责Ingress的RBAC授权的控制,其创建了Ingress用到的ServiceAccount、ClusterRole、Role、RoleBinding、ClusterRoleBinding
with-rbac.yaml
是Ingress的核心,用于创建ingress-controller。ingress-controller的作用是将新加入的Ingress进行转化为Nginx的配置
优化 ingress-nginx
使用 hostNetwork
修改 Deployment 下面的 spec

修改负载均衡问题
把 kind: Deployment 改为 kind: DaemonSet 模式,这样每台 node 上都有 ingress-nginx-controller pod 副本。

修改 ingressClass 问题
如果不关心 ingressClass 或者很多没有 ingressClass 配置的 ingress 对象,
添加参数 ingress-controller --watch-ingress-without-class=true 。
...
args:
- /nginx-ingress-controller
- --publish-service=$(POD_NAMESPACE)/ingress-nginx-dev-v1-test-controller
- --election-id=ingress-controller-leader
- --controller-class=k8s.io/ingress-nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-dev-v1-test-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
- --watch-ingress-without-class=true # 新增
...
应用yaml文件
kubectl apply -f deploy.yaml
!如果安装失败先删除命名空间:
kubectl delete ns ingress-nginx --force --grace-period=0
查看部署ingress状态
kubectl get pods -n ingress-nginx -o wide

创建nginx应用:
创建ingress规则
ingress-network.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx" #声名需要使用nginx方式解析ingress
spec:
rules: # 一个Ingress可以配置多个rules
- host: example.ingredemo.com # 域名配置,可以不写(不建议),支持匹配,一般写法:*.bar.com
http:
paths: # 相当于nginx的location配合,同一个host可以配置多个path 例如“/”或“/abc”
- path: "/"
pathType: Prefix
backend:
service:
name: web
port:
number: 80

通过servicename来匹配
kubectl apply -f ingress-network.yaml
kubectl get svc -n ingress-nginx -o wide

32671端口为ingress访问端口
测试规则是否生效,部署在了哪台机器上
kubectl get ing

修hosts文件(本地macbook测试方式,windows或线上环境根据实际情况自行修改)

查看(查看80端口即可)

创建一个多域名ingress
cat ingress-mulDomain.yaml
apiVersion: networking.k8s.io/v1beta1 # networking.k8s.io/v1 / extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: "nginx"
name: example
spec:
rules: # 一个Ingress可以配置多个rules
- host: foo.bar.com # 域名配置,可以不写,匹配*, *.bar.com
http:
paths: # 相当于nginx的location配合,同一个host可以配置多个path / /abc
- backend:
serviceName: nginx-svc
servicePort: 80
path: /
- host: foo2.bar.com # 域名配置,可以不写,匹配*, *.bar.com
http:
paths: # 相当于nginx的location配合,同一个host可以配置多个path / /abc
- backend:
serviceName: nginx-svc-external
servicePort: 80
path: /
给 ingress-nginx 配置 HTTPS 访问(属于secret)
创建自签证书文件
openssl req -x509 -nodes -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginx/O=nginx"
创建后会生成两个文件
tls.crt tls.key
创建 secret
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
修改ingress-network.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- example.ingredemo.com
secretName: tls-secret
rules:
- host: example.ingredemo.com
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: web
port:
number: 80
kubectl apply -f ingress-network.yaml
访问:

nginx添加BasicAuth
yum -y install httpd-tools
htpasswd -c auth testuser
kubectl create secret generic basic-auth --from-file=auth
生成一个基于base64加密的字符串


修改ingress
ingress-network.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
rules:
- host: example.ingredemo.com
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: web
port:
number: 80
输入用户名和密码

此功能常用在ELK 网页端
Nginx 进行重写
rewrite.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: https://www.baidu.com:443
spec:
tls:
- hosts:
- example.ingredemo.com
secretName: tls-secret
rules:
- host: example.ingredemo.com
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: web
port:
number: 80
| 名称 | 描述 |
|---|---|
| nginx.ingress.kubernetes.io/rewrite-target | 必须重定向流量的目标URI |
| nginx.ingress.kubernetes.io/ssl-redirect | 指示位置部分是否仅可访问SSL(当Ingress包含证书时默认为True) |
| nginx.ingress.kubernetes.io/force-ssl-redirect | 即使Ingress未启用TLS,也强制重定向到HTTPS |
| nginx.ingress.kubernetes.io/app-root | 定义Controller必须重定向的应用程序根,如果它在'/'上下文中 |
| nginx.ingress.kubernetes.io/use-regex | 指示Ingress上定义的路径是否使用正则表达式 |
tomcat部署练习:
创建应用yaml
vim tomcat.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
labels:
app: tomcat
spec:
replicas: 2
selector:
matchLabels:
app: tomcat
minReadySeconds: 1
progressDeadlineSeconds: 60
revisionHistoryLimit: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: tomcat
spec:
containers:
- name: tomcat
image: wenlongxue/tomcat:tomcat-demo-62-8fe6052
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
requests:
memory: "2Gi"
cpu: "80m"
limits:
memory: "2Gi"
cpu: "80m"
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 180
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 30
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
labels:
app: tomcat
spec:
selector:
app: tomcat
ports:
- name: tomcat-port
protocol: TCP
port: 8080
targetPort: 8080
type: ClusterIP
kubectl apply -f tomcat.yaml
创建 ingress yaml
vim tomcat-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tomcat
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: tomcat.cnsre.cn
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: tomcat-service
port:
number: 8080
kubectl apply -f tomcat-ingress.yaml
在 hosts 文件最后追加 ingress 节点的 IP 地址
192.168.1.11 tomcat.cnsre.cn
访问

给 ingress-tomcat 配置 HTTPS 访问
创建自签证书文件
openssl req -x509 -nodes -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginx/O=nginx"
创建后会生成两个文件
tls.crt tls.key
创建 secret
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
修改ingress-network.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tomcat
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls: # 新增
- hosts: # 新增
- tomcat.cnsre.cn # 新增
secretName: tls-secret # 新增
rules:
- host: tomcat.cnsre.cn
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: tomcat-service
port:
number: 8080
kubectl apply -f ingress-network.yaml
访问:

helm
什么是 Helm
在没使用 helm 之前,向 kubernetes 部署应用,我们要依次部署 deployment、svc 等,步骤较繁琐。况且随着很多项目微服务化,复杂的应用在容器中部署以及管理显得较为复杂,helm 通过打包的方式,支持发布的版本管理和控制,很大程度上简化了 Kubernetes 应用的部署和管理
Helm 本质就是让 K8s 的应用管理(Deployment,Service 等 ) 可配置,能动态生成。通过动态生成 K8s 资源清单文件(deployment.yaml,service.yaml)。然后调用 Kubectl 自动执行 K8s 资源部署
Helm 是官方提供的类似于 YUM 的包管理器,是部署环境的流程封装。Helm 有两个重要的概念:chart 和 release
helm引入原因
之前部署方式
通过导出的方式修改编写yaml文件
kubectl create deployment web --image=nginx --dry-run=client -o yaml > web.yaml
暴露端口
kubectl expose deployment web --port=80 --type=NodePort --target-port=80 --name=web1 -o yaml > web-expose.yaml
kubectl expose deployment web --port=80 --target-port=80 --type=NodePort
检查
初级部署方式
检查pods
kubectl get pods -o wide
检查网络
kubectl get svc
ingress部署方式
通过yaml文件指定对外暴露端口和域名
kubectl apply -f ingress-network.yaml
kubectl get ing
kubectl get pods -n ingress-nginx -o wide
之前的缺陷
如果使用之前方式部署的那一应用,少数服务的应用,比较合适
但是部署微服务项目,可能有几十个服务,每个服务都有一套yaml文件,需要维护大量yaml文件,版本管理特别不方便
Helm介绍
使用helm可以解决哪些问题?

Helm 是一个 Kubernetes 的包管理工具,就像 Linux 下的包管理器,如 yum/apt 等,可以很方便的将之前打包好的 yaml 文件部署到 kubernetes 上。
Helm 有 3 个重要概念:
(1)helm:一个命令行客户端工具,主要用于 Kubernetes 应用 chart 的创建、打包、发布和管理。
(2)Chart:应用描述,一系列用于描述 k8s 资源相关文件(yaml文件)的集合。
(3)Release:基于 Chart 的部署实体,一个 chart 被 Helm 运行后将会生成对应的一个release;将在 k8s 中创建出真实运行的资源对象。应用级别版本管理
Helm v3 的第一个稳定版本
1、最明显的变化是 Tiller 的删除
2、Release 名称可以在不同命名空间重用
3、支持将 Chart 推送至 Docker 镜像仓库中
4、使用 JSONSchema 验证 chart values
Helm架构变化

helm安装过程(v3)
helm安装文档:
https://helm.sh/docs/intro/install/
Helm 命令客户端命令下载地址:https://github.com/helm/helm/releases 解压移动到/usr/bin/目录即可

wget https://get.helm.sh/helm-v3.2.1-linux-amd64.tar.gz
tar zxvf helm-v3.2.1-linux-amd64.tar.gz
mv linux-amd64/helm /usr/bin/
配置国内 chart 仓库
微软仓库(http://mirror.azure.cn/kubernetes/charts/)这个仓库推荐,基本上官网有的 chart 这里都有。
阿里云仓库(https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts )
官方仓库(https://hub.kubeapps.com/charts/incubator)官方 chart 仓库,国内有点不好使。
添加存储库
添加微软仓库
helm repo add stable http://mirror.azure.cn/kubernetes/charts
可以继续添加阿里仓库
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
更新源仓库
helm repo update

搜索源内容
helm search repo 名称

如果想要删除阿里源
helm repo remove aliyun
helm命令补全:
source <(helm completion bash)
echo "source <(helm completion bash)" >> ~/.bash_profile
helm completion bash > /usr/share/bash-completion/completions/helm
使用helm快速部署应用
过程

一键安装(部署weave)
helm search repo weave

helm install ui stable/weave-scope

这个创建过程相当于自己联网从stable库中下载了一个部署weave-scope的yaml文件组在chart中,创建应用之后名称为ui,然后把应用跑起来
通过安装之后的名称查看应用
helm status ui

查看pods
kubectl get pods

暴露端口
kubectl get svc 默认不会暴露端口,需要将ClusterIP修改成NodePort

编写yaml文件
kubectl edit svc ui-weave-scope

kubectl get svc

web查看

helm安装dashboard
tips:非helm安装dashboard方式:
https://github.com/kubernetes/dashboard
进入helm官网,选择charts
https://helm.sh/docs/intro/install/
https://artifacthub.io/

搜索kubernetes-dashboard

安装带有release名称的chart:my-release
添加仓库并安装
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
helm repo update
helm install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --namespace kube-system

helm list -n kube-system

修改svc为NodePort模式提供对完访问端口
kubectl edit svc kubernetes-dashboard -n kube-system

kubectl get svc -n kube-system

访问
https://192.168.1.10:31321

填写token(如果遇到异常可以跳过)
kubectl -n kube-system describe $(kubectl get secret -n kube-system -o name | grep namespace) | grep token
kubectl -n kube-system get secret | grep dashboard-token
kubectl describe secrets kubernetes-dashboard-token-4bhsn -n kube-system
这里有些问题。使用这里的token无法编辑dashboard
创建一个SA管理员权限:
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kube-system
获取token:
kubectl -n kube-system get secret $(kubectl -n kube-system get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"

登入:

自定义一个chart部署一个自定义应用
创建一个chart模板
helm create mychart
查看生成的mychart目录

创建自己的应用
cd mychart/templates
rm -rf *
kubectl create deployment web1 --image=wangyanglinux/myapp:v1 --dry-run=client -o yaml > deployment.yaml
先把服务跑起来,在进行端口暴露的yaml创建,然后再把服务删掉
kubectl create -f deployment.yaml
kubectl expose deployment web1 --port=80 --target-port=80 --type=NodePort --dry-run=client -o yaml > service.yaml
kubectl delete deployment web1
当前目录成功创建了两个yaml文件deployment.yaml service.yaml
安装mychart
helm install web1 mychart

kubectl get pods,svc

应用升级

比如说修改了mychart中的yaml文件进行升级
将mychart2下的templete下的yaml文件进行修改

升级操作
helm upgrade web1 mychart

使用helm实现高效复用
实现方式讲解:

修改操作:

修改处:

具体操作:
1:在values.yaml定义变量和值
vim values

2:在templetes的yaml文件使用values.yaml定义变量
vim deployment.yaml

Release可以变化版本名称
vim service.yaml

这里".svc注意"不能用“.”需要用横杠
生成yaml文件
测试
helm install --dry-run web2 mychart

创建
helm install web3 mychart
kubectl get pods

kubectl get svc

测试

优化模版样例(deployment+service+ingress(tls证书版)):
目录结构

values.yaml
deployment:
label: nginx-label
replicas: 3
image: wangyanglinux/myapp:v1
imagename: nginx
service:
label: nginx-svc-label
port: 80
targetport: 80
porttype: NodePort
ingress:
hostname: example.ingredemo.com
port: 80
secret: tls-secret
templates:
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: {{.Values.deployment.label}}
name: {{.Release.Name}}-deploy
spec:
replicas: {{.Values.deployment.replicas}}
selector:
matchLabels:
app: {{.Values.deployment.label}}
template:
metadata:
labels:
app: {{.Values.deployment.label}}
spec:
containers:
- image: {{.Values.deployment.image}}
name: {{.Values.deployment.imagename}}
service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: {{.Values.service.label}}
name: {{.Release.Name}}-svc
spec:
ports:
- port: {{.Values.service.port}}
protocol: TCP
targetPort: {{.Values.service.targetport}}
selector:
app: {{.Values.deployment.label}}
type: {{.Values.service.porttype}}
ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{.Release.Name}}-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- {{.Values.ingress.hostname}}
secretName: {{.Values.ingress.secret}}
rules:
- host: {{.Values.ingress.hostname}}
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: {{.Release.Name}}-svc
port:
number: 80
helm install myapp mychart/
访问https://example.ingredemo.com/:

20230823更新:最近推荐使用helm安装ingress
使用helm安装ingress:https://kubernetes.github.io/ingress-nginx/deploy/#using-helm
添加 ingress-nginx 官方helm仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
下载ingress的helm包至本地
helm pull ingress-nginx/ingress-nginx --version=4.0.1
tar -xvf ingress-nginx-4.0.1.tgz
更改对应的配置
cd ingress-nginx && vim values.yaml
修改 ingress-nginx-controller 的镜像仓库地址,默认是 k8s.gcr.io 国内无法访问,这里用到github上一个同步 ingress-nginx-controller 的仓库 (最近貌似这个仓库也g了=-=,自行寻找国内文档吧)docker.io/willdockerhub/ingress-nginx-controller

修改 hostNetwork 的值为 true(建议使用专有宿主机单独去跑ingress):

dnsPolicy的值改为: ClusterFirstWithHostNet (修改 hostNetwork 的值为 true后必须跟着修改,否则内部无法解析)

nodeSelector 添加标签: ingress: "true",用于部署 ingress-controller 到指定节点

kind类型更改为:DaemonSet nodeSelector+DaemonSet方式可以更方便的进行nginx的扩容和缩容

kube-webhook-certgen的镜像地址改为国内仓库地址 registry.aliyuncs.com/google_containers/kube-webhook-certgen

type: LoadBalancer 如果非公有云部署修改成ClusterIP

执行安装
先创建一个名称空间
kubectl create ns ingress-nginx
给需要部署ingress的节点上打标签(k8s默认集群中,出于安全考虑,默认配置下Kubernetes不会将Pod调度到Master节点。可以选择执行“kubectl taint node master1 node-role.kubernetes.io/master-”命令去除master的污点,这里使用node进行打标签了)
kubectl label node k8s-node01 ingress=true
进入chart目录(ingress-nginx目录)进行helm安装
helm install ingress-nginx -n ingress-nginx .
安装后结果展示:

kubectl get all -n ingress-nginx

因为node01有ingress=true标签,所以部署到了node01,在扩容缩容ingress所部属的节点数量时,一定要先把前面的负载均衡后端的node先摘除掉

使用宿主机开启nginx监听可以避免使用kube-proxy进行代理,使用NodePort会影响服务器性能

数据卷
概念:
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题
Kubernetes 支持以下类型的卷
awsElasticBlockStoreazureDiskazureFilecephfscsidownwardAPIemptyDirfcflockergcePersistentDiskgitRepoglusterfshostPathiscsilocalnfspersistentVolumeClaimprojectedportworxVolumequobyterbdscaleIOsecretstorageosvsphereVolume
emptyDir
当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir 中的数据将被永久删除
emptyDir 的用法有:
- 暂存空间,例如用于基于磁盘的合并排序、用作长时间计算崩溃恢复时的检查点
- Web服务器容器提供数据时,保存内容管理器容器提取的文件
jobs-empty.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: jobs-empty
spec:
template:
spec:
restartPolicy: Never
initContainers:
- name: job-1
image: busybox:1.34.1
command:
- 'sh'
- '-c'
- >
for i in 1 2 3;
do
echo "job-1 `date`";
sleep 1s;
done;
echo job-1 GG > /srv/input/code
volumeMounts:
- mountPath: /srv/input/
name: input
- name: job-2
image: busybox:1.34.1
command:
- 'sh'
- '-c'
- >
for i in 1 2 3;
do
echo "job-2 `date`";
sleep 1s;
done;
cat /srv/input/code &&
echo job-2 GG > /srv/input/output/file
resources:
requests:
cpu: 3
volumeMounts:
- mountPath: /srv/input/
name: input
- mountPath: /srv/input/output/
name: output
containers:
- name: job-3
image: busybox:1.34.1
command:
- 'sh'
- '-c'
- >
echo "job-1 and job-2 completed";
sleep 3s;
cat /srv/output/file
volumeMounts:
- mountPath: /srv/output/
name: output
volumes:
- name: input
emptyDir: {}
- name: output
emptyDir: {}
查看:
kubectl describe pods jobs-empty-g7cqc

kubectl logs jobs-empty-g7cqc -c job-1
kubectl logs jobs-empty-g7cqc -c job-2
kubectl logs jobs-empty-g7cqc

hostPath
卷将主机节点的文件系统中的文件或目录挂载到集群中
hostPath的用途如下:
- 运行需要访问 Docker 内部的容器;使用
/var/lib/docker的hostPath - 在容器中运行 cAdvisor;使用
/dev/cgroups的hostPath - 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在
除了所需的 `path` 属性之外,用户还可以为 `hostPath` 卷指定 `type`

使用这种卷类型请注意,因为:
由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同*
当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath 卷
hostPath案例:
test-pd.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: wangyanglinux/myapp:v1
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /data
# this field is optional
type: Directory
kubectl apply -f test-pd.yaml
kubectl get pods

kubectl describe pod test-pd

两台node上操作:
mkdir /data
echo $(hostname) > /data/index.html
进入容器查看:

nfs网络存储
pod重启,数据还会存在
安装nfs
重新找一台机器(192.168.1.14)安装nfs
yum -y install nfs-utils rpcbind
mkdir -p /data/nfs
vim /etc/exports
/data/nfs *(rw,no_root_squash,no_all_squash,sync)
将node节点服务器安装nfs
yum -y install nfs-utils
nfs服务端开启nfs
systemctl start nfs
touch /data/nfs/aaa
master节点在k8s集群部署应用使用nfs持久网络存储
mkdir pv
cd pv
nfs-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dep1
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: wwwroot
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
volumes:
- name: wwwroot
nfs:
server: 192.168.1.14
path: /data/nfs

kubectl apply -f nfs-nginx.yaml

kubectl get pods

如果nfs开启的是匿名用户权限,补充(可忽略)
node节点授权
vim /etc/kubernetes/kubelet.conf

在master节点上,添加认证用户,直接使用下列命令实现
kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
进入pod进行查看是否挂载了nfs的共享目录
kubectl get pods

kubectl describe pod nginx-dep1-64d699cc8c-d4l8s

kubectl exec -it nginx-dep1-64d699cc8c-d4l8s bash
cd /usr/share/nginx/html/;ls

暴露端口并测试
kubectl expose deployment nginx-dep1 --port=80 --target-port=80 --type=NodePort
kubectl get svc

修改nfs挂载目录文件(14端操作)
echo successful > /data/nfs/index.html

PV,PVC
存储分类:
文件存储:一些数据可能需要被多个节点使用,比如用户的头像、用户上传的文件等,实现方式:NFS、NAS、FTP、CephFS等。
块存储:一些数据只能被一个节点使用,或者是需要将一块裸盘整个挂载使用,比如数据库、Redis等,实现方式:Ceph、GlusterFS、公有云。
对象存储:由程序代码直接实现的一种存储方式,无需挂载,云原生应用无状态化常用的实现方式,实现方式:一般是符合S3协议的云存储,比如AWS的S3存储、Minio、七牛云等(IO、吞吐量性能可能相对差一些)。
概念:

PersistentVolume(PV)
是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统
PersistentVolumeClaim(PVC)
是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)
静态 pv
集群管理员创建一些 PV。它们带有可供群集用户使用的实际存储的细节。它们存在于 Kubernetes API 中,可用于消费
动态pv
当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses:PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 "" 可以有效地禁用其动态配置
要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器] 。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作
PV 绑定到 PVC
master 中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定在一起。如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦 PV 和 PVC 绑定后,PersistentVolumeClaim 绑定是排他性的,不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射
持久化卷声明的保护
PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失(当 pod 状态为 Pending 并且 pod 已经分配给节点或 pod 为 Running 状态时,PVC 处于活动状态)
当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用
持久化卷类型
PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:
- GCEPersistentDisk AWSElasticBlockStore AzureFile AzureDisk FC (Fibre Channel)
- FlexVolume Flocker NFS iSCSI RBD (Ceph Block Device) CephFS
- Cinder (OpenStack block storage) Glusterfs VsphereVolume Quobyte Volumes
- HostPath VMware Photon Portworx Volumes ScaleIO Volumes StorageOS
访问模式限制:

回收策略(可以通过persistentVolumeReclaimPolicy: Recycle字段配置)
- Retain(保留)——手动回收
- Recycle(回收)——基本擦除(
rm -rf /thevolume/*) - Delete(删除)——删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete,目前支持Delete的存储后端包括AWS EBS, GCE PD, Azure Disk, or OpenStack Cinder等
当前,只有 NFS(旧版) 和 HostPath 支持回收策略(这种策略没啥优点)。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略(省钱)
PV的几种状态
- Available:可用,没有被PVC绑定的空闲资源。
- Bound:已绑定,已经被PVC绑定。
- Released:已释放,PVC被删除,但是资源还未被重新使用。
- Failed:失败,自动回收失败。
本实验将会讲解并验证StatefulSet相关知识点:
匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2
statefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:$(service name).$(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Pod name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv
Statefulset的启停顺序
有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态
有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0
有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态
StatefulSet使用场景
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现
稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变
有序部署,有序扩展,基于 init containers 来实现
有序收缩
PV和PVC实现过程

nfs服务器如果一旦变化,所有nodeIP都要发生改变,实际应用不方便,如果直接调用接口则可以简化工作量

实现流程

生产环境具体情况:

可以实现应用通过pvc绑定pv,以及通过存储容量和匹配模式进行绑定
实验一:nfs基于刚才的pv,pvc的升级操作
删除刚才的实验
kubectl delete -f nfs-nginx.yaml
首先了解一个PV全配置样例模版:
pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity: # 容量配置
storage: 5Gi
volumeMode: Filesystem
# volumeMode:卷的模式,目前支持Filesystem(文件系统) 和 Block(块),其中Block类型需要后端存储支持,默认为文件系统
accessModes: # accessModes:该PV的访问模式
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow # storageClassName:PV的类,一个特定类型的PV,只能绑定到特定类别的PVC
mountOptions: # 非必须,新版本中已弃用
- hard
- nfsvers=4.1
nfs: # nfs:NFS服务配置,包括1、path:NFS上的共享目录
2、server:NFS的IP地址
path: /tmp
server: 172.17.0.2
该PV的访问模式补充(https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reclaim-policy):
ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
ReadWriteMany(RWX):读写权限,可以被多个节点挂载

下面做自己的实验:
pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
nfs:
path: /data/nfs
server: 192.168.1.14

主要定义服务IP和路径,专业人士提供即可,如通过容量5G和读写模式的进行匹配
kubectl apply -f pv.yaml
pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-dep1
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: wwwroot
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
volumes:
- name: wwwroot
persistentVolumeClaim:
claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi

主要负责创建nginx并连接pvc,根据存储容量,读写匹配
kubectl apply -f pvc.yaml
kubectl get pv,pvc

kubectl get pods

进入容器
kubectl exec -it nginx-dep1-69f5bb95b-dzsrj bash
ls /usr/share/nginx/html/

实验二 基于statefuset+PV(nfs),PVC实现有状态服务:
安装 NFS 服务器
yum install -y nfs-common nfs-utils rpcbind
mkdir /nfs && cd /nfs
mkdir {1..10}
for i in {1..10};do echo $i > $i/index.html; done
chmod -R 777 /nfs
systemctl start nfs
systemctl enable rpcbind
systemctl enable nfs
vi /etc/exports

/nfs/1 *(rw,no_root_squash,no_all_squash,sync)
/nfs/2 *(rw,no_root_squash,no_all_squash,sync)
/nfs/3 *(rw,no_root_squash,no_all_squash,sync)
/nfs/4 *(rw,no_root_squash,no_all_squash,sync)
/nfs/5 *(rw,no_root_squash,no_all_squash,sync)
/nfs/6 *(rw,no_root_squash,no_all_squash,sync)
/nfs/7 *(rw,no_root_squash,no_all_squash,sync)
/nfs/8 *(rw,no_root_squash,no_all_squash,sync)
/nfs/9 *(rw,no_root_squash,no_all_squash,sync)
/nfs/10 *(rw,no_root_squash,no_all_squash,sync)
systemctl start rpcbind
systemctl restart nfs
所有node:
yum install -y nfs-common nfs-utils rpcbind
systemctl start nfs
systemctl enable rpcbind
systemctl enable nfs
测试可用性(nfs切记要关闭防火墙):
mount -t nfs 192.168.1.13:/nfs/1 /test
升级成PV(存储工程师去做):
vi nfspv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs/1
server: 192.168.1.13
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs/2
server: 192.168.1.13
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv3
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs1
nfs:
path: /nfs/3
server: 192.168.1.13
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv4
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs/4
server: 192.168.1.13
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv5
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs/5
server: 192.168.1.13
kubectl apply -f nfspv.yaml
kubectl get pv

创建服务并声明PVC:
myweb.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: wangyanglinux/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "nfs"
resources:
requests:
storage: 1Gi


statefuset特性:角标,顺序扩容缩
kubectl get pvc,pv

pv3 storageClassName不满足, pv4读写策略不满足
继续创建两个pv
vi nfspv2.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv6
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs/6
server: 192.168.1.13
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv7
spec:
capacity:
storage: 1.5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /nfs/7
server: 192.168.1.13
存储容量为2个g和1.5个g
kubectl get pv

预选和优选
kubectl edit statefulsets.apps web
修改 replicas: 4
kubectl get pv,pvc

5G空间的PV被剩下来,由此可知:
1:当请求策略不同时,PV和PVC不能匹配
2:当预选都满足时(预选所有条件必须都满足),则按照服务创建顺序进行优选(注意PVC从上到下排列顺序从最优->匹配)
验证网站搭建成功:

验证数据持久化:
输入一部分数据到pod中作为验证数据

验证稳定的网络标识:
kubectl get svc -n kube-system
dig -t A web-0.nginx.default.svc.cluster.local. @10.96.0.1

kubectl delete pod web-0
ingress固定了域名后端的访问

kubectl exec -it web-0 -- sh

测试PV的数据稳定性
只要PVC没有被删除,PV不会随着pod的删除而删除
kubectl delete statefulsets.apps web
删除之后PV依然绑定

重跑服务:
kubectl apply -f myweb.yaml
kubectl exec -it web-0 -- sh

如果pvc被删除,PV将会进入release状态;
kubectl delete -f myweb.yaml

首先先直接挂上来备份数据,然后可以选择edit方式将released变成为可用状态
处理方式1:
kubectl edit pv nfspv1
删除:

kubectl get pv

处理方式2:
批量删除pv,重新创建pv

其他PV配置
PV配置示例-HostPath
kind: PersistentVolume
apiVersion: v1
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: hostpath
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
PV配置示例-ceph
apiVersion: v1
kind: PersistentVolume
metadata:
name: ceph-rbd-pv
spec:
capacity:
storage: 1Gi
storageClassName: ceph-fast
accessModes:
- ReadWriteOnce
rbd:
monitors:
- 192.168.1.123:6789
- 192.168.1.124:6789
- 192.168.1.125:6789
pool: rbd
image: ceph-rbd-pv-test
user: admin
secretRef:
name: ceph-secret
fsType: ext4
readOnly: false
- monitors:Ceph的monitor节点的IP
- pool:所用Ceph Pool的名称,可以使用ceph osd pool ls查看
- image:Ceph块设备中的磁盘映像文件,可以使用rbd create POOL_NAME/IMAGE_NAME --size 1024创建,使用rbd list POOL_NAME查看
- user:Rados的用户名,默认是admin
- secretRef:用于验证Ceph身份的密钥
- fsType:文件类型,可以是ext4、XFS等
- readOnly:是否是只读挂载
PVC创建和挂载失败的原因
PVC一直Pending的原因:
- lPVC的空间申请大小大于PV的大小
- lPVC的StorageClassName和PV不一致
- lPVC的accessModes和PV的不一致
挂载PVC的Pod一直处于Pending(使用describe查看):
- PVC没有创建成功/PVC不存在
- PVC和Pod不在同一个Namespace
发布者:LJH,转发请注明出处:https://www.ljh.cool/8277.html