Following on from my previous post about hitting the Kubernetes API from SQL Server 2025 let’s go a little further and deploy SQL Server 2025 to Kubernetes from…SQL Server 2025.

You may be thinking….why? Well…

In all honesty, this is just a bit of fun, I’m just playing around with the sp_invoke_external_rest_endpoint stored procedure, I don’t expect anyone to actually do this in a live environment. Anyway…let’s run through how this works.

First things first, we need to update the role created previously to allow our service account access: –

cat <

Storage classes are a cluster-scoped resource so we need to create a ClusterRole to be able to create one: –

cat <

And then we need a ClusterRoleBinding to map the ClusterRole to the service account: –

cat <

Ok, ready to start deploying SQL Server….but before we get to that, we need a storage class. I’ve installed OpenEBS on my cluster so I’ll reference that: –

DECLARE @deploy NVARCHAR(MAX);
EXEC sp_invoke_external_rest_endpoint
    @url="https://api.dbafromthecold.local/apis/storage.k8s.io/v1/storageclasses",
    @headers="{"Authorization":"Bearer eyXXXXX....XXXX"}",
    @method = 'POST',
    @payload = '{
  "apiVersion": "storage.k8s.io/v1",
  "kind": "StorageClass",
  "metadata": {
    "name": "mssql-storage",
    "annotations": {
      "openebs.io/cas-type": "local",
      "cas.openebs.io/config": "- name: StorageTypen  value: hostpathn- name: BasePathn  value: /var/local-hostpath"
    }
  },
  "provisioner": "openebs.io/local",
  "reclaimPolicy": "Delete",
  "volumeBindingMode": "WaitForFirstConsumer"
}',
    @response = @deploy OUTPUT

SELECT 
    s.[name]
FROM OPENJSON(@deploy, '$.result')
WITH (
    [name] NVARCHAR(100)           '$.metadata.name'
) AS s;

And we’ll need a secret to store the SQL instance’s sa password: –

DECLARE @deploy NVARCHAR(MAX);
EXEC sp_invoke_external_rest_endpoint
    @url="https://api.dbafromthecold.local/api/v1/namespaces/default/secrets",
    @headers="{"Authorization":"Bearer eyXXXXX....XXXX"}",
    @method = 'POST',
    @payload = '{
  "apiVersion": "v1",
  "kind": "Secret",
  "metadata": {
    "name": "mssql-sa-secret"
  },
  "type": "Opaque",
  "stringData": {
    "MSSQL_SA_PASSWORD": "Testing1122"
  }
}',
    @response = @deploy OUTPUT

SELECT 
    s.[name]
FROM OPENJSON(@deploy, '$.result')
WITH (
    [name] NVARCHAR(100)           '$.metadata.name'
) AS s;

Right! Let’s deploy SQL Server 2025 to Kubernetes with 1 persistent volume claim using the storage class and referencing the secret for the sa password: –

DECLARE @deploy NVARCHAR(MAX);
EXEC sp_invoke_external_rest_endpoint
    @url="https://api.dbafromthecold.local/apis/apps/v1/namespaces/default/statefulsets",
    @headers="{"Authorization":"Bearer eyXXXXX....XXXX"}",
    @method = 'POST',
    @payload = '{
  "apiVersion": "apps/v1",
  "kind": "StatefulSet",
  "metadata": {
    "name": "mssql-statefulset"
  },
  "spec": {
    "serviceName": "mssql",
    "replicas": 1,
    "selector": {
      "matchLabels": {
        "name": "mssql-pod"
      }
    },
    "template": {
      "metadata": {
        "labels": {
          "name": "mssql-pod"
        }
      },
      "spec": {
        "securityContext": {
          "fsGroup": 10001
        },
        "containers": [
          {
            "name": "mssql-container",
            "image": "mcr.microsoft.com/mssql/server:2025-CTP2.0-ubuntu-22.04",
            "ports": [
              {
                "containerPort": 1433,
                "name": "mssql-port"
              }
            ],
            "env": [
              {
                "name": "MSSQL_PID",
                "value": "Developer"
              },
              {
                "name": "ACCEPT_EULA",
                "value": "Y"
              },
              {
                "name": "MSSQL_AGENT_ENABLED",
                "value": "1"
              },
              {
                "name": "MSSQL_SA_PASSWORD",
                "valueFrom": {
                  "secretKeyRef": {
                    "name": "mssql-sa-secret",
                    "key": "MSSQL_SA_PASSWORD"
                  }
                }
              }
            ],
            "volumeMounts": [
              {
                "name": "sqlsystem",
                "mountPath": "/var/opt/mssql"
              }
            ]
          }
        ]
      }
    },
    "volumeClaimTemplates": [
      {
        "metadata": {
          "name": "sqlsystem"
        },
        "spec": {
          "accessModes": [
            "ReadWriteOnce"
          ],
          "resources": {
            "requests": {
              "storage": "1Gi"
            }
          },
          "storageClassName": "mssql-storage"
        }
      }
    ]
  }
}',
    @response = @deploy OUTPUT

SELECT 
    s.[name],
    s.[namespace],
    s.replicas
FROM OPENJSON(@deploy, '$.result')
WITH (
    [name] NVARCHAR(100)           '$.metadata.name',
    [namespace] NVARCHAR(100)      '$.metadata.namespace',
    replicas INT                   '$.spec.replicas'
) AS s;

And finally, we’ll need a load balanced service (using Metallb that I’ve previously installed on the cluster) to connect to SQL Server within Kubernetes: –

DECLARE @deploy NVARCHAR(MAX);
EXEC sp_invoke_external_rest_endpoint
    @url="https://api.dbafromthecold.local/api/v1/namespaces/default/services",
    @headers="{"Authorization":"Bearer eyXXXXX....XXXX"}",
    @method = 'POST',
    @payload = '{
  "apiVersion": "v1",
  "kind": "Service",
  "metadata": {
    "name": "mssql-service"
  },
  "spec": {
    "ports": [
      {
        "name": "mssql-port",
        "port": 1433,
        "targetPort": 1433
      }
    ],
    "selector": {
      "name": "mssql-pod"
    },
    "type": "LoadBalancer"
  }
}',
    @response = @deploy OUTPUT

SELECT 
    s.[name],
    s.[namespace]
FROM OPENJSON(@deploy, '$.result')
WITH (
    [name] NVARCHAR(100)           '$.metadata.name',
    [namespace] NVARCHAR(100)      '$.metadata.namespace'
) AS s;

Cool! Let’s confirm on the cluster: –

kubectl get sc
kubectl get pvc
kubectl get secret
kubectl get all

confirm deployments

Final test, connecting to the instance in SSMS: –

connecting to sql instance

Thanks for reading!

Share.
Leave A Reply