Skip to content

Commit 6e0473f

Browse files
authored
Address review feedback on es default docker image (#126330) (#126616)
This addresses feedback we got for our default image at docker-library/official-images#18692 This also introduces separate docker source files to make maintaining those easier. We cannot take over all suggested changes as we require certain settings to have our packaging tests pass as expected.
1 parent dba5165 commit 6e0473f

File tree

6 files changed

+201
-13
lines changed

6 files changed

+201
-13
lines changed

.ci/scripts/packaging-test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# opensuse 15 has a missing dep for systemd
44

55
if which zypper > /dev/null ; then
6-
sudo zypper install -y insserv-compat
6+
sudo zypper install -y insserv-compat docker-buildx
77
fi
88

99
if [ -e /etc/sysctl.d/99-gce.conf ]; then

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,45 @@
1414
*/
1515
public enum DockerBase {
1616
// "latest" here is intentional, since the image name specifies "9"
17-
DEFAULT("redhat/ubi9-minimal:latest", "", "microdnf"),
17+
DEFAULT("redhat/ubi9-minimal:latest", "", "microdnf", "Dockerfile.default"),
1818

1919
// The Iron Bank base image is UBI (albeit hardened), but we are required to parameterize the Docker build
20-
IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}", "-ironbank", "yum"),
20+
IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}", "-ironbank", "yum", "Dockerfile"),
2121

2222
// Chainguard based wolfi image with latest jdk
2323
// This is usually updated via renovatebot
2424
// spotless:off
25-
WOLFI("docker.elastic.co/wolfi/chainguard-base:latest@sha256:29150cd940cc7f69407d978d5a19c86f4d9e67cf44e4d6ded787a497e8f27c9a",
25+
WOLFI(
26+
"docker.elastic.co/wolfi/chainguard-base:latest@sha256:29150cd940cc7f69407d978d5a19c86f4d9e67cf44e4d6ded787a497e8f27c9a",
2627
"-wolfi",
27-
"apk"
28+
"apk",
29+
"Dockerfile"
30+
),
31+
FIPS(
32+
"docker.elastic.co/wolfi/chainguard-base-fips:sha256-ebfc3f1d7dba992231747a2e05ad1b859843e81b5e676ad342859d7cf9e425a7@sha256:ebfc3f1d7dba992231747a2e05ad1b859843e81b5e676ad342859d7cf9e425a7",
33+
"-fips",
34+
"apk",
35+
"Dockerfile"
2836
),
2937
// spotless:on
3038
// Based on WOLFI above, with more extras. We don't set a base image because
3139
// we programmatically extend from the wolfi image.
32-
CLOUD_ESS(null, "-cloud-ess", "apk");
40+
CLOUD_ESS(null, "-cloud-ess", "apk", "Dockerfile.cloud-ess"),;
3341

3442
private final String image;
3543
private final String suffix;
3644
private final String packageManager;
45+
private final String dockerfile;
3746

3847
DockerBase(String image, String suffix) {
39-
this(image, suffix, "apt-get");
48+
this(image, suffix, "apt-get", "dockerfile");
4049
}
4150

42-
DockerBase(String image, String suffix, String packageManager) {
51+
DockerBase(String image, String suffix, String packageManager, String dockerfile) {
4352
this.image = image;
4453
this.suffix = suffix;
4554
this.packageManager = packageManager;
55+
this.dockerfile = dockerfile;
4656
}
4757

4858
public String getImage() {
@@ -56,4 +66,8 @@ public String getSuffix() {
5666
public String getPackageManager() {
5767
return packageManager;
5868
}
69+
70+
public String getDockerfile() {
71+
return dockerfile;
72+
}
5973
}

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/docker/DockerBuildTask.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ private void pullBaseImage(String baseImage) {
170170
maybeConfigureDockerConfig(spec);
171171
spec.executable("docker");
172172
spec.args("pull");
173+
spec.environment("DOCKER_BUILDKIT", "1");
173174
spec.args(baseImage);
174175
});
175176

@@ -205,7 +206,7 @@ public void execute() {
205206
maybeConfigureDockerConfig(spec);
206207

207208
spec.executable("docker");
208-
209+
spec.environment("DOCKER_BUILDKIT", "1");
209210
if (isCrossPlatform) {
210211
spec.args("buildx");
211212
}

distribution/docker/build.gradle

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,10 @@ ext.dockerBuildContext = { Architecture architecture, DockerBase base ->
180180
from projectDir.resolve("src/docker/config")
181181
}
182182
}
183-
from(projectDir.resolve("src/docker/Dockerfile")) {
183+
from(projectDir.resolve("src/docker/${base.dockerfile}")) {
184184
expand(varExpansions)
185185
filter SquashNewlinesFilter
186+
rename base.dockerfile, "Dockerfile"
186187
}
187188
}
188189
}
@@ -311,8 +312,7 @@ void addTransformDockerContextTask(Architecture architecture, DockerBase base) {
311312
String distributionFolderName = "elasticsearch-${VersionProperties.elasticsearch}"
312313

313314
from(tarTree("${project.buildDir}/distributions/${archiveName}.tar.gz")) {
314-
315-
if (base != DockerBase.IRON_BANK) {
315+
if (base != DockerBase.IRON_BANK && base != DockerBase.DEFAULT) {
316316
// iron bank always needs a COPY with the tarball file path
317317
eachFile { FileCopyDetails details ->
318318
if (details.name.equals("Dockerfile")) {
@@ -323,6 +323,17 @@ void addTransformDockerContextTask(Architecture architecture, DockerBase base) {
323323
}
324324
}
325325
}
326+
if (base == DockerBase.DEFAULT) {
327+
// iron bank always needs a COPY with the tarball file path
328+
eachFile { FileCopyDetails details ->
329+
if (details.name.equals("Dockerfile")) {
330+
filter { String contents ->
331+
return contents.replaceAll('^RUN *.*artifacts-no-kpi.*$', "COPY $distributionFolderName .")
332+
.replaceAll('^RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 &&', "RUN ")
333+
}
334+
}
335+
}
336+
}
326337
}
327338
into "${project.buildDir}/docker-context/${archiveName}"
328339

distribution/docker/src/docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ LABEL name="Elasticsearch" \\
242242
description="You know, for search."
243243
<% } %>
244244

245-
RUN mkdir /licenses && cp LICENSE.txt /licenses/LICENSE
245+
RUN mkdir /licenses && ln LICENSE.txt /licenses/LICENSE
246246
<% if (docker_base == 'iron_bank') { %>
247247
COPY LICENSE /licenses/LICENSE.addendum
248248
<% } %>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<% /*
2+
This file is passed through Groovy's SimpleTemplateEngine, so dollars and backslashes
3+
have to be escaped in order for them to appear in the final Dockerfile. You
4+
can also comment out blocks, like this one. See:
5+
6+
https://docs.groovy-lang.org/latest/html/api/groovy/text/SimpleTemplateEngine.html
7+
8+
We use control-flow tags in this file to conditionally render the content. The
9+
layout/presentation here has been adjusted so that it looks reasonable when rendered,
10+
at the slight expense of how it looks here.
11+
12+
Note that this file is also filtered to squash together newlines, so we can
13+
add as many newlines here as necessary to improve legibility.
14+
*/ %>
15+
16+
17+
################################################################################
18+
# Build stage 1 `builder`:
19+
# Extract Elasticsearch artifact
20+
################################################################################
21+
22+
FROM ${base_image} AS builder
23+
24+
RUN microdnf install -y findutils tar gzip
25+
26+
# `tini` is a tiny but valid init for containers. This is used to cleanly
27+
# control how ES and any child processes are shut down.
28+
#
29+
# The tini GitHub page gives instructions for verifying the binary using
30+
# gpg, but the keyservers are slow to return the key and this can fail the
31+
# build. Instead, we check the binary against the published checksum.
32+
RUN set -eux ; \\
33+
tini_bin="" ; \\
34+
arch="\$(rpm --query --queryformat='%{ARCH}' rpm)"; \
35+
case "\$(arch)" in \\
36+
aarch64) tini_bin='tini-arm64' ;; \\
37+
x86_64) tini_bin='tini-amd64' ;; \\
38+
*) echo >&2 ; echo >&2 "Unsupported architecture \$arch" ; echo >&2 ; exit 1 ;; \\
39+
esac ; \\
40+
curl -f --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\
41+
curl -f --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\
42+
sha256sum -c \${tini_bin}.sha256sum ; \\
43+
rm \${tini_bin}.sha256sum ; \\
44+
mv \${tini_bin} /bin/tini ; \\
45+
chmod 0555 /bin/tini
46+
47+
WORKDIR /usr/share/elasticsearch
48+
RUN arch="\$(rpm --query --queryformat='%{ARCH}' rpm)" && curl -f --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\$(arch).tar.gz
49+
RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 && \\
50+
# Configure the distribution for Docker
51+
sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' bin/elasticsearch-env && \\
52+
# Create required directory
53+
mkdir data && \\
54+
# Reset permissions on all directories
55+
find . -type d -exec chmod 0555 {} + && \\
56+
# keep default elasticsearch log4j config
57+
mv config/log4j2.properties config/log4j2.file.properties && \\
58+
# Reset permissions on all files
59+
find . -type f -exec chmod 0444 {} + && \\
60+
# Make CLI tools executable
61+
chmod 0555 bin/* jdk/bin/* jdk/lib/jspawnhelper modules/x-pack-ml/platform/linux-*/bin/* && \\
62+
# Make some directories writable. `bin` must be writable because
63+
# plugins can install their own CLI utilities.
64+
chmod 0775 bin config config/jvm.options.d data logs plugins && \\
65+
# Make some files writable
66+
find config -type f -exec chmod 0664 {} + && \\
67+
# Tighten up permissions on the ES home dir (the permissions of the contents are handled below)
68+
chmod 0775 . && \\
69+
# You can't install plugins that include configuration when running as `elasticsearch` and the `config`
70+
# dir is owned by `root`, because the installed tries to manipulate the permissions on the plugin's
71+
# config directory.
72+
chown 1000:1000 bin config config/jvm.options.d data logs plugins
73+
74+
# The distribution includes a `config` directory, no need to create it
75+
COPY --chmod=664 config/elasticsearch.yml config/log4j2.properties config/
76+
77+
78+
################################################################################
79+
# Build stage 2 (the actual Elasticsearch image):
80+
#
81+
# Copy elasticsearch from stage 1
82+
# Add entrypoint
83+
################################################################################
84+
85+
FROM ${base_image}
86+
87+
RUN microdnf install --setopt=tsflags=nodocs -y \\
88+
nc shadow-utils zip unzip findutils procps-ng && \\
89+
microdnf clean all
90+
91+
RUN groupadd -g 1000 elasticsearch && \\
92+
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
93+
chown -R 0:0 /usr/share/elasticsearch
94+
95+
ENV ELASTIC_CONTAINER=true
96+
97+
COPY --from=builder /bin/tini /bin/tini
98+
99+
WORKDIR /usr/share/elasticsearch
100+
101+
COPY --from=builder --chown=0:0 /usr/share/elasticsearch .
102+
103+
# Replace OpenJDK's built-in CA certificate keystore with the one from the OS
104+
# vendor. The latter is superior in several ways.
105+
# REF: https://github.com/elastic/elasticsearch-docker/issues/171
106+
RUN ln -sf /etc/pki/ca-trust/extracted/java/cacerts jdk/lib/security/cacerts
107+
108+
ENV PATH=/usr/share/elasticsearch/bin:\$PATH
109+
ENV SHELL=/bin/bash
110+
111+
COPY --chmod=0555 bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
112+
113+
RUN chmod g=u /etc/passwd && \\
114+
chmod 0555 /usr/local/bin/docker-entrypoint.sh && \\
115+
find / -xdev -perm -4000 -exec chmod ug-s {} + && \\
116+
chmod 0775 /usr/share/elasticsearch && \\
117+
chown elasticsearch bin config config/jvm.options.d data logs plugins
118+
119+
120+
EXPOSE 9200 9300
121+
122+
LABEL org.label-schema.build-date="${build_date}" \\
123+
org.label-schema.license="${license}" \\
124+
org.label-schema.name="Elasticsearch" \\
125+
org.label-schema.schema-version="1.0" \\
126+
org.label-schema.url="https://www.elastic.co/products/elasticsearch" \\
127+
org.label-schema.usage="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\
128+
org.label-schema.vcs-ref="${git_revision}" \\
129+
org.label-schema.vcs-url="https://github.com/elastic/elasticsearch" \\
130+
org.label-schema.vendor="Elastic" \\
131+
org.label-schema.version="${version}" \\
132+
org.opencontainers.image.created="${build_date}" \\
133+
org.opencontainers.image.documentation="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\
134+
org.opencontainers.image.licenses="${license}" \\
135+
org.opencontainers.image.revision="${git_revision}" \\
136+
org.opencontainers.image.source="https://github.com/elastic/elasticsearch" \\
137+
org.opencontainers.image.title="Elasticsearch" \\
138+
org.opencontainers.image.url="https://www.elastic.co/products/elasticsearch" \\
139+
org.opencontainers.image.vendor="Elastic" \\
140+
org.opencontainers.image.version="${version}"
141+
142+
LABEL name="Elasticsearch" \\
143+
maintainer="[email protected]" \\
144+
vendor="Elastic" \\
145+
version="${version}" \\
146+
release="1" \\
147+
summary="Elasticsearch" \\
148+
description="You know, for search."
149+
150+
RUN mkdir /licenses && ln LICENSE.txt /licenses/LICENSE
151+
152+
# Our actual entrypoint is `tini`, a minimal but functional init program. It
153+
# calls the entrypoint we provide, while correctly forwarding signals.
154+
ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
155+
# Dummy overridable parameter parsed by entrypoint
156+
CMD ["eswrapper"]
157+
158+
USER 1000:0
159+
160+
################################################################################
161+
# End of multi-stage Dockerfile
162+
################################################################################

0 commit comments

Comments
 (0)