Docker docker-compose not picking up relevant cached image - docker

I ran docker-compose build celery and (after hours of attempts on my poor connection) it succeeded. The first 80% of the app Dockerfile is identical, yet it doesn't reuse the cache. From what I can tell browsing around, Docker compares the base image and the instructions in Dockerfile and reuses if possible.
UPDATE: The problem this question refers to disappeared, no idea why. Notes below.
Yet I'm getting:
docker-compose build celery
Building celery
Step 1 : FROM python:2.7
---> eb867117097c
Step 2 : RUN apt-get update && apt-get install -y vim gdal-bin libgdal-dev postgresql-client
---> Using cache
---> 2966946ca235
. . . identical steps . . .
Step 9 : RUN pip install --no-cache-dir -r requirements/production.txt
---> Running in 02b42f721a34
Collecting git+https://github.com . . .
. . .
---> f70ecc01cada
Removing intermediate container 02b42f721a34
Step 10 : RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
---> Running in 3575383edcef
When I type docker images I can see f70ecc01cada, but it is not found in the following:
docker-compose build app
Building app
Step 1 : FROM python:2.7
---> eb867117097c
Step 2 : RUN apt-get update && apt-get install -y vim gdal-bin libgdal-dev postgresql-client
---> Using cache
---> 03e5040df047
. . . identical steps . . .
Step 9 : RUN pip install --no-cache-dir -r requirements/production.txt
---> Running in bb26fab28548
Collecting git+https://github.com . . .
. . .
Am I right in thinking the latter should reuse the f70ecc01cada cached image? [Edit: I now realize 03e5040df047 is just a container thanks #Ohmen, so no point deleting it]
I have tried editing docker-compose to be identical memory etc but it didn't help. Can I force reuse?
UPDATE: The app build all of a sudden started picking up the cache from the celery build. I don't know what fixed it. Between it failing and working, I did connect to the image on the celery hierarchy just before the Dockerfiles diverged, manually ran the remaining commands on the app image (docker run -it ... --entry=bash, adduser and chown app/), and docker committed the resulting container. I then re-ran docker-compose build app and for some reason it picked up the cache from the celery hierarchy correctly and ran the remaining commands (create user and chown) successfully. I have no idea what made it work all of a sudden.
EDIT: Docker history of the two image trees, showing that the history diverged on the line RUN apt-get update && apt-get install -y vim gdal-bin libgdal-dev postgresql-client. Maybe I ran both builds simultaneously? I was pretty sure I didn't, as on my connection they take an hour or more, but perhaps that is the boring and embarrassing explanation. Perhaps Docker was randomly selecting the tree that subsequently failed, then randomly (after 56 attempts) selected the one that didn't.
Initial successful build - layers sequenced from bottom up:
Chriss-MacBook-Pro:~ technicaltitch$ docker history fdw_celery
IMAGE CREATED CREATED BY SIZE COMMENT
d71bf8f2b902 28 hours ago /bin/sh -c #(nop) ENTRYPOINT ["/bin/sh" "-c" 0 B
336bdb2b9ca9 28 hours ago /bin/sh -c #(nop) USER [celery] 0 B
a1259f89bc1f 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 15.39 MB
57dd9a330337 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 335.2 kB
3accad8aa55c 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 0 B
b4ff0c1d71fb 28 hours ago /bin/sh -c #(nop) COPY dir:a8f922c5264fe2275a 15.39 MB
bc65bc84abbc 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 0 B
f70ecc01cada 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 214.1 MB
85eb413bd5da 28 hours ago /bin/sh -c #(nop) ARG PIP_INDEX_URL=https:// 0 B
3cee97bf3ce1 28 hours ago /bin/sh -c #(nop) ARG PIP_TRUSTED_HOST=127.0 0 B
cb090c4b1886 28 hours ago /bin/sh -c #(nop) WORKDIR /usr/src/app 0 B
d0a1d3d15f94 28 hours ago /bin/sh -c #(nop) COPY dir:ec1503af2cbef5220d 3.145 kB
e27ecd707562 28 hours ago |1 http_proxy= /bin/sh -c mkdir -p /usr/src/a 0 B
0b6e2857206c 28 hours ago |1 http_proxy= /bin/sh -c VERSION=$(gdal-conf 5.903 MB
2966946ca235 29 hours ago |1 http_proxy= /bin/sh -c apt-get update && a 241.4 MB
eb867117097c 42 hours ago /bin/sh -c #(nop) CMD ["python2"] 0 B
Second, failing build, with same script:
Chriss-MacBook-Pro:~ technicaltitch$ docker history 5ee
IMAGE CREATED CREATED BY SIZE COMMENT
5ee29875a771 29 hours ago /bin/sh -c #(nop) ARG PIP_INDEX_URL=https:// 0 B
daad37fd701b 29 hours ago /bin/sh -c #(nop) ARG PIP_TRUSTED_HOST=127.0 0 B
f891ee9277da 29 hours ago /bin/sh -c #(nop) WORKDIR /usr/src/app 0 B
2cce4acf9f2f 29 hours ago /bin/sh -c #(nop) COPY dir:ec1503af2cbef5220d 3.145 kB
9484e6b7fa51 29 hours ago |1 http_proxy=None /bin/sh -c mkdir -p /usr/s 0 B
0031cdd56926 29 hours ago |1 http_proxy=None /bin/sh -c VERSION=$(gdal- 5.903 MB
03e5040df047 <-- [already diverged] |1 http_proxy=None /bin/sh -c apt-get update 243.1 MB
eb867117097c 42 hours ago /bin/sh -c #(nop) CMD ["python2"] 0 B
Finally, this is the image history after it magically succeeded. I had not deleted the other images when this worked (and still haven't):
Chriss-MacBook-Pro:~ technicaltitch$ docker history fdw_app
IMAGE CREATED CREATED BY SIZE COMMENT
edc615da4d6f 26 hours ago /bin/sh -c #(nop) ENTRYPOINT ["/usr/src/app/ 0 B
747c8744f6a7 26 hours ago /bin/sh -c #(nop) EXPOSE 8000/tcp 0 B
8c543333e10b 26 hours ago /bin/sh -c #(nop) USER [django] 0 B
b08f02d80d29 26 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 15.39 MB
ba42415ad78b 26 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 335.2 kB
027e0c8e39a9 26 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 0 B
d56a78c02d18 26 hours ago /bin/sh -c #(nop) COPY dir:9274abe4540edd1e86 15.39 MB
bc65bc84abbc 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 0 B
f70ecc01cada 28 hours ago |3 PIP_INDEX_URL=https://pypi.python.org/simp 214.1 MB
85eb413bd5da 28 hours ago /bin/sh -c #(nop) ARG PIP_INDEX_URL=https:// 0 B
3cee97bf3ce1 28 hours ago /bin/sh -c #(nop) ARG PIP_TRUSTED_HOST=127.0 0 B
cb090c4b1886 28 hours ago /bin/sh -c #(nop) WORKDIR /usr/src/app 0 B
d0a1d3d15f94 28 hours ago /bin/sh -c #(nop) COPY dir:ec1503af2cbef5220d 3.145 kB
e27ecd707562 28 hours ago |1 http_proxy= /bin/sh -c mkdir -p /usr/src/a 0 B
0b6e2857206c 28 hours ago |1 http_proxy= /bin/sh -c VERSION=$(gdal-conf 5.903 MB
2966946ca235 <-- [correct img, suddenly..why?!] ..xy= /bin/sh -c apt-get update && a 241.4 MB
eb867117097c 42 hours ago /bin/sh -c #(nop) CMD ["python2"] 0 B
The following is the start of both Dockerfiles. I have tried making the machine definitions in docker-compose.yml identical but to no avail (possibly after the 03e image was created).
FROM python:2.7
RUN apt-get update && apt-get install -y vim gdal-bin libgdal-dev postgresql-client
# On Jessie, $(gdal-config --version) gives 1.10.1 and Pypi only has 1.10.0
# so we need to strip the last part of the version number
RUN VERSION=$(gdal-config --version); CFLAGS=$(gdal-config --cflags) easy_install GDAL==${VERSION%.*}
RUN mkdir -p /usr/src/app/requirements
COPY requirements /usr/src/app/requirements
WORKDIR /usr/src/app
ARG PIP_TRUSTED_HOST=127.0.0.1
ARG PIP_INDEX_URL=https://pypi.python.org/simple/
RUN pip install --no-cache-dir -r requirements/production.txt
# Clean up APT when done.
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY . /usr/src/app
RUN mkdir -p /usr/src/app/log

COPY requirements /usr/src/app/requirements
If you change ANY file in this directory it will invalidate the cache from this point onward. This includes tmp files created by your editor, unless you put them in your .dockerignore file.
ARG PIP_TRUSTED_HOST=127.0.0.1
ARG PIP_INDEX_URL=https://pypi.python.org/simple/
If you change either of these ARG it will invalidate the cache from this point onward.
My guess is that one of these changes, causing it to skip the cache.

The ---> 03e5040df047 is just a temporary name for the container this does not indicate diferent images...
Docker uses the cache if the base image is known and the commands are the same. For ADD od COPY also the checksum of the files has to match as well. As if I can tell docker uses the cache in your snippet.
You can only force docker to not use the cache.

Related

Missing <missing> Image Layer ID for subsequent child layers - DOCKER_BUILDKIT=1 docker build and docker history <image id>

Docker version 19.03.12, build 48a66213fe
OS: Red Hat Enterprise Linux Server release 7.9 (Maipo)
Please See: Dockerfile have minimal steps and it builds successfully.
To create docker image, I ran (this command will capture stdout/stderr in a .log file):
DOCKER_BUILDKIT=1 docker build \
--network=host \
-t project-opensuse-docker-image:15.2 . \
|& tee -a /tmp/project-opensuse-docker-image.log
When I use DOCKER_BUILDKIT=1 during docker build ....... command, it makes Image ID of the subsequent image layers listed as <missing>; If I re-run docker build again as soon as the first run completes successfully, it is NOT able to use any layer cache concept to go/build fast even though there's no change done to Dockerfile or any folder/file on the file system (i.e if used during COPY / ADD etc steps).
It builds everytime from scratch and I think, the reason is because there's NO valid IMAGE ID for those subsequent layers under the final Image ID: a07eebd0a9ba.
See Image ID in docker history <image> output showing for subsequent layer's IDs.
WHY am I getting under Image ID for these layers?
​
[gigauser#rh79maipo_machine opensuse-x]$ sudo docker history a07eebd0a9ba
IMAGE CREATED CREATED BY SIZE COMMENT
a07eebd0a9ba 3 hours ago CMD ["ls -l"] 0B buildkit.dockerfile.v0
<missing> 3 hours ago WORKDIR /home/nonroot_user 0B buildkit.dockerfile.v0
<missing> 3 hours ago VOLUME [/home/nonroot_user/git] 0B buildkit.dockerfile.v0
<missing> 3 hours ago RUN /bin/sh -c ls -l /home/nonroot_user /home/rayd… 174MB buildkit.dockerfile.v0
<missing> 3 hours ago COPY ./boost_1_68_0.tar.gz /home/nonroot_user/tool… 109MB buildkit.dockerfile.v0
<missing> 3 hours ago RUN /bin/sh -c ls -l /home/nonroot_user /home/rayd… 253MB buildkit.dockerfile.v0
<missing> 3 hours ago COPY ./wxWidgets-3.1.3.tar.bz2 /home/nonroot_user/… 21.3MB buildkit.dockerfile.v0
<missing> 3 hours ago RUN /bin/sh -c echo -e "\n-- Installing Zypper… 1.71GB buildkit.dockerfile.v0
<missing> 26 hours ago RUN /bin/sh -c useradd -r -m -l … 953kB buildkit.dockerfile.v0
<missing> 26 hours ago COPY ./CompanyCertBundle/PEM/*.cer /usr/sha… 34.7kB buildkit.dockerfile.v0
<missing> 26 hours ago ENV http_proxy=http://company.proxy.com:80/ … 0B buildkit.dockerfile.v0
<missing> 26 hours ago LABEL Project=aPROJECT IM CentOS_Version=CentOS… 0B buildkit.dockerfile.v0
<missing> 4 weeks ago KIWI 9.23.20 109MB
[gigauser#rh79maipo_machine opensuse-x]$

Retrieve file that was deleted during the image build

I have a docker image.
When I use the docker history command on the image, I can see
85d9bf810d44 9 days ago /bin/sh -c apk add vim 26.9MB
<missing> 9 days ago /bin/sh -c apk update 1.78MB
<missing> 9 days ago /bin/sh -c rm -f file.txt 0B
<missing> 9 days ago /bin/sh -c a=$(base64 -d < file.txt) && echo $a … 49B
<missing> 9 days ago /bin/sh -c #(nop) COPY file:98f5646751cb4985… 68B
<missing> 6 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 6 weeks ago /bin/sh -c #(nop) ADD file:f17f65714f703db90… 5.57MB
So there was a file.txt at some point in the image, but it was later removed. I would like to know if there is a way to retrieve the content of that file from the image layers.
I have looked into Dive and all sorts of stuff. Also navigating through Docker's overlay files (as indicated here) seemed promising, but I am using macOS and I couldn't find the corresponding directories...
docker image save will export a tarball that contains a tarball per layer.
https://docs.docker.com/engine/reference/commandline/image_save/

how to get size of docker image layers

I have pulled a couple of images from my private repo. am able to see the size of the layers using docker history <image-id> i don't see actual layer sha256 id for the layers instead it shows missing. So am not sure how I can get the size of each layer.
Actually, I want the size of each layer in the image.
am able to get layers details from docker-inspect command docker inspect <image-id> | jq .[].RootFS.Layers
docker history f183414e30ab
IMAGE CREATED CREATED BY SIZE COMMENT
f183414e30ab 16 months ago /bin/sh -c apt-get update && apt-get install… 317MB
<missing> 16 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 16 months ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 16 months ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 16 months ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 987kB
<missing> 16 months ago /bin/sh -c #(nop) ADD file:3ddd02d976792b6c6… 63.2MB
Problem trying to solve: am trying to get the size of docker image layers which also been shared with other images.
You are looking for command docker system df

Docker build hangs. How can I see what is going on?

This Dockerfile hangs after the download has completed:
FROM ubuntu:18.04
MAINTAINER Dean Schulze
ENV JS_CE_VERSION 7.1.0
ENV JS_CE_HOME /opt/jasperreports-server-cp-${JS_CE_VERSION}
ENV PATH $PATH:${JS_CE_HOME}
RUN apt-get update && apt-get install -y wget \
&& wget --progress=bar:force:noscroll -O TIB_js-jrs-cp_${JS_CE_VERSION}_linux_x86_64.run https://sourceforge.net/projects/jasperserver/files/JasperServer/JasperReports%20Server%20Community%20Edition%20${JS_CE_VERSION}/TIB_js-jrs-cp_${JS_CE_VERSION}_linux_x86_64.run \
&& chmod a+x TIB_js-jrs-cp_${JS_CE_VERSION}_linux_x86_64.run \
&& /TIB_js-jrs-cp_${JS_CE_VERSION}_linux_x86_64.run --mode unattended --jasperLicenseAccepted yes --postgres_password Postgres1 --prefix ${JS_CE_HOME} \
&& rm TIB_js-jrs-cp_${JS_CE_VERSION}_linux_x86_64.run \
&& rm -rf ${JS_CE_HOME}/apache-ant ${JS_CE_HOME}/buildomatic \
${JS_CE_HOME}/docs ${JS_CE_HOME}/samples ${JS_CE_HOME}/scripts \
&& apt-get clean
EXPOSE 8080
CMD ctlscript.sh start && tail -f /dev/null
The last few lines of output are
Resolving superb-dca2.dl.sourceforge.net (superb-dca2.dl.sourceforge.net)... 209.61.193.20
Connecting to superb-dca2.dl.sourceforge.net (superb-dca2.dl.sourceforge.net)|209.61.193.20|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 343517555 (328M) [application/x-makeself]
Saving to: 'TIB_js-jrs-cp_7.1.0_linux_x86_64.run'
TIB_js-jrs-cp_7.1.0 100%[===================>] 327.60M 1.60MB/s in 3m 52s
2018-07-28 03:15:28 (1.41 MB/s) - 'TIB_js-jrs-cp_7.1.0_linux_x86_64.run' saved [343517555/343517555]
How do I diagnose a docker build that hangs like this?
Edit
Here's the requested output:
$ docker image history d5d47e51eafc
IMAGE CREATED CREATED BY SIZE COMMENT
d5d47e51eafc 23 minutes ago /bin/sh -c #(nop) ENV PATH=/usr/local/sbin:… 0B
831a3a551fa7 23 minutes ago /bin/sh -c #(nop) ENV JS_CE_HOME=/opt/jaspe… 0B
e8361426e492 23 minutes ago /bin/sh -c #(nop) ENV JS_CE_VERSION=6.3.0 0B
7af364f52b1b 23 minutes ago /bin/sh -c #(nop) MAINTAINER JS Minet 0B
735f80812f90 30 hours ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 30 hours ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 30 hours ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$… 2.76kB
<missing> 30 hours ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 30 hours ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 30 hours ago /bin/sh -c #(nop) ADD file:4bb62bb0587406855… 83.5MB
It looks like the lines are in reverse order and the last one executed is the ENV PATH. The next line would be the RUN line which has multiple commands separated by &&.
This is a modification of a Dockerfile on DockerHub. I changed ubuntu versions and the version of the app being installed. That shouldn't have broken anything.
Should I file a bug report?
First list the "layers" of your finished or incomplete image. Each layer typically corresponds to an instruction in your Dockerfile.
Identify the image ID using
docker image ls
then list the layers inside the image using
docker image history <ID>
You will see something like this:
IMAGE CREATED CREATED BY SIZE COMMENT
6c32fe3da793 2 days ago /bin/sh -c #(nop) COPY file:c25ef1dcc737cb59… 635B
4c1309db9b9c 2 days ago /bin/sh -c #(nop) COPY dir:30506cf0fc0cdb096… 8.64kB
5f5ae40b5fd5 3 weeks ago /bin/sh -c apk update && apk add --no-cache … 164MB
989d78741d0b 3 weeks ago /bin/sh -c #(nop) ENV DOCKER_VERSION=18.03.… 0B
6160911711fc 3 weeks ago /bin/sh -c #(nop) CMD ["python3"] 0B
... etc
Then create a container from any point inside your image. From there you can perform the next instruction that would cause a problem in your Dockerfile.
Eg:
docker run -it --rm 4c1309db9b9c sh
At some point in the last couple of years, Buildkit has become the default Docker backend. Buildkit does not write out intermediate layers as images, as a performance optimization. Therefore, if you need to debug a hanging docker build, comment out the line that the build is hanging at and all subsequent lines. You now have a Dockerfile that you can execute $ docker build . with. Once the build is finished, you can launch the image, bash into the container, run the command that is causing the build to hang and then inspect the state of the container to debug the situation.
I got this to work by rolling back to ubuntu 16.04. There must be some change in 18.04 that causes this Dockerfile to fail, or maybe the ubuntu 18.04 image omitted something.

How do you know what you're getting when you pull an existing Docker image?

When you create your own docker image, you usually start a Docker file with FROM, and base your image off something that already exists on docker hub. How can I learn more about what is actually in the image I am referencing?
For example, I'm interested in starting with this image:
https://hub.docker.com/_/swift/
Besides what's listed in the description fields on that webpage, how can I verify what is actually getting installed? Is there a way to view a Dockerfile for an existing image on docker hub?
Thanks
Dockerfile links
Many images, especially "official" images, will contain Dockerfile links. You'll find them in the description on Docker Hub. For instance, right now at the link you posted in your question, you'll find a few image tags and a couple of links to Dockerfile.
3.1.0, 3.1, 3, latest (Dockerfile)
Simply click on "Dockerfile" and it will take you to the Dockerfile that was used to build that version of the image.
It should be noted that this is metadata associated with the Docker Hub account. You can't completely trust that it is correct, because it's just a link. (To GitHub, in this case, but it can be anywhere.)
Since you can't completely trust that, you may want to look also at...
Docker history
If you docker pull swift to fetch the image, you can then use the docker history command to take a closer look at it. Currently, that looks like this:
IMAGE CREATED CREATED BY SIZE COMMENT
d505ae70cb39 2 weeks ago /bin/sh -c swift --version 0B
<missing> 2 weeks ago /bin/sh -c SWIFT_URL=https://swift.org/bui... 403MB
<missing> 2 weeks ago /bin/sh -c #(nop) ENV SWIFT_PLATFORM=ubun... 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ARG SWIFT_VERSION=swift... 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ARG SWIFT_BRANCH=swift-... 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ARG SWIFT_PLATFORM=ubun... 0B
<missing> 2 weeks ago /bin/sh -c apt-get -q update && apt-ge... 626MB
<missing> 2 weeks ago /bin/sh -c #(nop) MAINTAINER Haris Amin <... 0B
<missing> 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 weeks ago /bin/sh -c mkdir -p /run/systemd && echo '... 7B
<missing> 2 weeks ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\... 2.76kB
<missing> 2 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 2 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' >... 745B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:5aff8c59a707833... 118MB
You'll notice that the commands used to build each layer of the image are truncated for display. That makes this display not especially useful, but you can use the --no-trunc flag to get a much more verbose output.
docker history --no-trunc swift:latest
Then you will get a lot of output (more than I will paste here), but here is a one-entry sample:
<missing> 2 weeks ago /bin/sh -c SWIFT_URL=https://swift.org/builds/$SWIFT_BRANCH/$(echo "$SWIFT_PLATFORM" | tr -d .)/$SWIFT_VERSION/$SWIFT_VERSION-$SWIFT_PLATFORM.tar.gz && curl -fSsL $SWIFT_URL -o swift.tar.gz && curl -fSsL $SWIFT_URL.sig -o swift.tar.gz.sig && export GNUPGHOME="$(mktemp -d)" && set -e; for key in 7463A81A4B2EEA1B551FFBCFD441C977412B37AD 1BE1E29A084CB305F397D62A9F597F4D21A56D5F A3BAFD3556A59079C06894BD63BC1CFE91D306C6 ; do gpg --quiet --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; done && gpg --batch --verify --quiet swift.tar.gz.sig swift.tar.gz && tar -xzf swift.tar.gz --directory / --strip-components=1 && rm -r "$GNUPGHOME" swift.tar.gz.sig swift.tar.gz 403MB
Most of the text is simply the commands executed by Dockerfile RUN statements. You will also see the other Dockerfile commands like ARG, CMD, ADD, COPY, etc.
Since this is encoded into the image layers, it is probably more reliable (if less readable) than the Dockerfile links found in the Docker Hub readme file.

Resources