0

Operating system error 995 when adding a database to an availability group

I was adding databases to an availability group (SQL Server 2017 CU20 instance) the other day and one database failed to automatically seed to the secondary.

When I looked in the SQL Server error log I saw this error message: –

BackupIoRequest::ReportIoError: write failure on backup device ‘{GUID}’. Operating system error 995(The I/O operation has been aborted because of either a thread exit or an application request.).

Followed up by: –

Automatic seeding of availability database ‘databasename’ in availability group ‘availabilitygroup’ failed with a transient error. The operation will be retried.

BackupVirtualDeviceFile::RequestDurableMedia: Flush failure on backup device ‘{GUID}’. Operating system error 995(The I/O operation has been aborted because of either a thread exit or an application request.).

Write on “{GUID}” failed: 995(The I/O operation has been aborted because of either a thread exit or an application request.)

A nonrecoverable I/O error occurred on file “{GUID}:” 995(The I/O operation has been aborted because of either a thread exit or an application request.).

OK I was a little concerned…until I went and checked the secondary.

The issue was, even though I’d created the file paths on the secondary for the data files…I hadn’t created the file path for the database’s log file.

So I removed the database from the availability group, created the file path for the database’s log file, and readded to the availability group.

This time the database automatically seeded over to the secondary no problem…phew!

Thanks for reading!

0

Using environment variable files for SQL Server in containers

There are a whole bunch of environment variables that can be used to configure SQL Server when run in a Docker container. You can check out the full list here.

So we could end up with a docker container run statement like this: –

docker container run -d `
--publish 15789:1433 `
--env MSSQL_PID=Developer `
--env MSSQL_SA_PASSWORD=Testing1122 `
--env ACCEPT_EULA=Y `
--env MSSQL_AGENT_ENABLED=True `
--env MSSQL_DATA_DIR=/var/opt/sqlserver/sqldata `
--env MSSQL_LOG_DIR=/var/opt/sqlserver/sqllog `
--env MSSQL_BACKUP_DIR=/var/opt/sqlserver/sqlbackups `
--name sqlcontainer1 `
mcr.microsoft.com/mssql/server:2019-CU12-ubuntu-18.04

Do we really want to be typing all that out every time we run a container? Ok, we could drop this into a script and execute that but another option is to use environment variable files.

Nice and simple, we take all the environment variables in the statement above and drop them into a file on our Docker host: –

MSSQL_PID=Developer
ACCEPT_EULA=Y
MSSQL_AGENT_ENABLED=True
MSSQL_DATA_DIR=/var/opt/sqlserver/sqldata
MSSQL_LOG_DIR=/var/opt/sqlserver/sqllog
MSSQL_BACKUP_DIR=/var/opt/sqlserver/sqlbackups

And now just reference that file in the docker container run statement (I saved my file as C:\Temp\var.env): –

docker container run -d `
--publish 15789:1433 `
--env MSSQL_SA_PASSWORD=Testing1122 `
--env-file C:\temp\var.env `
--name sqlcontainer1 `
mcr.microsoft.com/mssql/server:2019-CU12-ubuntu-18.04

To confirm the variables have been set: –

docker exec sqlcontainer1 printenv

And there they are! We can also use multiple files referenced using the –env-file flag.

Another way of doing is (probably better?) is to use Docker Compose. If you want to learn more about compose, check out the SQL Server & Containers Guide!

Thanks for reading!

0

Updating SQL Server container memory limits on the fly

When running multiple SQL Server containers on a Docker host we should always be setting CPU and Memory limits for each container (see the flags for memory and cpus here). This helps prevent the whole noisy neighbour situation, where one container takes all the host’s resources and staves the other containers.

But what if we forget to set those limits? Well, no worries…we can update them on the fly!

Let’s have a look. First let’s run a container with no memory limits: –

docker container run -d \
--publish 15789:1433 \
--env ACCEPT_EULA=Y \
--env MSSQL_SA_PASSWORD=Testing1122 \
--name sqlcontainer1 \
mcr.microsoft.com/mssql/server:2019-CU12-ubuntu-18.04

Confirm the container is up and running: –

docker container ls

Now have a look at the stats of the container: –

docker stats

This container is running on a host with 4GB of RAM so pretty much no limit in place. Let’s add a limit: –

docker update sqlcontainer1 --memory 2048MB

N.B. – Thanks to Anthony Nocentino (b|t) for pointing this out to me, I was under the (wrong) impression that limits couldn’t be changed on a running container…so this is really cool.

In fact, if you want a better explanation on container limits I recommend you check out Anthony’s post Container Limits and SQL Server

Anyhoo, now let’s have a look at the limits in place: –

docker stats

Cool! So we’ve set a limit for our running container!

BUT! What if we know how those limits are enforced…can we enforce a limit on a running container without using docker update?

Let’s have a go! First remove the existing container and re-deploy: –

docker container rm sqlcontainer1 -f

docker container run -d \
--publish 15789:1433 \
--env ACCEPT_EULA=Y \
--env MSSQL_SA_PASSWORD=Testing1122 \
--name sqlcontainer1 \
mcr.microsoft.com/mssql/server:2019-CU12-ubuntu-18.04

Confirm, no limit in place: –

docker stats

Same as before.

Right, limits on containers are enforced by control groups (cgroups)…when processes are placed in cgroups those cgroups (for memory, cpu etc.) limit the amount of the host’s resources that a process can use.

And that’s all containers really are…just processes running on the host.

So let’s find the cgroup for the memory limit for the container.

First grab the container ID (have to set the limit as root so switching here): –

sudo su

CONTAINERID=$(docker ps -aq) && echo $CONTAINERID

Then have a look at the memory control group

find /sys/fs/cgroup/ -name *$CONTAINERID*

MEMORYCGROUP=$(find /sys/fs/cgroup/ -name *$CONTAINERID* | grep memory) && echo $MEMORYCGROUP

ls $MEMORYCGROUP

cat $MEMORYCGROUP/memory.limit_in_bytes

So no limit in place. Let’s set a limit and confirm: –

echo 2147483648 > $MEMORYCGROUP/memory.limit_in_bytes

cat $MEMORYCGROUP/memory.limit_in_bytes

Looks good, now let’s check docker stats: –

docker stats

And there’s the limit in place! Kinda cool…working out how Docker is placing resource limits on containers by using cgroups. If you fancy reading more about how container works, check out my SQL Containers From Scratch blog here.

Thanks for reading!

2

A storage failover issue with SQL Server on Kubernetes

I’ve been running a proof of concept for SQL Server on Kubernetes over the last year or so (ok, probably longer than that…hey, I’m a busy guy ๐Ÿ™‚ ) and have come across an issue that has been sort of a show stopper.


UPDATE – This issue has been resolved in Kubernetes version 1.26.
Details are on this github issue: –
https://github.com/kubernetes/kubernetes/issues/65392

And there’s more on the official Kubernetes blog (when a feature called non-graceful node shutdown when into beta): –
https://kubernetes.io/blog/2022/12/16/kubernetes-1-26-non-graceful-node-shutdown-beta/


There are currently no HA solutions for SQL Server running on plain K8s (not discussing Azure Arc here) so my tests have been relying on the in-built HA that Kubernetes provides but there’s a problem.

Let’s see this in action.

First, as we’re running in AKS for this demo, check the storage classes available: –

kubectl get storageclass

We’re going to be using the default storage class for this demo, note the VOLUMEBINDINGMODE is set to WaitForFirstConsumer

Now create the PVC definitions referencing the default storage class: –

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mssql-system
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: default
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mssql-data
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: default
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mssql-log
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: default
  resources:
    requests:
      storage: 1Gi

Save that yaml as sqlserver_pvcs.yaml and deploy: –

kubectl apply -f sqlserver_pvcs.yaml

Confirm the PVCs have been created: –

kubectl get pvc

N.B. – The PVCs are in a status of pending as the VOLUMEBINDINGMODE mode of the storage class is set to WaitForFirstConsumer

Now create a sqlserver.yaml file: –

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: sqlserver
  name: sqlserver
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sqlserver
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: sqlserver
    spec:
      securityContext:
        fsGroup: 10001
      containers:
      - image: mcr.microsoft.com/mssql/server:2019-CU11-ubuntu-18.04
        name: sqlserver
        resources: {}
        env:
        - name: ACCEPT_EULA
          value: "Y"
        - name: MSSQL_SA_PASSWORD
          value: "Testing1122"
        volumeMounts:
        - name: system
          mountPath: /var/opt/mssql
        - name: user
          mountPath: /var/opt/sqlserver/data
        - name: log
          mountPath: /var/opt/sqlserver/log
      tolerations:
      - key: "node.kubernetes.io/unreachable"
        operator: "Exists"
        effect: "NoExecute"
        tolerationSeconds: 10
      - key: "node.kubernetes.io/not-ready"
        operator: "Exists"
        effect: "NoExecute"
        tolerationSeconds: 10
      volumes:
      - name: system
        persistentVolumeClaim:
          claimName: mssql-system
      - name: user
        persistentVolumeClaim:
          claimName: mssql-data
      - name: log
        persistentVolumeClaim:
          claimName: mssql-log
status: {}

N.B. – Note the tolerations set for this deployment, if you want to learn more you can check out my blog post here

Deploy that: –

kubectl apply -f sqlserver.yaml

And check that the deployment was successful: –

kubectl get deployments

Now the PVCs and corresponding PVs will have been created: –

kubectl get pvc
kubectl get pv

OK let’s have a look at the events of the pod: –

kubectl describe pod -l app=sqlserver

So we had a couple of errors from the scheduler initially, (probably) because the PVCs weren’t created in time…but then the attachdetach-controller kicked in and attached the volumes for the pod to use.

Now that the pod is up and confirm the node that the pod is running on: –

kubectl get pods -o wide

OK, shutdown the node in the Azure portal to simulate a node failure: –

Wait for the node to become unavailable: –

kubectl get nodes --watch

Once the node is reported as unavailable, check the status of the pod. A new one should be spun up on a new, available node:-

kubectl get pods -o wide

The old pod is in a Terminating state, a new one has been created but is in the ContainerCreating state and there it will stay…never coming online.

We can see why if we look at the events of the new pod: –

kubectl describe pod sqlserver-59c78ddc9f-tj9qr

And here’s the issue. The attachdetach-controller cannot move the volumes for the new pod to use as they’re still attached to the old pod.

(EDIT – Technically the volumes are attached to the node but the error reports that the volumes are in use by the old pod)

This is because the node that the old pod is on is in a state of NotReady…so the cluster has no idea of the state of that pod (it’s being reported as terminating but hasn’t been removed completely).

Let’s restart the node: –

And wait for it to come online: –

kubectl get nodes --watch

Once the node is online, the old pod will be removed and the new pod will come online: –

kubectl get pods -o wide

Looking at the pod events again: –

kubectl describe pod sqlserver-59c78ddc9f-tj9qr

We can see that once the node came online the attachdetach-controller was able to attach the volumes.

This is an issue as it requires manual intervention for the new pod to come online. Someone has to either bring the node back online or remove it from the cluster completely, not what you want as this will mean extended downtime for SQL Server running in the pod.

So what can we do about this? Well we’ve been looking at a couple of solutions which I’ll cover in upcoming blog posts ๐Ÿ™‚

Note, if anyone out there knows how to get around this issue (specifically altering the behaviour of the attachdetach-controller) please get in contact!

Thanks for reading!

0

EightKB Summer 2021 Edition

The schedule for EightKB Summer 2021 Edition has been announced!

Here’s the schedule: –

N.B. – I particularly like that if you click on the session on the website, it shows the session abstract…nice work Mark!

Once again we have five top class speakers delivering five great, in-depth sessions on various SQL Server internals topics.

As any conference organiser knows, session selection is the worst part of running a conference. We only have five slots in each event which meant we ended up not picking some amazing sessions. BUT…anyone who submitted for this event will automatically go into the selection pool for the next EightKB.

We’ve also opened up registration for the event, it’s completely free and you can sign up here: – https://eightkb.online/

The event will be in Zoom and we’ll have chat going in the EightKB channel in the SQL Community Slack…please come and hang out with us there!

The Mixed Extents podcast is also going strong…we’re 10 episodes in and we’ve had a whole bunch of experts from the industry join us to talk about different topics related to SQL Server. Theyโ€™re all on YouTube or you can listen to the podcasts wherever you get your podcasts!

Btw, all the sessions from previous EightKB events are also on YouTube so if you can’t wait until the next event to get your mind-melty internals content…check that out ๐Ÿ™‚

EightKB and Mixed Extents are 100% community driven with no sponsors so, we have our own Bonfire store selling t-shirts! This year we a limited edition EightKB Summer 2021 range: –

Don’t they look snazzy?!

Any money generated from the store will be put straight back into the events.

EightKB was setup by Anthony Nocentino (b|t), Mark Wilkinson (b|t), and myself as we wanted to put on an event that delved into the internals of SQL Server and weโ€™re having great fun doing just that.

Hope to see you there!