61

environment

  • docker 1.12
  • clusted on Ubuntu 16.04

Is there a way to force a rolling update to a docker swarm service already running if the service update is not changing any parameters but the docker hub image has been updated?

Example: I deployed service:

docker service create --replicas 1 --name servicename --publish 80:80 username/imagename:latest

My build process has updated the latest image on docker hub now I want to pull the latest again.

I have tried running:

docker service update --image username/imagename:latest servicename

When I follow this process, docker does not pull the latest, I guess it assumes that since I wanted latest and docker already pulled an image:latest then there is nothing to do.

The only work around I have is to remove the service servicename and redeploy.

longday
  • 4,075
  • 4
  • 28
  • 35
  • This doesn't work. I've create an issue to Docker: https://github.com/docker/docker/issues/26978 Workaround is to add meaningless metadata or deployment information etc like commented previously: > I was able to trigger a rolling update by utilizing container labels. I added --container-label-add deploy="1.0.1". So every time I change label, each service does a pull and redeploy of the service. – longday Aug 14 at 15:53 – t-my Sep 28 '16 at 10:36

7 Answers7

76

Just for future reference:

Docker 1.13 added a --force flag to service update:

--force: Force update even if no changes require it

Use it as in:

docker service update --force service_name
Aisamu
  • 988
  • 7
  • 8
  • 14
    I use Docker Swarm (17.04.0-ce) on Azure and just using `--force` doesn't update the image to the latest version. I have to add the the digest to the image tag: `docker service update --image registry.co/image:latest@sha256:109ca... service`. Then it updates the image to the latest push. Am i missing someting? – manixx May 16 '17 at 14:04
  • 12
    IIRC, if your service is set to use a specific digest, updating it will keep pulling that same image. You could try `docker service update --force --image registry.co/image service` to remove the digest pinning, and from then on then a single `--force` should do the trick. – Aisamu May 17 '17 at 21:10
  • 3
    Thanks for your response! I did some heavy testing and i got no luck with the `--force` flag. I tried a private Docker Registry (using `registry:2`) and der official Docker Hub (thought the registry could be the reason). I never specified any digest (on `service create` or `service update`) during the updates, but Docker always used the digest. `service inspect` also showed the digest and the automatic rollback also considered it. Maybe this is a expected behavior (tested on `17.05.0-ce, build 89658be`)? – manixx Jun 06 '17 at 08:29
  • 1
    I'm suspecting the `pull` before `service update` is the problem. It seems to trick update into thinking you're already using the `latest` tag (when service update really should be comparing the digest) – Hamy Jul 18 '19 at 21:41
  • How to make sure to have zero downtime after `docker service update`? – shaik moeed May 17 '22 at 15:11
35

Docker won't automatically perform a pull from DockerHub (or private registry) for an image:tag you already have locally.

If you performed a manual pull before the docker service update, or deleted the image locally, it would.

You could also chain the command:

docker pull image:tag && docker service update --image username/imagename:latest servicename

You can avoid this scenario by tagging your images numerically and using an updated tag. username/imagename:1.1.0

DevOps Dan
  • 1,724
  • 11
  • 11
  • I tried do a pull and service update on a manager with no success. Then tried to do a pull on each node and a service update from the manager with no success. – longday Aug 14 '16 at 15:51
  • 9
    I was able to trigger a rolling update by utilizing container labels. I added --container-label-add deploy="1.0.1". So every time I change label, each service does a pull and redeploy of the service. – longday Aug 14 '16 at 15:53
  • docker pull before update not helping, container basicly linked with image without latest tag, and there is new one with tag, but still not doing changes. so only --force helping – Andrey Nikishaev Jul 05 '19 at 05:55
  • why isn't this the accepted answer? :) – Nicholas DiPiazza Dec 20 '21 at 21:27
  • Because it doesn't work. docker pull does a pull.. to the current machine, and then it does a service update which updates the service that can reside on another machine.... – CodeOrElse Apr 10 '22 at 22:09
13

You could use image ID instead of username/imagename:latest like this:

docker service update --image \
$(docker inspect --type image --format '{{.Id}}' username/imagename:latest) \
servicename

But in this case all your nodes must pull this image before service update. Otherwise, containers will be updated only on those nodes where image with such ID exists. Fortunately, nodes which do not have this image will stop their containers, so there is nothing bad if some nodes will fail to pull new version of image.

UPDATE: or you could use image digest as follow:

docker service update --image \
$(docker inspect --type image --format '{{index .RepoDigests 0}}' username/imagename:latest) \
servicename

docker inspect --type image --format '{{index .RepoDigests 0}}' IMAGE returns image digest which includes unique hash of image generated by registry v2. Thus, image must be pulled from registry, otherwise digest will not be available.

Using digest allow you do not pull images on all of your nodes (images with specified digest will automatically be pulled from registry on necessary nodes). But you have to pull fresh version of image once on a manager node before service update.

BTW, last way will be default since Docker 1.13

renskiy
  • 1,330
  • 1
  • 13
  • 12
7

If you want to update docker service image, you can simply do docker service update --image myimage:tag servicename. e.g. docker service update --image traefik:1.7.5 traefik_proxy Also by not using myimage:latest tag, it makes sure you will not unintentionally update to latest updated major/minor release instead of patch release.

Ref: https://github.com/moby/moby/issues/30951#issuecomment-279479589

Pratik
  • 959
  • 1
  • 14
  • 20
4

What I do is

  1. pull the image first
  2. Run the update command with the new timestamp label

docker service update --container-label-add last_deployed=$(date -u +%Y-%m-%dT%H:%M:%S) service_name_or_id

matteocng
  • 72
  • 2
  • 2
  • 11
zeroweb
  • 2,612
  • 4
  • 22
  • 34
1

I'm writing this in 2020, I hope this script will help those who wants a clean way to pull and restart a container on a stack without publishing each times a new image on a new tag !

#!/bin/bash -e
if [ $# -lt 1 ]
then
  echo "this command needs the name of the container and the branch you want to use"
  echo "Syntax: ./update.sh <microservice> <version>"
  echo "Syntax: ./update.sh mymicroservice develop"
  exit 1
fi
declare -r STACK_NAME="mystack" # this should be generated or asked to user
declare -r REPO="192.168.1.100" # this should be generated or asked to user

echo "Pull the image ${REPO}/${1}:${2}"
# Pull latest image on the swarm master
docker pull ${REPO}/${1}:${2}
# Get the ID of that image
declare -r imageSha256=`docker image inspect --format '{{ .Id }}' ${REPO}/${1}:${2}`
declare -r image="${REPO}/${1}@${imageSha256}"

# Update the server and the image used
docker service update --image ${image} ${STACK_NAME}_${1}
echo "Update succeeded !"
Ser
  • 2,661
  • 23
  • 28
0

According to https://github.com/moby/moby/issues/34153:

When updating services that need credentials to pull the image, you need to pass --with-registry-auth. Images pulled for a service take a different path than a regular docker pull, because the actual pull is performed on each node in the swarm where an instance is deployed. To pull an image, the swarm cluster needs to have the credentials stored (so that the credentials can be passed on to the node it performs the pull on).

Even though the "node" in this case is your local node, swarm takes the same approach (otherwise it would only be able to pull the image on the local node, but not on any of the other nodes).

Setting the --with-registry-auth option passes your locally stored credentials to the daemon, and stores them in the raft store. After that, the image digest is resolved (using those credentials), and the image is pulled on the node that the task is scheduled on (again, using the credentials that were stored in the service).

Sarvar Nishonboyev
  • 12,262
  • 10
  • 69
  • 70