作为业内首个全托管Istio兼容的阿里云服务网格产品ASM,一开始从架构上就保持了与社区、业界趋势的一致性,控制平面的组件托管在阿里云侧,与数据面侧的用户集群独立。ASM产品是基于社区Istio定制实现的,在托管的控制面侧提供了用于支撑精细化的流量管理和安全管理的组件能力。通过托管模式,解耦了Istio组件与所管理的K8s集群的生命周期管理,使得架构更加灵活,提升了系统的可伸缩性。
本文介绍如何在应用监听localhost的情况下,如何通过配置Sidecar资源让监听localhost的应用可以被集群中其它Pod通过Service访问。
问题现象
当部署在集群中的应用监听localhost时,即使通过Service暴露应用的服务端口,该服务也无法被集群中的其他Pod访问。
比如,使用gunicorn部署flask应用,在代码中、gunicorn.conf.py 文件使用了如下配置:
workers = 10
worker_class = "gevent"
bind = "localhost:8000"
为应用创建Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test
name: test
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- image: 'xxxxxxxxxxtest:v2'
imagePullPolicy: IfNotPresent
name: test
ports:
- containerPort: 8000
name: http
protocol: TCP
对应在Kubernetes集群中为应用创建Service:
apiVersion: v1
kind: Service
metadata:
labels:
app: test
name: test
namespace: default
spec:
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 8000
selector:
app: test
此时,在集群中的其他Pod中访问test应用,发现访问是不通的
$ curl test:8000/calculate
upstream connect error or disconnect/reset before headers. reset reason: connection failure, transport failure reason: delayed connect error: 111
下面是一些不同语言的应用监听localhost的例子:
-
Golang: net.Listen("tcp", "localhost:8080")
-
Node.js: http.createServer().listen(8080, "localhost");
-
Python: socket.socket().bind(("localhost", 8083))
问题原因
当集群中应用监听localhost网络地址时,由于localhost是本地地址,集群中的其它Pod对其访问不通是正常现象,需要根据是否需要对外暴露应用来修改Service或通过Sidecar资源修改服务网格Sidecar代理配置。
解决方案
如果不希望对外暴露应用服务,则应该在选中该应用的Service端口声明中移除对应的端口声明。
如果希望对外暴露应用服务,可以选择以下两种方式:
1、修改应用监听的网络地址
如果希望应用提供的服务对外暴露,则最好修改应用代码,将应用监听的网络地址从localhost改为 0.0.0.0
2、使用服务网格暴露监听localhost的服务
如果不希望修改应用代码,同时需要将监听localhost的应用暴露给集群中其它Pod,可以选择基于服务网格ASM的Sidecar资源对Sidecar代理进行配置。
-
在左侧导航栏,选择 服务网格 > 网格管理 。
-
在 网格管理 页面,找到待配置的实例,单击实例的名称或在 操作 列中单击 管理 。
-
在网格详情页面左侧导航栏选择 流量管理中心 > Sidecar资源 ,然后在右侧页面单击 使用YAML创建 。
-
在YAML输入框中输入以下YAML,根据应用属性对大括号中的字段进行替换
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: localhost-access
namespace: {namespace}
spec:
ingress:
- defaultEndpoint: '127.0.0.1:{container_port}'
port:
name: tcp
number: {port}
protocol: TCP
workloadSelector:
labels:
{key}: {value}
其中,{namespace}需要替换为应用部署所在的命名空间,{container_port}需要替换为应用监听localhost的容器端口,port需要替换为应用的Service端口,{key} : {value}需要替换为选中应用Pod的标签。如何使得集群中监听localhost的应用能够被其它Pod访问
为应用注入Sidecar代理、并创建Sidecar资源后,再次尝试从应用外部访问,发现可以调通了!