K8S中网络这块主要考虑 如何访问外部网络以及外部如何访问内部网络
k8s集群内的pod需要访问mysql,由于mysql的性质,不适合部署在k8s集群内,故k8s集群内的应用需要链接mysql时,需要配置链接外网的mysql,本次测试 k8s集群ip段为
192.168.23.xx
。以下提供两种方式,Endpoint
和ExternalName
方式。
新建命名空间my-first-app
,需要访问外网的Pod,svc,endpoints等 都需要在该命名空间下。
> kubectl create namespace my-first-app
创建my-mysql-endpoints.yaml
> mkdir -p ~/mysql-endpoint
> cd ~/mysql-endpoint
> cat < my-mysql-endpoints.yaml
apiVersion: v1
kind: Endpoints
apiVersion: v1
metadata:
name: my-mysql-endpoint #此名字需与 my-mysql-service.yaml文件中的 metadata.name 的值一致
namespace: my-first-app #在固定的命名空间下
subsets:
- addresses:
- ip: 192.168.23.1 ## 宿主机,由于我的虚拟机ping不通我本机的mysql(安装mysql时候禁用了未打开)
- ip: 220.181.38.148 ## 随便取的一个 公网Ip
ports:
- port: 3306
EOF
创建my-mysql-service.yaml
> cat < my-mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-mysql-endpoint #此名字需与 my-mysql-endpoints.yaml文件中的 metadata.name 的值一致
namespace: my-first-app #在固定的命名空间下
spec:
ports:
- port: 3306
### 验证,进入,ping一下配置的Ip地址。
# kubectl exec -it my-first-springcloud-94cdd7487-xxxxx -n my-first-app -- /bin/sh
# ping 220.181.38.148
EOF
部署自己的服务到 my-first-app
命名空间下,并且进入容器,进行ping测试
> kubectl exec -it my-first-springcloud-94cdd7487-xxxxx -n my-first-app -- /bin/sh
> ping 220.181.38.148 ## 也可以在容器中直接调用对应的端口测试,此处只演示ping通,就代表能访问了
PING 220.181.38.148 (220.181.38.148): 56 data bytes
64 bytes from 220.181.38.148: seq=0 ttl=127 time=5.356 ms
64 bytes from 220.181.38.148: seq=1 ttl=127 time=4.946 ms
64 bytes from 220.181.38.148: seq=2 ttl=127 time=18.165 ms
。。。。
> 也可以查看service的 Endpoints 信息
> kubectl describe svc my-mysql-endpoint -n my-first-app
Name: my-mysql-endpoint
Namespace: my-first-app
Labels:
Annotations:
Selector:
Type: ClusterIP
IP: 10.102.160.141
Port: 3306/TCP
TargetPort: 3306/TCP
Endpoints: 192.168.23.1:3306,220.181.38.148:3306
Session Affinity: None
Events:
注意
此种方式只适合Ip访问,对于像阿里云rds等数据库的。需要用域名。则需要用ExternalName
方式不而不是Endpoints
方式。
创建``
**> mkdir -p ~/mysql-endpoint
> cd ~/mysql-endpoin
> cat < my-mysql-external.yaml
apiVersion: v1
kind: Service
metadata:
name: my-mysql-external #此名字随便起
namespace: my-first-app #在固定的命名空间下
spec:
type: ExternalName
externalName: www.baidu.com ##提供方的服务完全限定域名,如rds域名等。
ports:
- port: 80
### ExternalName类型的服务创建后,pod可以通过my-mysql-external.default.svc.cluster.local域名连接到外部服务,
#### 或者通过my-mysql-external。当需要指向其他外部服务时,只需要修改spec.externalName的值即可。
EOF
kubectl run 创建 pod
[root@master ~]#kubectl run nginx --image=nginx:1.14 --port=80 --replicas=3
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/nginx created
[root@master ~]#kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-59d795d786-5ln28 1/1 Running 0 34s
nginx-59d795d786-xnfjq 1/1 Running 0 34s
nginx-59d795d786-z86nn 1/1 Running 0 34s
[root@master ~]#kubectl get all
NAME READY STATUS RESTARTS AGE
pod/nginx-59d795d786-5ln28 1/1 Running 0 59s
pod/nginx-59d795d786-xnfjq 1/1 Running 0 59s
pod/nginx-59d795d786-z86nn 1/1 Running 0 59s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 443/TCP 3d9h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx 3/3 3 3 59s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-59d795d786 3 3 3 59s
kubectl expose 发布容器
[root@master ~]#kubectl expose deployment nginx --port=80 --target-port=80 --name=nginx-service --type=NodePort
service/nginx-service exposed
# 查看 Pod 网络状态详细信息和 Service 暴露的端口
[root@master ~]#kubectl get svc,pod -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 443/TCP 3d9h
service/nginx-service NodePort 10.96.158.55 80:31083/TCP 3m37s run=nginx
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-59d795d786-5ln28 1/1 Running 0 7m48s 10.244.1.3 node01
pod/nginx-59d795d786-xnfjq 1/1 Running 0 7m48s 10.244.2.3 node02
pod/nginx-59d795d786-z86nn 1/1 Running 0 7m48s 10.244.2.4 node02
K8S 模拟项目 pod 发布
k8s 之 PodIP、ClusterIP 和 ExternalIP
在 k8s 中创建 service 时,需要指定 type 类型,可以分别指定 ClusterIP、NodePort、LoadBalancer 三种,其中前面两种无论在内网还是公网环境下使用都很常见,只有 LoadBalancer 大部分情况下只适用于支持外部负载均衡器的云提供商(AWS、阿里云、华为云等)使用。
本地自己安装的 k8s 集群,默认是不支持 LoadBalancer 的,需要自己安装一个组件来支持。而云上的 k8s,肯定是都支持 LoadBalancer 的。如果自己公司搭建集群,那肯定也是需要安装 LoadBalancer 的,支持本地集群 LoadBalancer 的组件:
LoadBalancer 示意图
如果你的网络是运行在 IPVS 模式下(默认是 iptables),那么需要设置 strictARP 模式。 链接
[root@master ~]# curl localhost:10249/proxyMode
ipvs
[root@master ~]# kubectl edit configmap -n kube-system kube-proxy
# 修改其中的 strictARP 为 true,下面字段不是连续的上下行,只展示了需要注意的行数
......
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:
strictARP: true
......
官网可能执行不了
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.10.2/manifests/metallb.yaml
[root@master ~]#kubectl apply -f http://49.232.8.65/yaml/metallb/namespace.yaml
namespace/metallb-system created
[root@master ~]#kubectl apply -f http://49.232.8.65/yaml/metallb/metallb.yaml
......
[root@master ~]#kubectl get pods -n metallb-system
NAME READY STATUS RESTARTS AGE
controller-857846f7df-l245m 1/1 Running 0 42s
speaker-5ln28 1/1 Running 0 42s
speaker-xnfjq 1/1 Running 0 42s
speaker-z86nn 1/1 Running 0 42s
# metallb-system 命名空间下有 controller,speaker 等进程已经在 Running 状态,说明正常。
# metallb-system/controller deployment。用于处理 IP 分配的控制器。
# metallb-system/speakerdaemonset。集群中每个节点启动一个协议服务守护进程。
这里我们使用 layer2 协议,毕竟本地测试没有 BGP 设备,如果是正式用,还是要上 BGP 设备的。先申请预留的 IP 端,配置如下:
[root@master ~]#vim metallb.ip.yaml
[root@master ~]#cat metallb.ip.yaml
# 添加一个 ConfigMap 配置 metallb IP 池
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.10.170-192.168.10.200
[root@master ~]#kubectl apply -f metallb.ip.yaml
这样当我们创建一个 loadbalancer 类型的 service 时,EXTERNAL-IP 将会从地址池中获取一个用于外部访问的 IP,当外部流量进入时,ARP 将我们的请求地址广播以获取所属的 service,接着 k8s 内部通过 iptables 规则和 kube-proxy 将流量从服务端点引导到后端。
BGP 类型参考官方文档:
链接
现在部署一个 Nginx 看看效果
[root@master ~]#vim nginx-test.yaml
[root@master ~]#cat nginx-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-metallb-test
spec:
selector:
matchLabels:
app: nginx-metallb-test
template:
metadata:
labels:
app: nginx-metallb-test
spec:
containers:
- name: nginx
image: nginx:1.8
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-metallb-test
type: LoadBalancer
[root@master ~]#kubectl apply -f nginx-test.yaml
deployment.apps/nginx-metallb-test created
service/nginx-service created
上面声明的 Service 的类型为 LoadBalancer,无其他特殊设置。
[root@master ~]#kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 443/TCP 9d
nginx-service LoadBalancer 10.96.158.55 192.168.10.170 80:31083/TCP 70s app=nginx-metallb-test
打开浏览器,访问 EXTERNAL-IP 即可访问 nginx:http://192.168.10.170
如果是正式环境,则使用 DNS 解析到此 IP,使用域名访问服务即可
如果有多个服务,可以使用 Nginx Ingress 来通过域名和路径区分不同的服务
负载均衡可以建立在 OSI 网络模型的不同级别上,主要是在 L4(传输层,例如 TCP/UDP)和 L7(应用层,例如 HTTP)上。在 Kubernetes 中,Services是 L4 的抽象,LoadBalancer 类型负载均衡依然有局限性,同时我们看到每创建一个 service 对应的负载均衡器都会消耗一个静态 IP,这并不合理。当然 k8s 中的另一种资源对象 ingress 可工作在 L7 层实现应用程序协议(HTTP/HTTPS)的负载均衡。
[root@master ~]#curl 192.168.10.170 # 反应可能比较慢,我等待了几分钟,不知道为啥
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
K8s 网关选型初判:Nginx 还是 Envoy?
我们所说的 Ingress 包含两个部分:
外部流量进入集群时先经过 ingress-controller,然后根据 ingress 配置的路由规则将请求转发到后端 service。
ingress-controller 其实就是守护进程加一个反向代理的应用,守护进程不断监听集群中资源的变化,将 ingress 中的配置信息生成反向代理配置。在 nginx-ingress controller 中即生成 nginx.conf 的配置文件。
我们上面已经配置好了 loadbalancer 的服务,这样我们创建一个 type 为 LoadBalancer 的 service 关联这组 pod,再把域名解析指向该地址,就实现了集群服务的对外暴露。当然也可以使用 NodePort、Hostnetwork 的方式,本文不讨论。
PS:先前实验用的 v1.17.0,做 ingress 实验的时候换成了 v1.22.5,LoadBalancer 实验按照这个链接重做了一下。
ingress-controller 不是 k8s 内部组件,可以通过 helm 或资源清单方式安装。
[root@master ~]#kubectl apply -f http://49.232.8.65/yaml/deploy.yaml
......
[root@master ~]#kubectl get pods,svc -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/ingress-nginx-admission-create--1-srl4n 0/1 Completed 0 2m12s 10.244.1.3 node01
pod/ingress-nginx-admission-patch--1-vczxf 0/1 Completed 2 2m12s 10.244.2.3 node02
pod/ingress-nginx-controller-867b8ccd99-7gdhf 1/1 Running 0 2m12s 10.244.2.4 node02
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/ingress-nginx-controller NodePort 10.101.83.140 80:31700/TCP,443:30401/TCP 2m12s app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
service/ingress-nginx-controller-admission ClusterIP 10.101.43.212 443/TCP 2m12s app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
编辑 service 修改 spec.type 为 LoadBalancer
[root@master ~]#kubectl edit service/ingress-nginx-controller -n ingress-nginx
......
spec:
clusterIP: 10.101.83.140
clusterIPs:
- 10.101.83.140
......
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
sessionAffinity: None
type: LoadBalancer
......
这样我们创建好了 nginx-ingress controller,下一步就要配置 ingress 路由规则。
host:http://k8s.com
基于 url 的路由:
/api/v1
/api/v2
这两个 url 分别路由到不同的 service 中。
[root@master ~]#vim ingress.yaml
[root@master ~]#cat ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test
namespace: training
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: k8s.com
http:
paths:
- pathType: Prefix
path: /api/v1
backend:
service:
name: service-apiv1
port:
number: 80
- pathType: Prefix
path: /api/v2
backend:
service:
name: service-apiv2
port:
number: 80
[root@master ~]#kubectl create ns training
namespace/training created
[root@master ~]#kubectl apply -f ingress.yaml
ingress.networking.k8s.io/test created
[root@master ~]#kubectl get ingress -n training -o wide
NAME CLASS HOSTS ADDRESS PORTS AGE
test k8s.com 80 45s
ingress.kubernetes.io/rewrite-target
是 nginx-ingress controller 的一个注解,当后端服务中暴露的 URL 与 Ingress 规则中指定的路径不同时可以通过此重定向。
查看 svc 可以看到此时控制器已经获得了一个 EXTERNAL-IP
现在 nginx-ingress controller 和 ingress 路由规则都有了
通过 CCE 使用 K8S_Ingress
[root@master ~]#kubectl get svc -n ingress-nginx # 这个 EIP 是弹性 IP,我们是模拟出来的,在公有云上是公网的 IP,是购买的 LB 给的(假如我们用的百度云,这个 EIP 就是 BLB 给的,百度的公网负载均衡器,一个集群只需要一个 EIP,作为七层的集群入口。PS:在公有云上 ingress 一般是一台单独的机器,它和 nginx 类似,如果运行在 node 节点,流量太大可能接不住。可以将节点 IP 作为 EIP。)
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.101.83.140 192.168.1.241 80:31700/TCP,443:30401/TCP 44m
ingress-nginx-controller-admission ClusterIP 10.101.43.212 443/TCP
我们可以进入到 nginx-ingress controller pod 中查看 nginx.conf 可以看到此时我们的 ingress 配置已经被生成为路由规则
[root@master ~]#kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create--1-srl4n 0/1 Completed 0 90m
ingress-nginx-admission-patch--1-vczxf 0/1 Completed 2 90m
ingress-nginx-controller-867b8ccd99-7gdhf 1/1 Running 0 90m
[root@master ~]#kubectl exec -it ingress-nginx-controller-867b8ccd99-7gdhf -n ingress-nginx /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
bash-5.1$ ls
fastcgi.conf mime.types scgi_params
fastcgi.conf.default mime.types.default scgi_params.default
fastcgi_params modsecurity template
fastcgi_params.default modules uwsgi_params
geoip nginx.conf uwsgi_params.default
koi-utf nginx.conf.default win-utf
koi-win opentracing.json
lua owasp-modsecurity-crs
bash-5.1$ cat nginx.conf
......
接下来就是指定我们的 backend,即上面的 server-apiv1/2。我们添加两个用于暴露的 service 和 deployment,和 loadbalancer 中测试清单一样,我们稍稍修改一下名称即可。
[root@master ~]#vim backend1.yaml
[root@master ~]#vim backend2.yaml
[root@master ~]#cat backend1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-apiv1
namespace: training
spec:
selector:
matchLabels:
app: nginx-apiv1
template:
metadata:
labels:
app: nginx-apiv1
spec:
containers:
- name: nginx-apiv1
image: nginx:latest
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: training
name: service-apiv1
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-apiv1
type: NodePort
[root@master ~]#cat backend2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-apiv2
namespace: training
spec:
selector:
matchLabels:
app: nginx-apiv2
template:
metadata:
labels:
app: nginx-apiv2
spec:
containers:
- name: nginx-apiv2
image: nginx:latest
ports:
- name: http
containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: training
name: service-apiv2
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-apiv2
type: NodePort
[root@master ~]#kubectl apply -f backend1.yaml
deployment.apps/nginx-apiv1 created
service/service-apiv1 created
[root@master ~]#kubectl apply -f backend2.yaml
deployment.apps/nginx-apiv2 created
service/service-apiv2 created
[root@master ~]#kubectl get pods,svc -o wide -n training
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-apiv1-587fb4fd66-ndvhp 1/1 Running 0 48s 10.244.1.4 node01
pod/nginx-apiv2-c55b5bf96-dkkfj 1/1 Running 0 45s 10.244.2.5 node02
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/service-apiv1 NodePort 10.105.238.31 80:31845/TCP 48s app=nginx-apiv1
service/service-apiv2 NodePort 10.98.58.30 80:31481/TCP 45s app=nginx-apiv2
修改 hosts 解析,C:\Windows\System32\drivers\etc\hosts,我们是模拟的假的域名,所以需要修改,真的域名有公网的 DNS 解析
修改后端访问的 nginx pod 的 index.html,不如访问会 404
pod 中无法使用 vi 解决:
apt-get update
apt-get install vim
[root@master ~]#kubectl get pods -n training
NAME READY STATUS RESTARTS AGE
nginx-apiv1-587fb4fd66-ndvhp 1/1 Running 0 25m
nginx-apiv2-c55b5bf96-dkkfj 1/1 Running 0 25m
[root@master ~]#kubectl exec -it nginx-apiv1-587fb4fd66-ndvhp -n training /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-apiv1-587fb4fd66-ndvhp:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@nginx-apiv1-587fb4fd66-ndvhp:/# cd /usr/share/nginx/html/
root@nginx-apiv1-587fb4fd66-ndvhp:/usr/share/nginx/html# ls
50x.html index.html
注意:Linux 访问就修改 /etc/hosts,Windows 访问就修改 C:\Windows\System32\drivers\etc\hosts