5.4.2 服务条目的定义参数

服务条目(ServiceEntry)能够在Istio内部的服务注册表中加入额外的服务,它描述了服务的属性,包括DNS名称、虚拟IP、端口、协议以及端点等。在Istio中,服务条目定义的参数参见表5-3。

表5-3 服务条目的定义参数

ServiceEntry代码位于Istio代码包中的/istio.io/istio/pilot/pkg/model/validation.go,通过代码分析,可以看到:Host字段要求至少包含一个字符型的地址,并且值不能为星号,也不能只包含短域名,支持完全限定域名FQDN,或者带有通配符前缀的DNS名称(即第一段是通配符,其他段符合RFC 1123定义):


// ValidateServiceEntry validates a service entry.
func ValidateServiceEntry(name, namespace string, config proto.Message) (errs error) {
 ....
    // serviceentry至少包含一个host地址
    if len(serviceEntry.Hosts) == 0 {
    errs = appendErrors(errs, fmt.Errorf("service entry must have at least one  host"))
   }
   for _, host := range serviceEntry.Hosts {
    // host值不能为星号*,也不能只包含短域名,支持完全限定域名FQDN,或者带有通配符前缀的 DNS 名称
    if host == "*" || !strings.Contains(host, ".") {
      errs = appendErrors(errs, fmt.Errorf("invalid host %s", host))
    } else {
      errs = appendErrors(errs, ValidateWildcardDomain(host))
    }
  }   
}

Addresses字段可以是具体的IP地址或者是CIDR地址段。如果是CIDR形式,那么Resolution必须是None或者static,并且应当是点分十进制(Dotted Decimal Notation)形式,例如a.b.c.d/xx或者a.b.c.d:


cidrFound := false
for _, address := range serviceEntry.Addresses {
   cidrFound = cidrFound || strings.Contains(address, "/")
   errs = appendErrors(errs, ValidateIPv4Subnet(address))
}

if cidrFound {
   if serviceEntry.Resolution != networking.ServiceEntry_NONE && serviceEntry.Resolution != networking.ServiceEntry_STATIC {
      errs = appendErrors(errs, fmt.Errorf("CIDR addresses are allowed only for 
NONE/STATIC resolution types"))
   }
}

Port的名称和端口不能重复;端口范围为1与65535之间。支持的协议类型包括:TCP、UDP、GRPC、HTTP、HTTP2、HTTPS、TLS、Mongo以及Redis等:


servicePortNumbers := make(map[uint32]bool)
servicePorts := make(map[string]bool, len(serviceEntry.Ports))
for _, port := range serviceEntry.Ports {
   if servicePorts[port.Name] {
      errs = appendErrors(errs, fmt.Errorf("service entry port name %q already defined", port.Name))
   }
   servicePorts[port.Name] = true
   if servicePortNumbers[port.Number] {
      errs = appendErrors(errs, fmt.Errorf("service entry port %d already defined", 
port.Number))
   }
   servicePortNumbers[port.Number] = true
}
...
func ValidatePort(port int) error {
   if 1 <= port && port <= 65535 {
      return nil
   }
   return fmt.Errorf("port number %d must be in the range 1..65535", port)
}

Resolution字段为None时,endpoints应当为空;其值为static时,endpoints必须提供,并且其属性address值必须为IP v4的地址,或者以unix:///absolute/path/to/socket的形式使用Unix socket。其值为DNS时,如果没有提供endpoints,那么host值必须提供,并且值只能为完全限定域名FQDN;如果提供了endpoints,那么其属性address值必须为完全限定域名FQDN:


switch serviceEntry.Resolution {
  case networking.ServiceEntry_NONE:
    if len(serviceEntry.Endpoints) != 0 {
      errs = appendErrors(errs, fmt.Errorf("no endpoints should be provided for 
discovery type none"))
    }
  case networking.ServiceEntry_STATIC:
    if len(serviceEntry.Endpoints) == 0 {
      errs = appendErrors(errs,
        fmt.Errorf("endpoints must be provided if service entry discovery mode is static"))
    }
    ....
  case networking.ServiceEntry_DNS:
    if len(serviceEntry.Endpoints) == 0 {
      for _, host := range serviceEntry.Hosts {
        if err := ValidateFQDN(host); err != nil {
          errs = appendErrors(errs,
            fmt.Errorf("hosts must be FQDN if no endpoints are provided for discovery mode DNS"))
        }
      }
    }

    for _, endpoint := range serviceEntry.Endpoints {
      errs = appendErrors(errs,
        ValidateFQDN(endpoint.Address),
        Labels(endpoint.Labels).Validate())

      for name, port := range endpoint.Ports {
        if !servicePorts[name] {
          errs = appendErrors(errs, fmt.Errorf("endpoint port %v is not defined by 
the service entry", port))
        }
        errs = appendErrors(errs,
          validatePortName(name),
          ValidatePort(int(port)))
      }
    }
  default:
    errs = appendErrors(errs, fmt.Errorf("unsupported resolution type %s",
      networking.ServiceEntry_Resolution_name[int32(serviceEntry.Resolution)]))
  }