How to detect if docker run succeeded programmatically? - bash

I'm writing a very simple bash script to quickly check that my container still builds and starts correctly and that the app inside responds to requests.
Sometimes docker run fails, e.g. because the port I'm trying to bind the container to is already allocated. But when this happens docker run's exit code is still 0 so I can't use the exit code. How can I check programmatically that the container got started correctly?
The solutions I'm considering are:
parse the output for errors
docker ps to see if the container is running
but these both seem a little overkill and ugly. Am I missing a better way to check whether docker run succeeded?

As suggested by Abel Muiño in comments, this may have been fixed in more recent Docker versions (I'm currently running 0.9.1).
But, if you're temporarily stuck like me with an older version, I did find a decent workaround to check if the container started by using docker inspect.
docker inspect returns a JSON object with a lot of info about the container, and in particular whether the container is currently running or not. The -f flag lets you easily extract the bits needed:
docker inspect -f {{.State.Running}} $CONTAINER_ID
or
docker inspect -f "{{.State.Running}}" $CONTAINER_ID
will return true or false.
Note that you probably want to sleep 1 (or more) between starting the container and checking if it is up. If there's something wrong with your setup it's possible that it would appear as 'running' for a very short time before actually exiting.

To avoid parsing anything, you could use docker top, which returns 1 if the container is not running:
id=$(docker run mycontainer)
if ! docker top $id &>/dev/null
then
echo "Container crashed unexpectedly..."
return 1
fi

We could use docker exec $id true 2>/dev/null || echo not running.
This command does not write to stdout, as "docker top" does. It writes to stderr when the container is not running, the same message as "docker top".

Applying the suggestions aforementioned to a script.
1 - Create a script keepMyDockerUp.sh :
vi keepMyDockerUp.sh
#!/bin/bash
Container_ID=INSERT_YOUR_CONTAINER_ID HERE
result=$( docker inspect -f {{.State.Running}} $Container_ID)
echo "result is" $result
if [ $result = "true" ]
then
echo "docker is already running"
else
systemctl restart docker
docker start $Container_ID
fi
2 - Then simply add it to cron, so your script verifies whether your Docker container is up from time to time:
crontab -e
Go to the last line and add your script file. For example:
* * * * * /root/keepMyDockerUp.sh
3 - Save crontab and never worry about your Docker container being down again.
Hope it helps...
;-)

I had to use:
$ docker inspect -f {{.State.Health.Status}} xxx
(the container was in state running but the service inside the container wasn't fully started.
Part of inspect output:
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 1618,
"ExitCode": 0,
"Error": "",
"StartedAt": "2019-03-08T10:39:24.061732398Z",
"FinishedAt": "0001-01-01T00:00:00Z",
"Health": {
"Status": "starting",
"FailingStreak": 0,
"Log": []

Related

How can I build a shell script to build a Docker-Compose Image and start up the Docker container if successful?

I'm trying to write a shell script that will attempt to build a Docker-Compose image, then if that's successful, start up the Docker container with it by doing a Docker-Compose up -d.
In the second step, I load some JSON files into Mongo, but then if there's a problem with that step, I want want to stop all Docker services and print out an error message saying "Could not start Docker service because there was a problem seeding the DB."
My problem is that I don't know how to check if the image was created successfully in the first step. (I want to stop and print out a message saying that there were errors in the build.)
I think I can figure out something that checks if both services are running at the end, but I'm wondering if there is a way to print out the name of the problematic JSON file that couldn't load.
Does anyone have a similar script that I can use or modify for my own use?
My understanding is you are trying to branch out the logic of the script based on whether the build command was a success. Here is what you can do:
cat create-image.sh
#!/bin/bash
docker build -q -t test-image .
if [ $? -eq 0 ]; then
echo OK, images created
else
echo FAIL, image not created
exit 1
fi
echo "...DOING OTHER THINGS... (like docker-compose up)"
Let me know if this is what you were looking for.
You don't need a script if you just want to build images before starting containers:
docker-compose --build up
If you must use a script, you can certain call the above command and check its exit code. There is no need to call docker build individually for every service in the docker-compose file.

Bash script runs but no output on main commands and not executed

I'm setting a cron job that is a bash script containing the below:
#!/bin/bash
NUM_CONTAINERS=$(docker ps -q | wc -l)
if [ $NUM_CONTAINERS -lt 40 ]
then
echo "Time: $(date). Restart containers."
cd /opt
pwd
sudo docker kill $(docker ps -q)
docker-compose up -d
echo "Completed."
else
echo Nothing to do
fi
The output is appended to a log file:
>> cron.log
However the output in the cron file only shows:
Time: Sun Aug 15 10:50:01 UTC 2021. Restart containers.
/opt
Completed.
Both command do not seem to execute as I don't see any change in my containers either.
These 2 non working commands work well in a standalone .sh script without condition though.
What am I doing wrong?
User running the cron has sudo privileges, and we can see the second echo printing.
Lots of times, things that work outside of cron don't work within cron because the environment is not set up in the same way.
You should generally capture standard output and error, to see if something going wrong.
For example, use >> cron.log 2>&1 in your crontab file, this will capture both.
There's at least the possibility that docker is not in your path or, even if it is, the docker commands are not working for some other reason (that you're not seeing since you only capture standard output).
Capturing standard error should help out with that, if it is indeed the issue.
As an aside, I tend to use full path names inside cron scripts, or set up very limited environments at the start to ensure everything works correctly (once I've established why it's not working correctly).

Detect if docker ran successfully within the same script

My script.sh:
#/bin/sh
docker run --name foo
(Just assume that the docker command works and the container name is foo. Can't make the actual command public.)
I have a script that runs a docker container. I want to check that it ran successfully and echo the successful running status on the terminal.
How can I accomplish this using the container name? I know that I have to use something like docker inspect but when I try to add that command, it only gets executed after I ^C my script probably because docker has the execution.
In this answer, the docker is executed in some other script so it doesn't really work for my use case.
The linked answer from Jules Olléon works on permanently running services like webservers, application servers, database and similar software. In your example, it seems that you want to run a container on-demand, which is designed to do some work and then exit. Here, the status doesn't help.
When running the container in foreground mode as your example shows, it forwards the applications return code to the calling shell. Since you didn't post any code, I give you a simple example: We create a rc.sh script returning 1 as exit-code (which normally indicates some failure):
#!/bin/sh
echo "Testscript failed, returning exitcode 1"
exit 1
It got copied and executed in this Dockerfile:
FROM alpine:3.7
COPY rc.sh .
ENTRYPOINT [ "sh", "rc.sh" ]
Now we build this image using docker build -t rc-test . and execute a short living container:
$ docker run --rm rc-test
Testscript failed, returning exitcode 1
Bash give us the return code in $?:
$ echo $?
1
So we see that the container failed and could simply check them e.g. inside some bash script with an if-condition to perform some action when it fails:
#!/bin/bash
if ! docker run --rm rc-test; then
echo "Docker container failed with rc $?"
fi
After running your docker run command you can check this way if your docker container is still up and running:
s='^foo$'
status=$(docker ps -qf "name=$s" --format='{{.Status}}')
[[ -n $status ]] && echo "Running: $status" || echo "not running"
You just need to execute it with "-d" to execute the container in detached mode. With this, the solutions provided in the other post or the solution provided by #anubhava are both good solutions.
docker run -d -name some_name mycontainer
s='^some_name$'
status=$(docker ps -qf "name=$s" --format='{{.Status}}')
[[ -n $status ]] && echo "Running: $status" || echo "not running"

using a .sh script for docker healthchecks

sitting on that problem for like 2 hours now and iam getting crazy
here is the example bash script:
#!/bin/bash
exit 0;
here is the dockerfile:
HEALTHCHECK --interval=2s CMD HealthCheckTest.sh || exit 1
I still get always unhealthy.
Want i want to do is have some logic inside my bash script to determine if the container is healthy or not.
You can also use Compose health check if you use Docker Compose:
https://docs.docker.com/compose/compose-file/compose-file-v2/#healthcheck
Also you can define your health checks in a bash script which can be called using ENTRYPOINT in Dockerfile, e.g.:
https://github.com/ledermann/docker-rails/blob/develop/docker/wait-for-services.sh

How to check if docker machine exists programmatically?

I'm using docker-machine to manage my cloud servers. I'm writing some bash scripts to automate some tasks. The question is: "How to check in bash script if docker machine with the specific name already exists?". I need some expression to return true if it exists and false if it doesn't.
Thanks
Just run it through grep, if a regexp is good enough for you. For example if you have a machine called foo:
$ docker-machine ls -q | grep '^foo$'
Should work and return 0. The caret matches the start of the line and the space avoids partial matches. If it doesn't match you will get a non-zero return code.
You can use something like the following:
docker-machine status some-machine 2> /dev/null || echo "Machine does not exists"
Not a scripting guru but I would do a "docker-machine help", if this command runs and the exit code ($?) is zero, the docker-machine executable is available and functioning.
If the return code is 127 (typically this returned by bash for command not found) or anything other than non-zero, you can assume that either docker-machine is not installed or is not running properly.

Resources