I began building a service recently that will, upon user request, create a container (to provide said service, not a Container-as-a-Service-style workload), and then after 1–3 hours, terminate the container (taking the user’s session and local data away with it). The process for this required that I:
- Retrieve the container’s StartedAt timestamp
- Compare that timestamp to see if it’s been more than however long I’d like to limit a session to.
- If a container is over that limit, it is queued for being Stopped and then ultimate the image pruned (in a separate task)
The setup for the script looks something like:
from datetime import datetime
import docker, dateparser
and then initializing a string for comparison, normalized to look like the Docker API-provided timestamp (these get converted further down, this is just for readability in stdout and, while debugging, I can visually inspect the timestamps):
STARTING_TIME = str(datetime.now()).split(" ")[0]+ "T" + str(datetime.now()).split(" ")[1] + "Z"
then, before we do anything else, setup the Docker client:
client = docker.APIClient(base_url='unix://var/run/docker.sock')
In the SDK, stopping a container requires the use of the Low level API functionality, so unless you require these features, you can leave the client set to docker.from_env() without any further arguments.
def compare_time(run_time,container_started_at):
dt = dateparser.parse(container_started_at).timestamp()
mt = dateparser.parse(run_time).timestamp()
interval = mt - dt
return interval
This function will take two arguments: the STARTING_TIME above (when the script started running) and the timestamp the container was created, and converts them to seconds, and returns that time in seconds.
def eval_interval(interval):
eval = (interval / 60 ) / 60
if eval > 2.0:
return True
else:
return False
then we are checking if that difference in time exceeds, in this case, 2 hours. If it does, it returns True , which in the next function, indicates a running container is safe to terminate:
def check_containers():
to_delete = []
for c in client.containers.list():
start_at = c.attrs['State']['StartedAt']
interval = compare_time(STARTING_TIME,start_at)
deleteable = eval_interval(interval)
print(c.name + " (" + c.id + ") " + str(c.image) + " " + str(deleteable))
if deleteable == True:
to_delete.append({"id": c.id, "name": c.name})
else:
continue
return to_delete
The to_delete list this returns is a list of dictionaries that contains the id and name of the containers it detected a deletable value of True , with that in hand, it is handed to a function to delete, or handle:
def delete_containers(to_delete):
failed_deletions = []
for c in to_delete:
try:
running_container = str(c['id'])
print("Deleting: " + running_container)
**print(client.stop(container=running\_container))**
except:
failed_deletions.append(c)
print("Failed to delete: " + c.id)
continue
return failed_deletions
This ingests the list of deletable containers, and then creates its own list of new containers that could not be deleted (and then optionally, I can handle this however I want in retry behavior later, but for now, it just throws an exception before continuing).
All of this is executed via a main function:
def main():
to_delete = check_containers()
delete_expired = delete_containers(to_delete)
return delete_expired
if __name__ == " __main__":
main()
For more about the packages I used:
Top comments (0)