SQL Server in Kubernetes on Docker for Windows

Last week Docker announced a feature that I’ve been looking forward to for a while: –

And sure enough, when I opened Docker for Windows, there was the update: –

Let’s run through the steps to get this setup. First of all, enable the feature in settings: –

Once installed, you’ll be able to confirm that Kubernetes is up and running: –

Awesome stuff, but how do we interact with it?

Now, if this is the first time working with Kubernetes you won’t have to perform the next couple of steps but just to confirm, run the following: –

kubectl config current-context

If your shell cannot find the kubectl command, add
C:\Program Files\Docker\Docker\Resources\bin\
to your PATH environment variable and restart your shell.

If the command outputs anything other than docker-for-desktop you will need to switch to the desktop cluster. To do this run: –

kubectl config use-context docker-for-desktop

In the above screenshot I switched from my mySQLK8sCluster1 (my AKS cluster) to docker-for-desktop and then ran: –

kubectl get nodes

Now we are ready to go and build a pod running SQL Server in Kubernetes on Docker for Windows 🙂

So in C:\temp create a file called sqlserver.yml and drop in: –

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: sqlserver
  labels:
    app: sqlserver
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: sqlserver
    spec:
      containers:
      - name: sqlserver
        image: microsoft/mssql-server-linux:latest
        ports:
        - containerPort: 1433
        env:
        - name: SA_PASSWORD
          value: "Testing1122"
        - name: ACCEPT_EULA
          value: "Y"
---
apiVersion: v1
kind: Service
metadata:
  name: sqlserver-service
spec:
  ports:
  - name: sqlserver
    port: 1433
    targetPort: 1433
  selector:
    name: sqlserver
  type: LoadBalancer

This is a very simple .yml file to create one pod running SQL Server. To create the pod: –

kubectl create -f C:\temp\sqlserver.yml

kubectl get pods

And boom! There we have a pod running SQL Server.

But how are we going to connect to it? OK, the second part of the yaml file defined a service which exposes an endpoint to allow us to connect. We can see the service by running: –

kubectl get services

The service we created is exposed on localhost (127.0.0.1) so we can use that and the port number specified in the yaml file (1433 in this example).

And boom! We are connected 🙂

We can also remote into the pod and verify that SQL is up and running: –

kubectl exec -it sqlserver /bin/bash

ps aux | grep sql

And there’s SQL running in the pod! Cool!

Thanks for reading!

Configuring Persistent Storage for SQL Server in Azure Kubernetes Services

I’ve been playing around with SQL Server running in Kubernetes in Azure Container Services (AKS) for a while now and I think that the technology is really cool.


EDIT – Azure Container Services (AKS) has been renamed to Azure Kubernetes Services. Blog title has been updated


You can get a highly available instance of SQL Server up and running with a few lines of code! Ok, there’s a bit of setup to do but once you get that out of the way, you’re good to go.

One thing that has been missing though, is persistent storage. Any changes made to the SQL instance would have been lost if the pod that it was running in failed and was brought back up.

Until now.

I’ve been looking at Kubernetes persistent volumes but was really scratching my head on trying to get it to work. Thankfully Microsoft has now published how to do this: – https://docs.microsoft.com/en-us/sql/linux/tutorial-sql-server-containers-kubernetes

Let’s have a look at how this works. You don’t need to have read any of my previous articles to follow the code here, all you need is an Azure account.

I’m going to run in Bash for Windows but you can install the Azure-Cli for the command line (all the commands are the same). The MSI is available here.

If you are running in Bash for Windows the first thing to do is install Azure-CLI: –

echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main" | \
     sudo tee /etc/apt/sources.list.d/azure-cli.list
      
sudo apt-key adv --keyserver packages.microsoft.com --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893
sudo apt-get install apt-transport-https
sudo apt-get update && sudo apt-get install azure-cli

Then whether you’re running in Bash for Windows or the command line, after the install confirm that it has been successful by running:-

az --version

Now log into Azure: –

az login

This is an odd process but hey, it seems to work.

Once the login process is complete, the resource group to hold all the objects of the Kubernetes cluster can be created: –

az group create --name ApResourceGroup1 --location eastus


I created the resource group in eastus because as much as I tried, I kept getting errors when using other locations (e.g. – ukwest)

After the resource group is created, the cluster can be built: –

az aks create --resource-group ApResourceGroup1 --name mySQLK8sCluster --node-count 2 --generate-ssh-keys

I find it incredibly cool that this can be done with one line of code. Log into Azure and see the amount of resources this one line creates in the background.

Anyway, to manage the cluster, install kubectl: –

az aks install-cli

Grab credentials in order to connect and manage the cluster: –

az aks get-credentials --resource-group=ApResourceGroup1 --name=mySQLK8sCluster

Cool! The cluster is setup and we can connect. This can be tested by running: –

kubectl get nodes

There are the two nodes of the cluster and it is ready to host a sql instance in a pod.

The persistent volume and SQL instance are created by referencing .yml files on the Microsoft page but instead of having to copy/paste I’ve dropped the two files into a Github repo. The only change I’ve made is that the image that used is my custom linux image with the agent installed (as I like to have the agent available).

To clone the repo to your local machine: –

git clone  https://github.com/dbafromthecold/SqlAksPersistentVolumes.git

Navigate to the files. The first file we will use is the PersistentVolume.yml file.

kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
     name: azure-disk
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: Managed
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mssql-data
  annotations:
    volume.beta.kubernetes.io/storage-class: azure-disk
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi

What this does is setup a storage class (as an Azure-Disk) and then specifies a persistent volume claim. It is the persistent volume claim that we will use to attach to the SQL Server instance within the pod.

To create the persistent volume & claim: –

kubectl apply -f PersistentVolume.yml

To confirm the volume and claim have been created: –

kubectl describe pvc mssql-data

kubectl describe pv

Now that the storage is provisioned, the SQL instance can be setup.

But before the SQL instance is created, there’s one more step that needs to be done. Previously when I’ve worked with SQL in AKS I’ve specified the SA password in the .yml file used to create the pod that contains the instance.

This isn’t secure so instead of doing that, let’s create a secret to hold the password: –

kubectl create secret generic mssql --from-literal=SA_PASSWORD="Testing1122"

Great! So here’s the .yml file that will create the instance of SQL: –

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: mssql-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: mssql
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mssql
        image: microsoft/mssql-server-linux
        ports:
        - containerPort: 1433
        env:
        - name: ACCEPT_EULA
          value: "Y"
        - name: SA_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mssql
              key: SA_PASSWORD 
        volumeMounts:
        - name: mssqldb
          mountPath: /var/opt/mssql
      volumes:
      - name: mssqldb
        persistentVolumeClaim:
          claimName: mssql-data
---
apiVersion: v1
kind: Service
metadata:
  name: mssql-deployment
spec:
  selector:
    app: mssql
  ports:
    - protocol: TCP
      port: 1433
      targetPort: 1433
  type: LoadBalancer

What this does is spin up a pod with our SQL instance running from my custom image dbafromthecold/sqlserverlinuxagent:latest. It then maps the persistent volume claim to /var/opt/mssql to our pod (this is the important step). Finally, creates a service so that we can connect to the instance using the service’s external IP and the password specified in the secret.

To create the SQL pod and service: –

kubectl apply -f sqlserver.yml

Wait a few minutes for the pod to come online. Check this by running: –

kubectl get pods

Once the pod is up and running, check the service for the external IP: –

kubectl get service

The external IP is used to connect to SQL within the cluster: –

In previous posts I’ve written, HA is provided to the SQL instance by respawning the pod if the current one running fails. However, data within the instance is not persisted (aka any new databases/data will be lost when the pod is respawned).

But now that we have implemented a persistent volume, the SQL instance and its databases should be available in a new pod if there’s a failure.

Let’s test this by creating some databases: –

CREATE DATABASE [DatabaseA];
CREATE DATABASE [DatabaseB];
CREATE DATABASE [DatabaseC];
CREATE DATABASE [DatabaseD];
CREATE DATABASE [DatabaseE];

The trick here is that the databases were created using the default settings.

USE [DatabaseA];
GO

EXEC sp_helpfile;
GO

The database files were created in /var/opt/mssql/data which is on the volume that we mapped to the persistent volume claim. This means that if the existing pod fails, the database files will be retained and available in the new pod.

To simulate a failure, manually delete the pod: –

kubectl delete pods --all

The old pod will be terminated and a new one automatically created: –

kubectl get pods

Once that pod is up and running, reconnect to SQL (using the same external IP of the service).

And the databases are there! The original instance within the pod has been deleted but a new pod running SQL Server and the persistent volume was automatically created!

This means that we can deploy a SQL instance within AKS that can recover from a failure with no data loss. And that, to me, is awesome.

OK, it’s not perfect. It can error out when you’re setting up (ping me if you run into any problems), the new pod takes a little too long to come online, and there’s still no windows authentication but it’s looking pretty promising!

Last thing, to remove all the objects built in this demo you just need to run: –

az group delete --name ApResourceGroup1

Thanks for reading!

Using Windows stored credentials to connect to SQL in containers

I work with SQL Server in containers pretty much exclusively when testing code and one of my real bug bears is that SQL Server in containers does not support Windows authentication (unless you’re using Windocks).

So when I’m working I find it quite annoying to have to specify a SA username & password when I want to connect.

OK, I can use Get-Credential, assign to a variable, and then reference that in a connection string but I want something a bit more permanent especially as I always use the same password for all my containers (shoot me, it’s local dev 🙂 )

What I’ve setup on my laptop is a stored credential using the CredentialManager powershell module.

Here’s how it works, first I create the credential: –

Import-Module CredentialManager

New-StoredCredential -Target "SqlDocker" -UserName "sa" -Password "Testing1122" -Persist LocalMachine

The -Persist LocalMachine allows me to reference this credential in other sessions as the default scope is session only. I can check this in another session by running: –

Get-StoredCredential -Target "SqlDocker"

So now run a container (using the same credentials as stored above): –

docker run -d -p 15789:1433 `
    --env ACCEPT_EULA=Y `
        --env SA_PASSWORD=Testing1122 `
            --name testcontainer microsoft/mssql-server-linux:latest

And now use the credential to connect to the container. I’m going to drop it into the dbatools Connect-DbaInstance cmdlet to pull information back about the SQL instance within the container: –

# set credential to variable
$cred = Get-StoredCredential -Target "SqlDocker"

# connect to Sql using credential
$srv = Connect-DbaInstance 'localhost,15789' -Credential $cred
    $srv.Edition
    $srv.HostDistribution
    $srv.HostPlatform
    $srv.Version

Boom! Connected to the SQL instance within the container using the stored credential. No more fudging passwords when typing out commands 🙂

Thanks for reading!

Running SQL Server in Kubernetes on Azure Kubernetes Services (AKS)

I’ve previously blogged about running SQL Server in ACS but Microsoft has now released a new version still called Azure Container Services (AKS instead of ACS however) but now specifically tailored to building Kubernetes clusters.


EDIT – Azure Container Services (AKS) has been renamed to Azure Kubernetes Services. Blog title has been updated


There are some differences to the original ACS (making the process simpler) so let’s run through setting up a Kubernetes cluster running SQL Server in AKS.

Ok, first thing to do is install the CLI (I’m going to work from a Bash shell on my desktop): –

echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main" | \
     sudo tee /etc/apt/sources.list.d/azure-cli.list
	 
	 
sudo apt-key adv --keyserver packages.microsoft.com --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893
sudo apt-get install apt-transport-https
sudo apt-get update && sudo apt-get install azure-cli

Check the version of the CLI installed (make sure it’s at least version 2.0.20): –

az --version

Then login to Azure in the shell (and follow the instructions): –

az login

As AKS is still in preview a flag needs to be enabled on your Azure subscription.
To do this run: –

az provider register -n Microsoft.ContainerService

You can check that the flag has been successfully enabled by running: –

az provider show -n Microsoft.ContainerService

Cool. Now we’re good to go with setting up a Kubernetes cluster! Same as the original ACS, a resource group needs to be created to hold all the objects in the cloud: –

az group create --name ApResourceGroup1 --location ukwest

And now the cluster can be created. I’m going to create a two node cluster by running: –

az aks create --resource-group ApResourceGroup1 --name mySQLK8sCluster1 --node-count 2 --generate-ssh-keys


EDIT: updated agent-code to node-count has this switch seems to have changed since I wrote this post

What’s cool about this is the amount of objects it’s creating in the background: –

All that from one line of code!

Once that’s complete, Kubectl needs to be installed locally to manage the cluster: –

az aks install-cli

And then I need to connect my local shell to the cluster: –

az aks get-credentials --resource-group ApResourceGroup1 --name mySQLK8sCluster1

Ok, let’s check the nodes in the cluster: –

kubectl get nodes

Awesome, I have two nodes up and running in my cluster!

Next thing to do is spin up SQL Server in a container within the cluster. To do this I’m going to build it from a yaml file: –

nano sqlserver.yml

And drop the following into it: –

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: sqlserver
  labels:
    app: sqlserver
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: sqlserver
    spec:
      containers:
      - name: sqlserver1
        image: microsoft/mssql-server-linux:latest
        ports:
        - containerPort: 1433
        env:
        - name: SA_PASSWORD
          value: "Testing1122"
        - name: ACCEPT_EULA
          value: "Y"
---
apiVersion: v1
kind: Service
metadata:
  name: sqlserver-service
spec:
  ports:
  - name: sqlserver
    port: 1433
    targetPort: 1433
  selector:
    name: sqlserver
  type: LoadBalancer

This will spin up a container within the cluster (as a deployment) and create a load balanced service with an external IP so that I can connect to SQL Server from my desktop. So now run: –

kubectl create -f sqlserver.yml

Once that’s complete we can run some commands to view the objects created. To check the SQL Server container created: –

kubectl get pods

To check on the deployment:-

kubectl get deployments

And finally, to check on the service: –

kubectl get service

Once the service has an external IP, I can use that to connect to SQL Server within my Kubernetes cluster in AKS!

How awesome is that! Microsoft have made this a nice and simple way of getting into running Kubernetes in Azure. I’m going to play around with this some more 🙂

Last thing, to remove all the objects built in this demo you just need to run: –

az group delete --name ApResourceGroup1

Thanks for reading!

Enabling the SQL Server Agent in Linux Containers

EDIT 3 – November 2019 – There is an environment variable that can now be used to enable the SQL Server Agent, MSSQL_AGENT_ENABLED

docker run -d -p 15789:1433 `
--env ACCEPT_EULA=Y `
--env SA_PASSWORD=Testing1122 `
--env MSSQL_AGENT_ENABLED=True `
--name testcontainer `
mcr.microsoft.com/mssql/server:2019-GDR1-ubuntu-16.04

A full list of environment variables available for SQL Server can be found here


EDIT 2 – May 2018 – Thanks to Jon & Russell’s comments for pointing out that as of SQL 2017 CU4 the Agent no longer needs to be installed separately, just enabled. I have updated the code here and on Github to reflect that change

There is also the option to enable the agent in the microsoft/mssql-server-linux:latest image, just run the line of code below to enable the agent from within the container.


EDIT – Feb 2017 – Justin Hartman (t) pointed out that the original code here no longer works but a fix has been provided by Microsoft. I’ve updated the image in DockerHub and the code here and in my Github repository


At SQL Saturday Holland a few weeks ago I was (surprise, surprise) chatting about containers and mentioned that I hadn’t been able to get the SQL Agent working.

Now, one of the benefits of attending SQL Saturdays is that you get to pick the brains of a lot of very clever people and luckily for me, Jan Van Humbeek (blog|twitter) was there.

Jan said that he had gotten the SQL Agent running in Linux containers so I asked if he could send on his code and he very kindly obliged.

So, the disclaimer for this blog post is that I didn’t write the code here, Jan did. All I’ve done is drop it into a dockerfile so that an image can be built. Thank you very much Jan!

Here’s the dockerfile to build an image with the Agent installed: –

# building from ubuntu
FROM ubuntu:16.04

# install curl & sudo & apt-transport-https
RUN apt-get update && apt-get install -y curl sudo && apt-get install -y apt-transport-https

# Import the public repository GPG keys
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -

# Register the Microsoft SQL Server Ubuntu repository
RUN curl https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2017.list | tee /etc/apt/sources.list.d/mssql-server.list

# update package list 
RUN apt-get update -y

# install sql server
RUN apt-get install -y mssql-server

# enable the agent
RUN sudo /opt/mssql/bin/mssql-conf set sqlagent.enabled true 

# start sql server 
CMD /opt/mssql/bin/sqlservr

N.B. – This dockerfile is also available on my Github

Ok, so I then dropped this docker into C:\docker\builds\linuxwithagent and built the image: –

docker build -t linuxwithagent C:\docker\builds\linuxwithagent

This does throw a few errors but will successfully build an image. A container can then be run: –

docker run -d -p 15789:1433 --env ACCEPT_EULA=Y --env SA_PASSWORD=Testing11@@ --name testcontainer linuxwithagent

Once the container is running, connect via SSMS and the agent will be available.

I’ve made this image available on the docker hub. You can pull it down by running: –

docker pull dbafromthecold/sqlserverlinuxagent:latest

Thanks for reading!