【CKA考试笔记】十二、服务的配置

实验环境

完成初始化集群的环境:
(vms21)192.168.26.21——master1
(vms22)192.168.26.22——worker1
(vms23)192.168.26.23——worker2

一、概述

不管哪个控制器,只要重新创建了pod,那么pod的ip地址总是会发生变化的,因此直接访问pod是不方便的(ip地址不稳定),因此我们是通过访问svc来访问pod,而不是直接访问pod,svc只要不被删除,它的ip地址就不会变

svc就类似于一个负载均衡器的功能,用户将请求发送给svc,svc把请求转发给后端pod
在这里插入图片描述

二、svc的作用

svc接收用户请求,将请求转发给后端的pod:
1.svc转发请求给pod,是通过kube-proxy来实现的,它有两方式,iptables和ipvs,默认使用的是iptables
2.每创建一个svc,就会创建一个endpoints,将后端的每一个pod都看作为一个endpoint,将svc指向到后端的pod
3.当某个pod挂掉了,deployment重新创建这个pod,svc又会重新定位到新创建的pod,那么svc是怎么定位到这个新创建的pod呢?——通过标签定位(k8s中任何资源都有标签)

三、创建svc,验证svc的作用

a.命令行方式创建

#port表示访问这个svc服务的端口
#target-port表示转发到后端pod时,要访问的端口,这个端口不能随便设置,要根据pod的镜像来,如使用nginx镜像,那么默认的端口就是80,因此target-port必须得填80
#若没有指明target-port值,则target-port值保持和port一致
kubectl expose --name=[服务名] [资源类型] [资源名] --port=[服务端口] --target-port=[应用程序端口]

#--selector:选择器,在创建svc时,可以定义选择器的条件(标签),使svc关联所有标签满足这个选择器的资源
#若不定义selector,则这个svc是针对哪个具体资源做的,就会将这个资源的标签作为选择器的条件
kubectl expose --name=[服务名] [资源类型] [资源名] --port=[服务端口] --target-port=[应用程序端口] --selector=[标签]

#--external-ip:在服务发布之前,只有在这个集群内部才能访问到这个svc,若想让外部访问,可以通过external-ip指定外部访问地址
kubectl expose --name=[服务名] [资源类型] [资源名] --port=[服务端口] --target-port=[应用程序端口] --selector=[标签] --external-ip=[外部访问IP地址]

#--cluster-ip:指定集群内部对svc访问的地址
kubectl expose --name=[服务名] [资源类型] [资源名] --port=[服务端口] --target-port=[应用程序端口] --selector=[标签] --
cluster-ip=[内部访问IP地址]

b.yaml文件方式创建

输出yaml文件

kubectl expose --name=[服务名] [资源类型] [资源名] --port=[服务端口] --target-port=[应用程序端口] --dry-run=client -o yaml > [文件名.yaml]

实验

实验1:
创建3个pod,定义yaml使用nginx镜像,设置两个标签:run=pod1(第二、第三个pod分别为pod2、pod3)、run2=xx
(1)第一个pod的yaml文件(pod4.yaml)如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
    run2: xx
  name: pod1
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

(2)创建pod1

kubectl apply -f pod4.yaml

(3)第二个pod、第三个pod,分别把第一个pod的yaml中的 “pod1” 关键字替换为 “pod2”、“pod3” 然后创建

sed 's/pod1/pod2/g' pod4.yaml | kubectl apply -f -
sed 's/pod1/pod3/g' pod4.yaml | kubectl apply -f -

(4)查看pod,根据yaml文件的定义,这三个pod会有一个共同的标签run2=xx

kubectl get pods --show-labels
#输出:
NAME   READY   STATUS    RESTARTS   AGE   LABELS
pod1   1/1     Running   0          79s   run2=xx,run=pod1
pod2   1/1     Running   0          64s   run2=xx,run=pod2
pod3   1/1     Running   0          63s   run2=xx,run=pod3

(5)为了方便演示,将pod1、pod2、pod3内的nginx页面内容分别编辑为“111”、“222”、“333”

kubectl exec -it pod1 -- sh -c "echo 111 > /usr/share/nginx/html/index.html"
kubectl exec -it pod2 -- sh -c "echo 222 > /usr/share/nginx/html/index.html"
kubectl exec -it pod3 -- sh -c "echo 333 > /usr/share/nginx/html/index.html" 

(6)创建一个svc,根据nginx的端口,设置svc端口为80

kubectl expose --name=svc1 pod pod1 --port=80

创建成功

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
svc1         ClusterIP   10.102.70.96   <none>        80/TCP    8s

我们在创建这个服务的时候,是针对pod1来做的,所以当我们访问这个svc,就会将请求转发到pod1上
通过-o wide查看svc,可以看到SELECTOR选择器定义的是需要满足这两个标签:run2=xx, run=pod1,也就是只有pod1满足这两个标签

kubectl get svc -o wide
#输出:
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE     SELECTOR
svc1         ClusterIP   10.102.70.96   <none>        80/TCP    4h46m   run2=xx,run=pod1

kubectl get pods -l run2=xx,run=pod1
#输出:
NAME   READY   STATUS    RESTARTS        AGE
pod1   1/1     Running   2 (5m27s ago)   5h18m

因此这个svc是没有关联到pod2、pod3的
(7)在创建svc的时候,都会自动创建一个endpoints
查看endpoints

kubectl get ep
#输出:
NAME         ENDPOINTS            AGE
svc1         10.244.70.102:80     4h53m

可以看到这个endpoints指向了pod1的ip地址和nginx的端口

实验2:
(1)删除实验1的svc,重新创建一个svc,并指定选择器标签run2=xx,目的是让pod1、pod2、pod3都被svc关联上(pod1、pod2、pod3都含有标签run2=xx)

kubectl expose --name=svc1 pod pod1 --port=80 --selector=run2=xx

(2)创建后,可以看到这个svc的endpoints指向了pod1、pod2、pod3

kubectl get ep
NAME         ENDPOINTS                                          AGE
svc1         10.244.7.62:80,10.244.70.101:80,10.244.70.102:80   24s

(3)现在我们再创建一个pod4,标签设为能够满足svc的选择器,观察它能不能被svc动态关联上

sed 's/pod1/pod4/g' pod4.yaml | kubectl apply -f -

发现pod4即使是后面创建的,但是只要满足svc选择器,也是会被动态关联上的

kubectl get ep
#输出:
NAME         ENDPOINTS                                                  AGE
kubernetes   192.168.26.21:6443                                         6d
svc1         10.244.7.1:80,10.244.7.4:80,10.244.70.104:80 + 1 more...   91m

实验3:
(1)删除实验2中创建的pod4

kubectl delete pod pod4

(2)查看svc的访问地址

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
svc1         ClusterIP   10.97.10.18   <none>        80/TCP    18h

(3)在任一worker节点上循环访问svc(在服务发布之前,只有集群内部的主机,才能访问到svc)
可以看到它将请求转发给了pod1、pod2、pod3,实现了负载均衡

while true ; do curl 10.97.10.18 ; sleep 1 ; done
#输出:
111
111
111
222
333
111
111
111
222
333
222
222
333

(4)在服务发布之前,这个服务svc,只能被集群内部的主机访问到,集群外部是访问不到的,但是我们想让外部能够访问到
删除刚刚的svc,重新创建svc,并用external-ip来指定外部访问的地址

kubectl delete svc svc1
kubectl expose --name=svc1 pod pod1 --port=80 --selector=run2=xx --external-ip=192.168.26.22

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP     PORT(S)   AGE
svc1         ClusterIP   10.97.126.135   192.168.26.22   80/TCP    19s

(5)外部访问测试,打开cmd,输入curl访问

C:\Users\ctyFL>curl 192.168.26.22
#输出:
111

四、查看svc关联了哪几个pod

先查看svc的SELECTOR信息

kubectl get svc [svc名] -o wide

然后通过标签筛选pod

kubectl get pods -l run2=xxx

若用一行命令:

#$NF表示获取最后一列数据
kubectl get pods -l $(kubectl get svc [svc名] -o wide | awk '/[svc名]/{print $NF}')

五、服务的发现

什么是服务的发现?

前面我们了解到,我们访问一个svc,然后svc会将我们的请求转发给pod,但实际情况下,很少是这样单层的svc,而是更复杂的,比如一个wordpress应用,它需要连接一个mysql数据库,当wordpress需要访问mysql数据库时,它不是直接访问mysql所在的pod,而是访问关联这个pod的svc,然后由svc将请求转发给mysql所在的pod,当用户外部访问wordpress时,也是访问它关联的svc,如下图所示:
在这里插入图片描述
那么,wordpress怎么知道关联mysql的svc地址是什么呢?——这就是服务的发现
服务的发现:一个pod如何发现另外一个svc的地址

服务发现的三种方式:
1.clusterIP:直接查看需要访问的svc的地址,然后在pod的yaml中指定(这种方式,不管在哪个ns里,都能访问svc)
2.变量的方式
3.dns的方式(推荐):

实验1:clusterIP方式

沿用之前的实验环境(3个pod,pod1、pod2、pod3,关联svc1)
(1)打开另一个终端,创建一个临时pod,名为testpod,镜像使用nginx,创建好后进入终端,并且退出pod后就删除pod
(创建这个临时pod,目的是为了作为客户端去访问svc)

kubectl run testpod -it --rm --image nginx --image-pull-policy=IfNotPresent -- bash

(2)回到原来的终端,删除之前例子中的svc1,根据之前创建的pod1,针对pod1创建一个svc,port为80,选择器条件满足run2=xx

kubectl expose --name=svc1 pod pod1 --port=80 --selector run2=xx

(3)查看svc1的IP地址

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
svc1         ClusterIP   10.104.108.178   <none>        80/TCP    7s

(4)来到testpod的终端,创建之后就进入pod了,然后输入curl 10.104.108.178访问svc1测试,能访问成功

curl 10.104.108.178
#输出:
333

(5)创建命名空间test

kubectl create ns test

(6)再次重新打开一个终端,在test命名空间下创建一个临时pod,名为testpod2
(创建这个临时pod,目的是为了作为客户端去访问svc)

kubectl run testpod2 -it --rm --image nginx --image-pull-policy=IfNotPresent -n test -- bash

(7)创建好进入testpod2后,输入curl 10.104.108.178 测试

curl 10.104.108.178
#输出:
222

因此通过clusterIP的方式,不管是在哪个命名空间下,都能访问

实验2:变量的方式

沿用实验一的环境
(1)创建pod后,会自动生成一些pod的变量,这些变量都有固定的格式

#进入一个pod,输入env,可以查看系统自动生成的pod的变量
env
#输出:
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
HOSTNAME=testpod2
PWD=/
PKG_RELEASE=1~bullseye
HOME=/root
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
NJS_VERSION=0.7.1
TERM=xterm
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NGINX_VERSION=1.21.5
_=/usr/bin/env

其中有以下变量来记录svc的信息

SVCNAME_SERVICE_HOST——表示此svc的IP
SVCNAME_SERVICE_PORT——表示此svc的端口

在pod内时,可以通过env | grep ^SVC来查看svc相关变量

env | grep ^SVC

在pod内,执行curl变量的方式就可以访问svc

curl $SVC1_SERVICE_HOST

(2)在testpod的终端,在pod内查看svc相关变量

env | grep ^SVC
#输出:
SVC1_SERVICE_HOST=10.104.108.178
SVC1_PORT_80_TCP_PROTO=tcp
SVC1_PORT_80_TCP_ADDR=10.104.108.178
SVC1_PORT=tcp://10.104.108.178:80
SVC1_SERVICE_PORT=80
SVC1_PORT_80_TCP_PORT=80
SVC1_PORT_80_TCP=tcp://10.104.108.178:80

(3)切换到testpod2的终端(命名空间为test的这个pod),查看svc相关变量,但是发现没有svc相关变量
(4)创建svc2,选择器也为run2=xx(svc1与svc2都关联pod1、pod2、pod3)

kubectl expose --name=svc2 pod pod1 --port=80 --selector run2=xx

(5)再次分别在testpod终端中和testpod2的终端中查看svc相关变量,发现都没有svc信息
这是因为(变量的方式的弊端):
a.pod只能发现相同命名空间里的svc信息,不能发现其他命名空间里的svc
b.只能发现在pod创建之前创建的svc,不能发现在pod创建之后创建的svc

实验3:dns方式(推荐)

沿用实验2的环境
(1)在testpod中通过curl [svc名]来访问,发现可以访问svc1和svc2

curl svc1
#输出:
111

curl svc2
#输出:
333

(2)在testpod2中通过curl [svc名]来访问,发现访问不了svc1和svc2
这是因为testpod2在test命名空间内,而testpod、svc1、svc2都在default命名空间下
可以通过curl [svc名].[svc所在命名空间]来访问不同命名空间的svc

curl svc1.default
#输出:
222

curl svc2.default
#输出:
333

为什么可以用这种方式访问呢?
我们查看kube-system下的pods,可以看到coredns这两个pod,这两个pod都是由deployment类型的控制器管理的

kubectl get pods -n kube-system

#输出:
NAME                                       READY   STATUS    RESTARTS         AGE 
...
coredns-74586cf9b6-rlkhp                   1/1     Running   8 (3h15m ago)    7d20h
coredns-74586cf9b6-x5vld                   1/1     Running   8 (3h15m ago)    7d
...

我们再查看kube-system下的svc

kubectl get svc -n kube-system,可以看到kube-dns这个svc,IP为10.96.0.10
#输出:
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
kube-dns         ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP   7d20h
metrics-server   ClusterIP   10.107.27.247   <none>        443/TCP                  7d

我们再看kube-dns这个svc关联了哪些pod,可以看到它关联的就是coredns这两个pod

kubectl get pods -n kube-system -l $(kubectl get svc kube-dns -o wide -n kube-system | awk '/kube-dns/{print $NF}')

#输出:
NAME                       READY   STATUS    RESTARTS        AGE
coredns-74586cf9b6-rlkhp   1/1     Running   8 (3h25m ago)   7d20h
coredns-74586cf9b6-x5vld   1/1     Running   8 (3h25m ago)   7d20h

总的来说,kube-dns这个svc,它关联了coredns这两个pod,当每创建一个svc,系统都会自动去向kube-dns注册,因此,kube-dns这个svc知道每个svc的IP,每个pod都将kube-dns作为dns服务器,当某个pod要访问svc1时,会向kube-dns查询svc1的IP,然后进行访问
在这里插入图片描述

可以在kubelet的配置文件中查看dns的地址配置(clusterDNS)

vim /var/lib/kubelet/config.yaml

#输出:
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
cgroupDriver: systemd
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging:
  flushFrequency: 0
  options:
    json:
      infoBufferSize: "0"
  verbosity: 0
memorySwap: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true

实验(搭建wordpress和mysql)

一:clusterIP的方式
(1)各个worker节点上拉取mysql、wordpress镜像

nerdctl pull hub.c.163.com/library/mysql
nerdctl pull hub.c.163.com/library/wordpress

(2)创建mysql的pod,yaml文件dbpod.yaml 如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: dbpod
  name: dbpod
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - image: hub.c.163.com/library/mysql
    imagePullPolicy: IfNotPresent
    name: dbpod
    resources: {}
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: root
    - name: MYSQL_USER
      value: tom
    - name: MYSQL_PASSWORD
      value: root
    - name: MYSQL_DATABASE
      value: wordpress
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

(3)针对dbpod创建一个svc

kubectl expose --name=dbsvc pod dbpod --port 3306

(4)查看dbsvc的IP地址,这里服务的发现使用clusterIP的方式

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
dbsvc        ClusterIP   10.97.189.47     <none>        3306/TCP   8m27s

(5)创建wordpress的pod,连接mysql时的host地址即dbsvc的IP,yaml文件blogpod.yaml如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: blogpod
  name: blogpod
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - image: hub.c.163.com/library/wordpress
    imagePullPolicy: IfNotPresent
    name: blogpod
    resources: {}
    env:
    - name: WORDPRESS_DB_HOST
      value: 10.97.189.47
    - name: WORDPRESS_DB_USER
      value: root
    - name: WORDPRESS_DB_PASSWORD
      value: root
    - name: WORDPRESS_DB_NAME
      value: wordpress
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

(6)创建blogpod的svc

kubectl expose --name=blogsvc pod blogpod --port 80 --type NodePort

(7)查看svc

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
blogsvc      NodePort    10.97.200.16     <none>        80:31165/TCP   5s
dbsvc        ClusterIP   10.97.189.47     <none>        3306/TCP       17m

(8)浏览器输入地址 “http://192.168.26.21:31165” 便可以访问了(访问集群中任意一个节点都能访问)

二:以变量的方式
因为此时都是在同一个命名空间,所以可以使用变量的方式
wordpress的yaml文件中变量配置(yaml文件中,$符号后得用小括号)如下:

...
    env:
    - name: WORDPRESS_DB_HOST
      value: $(DBSVC_SERVICE_HOST)
...

三:dns的方式
wordpress的yaml文件中变量的配置如下:

...
    env:
    - name: WORDPRESS_DB_HOST
      value: dbsvc
...

六、服务的发布

服务发布之前,只有集群内部的主机可以访问服务(svc),服务发布就是为了可以让集群外部的主机访问服务

服务的类型为:
ClusterIP
ExternalName
NodePort
LoadBlancer
ingress

删除前面实验环境的所有svc、pod资源

kubectl delete pod dbpod
kubectl delete pod blogpod
kubectl delete svc blogsvc
kubectl delete svc dbsvc
kubectl delete pod testpod
kubectl delete svc svc1
kubectl delete pod pod1
kubectl delete pod pod2
kubectl delete pod pod3

NodePort

在没有svc之前,若我们想要使得一个pod从外部可以访问,那么我们是使用容器的端口(containerPort)与宿主机端口(hostPort)相映射,但由于实际中我们一般不会直接访问pod,而是访问svc,那么只需将svc的端口与一个宿主机端口相映射,这个端口即NodePort
在这里插入图片描述
1.创建svc时,指定服务类型为NodePort

kubectl expose --name=[svc名] [资源类型] [资源名] --port=[服务端口] --type=NodePort

命令行创建时不能指定NodePort端口,只能通过kubectl edit svc [svc名]修改,若想创建时就指定,只能通过yaml的创建方式

kubectl expose --name=[svc名] [资源类型] [资源名] --port=[服务端口] --type=NodePort --dry-run=client -o yaml > [文件名.yaml]

2.对于已经存在的svc,也可以通过kubectl edit svc [svc名]方式修改TYPE为NodePort

实验:
(1)创建pod1,yaml如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
    run2: xx
  name: pod1
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

(2)创建pod1后,创建svc1

kubectl expose --name=svc1 pod pod1 --port=80

(3)查看svc1,可以看到svc的TYPE为ClusterIP

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
svc1         ClusterIP   10.101.94.222    <none>        80/TCP    8s

(4)在线svc1,修改TYPE为NodePort

kubectl edit svc svc1

找到type配置项,改为NodePort

...
  selector:
    run: pod1
    run2: xx
  sessionAffinity: None
  type: NodePort
status:
...

(5)查看svc1,可以看TYPT已被修改为NodePort,PORT(s)端口会帮我们配置一个大于30000的端口

kubectl get svc
#输出:
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
svc1         NodePort    10.101.94.222    <none>        80:30991/TCP   4m52s

(6)也可以手动修改端口

kubectl edit svc svc1

#配置:
...
  ipFamilyPolicy: SingleStack
  ports:
  - nodePort: 30991
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
...

(7)由于命令行创建svc方式,不能在创建时就指定NodePort端口,因此也可以使用yaml方式

kubectl expose --name=svc1 pod pod1 --port=80 --type=NodePort --dry-run=client -o yaml > nodePortSvc.yaml

输出yaml如下:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    run: pod1
    run2: xx
  name: svc1
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: pod1
    run2: xx
  type: NodePort
status:
  loadBalancer: {}

(8)使用nodePort配置项配置端口

...
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30303
  selector:
    run: pod1
...

不指定、自动分配NodePort端口时不会出现端口冲突,但手动指定NodePort端口可能会产生冲突
NodePort缺点:
开放的端口很乱,很难维护,如下一个环境中有很多个命名空间,各命名空间下有很多svc,每个svc都开放一个端口,就会很乱,很难管理
在这里插入图片描述

LoadBalancer

创建svc时,指定服务类型为LoadBalancer

kubectl expose --name=[svc名] [资源类型] [资源名] --port=[服务端口] --type=LoadBalancer

创建一个类型为LoadBalancer的svc时,会给这个svc指定一个可路由的IP,这个IP并不是宿主机的IP
需要提前创建一个地址池(一个范围),创建svc时,给这个svc分配一个IP

这个地址池怎么来的?
——如果是内网环境,可以自己指定
——如果是公网环境—需要去服务商申请一个地址段

例:
1.创建一个pod1的LoadBalancer类型的svc

kubectl expose --name=svc1 pod pod1 --port=80 --type=LoadBalancer

2.查看该svc

kubectl get svc
#输出:
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
svc1         LoadBalancer   10.104.80.159   <pending>     80:30424/TCP   7s

可以看到该svc的EXTERNAL-IP为,这是因为kubernetes默认不支持LoadBalancer类型的svc,需要安装一个第三方插件——metallb
3.安装metallb
访问metallb官网:https://metallb.universe.tf/installation/
通过manifest安装
根据提示,我们首先需要创建一个命名空间——metallb-system
在这里插入图片描述
在master上创建命名空间metallb-system

kubectl create ns metallb-system

在这里插入图片描述
这里无法通过wget下载,因此通过浏览器打开这个yaml,然后复制内容
在master上编辑metallb. yaml,粘贴内容

vim metallb.yaml
:set paste

查看需要用到的镜像

grep image metallb.yaml

在这里插入图片描述
在所有节点上拉取这两个镜像

nerdctl pull quay.io/metallb/controller:v0.13.4
nerdctl pull quay.io/metallb/speaker:v0.13.4

拉取完后,编辑metallb.yaml,将镜像的下载策略设置为IfNotPresent

vim metallb.yaml

搜索关键字:speaker:v0.13.4,添加镜像下载策略配置
在这里插入图片描述
在这里插入图片描述
保存后,安装metallb

kubectl apply -f metallb.yaml

在这里插入图片描述
查看metallb-system下的pod,都是RUNNING状态了

kubectl get pods -n metallb-system

在这里插入图片描述
创建地址池,参考官网文档
在这里插入图片描述
将内容复制,在master上编辑一个pool.yaml,粘贴内容
然后将address配置改为192.168.26.240-192.168.26.250(范围即240~250)

vim pool.yaml

#yaml文件内容:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.26.240-192.168.26.250

然后创建这个pool

kubectl apply -f pool.yaml

到此,metallb的安装配置都完成了
4.然后重新创建类型为Loadbalancer的svc1

kubectl delete svc svc1
kubectl expose --name=svc1 pod pod1 --port=80 --type=LoadBalancer

5.查看svc1,可以看到EXTERNAL-IP分配为192.168.26.240

kubectl get svc
#输出:
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
svc1         LoadBalancer   10.99.76.65   192.168.26.240   80:30881/TCP   4s

Ingress

如果要使用ingress,那么需要创建一个ingress控制器——nginx-ingress-controller

反向代理
在这里插入图片描述
反向代理与负载均衡的区别:
反向代理指用户访问代理服务器,但不知道真实提供服务的服务器,代理服务器根据用户同的请求,将请求转发到对应的服务器上
例如访问www.a.com,而真正提供服务的服务器为A,那么反向代理服务器便将请求转发给A,若提供服务的服务器为B,便将请求转发给B,即是固定的,即便再多的请求,也是固定转发给真正提供服务的服务器
负载均衡则是用户访问的都是一个地址,然后负载均衡器来分发流量

ingress原理:
nginx-ingress-controller就是一个反向代理服务器(nginx实现的反向代理)
在ingress-nginx的命名空间中,创建一个pod作为ingress控制器(nginx-ingress-controller),并且hostNetwork为true,共享宿主机的网络空间

在其他各个命名空间中,命名空间里的svc都为ClusterIP形式的svc,各命名空间中都维护一个ingress规则,里面将访问地址与svc相映射,并将这个ingress规则嵌入到ingress控制器中(nginx-ingress-controller)
如下图所示:
命名空间1中clusterIP形式的svc1,svc1与pod1关联,命令空间1中维护一个ingress规则,将域名www.a.com与svc1的映射关系写入ingress规则中,并将ingress规则嵌入到ingress控制器,用户访问www.a.com时,首先dns解析,解析出ingress控制的地址,将请求转发给ingress控制器,ingress控制器读取ingress规则,将请求转发给svc1
在这里插入图片描述
创建ingress控制器:
1.参考k8s官方文档:https://kubernetes.io/zh-cn/docs/home/
搜索ingress
在这里插入图片描述
选择Ingress控制器
在这里插入图片描述
选择Nginx控制器
在这里插入图片描述
点击Getting Started
在这里插入图片描述
点击Bare-metal
在这里插入图片描述
这里便可以找到所需的yaml文件
在这里插入图片描述
wget这个yaml文件

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/baremetal/deploy.yaml

在这里插入图片描述
查看所需的镜像

grep image deploy.yaml

在这里插入图片描述
各节点上拉取这两个镜像
这里我通过科学上网拉取了镜像

nerdctl pull registry.k8s.io/ingress-nginx/controller:v1.3.0@sha256:d1707ca76d3b044ab8a28277a2466a02100ee9f58a86af1535a3edf9323ea1b5
nerdctl pull registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660

master上创建ingress控制器

kubectl apply -f deploy.yaml

在这里插入图片描述
查看ingress-nginx下的pod

kubectl get pods -n ingress-nginx

在这里插入图片描述
查看Ingress控制器这个pod的IP

kubectl get pods -n ingress-nginx -o wide

在这里插入图片描述

2.创建svc与pod
删除之前的svc1

kubectl delete svc svc1

创建一个名为pod2的pod,yaml如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod2
    run2: xx
  name: pod2
spec:
  terminationGracePeriodSeconds: 0
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod2
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

进入pod2,执行“echo 222 > /usr/share/nginx/html/index.html”

kubectl exec -it pod2 -- sh -c "echo 222 > /usr/share/nginx/html/index.html"

此时环境中有2个pod,pod1与pod2
进入pod1执行

kubectl exec -it pod1 -- sh -c "echo 111 > /usr/share/nginx/html/index.html"

分别针对pod1、pod2创建svc1、svc2

kubectl expose --name=svc1 pod pod1 --port=80
kubectl expose --name=svc2 pod pod2 --port=80

在这里插入图片描述
pod1、pod2、svc1、svc2都是在命名空间default下,此时,该命名空间下并没有ingress规则
在这里插入图片描述
在这里插入图片描述

3.创建ingress规则
参考k8s文档
搜索ingress
在这里插入图片描述
复制Ingress resource下的yaml内容
在这里插入图片描述
master上编辑ing.yaml,粘贴该内容,名字改为myingress

vim ing.yaml

yaml内容如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

ingressClassName——表示Ingress的存储类类型
查看文档,可以看到有这几种:
在这里插入图片描述
一般来说,这个类会自动帮我们创建
查看ingressclass

kubectl get ingressclass

在这里插入图片描述
若没有的需要自己创建
文档中搜索ingress,找到ingress控制器
在这里插入图片描述
找到“使用多个ingress控制器”
在这里插入图片描述
根据yaml文件创建
在这里插入图片描述
继续编辑ingress规则的yaml文件
a.因为ingressClass的name为nginx,因此ingressClassName处改为“nginx”
在rules下配置转发规则:
b.添加host配置项,设为www.a.com
c.path处设置为“/”,因为使用的是nginx,“/”便表示访问到“/usr/share/nginx/html/index.html”
d.service名字改为“svc1”
(www.a.com—>svc1)
同理,添加svc2的转发规则
(www.b.com—>svc2)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: www.a.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc1
            port:
              number: 80
  - host: www.b.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: svc2
            port:
              number: 80

创建这个ingress规则

kubectl apply -f ing.yaml

查看该ingress

kubectl get ing

在这里插入图片描述
4.测试
在其他一台虚拟机中添加hosts解析
注:因为Ingress控制器所在的节点为vms23.rhce.cc,IP为192.168.26.21
在这里插入图片描述
因此hosts的解析应为:

vim /etc/hosts

#解析为
192.168.26.21 www.a.com
192.168.26.21 www.b.com

访问测试

curl www.a.com
#输出:
111

curl www.b.com
#输出:
222

若担心都将请求转发到vms23的节点上负载过高
这时候就可以将部署的deploy改为daemonSet,就可以负载均衡了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戴陵FL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值