Skip to content

Commit 268047e

Browse files
committed
feat: implement service type NodePort
1 parent 2c57498 commit 268047e

File tree

15 files changed

+696
-20
lines changed

15 files changed

+696
-20
lines changed

charts/postgres-operator/crds/operatorconfigurations.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,18 @@ spec:
447447
enable_replica_pooler_load_balancer:
448448
type: boolean
449449
default: false
450+
enable_master_node_port:
451+
type: boolean
452+
default: false
453+
enable_master_pooler_node_port:
454+
type: boolean
455+
default: false
456+
enable_replica_node_port:
457+
type: boolean
458+
default: false
459+
enable_replica_pooler_node_port:
460+
type: boolean
461+
default: false
450462
external_traffic_policy:
451463
type: string
452464
enum:

charts/postgres-operator/crds/postgresqls.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,26 @@ spec:
196196
type: boolean
197197
enableReplicaPoolerLoadBalancer:
198198
type: boolean
199+
enableMasterNodePort:
200+
type: boolean
201+
masterNodePort:
202+
type: integer
203+
minimum: 0
204+
enableMasterPoolerNodePort:
205+
type: boolean
206+
masterPoolerNodePort:
207+
type: integer
208+
minimum: 0
209+
enableReplicaNodePort:
210+
type: boolean
211+
replicaNodePort:
212+
type: integer
213+
minimum: 0
214+
enableReplicaPoolerNodePort:
215+
type: boolean
216+
replicaPoolerNodePort:
217+
type: integer
218+
minimum: 0
199219
enableShmVolume:
200220
type: boolean
201221
env:

docs/administrator.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,41 @@ For the `external-dns.alpha.kubernetes.io/hostname` annotation the `-pooler`
926926
suffix will be appended to the cluster name used in the template which is
927927
defined in `master|replica_dns_name_format`.
928928

929+
## Node Ports
930+
931+
Alternatively to Load Balancers Node Ports can be used. Kubernetes services with type
932+
`NodePort` redirect traffic from a specified port on your kubernetes nodes to your service.
933+
To expose your services to an external network with NodePorts you can set `enableMasterNodePort` and/or `enableReplicaNodePort` to `true`
934+
in your cluster manifest. In the case any of these variables are omitted from the manifest, the operator configuration settings `enable_master_node_port` and `enable_replica_node_port` apply.
935+
Note that the operator settings affect all Postgresql services running in all namespaces watched
936+
by the operator.
937+
938+
**Enabling a NodePort configuration will override the corresponding LoadBalancer configuration.**
939+
940+
There are multiple options to specify service annotations that will be merged
941+
with each other and override in the following order (where latter take
942+
precedence):
943+
944+
1. Globally configured `custom_service_annotations`
945+
2. `serviceAnnotations` specified in the cluster manifest
946+
3. `masterServiceAnnotations` and `replicaServiceAnnotations` specified in the cluster manifest
947+
948+
Load-Balancer specific annotations are not applied.
949+
950+
Node port services can also be configured for the [connection pooler](user.md#connection-pooler) pods
951+
with the manifest flags `enableMasterPoolerNodePort` and/or `enableReplicaPoolerNodePort` or in the operator configuration with `enable_master_pooler_node_port`
952+
and/or `enable_replica_pooler_node_port`.
953+
954+
To configure which ports Kubernetes should use for your NodePort service you can configure ports in your cluster manifest
955+
for each type:
956+
957+
- masterNodePort
958+
- masterPoolerNodePort
959+
- replicaNodePort
960+
- replicaPoolerNodePort
961+
962+
When not defined or set to 0 kubernetes will choose a port for you from [your kubernetes cluster's configured range](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport).
963+
929964
## Running periodic 'autorepair' scans of K8s objects
930965

931966
The Postgres Operator periodically scans all K8s objects belonging to each

docs/reference/cluster_manifest.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,48 @@ These parameters are grouped directly under the `spec` key in the manifest.
114114
this parameter. Optional, when empty the load balancer service becomes
115115
inaccessible from outside of the Kubernetes cluster.
116116

117+
* **enableMasterNodePort**
118+
boolean flag to override the operator defaults (set by the
119+
`enable_master_node_port` parameter) to define whether to enable the node
120+
port pointing to the Postgres primary. Optional. Overrides `enableMasterLoadBalancer`.
121+
122+
* **enableMasterPoolerNodePort**
123+
boolean flag to override the operator defaults (set by the
124+
`enable_master_pooler_node_port` parameter) to define whether to enable
125+
the node port for master pooler pods pointing to the Postgres primary.
126+
Optional. Overrides `enableMasterPoolerLoadBalancer`.
127+
128+
* **enableReplicaNodePort**
129+
boolean flag to override the operator defaults (set by the
130+
`enable_replica_node_port` parameter) to define whether to enable the node
131+
port pointing to the Postgres standby instances. Optional. Overrides `enableReplicaLoadBalancer`.
132+
133+
* **enableReplicaPoolerNodePort**
134+
boolean flag to override the operator defaults (set by the
135+
`enable_replica_pooler_node_port` parameter) to define whether to enable
136+
the node port for replica pooler pods pointing to the Postgres standby
137+
instances. Optional. Overrides `enableReplicaPoolerLoadBalancer`.
138+
139+
* **masterNodePort**
140+
integer flag to specify a port number for the node port to the Postgres primary.
141+
Only used when `enableMasterNodePort` or `enable_master_node_port` are enabled.
142+
Optional. Kubernetes will provide a port number for you if not specified.
143+
144+
* **masterPoolerNodePort**
145+
integer flag to specify a port number for the node port for the master pooler pods pointing to the Postgres primary.
146+
Only used when `enableMasterPoolerNodePort` or `enable_master_pooler_node_port` are enabled.
147+
Optional. Kubernetes will provide a port number for you if not specified.
148+
149+
* **replicaNodePort**
150+
integer flag to specify a port number for the node port pointing to the Postgres standby instances.
151+
Only used when `enableReplicaNodePort` or `enable_replica_node_port` are enabled.
152+
Optional. Kubernetes will provide a port number for you if not specified.
153+
154+
* **replicaPoolerNodePort**
155+
integer flag to specify a port number for the node port for the replica pooler pods pointing to the Postgres standby instances
156+
Only used when `enableReplicaPoolerNodePort` or `enable_replica_pooler_node_port` are enabled.
157+
Optional. Kubernetes will provide a port number for you if not specified.
158+
117159
* **maintenanceWindows**
118160
a list which defines specific time frames when certain maintenance operations
119161
such as automatic major upgrades or master pod migration. Accepted formats

pkg/apis/acid.zalan.do/v1/crds.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,34 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{
312312
"enableReplicaPoolerLoadBalancer": {
313313
Type: "boolean",
314314
},
315+
"enableMasterNodePort": {
316+
Type: "boolean",
317+
},
318+
"masterNodePort": {
319+
Type: "integer",
320+
Minimum: &min0,
321+
},
322+
"enableMasterPoolerNodePort": {
323+
Type: "boolean",
324+
},
325+
"masterPoolerNodePort": {
326+
Type: "integer",
327+
Minimum: &min0,
328+
},
329+
"enableReplicaNodePort": {
330+
Type: "boolean",
331+
},
332+
"replicaNodePort": {
333+
Type: "integer",
334+
Minimum: &min0,
335+
},
336+
"enableReplicaPoolerNodePort": {
337+
Type: "boolean",
338+
},
339+
"replicaPoolerNodePort": {
340+
Type: "integer",
341+
Minimum: &min0,
342+
},
315343
"enableShmVolume": {
316344
Type: "boolean",
317345
},
@@ -1661,6 +1689,18 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
16611689
"enable_replica_pooler_load_balancer": {
16621690
Type: "boolean",
16631691
},
1692+
"enable_master_node_port": {
1693+
Type: "boolean",
1694+
},
1695+
"enable_master_pooler_node_port": {
1696+
Type: "boolean",
1697+
},
1698+
"enable_replica_node_port": {
1699+
Type: "boolean",
1700+
},
1701+
"enable_replica_pooler_node_port": {
1702+
Type: "boolean",
1703+
},
16641704
"external_traffic_policy": {
16651705
Type: "string",
16661706
Enum: []apiextv1.JSON{

pkg/apis/acid.zalan.do/v1/operator_configuration_type.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,24 @@ type OperatorTimeouts struct {
136136

137137
// LoadBalancerConfiguration defines the LB configuration
138138
type LoadBalancerConfiguration struct {
139-
DbHostedZone string `json:"db_hosted_zone,omitempty"`
140-
EnableMasterLoadBalancer bool `json:"enable_master_load_balancer,omitempty"`
141-
EnableMasterPoolerLoadBalancer bool `json:"enable_master_pooler_load_balancer,omitempty"`
142-
EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"`
143-
EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,omitempty"`
144-
CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"`
145-
MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"`
146-
MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"`
147-
ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"`
148-
ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"`
149-
ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"`
139+
DbHostedZone string `json:"db_hosted_zone,omitempty"`
140+
EnableMasterLoadBalancer bool `json:"enable_master_load_balancer,omitempty"`
141+
EnableMasterPoolerLoadBalancer bool `json:"enable_master_pooler_load_balancer,omitempty"`
142+
EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"`
143+
EnableReplicaPoolerLoadBalancer bool `json:"enable_replica_pooler_load_balancer,omitempty"`
144+
145+
// kept in LoadBalancerConfiguration because all the other parameters apply here too
146+
EnableMasterNodePort bool `json:"enable_master_node_port,omitempty"`
147+
EnableMasterPoolerNodePort bool `json:"enable_master_pooler_node_port,omitempty"`
148+
EnableReplicaNodePort bool `json:"enable_replica_node_port,omitempty"`
149+
EnableReplicaPoolerNodePort bool `json:"enable_replica_pooler_node_port,omitempty"`
150+
151+
CustomServiceAnnotations map[string]string `json:"custom_service_annotations,omitempty"`
152+
MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"`
153+
MasterLegacyDNSNameFormat config.StringTemplate `json:"master_legacy_dns_name_format,omitempty"`
154+
ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"`
155+
ReplicaLegacyDNSNameFormat config.StringTemplate `json:"replica_legacy_dns_name_format,omitempty"`
156+
ExternalTrafficPolicy string `json:"external_traffic_policy" default:"Cluster"`
150157
}
151158

152159
// AWSGCPConfiguration defines the configuration for AWS

pkg/apis/acid.zalan.do/v1/postgresql_type.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ type PostgresSpec struct {
5050
EnableReplicaLoadBalancer *bool `json:"enableReplicaLoadBalancer,omitempty"`
5151
EnableReplicaPoolerLoadBalancer *bool `json:"enableReplicaPoolerLoadBalancer,omitempty"`
5252

53+
// vars to enable and configure nodeport services
54+
// set ports to 0 or nil to let kubernetes decide which port to use
55+
// overrides loadbalancer configuration
56+
EnableMasterNodePort *bool `json:"enableMasterNodePort,omitempty"`
57+
MasterNodePort *int32 `json:"masterNodePort,omitempty"`
58+
EnableMasterPoolerNodePort *bool `json:"enableMasterPoolerNodePort,omitempty"`
59+
MasterPoolerNodePort *int32 `json:"masterPoolerNodePort,omitempty"`
60+
EnableReplicaNodePort *bool `json:"enableReplicaNodePort,omitempty"`
61+
ReplicaNodePort *int32 `json:"replicaNodePort,omitempty"`
62+
EnableReplicaPoolerNodePort *bool `json:"enableReplicaPoolerNodePort,omitempty"`
63+
ReplicaPoolerNodePort *int32 `json:"replicaPoolerNodePort,omitempty"`
64+
5365
// deprecated load balancer settings maintained for backward compatibility
5466
// see "Load balancers" operator docs
5567
UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"`

pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cluster/cluster.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,14 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) {
849849
return false, "new service's ExternalTrafficPolicy does not match the current one"
850850
}
851851

852+
if len(old.Spec.Ports) > 0 && len(new.Spec.Ports) > 0 {
853+
// we need to check whether the new port is not zero (=user-defined)
854+
// and only overwrite if it is
855+
if new.Spec.Ports[0].NodePort != 0 && old.Spec.Ports[0].NodePort != new.Spec.Ports[0].NodePort {
856+
return false, "new service's NodePort does not match the current one"
857+
}
858+
}
859+
852860
return true, ""
853861
}
854862

pkg/cluster/connection_pooler.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,10 @@ func (c *Cluster) generateConnectionPoolerService(connectionPooler *ConnectionPo
508508
c.configureLoadBalanceService(&serviceSpec, spec.AllowedSourceRanges)
509509
}
510510

511+
if ok, port := c.shouldCreateNodePortForPoolerService(poolerRole, spec); ok {
512+
c.configureNodePortService(&serviceSpec, port)
513+
}
514+
511515
service := &v1.Service{
512516
ObjectMeta: metav1.ObjectMeta{
513517
Name: connectionPooler.Name,
@@ -532,7 +536,10 @@ func (c *Cluster) generatePoolerServiceAnnotations(role PostgresRole, spec *acid
532536
var dnsString string
533537
annotations := c.getCustomServiceAnnotations(role, spec)
534538

535-
if c.shouldCreateLoadBalancerForPoolerService(role, spec) {
539+
// we do not want the load balancer specific annotations on a NodePort service
540+
nodePort, _ := c.shouldCreateNodePortForPoolerService(role, spec)
541+
542+
if c.shouldCreateLoadBalancerForPoolerService(role, spec) && !nodePort {
536543
// set ELB Timeout annotation with default value
537544
if _, ok := annotations[constants.ElbTimeoutAnnotationName]; !ok {
538545
annotations[constants.ElbTimeoutAnnotationName] = constants.ElbTimeoutAnnotationValue
@@ -577,6 +584,37 @@ func (c *Cluster) shouldCreateLoadBalancerForPoolerService(role PostgresRole, sp
577584
}
578585
}
579586

587+
func (c *Cluster) shouldCreateNodePortForPoolerService(role PostgresRole, spec *acidv1.PostgresSpec) (bool, int32) {
588+
switch role {
589+
case Replica:
590+
// if the value is explicitly set in a Postgresql manifest, follow this setting
591+
if spec.EnableReplicaPoolerNodePort != nil {
592+
port := int32(0)
593+
if spec.ReplicaPoolerNodePort != nil {
594+
port = *spec.ReplicaPoolerNodePort
595+
}
596+
597+
return *spec.EnableReplicaPoolerNodePort, port
598+
}
599+
600+
// otherwise, follow the operator configuration
601+
return c.OpConfig.EnableReplicaPoolerNodePort, 0
602+
case Master:
603+
if spec.EnableMasterPoolerNodePort != nil {
604+
port := int32(0)
605+
if spec.MasterPoolerNodePort != nil {
606+
port = *spec.MasterPoolerNodePort
607+
}
608+
609+
return *spec.EnableMasterPoolerNodePort, port
610+
}
611+
612+
return c.OpConfig.EnableMasterPoolerNodePort, 0
613+
default:
614+
panic(fmt.Sprintf("Unknown role %v", role))
615+
}
616+
}
617+
580618
func (c *Cluster) listPoolerPods(listOptions metav1.ListOptions) ([]v1.Pod, error) {
581619
pods, err := c.KubeClient.Pods(c.Namespace).List(context.TODO(), listOptions)
582620
if err != nil {

0 commit comments

Comments
 (0)