Skip to content

Use a tiny base image for Docker builds #52519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 64 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
22e78ce
Dump container logs on shell command failure
pugnascotia Feb 13, 2020
1c31c7f
Throw original exception docker logs capture
pugnascotia Feb 14, 2020
545150b
First attempt at building and using a tiny base image
pugnascotia Feb 18, 2020
185f2fd
Build the baseimage in Docker
pugnascotia Feb 19, 2020
d6a5b9a
Merge remote-tracking branch 'elastic/master' into tiny-baseimage
jasontedor Mar 11, 2020
032ecea
Add Dockerfile for statically building curl
pugnascotia Mar 30, 2020
2056374
Merge curl building into tiny image build
pugnascotia Mar 30, 2020
06165f8
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 7, 2020
5701d91
Tweaks
pugnascotia Apr 8, 2020
807f05c
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 14, 2020
f47ba97
Integrate building a base image into the Docker build
pugnascotia Apr 14, 2020
95db73f
Allow a platform to be specified
pugnascotia Apr 14, 2020
7ca75f8
More improvements
pugnascotia Apr 15, 2020
c5d9aea
Build a static curl binary during the Docker build
pugnascotia Apr 15, 2020
b9f652f
Trying to get cross-arch Docker builds to work
pugnascotia Apr 15, 2020
0cfa2cc
Don't provide curl, and suggest wget instead
pugnascotia Apr 15, 2020
2f48178
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 15, 2020
8a54c4c
Revert something
pugnascotia Apr 16, 2020
fc3a6a0
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 16, 2020
f90d9d5
Tweaks
pugnascotia Apr 16, 2020
91557a5
Add missing license and javadoc
pugnascotia Apr 16, 2020
6b40ac5
Use wget instead of curl for docker-compose heathchecks
pugnascotia Apr 16, 2020
b67bb4e
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 17, 2020
c381560
Address review feedback
pugnascotia Apr 20, 2020
426b422
Compress base filesystem tar with gzip
pugnascotia Apr 20, 2020
d7fcb2d
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 20, 2020
e54e83c
Fix typo that stopped :qa:remote-clusters from logging
pugnascotia Apr 20, 2020
d826cdf
Fix docker-compose healthchecks
pugnascotia Apr 20, 2020
2f23482
docker-compose fixes
pugnascotia Apr 20, 2020
56340da
Remove curl wrapper around wget
pugnascotia Apr 20, 2020
678c361
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 21, 2020
18b0128
Set GCOS in /etc/password for Docker
pugnascotia Apr 21, 2020
22d0035
Add a note about extending the shrunk Docker image
pugnascotia Apr 21, 2020
c8a82a7
Reintroduce a static curl build, only much faster
pugnascotia Apr 22, 2020
37a0e78
Reuse the docker base filesystem for OSS
pugnascotia Apr 22, 2020
02ee7f7
Docs and permissions fixes
pugnascotia Apr 22, 2020
1762bcd
Upgrade tini and validate its checksum
pugnascotia Apr 22, 2020
d527a69
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 22, 2020
864e39e
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 23, 2020
2d16037
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Apr 24, 2020
8b3f624
Address review feedback
pugnascotia May 12, 2020
f49b8b6
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia May 12, 2020
02ddf44
WIP - trying for a cross-arch Docker build
pugnascotia May 14, 2020
cf322b0
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia May 21, 2020
11ae944
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Jun 12, 2020
00c3cb8
Perform all the build steps via the Dockerfile
pugnascotia Jun 12, 2020
38557c2
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Jun 25, 2020
66bec1b
Explicitly specify packages to install instead of hacky exclusion fiasco
pugnascotia Jun 25, 2020
a508421
Tweak comment
pugnascotia Jun 25, 2020
f9e9e6a
Add more into to ProcessInfo Javadoc
pugnascotia Jun 25, 2020
0daa840
Use musl busybox for both archs
pugnascotia Jun 25, 2020
7eedc3f
Update docs
pugnascotia Jun 25, 2020
ccc1209
Test tweak
pugnascotia Jun 25, 2020
6535024
Follow Docker guidelines and put script contents into Dockerfile
pugnascotia Jun 26, 2020
c75980b
Trim dependencies further
pugnascotia Jun 26, 2020
36836ab
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Jul 2, 2020
7bcafbf
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Jul 7, 2020
af035fe
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Jul 7, 2020
efa85db
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Aug 3, 2020
90a59fe
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Sep 10, 2020
5b9e929
Tweaks
pugnascotia Sep 10, 2020
afd3ed2
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Sep 16, 2020
4ea14d7
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Sep 28, 2020
64ee957
Merge remote-tracking branch 'upstream/master' into tiny-baseimage
pugnascotia Sep 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion distribution/docker/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ext.expansions = { Architecture architecture, boolean oss, DockerBase base, bool
RUN curl --retry 8 -S -L \\
--output /opt/elasticsearch.tar.gz \\
https://artifacts.elastic.co/downloads/elasticsearch/$elasticsearch
"""
""".trim()
}

return [
Expand Down
2 changes: 1 addition & 1 deletion distribution/docker/docker-test-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ cd /usr/share/elasticsearch/bin/
echo "testnode" > /tmp/password
cat /tmp/password | ./elasticsearch-keystore add -x -f -v 'xpack.security.transport.ssl.keystore.secure_password'
cat /tmp/password | ./elasticsearch-keystore add -x -f -v 'xpack.security.http.ssl.keystore.secure_password'
/usr/local/bin/docker-entrypoint.sh | tee > /usr/share/elasticsearch/logs/console.log
/usr/local/bin/docker-entrypoint.sh | tee /usr/share/elasticsearch/logs/console.log
199 changes: 184 additions & 15 deletions distribution/docker/src/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
layout/presentation here has been adjusted so that it looks reasonable when rendered,
at the slight expense of how it looks here.
*/ %>
<% if (docker_base == "ubi") { %>
################################################################################
# Build stage 0 `builder`:
# Extract Elasticsearch artifact
################################################################################

FROM ${base_image} AS builder
<% if (docker_base == 'ubi') { %>

# Install required packages to extract the Elasticsearch distribution
RUN ${package_manager} install -y tar gzip
<% } %>

# `tini` is a tiny but valid init for containers. This is used to cleanly
# control how ES and any child processes are shut down.
#
Expand All @@ -41,21 +42,167 @@ RUN set -eux ; \\
curl --retry 8 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\
sha256sum -c \${tini_bin}.sha256sum ; \\
rm \${tini_bin}.sha256sum ; \\
mv \${tini_bin} /tini ; \\
chmod +x /tini
mv \${tini_bin} /bin/tini ; \\
chmod +x /bin/tini
<% } else { %>
################################################################################
# Stage 1. Build curl statically. Installing it from RPM on CentOS pulls in too
# many dependencies.
################################################################################
FROM alpine:latest AS curl

ENV VERSION 7.71.0
ENV TARBALL_URL https://curl.haxx.se/download/curl-\${VERSION}.tar.xz
ENV TARBALL_PATH curl-\${VERSION}.tar.xz

# Install dependencies
RUN apk add gnupg gcc make musl-dev openssl-dev openssl-libs-static file

RUN mkdir /work
WORKDIR /work

# Fetch curl sources and files for validation
RUN wget "https://daniel.haxx.se/mykey.asc" -O "curl-gpg.pub" && \\
wget "\${TARBALL_URL}.asc" -O "\${TARBALL_PATH}.asc" && \\
wget "\${TARBALL_URL}" -O "\${TARBALL_PATH}"

# Validate source
RUN gpg --import --always-trust "curl-gpg.pub" && \\
gpg --verify "\${TARBALL_PATH}.asc" "\${TARBALL_PATH}"

# Unpack and build
RUN tar xfJ "\${TARBALL_PATH}" && \\
cd "curl-\${VERSION}" && \\
./configure --disable-shared --with-ca-fallback --with-ca-bundle=/etc/pki/tls/certs/ca-bundle.crt && \\
make curl_LDFLAGS="-all-static" && \\
cp src/curl /work/curl && \\
strip /work/curl

################################################################################
# Step 2. Create a minimal root filesystem directory. This will form the basis
# for our image.
################################################################################
FROM ${base_image} AS rootfs

ENV BUSYBOX_VERSION 1.31.0
ENV TINI_VERSION 0.19.0

# Start off with an up-to-date system
RUN ${package_manager} update --setopt=tsflags=nodocs -y

# Create a directory into which we will install files
RUN mkdir /rootfs

# Create required devices
RUN mkdir -m 755 /rootfs/dev && \\
mknod -m 600 /rootfs/dev/console c 5 1 && \\
mknod -m 600 /rootfs/dev/initctl p && \\
mknod -m 666 /rootfs/dev/full c 1 7 && \\
mknod -m 666 /rootfs/dev/null c 1 3 && \\
mknod -m 666 /rootfs/dev/ptmx c 5 2 && \\
mknod -m 666 /rootfs/dev/random c 1 8 && \\
mknod -m 666 /rootfs/dev/tty c 5 0 && \\
mknod -m 666 /rootfs/dev/tty0 c 4 0 && \\
mknod -m 666 /rootfs/dev/urandom c 1 9 && \\
mknod -m 666 /rootfs/dev/zero c 1 5

# Install a minimal set of dependencies, and some for Elasticsearch
RUN ${package_manager} --installroot=/rootfs --releasever=/ --setopt=tsflags=nodocs \\
--setopt=group_package_types=mandatory -y \\
--skip-broken \\
install basesystem bash zip zlib
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding pigz?
It's very useful for compressing large heap dumps. It's not a must-have from Cloud's perspective (we can fall back to gzip) but since we will be lacking package manager it'd make a lot of sense to add it here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mieciu It's my assumption that heap dumps would be written to a volume that is mounted on the host (otherwise, they vanish when the container dies, which will happen when the process died because of an OOME), and thus could be processed on the host external to the container.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point - @mieciu what is Cloud doing with heapdumps exactly - compressing them in the container?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move the compression out of the ES container and into the allocator container. Then pigz is always available (if we want it to be).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm happy to do something to smooth the way, though compressing heapdumps does feel like an orchestration concern, so ultimately I think the allocator should handle it in whatever way it deems fit.


# `tini` is a tiny but valid init for containers. This is used to cleanly
# control how ES and any child processes are shut down.
#
# The tini GitHub page gives instructions for verifying the binary using
# gpg, but the keyservers are slow to return the key and this can fail the
# build. Instead, we check the binary against the published checksum.
#
# Also, we use busybox instead of installing utility RPMs, which pulls in
# all kinds of stuff we don't want.
RUN set -e ; \\
TINI_BIN="" ; \\
BUSYBOX_ARCH="" ; \\
case "\$(arch)" in \\
aarch64) \\
BUSYBOX_ARCH='armv8l' ; \\
TINI_BIN='tini-arm64' ; \\
;; \\
x86_64) \\
BUSYBOX_ARCH='x86_64' ; \\
TINI_BIN='tini-amd64' ; \\
;; \\
*) echo >&2 "Unsupported architecture \$(arch)" ; exit 1 ;; \\
esac ; \\
curl --retry 8 -S -L -O "https://github.com/krallin/tini/releases/download/v0.19.0/\${TINI_BIN}" ; \\
curl --retry 8 -S -L -O "https://github.com/krallin/tini/releases/download/v0.19.0/\${TINI_BIN}.sha256sum" ; \\
sha256sum -c "\${TINI_BIN}.sha256sum" ; \\
rm "\${TINI_BIN}.sha256sum" ; \\
mv "\${TINI_BIN}" /rootfs/bin/tini ; \\
chmod +x /rootfs/bin/tini ; \\
curl --retry 10 -L -o /rootfs/bin/busybox \\
"https://busybox.net/downloads/binaries/\${BUSYBOX_VERSION}-defconfig-multiarch-musl/busybox-\${BUSYBOX_ARCH}" ; \\
chmod +x /rootfs/bin/busybox

# Add links for most of the Busybox utilities
RUN set -e ; \\
for path in \$( /rootfs/bin/busybox --list-full | grep -v bin/sh | grep -v telnet | grep -v unzip); do \\
ln /rootfs/bin/busybox /rootfs/\$path ; \\
done

# Curl needs files under here. More importantly, we change Elasticsearch's
# bundled JDK to use /etc/pki/ca-trust/extracted/java/cacerts instead of
# the bundled cacerts.
RUN mkdir -p /rootfs/etc && \\
cp -a /etc/pki /rootfs/etc/

# Cleanup the filesystem
RUN ${package_manager} --installroot=/rootfs -y clean all && \\
cd /rootfs && \\
rm -rf \\
etc/{X11,centos-release*,csh*,profile*,skel*,yum*} \\
sbin/sln \\
usr/bin/rpm \\
{usr,var}/games \\
usr/lib/{dracut,systemd,udev} \\
usr/lib64/X11 \\
usr/local \\
usr/share/{awk,centos-release,cracklib,desktop-directories,gcc-*,i18n,icons,licenses,xsessions,zoneinfo} \\
usr/share/{man,doc,info,games,gdb,ghostscript,gnome,groff,icons,pixmaps,sounds,backgrounds,themes,X11} \\
usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive} \\
var/cache/yum \\
var/lib/{rpm,yum} \\
var/log/yum.log

# ldconfig
RUN rm -rf /rootfs/etc/ld.so.cache /rootfs/var/cache/ldconfig && \\
mkdir -p --mode=0755 /rootfs/var/cache/ldconfig

COPY --from=curl /work/curl /rootfs/usr/bin/curl

################################################################################
# Step 3. Fetch the Elasticsearch distribution and configure it for Docker
################################################################################
FROM ${base_image} AS builder
<% } %>

RUN mkdir /usr/share/elasticsearch
WORKDIR /usr/share/elasticsearch

# Fetch the appropriate Elasticsearch distribution for this architecture
${source_elasticsearch}

RUN tar zxf /opt/elasticsearch.tar.gz --strip-components=1

# Configure the distribution for Docker
RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' /usr/share/elasticsearch/bin/elasticsearch-env
RUN mkdir -p config config/jvm.options.d data logs
RUN chmod 0775 config config/jvm.options.d data logs
COPY config/elasticsearch.yml config/log4j2.properties config/
RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties

<% if (docker_base == "ubi") { %>
################################################################################
# Build stage 1 (the actual Elasticsearch image):
#
Expand All @@ -65,12 +212,10 @@ RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties

FROM ${base_image}

ENV ELASTIC_CONTAINER true

RUN for iter in {1..10}; do \\
${package_manager} update --setopt=tsflags=nodocs -y && \\
${package_manager} install --setopt=tsflags=nodocs -y \\
nc shadow-utils zip unzip <%= docker_base == 'ubi' ? 'findutils procps-ng' : '' %> && \\
nc shadow-utils zip unzip findutils procps-ng && \\
${package_manager} clean all && exit_code=0 && break || exit_code=\$? && echo "${package_manager} error: retry \$iter in 10s" && \\
sleep 10; \\
done; \\
Expand All @@ -80,10 +225,30 @@ RUN groupadd -g 1000 elasticsearch && \\
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
chmod 0775 /usr/share/elasticsearch && \\
chown -R 1000:0 /usr/share/elasticsearch
<% } else { %>
################################################################################
# Stage 4. Build the final image, using the rootfs above as the basis, and
# copying in the Elasticsearch distribution
################################################################################
FROM scratch

# Setup the initial filesystem.
COPY --from=rootfs /rootfs /

RUN addgroup -g 1000 elasticsearch && \\
adduser -D -u 1000 -G elasticsearch -g elasticsearch -h /usr/share/elasticsearch elasticsearch && \\
addgroup elasticsearch root && \\
chmod 0775 /usr/share/elasticsearch && \\
chgrp 0 /usr/share/elasticsearch
<% } %>

ENV ELASTIC_CONTAINER true

WORKDIR /usr/share/elasticsearch
COPY --from=builder --chown=1000:0 /usr/share/elasticsearch /usr/share/elasticsearch
COPY --from=builder --chown=0:0 /tini /tini
<% if (docker_base == "ubi") { %>
COPY --from=builder --chown=0:0 /bin/tini /bin/tini
<% } %>

# Replace OpenJDK's built-in CA certificate keystore with the one from the OS
# vendor. The latter is superior in several ways.
Expand All @@ -94,14 +259,15 @@ ENV PATH /usr/share/elasticsearch/bin:\$PATH

COPY bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

# The JDK's directories' permissions don't allow `java` to be executed under a different
# group to the default. Fix this.
# 1. The JDK's directories' permissions don't allow `java` to be executed under a different
# group to the default. Fix this.
# 2. Sync the user and group permissions of /etc/passwd
# 3. Set correct permissions of the entrypoint
# 4. Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks.
RUN find /usr/share/elasticsearch/jdk -type d -exec chmod 0755 '{}' \\; && \\
chmod g=u /etc/passwd && \\
chmod 0775 /usr/local/bin/docker-entrypoint.sh

# Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks.
RUN find / -xdev -perm -4000 -exec chmod ug-s {} +
chmod 0775 /usr/local/bin/docker-entrypoint.sh && \\
find / -xdev -perm -4000 -exec chmod ug-s {} +

EXPOSE 9200 9300

Expand Down Expand Up @@ -138,7 +304,10 @@ RUN mkdir /licenses && \\
<% } %>
USER elasticsearch:root

ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
# Our actual entrypoint is `tini`, a minimal but functional init program. It
# calls the entrypoint we provide, while correctly forwarding signals.
ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]

# Dummy overridable parameter parsed by entrypoint
CMD ["eswrapper"]

Expand Down
24 changes: 22 additions & 2 deletions docs/reference/setup/install/docker.asciidoc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[[docker]]
=== Install {es} with Docker

{es} is also available as Docker images.
The images use https://hub.docker.com/_/centos/[centos:8] as the base image.
{es} is also available as Docker images. Starting with version 8.0.0, these
are based upon a tiny core of essential files. Prior versions used
https://hub.docker.com/_/centos/[centos:8] as the base image.

A list of all published Docker images and tags is available at
https://www.docker.elastic.co[www.docker.elastic.co]. The source files
Expand Down Expand Up @@ -424,4 +425,23 @@ You must explicitly accept them either by:
See {plugins}/_other_command_line_parameters.html[Plugin management]
for more information.

The {es} Docker image only includes what is required to run {es}, and does
not provide a package manager. It is possible to add additional utilities
with a multi-phase Docker build. You must also copy any dependencies, for
example shared libraries.

[source,sh,subs="attributes"]
--------------------------------------------
FROM centos:8 AS builder
yum install -y some-package

FROM docker.elastic.co/elasticsearch/elasticsearch:{version}
COPY --from=builder /usr/bin/some-utility /usr/bin/
COPY --from=builder /usr/lib/some-lib.so /usr/lib/
--------------------------------------------

You should use `centos:8` as a base in order to avoid incompatibilities.
Use http://man7.org/linux/man-pages/man1/ldd.1.html[`ldd`] to list the
shared libraries required by a utility.

include::next-steps.asciidoc[]
Loading