>
kubernetes, Planet, PostgreSQL, Technical

Testing loadBalancerSourceRanges with CloudNativePG on Azure Kubernetes

This option didn’t seem super widely documented from my initial searches online; it should be able to basically enforce layer 4 ingress/firewall rules at the individual service level. This is a quick test to check if it works.

Steps were generated with ChatGPT, and mostly worked. It missed Azure provider registration, but I figure that out easily from the Azure error message. GPT was creating the VMs after the CNPG cluster … I had to reverse that so I’d know the IP for loadBalancerSourceRanges. I had to switch the VMs to the “westus” region because of quota limits. There were a couple more tweaks but overall I got this done in an hour or two – couldn’t have done that with just google.

I used an Azure free account; never had to set up a credit card with Azure. Note that free accounts are limited to 30 days and only available for new users. This blog post happened to be the first time I’d used azure with my personal account so it qualified. I’ll need to use a pay-as-you-go account in the future.

Step 1: Create an AKS Cluster

Create resource group

az group create --name myResourceGroup --location eastus

{
  "id": "/subscriptions/7460cc93-ed07-42e7-a246-3b87e52a3ad7/resourceGroups/myResourceGroup",
  "location": "eastus",
  "managedBy": null,
  "name": "myResourceGroup",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}

Register the provider

az provider register --namespace Microsoft.ContainerService

Registering is still on-going. You can monitor using 'az provider show -n Microsoft.ContainerService'

Create AKS cluster

az aks create \
  --resource-group myResourceGroup \
  --name myAKSCluster \
  --network-plugin kubenet \
  --network-policy calico \
  --load-balancer-sku standard \
  --node-count 2 \
  --enable-managed-identity \
  --generate-ssh-keys

{
  "aadProfile": null,
  "addonProfiles": null,
  "agentPoolProfiles": [
    {
      "availabilityZones": null,
      "capacityReservationGroupId": null,
      "count": 2,

...
  "upgradeSettings": null,
  "windowsProfile": null,
  "workloadAutoScalerProfile": {
    "keda": null,
    "verticalPodAutoscaler": null
  }
}

get cluster credentials

az aks get-credentials --resource-group myResourceGroup --name myAKSCluster

Merged "myAKSCluster" as current context in /home/jeremy/code/cnpg-playground/k8s/kube-config.yaml

Step 2: Install CloudNativePG (CNPG)

Installing CNPG with helm

helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo update
helm install cnpg cnpg/cloudnative-pg --namespace cnpg-system --create-namespace

NAME: cnpg
LAST DEPLOYED: Fri Mar 14 00:01:16 2025
NAMESPACE: cnpg-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CloudNativePG operator should be installed in namespace "cnpg-system".
You can now create a PostgreSQL cluster with 3 nodes as follows:

cat <<EOF | kubectl apply -f -
# Example of PostgreSQL cluster
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-example

spec:
  instances: 3
  storage:
    size: 1Gi
EOF

kubectl get -A cluster

Verifying install

kubectl get pods -n cnpg-system

NAME                                   READY   STATUS    RESTARTS   AGE
cnpg-cloudnative-pg-847b949f48-d4clp   1/1     Running   0          118s

Step 3: Create Client VMs

Create Ubuntu client VMs

az vm create \
  --resource-group myResourceGroup \
  --name VM1 \
  --location westus \
  --image Ubuntu2404 \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-address VM1PublicIP

{
  "fqdns": "",
  "id": "/subscriptions/7460cc93-ed07-42e7-a246-3b87e52a3ad7/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/VM1",
  "location": "westus",
  "macAddress": "60-45-BD-09-BD-FA",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.4",
  "publicIpAddress": "104.42.23.20",
  "resourceGroup": "myResourceGroup",
  "zones": ""
}

az vm create \
  --resource-group myResourceGroup \
  --name VM2 \
  --location westus \
  --image Ubuntu2404 \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-address VM2PublicIP

{
  "fqdns": "",
  "id": "/subscriptions/7460cc93-ed07-42e7-a246-3b87e52a3ad7/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/VM2",
  "location": "westus",
  "macAddress": "00-0D-3A-33-8C-32",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.5",
  "publicIpAddress": "104.42.13.179",
  "resourceGroup": "myResourceGroup",
  "zones": ""
}

Verify the public IPs

az vm list-ip-addresses --resource-group myResourceGroup --output table

VirtualMachine    PublicIPAddresses    PrivateIPAddresses
----------------  -------------------  --------------------
VM1               104.42.23.20         10.0.0.4
VM2               104.42.13.179        10.0.0.5

ssh azureuser@104.42.23.20

The authenticity of host '104.42.23.20 (104.42.23.20)' can't be established.
ED25519 key fingerprint is SHA256:glEquFeCDVHDsA8x6KK3E3yhkeIBwMwl1p+1Wq51AFw.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '104.42.23.20' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-1021-azure x86_64)
...
azureuser@VM1:~$ lsb_release  -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 24.04.2 LTS
Release:        24.04
Codename:       noble

Step 4: Deploy CNPG cluster

generate and store the password

export POSTGRES_PASSWORD=$(openssl rand -base64 16) && echo "Generated Password: $POSTGRES_PASSWORD"

Generated Password: R0L0W056xZdykI/GoAW0lQ==


kubectl create secret generic mydb-secret \
  --from-literal=username=myuser \
  --from-literal=password=$POSTGRES_PASSWORD

secret/mydb-secret created


kubectl get secrets mydb-secret -o yaml

apiVersion: v1
data:
  password: UjBMMFcwNTZ4WmR5a0kvR29BVzBsUT09
  username: bXl1c2Vy
kind: Secret
metadata:
  creationTimestamp: "2025-03-14T07:35:06Z"
  name: mydb-secret
  namespace: default
  resourceVersion: "13805"
  uid: f62e732a-95de-4b54-be9e-734db5dedb9e
type: Opaque

create the cluster

cat >cnpg-cluster.yaml <<EOF
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cnpg-cluster
spec:
  instances: 2
  imageName: ghcr.io/cloudnative-pg/postgresql:17
  storage:
    size: 1Gi
  bootstrap:
    initdb:
      database: mydb
      owner: myuser
      secret:
        name: mydb-secret
  postgresql:
    synchronous:
      method: any
      number: 1
      dataDurability: preferred
  managed:
    services:
      ## disable the default services
      disabledDefaultServices: ["ro", "r"]
      additional:
        - selectorType: rw
          serviceTemplate:
            metadata:
              name: "test-rw"
            spec:
              type: LoadBalancer
              loadBalancerSourceRanges:
              - 104.42.23.20/32          # VM1's public IP
EOF


kubectl apply -f cnpg-cluster.yaml

cluster.postgresql.cnpg.io/cnpg-cluster created


kubectl cnpg status cnpg-cluster

Cluster Summary
Name                 default/cnpg-cluster
System ID:           7481566161465909268
PostgreSQL Image:    ghcr.io/cloudnative-pg/postgresql:17
Primary instance:    cnpg-cluster-1
Primary start time:  2025-03-14 07:38:01 +0000 UTC (uptime 4m17s)
Status:              Cluster in healthy state
Instances:           2
Ready instances:     2
Size:                94M
Current Write LSN:   0/4055A58 (Timeline: 1 - WAL File: 000000010000000000000004)

Continuous Backup status
Not configured

Streaming Replication status
Replication Slots Enabled
Name            Sent LSN   Write LSN  Flush LSN  Replay LSN  Write Lag  Flush Lag  Replay Lag  State      Sync State  Sync Priority  Replication Slot
----            --------   ---------  ---------  ----------  ---------  ---------  ----------  -----      ----------  -------------  ----------------
cnpg-cluster-2  0/4055A58  0/4055A58  0/4055A58  0/4055A58   00:00:00   00:00:00   00:00:00    streaming  quorum      1              active

Instances status
Name            Current LSN  Replication role  Status  QoS         Manager Version  Node
----            -----------  ----------------  ------  ---         ---------------  ----
cnpg-cluster-1  0/4055A58    Primary           OK      BestEffort  1.25.1           aks-nodepool1-30726688-vmss000001
cnpg-cluster-2  0/4055A58    Standby (sync)    OK      BestEffort  1.25.1           aks-nodepool1-30726688-vmss000000


kubectl get svc test-rw

NAME      TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)          AGE
test-rw   LoadBalancer   10.0.126.67   57.152.72.113   5432:31291/TCP   16m

Test Connectivity from VMs

Test from VM1

ssh azureuser@104.42.23.20

azureuser@VM1:~$ sudo apt update; sudo apt install postgresql-client-common postgresql-client-16
...
The following additional packages will be installed:
  libpq5
Suggested packages:
  postgresql-16 postgresql-doc-16
The following NEW packages will be installed:
  libpq5 postgresql-client-16 postgresql-client-common
0 upgraded, 3 newly installed, 0 to remove and 43 not upgraded.
Need to get 1471 kB of archives.
After this operation, 4922 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
...

azureuser@VM1:~$ psql -h 57.152.72.113 -U myuser -d mydb

Password for user myuser:
psql (16.8 (Ubuntu 16.8-0ubuntu0.24.04.1), server 17.4 (Debian 17.4-1.pgdg110+2))
WARNING: psql major version 16, server major version 17.
         Some psql features might not work.
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

mydb=> select version();
                                                           version
-----------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 17.4 (Debian 17.4-1.pgdg110+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
(1 row)

mydb=> \q
azureuser@VM1:~$ exit
logout
Connection to 104.42.23.20 closed.

Test from VM2

ssh azureuser@104.42.13.179

azureuser@VM2:~$ sudo apt update; sudo apt install postgresql-client-common postgresql-client-16
...
The following additional packages will be installed:
  libpq5
Suggested packages:
  postgresql-16 postgresql-doc-16
The following NEW packages will be installed:
  libpq5 postgresql-client-16 postgresql-client-common
0 upgraded, 3 newly installed, 0 to remove and 43 not upgraded.
Need to get 1471 kB of archives.
After this operation, 4922 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
...

azureuser@VM2:~$ time psql -h 57.152.72.113 -U myuser -d mydb
^Z
[1]+  Stopped                 psql -h 57.152.72.113 -U myuser -d mydb

real    0m40.947s
user    0m0.001s
sys     0m0.000s
azureuser@VM2:~$ exit
logout
Connection to 104.42.13.179 closed.

Clean up resources

Delete VMs

az vm delete --name VM1 --resource-group myResourceGroup --yes
az vm delete --name VM2 --resource-group myResourceGroup --yes

az disk delete --name VM1_OSDisk --resource-group myResourceGroup --yes
az disk delete --name VM2_OSDisk --resource-group myResourceGroup --yes

az network nic list --output table
AuxiliaryMode    AuxiliarySku    DisableTcpStateTracking    EnableIPForwarding    Location    MacAddress         Name      NicType    ProvisioningState    ResourceGroup    ResourceGuid                          VnetEncryptionSupported
---------------  --------------  -------------------------  --------------------  ----------  -----------------  --------  ---------  -------------------  ---------------  ------------------------------------  -------------------------
None             None            False                      False                 westus      60-45-BD-09-BD-FA  VM1VMNic  Standard   Succeeded            myResourceGroup  185c9f84-bef6-4f45-b53e-bd40a72dd3f8  False
None             None            False                      False                 westus      00-0D-3A-33-8C-32  VM2VMNic  Standard   Succeeded            myResourceGroup  293a72b4-70ab-44ce-bc04-2dc42c2e0591  False

az network nic delete --name VM1VMNic --resource-group myResourceGroup
az network nic delete --name VM2VMNic --resource-group myResourceGroup

az network public-ip delete --name VM1PublicIP --resource-group myResourceGroup
az network public-ip delete --name VM2PublicIP --resource-group myResourceGroup

Delete AKS cluster

az aks delete --name myAKSCluster --resource-group myResourceGroup --yes

Delete resource group

az group delete --name myResourceGroup --yes --no-wait

Check they are gone

az aks list --output table
az vm list --output table
az group list --output table

Name              Location    Status
----------------  ----------  ---------
myResourceGroup   eastus      Deleting
NetworkWatcherRG  eastus      Succeeded

QED. Works as advertised.

Unknown's avatar

About Jeremy

Building and running reliable data platforms that scale and perform. about.me/jeremy_schneider

Discussion

No comments yet.

Leave a New Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Disclaimer

This is my personal website. The views expressed here are mine alone and may not reflect the views of my employer.

contact: 312-725-9249 or schneider @ ardentperf.com


https://about.me/jeremy_schneider

oaktableocmaceracattack

(a)

Enter your email address to receive notifications of new posts by email.

Join 76 other subscribers