In the first part of this short article series, we learned how to install the Docker SDK for Python libraries. I also gave you a very short summary of a container lifecycle, which is very important to understand in order to manage containers properly.
Let's continue using Python to help us managing SQL Server containers during their lifecycle.
Listing containers
The container.list() method is similar to the "docker ps" command, listing the information about all those containers that are in the “running” state. The output returned by this list method is a little bit limited, let's take a look:
client.containers.list() [<Container: f066cdfba8>, <Container: 32271b8752>, <Container: 98ec13bdc4>], <Container: 1bc16a3d9c>]
Note, the method just returned the ID of each one of the active containers running in my Docker daemon. This is not very practical when managing a large list of containers, I'm sure you want to get more information rather than just the container ID. To give you an idea, it will be nice for me to get the list of all the containers filter by status equals "running" returning the container ID and the name.
Fortunately, Python is a very rich programming language that allows to create flexible solutions like the one described above. In order to make this happen I will use a a "for loop" cycle to go through the list of containers filtering by all those containers in "running" state displaying the short ID and also the container name; here is how the code looks like:
for container in client.containers.list(all=True, filters={"status":"running"}): print (container.short_id + ' '+container.name) ... 32271b8752 SQL_CU12 98ec13bdc4 SQL_CU13 1bc16a3d9c SQL_CU14
I got the desired output. The three active containers are listed in "running" also including the container ID and name. This is way more helpful rather than just getting the container ID.
Checking Status of a specific container
Let’s say I’m interested in checking the status of a specific container. As the previous example shows, I can create a block of code to get me such status using a variable for the container name, but this information is also available using the containers.get() method. This method will return a list of attributes that will come handy when is required to understand more about our container object.
In this example, I will start setting a variable, called my_container, to a string value, SQL_CU14. This string value represents the name of one of the containers listed in the previous example. Then, I will declare a second variable, called container. This variable will store the returned value of the the containers.get() method, which receives the container name as a parameter. Finally, the output will display the property name and state of the container variable.
Here is how it looks in Python:
my_container = 'SQL_CU14' container = client.containers.get(my_container) print ('My container: ' + container.name + ' is in ' + container.status + ' state.')
This code block works fine, but it has a flaw. What do you think will happen if the string value passed to the my_container variable belongs to a non-existing container? Yes, it will fail. According to the SDK documentation, these are the two possible errors we will get:
- docker.errors.NotFound
- docker.errors.APIError
This is not a big deal as we can include some error handling in our code to prevent this issue. Python provides error handling support through the try \ except routine. This is a simple workaround to our situation. Here is how the updated block code will look like after this minor change:
my_container = 'SQL_CU14' try: container = client.containers.get(my_container) print ('My container: ' + container.name + ' is in ' + container.status + ' state.') except: print ("The " + My_Container + " container is stopped or doesn't exist...") ... My container: SQL_CU14 is in running state.
As you can see, the try \ except block will take care of validating the existence of the container. When the name of the container doesn’t exist or either the container is stopped, the code will return a message indicating this problem.
Let’s put this updated block code into test, passing the string value, RandomName, to the my_container variable:
my_container = 'RandomName' try: container = client.containers.get(my_container) print ('My container: ' + container.name + ' is in ' + container.status + ' state.') except: print ("The " + My_Container + " container is stopped or doesn't exist...") ... The RandomName container is stopped or doesn't exist...
As expected, the except was able to handle the error properly.
Running a Container
Let's create a basic SQL Server container. The required parameters do not differ much from the Docker client commands. The slight difference is the way the environment variables are declared. If you interested to learn more about what variables you declare when running a container check the reference from the Docker SDK for Python documentation here.
client.containers.run( 'mcr.microsoft.com/mssql/server:2017-CU14-ubuntu', detach=True, name="PythonSQL", environment={"ACCEPT_EULA":"Y","MSSQL_SA_PASSWORD":"P1th0nR0ck$!"}, ports={'1433/tcp': 1433} ) <Container: bf63511727>
The containers.run method returned the container ID, in my case: bf63511727. Let's quickly find out the status of this container putting in practice one of the previous example, using the containers.list() method:
client.containers.list() [<Container: bf63511727>]
As expected, the only active container listed is bf63511727. Of course this ID belongs to "PythonSQL" the container I created few seconds ago.
Stopping a container
The easiest way to stop a container is using a variable to store the container name. Then it is just a matter to call the container.stop() method to stop it. Note, in my next example I'm also calling the container.status() to check the container status after stopping it.
container = client.containers.get("PythonSQL") container.stop() container.status 'exited'
I can see the message"exited" is returned. My container "PythonSQL" was successfully stopped.
Starting a container
There is not much difference between stopping and starting a container. Once again I will use a variable to change my context to the container I want to manage for later be started calling the container.start() method.
container = client.containers.get("PythonSQL") container.start() container.status 'running'
Remove a container
Keeping your Docker environment as clear as possible is always a good practice, plus you will save some storage capacity. The following example will use the container.kill() method in combination of the container.remove() to remove the "PythonSQL" container from my Docker environment:
container = client.containers.get("PythonSQL") container.kill() container.remove()
You might be asking yourself why I didn't use the container.stop() method instead of container.kill(). The answer is simple: I don't wait for this container to be gracefully shutdown if I already slated it for decommission.
It's a wrap! This last example concludes the second part of this short series about how to manage SQL Server containers using Docker SDK for Python.
Conclusion
The Docker SDK for Python is a very powerful tool that can be used to automate the administration and provisioning of Docker containers for SQL Server.
Developers that are already familiar with Python can leverage this SDK to build SQL Server environments quickly, for more information please check the official Docker SDK for Python documentation here.
Thanks for reading!