Reputation: 526
i am trying to build up a base image with certain components only - in my case, just some packages to run a java application. The idea is for this to be as minimal as possible.
My question is how can I get the bare minimum required to get the most optimal image and get all the dependencies for my packages(like openssl) to run without an issue?
I am aware of this answer. However, it shows that we still need to manually copy all the dependencies. is there a better way to do it? such that i can automatically copy all the dependencies and the executable itself to the new image.
Upvotes: 0
Views: 1426
Reputation: 311238
I once wrote a tool called dockerize that does most of what you want. Given a command, it reads the name of the dynamic linker embedded in that command, and then uses that to get a list of dependencies, which it uses to build a Docker image.
So for example, run as dockerize -o openssl /usr/bin/openssl
, this produces a directory with a Dockerfile
and all the necessary files:
openssl
├── Dockerfile
├── etc
│ ├── group
│ ├── nsswitch.conf
│ └── passwd
├── lib64
│ ├── ld-linux-x86-64.so.2
│ ├── libcrypto.so.1.1
│ ├── libc.so.6
│ ├── libdl.so.2
│ ├── libnss3.so
│ ├── libnssckbi.so
│ ├── libnss_compat-2.33.so
│ ├── libnss_compat.so.2
│ ├── libnss_dns-2.33.so
│ ├── libnss_dns.so.2
│ ├── libnss_files-2.33.so
│ ├── libnss_files.so.2
│ ├── libnss_libvirt_guest.so.2
│ ├── libnss_libvirt.so.2
│ ├── libnss_mdns4_minimal.so.2
│ ├── libnss_mdns4.so.2
│ ├── libnss_mdns6_minimal.so.2
│ ├── libnss_mdns6.so.2
│ ├── libnss_mdns_minimal.so.2
│ ├── libnss_mdns.so.2
│ ├── libnss_myhostname.so.2
│ ├── libnss_mymachines.so.2
│ ├── libnss_resolve.so.2
│ ├── libnss_sss.so.2
│ ├── libnsssysinit.so
│ ├── libnss_systemd.so.2
│ ├── libnssutil3.so
│ ├── libpthread.so.0
│ ├── libresolv-2.33.so
│ ├── libresolv.so
│ ├── libresolv.so.2
│ ├── libssl.so.1.1
│ └── libz.so.1
└── usr
└── bin
└── openssl
Building a Docker image from this directory results in a fully functional openssl
binary.
Now, you're asking to this within the context of a multi-stage Dockerfile
. That might look like this:
FROM ubuntu:bionic as base
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
git \
openssl \
python3 \
python3-pip \
rsync \
&& \
apt-get clean
RUN pip3 install git+https://github.com/larsks/dockerize
RUN dockerize -o /tmp/openssl -n /usr/bin/openssl
FROM scratch
COPY --from=base /tmp/openssl/ /
ENTRYPOINT ["/usr/bin/openssl"]
This installs dockerize
in the build stage, and uses that to generate a directory with the target command and its dependencies, then copies this into the scratch image in the final stage.
There's nothing special about the dockerize
script; it's mostly a convenience wrapper and we could achieve a similar result like this:
FROM ubuntu:bionic as base
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
openssl \
&& \
apt-get clean
RUN mkdir /tmp/openssl
# The following command (a) produces a list of files to copy into
# the target directory, and then (b) uses `xargs` and `cp` to copy
# them.
#
# We list the binary itself, the appropriate dynamic interpreter, and
# then we use `ldd` to get a list of dependencies from the binary.
RUN (echo /usr/bin/openssl; \
echo /lib64/ld-linux*; \
ldd /usr/bin/openssl | awk '/=>/ {print $3}') | \
xargs -iFILE sh -c 'mkdir -p /tmp/openssl/$(dirname FILE); cp FILE /tmp/openssl/FILE'
FROM scratch
COPY --from=base /tmp/openssl/ /
ENTRYPOINT ["/usr/bin/openssl"]
For your specific example, this also produces a runnable openssl
image.
Upvotes: 2