In git, there's git rebase to take some commits and copy them on another branch. This is a relatively light operation because the blob nodes underneath aren't duplicated, only the commit node on top is changed. Is there such a thing for docker layers? Can I docker rebase ... a layer onto another image?
Imagine I have a layer that adds a bunch of files. (In this example we can assume it's always additive -- no changing files, no deleting files, only creating new ones where I'm confident nothing in the path existed previously.) Imagine this layer is big and bulky.
Now I invalidate an earlier layer because I wanted to change an environment variable or change a line in a file on a lower-layer. Of course when I next docker build ... it rebuilds from there on, so my new layer is rebuilt too. Except the layer in question doesn't really change -- the hash changes, but the content doesn't. I really don't want to push this layer to my docker repository and pull it to my production runtime. After all, the previous version had all this content.
So can I docker rebase layer abc123 from image mything:v.old onto mynewbase:v.next then tag it as mything:v.next?
Related
I'm just getting to grips with Docker. I need to update a base image for my image.
Questions
Do I need to completely recreate all the changes I made on top of the
new base image and save it as a new image?
What do people do to remember the changes they've made to their
image?
Do I need to completely recreate all the changes I made on top of the new base image and save it as a new image?
You don't. It is up to you whether you want to completely rebuild the image or to use your old one as a new base but unless we are talking about generic base image, such as one where you just preinstall things that you want available to all the derived images, it is probably better to just rebuild the image from scratch, otherwise you might end up cluttering images with stuff they don't need which is never a good thing (both from the perspective of size and security).
What do people do to remember the changes they've made to their image?
Right out of the box you can use history command to see what went into the image
docker image history <image>
which lists image's filesystem layers.
Personally, when I build images, I copy Dockerfile to the image so that I can quickly cat it.
docker exec <image> cat Dockerfile
It is more convenient for me than listing through the history output (I don't include anything sensitive in a dockerfile and all the information that it has is already available within the container if someone breaks in).
I'm using WebSphere Liberty inside. As WebSphere Liberty requires frequent xml editing, which is impossible with Dockerfile commands. I have to docker-commit the container from time to time, for others to make use of my images.
The command is like:
docker commit -m "updated sa1" -a "Song" $id company/wlp:v0.1
Colleges are doing similar things to the image, they continue to docker commit the container several times every day.
One day we're going to deploy the image on production.
Q1: Is the practice of frequent docker-committing advised?
Q2: Does it leave any potential problem behind?
Q3: Does it create an extra layer? I read the docker-commit document, which didn't mention if it creates another layer, I assume it means no.
I wouldn't use docker commit,
It seems like a really good idea but you can't reproduce the image at will like you can with a Dockerfile and you can't change the base image once this is done either, so makes it very hard to commit say for example a security patch to the underlying os base image.
If you go the full Dockerfile approach you can re-run docker build and you'll get the same image again. And you are able to change the base image.
So my rule of thumb is if you are creating a temporary tool and you don't care about reuse or reproducing the image at will then commit is convenient to use.
As I understand Docker every container image has two parts this is a group of read-only layers making up the bulk of the image and then a small layer which is writeable where any changes are committed.
When you run commit docker goes ahead and creates a new image this is the base image plus changes you made (the image created is a distinct image), it copies up the code to the thin writable layer. So a new read-only layer is not created it merely stores the deltas you make into the thin writeable layer.
Don't just take my word for it, take Redhats advice
For clarity that article in step 5 says:
5) Don’t create images from running containers – In other terms, don’t
use “docker commit” to create an image. This method to create an image
is not reproducible and should be completely avoided. Always use a
Dockerfile or any other S2I (source-to-image) approach that is totally
reproducible, and you can track changes to the Dockerfile if you store
it in a source control repository (git).
Many people know, what docker build and docker push are doing in general on a high level, but what do they exactly do on a low level?
let's say we have a Dockerfile like this
FROM alpine:latest
RUN touch ~/tmp
RUN touch ~/tmp2
this will create the delta filesystem (only changes) for each layer in /var/lib/docker/overlay2.
layer contains a whole filesystem
layer contains the file ~/tmp
layer contains the file ~/tmp2
Open questions
What is the actual link between the layers? Is there a json, containing all the image info, including a sorted list of layers?
What kind of deliverable is generated to send it to the docker registry while performing docker push is it a tar.gz, similar to docker save
From my point of view:
Each layer is transferred (only modified files add/update/deleted files instruction) to docker-registry.
Each layer know it's parent layer.
So when you pull down a child layer - it will pull parent layer hierarchy till the top(base layer).
Each layer is identified by identifiers (sha256 code and not by name).
Any change in the hierarchy will cause a different sha256 code-name for all the child image layers(even though there is no change in that layer).
Feel free to add or to suggest improvements.
I'm creating some very simple Docker containers. I understand that after each step a new container is created. However, when using other Dockerfiles from the Hub I don't wind up with untagged images. So where do they come from? After browsing around online I have found out how to remove them but I want to gain a better understanding where they come from. Ideally I would like to prevent them from ever being created.
From their documentation
This will display untagged images, that are the leaves of the images
tree (not intermediary layers). These images occur when a new build of
an image takes the repo:tag away from the IMAGE ID, leaving it
untagged. A warning will be issued if trying to remove an image when a
container is presently using it. By having this flag it allows for
batch cleanup.
I don't quite understand this. Why are builds taking the repo:tag away from the IMAGE ID?
Whenever you assign a tag that is already in use to a new image (say, by building image foo, making a change in its Dockerfile, and then building foo again), the old image will lose that tag but will still stay around, even if all of its tags are deleted. These older versions of your images are the untagged entries in the output of docker images, and you can safely delete them with docker rmi <IMAGE HASH> (though the deletion will be refused if there's an extant container still using that image).
Docker uses a file system called AUFS, which stands for Augmented File System. Pretty much each line of a Docker file will create a new image and when you stack or augment them all on top of each other you'll get your final docker image. This is essentially a way of caching, so if you change only the 9th line of your Docker file it wont rebuild the entire image set. (Well depends on what commands you have on your Docker file, if you have a COPY or ADD nothing after that point is cached for ex)
The final image will get tagged with whatever label it has, but all these intermediary images are necessary in order to create the final image so it doesn't make sense to delete them or prevent them from being created. Hope that makes sense.
I am considering using Docker for a project, but I have one question I have not been able to find an answer to in the docs or anywhere else: is it possible to commit only a subset of the changes I make to an image?
Let's say I start a container, make a bunch of changes to the filesystem, get my thing working well and then want to commit them to a new base image. However, I only want a small subset of the changes that were actually made (maybe some of them were to log files, or other things that are unimportant). Does docker commit allow to specify that I only want changes, say, under some part of the filesystem to be committed?
You would just first remove the unnessecary files. To my knowledge, partial commits are not supported.
If you want to do this, using docker diff is really handy. You can easily see what the file system changes are that way. You can even run that against a running container.