服务条目(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)]))
}