Docker change existing image - docker

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.

Related

How to delete cached/intermediate docker images after the cache gets invalidated?

I have a CI-pipeline that builds a docker image for my app for every run of the pipeline (and the pipeline is triggered by a code-push to the git repository.)
The docker image consists of several intermediate layers which progressively become very large in size. Most of the intermediate images are identical for each run, hence the caching mechanism of docker is significantly utilized.
However, the problem is that the final couple layers are different for each run, as they result from a COPY statement in dockerfile, where the built application artifacts are copied into the image. Since the artifacts are modified for every run, the already cached bottommost images will ALWAYS be invalidated. These images have a size of 800mb each.
What docker command can I use to identify (and delete) these image that gets replaced by newer images, i.e. when they get invalidated?
I would like to have my CI-pipeline to remove them at the end of the run so they don't end up dangling on the CI-server and waste a lot of disk space.
If I understand correctly: With every code push, CI pipeline creates new image, where new version of application is deployed. As a result, previously created image becomes outdated, so you want to remove it. To do so, you have to:
Get rid of all outdated containers, which where created from outdated image
display all containers with command docker ps -a
if still running, stop outdated containers with command docker stop [containerID]
remove them with command docker rm [containerID]
Remove outdated images with command: docker rmi [imageID]
To sum up why this process is needed: you cannot remove any image, until it is used by any existing container (even stopped containers still require their images). For this reason, you should first stop and remove old containers, and then remove old images.
Detection part, and automation of deletion process should be based on image versions and container names, which CI pipeline generates while creating new images.
Edit 1
To list all images, which have no relationship to any tagged images, you can use command: docker images -f dangling=true. You can delete them with the command: docker images purge.
Just one thing to remember here: If you build an image without tagging it, the image will appear on the list of "dangling" images. You can avoid this situation by providing a tag when you build it.
Edit 2
The command for image purging has changed. Right now the proper command is:
docker image prune
Here is a link with a documentation

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

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 )

How to catch changes in building docker image?

I am new to docker container. I met a problem about how to catch changes of my code. Some lines in my local style.css file have been changed. Then I built docker image again, but actually nothing changed when I browsed my app.
Here are some methods I found online and tried, but didn't work.
remove image and build again
--no-cache=true
add a comment in Dockerfile to make it different
docker system prune
--pull
(I also used git pull to get code on my cloud instance, these files were checked to be the latest.)
I know little about docker mechanism, could anyone tell me what the problem is?
Extra info I found:
After stopping container and removing image, I restart my instance, then build image, run container again. Only in this way, can I catch those changes. Does anyone know the problem?
Many thanks!
There appears to be a disconnect on the difference between a container and an image. The container is the running instance of your application. It is based on an image that you have already built. That image has a tag for easier referencing, but the real reference to the image is a sha256 hash and building a new image will change the hash that the tag points to without impacting any of your running containers.
Therefore, the workflow to update your running application in docker is to:
Build a new image
Stop the running container
Start a new container pointing to that image
Cleanup any old images or stopped containers
If you are using docker-compose, it automates the middle two steps with a docker-compose up command, and that even deletes the old container. Most users keep a few copies of older images to allow easy rollback.

updating docker image given changes to local filesystem

I am trying to work out how I can update an existing image when I make changes to the local filesystem that was used to create the docker image. I thought that I could use docker commits to do that, but it seems that that allows you to change the image when there are changes to the filesystem on a running image?
/app.py
build from file system
sudo docker build -t app
now there are local changes to /app.py. How do I change the image app to reflect the changes to /app.py? right now I'm having to delete the old image and then create a new one.
sudo docker rmi app
sudo docker build -t app
any help is appreciated!
First of all, there's no running image, only running container. Image is something deliverable in Docker way, you build your image and then start a container from it.
To your problem, I think you have mentioned your options:
Rebuild your image
Go inside a running container, make changes and docker commit it back. Personally I only use this way to fix a tiny problem or make a hotfix to my image if docker build takes a really long time.
Docker uses union FS with copy on write to build image, which means if you want make a change to an image, you can't change it in-place, it'll create extra layer(s) to reflect your change(s), it'll just use the same image name in some cases. And from the perspective of delivery, I think it's totally OK to build a new image (with different tag) for each release, or even it should be done this way, that's why you have an Dockerfile, and images are not only something you start your container, they're actually versioned delivery artifacts and you can roll back to any version if you want/need. So I think your current solution is OK.
A few more words here: for local development and test, you can just mount your /app.py as a volume to your container when you start it, something like docker run -v /path/to/host/app.py:/path/to/container/app.py your_base_image_to_run_app, then anything you changed on your local FS to app.py, it'll reflect to the container. When you finish your job, build a new image.
As per your current design Solution is to create new image and assign same tag.
Best solution is expose environment variables from docker image and use those variable to update app.py so that you don't need to change image every time.Only one image is sufficient.

Rebuild same docker image with only the additional changes in the Dockerfile

I build an docker image using a Dockerfile. After building the image, I made some basic changes on the Dockerfile. Is it possible to rebuild the same image with just the additional changes. Since, it takes very long time to create the image, I don't want to build it completely. Thanks in advance.
All docker build work in the way, that you describe.
The only thing need to be taken into account is layer dependencies.
Consider Dockerfile
FROM something
RUN cmd1
RUN cmd2
RUN cmd3
RUN cmd4
If you change cmd1 then all layers will be rebuilt, because they could be different with respect to cmd1
If you change cmd4 than only this command will be rebuilt, because it has not affect any other layers.
Think about what commands need to be run in what order - maybe you can improve it by reordering the statements.
Yes, if you tag your docker image myimage, just start your other Dockerfile with
FROM myimage
and put after this your additional changes
You can't rebuild it with the changes, you would need to store persistent data on a volume for that.
To save your changes,however, you can use commit:
https://docs.docker.com/engine/reference/commandline/commit/
Create a new image from a container's changes
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
It can be useful to commit a container’s file changes or settings into
a new image. This allows you debug a container by running an
interactive shell, or to export a working dataset to another server.
Generally, it is better to use Dockerfiles to manage your images in a
documented and maintainable way. Read more about valid image names and
tags.
The commit operation will not include any data contained in volumes
mounted inside the container.
By default, the container being committed and its processes will be
paused while the image is committed. This reduces the likelihood of
encountering data corruption during the process of creating the
commit.

Resources