mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
Merge pull request #12561 from ArtfulCoder/externalIPs
External IPs support
This commit is contained in:
commit
cd798a471f
@ -13430,12 +13430,12 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "type of this service; must be ClusterIP, NodePort, or LoadBalancer; defaults to ClusterIP; see http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services"
|
"description": "type of this service; must be ClusterIP, NodePort, or LoadBalancer; defaults to ClusterIP; see http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services"
|
||||||
},
|
},
|
||||||
"deprecatedPublicIPs": {
|
"externalIPs": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": "deprecated. externally visible IPs (e.g. load balancers) that should be proxied to this service"
|
"description": "externally visible IPs (e.g. load balancers) that should be proxied to this service"
|
||||||
},
|
},
|
||||||
"sessionAffinity": {
|
"sessionAffinity": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -749,6 +749,7 @@ _kubectl_expose()
|
|||||||
flags+=("--container-port=")
|
flags+=("--container-port=")
|
||||||
flags+=("--create-external-load-balancer")
|
flags+=("--create-external-load-balancer")
|
||||||
flags+=("--dry-run")
|
flags+=("--dry-run")
|
||||||
|
flags+=("--external-ip=")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--filename")
|
flags_with_completion+=("--filename")
|
||||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||||
@ -768,7 +769,6 @@ _kubectl_expose()
|
|||||||
flags+=("--overrides=")
|
flags+=("--overrides=")
|
||||||
flags+=("--port=")
|
flags+=("--port=")
|
||||||
flags+=("--protocol=")
|
flags+=("--protocol=")
|
||||||
flags+=("--public-ip=")
|
|
||||||
flags+=("--selector=")
|
flags+=("--selector=")
|
||||||
flags+=("--session-affinity=")
|
flags+=("--session-affinity=")
|
||||||
flags+=("--show-all")
|
flags+=("--show-all")
|
||||||
|
@ -34,6 +34,10 @@ re\-use the labels from the resource it exposes.
|
|||||||
\fB\-\-dry\-run\fP=false
|
\fB\-\-dry\-run\fP=false
|
||||||
If true, only print the object that would be sent, without creating it.
|
If true, only print the object that would be sent, without creating it.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
\fB\-\-external\-ip\fP=""
|
||||||
|
External IP address to set for the service. The service can be accessed by this IP in addition to its generated service IP.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
\fB\-f\fP, \fB\-\-filename\fP=[]
|
\fB\-f\fP, \fB\-\-filename\fP=[]
|
||||||
Filename, directory, or URL to a file identifying the resource to expose a service
|
Filename, directory, or URL to a file identifying the resource to expose a service
|
||||||
@ -78,10 +82,6 @@ re\-use the labels from the resource it exposes.
|
|||||||
\fB\-\-protocol\fP="TCP"
|
\fB\-\-protocol\fP="TCP"
|
||||||
The network protocol for the service to be created. Default is 'tcp'.
|
The network protocol for the service to be created. Default is 'tcp'.
|
||||||
|
|
||||||
.PP
|
|
||||||
\fB\-\-public\-ip\fP=""
|
|
||||||
Name of a public IP address to set for the service. The service will be assigned this IP in addition to its generated service IP.
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
\fB\-\-selector\fP=""
|
\fB\-\-selector\fP=""
|
||||||
A label selector to use for this service. If empty (the default) infer the selector from the replication controller.
|
A label selector to use for this service. If empty (the default) infer the selector from the replication controller.
|
||||||
|
@ -45,7 +45,7 @@ selector for a new Service on the specified port. If no labels are specified, th
|
|||||||
re-use the labels from the resource it exposes.
|
re-use the labels from the resource it exposes.
|
||||||
|
|
||||||
```
|
```
|
||||||
kubectl expose (-f FILENAME | TYPE NAME) --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--public-ip=ip] [--type=type]
|
kubectl expose (-f FILENAME | TYPE NAME) --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [----external-ip=external-ip-of-service] [--type=type]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
@ -70,6 +70,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
|
|||||||
--container-port="": Synonym for --target-port
|
--container-port="": Synonym for --target-port
|
||||||
--create-external-load-balancer[=false]: If true, create an external load balancer for this service (trumped by --type). Implementation is cloud provider dependent. Default is 'false'.
|
--create-external-load-balancer[=false]: If true, create an external load balancer for this service (trumped by --type). Implementation is cloud provider dependent. Default is 'false'.
|
||||||
--dry-run[=false]: If true, only print the object that would be sent, without creating it.
|
--dry-run[=false]: If true, only print the object that would be sent, without creating it.
|
||||||
|
--external-ip="": External IP address to set for the service. The service can be accessed by this IP in addition to its generated service IP.
|
||||||
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to expose a service
|
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to expose a service
|
||||||
--generator="service/v2": The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'.
|
--generator="service/v2": The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'.
|
||||||
-h, --help[=false]: help for expose
|
-h, --help[=false]: help for expose
|
||||||
@ -81,7 +82,6 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
|
|||||||
--overrides="": An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.
|
--overrides="": An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.
|
||||||
--port=-1: The port that the service should serve on. Copied from the resource being exposed, if unspecified
|
--port=-1: The port that the service should serve on. Copied from the resource being exposed, if unspecified
|
||||||
--protocol="TCP": The network protocol for the service to be created. Default is 'tcp'.
|
--protocol="TCP": The network protocol for the service to be created. Default is 'tcp'.
|
||||||
--public-ip="": Name of a public IP address to set for the service. The service will be assigned this IP in addition to its generated service IP.
|
|
||||||
--selector="": A label selector to use for this service. If empty (the default) infer the selector from the replication controller.
|
--selector="": A label selector to use for this service. If empty (the default) infer the selector from the replication controller.
|
||||||
--session-affinity="": If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'
|
--session-affinity="": If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'
|
||||||
-a, --show-all[=false]: When printing, show all resources (default hide terminated pods.)
|
-a, --show-all[=false]: When printing, show all resources (default hide terminated pods.)
|
||||||
@ -124,7 +124,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
|
|||||||
|
|
||||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||||
|
|
||||||
###### Auto generated by spf13/cobra at 2015-08-20 22:01:12.478645014 +0000 UTC
|
###### Auto generated by spf13/cobra at 2015-08-20 23:09:42.260392956 +0000 UTC
|
||||||
|
|
||||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||||
[]()
|
[]()
|
||||||
|
@ -77,6 +77,7 @@ executor-suicide-timeout
|
|||||||
experimental-keystone-url
|
experimental-keystone-url
|
||||||
experimental-prefix
|
experimental-prefix
|
||||||
external-hostname
|
external-hostname
|
||||||
|
external-ip
|
||||||
failover-timeout
|
failover-timeout
|
||||||
file-check-frequency
|
file-check-frequency
|
||||||
file-suffix
|
file-suffix
|
||||||
@ -180,7 +181,6 @@ proxy-bindall
|
|||||||
proxy-logv
|
proxy-logv
|
||||||
proxy-port-range
|
proxy-port-range
|
||||||
public-address-override
|
public-address-override
|
||||||
public-ip
|
|
||||||
pvclaimbinder-sync-period
|
pvclaimbinder-sync-period
|
||||||
read-only-port
|
read-only-port
|
||||||
really-crash-for-testing
|
really-crash-for-testing
|
||||||
|
@ -1949,13 +1949,13 @@ func deepCopy_api_ServiceSpec(in ServiceSpec, out *ServiceSpec, c *conversion.Cl
|
|||||||
}
|
}
|
||||||
out.ClusterIP = in.ClusterIP
|
out.ClusterIP = in.ClusterIP
|
||||||
out.Type = in.Type
|
out.Type = in.Type
|
||||||
if in.DeprecatedPublicIPs != nil {
|
if in.ExternalIPs != nil {
|
||||||
out.DeprecatedPublicIPs = make([]string, len(in.DeprecatedPublicIPs))
|
out.ExternalIPs = make([]string, len(in.ExternalIPs))
|
||||||
for i := range in.DeprecatedPublicIPs {
|
for i := range in.ExternalIPs {
|
||||||
out.DeprecatedPublicIPs[i] = in.DeprecatedPublicIPs[i]
|
out.ExternalIPs[i] = in.ExternalIPs[i]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.DeprecatedPublicIPs = nil
|
out.ExternalIPs = nil
|
||||||
}
|
}
|
||||||
out.SessionAffinity = in.SessionAffinity
|
out.SessionAffinity = in.SessionAffinity
|
||||||
return nil
|
return nil
|
||||||
|
@ -1190,10 +1190,9 @@ type ServiceSpec struct {
|
|||||||
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
|
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
|
||||||
Type ServiceType `json:"type,omitempty"`
|
Type ServiceType `json:"type,omitempty"`
|
||||||
|
|
||||||
// DeprecatedPublicIPs are deprecated and silently ignored.
|
// ExternalIPs are used by external load balancers, or can be set by
|
||||||
// Old behaviour: PublicIPs are used by external load balancers, or can be set by
|
|
||||||
// users to handle external traffic that arrives at a node.
|
// users to handle external traffic that arrives at a node.
|
||||||
DeprecatedPublicIPs []string `json:"deprecatedPublicIPs,omitempty"`
|
ExternalIPs []string `json:"externalIPs,omitempty"`
|
||||||
|
|
||||||
// Required: Supports "ClientIP" and "None". Used to maintain session affinity.
|
// Required: Supports "ClientIP" and "None". Used to maintain session affinity.
|
||||||
SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty"`
|
SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty"`
|
||||||
|
@ -2166,13 +2166,13 @@ func convert_api_ServiceSpec_To_v1_ServiceSpec(in *api.ServiceSpec, out *Service
|
|||||||
}
|
}
|
||||||
out.ClusterIP = in.ClusterIP
|
out.ClusterIP = in.ClusterIP
|
||||||
out.Type = ServiceType(in.Type)
|
out.Type = ServiceType(in.Type)
|
||||||
if in.DeprecatedPublicIPs != nil {
|
if in.ExternalIPs != nil {
|
||||||
out.DeprecatedPublicIPs = make([]string, len(in.DeprecatedPublicIPs))
|
out.ExternalIPs = make([]string, len(in.ExternalIPs))
|
||||||
for i := range in.DeprecatedPublicIPs {
|
for i := range in.ExternalIPs {
|
||||||
out.DeprecatedPublicIPs[i] = in.DeprecatedPublicIPs[i]
|
out.ExternalIPs[i] = in.ExternalIPs[i]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.DeprecatedPublicIPs = nil
|
out.ExternalIPs = nil
|
||||||
}
|
}
|
||||||
out.SessionAffinity = ServiceAffinity(in.SessionAffinity)
|
out.SessionAffinity = ServiceAffinity(in.SessionAffinity)
|
||||||
return nil
|
return nil
|
||||||
@ -4581,13 +4581,13 @@ func convert_v1_ServiceSpec_To_api_ServiceSpec(in *ServiceSpec, out *api.Service
|
|||||||
}
|
}
|
||||||
out.ClusterIP = in.ClusterIP
|
out.ClusterIP = in.ClusterIP
|
||||||
out.Type = api.ServiceType(in.Type)
|
out.Type = api.ServiceType(in.Type)
|
||||||
if in.DeprecatedPublicIPs != nil {
|
if in.ExternalIPs != nil {
|
||||||
out.DeprecatedPublicIPs = make([]string, len(in.DeprecatedPublicIPs))
|
out.ExternalIPs = make([]string, len(in.ExternalIPs))
|
||||||
for i := range in.DeprecatedPublicIPs {
|
for i := range in.ExternalIPs {
|
||||||
out.DeprecatedPublicIPs[i] = in.DeprecatedPublicIPs[i]
|
out.ExternalIPs[i] = in.ExternalIPs[i]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.DeprecatedPublicIPs = nil
|
out.ExternalIPs = nil
|
||||||
}
|
}
|
||||||
out.SessionAffinity = api.ServiceAffinity(in.SessionAffinity)
|
out.SessionAffinity = api.ServiceAffinity(in.SessionAffinity)
|
||||||
return nil
|
return nil
|
||||||
|
@ -1954,13 +1954,13 @@ func deepCopy_v1_ServiceSpec(in ServiceSpec, out *ServiceSpec, c *conversion.Clo
|
|||||||
}
|
}
|
||||||
out.ClusterIP = in.ClusterIP
|
out.ClusterIP = in.ClusterIP
|
||||||
out.Type = in.Type
|
out.Type = in.Type
|
||||||
if in.DeprecatedPublicIPs != nil {
|
if in.ExternalIPs != nil {
|
||||||
out.DeprecatedPublicIPs = make([]string, len(in.DeprecatedPublicIPs))
|
out.ExternalIPs = make([]string, len(in.ExternalIPs))
|
||||||
for i := range in.DeprecatedPublicIPs {
|
for i := range in.ExternalIPs {
|
||||||
out.DeprecatedPublicIPs[i] = in.DeprecatedPublicIPs[i]
|
out.ExternalIPs[i] = in.ExternalIPs[i]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.DeprecatedPublicIPs = nil
|
out.ExternalIPs = nil
|
||||||
}
|
}
|
||||||
out.SessionAffinity = in.SessionAffinity
|
out.SessionAffinity = in.SessionAffinity
|
||||||
return nil
|
return nil
|
||||||
|
@ -1150,7 +1150,7 @@ type ServiceSpec struct {
|
|||||||
|
|
||||||
// Deprecated. PublicIPs are used by external load balancers, or can be set by
|
// Deprecated. PublicIPs are used by external load balancers, or can be set by
|
||||||
// users to handle external traffic that arrives at a node.
|
// users to handle external traffic that arrives at a node.
|
||||||
DeprecatedPublicIPs []string `json:"deprecatedPublicIPs,omitempty" description:"deprecated. externally visible IPs (e.g. load balancers) that should be proxied to this service"`
|
ExternalIPs []string `json:"externalIPs,omitempty" description:"externally visible IPs (e.g. load balancers) that should be proxied to this service"`
|
||||||
|
|
||||||
// Optional: Supports "ClientIP" and "None". Used to maintain session affinity.
|
// Optional: Supports "ClientIP" and "None". Used to maintain session affinity.
|
||||||
SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty" description:"enable client IP based session affinity; must be ClientIP or None; defaults to None; see http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies"`
|
SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty" description:"enable client IP based session affinity; must be ClientIP or None; defaults to None; see http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies"`
|
||||||
|
@ -1090,12 +1090,11 @@ func ValidateService(service *api.Service) errs.ValidationErrorList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ip := range service.Spec.DeprecatedPublicIPs {
|
for _, ip := range service.Spec.ExternalIPs {
|
||||||
if ip == "0.0.0.0" {
|
if ip == "0.0.0.0" {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("spec.publicIPs", ip, "is not an IP address"))
|
allErrs = append(allErrs, errs.NewFieldInvalid("spec.externalIPs", ip, "is not an IP address"))
|
||||||
} else if util.IsValidIPv4(ip) && net.ParseIP(ip).IsLoopback() {
|
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("spec.publicIPs", ip, "publicIP cannot be a loopback"))
|
|
||||||
}
|
}
|
||||||
|
allErrs = append(allErrs, validateIpIsNotLinkLocalOrLoopback(ip, "spec.externalIPs")...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if service.Spec.Type == "" {
|
if service.Spec.Type == "" {
|
||||||
@ -1740,18 +1739,26 @@ func validateEndpointAddress(address *api.EndpointAddress) errs.ValidationErrorL
|
|||||||
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "invalid IPv4 address"))
|
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "invalid IPv4 address"))
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
// We disallow some IPs as endpoints. Specifically, loopback addresses are
|
return validateIpIsNotLinkLocalOrLoopback(address.IP, "ip")
|
||||||
// nonsensical and link-local addresses tend to be used for node-centric
|
}
|
||||||
// purposes (e.g. metadata service).
|
|
||||||
ip := net.ParseIP(address.IP)
|
func validateIpIsNotLinkLocalOrLoopback(ipAddress, fieldName string) errs.ValidationErrorList {
|
||||||
|
// We disallow some IPs as endpoints or external-ips. Specifically, loopback addresses are
|
||||||
|
// nonsensical and link-local addresses tend to be used for node-centric purposes (e.g. metadata service).
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
ip := net.ParseIP(ipAddress)
|
||||||
|
if ip == nil {
|
||||||
|
allErrs = append(allErrs, errs.NewFieldInvalid(fieldName, ipAddress, "not a valid IP address"))
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
if ip.IsLoopback() {
|
if ip.IsLoopback() {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the loopback range (127.0.0.0/8)"))
|
allErrs = append(allErrs, errs.NewFieldInvalid(fieldName, ipAddress, "may not be in the loopback range (127.0.0.0/8)"))
|
||||||
}
|
}
|
||||||
if ip.IsLinkLocalUnicast() {
|
if ip.IsLinkLocalUnicast() {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the link-local range (169.254.0.0/16)"))
|
allErrs = append(allErrs, errs.NewFieldInvalid(fieldName, ipAddress, "may not be in the link-local range (169.254.0.0/16)"))
|
||||||
}
|
}
|
||||||
if ip.IsLinkLocalMulticast() {
|
if ip.IsLinkLocalMulticast() {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("ip", address.IP, "may not be in the link-local multicast range (224.0.0.0/24)"))
|
allErrs = append(allErrs, errs.NewFieldInvalid(fieldName, ipAddress, "may not be in the link-local multicast range (224.0.0.0/24)"))
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
@ -1721,23 +1721,23 @@ func TestValidateService(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid publicIPs localhost",
|
name: "invalid publicIPs localhost",
|
||||||
tweakSvc: func(s *api.Service) {
|
tweakSvc: func(s *api.Service) {
|
||||||
s.Spec.DeprecatedPublicIPs = []string{"127.0.0.1"}
|
s.Spec.ExternalIPs = []string{"127.0.0.1"}
|
||||||
},
|
},
|
||||||
numErrs: 1,
|
numErrs: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid publicIPs",
|
name: "invalid publicIPs",
|
||||||
tweakSvc: func(s *api.Service) {
|
tweakSvc: func(s *api.Service) {
|
||||||
s.Spec.DeprecatedPublicIPs = []string{"0.0.0.0"}
|
s.Spec.ExternalIPs = []string{"0.0.0.0"}
|
||||||
},
|
},
|
||||||
numErrs: 1,
|
numErrs: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid publicIPs host",
|
name: "invalid publicIPs host",
|
||||||
tweakSvc: func(s *api.Service) {
|
tweakSvc: func(s *api.Service) {
|
||||||
s.Spec.DeprecatedPublicIPs = []string{"myhost.mydomain"}
|
s.Spec.ExternalIPs = []string{"myhost.mydomain"}
|
||||||
},
|
},
|
||||||
numErrs: 0,
|
numErrs: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "dup port name",
|
name: "dup port name",
|
||||||
|
@ -378,8 +378,8 @@ func (s *ServiceController) createExternalLoadBalancer(service *api.Service) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
name := s.loadBalancerName(service)
|
name := s.loadBalancerName(service)
|
||||||
if len(service.Spec.DeprecatedPublicIPs) > 0 {
|
if len(service.Spec.ExternalIPs) > 0 {
|
||||||
for _, publicIP := range service.Spec.DeprecatedPublicIPs {
|
for _, publicIP := range service.Spec.ExternalIPs {
|
||||||
// TODO: Make this actually work for multiple IPs by using different
|
// TODO: Make this actually work for multiple IPs by using different
|
||||||
// names for each. For now, we'll just create the first and break.
|
// names for each. For now, we'll just create the first and break.
|
||||||
status, err := s.balancer.EnsureTCPLoadBalancer(name, s.zone.Region, net.ParseIP(publicIP),
|
status, err := s.balancer.EnsureTCPLoadBalancer(name, s.zone.Region, net.ParseIP(publicIP),
|
||||||
@ -477,11 +477,11 @@ func needsUpdate(oldService *api.Service, newService *api.Service) bool {
|
|||||||
if !portsEqualForLB(oldService, newService) || oldService.Spec.SessionAffinity != newService.Spec.SessionAffinity {
|
if !portsEqualForLB(oldService, newService) || oldService.Spec.SessionAffinity != newService.Spec.SessionAffinity {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if len(oldService.Spec.DeprecatedPublicIPs) != len(newService.Spec.DeprecatedPublicIPs) {
|
if len(oldService.Spec.ExternalIPs) != len(newService.Spec.ExternalIPs) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for i := range oldService.Spec.DeprecatedPublicIPs {
|
for i := range oldService.Spec.ExternalIPs {
|
||||||
if oldService.Spec.DeprecatedPublicIPs[i] != newService.Spec.DeprecatedPublicIPs[i] {
|
if oldService.Spec.ExternalIPs[i] != newService.Spec.ExternalIPs[i] {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream`
|
|||||||
|
|
||||||
func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "expose (-f FILENAME | TYPE NAME) --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--public-ip=ip] [--type=type]",
|
Use: "expose (-f FILENAME | TYPE NAME) --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [----external-ip=external-ip-of-service] [--type=type]",
|
||||||
Short: "Take a replicated application and expose it as Kubernetes Service",
|
Short: "Take a replicated application and expose it as Kubernetes Service",
|
||||||
Long: expose_long,
|
Long: expose_long,
|
||||||
Example: expose_example,
|
Example: expose_example,
|
||||||
@ -70,7 +70,7 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
|||||||
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.")
|
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.")
|
||||||
cmd.Flags().String("container-port", "", "Synonym for --target-port")
|
cmd.Flags().String("container-port", "", "Synonym for --target-port")
|
||||||
cmd.Flags().String("target-port", "", "Name or number for the port on the container that the service should direct traffic to. Optional.")
|
cmd.Flags().String("target-port", "", "Name or number for the port on the container that the service should direct traffic to. Optional.")
|
||||||
cmd.Flags().String("public-ip", "", "Name of a public IP address to set for the service. The service will be assigned this IP in addition to its generated service IP.")
|
cmd.Flags().String("external-ip", "", "External IP address to set for the service. The service can be accessed by this IP in addition to its generated service IP.")
|
||||||
cmd.Flags().String("overrides", "", "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.")
|
cmd.Flags().String("overrides", "", "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.")
|
||||||
cmd.Flags().String("name", "", "The name for the newly created object.")
|
cmd.Flags().String("name", "", "The name for the newly created object.")
|
||||||
cmd.Flags().String("session-affinity", "", "If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'")
|
cmd.Flags().String("session-affinity", "", "If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'")
|
||||||
|
@ -585,8 +585,14 @@ func printReplicationControllerList(list *api.ReplicationControllerList, w io.Wr
|
|||||||
func getServiceExternalIP(svc *api.Service) string {
|
func getServiceExternalIP(svc *api.Service) string {
|
||||||
switch svc.Spec.Type {
|
switch svc.Spec.Type {
|
||||||
case api.ServiceTypeClusterIP:
|
case api.ServiceTypeClusterIP:
|
||||||
|
if len(svc.Spec.ExternalIPs) > 0 {
|
||||||
|
return strings.Join(svc.Spec.ExternalIPs, ",")
|
||||||
|
}
|
||||||
return "<none>"
|
return "<none>"
|
||||||
case api.ServiceTypeNodePort:
|
case api.ServiceTypeNodePort:
|
||||||
|
if len(svc.Spec.ExternalIPs) > 0 {
|
||||||
|
return strings.Join(svc.Spec.ExternalIPs, ",")
|
||||||
|
}
|
||||||
return "nodes"
|
return "nodes"
|
||||||
case api.ServiceTypeLoadBalancer:
|
case api.ServiceTypeLoadBalancer:
|
||||||
ingress := svc.Status.LoadBalancer.Ingress
|
ingress := svc.Status.LoadBalancer.Ingress
|
||||||
@ -596,6 +602,9 @@ func getServiceExternalIP(svc *api.Service) string {
|
|||||||
result = append(result, ingress[i].IP)
|
result = append(result, ingress[i].IP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(svc.Spec.ExternalIPs) > 0 {
|
||||||
|
result = append(result, svc.Spec.ExternalIPs...)
|
||||||
|
}
|
||||||
return strings.Join(result, ",")
|
return strings.Join(result, ",")
|
||||||
}
|
}
|
||||||
return "unknown"
|
return "unknown"
|
||||||
|
@ -18,11 +18,10 @@ package kubectl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The only difference between ServiceGeneratorV1 and V2 is that the service port is named "default" in V1, while it is left unnamed in V2.
|
// The only difference between ServiceGeneratorV1 and V2 is that the service port is named "default" in V1, while it is left unnamed in V2.
|
||||||
@ -54,7 +53,7 @@ func paramNames() []GeneratorParam {
|
|||||||
{"selector", true},
|
{"selector", true},
|
||||||
{"port", true},
|
{"port", true},
|
||||||
{"labels", false},
|
{"labels", false},
|
||||||
{"public-ip", false},
|
{"external-ip", false},
|
||||||
{"create-external-load-balancer", false},
|
{"create-external-load-balancer", false},
|
||||||
{"type", false},
|
{"type", false},
|
||||||
{"protocol", false},
|
{"protocol", false},
|
||||||
@ -144,8 +143,8 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
|||||||
if params["create-external-load-balancer"] == "true" {
|
if params["create-external-load-balancer"] == "true" {
|
||||||
service.Spec.Type = api.ServiceTypeLoadBalancer
|
service.Spec.Type = api.ServiceTypeLoadBalancer
|
||||||
}
|
}
|
||||||
if len(params["public-ip"]) != 0 {
|
if len(params["external-ip"]) > 0 {
|
||||||
service.Spec.DeprecatedPublicIPs = []string{params["public-ip"]}
|
service.Spec.ExternalIPs = []string{params["external-ip"]}
|
||||||
}
|
}
|
||||||
if len(params["type"]) != 0 {
|
if len(params["type"]) != 0 {
|
||||||
service.Spec.Type = api.ServiceType(params["type"])
|
service.Spec.Type = api.ServiceType(params["type"])
|
||||||
|
@ -128,7 +128,7 @@ func TestGenerateService(t *testing.T) {
|
|||||||
"port": "80",
|
"port": "80",
|
||||||
"protocol": "UDP",
|
"protocol": "UDP",
|
||||||
"container-port": "foobar",
|
"container-port": "foobar",
|
||||||
"public-ip": "1.2.3.4",
|
"external-ip": "1.2.3.4",
|
||||||
},
|
},
|
||||||
expected: api.Service{
|
expected: api.Service{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
@ -146,7 +146,7 @@ func TestGenerateService(t *testing.T) {
|
|||||||
TargetPort: util.NewIntOrStringFromString("foobar"),
|
TargetPort: util.NewIntOrStringFromString("foobar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DeprecatedPublicIPs: []string{"1.2.3.4"},
|
ExternalIPs: []string{"1.2.3.4"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -158,7 +158,7 @@ func TestGenerateService(t *testing.T) {
|
|||||||
"port": "80",
|
"port": "80",
|
||||||
"protocol": "UDP",
|
"protocol": "UDP",
|
||||||
"container-port": "foobar",
|
"container-port": "foobar",
|
||||||
"public-ip": "1.2.3.4",
|
"external-ip": "1.2.3.4",
|
||||||
"create-external-load-balancer": "true",
|
"create-external-load-balancer": "true",
|
||||||
},
|
},
|
||||||
expected: api.Service{
|
expected: api.Service{
|
||||||
@ -177,8 +177,8 @@ func TestGenerateService(t *testing.T) {
|
|||||||
TargetPort: util.NewIntOrStringFromString("foobar"),
|
TargetPort: util.NewIntOrStringFromString("foobar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Type: api.ServiceTypeLoadBalancer,
|
Type: api.ServiceTypeLoadBalancer,
|
||||||
DeprecatedPublicIPs: []string{"1.2.3.4"},
|
ExternalIPs: []string{"1.2.3.4"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -130,7 +130,7 @@ type serviceInfo struct {
|
|||||||
stickyMaxAgeSeconds int
|
stickyMaxAgeSeconds int
|
||||||
endpoints []string
|
endpoints []string
|
||||||
// Deprecated, but required for back-compat (including e2e)
|
// Deprecated, but required for back-compat (including e2e)
|
||||||
deprecatedPublicIPs []string
|
externalIPs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a new serviceInfo struct
|
// returns a new serviceInfo struct
|
||||||
@ -236,7 +236,7 @@ func (proxier *Proxier) sameConfig(info *serviceInfo, service *api.Service, port
|
|||||||
if !info.clusterIP.Equal(net.ParseIP(service.Spec.ClusterIP)) {
|
if !info.clusterIP.Equal(net.ParseIP(service.Spec.ClusterIP)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !ipsEqual(info.deprecatedPublicIPs, service.Spec.DeprecatedPublicIPs) {
|
if !ipsEqual(info.externalIPs, service.Spec.ExternalIPs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !api.LoadBalancerStatusEqual(&info.loadBalancerStatus, &service.Status.LoadBalancer) {
|
if !api.LoadBalancerStatusEqual(&info.loadBalancerStatus, &service.Status.LoadBalancer) {
|
||||||
@ -318,7 +318,7 @@ func (proxier *Proxier) OnServiceUpdate(allServices []api.Service) {
|
|||||||
info.port = servicePort.Port
|
info.port = servicePort.Port
|
||||||
info.protocol = servicePort.Protocol
|
info.protocol = servicePort.Protocol
|
||||||
info.nodePort = servicePort.NodePort
|
info.nodePort = servicePort.NodePort
|
||||||
info.deprecatedPublicIPs = service.Spec.DeprecatedPublicIPs
|
info.externalIPs = service.Spec.ExternalIPs
|
||||||
// Deep-copy in case the service instance changes
|
// Deep-copy in case the service instance changes
|
||||||
info.loadBalancerStatus = *api.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer)
|
info.loadBalancerStatus = *api.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer)
|
||||||
info.sessionAffinityType = service.Spec.SessionAffinity
|
info.sessionAffinityType = service.Spec.SessionAffinity
|
||||||
@ -556,7 +556,7 @@ func (proxier *Proxier) syncProxyRules() error {
|
|||||||
"-j", string(svcChain))
|
"-j", string(svcChain))
|
||||||
|
|
||||||
// Capture externalIPs.
|
// Capture externalIPs.
|
||||||
for _, externalIP := range info.deprecatedPublicIPs {
|
for _, externalIP := range info.externalIPs {
|
||||||
args := []string{
|
args := []string{
|
||||||
"-A", string(iptablesServicesChain),
|
"-A", string(iptablesServicesChain),
|
||||||
"-m", "comment", "--comment", fmt.Sprintf("\"%s external IP\"", name.String()),
|
"-m", "comment", "--comment", fmt.Sprintf("\"%s external IP\"", name.String()),
|
||||||
@ -564,10 +564,23 @@ func (proxier *Proxier) syncProxyRules() error {
|
|||||||
"-d", fmt.Sprintf("%s/32", externalIP),
|
"-d", fmt.Sprintf("%s/32", externalIP),
|
||||||
"--dport", fmt.Sprintf("%d", info.port),
|
"--dport", fmt.Sprintf("%d", info.port),
|
||||||
}
|
}
|
||||||
// We have to SNAT packets from external IPs.
|
// We have to SNAT packets to external IPs.
|
||||||
writeLine(rulesLines, append(args,
|
writeLine(rulesLines, append(args,
|
||||||
"-j", "MARK", "--set-xmark", fmt.Sprintf("%s/0xffffffff", iptablesMasqueradeMark))...)
|
"-j", "MARK", "--set-xmark", fmt.Sprintf("%s/0xffffffff", iptablesMasqueradeMark))...)
|
||||||
writeLine(rulesLines, append(args,
|
|
||||||
|
// Allow traffic for external IPs that does not come from a bridge (i.e. not from a container)
|
||||||
|
// nor from a local process to be forwarded to the service.
|
||||||
|
// This rule roughly translates to "all traffic from off-machine".
|
||||||
|
// This is imperfect in the face of network plugins that might not use a bridge, but we can revisit that later.
|
||||||
|
externalTrafficOnlyArgs := append(args,
|
||||||
|
"-m", "physdev", "!", "--physdev-is-in",
|
||||||
|
"-m", "addrtype", "!", "--src-type", "LOCAL")
|
||||||
|
writeLine(rulesLines, append(externalTrafficOnlyArgs,
|
||||||
|
"-j", string(svcChain))...)
|
||||||
|
dstLocalOnlyArgs := append(args, "-m", "addrtype", "--dst-type", "LOCAL")
|
||||||
|
// Allow traffic bound for external IPs that happen to be recognized as local IPs to stay local.
|
||||||
|
// This covers cases like GCE load-balancers which get added to the local routing table.
|
||||||
|
writeLine(rulesLines, append(dstLocalOnlyArgs,
|
||||||
"-j", string(svcChain))...)
|
"-j", string(svcChain))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type portal struct {
|
type portal struct {
|
||||||
ip net.IP
|
ip net.IP
|
||||||
port int
|
port int
|
||||||
|
isExternal bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceInfo struct {
|
type serviceInfo struct {
|
||||||
@ -51,7 +52,7 @@ type serviceInfo struct {
|
|||||||
sessionAffinityType api.ServiceAffinity
|
sessionAffinityType api.ServiceAffinity
|
||||||
stickyMaxAgeMinutes int
|
stickyMaxAgeMinutes int
|
||||||
// Deprecated, but required for back-compat (including e2e)
|
// Deprecated, but required for back-compat (including e2e)
|
||||||
deprecatedPublicIPs []string
|
externalIPs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func logTimeout(err error) bool {
|
func logTimeout(err error) bool {
|
||||||
@ -330,7 +331,7 @@ func (proxier *Proxier) OnServiceUpdate(services []api.Service) {
|
|||||||
}
|
}
|
||||||
info.portal.ip = serviceIP
|
info.portal.ip = serviceIP
|
||||||
info.portal.port = servicePort.Port
|
info.portal.port = servicePort.Port
|
||||||
info.deprecatedPublicIPs = service.Spec.DeprecatedPublicIPs
|
info.externalIPs = service.Spec.ExternalIPs
|
||||||
// Deep-copy in case the service instance changes
|
// Deep-copy in case the service instance changes
|
||||||
info.loadBalancerStatus = *api.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer)
|
info.loadBalancerStatus = *api.LoadBalancerStatusDeepCopy(&service.Status.LoadBalancer)
|
||||||
info.nodePort = servicePort.NodePort
|
info.nodePort = servicePort.NodePort
|
||||||
@ -368,7 +369,7 @@ func sameConfig(info *serviceInfo, service *api.Service, port *api.ServicePort)
|
|||||||
if !info.portal.ip.Equal(net.ParseIP(service.Spec.ClusterIP)) {
|
if !info.portal.ip.Equal(net.ParseIP(service.Spec.ClusterIP)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !ipsEqual(info.deprecatedPublicIPs, service.Spec.DeprecatedPublicIPs) {
|
if !ipsEqual(info.externalIPs, service.Spec.ExternalIPs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !api.LoadBalancerStatusEqual(&info.loadBalancerStatus, &service.Status.LoadBalancer) {
|
if !api.LoadBalancerStatusEqual(&info.loadBalancerStatus, &service.Status.LoadBalancer) {
|
||||||
@ -397,15 +398,15 @@ func (proxier *Proxier) openPortal(service proxy.ServicePortName, info *serviceI
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, publicIP := range info.deprecatedPublicIPs {
|
for _, publicIP := range info.externalIPs {
|
||||||
err = proxier.openOnePortal(portal{net.ParseIP(publicIP), info.portal.port}, info.protocol, proxier.listenIP, info.proxyPort, service)
|
err = proxier.openOnePortal(portal{net.ParseIP(publicIP), info.portal.port, true}, info.protocol, proxier.listenIP, info.proxyPort, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ingress := range info.loadBalancerStatus.Ingress {
|
for _, ingress := range info.loadBalancerStatus.Ingress {
|
||||||
if ingress.IP != "" {
|
if ingress.IP != "" {
|
||||||
err = proxier.openOnePortal(portal{net.ParseIP(ingress.IP), info.portal.port}, info.protocol, proxier.listenIP, info.proxyPort, service)
|
err = proxier.openOnePortal(portal{net.ParseIP(ingress.IP), info.portal.port, false}, info.protocol, proxier.listenIP, info.proxyPort, service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -422,18 +423,40 @@ func (proxier *Proxier) openPortal(service proxy.ServicePortName, info *serviceI
|
|||||||
|
|
||||||
func (proxier *Proxier) openOnePortal(portal portal, protocol api.Protocol, proxyIP net.IP, proxyPort int, name proxy.ServicePortName) error {
|
func (proxier *Proxier) openOnePortal(portal portal, protocol api.Protocol, proxyIP net.IP, proxyPort int, name proxy.ServicePortName) error {
|
||||||
// Handle traffic from containers.
|
// Handle traffic from containers.
|
||||||
args := proxier.iptablesContainerPortalArgs(portal.ip, portal.port, protocol, proxyIP, proxyPort, name)
|
args := proxier.iptablesContainerPortalArgs(portal.ip, portal.isExternal, false, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
existed, err := proxier.iptables.EnsureRule(iptables.Append, iptables.TableNAT, iptablesContainerPortalChain, args...)
|
existed, err := proxier.iptables.EnsureRule(iptables.Append, iptables.TableNAT, iptablesContainerPortalChain, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Failed to install iptables %s rule for service %q", iptablesContainerPortalChain, name)
|
glog.Errorf("Failed to install iptables %s rule for service %q, args:%v", iptablesContainerPortalChain, name, args)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !existed {
|
if !existed {
|
||||||
glog.V(3).Infof("Opened iptables from-containers portal for service %q on %s %s:%d", name, protocol, portal.ip, portal.port)
|
glog.V(3).Infof("Opened iptables from-containers portal for service %q on %s %s:%d", name, protocol, portal.ip, portal.port)
|
||||||
}
|
}
|
||||||
|
if portal.isExternal {
|
||||||
|
args := proxier.iptablesContainerPortalArgs(portal.ip, false, true, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
|
existed, err := proxier.iptables.EnsureRule(iptables.Append, iptables.TableNAT, iptablesContainerPortalChain, args...)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Failed to install iptables %s rule that opens service %q for local traffic, args:%v", iptablesContainerPortalChain, name, args)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !existed {
|
||||||
|
glog.V(3).Infof("Opened iptables from-containers portal for service %q on %s %s:%d for local traffic", name, protocol, portal.ip, portal.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = proxier.iptablesHostPortalArgs(portal.ip, true, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
|
existed, err = proxier.iptables.EnsureRule(iptables.Append, iptables.TableNAT, iptablesHostPortalChain, args...)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Failed to install iptables %s rule for service %q for dst-local traffic", iptablesHostPortalChain, name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !existed {
|
||||||
|
glog.V(3).Infof("Opened iptables from-host portal for service %q on %s %s:%d for dst-local traffic", name, protocol, portal.ip, portal.port)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Handle traffic from the host.
|
// Handle traffic from the host.
|
||||||
args = proxier.iptablesHostPortalArgs(portal.ip, portal.port, protocol, proxyIP, proxyPort, name)
|
args = proxier.iptablesHostPortalArgs(portal.ip, false, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
existed, err = proxier.iptables.EnsureRule(iptables.Append, iptables.TableNAT, iptablesHostPortalChain, args...)
|
existed, err = proxier.iptables.EnsureRule(iptables.Append, iptables.TableNAT, iptablesHostPortalChain, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Failed to install iptables %s rule for service %q", iptablesHostPortalChain, name)
|
glog.Errorf("Failed to install iptables %s rule for service %q", iptablesHostPortalChain, name)
|
||||||
@ -522,12 +545,12 @@ func (proxier *Proxier) openNodePort(nodePort int, protocol api.Protocol, proxyI
|
|||||||
func (proxier *Proxier) closePortal(service proxy.ServicePortName, info *serviceInfo) error {
|
func (proxier *Proxier) closePortal(service proxy.ServicePortName, info *serviceInfo) error {
|
||||||
// Collect errors and report them all at the end.
|
// Collect errors and report them all at the end.
|
||||||
el := proxier.closeOnePortal(info.portal, info.protocol, proxier.listenIP, info.proxyPort, service)
|
el := proxier.closeOnePortal(info.portal, info.protocol, proxier.listenIP, info.proxyPort, service)
|
||||||
for _, publicIP := range info.deprecatedPublicIPs {
|
for _, publicIP := range info.externalIPs {
|
||||||
el = append(el, proxier.closeOnePortal(portal{net.ParseIP(publicIP), info.portal.port}, info.protocol, proxier.listenIP, info.proxyPort, service)...)
|
el = append(el, proxier.closeOnePortal(portal{net.ParseIP(publicIP), info.portal.port, true}, info.protocol, proxier.listenIP, info.proxyPort, service)...)
|
||||||
}
|
}
|
||||||
for _, ingress := range info.loadBalancerStatus.Ingress {
|
for _, ingress := range info.loadBalancerStatus.Ingress {
|
||||||
if ingress.IP != "" {
|
if ingress.IP != "" {
|
||||||
el = append(el, proxier.closeOnePortal(portal{net.ParseIP(ingress.IP), info.portal.port}, info.protocol, proxier.listenIP, info.proxyPort, service)...)
|
el = append(el, proxier.closeOnePortal(portal{net.ParseIP(ingress.IP), info.portal.port, false}, info.protocol, proxier.listenIP, info.proxyPort, service)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if info.nodePort != 0 {
|
if info.nodePort != 0 {
|
||||||
@ -545,14 +568,29 @@ func (proxier *Proxier) closeOnePortal(portal portal, protocol api.Protocol, pro
|
|||||||
el := []error{}
|
el := []error{}
|
||||||
|
|
||||||
// Handle traffic from containers.
|
// Handle traffic from containers.
|
||||||
args := proxier.iptablesContainerPortalArgs(portal.ip, portal.port, protocol, proxyIP, proxyPort, name)
|
args := proxier.iptablesContainerPortalArgs(portal.ip, portal.isExternal, false, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
if err := proxier.iptables.DeleteRule(iptables.TableNAT, iptablesContainerPortalChain, args...); err != nil {
|
if err := proxier.iptables.DeleteRule(iptables.TableNAT, iptablesContainerPortalChain, args...); err != nil {
|
||||||
glog.Errorf("Failed to delete iptables %s rule for service %q", iptablesContainerPortalChain, name)
|
glog.Errorf("Failed to delete iptables %s rule for service %q", iptablesContainerPortalChain, name)
|
||||||
el = append(el, err)
|
el = append(el, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle traffic from the host.
|
if portal.isExternal {
|
||||||
args = proxier.iptablesHostPortalArgs(portal.ip, portal.port, protocol, proxyIP, proxyPort, name)
|
args := proxier.iptablesContainerPortalArgs(portal.ip, false, true, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
|
if err := proxier.iptables.DeleteRule(iptables.TableNAT, iptablesContainerPortalChain, args...); err != nil {
|
||||||
|
glog.Errorf("Failed to delete iptables %s rule for service %q", iptablesContainerPortalChain, name)
|
||||||
|
el = append(el, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = proxier.iptablesHostPortalArgs(portal.ip, true, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
|
if err := proxier.iptables.DeleteRule(iptables.TableNAT, iptablesHostPortalChain, args...); err != nil {
|
||||||
|
glog.Errorf("Failed to delete iptables %s rule for service %q", iptablesHostPortalChain, name)
|
||||||
|
el = append(el, err)
|
||||||
|
}
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle traffic from the host (portalIP is not external).
|
||||||
|
args = proxier.iptablesHostPortalArgs(portal.ip, false, portal.port, protocol, proxyIP, proxyPort, name)
|
||||||
if err := proxier.iptables.DeleteRule(iptables.TableNAT, iptablesHostPortalChain, args...); err != nil {
|
if err := proxier.iptables.DeleteRule(iptables.TableNAT, iptablesHostPortalChain, args...); err != nil {
|
||||||
glog.Errorf("Failed to delete iptables %s rule for service %q", iptablesHostPortalChain, name)
|
glog.Errorf("Failed to delete iptables %s rule for service %q", iptablesHostPortalChain, name)
|
||||||
el = append(el, err)
|
el = append(el, err)
|
||||||
@ -681,7 +719,7 @@ var zeroIPv6 = net.ParseIP("::0")
|
|||||||
var localhostIPv6 = net.ParseIP("::1")
|
var localhostIPv6 = net.ParseIP("::1")
|
||||||
|
|
||||||
// Build a slice of iptables args that are common to from-container and from-host portal rules.
|
// Build a slice of iptables args that are common to from-container and from-host portal rules.
|
||||||
func iptablesCommonPortalArgs(destIP net.IP, destPort int, protocol api.Protocol, service proxy.ServicePortName) []string {
|
func iptablesCommonPortalArgs(destIP net.IP, addPhysicalInterfaceMatch bool, addDstLocalMatch bool, destPort int, protocol api.Protocol, service proxy.ServicePortName) []string {
|
||||||
// This list needs to include all fields as they are eventually spit out
|
// This list needs to include all fields as they are eventually spit out
|
||||||
// by iptables-save. This is because some systems do not support the
|
// by iptables-save. This is because some systems do not support the
|
||||||
// 'iptables -C' arg, and so fall back on parsing iptables-save output.
|
// 'iptables -C' arg, and so fall back on parsing iptables-save output.
|
||||||
@ -702,12 +740,20 @@ func iptablesCommonPortalArgs(destIP net.IP, destPort int, protocol api.Protocol
|
|||||||
args = append(args, "-d", fmt.Sprintf("%s/32", destIP.String()))
|
args = append(args, "-d", fmt.Sprintf("%s/32", destIP.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if addPhysicalInterfaceMatch {
|
||||||
|
args = append(args, "-m", "physdev", "!", "--physdev-is-in")
|
||||||
|
}
|
||||||
|
|
||||||
|
if addDstLocalMatch {
|
||||||
|
args = append(args, "-m", "addrtype", "--dst-type", "LOCAL")
|
||||||
|
}
|
||||||
|
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a slice of iptables args for a from-container portal rule.
|
// Build a slice of iptables args for a from-container portal rule.
|
||||||
func (proxier *Proxier) iptablesContainerPortalArgs(destIP net.IP, destPort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
func (proxier *Proxier) iptablesContainerPortalArgs(destIP net.IP, addPhysicalInterfaceMatch bool, addDstLocalMatch bool, destPort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
||||||
args := iptablesCommonPortalArgs(destIP, destPort, protocol, service)
|
args := iptablesCommonPortalArgs(destIP, addPhysicalInterfaceMatch, addDstLocalMatch, destPort, protocol, service)
|
||||||
|
|
||||||
// This is tricky.
|
// This is tricky.
|
||||||
//
|
//
|
||||||
@ -753,8 +799,8 @@ func (proxier *Proxier) iptablesContainerPortalArgs(destIP net.IP, destPort int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build a slice of iptables args for a from-host portal rule.
|
// Build a slice of iptables args for a from-host portal rule.
|
||||||
func (proxier *Proxier) iptablesHostPortalArgs(destIP net.IP, destPort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
func (proxier *Proxier) iptablesHostPortalArgs(destIP net.IP, addDstLocalMatch bool, destPort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
||||||
args := iptablesCommonPortalArgs(destIP, destPort, protocol, service)
|
args := iptablesCommonPortalArgs(destIP, false, addDstLocalMatch, destPort, protocol, service)
|
||||||
|
|
||||||
// This is tricky.
|
// This is tricky.
|
||||||
//
|
//
|
||||||
@ -789,7 +835,7 @@ func (proxier *Proxier) iptablesHostPortalArgs(destIP net.IP, destPort int, prot
|
|||||||
// See iptablesContainerPortalArgs
|
// See iptablesContainerPortalArgs
|
||||||
// TODO: Should we just reuse iptablesContainerPortalArgs?
|
// TODO: Should we just reuse iptablesContainerPortalArgs?
|
||||||
func (proxier *Proxier) iptablesContainerNodePortArgs(nodePort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
func (proxier *Proxier) iptablesContainerNodePortArgs(nodePort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
||||||
args := iptablesCommonPortalArgs(nil, nodePort, protocol, service)
|
args := iptablesCommonPortalArgs(nil, false, false, nodePort, protocol, service)
|
||||||
|
|
||||||
if proxyIP.Equal(zeroIPv4) || proxyIP.Equal(zeroIPv6) {
|
if proxyIP.Equal(zeroIPv4) || proxyIP.Equal(zeroIPv6) {
|
||||||
// TODO: Can we REDIRECT with IPv6?
|
// TODO: Can we REDIRECT with IPv6?
|
||||||
@ -806,7 +852,7 @@ func (proxier *Proxier) iptablesContainerNodePortArgs(nodePort int, protocol api
|
|||||||
// See iptablesHostPortalArgs
|
// See iptablesHostPortalArgs
|
||||||
// TODO: Should we just reuse iptablesHostPortalArgs?
|
// TODO: Should we just reuse iptablesHostPortalArgs?
|
||||||
func (proxier *Proxier) iptablesHostNodePortArgs(nodePort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
func (proxier *Proxier) iptablesHostNodePortArgs(nodePort int, protocol api.Protocol, proxyIP net.IP, proxyPort int, service proxy.ServicePortName) []string {
|
||||||
args := iptablesCommonPortalArgs(nil, nodePort, protocol, service)
|
args := iptablesCommonPortalArgs(nil, false, false, nodePort, protocol, service)
|
||||||
|
|
||||||
if proxyIP.Equal(zeroIPv4) || proxyIP.Equal(zeroIPv6) {
|
if proxyIP.Equal(zeroIPv4) || proxyIP.Equal(zeroIPv6) {
|
||||||
proxyIP = proxier.hostIP
|
proxyIP = proxier.hostIP
|
||||||
|
@ -787,8 +787,8 @@ func TestProxyUpdatePublicIPs(t *testing.T) {
|
|||||||
Port: svcInfo.portal.port,
|
Port: svcInfo.portal.port,
|
||||||
Protocol: "TCP",
|
Protocol: "TCP",
|
||||||
}},
|
}},
|
||||||
ClusterIP: svcInfo.portal.ip.String(),
|
ClusterIP: svcInfo.portal.ip.String(),
|
||||||
DeprecatedPublicIPs: []string{"4.3.2.1"},
|
ExternalIPs: []string{"4.3.2.1"},
|
||||||
},
|
},
|
||||||
}})
|
}})
|
||||||
// Wait for the socket to actually get free.
|
// Wait for the socket to actually get free.
|
||||||
|
Loading…
Reference in New Issue
Block a user