Are you using Docker on a Linux host?
Are you deploying your containers via Docker-Compose?
Do more than one of your containers/stacks use the same image (such as nginx:latest
)?
Is constantly running docker-compose up -d --force-recreate --always-recreate-deps --build --remove-orphans
to check & update seem off to you as it will redeploy (interrupt services) if the image is or is not updated?
This ‘bash script to check and update docker stack and container images’ might just be for you!
It will check each image that is currently deployed in a Stack, or just the image deployed for a Docker Container, from a docker-compose deploy.
You will need:
- A Linux host with Docker and Docker Compose successfully installed
- A ‘
docker-compose.yml
‘ file that generates a fully working docker container or stack
If you meet the requirements, then follow these steps to create the following file, and then make a “crontab -e
” entry for this file with a frequency to execute every day/week/month so that the check/update is made and your services stay up-to-date with minimal interruption.
A suggested crontab entry that runs every 6 hours (4 times a day) might look like this:
19 */6 * * * ~/dockerenvs/dmz/reverseproxy/deploy-container.sh
Step 1 – Create a file named “deploy-container.sh
” in your Docker Container / Stack folder
In this, generally, we create our container/stack’s docker-compose.yml file in a folder for that container/stack. This is best practice, and I would suggest that if you are not already tracking each docker stack as its own repo, you at least organize the folders of stacks into a logical pattern (environment such as Development/Production/DMZ) and then in each of Env folder, you have each Docker Stack as it own folder – so in your DMZ folder, you might have your “Nginx Reverse Proxy” folder, and in that folder, the docker-compose.yml
that builds your Nginx Reverse Proxy for your DMZ. It is next to each docker-compose.yml
file that you will put this “deploy-container.sh
” file.
Step 2 – Set the file to execute
A “chmod +x deploy-container.sh
” should do the trick here.
Step 3 – Edit and paste the example script
You should be able to either nano/vim the file or simply open the file in an editor, and paste the following example code:
#!/bin/bash
set -eo pipefail
# Get the name of the current folder (stack)
STACK_NAME=$(basename "$(pwd)")
echo "Starting image update check for stack ${STACK_NAME}..."
# Create an array of all container ids for this stack and get their image names and hashes
declare -a IMAGE_NAMES
declare -a IMAGE_HASHES
declare -a CONTAINER_IDS
mapfile -t CONTAINER_IDS < <(docker-compose ps -q -a)
for CONTAINER_ID in "${CONTAINER_IDS[@]}"; do
echo "Getting image hash for container ${CONTAINER_ID}"
IMAGE_NAME=$(docker inspect --format '{{ .Config.Image }}' "$CONTAINER_ID")
IMAGE_NAMES+=("$IMAGE_NAME")
IMAGE_HASH=$(docker inspect --format '{{ .Image }}' "$CONTAINER_ID")
IMAGE_HASHES+=("$IMAGE_HASH")
done
echo "At stage 01 - Image names and hashes retrieved."
# Get the list of latest images from Docker Hub and compare their hashes with current container image hashes
declare -a LATEST_HASHES
for (( i = 0; i < ${#CONTAINER_IDS[@]}; ++i )); do
#DOCKER_NAME=$(echo "$IMAGE" | awk -F: '{print $1":"$2}')
IMAGE_NAME=${IMAGE_NAMES[$i]}
echo "Checking image ${IMAGE_NAME}"
# Pull the latest version of the image if needed, suppress output to avoid clutter in logs
if ! docker pull "$IMAGE_NAME" > /dev/null 2>&1; then
echo "Failed to pull image ${IMAGE_NAME}"
continue
fi
# Get the latest hash of the image
LATEST_HASH=$(docker image inspect "$IMAGE_NAME" --format '{{ .Id }}')
LATEST_HASHES+=("$LATEST_HASH")
done
echo "At stage 02 - Latest image hashes retrieved."
# Compare the current and latest image hashes, set a value to re-run docker compose if any images are newer
UPDATE_PRESENT=0
for (( i = 0; i < ${#CONTAINER_IDS[@]}; ++i )); do
CURRENT=${IMAGE_HASHES[$i]}
LATEST=${LATEST_HASHES[$i]}
# Check if current image is up-to-date, otherwise set update present flag
if [[ $CURRENT != "$LATEST" ]]; then
echo "Image ${IMAGE} has been updated!"
UPDATE_PRESENT=1
fi
# Output no update
if [[ $CURRENT == "$LATEST" ]]; then
echo "Container ${CONTAINER_IDS[$i]} with Image ${IMAGE_NAMES[$i]} check:"
echo "Current: ${CURRENT}"
echo "Lastest: ${LATEST}"
fi
done
echo "At stage 03 - Image updates checked."
# Restart the stack to use the updated images if there were any updates
if [ $UPDATE_PRESENT -eq 1 ]; then
echo "Updated images for stack: $STACK_NAME - redeploying..."
docker-compose up -d --force-recreate --always-recreate-deps --build --remove-orphans
else
echo "Images and Containers are up to date."
fi
echo "Image update check completed."
The script when ran will get each container image name, then get the sha256 hash value for that container’s image.
It will then “docker pull ...
” each image, and compare the hash from the image in use verses the image of the latest we just pulled, if they are different, it will set a variable that would normally stay “0” to “1” and then turn on the run of the docker stack re-deploy at the end.
Jonny5