关于 OpenShift(OKD) 网络 Service、Routes的一些笔记

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波

写在前面


  • 因为参加考试,会陆续分享一些 OpenShift 的笔记
  • 博文内容为 OpenShift 网络相关组件 Service、Routes 很浅的一些认识
  • 学习环境为 openshift v3 的版本,有些旧
  • 这里如果专门学习 openshift ,建议学习 v4 版本
  • 理解不足小伙伴帮忙指正

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波


软件定义网络(SDN)

SDN 即软件定义网络,一种网络架构理念,倡导网络架构与底层基础架构分离,简单讲,SDN 即使用软件方式实现传统硬件交换机和路由器完成的工作,在物理网络上实现多个虚拟的网络。

当前容器平台常用的软件定义网络解决方案或组件有开源的 Weave NetflannelCalicoOpen vSwitch

不同软件定义网络的解决方案或组件各有特点,如 Weave 、 flannel 及 Calico 配置比较简单,功能比较基础 。 Open vSwitch 比较成熟且功能强大,配置起来相对比较复杂

容器网络

默认 docker 使用虚拟网桥,同一个主机上所有容器都连接到该网桥,这些容器利用 Linux namesacp 技术,创建了一对 虚拟网卡,一端在容器内,一端在网桥,这对虚拟网卡类似一个通道一样,两端可以直接通信,同时这些容器只能与同一台主机上的其他容器通信,无法与其他主机上容器通信。

以前 容器跨主机通信中 解决方法,Host 模式端口绑定 的方式都有一定的局限的,通过 SDN 定义专门的容器网络,使网络架构更加灵活,更适用于云计算数据中心的要求.

Host模式

容器直接使用宿主机的网络,这样天生就可以支持跨主机通信。这种方式虽然可以解决跨主机通信问题,但应用场景很有限,容易出现端口冲突,也无法做到隔离网络环境,一个容器崩溃很可能引起整个宿主机的崩溃。

端口绑定

通过绑定容器端口到宿主机端口,跨主机通信时使用“主机IP+端口的方式访问容器中的服务。显然,这种方式 仅能支持网络栈的4层及以上的应用,·并且容器与宿主机紧耦合,很难灵活地处理问题,可扩展性不佳。

定义容器网络

使用 Open vSwitch 或 Flannel/Calico 等第三方SDN工具,为容器构建可以跨主机通信的网络环境。这类方案一般要求各个主机上的 Dockero 网桥的 cidr 不同,以避免出现IP冲突的问题,限制容器在宿主机上可获取的IP范围。并且在容器需要对集群外提供服务时,需要比较复杂的配置,对部署实施人员的网络技能要求比较高。

OpenShift 容器网络

OpenShift 3.9 版本使用 Open vSwitch 构建和维护 OpenShift 网络。 基于 Open vSwitch ,OpenShift 提供了三种不同网络方案:

  • ovs-subnet 插件,默认插件,提供一个 flat 网络,该网络中所有 pod 和 service 可以互相访问。
  • ovs-multitenant 插件,用于隔离 pods 和 service ,每个 project 都会分配一个唯一的虚拟网络 ID(VNID),每个项目中的 pod 都无法与其他项目中 pod 通信。VNID 为 0 的项目中 pod 可以与其他项目中所有 pod 通信,default 项目的 VNID 是 0。
  • ovs-networkpolicy 插件,支持管理员使用 NetworkPolicy 对象定义自己的隔离策略。

默认 master 是不允许通过集群网络访问容器,除非 master 节点同时配置为 nodes 节点。

在 OKD 默认安装环境中,每个 pod 都会获得唯一 ip 地址。一个 pod 中所有容器,就像在同一个主机上。每个 pod 分配一个 ip 意味着,可以把每个 pod 看做是一个物理机或者虚拟机。

OpenShift Service

service 是 Kubernetes 中一个核心的对象,通过 kube-proxy 或者 Ingress 为多个 pods 提供前端负载均衡。service 提供一个稳定的 IP 地址,与后端的多个 pods 通信,而客户端不需要跟踪各个 pod IP 地址。只需要知道 SVC 的地址或者 服务向外发布的地址即可。

在这里插入图片描述

Kubernetes 中的 Service 解决的问题

  • Pod 的 IP 地址是不可靠的,当 Pod 所在的 Node 发生故障时,Pod 将被 Kubernetes 重新调度到另一个 Node,Pod 的 IP 地址将发生变化。无法被跟踪,
  • 如果容器应用本身是分布式的部署方式,做水平扩展,通过多个实例共同提供服务,需要在这些实例的前端设置一个负载均衡器来实现请求的分发。

下面的配置为一个 Service 的核心部分,这里为了方便,我们使用 json 的格式展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──[root@vms16.liruilongs.github.io]-[~]
└─$oc get svc hello-openshift -o json | jq .spec
{
"clusterIP": "172.30.169.91",
"ports": [
{
"name": "8080-tcp",
"port": 8080,
"protocol": "TCP",
"targetPort": 8080
},
{
"name": "8888-tcp",
"port": 8888,
"protocol": "TCP",
"targetPort": 8888
}
],
"selector": {
"app": "hello-openshift",
"deploymentconfig": "hello-openshift"
},
"sessionAffinity": "None",
"type": "ClusterIP"
}

具体的属性我们来了解下

  • clusterIP : 指定当前服务发布的 IP ,这个 IP 是作为 负载均衡的 入口地址
  • ports: 为服务发布的端口号,这里的端口 是 Service 提供能力的具体端口,对应 Pod 暴露的 端口,可以看到这是一个多端口的 Service
1
2
3
4
5
6
┌──[root@vms16.liruilongs.github.io]-[~]
└─$oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-openshift ClusterIP 172.30.169.91 <none> 8080/TCP,8888/TCP 11h
┌──[root@vms16.liruilongs.github.io]-[~]
└─$
  • selector : 选择器,K8s 通过标签的方式来选择对应的 后端能力提供者, 可以选择 deployment,replicaset,pod,replicationcontrollerd 等。选择后会自动创建对应的 Endpoint
1
2
3
4
5
6
7
8
┌──[root@vms16.liruilongs.github.io]-[~]
└─$oc get svc hello-openshift --show-labels
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE LABELS
hello-openshift ClusterIP 172.30.169.91 <none> 8080/TCP,8888/TCP 15h app=hello-openshift
┌──[root@vms16.liruilongs.github.io]-[~]
└─$oc get pods --selector=app=hello-openshift
NAME READY STATUS RESTARTS AGE
hello-openshift-1-j46gc 1/1 Running 0 15h
  • sessionAffinity : 负载分发策略, 目前 K8s 提供了两个负载分发策略:RoundRobin(轮询)SessionAffinity(基于客户端IP进行会话保持), 在默认情况下,Kubernetes 采用 RoundRobin 轮询模式对客户端请求进行负载分发,可以通过设置 service.spec.sessionAffinity=ClientIP 来启用 SessionAffinity 策略。还有最后一个 type.

type 用于定义发布的服务类型,默认为 ClusterIP

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问
  • NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。Kubernetes master 将从给定的配置范围内(默认:30000-32767)分配端口,每个 Node 将从该端口(每个 Node 上的同一端口)代理到 Service。该端口将通过 Service 的 spec.ports[*].nodePort 字段被指定。
  • LoadBalancer:外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。使用支持外部负载均衡器的云提供商的服务,设置 type 的值为”LoadBalancer”,将为 Service 提供负载均衡器。

关于 Service 的创建这里不做分享, OKD 中,大多数情况下会自动创建对应的 Service 资源对象。

OpenShift Routes

RouterOKD 所特有的,在 K8s 体系中,没有对应的API资源,包括上面我们谈到的 DC(DeploymentConfig)、IS 等。

Route 由共享的 router service 提供,以 实例方式方式运行在 pod 中,可以像普通 pod 一样水平扩展。

router service 是基于开源软件 HAProxy,提供集群外客户端访问集群内 pod

1
2
3
4
5
[student@master ~]$ oc get route -o wide
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
docker-registry docker-registry-default.apps.lab.example.com docker-registry <all> passthrough None
registry-console registry-console-default.apps.lab.example.com registry-console <all> passthrough None
[student@master ~]$

route 连接公网 IP 和内部 service IP 地址 。为了提高性能和减少延迟,OpenShift 路由器直接连接 pod ,此时 service 仅仅提供查询 endpoints 功能。管理员需要配置一个公网可以解析的主机名指向运行 route 的 node 公网 IP。典型地方式是使用 DNS wildcard 配置。

在这里插入图片描述

创建 Routes

yaml 文件方式创建

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Route
metadata:
name: hello
spec:
host: hello.apps.lab.example.com
port:
targetPort: 8080-tcp #代表 svc 中某个 port 名称,svc 可以定义多个 port
to:
kind: Service
name: hello

直接通过命令行创建, 查看帮助文档

1
2
3
4
oc expose -h
oc expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|U
DP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-s
ervice] [--type=type] [options]

一个Demo

1
oc expose service nginx --name www --hostname=www.apps.lab.example.com

以下是该命令及其选项的详细说明:

因此,当您运行此命令时,它将创建一个名为 www 的路由,其主机名为 www.apps.lab.example.com,将 nginx 服务暴露到 OpenShift 集群之外。

执行不带 --hostname 选项的 oc expose 命令创建的 routes 名称格式:

1
<route-name>-<project-name>.<default-domain>
  • route-name 代表路由名或者参考资源名(对于 oc new-app 命令代表模板,对于 oc expose 命令代表 service)
  • project-name 代表该资源所属 project 名
  • default-domain 配置在 master 节点,匹配通配 DNS 域。

示例,test 项目中名称为 quote 的路由器,显示为:quote-test.cloudapps.example.com

查找默认 routing 子域

默认 routing 子域定义在 master 节点 /etc/origin/master/master-config.yaml 配置文件 routingConfig 块。

示例:

1
2
3
4
[root@master master]# cat master-config.yaml | grep -A 1 routingConfig
routingConfig:
subdomain: apps.lab.example.com
[root@master master]#

OpenShift HAProxy router 默认监听 80 和 443 端口,所以路由只能创建在那些 80 和 443 端口未使用的 node 上。否则只能更改路由监听端口,在路由部署配置文件中设置环境变量 ROUTER_SERVICE_HTTP_PORTROUTER_SERVICE_HTTPS_PORT

router 支持协议
  • HTTP
  • HTTPS with SNI
  • WebSockets
  • TLS with SNI

Routing 选择 和 类型

route 可以加密,也可以不加密。安全的 route 支持使用多种 TLS 加密。非安全的路由配置最简单,不需要 key 和证书

安全路由指定路由 TLS termination。可用的 TLS termination 类型如下:

  • Edge Termination,流量到达 pod 前,在 router 上就终止了。TLS 证书由 router 提供,因此路由必须配置 TLS 证书,否则 TLS termination 使用路由默认证书。由于 TLS 是在 router 上终止的,所以 router 到 endpoints 之间内部网络是非加密的。
  • Pass-through Termination,加密的流量直接发送给目的 pod,不需要路由提供 TLS termination。不需要 key 和证书。目的 pod 提供证书。这也是当前唯一支持客户端证书的方法(双向认证)。
  • Re-encryption Termination,基于 edge termination,但是 route 与目的 pod之间通信会再次加密,可能会使用不同证书。所有路径都做了加密,包括内部网络。route 可以使用健康检查确定 host 认证。

创建安全 route

创建安全路由前,需要生产 TLS 证书。

示例,为名称是 test.apps.lab.example.com 路由创建自签名证书。

  1. 创建私钥。openssl genrsa -out example.key 2048
  2. 使用私钥创建 certificate signing request(csr)。
    1
    openssl req -new -key example.key -out example.csr -subj "/C=US/ST=CA/L=LosAngeles/O=Example/OU=IT/CN=test.apps.lab.example.com"
  3. 使用 key 和 CSR 创建证书。
    1
    openssl x509 -req -days 366 -in example.csr -signkey example.key -out example.crt
  4. 创建路由
    1
    oc create route edge --service=test --hostname=test.apps.lab.example.com --key=example.key --cert=example.crt

访问加密路由 https://test.apps.lab.example.com

可以把证书相关的写成 shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@master ~]# cat gencert.sh
#!/bin/bash

echo "Generating a private key..."
openssl genrsa -out $1.key 2048
echo

echo "Generating a CSR..."
openssl req -new -key $1.key -out $1.csr -subj "/C=US/ST=NC/L=Raleigh/O=RedHat/OU=RHT/CN=$1"
echo

echo "Generating a certificate..."
openssl x509 -req -days 366 -in $1.csr -signkey $1.key -out $1.crt
echo
echo "DONE."
echo
[root@master ~]#

Demo,创建一个支持 https 的路由

1
2
3
4
5
6
7
8
9
[root@master ~]# oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
greeter ClusterIP 172.30.106.99 <none> 8080/TCP,8888/TCP 6m
[root@master ~]# oc create route edge --service=greeter --hostname=greeter.apps.lab.example.com --key=greeter.apps.lab.example.com.key --cert=greeter.apps.lab.example.com.crt
route "greeter" created
[root@master ~]# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
greeter greeter.apps.lab.example.com greeter 8080-tcp edge None
[root@master ~]# curl greeter.apps.lab.example.com

博文部分内容参考

© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知,这是一个开源项目,如果你认可它,不要吝啬星星哦 :)


《OKD 3.9 DO280 Red Hat OpenShift Administration I》

《开源容器云OpenShift:构建基于Kubernetes的企业应用云平台》

https://docs.okd.io/latest/welcome/index.html


© 2018-2023 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)

发布于

2023-04-17

更新于

2023-06-21

许可协议

评论
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×