一、k8s的部署

1.1.集群环境初始化

1.1.1.所有主机禁用swap
[root@k8s- ~]# systemctl mask dev-nvme0n1p3.swap
[root@k8s- ~]# swapoff -a
[root@k8s- ~]# systemctl status dev-nvme0n1p3.swap
[root@k8s- ~]# vim /etc/fstab 内容:
注释swap
1.1.2.安装k8s部署工具
[root@k8s-master ~]# dnf install kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 -y#设置kubectl命令自动补全功能
[root@k8s-master ~]# dnf install bash-completion -y
[root@k8s-master ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@k8s-master ~]# source  ~/.bashrc
1.1.2.所有节点安装cri-docker
[root@k8s-master ~]# dnf install libcgroup-0.41-19.el8.x86_64.rpm \
> cri-dockerd-0.3.14-3.el8.x86_64.rpm -y[root@k8s-master ~]# vim /lib/systemd/system/cri-docker.service编辑内容:#指定网络插件名称及基础容器镜像
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --pod-infra-container-image=reg.timinglee.org/k8s/pause:3.9[root@k8s-master ~]# systemctl daemon-reload
[root@k8s-master ~]# systemctl start cri-docker
[root@k8s-master ~]# ll /var/run/cri-dockerd.sock

将该配置文件拷贝给其他两台主机:

[root@k8s-master ~]# scp /lib/systemd/system/cri-docker.service root:/172.25.254.10:/lib/systemd/system/cri-docker.service
[root@k8s-master ~]# scp /lib/systemd/system/cri-docker.service root:/172.25.254.20:/lib/systemd/system/cri-docker.service
1.1.3.在master节点拉取K8S所需镜像
#拉取k8s集群所需要的镜像
[root@k8s-master ~]# kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.30.0 --cri-socket=unix:///var/run/cri-dockerd.sock​#上传镜像到harbor仓库
[root@k8s-master ~]# docker images | awk '/google/{ print $1":"$2}' \| awk -F "/" '{system("docker tag "$0" reg.gaoyingjie.org/k8s/"$3)}'​[root@k8s-master ~]# docker images  | awk '/k8s/{system("docker push "$1":"$2)}'  

1.1.4.集群初始化
[root@k8s-master ~]# systemctl enable --now  kubelet.service
[root@k8s-master ~]# systemctl status kubelet.service#执行初始化命令
[root@k8s-master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 \
--image-repository reg.gaoyingjie.org/k8s \
--kubernetes-version v1.30.0 \
--cri-socket=unix:///var/run/cri-dockerd.sock#指定集群配置文件变量[root@k8s-master ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile​
#当前节点没有就绪,因为还没有安装网络插件,容器没有运行
[root@k8s-master ~]# kubectl get node[root@k8s-master ~]# kubectl get pod -A
1.1.5.其他两台主机加入集群
[root@k8s-master ~]#   kubeadm token create --print-join-command kubeadm join 172.25.254.100:6443 --token 5hwptm.zwn7epa6pvatbpwf --discovery-token-ca-cert-hash 
[root@k8s-node1 ~]# kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock
[root@k8s-node1 ~]# kubeadm join 172.25.254.100:6443 --token baujlw.w4xhwevafprh8uk9 --discovery-token-ca-cert-hash sha256:f05eb014ffdee15265806a1bc7a54270d8b28cccf90b88cb2b2910fe3aaab05f --cri-socket=unix:///var/run/cri-dockerd.sock[root@k8s-node2 ~]# kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock
[root@k8s-node2 ~]# kubeadm join 172.25.254.100:6443 --token baujlw.w4xhwevafprh8uk9 --discovery-token-ca-cert-hash sha256:f05eb014ffdee15265806a1bc7a54270d8b28cccf90b88cb2b2910fe3aaab05f --cri-socket=unix:///var/run/cri-dockerd.sock
1.1.6.安装flannel网络插件
#注意关闭防火墙,并且seinux的状态都是disabled#现在镜像:
[root@k8s-master ~]# docker pull docker.io/flannel/flannel:v0.25.5
[root@k8s-master ~]# docker pull docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel1​#上传镜像到仓库[root@k8s-master ~]# docker tag flannel/flannel:v0.25.5 reg.gaoyingjie.org/flannel/flannel:v0.25.5
[root@k8s-master ~]# docker push reg.gaoyingjie.org/flannel/flannel:v0.25.5​#编辑kube-flannel.yml 修改镜像下载位置
[root@k8s-master ~]# vim kube-flannel.yml#需要修改以下几行
[root@k8s-master ~]# grep -n image kube-flannel.yml
146:        image: reg.timinglee.org/flannel/flannel:v0.25.5
173:        image: reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
184:        image: reg.timinglee.org/flannel/flannel:v0.25.5#安装flannel网络插件
[root@k8s-master ~]# kubectl apply -f kube-flannel.yml
1.1.7.查看所有node的状态

都是ready说明k8s集群已经完成。

[root@k8s-master ~]# kubectl get nodes
NAME                       STATUS   ROLES           AGE   VERSION
k8s-master.timinglee.org   Ready    control-plane   98m   v1.30.0
k8s-node1.timinglee.org    Ready    <none>          21m   v1.30.0
k8s-node2.timinglee.org    Ready    <none>          21m   v1.30.0

三、pod的管理和优化

3、pod的生命周期

(1)、INIT容器

init容器可以独立于Pod应用容器运行,init容器必须在应用容器启动前完成,它可以检测某个自定义任务是否完成,若没有完成,则会一直检测直到完成为止,若该任务完成后,init容器则会启动,当init容器启动后应用容器才会并行启动。也就是说,因此 Init 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动

[root@k8s-master ~]# vim pod.yml
[root@k8s-master ~]# kubectl apply -f pod.yml
pod/initpod created
[root@k8s-master ~]# kubectl get pods
NAME      READY   STATUS     RESTARTS      AGE
initpod   0/1     Init:0/1   0             43s     #可以看到status没有running
nginx1    1/1     Running    1 (40h ago)   2d
[root@k8s-master ~]# kubectl logs pod/initpod init-myservice 
wating for myservice
wating for myservice
wating for myservice
wating for myservice
wating for myservice
wating for myservice
wating for myservice
wating for myservice
[root@k8s-master ~]# kubectl exec -it pods/initpod -c init-myservice -- /bin/sh
/ # 
/ # touch testdfile
/ # ls
bin        etc        lib        proc       sys        tmp        var
dev        home       lib64      root       testdfile  usr
/ # exit
[root@k8s-master ~]# kubectl get pods
NAME      READY   STATUS     RESTARTS      AGE
initpod   0/1     Init:0/1   0             2m47s     #建立文件后init容器已经running
nginx1    1/1     Running    1 (40h ago)   2d

2、探针

(1)、存活探针

配置文件内容:

apiVersion: v1
kind: Pod
metadata:labels:name: initpodname: initpod
spec:containers:- image: myapp:v1name: myapplivenessProbe:tcpSocket:                ##检测端口存在性port: 8080initalDelaySeconds: 3        #容器启动后要等待多少秒后就探针开始工作,默认是 0periodSeconds: 1             #执行探测的时间间隔,默认为 10s 如果监测成功,每1s检测一次timeoutSeconds: 1            #探针执行检测请求后,如果检测失败,等待响应的超时时间,默认为1s

四、k8s的控制器

1、replicaset

  • ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行

  • 虽然 ReplicaSets 可以独立使用,但今天它主要被Deployments 用作协调 Pod 创建、删除和更新的机制

#生成yml文件
[root@k8s-master ~]# kubectl create deployment replicaset --image myapp:v1 --dry-run=client -o yaml > replicaset.yml​
apiVersion: apps/v1
kind: ReplicaSet
metadata:name: replicaset		#指定pod名称,一定小写,如果出现大写报错
spec:replicas: 2			#指定维护pod数量为2selector:				#指定检测匹配方式matchLabels:		#指定匹配方式为匹配标签app: myapp		#指定匹配的标签为app=myapptemplate:				#模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: myappspec:containers:- image: myapp:v1name: myapp

2、deployment控制器

  • Deployment控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod

  • Deployment管理ReplicaSet,ReplicaSet管理Pod

  • 主要用来:创建Pod和ReplicaSet、滚动更新和回滚、扩容和缩容、暂停与恢复

#生成yaml文件
[root@k8s-master ~]# kubectl create deployment deployment --image myapp:v1  --dry-run=client -o yaml > deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:name: deployment
spec:replicas: 4selector:matchLabels:app: myapptemplate:metadata:labels:app: myappspec:containers:- image: myapp:v1name: myapp

更新镜像和修改资源没有触发更新

#恢复后开始触发更新
[root@k8s2 pod]# kubectl rollout resume deployment deployment-example[root@k8s2 pod]# kubectl rollout history  deployment deployment-example
deployment.apps/deployment-example
REVISION  CHANGE-CAUSE
3         <none>
4         <none>
5         <none>

3、daemonset控制器

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时, 也会为他们新增一个 Pod ,当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod

DaemonSet 的典型用法:

在每个节点上运行集群存储 DaemonSet,例如 glusterd、ceph。

在每个节点上运行日志收集 DaemonSet,例如 fluentd、logstash。

在每个节点上运行监控 DaemonSet,例如 Prometheus Node Exporter、zabbix agent等

一个简单的用法是在所有的节点上都启动一个 DaemonSet,将被作为每种类型的 daemon 使用

一个稍微复杂的用法是单独对每种 daemon 类型使用多个 DaemonSet,但具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求

[root@k8s2 pod]# cat daemonset-example.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:name: daemonset-example
spec:selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:tolerations:		#对于污点节点的容忍- effect: NoScheduleoperator: Existscontainers:- name: nginximage: nginx[root@k8s-master ~]# kubectl get pods  -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
daemonset-87h6s   1/1     Running   0          47s   10.244.0.8    k8s-master   <none>           <none>
daemonset-n4vs4   1/1     Running   0          47s   10.244.2.38   k8s-node2    <none>           <none>
daemonset-vhxmq   1/1     Running   0          47s   10.244.1.40   k8s-node1    <none>           <none>

4、job控制器

Job,主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务。

Job特点如下:

  • 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量

  • 当成功结束的pod达到指定的数量时,Job将完成执行

前提:上传perl镜像到harbor:用来计算。

[root@k8s-master ~]# kubectl create job testjob --image perl:5.34.0 --dry-run=client -o yaml > job.yml

配置文件内容

apiVersion: batch/v1
kind: Job
metadata:creationTimestamp: nullname: testjob
spec:completions: 6        #一共完成任务数为6parallelism: 2        #每次并行完成2个backoffLimit: 4        #运行失败后尝试4重新运行template:spec:containers:- image: perl:5.34.0name: testjobcommand: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]restartPolicy: Never        #关闭后不自动重启

5、cronjob控制器

  • Cron Job 创建基于时间调度的 Jobs。

  • CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象。

  • CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。

  • CronJob可以在特定的时间点(反复的)去运行job任务。

    生成cronjob的yml文件
    [root@k8s-master ~]# kubectl create cronjob testcronjob --image busyboxplus:latest --schedule="* * * * *" --restart Never --dry-run=client -o yaml > cronjob.yml
    apiVersion: batch/v1
    kind: CronJob
    metadata:creationTimestamp: nullname: testcronjob
    spec:schedule: '* * * * *'jobTemplate:spec:template:spec:containers:- image: busyboxplus:latestname: testcronjobcommand: ["/bin/sh","-c","date;echo Hello from the Kubernetes cluster"]restartPolicy: Never

    五、k8s的微服务

  • Service是一组提供相同服务的Pod对外开放的接口。

  • 借助Service,应用可以实现服务发现和负载均衡。

  • service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)

  • 5.1、ipvs模式 

  • Service 是由 kube-proxy 组件,加上 iptables 来共同实现的

    kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源

    IPVS模式的service,可以使K8s集群支持更多量级的Pod

    简而言之:微服务默认使用iptables调度,意味着增减pod时,策略都需要刷新,大量消耗资源,所以使用ipvs模式。

    (1)、安装ipvsadm

    [root@k8s-所有节点 ]yum install ipvsadm –y

    (2)、修改master节点的代理配置

    [root@k8s-master ~]# kubectl -n kube-system edit cm kube-proxymetricsBindAddress: ""mode: "ipvs"							#设置kube-proxy使用ipvs模式nftables:

     (3)、重启pod

    在pod运行时配置文件中采用默认配置,当改变配置文件后已经运行的pod状态不会变化,所以要重启pod。

    [root@k8s-master ~]# kubectl -n kube-system get  pods   | awk '/kube-proxy/{system("kubectl -n kube-system delete pods "$1)}'[root@k8s-master ~]# ipvsadm -Ln
    [root@k8s-master services]# kubectl get svc gaoyingjie 
    NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    gaoyingjie   ClusterIP   10.108.100.16   <none>        80/TCP    43m

    5.2、建立微服务

    (1)、ClusterIP
  • clusterip模式只能在集群内访问,并对集群内的pod提供健康检测和自动发现功能
    根据标签将pod的ip加入Endpoints中,通过services的ip在集群内部访问,若标签不一致,则该pod会被驱逐出Endpoints。
    也就是所有pod的IP直接解析到services的虚拟ip上,services的ip是由ipvs模式下kube-proxy生成的kube-ipvs0网卡分配的。

    #clusterip类型
    [root@k8s-master services]#  kubectl create deployment gaoyingjie --image myapp:v1  --replicas 2 --dry-run=client -o yaml > services.yaml
    [root@k8s-master services]#  kubectl expose deployment timinglee --port 80 --target-port 80 --dry-run=client -o yaml >> services.yaml[root@k8s-master services]# vim services.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:labels:app: gaoyingjiename: gaoyingjie
    spec:replicas: 2selector:matchLabels:app: gaoyingjietemplate:metadata:labels:app: gaoyingjiespec:containers:- image: myapp:v1name: myapp---apiVersion: v1
    kind: Service
    metadata:labels:app: gaoyingjiename: gaoyingjie
    spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: gaoyingjie            #标签type:ClusterIP[root@k8s-master services]# kubectl apply -f services.yaml 
    deployment.apps/gaoyingjie created
    service/gaoyingjie created[root@k8s-master services]# kubectl get svc
    NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    gaoyingjie   ClusterIP   10.108.100.16   <none>        80/TCP    9s
    kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   59d
    nginx-svc    ClusterIP   None            <none>        80/TCP    3d18h[root@k8s-master services]# kubectl get pods --show-labels 
    NAME                          READY   STATUS    RESTARTS   AGE   LABELS
    gaoyingjie-74f5f4bf55-56t47   1/1     Running   0          53m   app=gaoyingjie,pod-template-hash=74f5f4bf55
    gaoyingjie-74f5f4bf55-vrh5w   1/1     Running   0          53m   app=gaoyingjie,pod-template-hash=74f5f4bf55

    自动发现是指当有新的pod创建时,会自动识别pod的标签,若符合标签就将该pod的ip加入到endpoints中,进行ipvs调度,示例如下:

    #新建pod名为testpod
    [root@k8s-master services]# kubectl run testpod --image myapp:v1 
    pod/testpod created#查看新pod的ip 是10.244.36.121
    [root@k8s-master services]# kubectl get pods -o wide 
    NAME                          READY   STATUS    RESTARTS   AGE    IP               NODE        NOMINATED NODE   READINESS GATES
    gaoyingjie-74f5f4bf55-56t47   1/1     Running   0          64m    10.244.36.120    k8s-node1   <none>           <none>
    gaoyingjie-74f5f4bf55-vrh5w   1/1     Running   0          64m    10.244.169.150   k8s-node2   <none>           <none>
    testpod                       1/1     Running   0          105s   10.244.36.121    k8s-node1   <none>           <none>#查看pod的标签,此时testpod的标签与其他两个不一样
    [root@k8s-master services]# kubectl get pods --show-labels 
    NAME                          READY   STATUS    RESTARTS   AGE   LABELS
    gaoyingjie-74f5f4bf55-56t47   1/1     Running   0          63m   app=gaoyingjie,pod-template-hash=74f5f4bf55
    gaoyingjie-74f5f4bf55-vrh5w   1/1     Running   0          63m   app=gaoyingjie,pod-template-hash=74f5f4bf55
    testpod                       1/1     Running   0          21s   run=testpod#更改testpod标签
    [root@k8s-master services]# kubectl label pod testpod app=gaoyingjie --overwrite
    pod/testpod labeled#查看testpod的标签
    [root@k8s-master services]# kubectl get pods --show-labels 
    NAME                          READY   STATUS    RESTARTS   AGE   LABELS
    gaoyingjie-74f5f4bf55-56t47   1/1     Running   0          64m   app=gaoyingjie,pod-template-hash=74f5f4bf55
    gaoyingjie-74f5f4bf55-vrh5w   1/1     Running   0          64m   app=gaoyingjie,pod-template-hash=74f5f4bf55
    testpod                       1/1     Running   0          79s   app=gaoyingjie,run=testpod#查看services的endpoints,可以看到testpod的ip:10.244.121已经加入endpoints
    [root@k8s-master services]# kubectl describe svc gaoyingjie 
    Name:              gaoyingjie
    Namespace:         default
    Labels:            app=gaoyingjie
    Annotations:       <none>
    Selector:          app=gaoyingjie
    Type:              ClusterIP
    IP Family Policy:  SingleStack
    IP Families:       IPv4
    IP:                10.108.100.16
    IPs:               10.108.100.16
    Port:              <unset>  80/TCP
    TargetPort:        80/TCP
    Endpoints:         10.244.169.150:80,10.244.36.120:80,10.244.36.121:80
    Session Affinity:  None
    Events:            <none>

    services使用集群自带 DNS解析,因为当services重新启动时候ip会改变,所以集群内部的沟通都是通过域名进行的,如下:

    #查看dns
    [root@k8s-master services]# kubectl -n kube-system get svc
    NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
    calico-typha   ClusterIP   10.98.254.47   <none>        5473/TCP                 52d
    kube-dns       ClusterIP   10.96.0.10     <none>        53/UDP,53/TCP,9153/TCP   59d#service创建后集群DNS提供解析
    [root@k8s-master services]# dig gaoyingjie.default.svc.cluster.local @10.96.0.10
    ......
    ;; ANSWER SECTION:                                                #解析到clusterip上
    gaoyingjie.default.svc.cluster.local. 30 IN A	10.108.100.16     #将10.108.100.16 解析为gaoyingjie.default.svc.cluster.local. 
    ......#进入集群内部
    [root@k8s-master services]# kubectl run busybox --image busyboxplus:latest
    [root@k8s-master services]# kubectl exec -it pods/busybox -- /bin/sh
    / # curl gaoyingjie.default.svc.cluster.local.
    Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
    / # nslookup gaoyingjie.default.svc.cluster.local.
    Server:    10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    Name:      gaoyingjie
    Address 1: 10.108.100.16  gaoyingjie.default.svc.cluster.local  #也可以看到DNS解析到clusterip上#可以看到用域名gaoyingjie.default.svc.cluster.local.进行访问

    ClusterIP 的特殊模式-headless
    原有模式是当进行交流时,DNS通过services名称先解析到services的cluster IP,然后services用ipvs对所代理的pod进行路由与负载均衡。

    headless无头服务,kube-proxy并不会给services分配 Cluster IP(services的ip), 而且平台也不会为它们进行负载均衡和路由,集群访问通过dns解析直接指向到业务pod上的IP,所有的调度有dns单独完成。相当于把后端pod的ip直接暴露出来。

    如果后端的pod重建后名称和ip发生变化后,DNS如何解析到每个pod的ip上?请看后续statefulset控制器。

    [root@k8s-master services]# vim headless.yml
    [root@k8s-master services]# kubectl apply -f headless.yml #可以看到clusterips是none
    [root@k8s-master services]# kubectl get svc -o wide
    NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE     SELECTOR
    gaoyingjie   ClusterIP   None         <none>        80/TCP    20s     app=gaoyingjie
    kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   59d     <none>[root@k8s-master services]# kubectl describe svc gaoyingjie 
    Name:              gaoyingjie
    Namespace:         default
    Labels:            app=gaoyingjie
    Annotations:       <none>
    Selector:          app=gaoyingjie
    Type:              ClusterIP
    IP Family Policy:  SingleStack
    IP Families:       IPv4
    IP:                None                            #clusterips是<none>
    IPs:               None
    Port:              <unset>  80/TCP
    TargetPort:        80/TCP
    Endpoints:         10.244.169.153:80,10.244.36.122:80
    Session Affinity:  None
    Events:            <none>#查看pod的ip
    [root@k8s-master services]# kubectl get pods -o wide
    NAME                          READY   STATUS    RESTARTS   AGE    IP               NODE        NOMINATED NODE   READINESS GATES
    busybox                       1/1     Running   0          141m   10.244.169.152   k8s-node2   <none>           <none>
    gaoyingjie-74f5f4bf55-g2nb2   1/1     Running   0          7s     10.244.36.122    k8s-node1   <none>           <none>
    gaoyingjie-74f5f4bf55-hlqxv   1/1     Running   0          7s     10.244.169.153   k8s-node2   <none>           <none>#查看
    [root@k8s-master services]# dig  gaoyingjie.default.svc.cluster.local @10.96.0.10
    ......
    ;; ANSWER SECTION:
    gaoyingjie.default.svc.cluster.local. 30 IN A	10.244.169.153    #直接解析到pod上
    gaoyingjie.default.svc.cluster.local. 30 IN A	10.244.36.122
    ......#开启容器进入集群内部,查看services,也可以看到直接解析到pod的ip上
    / # nslookup gaoyingjie
    Server:    10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    Name:      gaoyingjie
    Address 1: 10.244.36.122 10-244-36-122.gaoyingjie.default.svc.cluster.local    #解析到pod的ip上
    Address 2: 10.244.169.153 10-244-169-153.gaoyingjie.default.svc.cluster.local
    (2)、NodePoints

    通过ipvs暴漏端口从而使外部主机通过master节点的对外ip:<port>来访问pod业务,也就是说,将services的clusterip暴露出一个端口供其他主机使用。

     
  •  
    #nodeport类型
    [root@k8s-master services]# vim nodeport.yml
    [root@k8s-master services]# kubectl apply -f nodeport.yml #可以看出暴露出的端口是31998
    [root@k8s-master services]# kubectl get svc -o wide 
    NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR
    gaoyingjie   NodePort    10.111.32.111   <none>        80:31998/TCP   24s   app=gaoyingjie
    kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        60d   <none>#在另一台主机上访问该微服务
    [root@docker-harbor harbor]# curl 172.25.254.100:31998
    Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

    5.3、Ingress-nginx

  • 前情提要:loadbalancer类型中,裸金属主机中用metalLB为每个services分配外部ip,而在真实的业务中,对外ip是是需要收费的,若每个services都需要一个对外ip则会花费昂贵,所以就产生了ingress-nginx控制器。

    service只支持四层负载,也就是对ip端口的处理,不能对请求业务、动静分离等进行处理。

    ingress-nginx支持七层负载,支持动静分离。

    一种全局的、为了代理不同后端 Service 而设置的负载均衡服务,支持7层

    Ingress由两部分组成:Ingress controller和Ingress服务

    Ingress Controller 会根据你定义的 Ingress 对象,提供对应的代理能力。

    业界常用的各种反向代理项目,比如 Nginx、HAProxy、Envoy、Traefik 等,都已经为Kubernetes 专门维护了对应的 Ingress Controller。

    6.4.1 部署ingress

    下载部署文件(自备文件)

    上传ingress所需镜像到harbor

    [root@yifanhu ~]# docker tag reg.timinglee.org/ingress-nginx/controller:v1.11.2 reg.timinglee.org/ingress-nginx/controller:v1.11.2
    [root@yifanhu ~]# docker tag reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.4.3 reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.4.3
    [root@yifanhu ~]# docker push reg.timinglee.org/ingress-nginx/controller:v1.11.2
    The push refers to repository [reg.timinglee.org/ingress-nginx/controller]
    [root@yifanhu ~]# docker push reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.4.3
    The push refers to repository [reg.timinglee.org/ingress-nginx/kube-webhook-certgen]

安装ingress

[root@yifanhu ~]# kubectl -n ingress-nginx get pods
NAME                                       READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-r4mq7       0/1     Completed   0          11s
ingress-nginx-admission-patch-72xsg        0/1     Completed   1          11s
ingress-nginx-controller-bb7d8f97c-jttq6   0/1     Running     0          11s
[root@yifanhu ~]# kubectl -n ingress-nginx get svc
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.97.52.210     <none>        80:30602/TCP,443:30149/TCP   30s
ingress-nginx-controller-admission   ClusterIP   10.100.179.101   <none>        443/TCP                      30s修改微服务为Loadbalancer
[root@k8s-master ~]# kubectl -n ingress-nginx edit svc ingress-nginx-controller
49   type: LoadBalancer
[root@yifanhu ~]# kubectl -n ingress-nginx get services
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.97.52.210     <pending>     80:30602/TCP,443:30149/TCP   2m51s
ingress-nginx-controller-admission   ClusterIP      10.100.179.101   <none>        443/TCP                      2m51s

测试ingress

#生成yaml文件
[root@k8s-master ~]# kubectl create ingress webcluster --rule '*/=timinglee-svc:80' --dry-run=client -o yaml > timinglee-ingress.yml[root@k8s-master ~]# vim timinglee-ingress.yml
aapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: test-ingress
spec:ingressClassName: nginxrules:- http:paths:- backend:service:name: timinglee-svcport:number: 80path: /pathType: Prefix	[root@yifanhu ~]# kubectl apply -f timinglee-ingress.yml
ingress.networking.k8s.io/test-ingress created
[root@yifanhu ~]# kubectl get ingress
NAME           CLASS   HOSTS   ADDRESS         PORTS   AGE
test-ingress   nginx   *       172.25.254.20   80      21m
[root@yifanhu ~]# kubectl -n ingress-nginx get svc
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.97.52.210     172.25.254.50   80:30602/TCP,443:30149/TCP   28m
ingress-nginx-controller-admission   ClusterIP      10.100.179.101   <none>          443/TCP [root@yifanhu ~]# for i in {1..5};do curl 172.25.254.50/hostname.html;done
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
(2)、Ingress基于路径的访问

根据访问路径的不同调度到不同的service上。

[root@k8s-master ingress]# vim ingress2.yml 
...
metadata:annotations:nginx.ingress.kubernetes.io/rewrite-target: /     #访问路径后加任何内容都被定向到/name: webcluster
#当外界访问www.gaoyingjie.org/v1时,实际是访问网页的首发目录,也就是/usr/share/nginx/html/v1,所以就导致找不到页面404报错,因此需要重定向到根目录。
...
[root@k8s-master ingress]# kubectl apply -f ingress2.yml 
ingress.networking.k8s.io/webcluster created
[root@k8s-master ingress]# kubectl get ingress
NAME         CLASS   HOSTS                ADDRESS   PORTS   AGE
webcluster   nginx   www.gaoyingjie.org             80      6s
[root@k8s-master ingress]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   62d
myapp-v1     ClusterIP   10.104.22.5      <none>        80/TCP    3h4m
myapp-v2     ClusterIP   10.104.144.174   <none>        80/TCP    3h4m#测试
[root@docker-harbor ~]# echo 172.25.254.50 www.gaoyingjie.org >> /etc/hosts
#当访问路径为v1时,ingress会调度到myapp-v1上(myapp-v1是一个services的名字),同理访问v2调度到2上
[root@docker-harbor ~]# curl www.gaoyingjie.org/v1
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@docker-harbor ~]# curl www.gaoyingjie.org/v2
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>#nginx.ingress.kubernetes.io/rewrite-target: / 的功能实现
[root@docker-harbor ~]# curl www.gaoyingjie.org/v2/bbb
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@docker-harbor ~]# curl www.gaoyingjie.org/v1/bbb
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
[root@docker-harbor ~]# curl www.gaoyingjie.org/v1/adavc
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
(3)、Ingress基于域名的访问

根据访问域名的不同调度到不同的service上。

[root@k8s-master ingress]# vim ingress3.yml [root@k8s-master ingress]# kubectl apply -f ingress3.yml 
ingress.networking.k8s.io/webcluster unchanged
[root@k8s-master ingress]# kubectl describe ingress webcluster 
Name:             webcluster
Labels:           <none>
Namespace:        default
Address:          172.25.254.10
Ingress Class:    nginx
Default backend:  <default>
Rules:Host                   Path  Backends----                   ----  --------myapp1.gaoyingjie.org  /   myapp-v1:80 (10.244.169.173:80,10.244.36.80:80)myapp2.gaoyingjie.org  /   myapp-v2:80 (10.244.169.181:80,10.244.36.81:80)
Annotations:             nginx.ingress.kubernetes.io/rewrite-target: /
Events:Type    Reason  Age                    From                      Message----    ------  ----                   ----                      -------Normal  Sync    3m26s (x2 over 3m55s)  nginx-ingress-controller  Scheduled for sync#测试
[root@docker-harbor ~]# echo 172.25.254.50 myapp1.gaoyingjie.org myapp2.gaoyingjie.org >> /etc/hosts
[root@docker-harbor ~]# curl myapp1.gaoyingjie.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@docker-harbor ~]# curl myapp2.gaoyingjie.org
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
 (4)、Ingress的tls加密访问
#生成证书和key
[root@k8s-master ingress]# openssl req -newkey rsa:2048 -nodes -keyout tls.key -x509 -days 365 -subj "/CN=nginxsvc/O=nginxsvc" -out tls.crt
[root@k8s-master ingress]# ls
deploy.yaml   ingress2.yml  ingress-nginx-1.11.2.tar.gz  myapp:v2.yml  tls.key
ingress1.yml  ingress3.yml  myapp:v1.yml                 tls.crt#将证书和key写入集群资源,secret是集群用来保存敏感资源的一种配置文件。
[root@k8s-master ingress]# kubectl create secret tls web-tls-secret --key tls.key --cert tls.crt 
secret/web-tls-secret created
[root@k8s-master ingress]# kubectl get secrets 
NAME             TYPE                             DATA   AGE
docker-auth      kubernetes.io/dockerconfigjson   1      49d
docker-login     kubernetes.io/dockerconfigjson   1      3d22h
web-tls-secret   kubernetes.io/tls                2      7s[root@k8s-master ingress]# vim ingress4.yml 
...
spec:tls:                            #https加密- hosts:- myapp-tls.gaoyingjie.org    #当访问该域名时要加httpssecretName: web-tls-secret    #读取secret配置文件ingressClassName: nginx
...
[root@k8s-master ingress]# kubectl apply -f ingress4.yml 
ingress.networking.k8s.io/webcluster created
[root@k8s-master ingress]# kubectl get ingress webcluster 
NAME         CLASS   HOSTS                      ADDRESS   PORTS     AGE
webcluster   nginx   myapp-tls.gaoyingjie.org             80, 443   12s#测试
[root@docker-harbor ~]# echo 172.25.254.50 myapp-tls.gaoyingjie.org >> /etc/hosts
[root@docker-harbor ~]# curl -k https://myapp-tls.gaoyingjie.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

六、k8s的存储

6.1、Configmap

(1)、congifmap的创建方式
[root@k8s-master storage]# kubectl create  configmap userlist  --from-literal name=gyj --from-literal age=23
configmap/userlist created
[root@k8s-master storage]# kubectl describe cm userlist 
Name:         userlist
Namespace:    default
Labels:       <none>
Annotations:  <none>Data
====
age:
----
23
name:
----
gyjBinaryData
====Events:  <none>
[root@k8s-master storage]# 
[root@k8s-master storage]# kubectl create cm configmap2 --from-file /etc/resolv.conf 
configmap/configmap2 created
[root@k8s-master storage]# kubectl describe cm configmap2 
Name:         configmap2
Namespace:    default
Labels:       <none>
Annotations:  <none>Data
====
resolv.conf:
----
# Generated by NetworkManager
nameserver 114.114.114.114BinaryData
====Events:  <none>
[root@k8s-master storage]# mkdir test
[root@k8s-master storage]# cp /etc/hosts /etc/rc.d/rc.local /root/storage/test/
[root@k8s-master storage]# kubectl create cm configmap2 --from-file test/
configmap/configmap2 created
[root@k8s-master storage]# kubectl describe cm configmap2 
Name:         configmap2
Namespace:    default
Labels:       <none>
Annotations:  <none>Data
====
hosts:
----
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.100  k8s-master
172.25.254.10   k8s-node1
172.25.254.20   k8s-node2
172.25.254.250  reg.gaoyingjie.orgrc.local:
----
#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.touch /var/lock/subsys/local
mount /dev/cdrom /rhel9/BinaryData
====Events:  <none>
[root@k8s-master storage]# kubectl create cm configmap3 --from-literal db_host=172.25.254.100 --from-literal db_port=3306 --dry-run=client -o yaml > configmap3.yaml
[root@k8s-master storage]# vim configmap3.yaml 
[root@k8s-master storage]# kubectl apply -f configmap3.yaml 
configmap/configmap3 created
[root@k8s-master storage]# kubectl describe configmaps configmap3 
Name:         configmap3
Namespace:    default
Labels:       <none>
Annotations:  <none>Data
====
db_port:
----
3306
db_host:
----
172.25.254.100BinaryData
====Events:  <none>
(2)、configmap的使用方式
  • 使用configmap 填充环境变量
    [root@k8s-master storage]# vim testpod1.yml 
    [root@k8s-master storage]# kubectl apply -f testpod1.yml 
    pod/configmap-pod created
    [root@k8s-master storage]# kubectl get pods
    NAME            READY   STATUS      RESTARTS   AGE
    configmap-pod   0/1     Completed   0          7s
    test            1/1     Running     0          5m51s
    [root@k8s-master storage]# kubectl logs pods/configmap-pod 
    KUBERNETES_SERVICE_PORT=443
    KUBERNETES_PORT=tcp://10.96.0.1:443
    HOSTNAME=configmap-pod
    SHLVL=1
    HOME=/
    KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    KUBERNETES_PORT_443_TCP_PORT=443
    KUBERNETES_PORT_443_TCP_PROTO=tcp
    key1=172.25.254.100
    key2=3306
    KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
    KUBERNETES_SERVICE_PORT_HTTPS=443
    PWD=/
    KUBERNETES_SERVICE_HOST=10.96.0.1
    [root@k8s-master storage]# 
    [root@k8s-master storage]# vim testpod1.yml apiVersion: v1
    kind: Pod
    metadata:labels:run: configmap-podname: configmap-pod
    spec:containers:- image: busyboxplus:latestname: configmap-podcommand:- /bin/sh- -c- envenvFrom:- configMapRef:name: configmap3restartPolicy: Never[root@k8s-master storage]# kubectl apply -f testpod1.yml 
    pod/configmap-pod created
    [root@k8s-master storage]# kubectl logs pods/configmap-pod 
    KUBERNETES_PORT=tcp://10.96.0.1:443
    KUBERNETES_SERVICE_PORT=443
    HOSTNAME=configmap-pod
    SHLVL=1
    HOME=/
    db_port=3306
    KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    KUBERNETES_PORT_443_TCP_PORT=443
    KUBERNETES_PORT_443_TCP_PROTO=tcp
    KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
    KUBERNETES_SERVICE_PORT_HTTPS=443
    PWD=/
    KUBERNETES_SERVICE_HOST=10.96.0.1
    db_host=172.25.254.100
    #在pod命令行中使用变量
    [root@k8s-master ~]# vim testpod3.yml
    apiVersion: v1
    kind: Pod
    metadata:labels:run: testpodname: testpod
    spec:containers:- image: busyboxplus:latestname: testpodcommand:- /bin/sh- -c- echo ${db_host} ${db_port}		#变量调用需envFrom:- configMapRef:name: lee4-configrestartPolicy: Never#查看日志
    [root@k8s-master ~]# kubectl logs pods/testpod
    172.25.254.100 3306

  • 通过数据卷使用configmap
  • 声明一个卷并挂载,把configmap里的键变为挂载目录下的文件名字、把值变为文件内容。

    [root@k8s-master storage]# vim testpod2.yml apiVersion: v1
    kind: Pod
    metadata:labels:run: testpodname: testpod
    spec:containers:- image: busyboxplus:latestname: testpodcommand:- /bin/sh- -c- sleep 1000000volumeMounts:- name: config-volumemountPath: /configvolumes:- name: config-volumeconfigMap:name: configmap3       #配置文件restartPolicy: Never

  • 利用configmap填充pod的配置文件

举例:当容器运行nginx时,nginx的配置文件可以放在configmap(通过文件创建configmap),修改配置时可以直接修改configmap文件内容,就不用修改每个pod的nginx配置文件

#建立配置文件模板
[root@k8s-master ~]# vim nginx.conf
server {listen 8000;server_name _;root /usr/share/nginx/html;index index.html;
}
#利用nging模板文件生成cm(通过文件创立cm)
root@k8s-master ~]# kubectl create cm nginx-conf --from-file nginx.conf     #通过nginx.conf 这个文件创建名为nginx-conf的cm
configmap/nginx-conf created
[root@k8s-master ~]# kubectl describe cm nginx-conf
Name:         nginx-conf
Namespace:    default
Labels:       <none>
Annotations:  <none>Data
====
nginx.conf:                           #该cm的键
----
server {                              #该cm的值listen 8000;server_name _;root /usr/share/nginx/html;index index.html;
}BinaryData
====Events:  <none>
#建立nginx控制器文件
[root@k8s-master ~]# kubectl create deployment nginx --image nginx:latest --replicas 1 --dry-run=client -o yaml > nginx.yml#设定nginx.yml中的卷
[root@k8s-master ~]# vim nginx.yml
[root@k8s-master ~]# cat nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginxname: nginx
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- image: nginx:latestname: nginxvolumeMounts:- name: config-volumemountPath: /etc/nginx/conf.d     #挂载目录,在该目录下cm的键成为文件名,值成为文件内容volumes:- name: config-volumeconfigMap:name: nginx-conf               #cm名字

6.2、secrets

Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 ssh key。

敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活

Pod 可以用两种方式使用 secret:

作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里。

当 kubelet 为 pod 拉取镜像时使用。

Secret的类型:

Service Account:Kubernetes 自动创建包含访问 API 凭据的 secret,并自动修改 pod 以使用此类型的 secret。

Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。

kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息

(1)、secrets的创建
[root@k8s-master secrets]# echo -n gaoyingjie > username.txt
[root@k8s-master secrets]# echo -n gyj > username.txt
[root@k8s-master secrets]# kubectl create secret generic userlist --from-file username.txt --from-file ^C
[root@k8s-master secrets]# echo -n gaoyingjie > username.txt
[root@k8s-master secrets]# echo -n gaoyingjie > password.txt
[root@k8s-master secrets]# kubectl create secret generic userlist --from-file username.txt --from-file password.txt 
secret/userlist created
[root@k8s-master secrets]# kubectl get secrets userlist -o yaml
apiVersion: v1
data:password.txt: Z2FveWluZ2ppZQ==           #username已加密username.txt: Z2FveWluZ2ppZQ==           #password已加密
kind: Secret
metadata:creationTimestamp: "2024-09-15T11:54:24Z"name: userlistnamespace: defaultresourceVersion: "200519"uid: 4a144911-0b46-4e1e-95b1-13b403cac262
type: Opaque
#将要存储的username和password用base64加密
[root@k8s-master secrets]# echo -n gaoyingjie | base64
Z2FveWluZ2ppZQ==
[root@k8s-master secrets]# echo -n gyj | base64
Z3lq#生成secrets配置文件
[root@k8s-master secrets]# kubectl create secret generic userlist --dry-run=client -o yaml > userlist.yml
[root@k8s-master secrets]# vim userlist.yml #配置文件内容
apiVersion: v1
kind: Secret
metadata:creationTimestamp: nullname: userlist
type: Opaque
data:username: Z2FveWluZ2ppZQ==password: Z3lq[root@k8s-master secrets]# kubectl apply -f userlist.yml 
secret/userlist created
[root@k8s-master secrets]# kubectl get secrets 
NAME           TYPE                             DATA   AGE
docker-login   kubernetes.io/dockerconfigjson   1      4d9h
userlist       Opaque                           2      10s
#查看
[root@k8s-master secrets]# kubectl describe secrets userlist 
Name:         userlist
Namespace:    default
Labels:       <none>
Annotations:  <none>Type:  OpaqueData
====
username:  10 bytes
password:  3 bytes[root@k8s-master secrets]# kubectl get secrets userlist -o yaml
apiVersion: v1
data:password: Z3lqusername: Z2FveWluZ2ppZQ==
kind: Secret
metadata:annotations:kubectl.kubernetes.io/last-applied-configuration: |{"apiVersion":"v1","data":{"password":"Z3lq","username":"Z2FveWluZ2ppZQ=="},"kind":"Secret","metadata":{"annotations":{},"creationTimestamp":null,"name":"userlist","namespace":"default"},"type":"Opaque"}creationTimestamp: "2024-09-15T12:02:45Z"name: userlistnamespace: defaultresourceVersion: "201277"uid: 3ef997b4-aed7-4b2a-b05d-15064df95bae
type: Opaque

将Secret挂载到Volume中

[root@yifanhu ~]# echo -n timinglee | base64
dGltaW5nbGVl
[root@yifanhu ~]# echo -n lee | base64
bGVl
[root@yifanhu ~]# kubectl create secret generic userlist --dry-run=client -o yaml > userlist.yml
[root@yifanhu ~]# vim userlist.yml
[root@yifanhu ~]# cat userlist.yml
apiVersion: v1
kind: Secret
metadata:creationTimestamp: nullname: userlist
type: Opaque
data:username: dGltaW5nbGVlpassword: bGVl
[root@yifanhu ~]# kubectl apply -f userlist.yml
Warning: resource secrets/userlist is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
secret/userlist configured
[root@yifanhu ~]# kubectl describe secrets userlist
Name:         userlist
Namespace:    default
Labels:       <none>
Annotations:  <none>Type:  OpaqueData
====
password:      3 bytes
password.txt:  3 bytes
username:      9 bytes
username.txt:  9 bytes

6.3、volumes配置管理

容器中文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题

当容器崩溃时,kubelet将重新启动容器,容器中的文件将会丢失,因为容器会以干净的状态重建。

当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。

Kubernetes 卷具有明确的生命周期与使用它的 Pod 相同

卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留

当一个 Pod 不再存在时,卷也将不再存在。

Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。

卷不能挂载到其他卷,也不能与其他卷有硬链接。 Pod 中的每个容器必须独立地指定每个卷的挂载位置。

(1)、emptyDir卷

功能:

当Pod指定到某个节点上时,首先创建的是一个emptyDir卷,并且只要 Pod 在该节点上运行,卷就一直存在。卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但是这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会永久删除

emptyDir 的使用场景:

缓存空间,例如基于磁盘的归并排序。

耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。

在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。

[root@k8s-master ~]# mkdir volumes
[root@k8s-master volumes]# vim pod1.yml
apiVersion: v1
kind: Pod
metadata:name: vol1    #pod的名字
spec:containers:- image: busyboxplus:latest        #开启的镜像name: vm1                        #名字command:                    - /bin/sh- -c- sleep 30000000volumeMounts:- mountPath: /cache            #挂载目录name: cache-vol            #指定的卷的名字- image: nginx:latestname: vm2volumeMounts:- mountPath: /usr/share/nginx/html        
#挂载目录,在该目录下存储的内容就是存储到cache-vol卷中,同样,在该目录下存储的内容,可以在busyboxplus的挂载目录/cache下可以看到。name: cache-volvolumes:                          #声明卷- name: cache-vol                 #卷的名字emptyDir:                       #卷的类型medium: Memory                #卷的设备:内存    sizeLimit: 100Mi              #大小[root@k8s-master volumes]# kubectl describe pod vol1
#进入容器查看
[root@k8s-master volumes]# kubectl exec -it pods/vol1 -c vm1 -- /bin/sh
/ # ls
bin      dev      home     lib64    media    opt      root     sbin     tmp      var
cache    etc      lib      linuxrc  mnt      proc     run      sys      usr
/ # cd cache/
/cache # echo gaoyingjie > index.html
/cache # ls
index.html
/cache # curl localhost        #basybox和nginx在一个pod内,共享网络栈,所以可以curl localhost
gaoyingjie
/cache # /cache # dd if=/dev/zero of=bigfile bs=1M count=99        #卷设置了100M 测试
99+0 records in
99+0 records out
/cache # dd if=/dev/zero of=bigfile bs=1M count=100
100+0 records in
99+1 records out
/cache # dd if=/dev/zero of=bigfile bs=1M count=101        #超过100则没有空间
dd: writing 'bigfile': No space left on device
101+0 records in
99+1 records out
/cache # 
(2)、hostpath卷

功能:hostPath 卷能将主机节点文件系统上的文件或目录挂载到您的 Pod 中,不会因为pod关闭而被删除。也就是将该pod的一些资源存在本机上。

hostPath 的一些用法

运行一个需要访问 Docker 引擎内部机制的容器,挂载 /var/lib/docker 路径。

在容器中运行 cAdvisor(监控) 时,以 hostPath 方式挂载 /sys。

允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在

hostPath的安全隐患

具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。

当 Kubernetes 按照计划添加资源感知的调度时,这类调度机制将无法考虑由 hostPath 使用的资源。

基础主机上创建的文件或目录只能由 root 用户写入。您需要在 特权容器 中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。

(3)、nfs卷

NFS 卷允许将一个现有的 NFS 服务器上的目录挂载到 Kubernetes 中的 Pod 中。这对于在多个 Pod 之间共享数据或持久化存储数据非常有用

例如,如果有多个容器需要访问相同的数据集,或者需要将容器中的数据持久保存到外部存储,NFS 卷可以提供一种方便的解决方案。

意义:用另外一台主机做存储,k8s集群和集群数据资源不在同一台主机上,实现了存储分离。

准备工作:

4台主机都要安装nfs,175.25.254.250是nfs服务器,其他主机是为了访问nfs服务器。

[root@docker-harbor ~]# dnf install nfs-utils -y
[root@docker-harbor rhel9]# systemctl enable --now nfs-server.service
[root@docker-harbor rhel9]# systemctl status nfs-server.service 
#编辑策略文件
[root@docker-harbor ~]# vim /etc/exports
/nfsdata   *(rw,sync,no_root_squash)
#rw:读写,sync:数据完完整性同步,async:实时性同步,no_root_squash:设置权限,欣慰nfsdata是需要root权限[root@docker-harbor ~]# exportfs -rv
exporting *:/nfsdata
#关闭火墙后查看,每台主机都能看到
[root@docker-harbor ~]# showmount -e 172.25.254.250
Export list for 172.25.254.250:
/nfsdata *
[root@k8s-master ~]# showmount -e 172.25.254.250
Export list for 172.25.254.250:
/nfsdata *

部署nfs卷:

[root@k8s-master volumes]# vim pod3.yml
apiVersion: v1
kind: Pod
metadata:name: vol1
spec:containers:- image: nginx:latestname: vm1volumeMounts:- mountPath: /usr/share/nginx/html        #卷挂载目录name: cache-volvolumes:                                    #声明卷- name: cache-vol                           #卷的名称nfs:                                      #使用的是nfs卷server: 172.25.254.250                  #nfs服务器的ip,就是挂载点ippath: /nfsdata                          #nfs共享出来的目录#pod开启后会将/usr/share/nginx/html下的内容挂载到nfs服务器的/nfsdata目录下[root@k8s-master volumes]# kubectl apply -f pod3.yml 
pod/vol1 created
[root@k8s-master volumes]# kubectl describe pod vol1 #nfsdata目录下还没有内容,所以访问nginx的首发目录是403
[root@k8s-master volumes]# curl 10.244.36.87
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
#进入nfsdata的目录下写入内容
[root@docker-harbor ~]# cd  /nfsdata/
[root@docker-harbor nfsdata]# echo gaoyingjie > index.html
[root@docker-harbor nfsdata]# ls
index.html#再次查看,可以看到网页内容了
[root@k8s-master volumes]# curl 10.244.36.87
gaoyingjie
(4)、持久卷

静态pv卷:

管理员手动建立pv,然后才能建立pvc,pod才能使用pvc申请pv。

[root@k8s-master pvc]# vim pv.yml 
apiVersion: v1
kind: PersistentVolume
metadata:name: pv1        #pv的名字
spec:                capacity:storage: 5Gi        #pv的大小volumeMode: Filesystem        #文件系统accessModes:- ReadWriteOnce        #访问模式:单点读写persistentVolumeReclaimPolicy: Retain      #回收模式:保留storageClassName: nfs        #存储类为nfsnfs:path: /nfsdata/pv1           #访问路径,将pv1的存储数据挂载到该目录下server: 172.25.254.250---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv2
spec:capacity:storage: 15GivolumeMode: FilesystemaccessModes:- ReadWriteMany            #多点读写persistentVolumeReclaimPolicy: RetainstorageClassName: nfsnfs:path: /nfsdata/pv2server: 172.25.254.250
---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv3
spec:capacity:storage: 25GivolumeMode: FilesystemaccessModes:- ReadOnlyMangy            #多点只读persistentVolumeReclaimPolicy: RetainstorageClassName: nfsnfs:path: /nfsdata/pv3server: 172.25.254.250[root@k8s-master pvc]# kubectl apply -f pv.yml 
persistentvolume/pv1 unchanged
persistentvolume/pv2 unchanged
persistentvolume/pv3 created
[root@k8s-master pvc]# kubectl get pv
\NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pv1    5Gi        RWO            Retain           Available           nfs            <unset>                          37s
pv2    15Gi       RWX            Retain           Available           nfs            <unset>                          37s
pv3    25Gi       ROX            Retain           Available           nfs            <unset>  
[root@k8s-master pvc]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc1               #名字可以随机,只看pvc的访问模式和请求大小这两个是否有匹配的pv
spec:storageClassName: nfsaccessModes:- ReadWriteOnce            #pv和pvc的方式必须一致resources:requests:storage: 1Gi             #pvc1的请求大小比pv小,不能超过pv的大小---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc2
spec:storageClassName: nfsaccessModes:- ReadWriteManyresources:requests:storage: 10Gi---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc3
spec:storageClassName: nfsaccessModes:- ReadOnlyManyresources:requests:storage: 15Gi[root@k8s-master pvc]# kubectl apply -f pvc.yml 
persistentvolumeclaim/pvc1 created
persistentvolumeclaim/pvc2 created
persistentvolumeclaim/pvc3 created
[root@k8s-master pvc]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
pvc1   Bound    pv1      5Gi        RWO            nfs            <unset>                 10s
pvc2   Bound    pv2      15Gi       RWX            nfs            <unset>                 10s
pvc3   Bound    pv3      25Gi       ROX            nfs            <unset>                 10s
[root@k8s-master pvc]# vim pod1.yml
apiVersion: v1
kind: Pod
metadata:name: gaoyingjie
spec:containers:- image: nginxname: nginxvolumeMounts:- mountPath: /usr/share/nginx/html      #pv1挂载到该目录下,该目录最终访问的就是pv1的内容name: vol1volumes:                #声明卷- name: vol1persistentVolumeClaim:       #持久卷请求,用请求使用卷pv1claimName: pvc1[root@k8s-master pvc]# kubectl apply -f pod1.yml 
pod/gaoyingjie created
[root@k8s-master pvc]# kubectl get pods 
NAME         READY   STATUS    RESTARTS   AGE
gaoyingjie   1/1     Running   0          10s
[root@k8s-master pvc]# kubectl describe pods gaoyingjie 
#pv1中没有内容时,访问不到
[root@k8s-master pvc]# kubectl get pods -o wide
NAME         READY   STATUS    RESTARTS   AGE    IP             NODE        NOMINATED NODE   READINESS GATES
gaoyingjie   1/1     Running   0          6m8s   10.244.36.88   k8s-node1   <none>           <none>
[root@k8s-master pvc]# curl 10.244.36.88 
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>#在pv1中写入内容
[root@docker-harbor nfsdata]# echo gaoyingjie > pv1/index.html
#可以看到写入的内容
[root@k8s-master pvc]# curl 10.244.36.88 
gaoyingjie
#也可以进入容器中,看到pv1的内容已挂载到/usr/share/nginx/html/目录下
[root@k8s-master pvc]# kubectl exec -it pods/gaoyingjie -- /bin/bash
root@gaoyingjie:/# cd /usr/share/nginx/html/
root@gaoyingjie:/usr/share/nginx/html# ls
index.html
root@gaoyingjie:/usr/share/nginx/html# cat index.html 
gaoyingjie

6.4、存储类

StorageClass提供了一种描述存储类(class)的方法,不同的class可能会映射到不同的服务质量等级和备份策略或其他策略等。

每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在StorageClass需要动态分配 PersistentVolume 时会使用到

动态pv:

创建pvc的时候会调用存储类去自动创建目录,如果没有指定存储类则调用默认存储类(前提是有默认存储类,没有则pvc状态显示pending)。简而言之,就是帮助pvc自动生成pv目录。

[root@k8s-master storageclass]# vim rbac.yml
apiVersion: v1
kind: Namespace
metadata:name: nfs-client-provisioner        #名命空间
---
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisionernamespace: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: nfs-client-provisioner-runner
rules:- apiGroups: [""]resources: ["nodes"]verbs: ["get", "list", "watch"]- apiGroups: [""]resources: ["persistentvolumes"]verbs: ["get", "list", "watch", "create", "delete"]- apiGroups: [""]resources: ["persistentvolumeclaims"]verbs: ["get", "list", "watch", "update"]- apiGroups: ["storage.k8s.io"]resources: ["storageclasses"]verbs: ["get", "list", "watch"]- apiGroups: [""]resources: ["events"]verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: run-nfs-client-provisioner
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: nfs-client-provisioner
roleRef:kind: ClusterRolename: nfs-client-provisioner-runnerapiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: leader-locking-nfs-client-provisionernamespace: nfs-client-provisioner
rules:- apiGroups: [""]resources: ["endpoints"]verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: leader-locking-nfs-client-provisionernamespace: nfs-client-provisioner
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: nfs-client-provisioner
roleRef:kind: Rolename: leader-locking-nfs-client-provisionerapiGroup: rbac.authorization.k8s.io[root@k8s-master storageclass]# kubectl apply -f rbac.yml 
namespace/nfs-client-provisioner created
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created#查看rbac信息
[root@k8s-master storageclass]# kubectl -n nfs-client-provisioner get sa
NAME                     SECRETS   AGE
default                  0         18s
nfs-client-provisioner   0         19s
(2)、存储分配器所用的控制器
[root@k8s-master storageclass]# vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisionernamespace: nfs-client-provisioner            #命名空间
spec:replicas: 1strategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisionerimage: sig-storage/nfs-subdir-external-provisioner:v4.0.2volumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: k8s-sigs.io/nfs-subdir-external-provisioner        #标签- name: NFS_SERVERvalue: 172.25.254.250- name: NFS_PATH                    #通过该变量识别nfs的地址和内容value: /nfsdatavolumes:- name: nfs-client-rootnfs:server: 172.25.254.250            #nfs服务器的ippath: /nfsdata                    #nfs的共享目录#上传镜像到镜像仓库
[root@docker-harbor ~]# docker load  -i nfs-subdir-external-provisioner-4.0.2.tar 
1a5ede0c966b: Loading layer  3.052MB/3.052MB
ad321585b8f5: Loading layer  42.02MB/42.02MB
Loaded image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2[root@docker-harbor ~]# docker tag registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 reg.gaoyingjie.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2[root@docker-harbor ~]# docker push reg.gaoyingjie.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2
The push refers to repository [reg.gaoyingjie.org/sig-storage/nfs-subdir-external-provisioner]
ad321585b8f5: Pushed 
1a5ede0c966b: Pushed 
v4.0.2: digest: sha256:f741e403b3ca161e784163de3ebde9190905fdbf7dfaa463620ab8f16c0f6423 size: 739[root@k8s-master storageclass]# kubectl apply -f deployment.yml 
[root@k8s-master storageclass]# kubectl get -n nfs-client-provisioner deployments.apps 
(3)、创建存储类
[root@k8s-master storageclass]# vim class.ymlapiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:archiveOnDelete: "false"         #若是“true”,当删除该类的时候,会对nfs存储目录进行打包,false则不会打包[root@k8s-master storageclass]# kubectl apply -f class.yml 
[root@k8s-master storageclass]# kubectl get -n nfs-client-provisioner storageclasses.storage.k8s.io
(4)、创建pvc
[root@k8s-master storageclass]# vim pvc.ymlkind: PersistentVolumeClaim
apiVersion: v1
metadata:name: test-claim
spec:storageClassName: nfs-clientaccessModes:- ReadWriteManyresources:requests:storage: 1G[root@k8s-master storageclass]# kubectl apply -f pvc.yml 
persistentvolumeclaim/test-claim created
[root@k8s-master storageclass]# kubectl get pvc
(5)、创建测试pod

将名为nfs-pvc的pv卷挂载到/mnt,名为test-claim的pvc调用存储类。存储类是动态的,它通过deployment控制器在nfs服务器端建立一个动态目录(类似default-test-claim-pvc-10b58d9a-c212-4f01-b5c3-b6428d42ba1b )。

[root@k8s-master storageclass]# vim pod.ymlkind: Pod
apiVersion: v1
metadata:name: test-pod
spec:containers:- name: test-podimage: busybox            #运行了busybox的容器command:- "/bin/sh"             #动作args:                     #参数- "-c"                    - "touch /mnt/SUCCESS && exit 0 || exit 1"    #会写到pvc所创建的目录中volumeMounts:- name: nfs-pvc        #定义pv卷的名称mountPath: "/mnt"restartPolicy: "Never"        #执行一次就退出,不会进行读写volumes:- name: nfs-pvc                  #最终会把名为nfs-pvc的pv卷挂载到/mnt,名为test-claim的pvc调用存储类。存储类是动态的,它通过deployment控制器建立一个动态目录(default-test-claim-pvc-10b58d9a-c212-4f01-b5c3-b6428d42ba1b )persistentVolumeClaim:claimName: test-claim        #使用的pvc名称为test-claim[root@k8s-master storageclass]# kubectl apply -f pod.yml 
pod/test-pod created[root@k8s-master storageclass]# kubectl get pods 
(6)、默认存储类
[root@k8s-master storageclass]# vim pvc.yml kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: test-claim
spec:#storageClassName: nfs-client 删除accessModes:- ReadWriteManyresources:requests:storage: 1G[root@k8s-master storageclass]# kubectl apply -f pvc.yml 
persistentvolumeclaim/test-claim created
[root@k8s-master storageclass]# kubectl get pvc
[root@k8s-master storageclass]# kubectl edit sc nfs-clientstorageclass.kubernetes.io/is-default-class: "true"[root@k8s-master storageclass]# kubectl get sc[root@k8s-master storageclass]# kubectl apply -f pvc.yml 
persistentvolumeclaim/test-claim created
[root@k8s-master storageclass]# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECL
test-claim   Bound    pvc-3b3c6e40-7b8b-4c41-811a-3e081af9866b   1G         RWX            nfs-clien

七、k8s的网络通信

7.1、flannel

当容器发送IP包,通过veth pair 发往cni网桥,再路由到本机的flannel.1设备进行处理。

VTEP设备之间通过二层数据帧进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个内部数据帧,发送给目的VTEP设备。

内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,承载着内部数据帧通过宿主机的eth0进行传输。

Linux会在内部数据帧前面,加上一个VXLAN头,VXLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。

flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的。

linux内核在IP包前面再加上二层数据帧头,把目标节点的MAC地址填进去,MAC地址从宿主机的ARP表获取。

此时flannel.1设备就可以把这个数据帧从eth0发出去,再经过宿主机网络来到目标节点的eth0设备。目标主机内核网络栈会发现这个数据帧有VXLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据帧,根据VNI的值,交给本机flannel.1设备处理,flannel.1拆包,根据路由表发往cni网桥,最后到达目标容器。

7.2、calico

calico网络插件官网:

Install Calico networking and network policy for on-premises deployments | Calico Documentation

将calico上传到私人镜像仓库:部署私人仓库请看Harbor部署docker私人仓库-CSDN博客

(1)、部署calico
[root@k8s-master network]# vim calico.yaml 
4835           image: calico/cni:v3.28.1
4835           image: calico/cni:v3.28.1
4906           image: calico/node:v3.28.1
4932           image: calico/node:v3.28.1
5160           image: calico/kube-controllers:v3.28.1
5249         - image: calico/typha:v3.28.14973             - name: CALICO_IPV4POOL_VXLAN
4974               value: "Never"4999             - name: CALICO_IPV4POOL_CIDR
5000               value: "10.244.0.0/16"
5001             - name: CALICO_AUTODETECTION_METHOD
5002               value: "interface=eth0"[root@k8s-master network]# kubectl apply -f calico.yaml
[root@k8s-master network]# kubectl -n kube-system get pods 

测试

[root@k8s-master calico]# kubectl run  web --image myapp:v1
pod/web created
[root@k8s-master calico]# kubectl get pods  -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
web    1/1     Running   0          5s    10.244.169.129   k8s-node2   <none>           <none>
[root@k8s-master calico]# curl  10.244.169.129
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

八、k8s的调度

  • 调度是指将未调度的Pod自动分配到集群中的节点的过程

  • 调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod

  • 调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行

    8.1、常用调度方法

    (1)、nodename
    [root@k8s-master scheduler]# kubectl run testpod --image myapp:v1 --dry-run=client -o yaml > nodename.yml
    [root@k8s-master scheduler]# kubectl apply -f nodename.yml 
    pod/testpod created
    [root@k8s-master scheduler]# kubectl get pods
    NAME      READY   STATUS    RESTARTS   AGE
    testpod   1/1     Running   0          3s[root@k8s-master scheduler]# kubectl get pods -o wide
    NAME      READY   STATUS    RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
    testpod   1/1     Running   0          6m14s   10.244.36.103   k8s-node1   <none>           <none>#testpod在node1节点上

    修改配置指定到node2上:

    [root@k8s-master scheduler]# vim nodename.yml 
    [root@k8s-master scheduler]# kubectl apply -f nodename.yml[root@k8s-master scheduler]# kubectl get pods -o wide
    NAME      READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
    testpod   1/1     Running   0          4s    10.244.169.167   k8s-node2   <none>           <none>#testpod在node2上
    (2)、Nodeselector

    通过标签控制节点调度,给节点添加标签,让调度器根据标签调度pod所在的节点,更加灵活。

  • 通过命令添加标签:
  • 通过配置文件进行调度设置
    [root@k8s-master scheduler]# vim nodeselector.yml[root@k8s-master scheduler]# kubectl apply -f nodeselector.yml 
    pod/testpod created
    [root@k8s-master scheduler]# kubectl get pod -o wide
    NAME      READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
    testpod   1/1     Running   0          3s    10.244.36.104   k8s-node1   <none>           <none>#testpod运行在了有disktype=ssd标签的node1节点上

    8.2、affinity亲和与反亲和

  • 上述两种调度算法只是简单的将pod约束到固定的节点上,亲和与反亲和扩展了想约束的类型
  • (1)、nodeAffinity-节点亲和
  • 那个节点服务指定条件就在那个节点运行

    requiredDuringSchedulingIgnoredDuringExecution 必须满足,但不会影响已经调度

    preferredDuringSchedulingIgnoredDuringExecution 倾向满足,在无法满足情况下也会调度pod,IgnoreDuringExecution 表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。

    匹配规则功能
    lnlabel 的值在列表内
    Notlnlabel 的值不在列表内
    Gtlabel 的值大于设置的值,不支持Pod亲和性
    Ltlabel 的值小于设置的值,不支持pod亲和性
    Exists设置的label 存在
    DoesNotExist设置的 label 不存在
[root@k8s-master scheduler]# vim nodeaffinity.yml
[root@k8s-master scheduler]# kubectl apply -f nodeaffinity.yml #因为配置文件的匹配规则是operator: IN 
#pod在有disktype=ssd标签的node1节点上
[root@k8s-master scheduler]# kubectl get pods -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
node-affinity   1/1     Running   0          12s   10.244.36.105   k8s-node1   <none>           <none>#如果匹配规则是operator: NotIn 
#则pod在没有disktype=ssd标签的node2节点上
[root@k8s-master scheduler]# kubectl get pods -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
node-affinity   1/1     Running   0          2s    10.244.169.171   k8s-node2   <none>           <none>

(2)、pod亲和与反亲和
pod之间的亲和性
podAffinity 主要解决POD可以和哪些POD部署在同一个节点中的问题

podAntiAffinity主要解决POD不能和哪些POD部署在同一个节点中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。

Pod 间亲和与反亲和在与更高级别的集合(例如 ReplicaSets,StatefulSets,Deployments 等)一起使用时

Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度。

podAffinity亲和示例:

[root@k8s-master scheduler]# vim podaffinity.yml
[root@k8s-master scheduler]# kubectl apply -f podaffinity.yml 
deployment.apps/nginx-deployment created#所有节点都在node1[root@k8s-master scheduler]# kubectl get pods -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
nginx-deployment-658496fff-72b4j   1/1     Running   0          20s   10.244.36.106   k8s-node1   <none>           <none>
nginx-deployment-658496fff-jb7ck   1/1     Running   0          20s   10.244.36.107   k8s-node1   <none>           <none>
nginx-deployment-658496fff-lnzkz   1/1     Running   0          20s   10.244.36.108   k8s-node1   <none>           <none>

podAntiAffinity反亲和示例:

[root@k8s-master scheduler]# kubectl apply -f podantiaffinity.yml 
deployment.apps/nginx-deployment created#因为配置文件设置的是反亲和podantiaffinity
#三个pod占据不同节点,只有两个节点,所哟最后一个pod状态为pendding
[root@k8s-master scheduler]# kubectl get pods -o wide 
NAME                                READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
nginx-deployment-5f5fc7b8b9-4s94m   1/1     Running   0          10s   10.244.36.109    k8s-node1   <none>           <none>
nginx-deployment-5f5fc7b8b9-cbpww   1/1     Running   0          10s   10.244.169.172   k8s-node2   <none>           <none>
nginx-deployment-5f5fc7b8b9-jzpqh   0/1     Pending   0          10s   <none>           <none>      <none>           <none>

九、k8s的认证授权

9.1、ServiceAccount

服务账户控制器(Service account controller)

服务账户管理器管理各命名空间下的服务账户

每个活跃的命名空间下存在一个名为 “default” 的服务账户

服务账户准入控制器(Service account admission controller)

相似pod中 ServiceAccount默认设为 default。

保证 pod 所关联的 ServiceAccount 存在,否则拒绝该 pod。

如果pod不包含ImagePullSecrets设置那么ServiceAccount中的ImagePullSecrets 被添加到pod中

将挂载于 /var/run/secrets/kubernetes.io/serviceaccount 的 volumeSource 添加到 pod 下的每个容器中

将一个包含用于 API 访问的 token 的 volume 添加到 pod 中


(1)、建立sa

#建立一个名为gaoyingjie的sa认证
[root@k8s-master ~]# kubectl create sa gaoyingjie#可以看到就默认的default,也有新建的名为gaoyingjie的sa
[root@k8s-master ~]# kubectl describe sa
Name:                default
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>Name:                gaoyingjie
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   <none>
Tokens:              <none>
Events:              <none>
[root@k8s-master ~]# 
(2)、建立secrets
  1.  
    #建立名为docker-login的secrets
    #正是在前文k8s存储中的secrtes配置文件
    [root@k8s-master ~]# kubectl create secret docker-registry docker-login --docker-username admin --docker-password gyj --docker-server reg.gaoyingjie.org [root@k8s-master ~]# kubectl describe secrets docker-login 
    Name:         docker-login
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>Type:  kubernetes.io/dockerconfigjsonData
    ====
    .dockerconfigjson:  123 bytes
    (3)、 将secrets注入
  2. (pod默认的sa是default,所以也可以将secrets注入到default中。不同的命名空间都有一个default)

    此步骤通俗来讲就是,secrets是定义授权用户拉取私人仓库镜像规则的配置文件,然后让sa读取这个配置文件,所以就可以拉取私人仓库里的镜像了。

    [root@k8s-master ~]# kubectl edit sa gaoyingjie #可以看到image的拉取授权为名为docker-login的secrtes配置文件[root@k8s-master ~]# kubectl describe sa gaoyingjie 
    Name:                gaoyingjie
    Namespace:           default
    Labels:              <none>
    Annotations:         <none>
    Image pull secrets:  docker-login
    Mountable secrets:   <none>
    Tokens:              <none>
    Events:              <none>
    (4)、pod绑定sa
    [root@k8s-master auth]# vim example1.yml 
    [root@k8s-master auth]# kubectl apply -f example1.yml 
    pod/testpod created#可以看到该pod的状态是running
    [root@k8s-master auth]# kubectl get pods
    NAME      READY   STATUS    RESTARTS   AGE
    testpod   1/1     Running   0          6m39s

    9.2、UserAccount

    [root@k8s-master ~]# cd /etc/kubernetes/pki/etcd/
    [root@k8s-master etcd]# ls
    ca.crt  healthcheck-client.crt  peer.crt  server.crt
    ca.key  healthcheck-client.key  peer.key  server.key
    [root@k8s-master etcd]# cd ..
    #都是集群用户的认证证书
    [root@k8s-master pki]# ls
    apiserver.crt                 ca.key                  gaoyingjie.crt
    apiserver-etcd-client.crt     ca.srl                  gaoyingjie.csr
    apiserver-etcd-client.key     etcd                    gaoyingjie.key
    apiserver.key                 front-proxy-ca.crt      sa.key
    apiserver-kubelet-client.crt  front-proxy-ca.key      sa.pub
    apiserver-kubelet-client.key  front-proxy-client.crt
    ca.crt                        front-proxy-client.key
    (1)、集群用户建立
    #建立集群用户的认证证书[root@k8s-master auth]# cd /etc/kubernetes/pki/
    #建立密钥
    [root@k8s-master pki]# openssl genrsa -out gaoyingjie.key 2048
    #认证请求
    [root@k8s-master pki]# openssl req  -new -key gaoyingjie.key -out gaoyingjie.csr -subj "/CN=gaoyingjie"
    #用认证请求生成证书
    [root@k8s-master pki]# openssl x509 -req  -in gaoyingjie.csr -CA ca.crt -CAkey ca.key -CAcreateserial  -out gaoyingjie.crt -days 365
    Certificate request self-signature ok#查看证书
    [root@k8s-master pki]# openssl x509 -in gaoyingjie.crt -text -noout
    #建立用户,其中set-credentials timinglee 指定用户名称 ,--client-certificate 指定证书,--client-key 指定key,-embed-certs=true 把这个动作写入文件里
    [root@k8s-master pki]# kubectl config set-credentials gaoyingjie --client-certificate /etc/kubernetes/pki/gaoyingjie.crt --client-key /etc/kubernetes/pki/gaoyingjie.key --embed-certs=true#为用户创建集群的安全上下文
    root@k8s-master pki]# kubectl config set-context gaoyingjie@kubernetes --cluster kubernetes --user gaoyingjie
    Context "gaoyingjie@kubernetes" created.#切换用户,用户在集群中只有用户身份没有授权
    [root@k8s-master ~]# kubectl config use-context gaoyingjie@kubernetes
    Switched to context "gaoyingjie@kubernetes".#没有授权所以不能查看pod
    [root@k8s-master ~]# kubectl get pods
    Error from server (Forbidden): pods is forbidden: User "gaoyingjie" cannot list resource "pods" in API group "" in the namespace "default"#切换会集群管理
    [root@k8s-master ~]# kubectl config use-context kubernetes-admin@kubernetes
    Switched to context "kubernetes-admin@kubernetes".#如果需要删除用户
    [root@k8s-master pki]# kubectl config delete-user gaoyingjie
    deleted user gaoyingjie from /etc/kubernetes/admin.conf
    (2)、集群用户rbac授权

    rbac:Role Based Access Control,基于角色访问控制授权,对用户功能权限声明的一种方式。——常用的授权方式。

    RBAC只有授权,没有拒绝授权,所以只需要定义允许该用户做什么即可

    RBAC的三个基本概念

    Subject:被作用者,它表示k8s中的三类主体, user, group, serviceAccount

    Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限。

    RoleBinding:定义了“被作用者”和“角色”的绑定关系

    RBAC包括四种类型:Role、ClusterRole、RoleBinding、ClusterRoleBinding

    Role 和 ClusterRole

    Role是一系列的权限的集合,Role只能授予单个namespace 中资源的访问权限。

    ClusterRole 跟 Role 类似,但是可以在集群中全局使用。

    Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用

    cluster-amdin、admin、edit、view

    简而言之:role是被定义好的资源授权规则,rolebanding赋予用户某个role,使用户可以使用role的授权资源。

    @role授权
    创建role:

    #生成role的yaml文件
    [root@k8s-master rbac]# kubectl create role myrole --dry-run=client --verb=get --resource pods -o yaml > myrole.yml#更改文件内容
    [root@k8s-master rbac]# vim myrole.yml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:creationTimestamp: nullname: myrole                    #role的名字为myrole
    rules:
    - apiGroups:- ""resources:                       #授权的资源是pod,也就是说这个role可以控制pod- podsverbs:                           #可以对pod的控制包括以下动作- get- watch- list- create- update- path- delete#创建role
    [root@k8s-master rbac]# kubectl apply -f  myrole.yml
    [root@k8s-master rbac]# kubectl describe role myrole

    rolebanding 角色绑定:

    [root@k8s-master rbac]# kubectl config use-context gaoyingjie@kubernetes 
    Switched to context "gaoyingjie@kubernetes".#可以看到gaoyingjie用户可以创建pod,也可以查看当前命名空间的pod
    [root@k8s-master rbac]# kubectl run  testpod --image myapp:v1
    pod/testpod created
    [root@k8s-master rbac]# kubectl get pods
    NAME      READY   STATUS    RESTARTS   AGE
    testpod   1/1     Running   0          5s
    @clusterrole授权
    #建立集群角色
    #myclusterrole 名称,--resource=deployment  可以控制的资源是控制器,--verb get 对控制器的动作
    [root@k8s-master rbac]# kubectl create clusterrole myclusterrole --resource=deployment --verb get --dry-run=client -o yaml > myclusterrole.yml[root@k8s-master rbac]# vim myclusterrole.yml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:name: myclusterrole
    rules:
    - apiGroups:- appsresources:- deployments            #可以控制的资源是控制器verbs:                   #对控制器的动作- get- list- watch- create- update- path- delete
    - apiGroups:- ""resources:               #可以控制的资源是pod- podsverbs:                   #对pod的动作- get- list- watch- create- update- path- delete
    - apiGroups:- ""resources:               #可以控制的资源是service- servicesverbs:                   #对service的动作- get- list- watch- create- update- path- delete[root@k8s-master rbac]# kubectl apply -f myclusterrole.yml 
    clusterrole.rbac.authorization.k8s.io/myclusterrole created#查看集群role
    [root@k8s-master rbac]# kubectl describe clusterrole myclusterrole 
    Name:         myclusterrole
    Labels:       <none>
    Annotations:  <none>
    PolicyRule:Resources         Non-Resource URLs  Resource Names  Verbs---------         -----------------  --------------  -----pods              []                 []              [get list watch create update path delete]services          []                 []              [get list watch create update path delete]deployments.apps  []                 []              [get list watch create update path delete]

    建立clusterrole绑定:

    #建立集群角色与用户绑定
    #绑定clusterrole与gaoyingjie用户,将clusterrole所声明的规则赋予用户使用,该clusterrole是没有命名空间的
    [root@k8s-master rbac]#  kubectl create clusterrolebinding  clusterrolebind-myclusterrole --clusterrole myclusterrole  --user gaoyingjie --dry-run=client -o yaml > clusterrolebind-myclusterrole.yml
    [root@k8s-master rbac]# vim clusterrolebind-myclusterrole.yml 
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:name: clusterrolebind-myclusterrole
    roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: myclusterrole
    subjects:
    - apiGroup: rbac.authorization.k8s.iokind: Username: gaoyingjie[root@k8s-master rbac]# kubectl apply -f myclusterrole.yml 
    clusterrole.rbac.authorization.k8s.io/myclusterrole configured[root@k8s-master rbac]# kubectl describe clusterrolebindings.rbac.authorization.k8s.io clusterrolebind-myclusterrole 
    Name:         clusterrolebind-myclusterrole
    Labels:       <none>
    Annotations:  <none>
    Role:Kind:  ClusterRoleName:  myclusterrole
    Subjects:Kind  Name        Namespace----  ----        ---------User  gaoyingjie  

    切换用户测试:

    #查看所有pod
    [root@k8s-master rbac]# kubectl get pods -A 
    NAMESPACE                NAME                                       READY   STATUS    RESTARTS        AGE
    default                  testpod                                    1/1     Running   0               39m
    kube-system              calico-kube-controllers-6849cb478c-828tr   1/1     Running   58 (18h ago)    52d
    kube-system              calico-node-2rq47                          1/1     Running   15 
    ......#查看控制器
    [root@k8s-master rbac]# kubectl get deployments.apps -A
    NAMESPACE                NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
    kube-system              calico-kube-controllers   1/1     1            1           52d
    kube-system              calico-typha              1/1     1            1           52d
    kube-system              coredns                   2/2     2            2           58d
    nfs-client-provisioner   nfs-client-provisioner    1/1     1            1           3d#查看微服务
    [root@k8s-master rbac]# kubectl get svc -A
    NAMESPACE     NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE
    default       kubernetes     ClusterIP   10.96.0.1      <none>        443/TCP                  58d
    default       nginx-svc      ClusterIP   None           <none>        80/TCP                   3d
    kube-system   calico-typha   ClusterIP   10.98.254.47   <none>        5473/TCP                 52d
    kube-system   kube-dns       ClusterIP   10.96.0.10     <none>        53/UDP,53/TCP,9153/TCP   58d

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/919967.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/919967.shtml
英文地址,请注明出处:http://en.pswp.cn/news/919967.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年机械工程与自动化技术国际会议(ICMEAT 2025)

2025年机械工程与自动化技术国际会议&#xff08;ICMEAT 2025&#xff09; 2025 International Conference on Mechanical Engineering and Automation Technology一、大会信息会议简称&#xff1a;ICMEAT 2025 大会地点&#xff1a;中国杭州 审稿通知&#xff1a;投稿后2-3日内…

高数 不定积分(4-3):分部积分法

文章目录写在前面分部积分法&#x1f615; 一个小问题✨ 分部积分法是怎么来的&#xff1f;&#x1f330; 几个小例子⭐ 最终总结&#xff01;后话写在前面 文章传送门&#xff1a;高数 不定积分&#xff08;4-2&#xff09;&#xff1a;换元积分法 今天再更一篇:) 上篇文章&…

Chrome/360 浏览器 WebUI 资源底层机制解析:共享资源与专属资源的奥秘

在 Chromium 和 360 浏览器源码中&#xff0c;我们会发现 WebUI 页面不仅有 C 逻辑处理&#xff08;如 WebUIMessageHandler&#xff09;&#xff0c;还伴随着大量 HTML、CSS 和 JS 文件。尤其是 src/ui/webui/resources 和 src/chrome/browser/360/webui 这两个目录&#xff0…

基于springboot的高校后勤保修服务系统/基于android的高校后勤保修服务系统app

基于springboot的高校后勤保修服务系统/基于android的高校后勤保修服务系统app

Qt QML 用Q_PROPERTY快捷访问c++属性

在之前我写过如何调用函数&#xff0c;当时的属性都是手搓的&#xff0c;也就是自己写成员变量、变化信号和读写函数&#xff0c;但其实有一个很便捷的方法&#xff0c;即使用Q_PROPERTY&#xff0c;下面给出标准结构&#xff1a;Q_PROPERTY(数据类型 变量名 READ 变量名 WRITE…

ubuntu中网卡的 IP 及网关配置设置为永久生效

要将 Ubuntu 中 ens33 和 ens36 网卡的 IP 及网关配置设置为永久生效&#xff08;重启后不丢失&#xff09;&#xff0c;需通过 netplan 配置并禁用 cloud-init 对网络的干扰&#xff08;避免重启后配置被覆盖&#xff09;&#xff0c;具体步骤如下&#xff1a;一、最终的永久生…

不再让Windows更新!Edge游戏助手卸载及关闭自动更新

文章目录Windows系统更新问题方法一&#xff1a;通过注册表手动设置1. 打开注册表编辑器2. 定位到目标路径3. 创建新的DWORD值4. 修改数值方法二&#xff1a;命令行设置1. 打开命令提示符2. 输入命令验证设置是否生效恢复更新Edge关闭游戏助手Edge关闭后台运行Edge关闭自动更新…

css3之flex布局

flex布局要牢记的两个知识点&#xff1a; 开启了flex布局的元素叫flex containerflex container里面的直接子元素叫flex items 这两点要记牢&#xff0c;设置属性的时候才不会搞混这个是flex布局的整体图 一、flex container上的属性 1.flex-direction 修改主轴方向的属性&…

vscode 搭建C/C++开发环境搭建(linux)

1.编译器/调试器安装首先&#xff0c;需要安装编译器&#xff08;GCC/G&#xff09;和调试器&#xff08;GDB&#xff09;,用于编译和调试代码。1.打开终端(Ctrl Alt T)2.更新软件包获取新版本信息sudo apt update3.安装build-essential包,它包含gcc,g等必要库sudo apt install…

vue-pure-admin页面引入和功能添加流程解析

vue-pure-admin (opens new window)是一款开源完全免费且开箱即用的中后台管理系统模版。完全采用 ECMAScript 模块&#xff08;ESM&#xff09;规范来编写和组织代码&#xff0c;使用了最新的 Vue3、Vite、Element-Plus、TypeScript、Pinia、Tailwindcss 等主流技术开发 以下是…

vlc-android: 编译自己的libvlc

概述 VLC 媒体播放器作为一款由志愿者开发团队精心维护的自由、开源且跨平台的多媒体播放器&#xff0c;能轻松驾驭绝大多数多媒体文件&#xff0c;无论是本地磁盘中的视频、音频&#xff0c;还是来自网络的流媒体协议. VLC for Android 支持网络串流&#xff0c;无论是基于 H…

并联谐振与串联谐振

在LC电路中&#xff0c;感抗和容抗相等时对应的频率值称为谐振频率&#xff0c;在接收广播电视信号或无线通信信号时&#xff0c;使接收电路的频率与所选择的发射的信号频率相同就叫做调谐。并联谐振LC并联谐振电路是指将电感器和电容器并联形成&#xff0c;如图所示。在并联谐…

打印机怎么连接电脑?打印机驱动?【图文详解】USB连接打印机?wifi连接打印机?

一、问题背景 在日常办公与生活里&#xff0c;把电脑和打印机连接起来&#xff0c;是实现文档、照片等打印的基础操作。但很多人初次尝试时&#xff0c;会因不熟悉流程而感到无从下手。 无论是办公场景下急需打印重要文件&#xff0c;还是日常生活中想要打印照片留念&#xff0…

CVPR 2025 | 医学影像加速进化:深度学习×多模态,精准诊断再升级

关注gongzhonghao【CVPR顶会精选】今天聊一个医学图像领域的前沿探索&#xff1a;结合空间感知卷积、扩散模型与视觉语言模型&#xff0c;从图像配准到合成分割&#xff0c;再到跨模态理解&#xff0c;打造了一个更加智能、鲁棒且可泛化的医学影像工具链。无论是SACB-Net带来的…

[每周一更]-(第157期):深入理解Go语言的垃圾回收机制:调优与监控

Go语言以其简洁的语法和强大的并发能力而闻名&#xff0c;而它的垃圾回收&#xff08;GC&#xff09;机制则是支撑其高性能的关键组件之一。本文将深入探讨Go语言的垃圾回收原理&#xff0c;并介绍如何对其进行调优与监控&#xff0c;以提升应用程序的性能。 Go语言垃圾回收机制…

Java 学习笔记(基础篇9)

1. 综合练习题目 1 &#xff1a;金额转换为中文大写格式请编写一个 Java 程序&#xff0c;实现将数字金额转换为中文大写格式&#xff08;带单位&#xff09;的功能&#xff0c;具体要求如下&#xff1a;(1) 程序接收用户输入的一个整数金额&#xff08;范围&#xff1a;0-9999…

云原生俱乐部-k8s知识点归纳(5)

写到这里&#xff0c;k8s的内容已经到一半了&#xff0c;虽然后面的内容我觉得更加玄学一点。控制器真的是个神奇的东西&#xff0c;虽然后面的CRD会带着大家一起做一个控制器&#xff0c;但是还是觉得很奇妙。控制器大概就是k8s中的精华了&#xff0c;通过控制器去监听k8s中ap…

C++复习2

C继承 继承的概念 继承&#xff08;inheritance&#xff09;机制是面向对象程序设计使代码可以复用的重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称为派生类。 继承呈现了面向对象程序设计…

ZKmall模块商城的跨境电商支付安全方案:加密与权限的双重防护

跨境电商支付环节面临双重挑战&#xff1a;一方面&#xff0c;不同国家的支付协议、货币结算规则差异显著&#xff0c;需满足多币种、多渠道的支付需求&#xff1b;另一方面&#xff0c;跨境数据传输的安全性与操作权限的严格管控直接关系到资金安全与合规性。ZKmall 模块商城针…

【数据结构】-5- 顺序表 (下)

一、集合框架 这是 Java 集合框架&#xff08;Java Collections Framework&#xff09;的核心继承关系树状图1. 最顶层&#xff1a;Iterable&#xff08;接口&#xff09;作用&#xff1a;所有 “可迭代” 的集合&#xff08;如 List、Set、Queue&#xff09;都必须实现它&…