Add chainguard docker image (#112103)

This commit is contained in:
Rene Groeschke 2024-09-06 19:32:42 +02:00 committed by GitHub
parent 7cd6de76f4
commit 4dee614707
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 118 additions and 42 deletions

View file

@ -12,27 +12,40 @@ package org.elasticsearch.gradle.internal;
* This class models the different Docker base images that are used to build Docker distributions of Elasticsearch.
*/
public enum DockerBase {
DEFAULT("ubuntu:20.04", ""),
DEFAULT("ubuntu:20.04", "", "apt-get"),
// "latest" here is intentional, since the image name specifies "8"
UBI("docker.elastic.co/ubi8/ubi-minimal:latest", "-ubi8"),
UBI("docker.elastic.co/ubi8/ubi-minimal:latest", "-ubi8", "microdnf"),
// The Iron Bank base image is UBI (albeit hardened), but we are required to parameterize the Docker build
IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}", "-ironbank"),
IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}", "-ironbank", "yum"),
// Base image with extras for Cloud
CLOUD("ubuntu:20.04", "-cloud"),
CLOUD("ubuntu:20.04", "-cloud", "apt-get"),
// Based on CLOUD above, with more extras. We don't set a base image because
// we programmatically extend from the Cloud image.
CLOUD_ESS(null, "-cloud-ess");
CLOUD_ESS(null, "-cloud-ess", "apt-get"),
// Chainguard based wolfi image with latest jdk
WOLFI(
"docker.elastic.co/wolfi/chainguard-base:latest@sha256:c16d3ad6cebf387e8dd2ad769f54320c4819fbbaa21e729fad087c7ae223b4d0",
"wolfi",
"apk"
);
private final String image;
private final String suffix;
private final String packageManager;
DockerBase(String image, String suffix) {
this(image, suffix, "apt-get");
}
DockerBase(String image, String suffix, String packageManager) {
this.image = image;
this.suffix = suffix;
this.packageManager = packageManager;
}
public String getImage() {
@ -42,4 +55,8 @@ public enum DockerBase {
public String getSuffix() {
return suffix;
}
public String getPackageManager() {
return packageManager;
}
}

View file

@ -177,6 +177,9 @@ public class InternalDistributionDownloadPlugin implements Plugin<Project> {
if (distribution.getType() == InternalElasticsearchDistributionTypes.DOCKER_CLOUD_ESS) {
return projectName + "cloud-ess-docker" + archString + "-export";
}
if (distribution.getType() == InternalElasticsearchDistributionTypes.DOCKER_WOLFI) {
return projectName + "wolfi-docker" + archString + "-export";
}
return projectName + distribution.getType().getName();
}

View file

@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.gradle.internal.distribution;
import org.elasticsearch.gradle.ElasticsearchDistributionType;
public class DockerWolfiElasticsearchDistributionType implements ElasticsearchDistributionType {
DockerWolfiElasticsearchDistributionType() {}
@Override
public String getName() {
return "dockerWolfi";
}
@Override
public boolean isDocker() {
return true;
}
}

View file

@ -20,6 +20,7 @@ public class InternalElasticsearchDistributionTypes {
public static ElasticsearchDistributionType DOCKER_IRONBANK = new DockerIronBankElasticsearchDistributionType();
public static ElasticsearchDistributionType DOCKER_CLOUD = new DockerCloudElasticsearchDistributionType();
public static ElasticsearchDistributionType DOCKER_CLOUD_ESS = new DockerCloudEssElasticsearchDistributionType();
public static ElasticsearchDistributionType DOCKER_WOLFI = new DockerWolfiElasticsearchDistributionType();
public static List<ElasticsearchDistributionType> ALL_INTERNAL = List.of(
DEB,
@ -28,6 +29,7 @@ public class InternalElasticsearchDistributionTypes {
DOCKER_UBI,
DOCKER_IRONBANK,
DOCKER_CLOUD,
DOCKER_CLOUD_ESS
DOCKER_CLOUD_ESS,
DOCKER_WOLFI
);
}

View file

@ -52,6 +52,7 @@ import static org.elasticsearch.gradle.internal.distribution.InternalElasticsear
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_CLOUD_ESS;
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_IRONBANK;
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_UBI;
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.DOCKER_WOLFI;
import static org.elasticsearch.gradle.internal.distribution.InternalElasticsearchDistributionTypes.RPM;
/**
@ -93,6 +94,7 @@ public class DistroTestPlugin implements Plugin<Project> {
for (ElasticsearchDistribution distribution : testDistributions) {
String taskname = destructiveDistroTestTaskName(distribution);
ElasticsearchDistributionType type = distribution.getType();
TaskProvider<Test> destructiveTask = configureTestTask(project, taskname, distribution, t -> {
t.onlyIf(
"Docker is not available",
@ -106,12 +108,14 @@ public class DistroTestPlugin implements Plugin<Project> {
if (distribution.getPlatform() == Platform.WINDOWS) {
windowsTestTasks.add(destructiveTask);
} else {
linuxTestTasks.computeIfAbsent(distribution.getType(), k -> new ArrayList<>()).add(destructiveTask);
linuxTestTasks.computeIfAbsent(type, k -> new ArrayList<>()).add(destructiveTask);
}
destructiveDistroTest.configure(t -> t.dependsOn(destructiveTask));
lifecycleTasks.get(distribution.getType()).configure(t -> t.dependsOn(destructiveTask));
TaskProvider<?> lifecycleTask = lifecycleTasks.get(type);
System.out.println("lifecycleTask.getName() = " + lifecycleTask.getName());
lifecycleTask.configure(t -> t.dependsOn(destructiveTask));
if ((distribution.getType() == DEB || distribution.getType() == RPM) && distribution.getBundledJdk()) {
if ((type == DEB || type == RPM) && distribution.getBundledJdk()) {
for (Version version : BuildParams.getBwcVersions().getIndexCompatible()) {
final ElasticsearchDistribution bwcDistro;
if (version.equals(Version.fromString(distribution.getVersion()))) {
@ -121,7 +125,7 @@ public class DistroTestPlugin implements Plugin<Project> {
bwcDistro = createDistro(
allDistributions,
distribution.getArchitecture(),
distribution.getType(),
type,
distribution.getPlatform(),
distribution.getBundledJdk(),
version.toString()
@ -147,6 +151,7 @@ public class DistroTestPlugin implements Plugin<Project> {
lifecyleTasks.put(DOCKER_IRONBANK, project.getTasks().register(taskPrefix + ".docker-ironbank"));
lifecyleTasks.put(DOCKER_CLOUD, project.getTasks().register(taskPrefix + ".docker-cloud"));
lifecyleTasks.put(DOCKER_CLOUD_ESS, project.getTasks().register(taskPrefix + ".docker-cloud-ess"));
lifecyleTasks.put(DOCKER_WOLFI, project.getTasks().register(taskPrefix + ".docker-wolfi"));
lifecyleTasks.put(ARCHIVE, project.getTasks().register(taskPrefix + ".archives"));
lifecyleTasks.put(DEB, project.getTasks().register(taskPrefix + ".packages"));
lifecyleTasks.put(RPM, lifecyleTasks.get(DEB));

View file

@ -6,11 +6,13 @@ the [DockerBase] enum.
* Default - this is what most people use, and is based on Ubuntu
* UBI - the same as the default image, but based upon [RedHat's UBI
images][ubi], specifically their minimal flavour.
* Wolfi - the same as the default image, but based upon [Wolfi](https://github.com/wolfi-dev)
* Iron Bank - this is the US Department of Defence's repository of digitally
signed, binary container images including both Free and Open-Source
software (FOSS) and Commercial off-the-shelf (COTS). In practice, this is
another UBI build, this time on the regular UBI image, with extra
hardening. See below for more details.
* Cloud - this is mostly the same as the default image, with some notable differences:
* `filebeat` and `metricbeat` are included
* `wget` is included

View file

@ -21,8 +21,6 @@ apply plugin: 'elasticsearch.dra-artifacts'
String buildId = providers.systemProperty('build.id').getOrNull()
boolean useLocalArtifacts = buildId != null && buildId.isBlank() == false && useDra == false
repositories {
// Define a repository that allows Gradle to fetch a resource from GitHub. This
// 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 ->
'config_dir' : base == DockerBase.IRON_BANK ? 'scripts' : 'config',
'git_revision' : BuildParams.gitRevision,
'license' : base == DockerBase.IRON_BANK ? 'Elastic License 2.0' : 'Elastic-License-2.0',
'package_manager' : base == DockerBase.IRON_BANK ? 'yum' : (base == DockerBase.UBI ? 'microdnf' : 'apt-get'),
'package_manager' : base.packageManager,
'docker_base' : base.name().toLowerCase(),
'version' : VersionProperties.elasticsearch,
'major_minor_version': "${major}.${minor}",
@ -182,21 +180,12 @@ ext.dockerBuildContext = { Architecture architecture, DockerBase base ->
from projectDir.resolve("src/docker/config")
}
}
from(projectDir.resolve("src/docker/Dockerfile")) {
expand(varExpansions)
filter SquashNewlinesFilter
}
}
}
//
//def createAndSetWritable(Object... locations) {
// locations.each { location ->
// File file = file(location)
// file.mkdirs()
// file.setWritable(true, false)
// }
//}
tasks.register("copyNodeKeyMaterial", Sync) {
def certsDir = file("build/certs")

View file

@ -43,17 +43,21 @@ RUN chmod 0555 /bin/tini
# Install required packages to extract the Elasticsearch distribution
<% if (docker_base == 'default' || docker_base == 'cloud') { %>
RUN <%= retry.loop(package_manager, "${package_manager} update && DEBIAN_FRONTEND=noninteractive ${package_manager} install -y curl ") %>
<% } else if (docker_base == "wolfi") { %>
RUN <%= retry.loop(package_manager, "export DEBIAN_FRONTEND=noninteractive && ${package_manager} update && ${package_manager} update && ${package_manager} add --no-cache curl") %>
<% } else { %>
RUN <%= retry.loop(package_manager, "${package_manager} install -y findutils 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.
#
# 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.
RUN set -eux ; \\
<% if (docker_base != 'wolfi') { %>
# `tini` is a tiny but valid init for containers. This is used to cleanly
# control how ES and any child processes are shut down.
# For wolfi we pick it from the blessed wolfi package registry.
#
# 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.
RUN set -eux ; \\
tini_bin="" ; \\
case "\$(arch)" in \\
aarch64) tini_bin='tini-arm64' ;; \\
@ -66,6 +70,7 @@ RUN set -eux ; \\
rm \${tini_bin}.sha256sum ; \\
mv \${tini_bin} /bin/tini ; \\
chmod 0555 /bin/tini
<% } %>
<% } %>
@ -152,6 +157,15 @@ RUN ${package_manager} update --setopt=tsflags=nodocs -y && \\
nc shadow-utils zip findutils unzip procps-ng && \\
${package_manager} clean all
<% } else if (docker_base == "wolfi") { %>
RUN <%= retry.loop(package_manager,
"export DEBIAN_FRONTEND=noninteractive && \n" +
" ${package_manager} update && \n" +
" ${package_manager} upgrade && \n" +
" ${package_manager} add --no-cache \n" +
" bash ca-certificates curl libsystemd netcat-openbsd p11-kit p11-kit-trust shadow tini unzip zip zstd && \n" +
" rm -rf /var/cache/apk/* "
) %>
<% } else if (docker_base == "default" || docker_base == "cloud") { %>
# Change default shell to bash, then install required packages with retries.
@ -185,6 +199,11 @@ RUN groupadd -g 1000 elasticsearch && \\
adduser --uid 1000 --gid 1000 --home /usr/share/elasticsearch elasticsearch && \\
adduser elasticsearch root && \\
chown -R 0:0 /usr/share/elasticsearch
<% } else if (docker_base == "wolfi") { %>
RUN groupadd -g 1000 elasticsearch && \
adduser -G elasticsearch -u 1000 elasticsearch -D --home /usr/share/elasticsearch elasticsearch && \
adduser elasticsearch root && \
chown -R 0:0 /usr/share/elasticsearch
<% } else { %>
RUN groupadd -g 1000 elasticsearch && \\
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
@ -196,7 +215,9 @@ ENV ELASTIC_CONTAINER true
WORKDIR /usr/share/elasticsearch
COPY --from=builder --chown=0:0 /usr/share/elasticsearch /usr/share/elasticsearch
<% if (docker_base != "wolfi") { %>
COPY --from=builder --chown=0:0 /bin/tini /bin/tini
<% } %>
<% if (docker_base == 'cloud') { %>
COPY --from=builder --chown=0:0 /opt /opt
@ -280,7 +301,12 @@ CMD ["/app/elasticsearch.sh"]
RUN mkdir /app && \\
echo -e '#!/bin/bash\\nexec /usr/local/bin/docker-entrypoint.sh eswrapper' > /app/elasticsearch.sh && \\
chmod 0555 /app/elasticsearch.sh
<% } else if (docker_base == "wolfi") { %>
# Our actual entrypoint is `tini`, a minimal but functional init program. It
# calls the entrypoint we provide, while correctly forwarding signals.
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
# Dummy overridable parameter parsed by entrypoint
CMD ["eswrapper"]
<% } else { %>
# Our actual entrypoint is `tini`, a minimal but functional init program. It
# calls the entrypoint we provide, while correctly forwarding signals.

View file

@ -0,0 +1,2 @@
// This file is intentionally blank. All configuration of the
// export is done in the parent project.

View file

@ -0,0 +1,2 @@
// This file is intentionally blank. All configuration of the
// export is done in the parent project.

View file

@ -73,6 +73,8 @@ List projects = [
'distribution:docker:ironbank-docker-export',
'distribution:docker:ubi-docker-aarch64-export',
'distribution:docker:ubi-docker-export',
'distribution:docker:wolfi-docker-aarch64-export',
'distribution:docker:wolfi-docker-export',
'distribution:packages:aarch64-deb',
'distribution:packages:deb',
'distribution:packages:aarch64-rpm',