背景
从源码了解istio如何构建listener
源码
listener相关
pilot/pkg/networking/core/listener.go中
构建sidecar listener配置
func (configgen *ConfigGeneratorImpl) buildSidecarListeners(builder *ListenerBuilder) *ListenerBuilder {
if builder.push.Mesh.ProxyListenPort > 0 {
builder.appendSidecarInboundListeners().
appendSidecarOutboundListeners().
buildHTTPProxyListener().
buildVirtualOutboundListener()
}
return builder
}
构建所有sidecar outbound listener配置
func (lb *ListenerBuilder) buildSidecarOutboundListeners(node *model.Proxy,
push *model.PushContext,
) []*listener.Listener {
...
获取通配符和本地主机名
actualWildcards, actualLocalHosts := getWildcardsAndLocalHost(node.GetIPMode())
...
for _, egressListener := range node.SidecarScope.EgressListeners {
...
如果egressListener.IstioListener.Bind非空,则使用egressListener.IstioListener.Bind
if egressListener.IstioListener != nil && egressListener.IstioListener.Bind != "" {
bind.binds = []string{egressListener.IstioListener.Bind}
}
...
if egressListener.IstioListener != nil &&
egressListener.IstioListener.Port != nil {
for _, service := range services {
listenerOpts := outboundListenerOpts{
push: push,
proxy: node,
bind: bind,
port: listenPort,
service: service,
}
// Set service specific attributes here.
lb.buildSidecarOutboundListener(listenerOpts, listenerMap, virtualServices, actualWildcards)
}
} else {
...
for _, service := range services {
saddress := service.GetAddressForProxy(node)
for _, servicePort := range service.Ports {
...
listenerOpts := outboundListenerOpts{
push: push,
proxy: node,
bind: bind,
port: servicePort,
service: service,
}
...
lb.buildSidecarOutboundListener(listenerOpts, listenerMap, virtualServices, actualWildcards)
}
}
...
}
...
}
...
}
构建sidecar outbound listener配置
func (lb *ListenerBuilder) buildSidecarOutboundListener(listenerOpts outboundListenerOpts,
listenerMap map[listenerKey]*outboundListenerEntry, virtualServices []*config.Config, actualWildcards []string,
) {
...
内部端口协议转换为listener协议
listenerProtocol := istionetworking.ModelProtocolToListenerProtocol(listenerOpts.port.Protocol)
...
switch listenerProtocol {
case istionetworking.ListenerProtocolTCP, istionetworking.ListenerProtocolAuto:
...
if listenerOpts.bind.Primary() == "" {
获取服务监听地址
svcListenAddress := listenerOpts.service.GetAddressForProxy(listenerOpts.proxy)
if len(svcListenAddress) > 0 {
if !strings.Contains(svcListenAddress, "/") {
设置svc地址作为listener地址
listenerOpts.bind.binds = append([]string{svcListenAddress}, svcExtraListenAddresses...)
} else {
设置通配符作为listener地址
listenerOpts.bind.binds = actualWildcards
listenerOpts.cidr = append([]string{svcListenAddress}, svcExtraListenAddresses...)
}
}
}
...
case istionetworking.ListenerProtocolHTTP:
设置通配符作为listener地址
if len(listenerOpts.bind.Primary()) == 0 {
listenerOpts.bind.binds = actualWildcards
}
listenerMapKey = listenerKey{listenerOpts.bind.Primary(), listenerOpts.port.Port}
...
}
...
}
pilot/pkg/networking/core/listener_builder.go中
添加sidecar outbound listener配置
func (lb *ListenerBuilder) appendSidecarOutboundListeners() *ListenerBuilder {
lb.outboundListeners = lb.buildSidecarOutboundListeners(lb.node, lb.push)
return lb
}
协议相关
pilot/pkg/serviceregistry/kube/conversion.go中
k8s port到内部端口表示转换
func convertPort(port corev1.ServicePort) *model.Port {
return &model.Port{
Name: port.Name,
Port: int(port.Port),
Protocol: kube.ConvertProtocol(port.Port, port.Name, port.Protocol, port.AppProtocol),
}
}
pkg/config/kube/conversion.go中
转换协议
func ConvertProtocol(port int32, portName string, proto corev1.Protocol, appProto *string) protocol.Instance {
// 如果协议是UDP,则返回UDP
if proto == corev1.ProtocolUDP {
return protocol.UDP
}
根据appProto判断协议
name := portName
if appProto != nil {
name = *appProto
switch name {
case "kubernetes.io/h2c":
return protocol.HTTP2
case "kubernetes.io/ws":
return protocol.HTTP
case "kubernetes.io/wss":
return protocol.HTTPS
}
}
如果端口名是grpc-web开头,则返回GRPCWeb协议
if len(name) >= grpcWebLen && strings.EqualFold(name[:grpcWebLen], grpcWeb) {
return protocol.GRPCWeb
}
仅保留-前面的内容
i := strings.IndexByte(name, '-')
if i >= 0 {
name = name[:i]
}
从端口名解析协议
p := protocol.Parse(name)
如果不支持的协议判断是不是知名端口
if p == protocol.Unsupported {
if wellKnownPorts.Contains(port) {
return protocol.TCP
}
}
return p
}
知名端口
const (
SMTP = 25
DNS = 53
MySQL = 3306
MongoDB = 27017
)
var wellKnownPorts = sets.New[int32](
SMTP,
DNS,
MySQL,
MongoDB,
)
pkg/config/protocol/instance.go
从端口名解析协议
func Parse(s string) Instance {
switch strings.ToLower(s) {
case "tcp":
return TCP
case "udp":
return UDP
case "grpc":
return GRPC
case "grpc-web":
return GRPCWeb
case "http":
return HTTP
case "http_proxy":
return HTTP_PROXY
case "http2":
return HTTP2
case "https":
return HTTPS
case "tls":
return TLS
case "mongo":
return Mongo
case "redis":
return Redis
case "mysql":
return MySQL
case "DoubleHBONE":
return DoubleHBONE
}
return Unsupported
}
pilot/pkg/networking/networking.go中
内部端口协议转换为listener协议
func ModelProtocolToListenerProtocol(p protocol.Instance) ListenerProtocol {
switch p {
case protocol.HTTP, protocol.HTTP2, protocol.HTTP_PROXY, protocol.GRPC, protocol.GRPCWeb:
return ListenerProtocolHTTP
case protocol.TCP, protocol.HTTPS, protocol.TLS,
protocol.Mongo, protocol.Redis, protocol.MySQL:
return ListenerProtocolTCP
case protocol.UDP:
return ListenerProtocolUnknown
case protocol.Unsupported:
return ListenerProtocolAuto
default:
return ListenerProtocolAuto
}
}