Configure Per-pod Services using Service Templates
The DxSqlAg custom resource has a section called serviceTemplates that describes what a service should look like, somewhat like how a podTemplate works.
The DxSqlAg custom resource uses the service template to create a service for each pod in the cluster. This is useful for configuring load balancers and other services for the pods.
Prerequisites
- DxOperator installed in the Kubernetes cluster.
Service Template Overview
ServiceTemplates let you describe one or more Kubernetes Service definitions that will be instantiated per pod in the DxSqlAg StatefulSet.
Service templates may also be added or removed to an existing DxSqlAg.
For each ServiceTemplateSpec in spec.serviceTemplates, the operator creates a Service for each replica in the StatefulSet.
Template to Service Mapping
- You define templates under
spec.serviceTemplates[]. - For each template and each replica, the operator computes:
serviceName = <templateName>-<podName>
- A
Serviceis created with that name and a selector that targets exactly that pod.
serviceTemplates is located underneath the top-level spec:
apiVersion: dh2i.com/v1
kind: DxSqlAg
metadata:
name: dxsqlag
spec:
statefulSetSpec:
synchronousReplicas: 3
# ...
serviceTemplates:
- name: test
# ...
spec.sqlAgConfiguration.*Replicas(synchronous, asynchronous, and configuration-only) control how many pods (and therefore how many services per template) are created.spec.serviceTemplates[]holds one or moreServiceTemplateSpecobjects.
ServiceTemplateSpec
Each ServiceTemplateSpec is roughly equivalent to a
V1ServiceSpec plus some metadata.
spec:
serviceTemplates:
- name: test
metadata:
labels:
role: sql
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
type: LoadBalancer
ports:
- name: sql
port: 1433
targetPort: 1433
allocateLoadBalancerNodePorts: true
externalTrafficPolicy: Local
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
loadBalancerClass: example.com/my-lb
publishNotReadyAddresses: false
sessionAffinity: None
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
When generating services, the operator:
-
Copies
selectorfrom the template (if provided) to theService: selector. -
Ensures the selector includes:
statefulset.kubernetes.io/pod-name = <podName>
This means each generated Service ends up targeting exactly one pod (the corresponding StatefulSet replica).
You are responsible for ensuring the rest of the selector matches the pod labels (or you can omit selector and
rely only on statefulset.kubernetes.io/pod-name if that's sufficient for your use case).
Per-Service Customizations
perServiceCustomizations lets you provide fine-grained overrides for a specific generated Service,
e.g. to add an IP, add extra labels, or set different LB ranges.
Each entry is a PerServiceCustomizationSpec:
spec:
serviceTemplates:
- name: test
type: LoadBalancer
ports:
- name: sql
port: 1433
targetPort: 1433
perServiceCustomizations:
- name: test-dxsqlag-0 # full service name
metadata:
annotations:
monitoring.example.com/scrape: "true"
labels:
traffic-profile: "primary"
clusterIP: 10.0.0.50
externalIPs:
- 203.0.113.10
loadBalancerIP: 203.0.113.20
loadBalancerSourceRanges:
- 10.0.0.0/24
- name: test-dxsqlag-1
metadata:
labels:
traffic-profile: "secondary"
PerServiceCustomizationSpec.name is required and must match the full name of the generated service:
<templateName>-<statefulSetName>-<ordinal>
For example:
- name:
dxsqlag spec.sqlAgConfiguration.synchronousReplicas:3- Template:
name: test
Generated services:
test-dxsqlag-0test-dxsqlag-1test-dxsqlag-2
If no PerServiceCustomizationSpec exists for a given service name, the service is created using only the template.
If perServiceCustomizations[].ports is set, for each port:
- The operator looks for an existing port in
serviceTemplate.ports[]with the samename. - If a match is found, the template port is removed.
- The per-service port is then added.
This means per-service ports override any same-named template ports and can also add entirely new ports for that particular service instance.
Full Example
This example has a DxSqlAg with three synchronous replicas and one LoadBalancer service template, plus one per-service override that tweaks ports:
apiVersion: dh2i.com/v1
kind: DxSqlAg
metadata:
name: dxsqlag
spec:
# Your DxSqlAg StatefulSet configuration
statefulSetSpec:
# ...
sqlAgConfiguration:
synchronousReplicas: 3
serviceTemplates:
- name: test
metadata:
labels:
role: sql
type: LoadBalancer
ports:
- name: sql
protocol: TCP
port: 1433
targetPort: 1433
perServiceCustomizations:
- name: test-dxsqlag-0
metadata:
labels:
traffic-profile: "primary"
ports:
# Overrides the template's "sql" port for this service only
- name: sql
protocol: TCP
port: 1433
targetPort: 1433
nodePort: 31000
# Adds an extra health port for this service only
- name: health
protocol: TCP
port: 59999
targetPort: 59999
Result:
- Services
test-dxsqlag-0,test-dxsqlag-1,test-dxsqlag-2are created. - All three are LoadBalancer services with the base template config.
test-dxsqlag-0:- Gets additional labels and LB source ranges from its
PerServiceCustomizationSpec. - Has its
sqlport replaced by the per-service definition. - Gets an extra health port not present on other replicas.
- Gets additional labels and LB source ranges from its
test-dxsqlag-1andtest-dxsqlag-2use only the template ports.
Automatic Load Balancer Service Template
The operator can automatically create a default LoadBalancer service template for you when createLoadBalancers is enabled in spec.sqlAgConfiguration.
When the Default Load Balancer Template is Created
When the default LB template is created, the operator looks for an existing template named lb:
-
If a template with
name: lbalready exists, the operator does nothing and assumes you know what you’re doing. -
If no
lbtemplate is found, the operator appends a newServiceTemplateSpecto yourDxSqlAg:serviceTemplates:
- name: lb
type: LoadBalancer
ports:
- name: dxlmonitor
port: 7979
targetPort: 7979
- name: dxcmonitor
port: 7980
targetPort: 7980
- name: dxcmonitor-udp
port: 7981
targetPort: 7981
protocol: UDP
- name: mssql
port: 1433
targetPort: 1433
Customizing the Auto-Created Load Balancers
You have a couple of options if the default behavior isn’t exactly what you want:
-
Override the
lbtemplate entirely. You can define your own ServiceTemplateSpec withname: lb. The operator will see it and skip adding the default. -
Use
perServiceCustomizationson thelbtemplate:Once the
lbtemplate exists (either created by you or by the operator), you can useperServiceCustomizationsto tweak ports or metadata for specific replicas:spec:
serviceTemplates:
- name: lb
type: LoadBalancer
ports:
- name: mssql
port: 1433
targetPort: 1433
perServiceCustomizations:
- name: lb-dxsqlag-0
ports:
- name: sql
port: 1433
targetPort: 1433
nodePort: 31000 # override for this replica only
- name: health
port: 59999
targetPort: 59999
In short, createLoadBalancers gives you a sane default LoadBalancer service template for DxEnterprise and SQL Server, but you can override the template completely or customize individual services as needed.
Removing Service Templates
To remove all services created from a given template, you delete the template entry from spec.serviceTemplates. The operator will reconcile and remove all corresponding templated services.
If you created a serviceTemplate using spec.createLoadBalancers, flip that value to false (or remove it from the spec), otherwise the operator will re-create the service template.