掌握 SVC 原理及其构建方式

k8s-06-服务发现_ide

Pod、RC、Service关系

k8s-06-服务发现_github_02

Service

Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实力。 Service与其后端Pod副本集群之间则是通过Label Selector来实现"无缝对接"。而RC的作用实际上是保证Service 的服务能力和服务质量处于预期的标准。

k8s-06-服务发现_负载均衡_03

Service能够提供负载均衡能力,但是在使用上还有以下限制:

只提供4层负载,没有7层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上4层负载均衡是不支持的。

k8s-06-服务发现_github_04

VIP和Service代理

在集群中,每个Node运行一个kube-proxy进程,kub-proxy负责为Service实现一种VIP(虚拟IP)的形式,而不是ExternalNmae的形式。

1.0版本,代理完全在userspace【kub-proxy压力较大】;

k8s-06-服务发现_nginx_05

1.1版本,新增了iptables代理,单并不是默认运行模式,1.2起默认是iptables代理

k8s-06-服务发现_vim_06

1.8-beat.0添加了ipvs代理,1.14默认使用ipvs;【kube-proxy会监视kuberneters Service对象和EndPoints,调用netlink接口以相应地创建ipvs规则并定期与kuberneters Service对象和EndPoints 对象同步ipvs规则;以确保ipvs状态与期望一致,访问服务时,流量被重定向到其中一个后端pod与iptables类似,ipvs于netfilter的hook功能。但使用哈希表作为底层数据结构并在内核空间中工作,这意味着ipvs可以更快的重定向流量,并且在同步代理规则时具有更好的性能。此外ipvs为负载均衡算法提供了更多选项(rr-轮询/lc-最小连接数/dh-目标哈希/sh-源哈希/sed-最短期望值/nq-不排队调度)】

k8s-06-服务发现_负载均衡_07

1)ipvs模式假定在运行kube-proxy之前在节点上都已经安装了ipvs内核模块,当kube-proxy以ipvs模式启动时,kube-proxy会验证节点上是否安装了ipvs模块,如果未安装kube-proxy将回退到iptables模式 # ipvsadm -Ln
2)1.0版本,Servie是4层;1.1中新增ingress API(bate),用来表示7层(HTTP)服务

kube-proxy开启ipvs的前置条件

  1. 安装IPvS
#  yum -y install ipvsadm ipset    # 默认安装(不用处理)

ipvs需要加载一些内核模块

ip_vs

ip_vs_rr

ip_vs_wrr

ip_vs_sh

nf_conntrack_ipv4

1.1)查看内核模块是否加载

lsmod|grep ip_vs

  1. 在各个节点执行以下脚本(如果没有加载,直接执行modprobe也行):
# cat > /etc/sysconfig/modules/ipvs.modules <<EOF
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
# chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4

  1. kube-proxy开启ipvs并重启kube-proxy (master)
# kubectl edit configmap kube-proxy -n kube-system

找到如下部分的内容
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: "" # scheduler默认是空,默认负载均衡算法为轮训
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: "" # 其中mode原来是空,默认为iptables模式,改为ipvs
nodePortAddresses: null
# kubectl get pod -n kube-system | grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'

  1. 此时再像往常一样创建Deployment、Pod、Service之后查看就有ipvs规则了
# ipvsadm -Ln

四种类型 (pod和svc是多对多的关系)

为何不使用round-robin DNS? dns有缓存,很多服务都不会处理

一、ClusterIp:默认类型,自动分配一个Cluster内部可以访问的虚拟IP

1.创建deployment
# vim rs-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
release: test
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80

运行、查看
kubectl apply -f rs-deployment.yaml --record
kubectl get deployment
kubectl get pods

2.创建service 【 要和deployment 的标签一致 才能找到】
#vim server.yaml

apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: ClusterIP
selector:
app: myapp
release: test
ports:
- name: http
port: 80
targetPort: 80


运行、查看
kubectl apply -f server.yaml
kubectl get service # kubectl get svc

3.测试访问

k8s-06-服务发现_负载均衡_08

二、Headless Service (特殊的ClusterIp)有时不需要或不想要负载均衡,以及单独的Server IP.遇到这种情况,可以通过指定CluserIp 的值为"Node" 来创建Headless Service .这类Service并不会分配ClusterIp,kube-proxy不会处理他们,而且平台也不会为他们进行负载均衡和路由

# vim headless.yaml 

kind: Service
apiVersion: v1
metadata:
name: headless-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
clusterIP: None

# kubectl apply -f headless.yaml
查询服务
# kubectl get svc
安装dig命令
# yum install bind-utils -y

查询DNS 【svc创建以后 会以域名的方式写入coredns】
# kubectl get pod -n kube-system -o wide

A记录解析 无头服务名称.命名空间.集群地址. @DNS服务地址
# dig -t -A headless-service.default.svc.cluster.local. @10.244.0.34

实验结果(在无头服务中虽然没有自己的svc,但是可以通过访问域名的方式访问服务)

k8s-06-服务发现_github_09

三、NodePort:在clusterip基础上为Service在每台机器上绑定一个端口,这样就可以用过NodePort来访问该服务

#vim node-port-server.yaml

apiVersion: v1
kind: Service
metadata:
name: node-port-server
spec:
type: NodePort
selector:
app: myapp
release: test
ports:
- name: http
port: 80
targetPort: 80

测试访问:

k8s-06-服务发现_vim_10

通过访问 http://{nodeip}:31356 访问服务 所有的 node节点都会开启该端口

三+、LoadBalancer:在NodePort的基础上,借助cloud provider 创建一个外部负载均衡器,并将你请求转发到:NodePort (需要借助云供应商服务,需要收费)

k8s-06-服务发现_ide_11

四、ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用,没有任务类型代理被创建,这只有k8s 1.7或更高的kube-dns才支持

# vim baidu-service.yaml

apiVersion: v1
kind: Service
metadata:
name: baidu-service
spec:
type: ExternalName
externalName: www.baidu.com

# kubectl apply -f baidu-service.yaml
服务名称.命名空间.集群地址 访问外部服务
# dig -t A baidu-service.default.svc.cluster.local

Ingress

https://kubernetes.github.io/ingress-nginx/

https://github.com/kubernetes/ingress-nginx

在Kubernetes中,服务和Pod的IP地址仅可以在集群网络内部使用,对于集群外的应用是不可见的。为了使外部的应用能够访问集群内的服务,在Kubernetes 目前 提供了以下几种方案:

NodePort LoadBalancer Ingress

k8s-06-服务发现_负载均衡_12

工作原理

1.ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化,

2.然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置,

3.再写到nginx-ingress-control的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,

4.然后reload一下使配置生效。以此达到域名分配置和动态更新的问题。

可以解决什么问题

1.动态配置服务

  如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指向我们新的k8s服务. 而如果用了Ingress, 只需要配置好这个服务, 当服务启动时, 会自动注册到Ingress的中, 不需要而外的操作.

2.减少不必要的端口暴露

  配置过k8s的都清楚, 第一步是要关闭防火墙的, 主要原因是k8s的很多服务会以NodePort方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅. 而Ingress可以避免这个问题, 除了Ingress自身服务可能需要映射出去, 其他服务都不要用NodePort方式

组件:

configmap.yaml:提供configmap可以在线更行nginx的配置
default-backend.yaml:提供一个缺省的后台错误页面 404 namespace.yaml:创建一个独立的命名空间
ingress-nginx rbac.yaml:创建对应的role rolebinding 用于rbac
tcp-services-configmap.yaml:修改L4负载均衡配置的configmap
udp-services-configmap.yaml:修改L4负载均衡配置的configmap
with-rbac.yaml:有应用rbac的nginx-ingress-controller组件
service-nodeport:指定nginx的80端口和443端口

安装(不同版本安装方法差异比较大)

安装文件下载 :​​javascript:void(0)​

下载安装文件

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

Namespace

ingress-nginx

依赖的镜像 (注意版本)

docker.io/jettech/kube-webhook-certgen:v1.5.0
k8s.gcr.io/ingress-nginx/controller:v0.43.0 (该镜像下载不下来,需要替换成阿里云的)
#所有节点先安装镜像,镜像太大了 (记得修改配置文件中镜像地址)
docker pull docker.io/jettech/kube-webhook-certgen:v1.5.0
docker pull registry.aliyuncs.com/google_containers/nginx-ingress-controller:v0.43.0

无法直接在国内网络环境下从k8s.gcr.io下载镜像问题

阿里云代理仓库地址为:registry.aliyuncs.com/google_containers

比如下载

k8s.gcr.io/coredns:1.6.5

可以代理为:

registry.aliyuncs.com/google_containers/coredns:1.6.5

安装
kubectl apply -f deploy.yaml
验证 (Once the ingress controller pods are running, you can cancel the command typing Ctrl+C)
kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --watch
确认 ingress controller pods 是否启动
[root@k8s ~]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-vtrc9 0/1 Completed 0 27m
ingress-nginx-admission-patch-8qn2l 0/1 Completed 0 27m
ingress-nginx-controller-6b6f79df87-8zrdv 1/1 Running 0 27m
查看版本号
[root@k8s svc]# kubectl exec -it ingress-nginx-controller-6b6f79df87-8zrdv -n ingress-nginx -- /nginx-ingress-controller --version
-------------------------------------------------------------------------------
NGINX Ingress controller
Release: v0.43.0
Build: f3f6da12ac7c59b85ae7132f321bc3bcf144af04
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.19.6
查看服务
[root@k8s ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.100.172.233 <none> 80:31879/TCP,443:31041/TCP 29m
ingress-nginx-controller-admission ClusterIP 10.102.21.102 <none> 443/TCP 29m

可以通过 htpp(31879) https(31041)访问nginx服务

githubusercontent地址无法访问问题 https://zhuanlan.zhihu.com/p/107691233
#vim hosts
199.232.96.133 raw.githubusercontent.com

Ingress代理HTTP访问服务

(https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/)

deployment+service

# vim myapp-svc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
release: test
spec:
containers:
- name: myapp
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: ClusterIP
selector:
app: myapp
release: test
ports:
- name: http
port: 80
targetPort: 80
# kubectl apply -f myapp-svc.yaml
# kubectl get pods
# kubectl get svc

创建对应的一个ingress对象来暴露集群里这个 deployment 的 http 服务

# vim ingress-myapp.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-myapp-v1
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: test.ingress.com
http:
paths:
- path: /
backend:
serviceName: myapp-service
servicePort: 80
# kubectl apply -f ingress-myapp.yaml
域名解析
192.168.44.129 test.ingress.com
访问 (注意端口是ingress暴漏的http端口)服务
http://test.ingress.com:31879
查看
# kubectl get ingress

Ingress代理HTTPS访问服务

https://kubernetes.github.io/ingress-nginx/user-guide/tls/

创建证书

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=test.ingress.com/O=test.ingress.com"

kubectl create secret tls tls-secret --key tls.key --cert tls.crt

部署ingress

# vim ingress-https-myapp.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-myapp-v1
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: test.ingress.com
http:
paths:
- path: /
backend:
serviceName: myapp-service
servicePort: 80
# kubectl apply -f ingress-https-myapp.yaml
域名解析
192.168.44.129 test.ingress.com
访问 (注意端口是ingress暴漏的https端口)服务
https://test.ingress.com:31041/
查看
# kubectl get ingress

Nginx进行BasicAuth

(https://kubernetes.github.io/ingress-nginx/examples/auth/basic/)

安装依赖
yum -y install httpd
创建密码文件 (用户名/密码:foo/123456)
[root@k8s basic-auth]# htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo
保存密码文件
kubectl create secret generic basic-auth --from-file=auth

部署ingress

# vim ingress-basic.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-with-auth
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
rules:
- host: test.ingress.com
http:
paths:
- path: /
backend:
serviceName: myapp-service
servicePort: 80
域名解析
192.168.44.129 test.ingress.com
访问 (注意端口是ingress暴漏的http端口)服务
http://test.ingress.com:31879
查看
# kubectl get ingress

k8s-06-服务发现_vim_13

Nginx进行重写

(https://kubernetes.github.io/ingress-nginx/examples/rewrite/#rewrite-target)

nginx.ingress.kubernetes.io/rewrite-target 必须重定向流量的目标URI string
nginx.ingress.kubernetes.io/ssl-redirect
指示位置部分是否仅可以访问ssl(当ingress包含证书时默认为true) bool
nginx.ingress.kubernetes.io/force-ssl-redirect
j即使ingress未启用TLS,也强制定向到https bool nginx.ingress.kubernetes.io/app-root
定义Controller必须重定向的应用程序,如果它在’/上下文中’ string
nginx.ingress.kubernetes.io/use-regex 指示ingress上定义的路径是否使用正则表达式 bool

# vim ingress-rewrite.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://test.ingress.com:31879
name: rewrite
namespace: default
spec:
rules:
- host: k8s.master.com
http:
paths:
- path: /
backend:
serviceName: http-svc
servicePort: 80
域名解析
192.168.44.129 k8s.master.com
192.168.44.129 test.ingress.com
访问 (注意端口是ingress暴漏的http端口)服务
http://k8s.master.com:31879/
重定向到
http://test.ingress.com:31879
查看
# kubectl get ingress