What happens when the base image of my image gets updated? - docker

I have images based on microsoft/aspnet:4.6.2, when those (my) images were built, the microsoft/aspnet:4.6.2 was pulled down in order to build my own image. So, in my local computer I have the microsoft/aspnet:4.6.2 image. Let's say the base image gets updated, Microsoft finds a bug with the image and decided to make a fix maintaining the tag, so it's still called microsoft/aspnet:4.6.2 but it actually is a different image than it was when I built my own.
So I have two questions:
1. Everytime my image gets pulled down, it will get the base image as it was when I built my image, right? (it seems obvious but I need to check)
2. If I notice (web hook, trigger?) there's a newer version of microsoft/aspnet:4.6.2 can I just run the docker build command again and the newer image would get pulled down? Keep in mind the old base image is in my file system (called the same). Is Docker smart enough to realize that I have an older version of that base image and it'll download the latest version of it?
Hope I made myself clear

Your image, as downloaded by someone, will always remain the same. An image relies on specific layers to give the image it's SHA256 checksum. Modifying parent layers would modify the checksum used to reference the image, so that would become a new image. The only way for that image to change is if the image is referenced by a tag and the local tag changes, either manually or by pulling the image tag again.
docker build will use the a local image first by default. You either need to run docker build --pull, separately docker pull or docker rmi IMAGE for the build to use the latest tagged image.
The Docker Hub build service has a build feature to automatically rebuild when any specified image's are updated in the hub.

use the —no-cache option during docker build if you need latest released base images else docker will always use the image available locally unless you do a cleanup post docker build

yes. (internally it uses IMAGE ID, not the name, to refer base images)
IIRC not smart by default. (there is a --pull switch in docker build )

Related

How to improve automation of running container's base image updates?

I want all running containers on my server to always use the latest version of an official base image e.g. node:16.3 in order to get security updates. To achieve that I have implemented an image update mechanism for all container images in my registry using a CI workflow which has some limitations described below.
I have read the answers to this question but they either involve building or inspecting images on the target server which I would like to avoid.
I am wondering whether there might be an easier way to achieve the container image updates or to alleviate some of the caveats I have encountered.
Current Image Update Mechanism
I build my container images using the FROM directive with the minor version I want to use:
FROM node:16.13
COPY . .
This image is pushed to a registry as my-app:1.0.
To check for changes in the node:16.3 image compared to when I built the my-app:1.0 image I periodically compare the SHA256 digests of the layers of the node:16.3 with those of the first n=(number of layers of node:16.3) layers of my-app:1.0 as suggested in this answer. I retrieve the SHA256 digests with docker manifest inpect <image>:<tag> -v.
If they differ I rebuild my-app:1.0 and push it to my registry thus ensuring that my-app:1.0 always uses the latest node:16.3 base image.
I keep the running containers on my server up to date by periodically running docker pull my-app:1.0 on the server using a cron job.
Limitations
When I check for updates I need to download the manifests for all my container images and their base images. For images hosted on Docker Hub this unfortunately counts against the download rate limit.
Since I always update the same image my-app:1.0 it is hard to track which version is currently running on the server. This information is especially important when the update process breaks a service. I keep track of the updates by logging the output of the docker pull command from the cron job.
To be able to revert the container image on the server I have to keep previous versions of the my-app:1.0 images as well. I do that by pushing incremental patch version tags along with the my-app:1.0 tag to my registry e.g. my-app:1.0.1, my-app:1.0.2, ...
Because of the way the layers of the base image and the app image are compared it is not possible to detect a change in the base image where only the uppermost layers have been removed. However I do not expect this to happen very frequently.
Thank you for your help!
There are a couple of things I'd do to simplify this.
docker pull already does essentially the sequence you describe, of downloading the image's manifest and then downloading layers you don't already have. If you docker build a new image with an identical base image, an identical Dockerfile, and identical COPY source files, then it won't actually produce a new image, just put a new name on the existing image ID. So it's possible to unconditionally docker build --pull images on a schedule, and it won't really use additional space. (It could cause more redeploys if neither the base image nor the application changes.)
[...] this unfortunately counts against the download rate limit.
There's not a lot you can do about that beyond running your own mirror of Docker Hub or ensuring your CI system has a Docker Hub login.
Since I always update the same image my-app:1.0 it is hard to track which version is currently running on the server. [...] To be able to revert the container image on the server [...]
I'd recommend always using a unique image tag per build. A sequential build ID as you have now works, date stamps or source-control commit IDs are usually easy to come up with as well. When you go to deploy, always use the full image tag, not the abbreviated one.
docker pull registry.example.com/my-app:1.0.5
docker stop my-app
docker rm my-app
docker run -d ... registry.example.com/my-app:1.0.5
docker rmi registry.example.com/my-app:1.0.4
Now you're absolutely sure which build your server is running, and it's easy to revert should you need to.
(If you're using Kubernetes as your deployment environment, this is especially important. Changing the text value of a Deployment object's image: field triggers Kubernetes's rolling-update mechanism. That approach is much easier than trying to ensure that every node has the same version of a shared tag.)

How do I get the last push of an image with the x.x tag if I already have an old push of an x.x image?

In 2019, I made a pull image of Python 3.6. After that, I was sure that the image was self-updating (I did not use it actively, I just hoped that the latest pushes themselves were pulled from the repository or something like that), but I was surprised when I accidentally noticed the download/creation date is 2019.
Q: How does image pull work? Are there flags so that the layer hash/its relevance* is checked every time the image is built? Perhaps there is a way to set this check through the docker daemon config file? Or do I have to delete the base image every time to get a new image?
What I want: So that every time I build my images, the base image is checked for the last push (publication of image) in the docker hub repository.
Note: I'm talking about images with an identical tag. Also, I'm not afraid to re-build my images, there is no purpose to preserve them.
Thanks.
You need to explicitly docker pull the image to get updates. For your custom images, there are docker build --pull and docker-compose build --pull options that will pull the base image (though there is not a "pull" option for docker-compose up --build).
Without this, Docker will never check for updates for an image it already has. If your Dockerfile starts FROM python:3.6 and you already have a local image with that name and tag, Docker just uses it without contacting Docker Hub. If you don't have it then Docker will pull it, once, and then you'll have it locally.
The other thing to watch for is that the updates do eventually stop. If you look at the Docker Hub python image page you'll notice that there are no longer rebuilds for Python 3.5. If you pin to a very specific patch version, the automated builds generally only build the latest patch version for each supported minor version; if your image is FROM python:3.6.11 it will never get updates because 3.6.12 is the latest 3.6.x version.

how to keep docker images if pulling newer image

I'm using some docker images, which I have pulled from a registry:
docker pull registry.example.com/project/backend:latest
docker pull registry.example.com/project/frontend:latest
Now there is a new version on the server registry. If I do a new pull, I will overwrite the current images. But I need to keep the current working images in case I do get some problems with the newest latest images.
So, how do I create a kind of backup of my running backend:latest and frontend:latest? After that I can pull the latest latest image and in case I need to, I can use the old working images...
To keep the current image on your local environment you can use docker tag
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
For example:
docker tag registry.example.com/project/backend:latest registry.example.com/project/backend:backup
Then when you pull the latest, the registry.example.com/project/backend:backup still existing
Pulling an image never deletes an existing image. However, if you have an image with the same name, the old image will become unnamed, and you'll have to refer to it by its image ID.
You've now seen the downside to using :latest tags. This is why it is better to reference an image by a specific version tag that the maintainer won't re-push.
First, you shouldn't be using latest in production environments. Rather define a tag you confirmed working.
And instead of executing stuff in an image to set it up, you should write a Dockerfile and make the installation repeatable and create your local image. That's actually one of the main reasons why docker is used.

Docker base image update in Kubernetes deployment

We have a base image with the tag latest. This base image is being used for bunch of applications. There might be some update on the base image like ( OS upgrade, ...).
Do we need to rebuild and redeploy all applications when there is a change in the base image? Or, since the tag is latest and the new base image also will be with the tag latest, it will be updating in the docker layer and will be taken care without a restart?
Kubernetes has an imagePullPolicy: setting to control this. The default is that a node will only pull an image if it doesn’t already have it, except that if the image is using the :latest tag, it will always pull the image.
If you have a base image and then some derived image FROM my/base:latest, the derived image will include a specific version of the base image as its lowermost layers. If you update the base image and don’t rebuild the derived images, they will still use the same version of the base image. So, if you update the base image, you need to rebuild all of the deployed images.
If you have a running pod of some form and it’s running a :latest tag and the actual image that tag points at changes, Kubernetes has no way of noticing that, so you need to manually delete pods to force it to recreate them. That’s bad. Best practice is to use some explicit non-latest version tag (a date stamp works fine) so that you can update the image in the deployment and Kubernetes will redeploy for you.
There are two levels to this question.
Docker
If you use something like FROM baseimage:latest, this exact image is pulled down on your first build. Docker caches layers on consecutive builds, so not only will it build from the same baseimage:latest, but it will also skip execution of the Dockerfile elements untill first changed/not-cached one. To make the build notice changes to your baseimage you need to run docker pull baseimage:latest prior to the build, so that next run uses new content under latest tag.
The same goes for versioned tags when they aggregate minor/patch versions like when you use baseimage:v1.2 but the software is updated from baseimage:v1.2.3 to v1.2.4, and by the same process content of v1.2.4 is published as v1.2. So be aware of how versioning for particular image is handled.
Kubernetes
When you use :latest to deploy to Kubernetes you usually have imagePullPolicy: Always set. Which as for Docker build above, means that the image is always pulled before run. This is far from ideal, and far from immutable. Depending on the moment of container restart you might end up with two pods running at the same time, both the same :latest image yet the :latest for both of them will mean different actual image underneath it.
Also, you can't really change image in Deployment from :latest to :latest cause that's no change obviously, meaning you're out of luck for triggering rolling update, unless you pass version in label or something.
The good practice is to version your images somehow and push updates to cluster with that version. That is how it's designed and intended to use in general. Some versioning schemas I used were :
semantic (ie. v1.2.7) : nice if your CI/CD tool supports it well, I used it in Concourse CI
git_sha : works in many cases but is problematic for rebuilds that are not triggered by code changes
branch-buildnum or branch-sha-buildnum : we use it quite a lot
that is not to say I completely do not use latest. In fact most of my builds are built as branch-num, but when they are released to production that are also tagged and pushed to registry as branch-latest (ie. for prod as master-latest), which is very helpful when you want to deploy fresh cluster with current production versions (default tag values in our helm charts are pointing to latest and are set to particular tag when released via CI)

Docker change existing image

Docker novice here. Is Docker analogous to GitHub in that you can commit changes to an image without having to re-build the image from scratch? If yes, what commands are used to do that?
Right now every time I make a change to my code I delete the current Docker image using docker system prune -a and re-build the image using docker build -t appname.
There's no need to delete the existing image first, you can rebuild and create a tag to the same image name that already exists. Images themselves are resolved to an immutable image ID that does not change. To change the contents of an image, you must build a new image that has a new image ID. And then to use it, you need to start new containers referencing that new image.
A rebuild from scratch will reuse the cache, so only commands in your Dockerfile that changed, or are after a change, will result in a rebuild. The layers in the beginning of your Dockerfile that are same as previous builds will be reused between images. Those layers need to be built previous on this host (or there's a --cache-from option if you are building in ephemeral cloud environments). Order matters with the build cache, as does the exact hash of the files and their metadata that you copy into your image.
The docker image prune command is useful after you rebuild an image with the same image name. In that scenario, docker will delete old image ID's that no longer have a reference (image name) pointing to it, and do not currently have a container using it. Note that this also removes those old images from the build cache, so you may want to keep some old images around to speed up builds should a change get reverted from a commit.

Resources