Skip to content

Commit 4dee614

Browse files
authored
Add chainguard docker image (#112103)
1 parent 7cd6de7 commit 4dee614

File tree

11 files changed

+118
-42
lines changed

11 files changed

+118
-42
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,40 @@
1212
* This class models the different Docker base images that are used to build Docker distributions of Elasticsearch.
1313
*/
1414
public enum DockerBase {
15-
DEFAULT("ubuntu:20.04", ""),
15+
DEFAULT("ubuntu:20.04", "", "apt-get"),
1616

1717
// "latest" here is intentional, since the image name specifies "8"
18-
UBI("docker.elastic.co/ubi8/ubi-minimal:latest", "-ubi8"),
18+
UBI("docker.elastic.co/ubi8/ubi-minimal:latest", "-ubi8", "microdnf"),
1919

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

2323
// Base image with extras for Cloud
24-
CLOUD("ubuntu:20.04", "-cloud"),
24+
CLOUD("ubuntu:20.04", "-cloud", "apt-get"),
2525

2626
// Based on CLOUD above, with more extras. We don't set a base image because
2727
// we programmatically extend from the Cloud image.
28-
CLOUD_ESS(null, "-cloud-ess");
28+
CLOUD_ESS(null, "-cloud-ess", "apt-get"),
29+
30+
// Chainguard based wolfi image with latest jdk
31+
WOLFI(
32+
"docker.elastic.co/wolfi/chainguard-base:latest@sha256:c16d3ad6cebf387e8dd2ad769f54320c4819fbbaa21e729fad087c7ae223b4d0",
33+
"wolfi",
34+
"apk"
35+
);
2936

3037
private final String image;
3138
private final String suffix;
39+
private final String packageManager;
3240

3341
DockerBase(String image, String suffix) {
42+
this(image, suffix, "apt-get");
43+
}
44+
45+
DockerBase(String image, String suffix, String packageManager) {
3446
this.image = image;
3547
this.suffix = suffix;
48+
this.packageManager = packageManager;
3649
}
3750

3851
public String getImage() {
@@ -42,4 +55,8 @@ public String getImage() {
4255
public String getSuffix() {
4356
return suffix;
4457
}
58+
59+
public String getPackageManager() {
60+
return packageManager;
61+
}
4562
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ private static String distributionProjectName(ElasticsearchDistribution distribu
177177
if (distribution.getType() == InternalElasticsearchDistributionTypes.DOCKER_CLOUD_ESS) {
178178
return projectName + "cloud-ess-docker" + archString + "-export";
179179
}
180+
if (distribution.getType() == InternalElasticsearchDistributionTypes.DOCKER_WOLFI) {
181+
return projectName + "wolfi-docker" + archString + "-export";
182+
}
180183
return projectName + distribution.getType().getName();
181184
}
182185

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.gradle.internal.distribution;
10+
11+
import org.elasticsearch.gradle.ElasticsearchDistributionType;
12+
13+
public class DockerWolfiElasticsearchDistributionType implements ElasticsearchDistributionType {
14+
15+
DockerWolfiElasticsearchDistributionType() {}
16+
17+
@Override
18+
public String getName() {
19+
return "dockerWolfi";
20+
}
21+
22+
@Override
23+
public boolean isDocker() {
24+
return true;
25+
}
26+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class InternalElasticsearchDistributionTypes {
2020
public static ElasticsearchDistributionType DOCKER_IRONBANK = new DockerIronBankElasticsearchDistributionType();
2121
public static ElasticsearchDistributionType DOCKER_CLOUD = new DockerCloudElasticsearchDistributionType();
2222
public static ElasticsearchDistributionType DOCKER_CLOUD_ESS = new DockerCloudEssElasticsearchDistributionType();
23+
public static ElasticsearchDistributionType DOCKER_WOLFI = new DockerWolfiElasticsearchDistributionType();
2324

2425
public static List<ElasticsearchDistributionType> ALL_INTERNAL = List.of(
2526
DEB,
@@ -28,6 +29,7 @@ public class InternalElasticsearchDistributionTypes {
2829
DOCKER_UBI,
2930
DOCKER_IRONBANK,
3031
DOCKER_CLOUD,
31-
DOCKER_CLOUD_ESS
32+
DOCKER_CLOUD_ESS,
33+
DOCKER_WOLFI
3234
);
3335
}

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_CLOUD_ESS;
5353
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_IRONBANK;
5454
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_UBI;
55+
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_WOLFI;
5556
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.RPM;
5657

5758
/**
@@ -93,6 +94,7 @@ public void apply(Project project) {
9394

9495
for (ElasticsearchDistribution distribution : testDistributions) {
9596
String taskname = destructiveDistroTestTaskName(distribution);
97+
ElasticsearchDistributionType type = distribution.getType();
9698
TaskProvider<Test> destructiveTask = configureTestTask(project, taskname, distribution, t -> {
9799
t.onlyIf(
98100
"Docker is not available",
@@ -106,12 +108,14 @@ public void apply(Project project) {
106108
if (distribution.getPlatform() == Platform.WINDOWS) {
107109
windowsTestTasks.add(destructiveTask);
108110
} else {
109-
linuxTestTasks.computeIfAbsent(distribution.getType(), k -> new ArrayList<>()).add(destructiveTask);
111+
linuxTestTasks.computeIfAbsent(type, k -> new ArrayList<>()).add(destructiveTask);
110112
}
111113
destructiveDistroTest.configure(t -> t.dependsOn(destructiveTask));
112-
lifecycleTasks.get(distribution.getType()).configure(t -> t.dependsOn(destructiveTask));
114+
TaskProvider<?> lifecycleTask = lifecycleTasks.get(type);
115+
System.out.println("lifecycleTask.getName() = " + lifecycleTask.getName());
116+
lifecycleTask.configure(t -> t.dependsOn(destructiveTask));
113117

114-
if ((distribution.getType() == DEB || distribution.getType() == RPM) && distribution.getBundledJdk()) {
118+
if ((type == DEB || type == RPM) && distribution.getBundledJdk()) {
115119
for (Version version : BuildParams.getBwcVersions().getIndexCompatible()) {
116120
final ElasticsearchDistribution bwcDistro;
117121
if (version.equals(Version.fromString(distribution.getVersion()))) {
@@ -121,7 +125,7 @@ public void apply(Project project) {
121125
bwcDistro = createDistro(
122126
allDistributions,
123127
distribution.getArchitecture(),
124-
distribution.getType(),
128+
type,
125129
distribution.getPlatform(),
126130
distribution.getBundledJdk(),
127131
version.toString()
@@ -147,6 +151,7 @@ private static Map<ElasticsearchDistributionType, TaskProvider<?>> lifecycleTask
147151
lifecyleTasks.put(DOCKER_IRONBANK, project.getTasks().register(taskPrefix + ".docker-ironbank"));
148152
lifecyleTasks.put(DOCKER_CLOUD, project.getTasks().register(taskPrefix + ".docker-cloud"));
149153
lifecyleTasks.put(DOCKER_CLOUD_ESS, project.getTasks().register(taskPrefix + ".docker-cloud-ess"));
154+
lifecyleTasks.put(DOCKER_WOLFI, project.getTasks().register(taskPrefix + ".docker-wolfi"));
150155
lifecyleTasks.put(ARCHIVE, project.getTasks().register(taskPrefix + ".archives"));
151156
lifecyleTasks.put(DEB, project.getTasks().register(taskPrefix + ".packages"));
152157
lifecyleTasks.put(RPM, lifecyleTasks.get(DEB));

distribution/docker/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ the [DockerBase] enum.
66
* Default - this is what most people use, and is based on Ubuntu
77
* UBI - the same as the default image, but based upon [RedHat's UBI
88
images][ubi], specifically their minimal flavour.
9+
* Wolfi - the same as the default image, but based upon [Wolfi](https://github.com/wolfi-dev)
910
* Iron Bank - this is the US Department of Defence's repository of digitally
1011
signed, binary container images including both Free and Open-Source
1112
software (FOSS) and Commercial off-the-shelf (COTS). In practice, this is
1213
another UBI build, this time on the regular UBI image, with extra
1314
hardening. See below for more details.
15+
1416
* Cloud - this is mostly the same as the default image, with some notable differences:
1517
* `filebeat` and `metricbeat` are included
1618
* `wget` is included

distribution/docker/build.gradle

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ apply plugin: 'elasticsearch.dra-artifacts'
2121
String buildId = providers.systemProperty('build.id').getOrNull()
2222
boolean useLocalArtifacts = buildId != null && buildId.isBlank() == false && useDra == false
2323

24-
25-
2624
repositories {
2725
// Define a repository that allows Gradle to fetch a resource from GitHub. This
2826
// is only used to fetch the `tini` binary, when building the Iron Bank docker image
@@ -131,7 +129,7 @@ ext.expansions = { Architecture architecture, DockerBase base ->
131129
'config_dir' : base == DockerBase.IRON_BANK ? 'scripts' : 'config',
132130
'git_revision' : BuildParams.gitRevision,
133131
'license' : base == DockerBase.IRON_BANK ? 'Elastic License 2.0' : 'Elastic-License-2.0',
134-
'package_manager' : base == DockerBase.IRON_BANK ? 'yum' : (base == DockerBase.UBI ? 'microdnf' : 'apt-get'),
132+
'package_manager' : base.packageManager,
135133
'docker_base' : base.name().toLowerCase(),
136134
'version' : VersionProperties.elasticsearch,
137135
'major_minor_version': "${major}.${minor}",
@@ -182,21 +180,12 @@ ext.dockerBuildContext = { Architecture architecture, DockerBase base ->
182180
from projectDir.resolve("src/docker/config")
183181
}
184182
}
185-
186183
from(projectDir.resolve("src/docker/Dockerfile")) {
187184
expand(varExpansions)
188185
filter SquashNewlinesFilter
189186
}
190187
}
191188
}
192-
//
193-
//def createAndSetWritable(Object... locations) {
194-
// locations.each { location ->
195-
// File file = file(location)
196-
// file.mkdirs()
197-
// file.setWritable(true, false)
198-
// }
199-
//}
200189

201190
tasks.register("copyNodeKeyMaterial", Sync) {
202191
def certsDir = file("build/certs")

distribution/docker/src/docker/Dockerfile

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,34 @@ RUN chmod 0555 /bin/tini
4343
# Install required packages to extract the Elasticsearch distribution
4444
<% if (docker_base == 'default' || docker_base == 'cloud') { %>
4545
RUN <%= retry.loop(package_manager, "${package_manager} update && DEBIAN_FRONTEND=noninteractive ${package_manager} install -y curl ") %>
46+
<% } else if (docker_base == "wolfi") { %>
47+
RUN <%= retry.loop(package_manager, "export DEBIAN_FRONTEND=noninteractive && ${package_manager} update && ${package_manager} update && ${package_manager} add --no-cache curl") %>
4648
<% } else { %>
4749
RUN <%= retry.loop(package_manager, "${package_manager} install -y findutils tar gzip") %>
4850
<% } %>
4951
50-
# `tini` is a tiny but valid init for containers. This is used to cleanly
51-
# control how ES and any child processes are shut down.
52-
#
53-
# The tini GitHub page gives instructions for verifying the binary using
54-
# gpg, but the keyservers are slow to return the key and this can fail the
55-
# build. Instead, we check the binary against the published checksum.
56-
RUN set -eux ; \\
57-
tini_bin="" ; \\
58-
case "\$(arch)" in \\
59-
aarch64) tini_bin='tini-arm64' ;; \\
60-
x86_64) tini_bin='tini-amd64' ;; \\
61-
*) echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ;; \\
62-
esac ; \\
63-
curl --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\
64-
curl --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\
65-
sha256sum -c \${tini_bin}.sha256sum ; \\
66-
rm \${tini_bin}.sha256sum ; \\
67-
mv \${tini_bin} /bin/tini ; \\
68-
chmod 0555 /bin/tini
52+
<% if (docker_base != 'wolfi') { %>
53+
# `tini` is a tiny but valid init for containers. This is used to cleanly
54+
# control how ES and any child processes are shut down.
55+
# For wolfi we pick it from the blessed wolfi package registry.
56+
#
57+
# The tini GitHub page gives instructions for verifying the binary using
58+
# gpg, but the keyservers are slow to return the key and this can fail the
59+
# build. Instead, we check the binary against the published checksum.
60+
RUN set -eux ; \\
61+
tini_bin="" ; \\
62+
case "\$(arch)" in \\
63+
aarch64) tini_bin='tini-arm64' ;; \\
64+
x86_64) tini_bin='tini-amd64' ;; \\
65+
*) echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ;; \\
66+
esac ; \\
67+
curl --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\
68+
curl --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\
69+
sha256sum -c \${tini_bin}.sha256sum ; \\
70+
rm \${tini_bin}.sha256sum ; \\
71+
mv \${tini_bin} /bin/tini ; \\
72+
chmod 0555 /bin/tini
73+
<% } %>
6974
7075
<% } %>
7176
@@ -152,6 +157,15 @@ RUN ${package_manager} update --setopt=tsflags=nodocs -y && \\
152157
nc shadow-utils zip findutils unzip procps-ng && \\
153158
${package_manager} clean all
154159
160+
<% } else if (docker_base == "wolfi") { %>
161+
RUN <%= retry.loop(package_manager,
162+
"export DEBIAN_FRONTEND=noninteractive && \n" +
163+
" ${package_manager} update && \n" +
164+
" ${package_manager} upgrade && \n" +
165+
" ${package_manager} add --no-cache \n" +
166+
" bash ca-certificates curl libsystemd netcat-openbsd p11-kit p11-kit-trust shadow tini unzip zip zstd && \n" +
167+
" rm -rf /var/cache/apk/* "
168+
) %>
155169
<% } else if (docker_base == "default" || docker_base == "cloud") { %>
156170
157171
# Change default shell to bash, then install required packages with retries.
@@ -185,6 +199,11 @@ RUN groupadd -g 1000 elasticsearch && \\
185199
adduser --uid 1000 --gid 1000 --home /usr/share/elasticsearch elasticsearch && \\
186200
adduser elasticsearch root && \\
187201
chown -R 0:0 /usr/share/elasticsearch
202+
<% } else if (docker_base == "wolfi") { %>
203+
RUN groupadd -g 1000 elasticsearch && \
204+
adduser -G elasticsearch -u 1000 elasticsearch -D --home /usr/share/elasticsearch elasticsearch && \
205+
adduser elasticsearch root && \
206+
chown -R 0:0 /usr/share/elasticsearch
188207
<% } else { %>
189208
RUN groupadd -g 1000 elasticsearch && \\
190209
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
@@ -196,7 +215,9 @@ ENV ELASTIC_CONTAINER true
196215
WORKDIR /usr/share/elasticsearch
197216
198217
COPY --from=builder --chown=0:0 /usr/share/elasticsearch /usr/share/elasticsearch
218+
<% if (docker_base != "wolfi") { %>
199219
COPY --from=builder --chown=0:0 /bin/tini /bin/tini
220+
<% } %>
200221
201222
<% if (docker_base == 'cloud') { %>
202223
COPY --from=builder --chown=0:0 /opt /opt
@@ -280,7 +301,12 @@ CMD ["/app/elasticsearch.sh"]
280301
RUN mkdir /app && \\
281302
echo -e '#!/bin/bash\\nexec /usr/local/bin/docker-entrypoint.sh eswrapper' > /app/elasticsearch.sh && \\
282303
chmod 0555 /app/elasticsearch.sh
283-
304+
<% } else if (docker_base == "wolfi") { %>
305+
# Our actual entrypoint is `tini`, a minimal but functional init program. It
306+
# calls the entrypoint we provide, while correctly forwarding signals.
307+
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
308+
# Dummy overridable parameter parsed by entrypoint
309+
CMD ["eswrapper"]
284310
<% } else { %>
285311
# Our actual entrypoint is `tini`, a minimal but functional init program. It
286312
# calls the entrypoint we provide, while correctly forwarding signals.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// This file is intentionally blank. All configuration of the
2+
// export is done in the parent project.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// This file is intentionally blank. All configuration of the
2+
// export is done in the parent project.

0 commit comments

Comments
 (0)