Why I’m uninstalling Docker Desktop

Unkillable monster

It might be the parting of the ways for me and Docker Desktop for now. There have been good times. There have been bad times. But it’s relentless demands have become too much for one software developer to bear.

I’ve been noticing that both Visual Studio and SQL Server Management Studio have been particularly sluggish of late. They’ve never been the lightest of software companions but we’ve learnt to rub along together. I know their foibles and they know mine. So what gone wrong?

My hard disk is a reasonable 500GB but has been filling up rapidly of late. Could a steady diet of docker images and containers be to blame? But this shouldn’t phase the trusty SSMS warhorse. It knows how to get what it needs (typically most of my RAM). But what is this? It’s a new kid of the block. The Vmmem process is new and seems to be gobbling up CPU. I’m frequently running at 100% utilisation now. No wonder we are all looking a tad jaded.

An unwelcome visitor

I’m not deterred. Surely stopping the docker service and making sure it doesn’t auto restart should be enough. But no – vmmem continues to eat up my CPU. I try to kill it with task manager but it proves to be an unkillable monster. Could powershell assist as it has assisted me so often in the past. Not this time. Vmmem continues unbowed and unbroken.

But what is it? What is this ferocious beast? It appears to be a process used by virtual machines that bizarrely still runs when docker is turned off. It can’t be stopped. It can’t be killed. It continues to drain the life out of my computer. This intolerable situation has to come to an end.

And end it does; with a heavy heart Docker desktop is uninstalled.

End of the affair

Postscript

It’s a windows thing. I’ve heard complains that the windows implementation of docker desktop is particularly heavy so maybe things would be OK on a Linux box. It think my relationship with Docker Desktop is on a break and hasn’t irretrievably broken down. I’m talking to my local IT shop about an upgrade. When I’m on the latest Intel i9 chip with a bunch more hard disk space and probably more RAM too, then docker desktop and I can talk. We can go into couples counselling and see if there is a way to repair our fractured relationship.

80/20 rule for blogs

There is a pithy saying I’m fond of

The first 80% of a product features will be developed in 20% of the time
The last 20% of a product features will be developed in 80% of the time

I think this is truth of it. I’ve just noticed my blog is following the same rule

80% of my traffic comes from 20% of the posts.
20% of the traffic hits the remaining long tail of 80% of the posts

The bugger of it is, is that I have no idea if a blog post will be in the top 20% of the posts when I’m writing it. Conversely, I’ve a shrewd idea when I post, if is going to be in the bottom 80% of hyper unviewed posts. This post will definitely be in the stinky 80% pile. It’s all fairly immaterial though – most of my post are just for my own personal amusement anyway. And I’m easily amused.

Publishing SQL Server database in Docker

To me docker containers have an ethereal, almost unreal quality to them. Do they really exist? Where are they? What do they look like? To convince myself of their reality I want to use SQL Server Management Studio on the host to connect to a SQL instance in a running container. Along the way we shall

– bring up the container
– access container command shell
– find out where it is on the network
– connect to it from the host machine
– compare static and dynamically assigned container IPs

 Environment

I’m on windows 10 and I’m going to be working with windows containers.

docker-compose.yml

For this I don’t need a DockerFile as I’m just going to run a library image directly so I’m going straight to a docker compose file docker-compose.yml

version: '3.2'

services:
  db:
    image: microsoft/mssql-server-windows-developer
    ports:
      - "49401:1433"
    environment:
      - sa_password=Secret12345
      - ACCEPT_EULA=Y      
    container_name: sqlserver_db1

networks:
  default:
    external:
      name: nat


so to break it down

image: microsoft/mssql-server-windows-developer

Create the container from the image microsoft/mssql-server-windows-developer. If it hasn’t been downloaded then it will be when we bring up the container

ports:
- "49401:1433"

we are running on port 1433 internally to the container and the port 49401 will be bound to the host. I’m not running it on 1433:1433 because I’ve already got SQL Server on my host so that port is taken. Attempts to bind the host to a port that is already taken generates odd errors.

environment:
- sa_password=Secret12345
- ACCEPT_EULA=Y

we set the system admin password and accept the licence agreement

container_name: sqlserver_db1

and the container is given a name rather than one assigned by docker. It just keeps the rest of the examples easier. It’s not needed.

We haven’t specified a subnet or IP address so the network section tells docker to use its default network connection

Bringing the container up

To bring it up run

docker-compose up

which runs and brings up the docker container

If we jump onto another cmd window and run

docker container ls

We get the list of running containers thus

so we can see our sql box in it’s container in the platonic realm of container space that it is currently inhabiting. It’s called sqlserver_db1 as specified in our docker-compose.yml file

Getting an interactive shell on our container

The container still has an air on unreality to it. To start to resolve it into the real world I want to be able to run commands on it. To do this we need to bring up an interactive shell. I can bring up a cmd window

docker exec -ti sqlserver_db1 cmd

or a powershell window for more options

docker exec -ti sqlserver_db1 powershell

I’m doing the powershell. Once there I cna start to run whatever commands I see fit

echo 'Hello world'
hostname

and so on.

ping google.commands

is a good one to run to check if the container has network connectivity to the outside world. Mine didn’t and it took a while to work out why.

Where is it on the network

It’s not immediately apparent where the container is on the network. We’ve let docker dynamically assign the IP. To find it we go to our interactive shell and run

ipconfig

which will tell use the IP4 address

or outside of an interactive shell (from the host) we could use

docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" sqlserver_db1

which will tell us the IP4 address also.

So right now our container is on 192.168.154.121 and we can ping it there. Going back to docker-compose.yml we also specify ports.

ports:
- "49401:1433"

so 1433 is the one that the docker container uses and we can telnet to it from the host to prove it’s open

telnet 192.168.154.121 1433

which it is. Also we can telnet to the host on the other port

telnet 127.0.0.1 49401

which is the same place but via the port that is bound to the host.

Connecting to the container with SSMS

I like to see things to full accept their existence. To convince my inner self let’s connect to the container with SQL Server Management Studio

We can connect to the host on its IP4

Or on the host IP by specifying the loopback IP and the port as we have bound the host to a port other than the default for SQL Server (1433).

Note that to connect via SSMS to a specific port the IP is separated by a comma i.e.

127.0.0.1,49401

Now we can see our container database from the host. We are convinced. It exists.

Connecting with a static IP

We can also assign the database container a static IP address by specifying a subnet and an IP address for our container in the docker-compose.yml file thus

version: '3.2'

services:
  db:
    image: microsoft/mssql-server-windows-developer
    ports:
      - "49401:1433"
    environment:
      - sa_password=Secret12345
      - ACCEPT_EULA=Y      
    networks:
      vpcbr:
        ipv4_address: 10.5.0.5
    container_name: sqlserver_db1
    
networks:
  vpcbr:
    driver: nat
    ipam:
     config:
       - subnet: 10.5.0.0/16      

The container will now always be on 10.5.0.5 and we can connect with SSMS on 10.5.0.5 without worrying about any of the intervening steps. I guess that’s the easy way.

Demo Code

In case anyone needs it the docker files for static and dynamic IP implementations are at on my github site here.

Useful Links

https://docs.docker.com/compose/compose-file/
Full specification of docker compose

The process cannot access the file error with Docker Compose

I came across this error the other day when I was working on windows with Docker

 Cannot start service db: failed to create endpoint dotnet-album-viewer_db_1 on network nat: failed during hnsCallRawResponse: hnsCall failed in Win32: The process cannot access the file because it is being used by another process. (0x20)

To say it’s misleading would be charitable – I was confused by this confusing message. Found the answer but it was a bit scattered over the internet so I thought I would bring it together.

The error

Consider this docker-compose.yml file

version: '3.2'

services:
  db:
    image: dockersamples/tidb:nanoserver-sac2016
    ports:
      - "3306:4000"

  app:
    image: dockersamples/dotnet-album-viewer
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    ports:
      - "80:80"
    environment:
      - "Data:Provider=MySQL"
      - "Data:ConnectionString=Server=db;Port=4000;Database=AlbumViewer;User=root;SslMode=None"      
    depends_on:
      - db

networks:
  default:
    external:
      name: nat

We are running a database container (db) and an application container (app). When we run

docker-compose up -d

to bring up the containers we get the error

Cannot start service db: failed to create endpoint dotnet-album-viewer_db_1 on network nat: failed during hnsCallRawResponse: hnsCall failed in Win32: The process cannot access the file because it is being used by another process. (0x20)

It makes it seem like the container is locked in some way, perhaps two instances running concurrently. That’s not the issue.

The cause

The problem is that we are running the containers on ports that have been taken by other processes. So, in my case it is both the database container and the application server that are at fault

Database container

  db:
    image: dockersamples/tidb:nanoserver-sac2016
    ports:
      - "3306:4000"

The database container connects on port 4000 in the network internal to the containers.  The second port 3306 is what is exposed to the host machine. It’s port 3306 that is already taken by another process. To find which application is running on that port use netstat and PowerShell i.e.

netstat -aon | findstr 3306

which gives

MySql is on port 3306 hence I can’t run a container on that port and I get the weird ‘process cannot access file’ error. That does make sense as the image dockersamples/tidb:nanoserver-sac2016 is a MySQL compatible database – so it’s on the same port.

Application container

The application server is also targeting an in-use port

  app:
    image: dockersamples/dotnet-album-viewer
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    ports:
      - "80:80"

The application container will use port 80 internally and also port 80 externally. I don’t need to go the trouble of running netstat. I know I’ve got a webserver (IIS) on that machine which has a default website running on port 80. I should have spotted that straight away and been a bit less baffled.

The resolution

I can either kill the processes that are taking that port or change the external port that the docker containers are going to use. I don’t really want to kill MySQL or IIS so I need 2 new ports.

I tend to use ports above 49200 as they are dynamic ports and are less likely to be in use (though I tend to use them up myself, so I still get clashes). Specifically

Well-known ports range from 0 through 1023.
Registered ports are 1024 to 49151.
Dynamic ports (also called private ports) are 49152 to 65535.

Once you’ve identified a port you can check if it’s in use by using netstat again

netstat -aon | findstr 49301

And see if you get a result. So, the final resolution is

version: '3.2'

services:
  db:
    image: dockersamples/tidb:nanoserver-sac2016
    ports:
      - "49301:4000"

  app:
    image: dockersamples/dotnet-album-viewer
    build:
      context: .
      dockerfile: docker/app/Dockerfile
    ports:
      - "49250:80"
    environment:
      - "Data:Provider=MySQL"
      - "Data:ConnectionString=Server=db;Port=4000;Database=AlbumViewer;User=root;SslMode=None"      
    depends_on:
      - db

networks:
  default:
    external:
      name: nat

So db and app external ports are now in the dynamic range and when I run

docker-compose up -d

the error is gone, and my containers and the application come up. Lovely.

Useful links

The examples in this post come from here https://github.com/docker/labs/blob/master/windows/windows-containers/WindowsContainers.md

Useful post on how to check if your ports are in use. Further detail and explanation to what I’ve posted here.

List of well known ports https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers. Searching for port 3306 on Wikipedia would have shown me that it’s in use by MySQL. I guess a lot of people would have just known this. I didn’t.

Stack overflow answer as a reminder on which port in the docker-compose file is the external port and which one is the one used internally by docker. Spoiler alert: the first one is the externally exposed port and the second one is use by the applications running inside of the docker containers. It’s the first port that is causing us the problems.