Skip to content

Commit f30d88b

Browse files
authored
Support building Iron Bank Docker context (#65074)
Backport of #64336. This PR adds support for building a Docker context for Iron Bank. It doesn't actually build the image - we could add that at a later stage, but this is an attempt to automate at least some of the process. Iron Bank is a lot like our UBI build, except it uses a hardened version of the full UBI image, not the minimal UBI image. They have particular requirements around how the Docker context should be arranged. The Docker build cannot fetch its own artefacts, but instead the context provides a descriptor that locates what is needed for the build. I also added a filter so that after performing expansions on the `Dockerfile`, we squash long runs on newlines together. This makes the output cleaner, while allowing us to break up the unprocessed `Dockerfile` for clarity.
1 parent e8d7dc2 commit f30d88b

File tree

11 files changed

+262
-30
lines changed

11 files changed

+262
-30
lines changed

buildSrc/src/main/java/org/elasticsearch/gradle/DockerBase.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
public enum DockerBase {
2626
CENTOS("centos:8"),
2727
// "latest" here is intentional, since the image name specifies "8"
28-
UBI("docker.elastic.co/ubi8/ubi-minimal:latest");
28+
UBI("docker.elastic.co/ubi8/ubi-minimal:latest"),
29+
// The Iron Bank base image is UBI (albeit hardened), but we are required to parameterize the Docker build
30+
IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}");
2931

3032
private final String image;
3133

distribution/docker/build.gradle

+72-18
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import org.elasticsearch.gradle.VersionProperties
66
import org.elasticsearch.gradle.docker.DockerBuildTask
77
import org.elasticsearch.gradle.info.BuildParams
88
import org.elasticsearch.gradle.testfixtures.TestFixturesPlugin
9+
10+
import java.nio.file.Path
11+
912
apply plugin: 'elasticsearch.standalone-rest-test'
1013
apply plugin: 'elasticsearch.test.fixtures'
1114
apply plugin: 'elasticsearch.internal-distribution-download'
@@ -46,6 +49,15 @@ ext.expansions = { Architecture architecture, boolean oss, DockerBase base, bool
4649

4750
final String elasticsearch = "elasticsearch-${oss ? 'oss-' : ''}${VersionProperties.elasticsearch}-${classifier}.tar.gz"
4851

52+
String buildArgs = ''
53+
if (base == DockerBase.IRON_BANK) {
54+
buildArgs = """
55+
ARG BASE_REGISTRY=nexus-docker-secure.levelup-nexus.svc.cluster.local:18082
56+
ARG BASE_IMAGE=redhat/ubi/ubi8
57+
ARG BASE_TAG=8.2
58+
"""
59+
}
60+
4961
/* Both the following Dockerfile commands put the resulting artifact at
5062
* the same location, regardless of classifier, so that the commands that
5163
* follow in the Dockerfile don't have to know about the runtime
@@ -61,58 +73,98 @@ RUN curl --retry 8 -S -L \\
6173
"""
6274
}
6375

76+
def (major,minor) = VersionProperties.elasticsearch.split("\\.")
77+
6478
return [
6579
'base_image' : base.getImage(),
80+
'bin_dir' : base == DockerBase.IRON_BANK ? 'scripts' : 'bin',
81+
'build_args' : buildArgs,
6682
'build_date' : BuildParams.buildDate,
83+
'config_dir' : base == DockerBase.IRON_BANK ? 'scripts' : 'config',
6784
'git_revision' : BuildParams.gitRevision,
6885
'license' : oss ? 'Apache-2.0' : 'Elastic-License',
6986
'package_manager' : base == DockerBase.UBI ? 'microdnf' : 'yum',
7087
'source_elasticsearch': sourceElasticsearch,
7188
'docker_base' : base.name().toLowerCase(),
72-
'version' : VersionProperties.elasticsearch
89+
'version' : VersionProperties.elasticsearch,
90+
'major_minor_version' : "${major}.${minor}"
7391
]
7492
}
7593

94+
/**
95+
* This filter squashes long runs of newlines so that the output
96+
* is a little more aesthetically pleasing.
97+
*/
98+
class SquashNewlinesFilter extends FilterReader {
99+
SquashNewlinesFilter(Reader input) {
100+
super(new StringReader(input.text.replaceAll("\n{2,}", "\n\n")))
101+
}
102+
}
103+
76104
private static String buildPath(Architecture architecture, boolean oss, DockerBase base) {
77105
return 'build/' +
78106
(architecture == Architecture.AARCH64 ? 'aarch64-' : '') +
79107
(oss ? 'oss-' : '') +
80108
(base == DockerBase.UBI ? 'ubi-' : '') +
109+
(base == DockerBase.UBI ? 'ubi-' : (base == DockerBase.IRON_BANK ? 'ironbank-' : '')) +
81110
'docker'
82111
}
83112

84113
private static String taskName(String prefix, Architecture architecture, boolean oss, DockerBase base, String suffix) {
85114
return prefix +
86115
(architecture == Architecture.AARCH64 ? 'Aarch64' : '') +
87116
(oss ? 'Oss' : '') +
88-
(base == DockerBase.UBI ? 'Ubi' : '') +
117+
(base == DockerBase.UBI ? 'Ubi' : (base == DockerBase.IRON_BANK ? 'IronBank' : '')) +
89118
suffix
90119
}
91120

92121
project.ext {
93122
dockerBuildContext = { Architecture architecture, boolean oss, DockerBase base, boolean local ->
94123
copySpec {
95-
into('bin') {
96-
from project.projectDir.toPath().resolve("src/docker/bin")
97-
}
98-
99-
into('config') {
100-
/*
101-
* The OSS and default distributions have different configurations, therefore we want to allow overriding the default configuration
102-
* from files in the 'oss' sub-directory. We don't want the 'oss' sub-directory to appear in the final build context, however.
103-
*/
104-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
105-
from(project.projectDir.toPath().resolve("src/docker/config")) {
106-
exclude 'oss'
124+
final Map<String,String> varExpansions = expansions(architecture, oss, base, local)
125+
final Path projectDir = project.projectDir.toPath()
126+
127+
if (base == DockerBase.IRON_BANK) {
128+
into('scripts') {
129+
from(projectDir.resolve("src/docker/bin")) {
130+
// We need to use an entrypoint that doesn't expect to run as `root` for Iron Bank,
131+
// so don't copy this one. It's actually the version from v8.0, so this won't be
132+
// necessary in future versions.
133+
exclude 'docker-entrypoint.sh'
134+
}
135+
from(projectDir.resolve("src/docker/config")) {
136+
exclude '**/oss'
137+
}
138+
from(projectDir.resolve("src/docker/iron_bank")) {
139+
expand(varExpansions)
140+
// Exclude this script so that no expansions are performed
141+
exclude 'docker-entrypoint.sh'
142+
}
143+
from(projectDir.resolve("src/docker/iron_bank")) {
144+
// ...and now copy the entrypoint without expansions
145+
include 'docker-entrypoint.sh'
146+
}
107147
}
108-
if (oss) {
109-
// Overlay the config file
110-
from project.projectDir.toPath().resolve("src/docker/config/oss")
148+
} else {
149+
into('bin') {
150+
from projectDir.resolve("src/docker/bin")
151+
}
152+
153+
into('config') {
154+
// The OSS and default distribution can have different configuration, therefore we want to
155+
// allow overriding the default configuration by creating config files in oss or default
156+
// build-context sub-modules.
157+
duplicatesStrategy = DuplicatesStrategy.INCLUDE
158+
from projectDir.resolve("src/docker/config")
159+
if (oss) {
160+
from projectDir.resolve("src/docker/config/oss")
161+
}
111162
}
112163
}
113164

114165
from(project.projectDir.toPath().resolve("src/docker/Dockerfile")) {
115-
expand(expansions(architecture, oss, base, local))
166+
expand(varExpansions)
167+
filter SquashNewlinesFilter
116168
}
117169
}
118170
}
@@ -324,6 +376,8 @@ subprojects { Project subProject ->
324376

325377
final Architecture architecture = subProject.name.contains('aarch64-') ? Architecture.AARCH64 : Architecture.X64
326378
final boolean oss = subProject.name.contains('oss-')
379+
// We can ignore Iron Bank at the moment as we don't
380+
// build those images ourselves.
327381
final DockerBase base = subProject.name.contains('ubi-') ? DockerBase.UBI : DockerBase.CENTOS
328382

329383
final String arch = architecture == Architecture.AARCH64 ? '-aarch64' : ''
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import org.elasticsearch.gradle.Architecture
2+
import org.elasticsearch.gradle.DockerBase
3+
4+
apply plugin: 'base'
5+
6+
tasks.register("buildIronBankDockerBuildContext", Tar) {
7+
archiveExtension = 'tar.gz'
8+
compression = Compression.GZIP
9+
archiveClassifier = "docker-build-context"
10+
archiveBaseName = "elasticsearch-ironbank"
11+
// We always treat Iron Bank builds as local, because that is how they
12+
// are built
13+
with dockerBuildContext(Architecture.X64, false, DockerBase.IRON_BANK, true)
14+
}

distribution/docker/src/docker/Dockerfile

+48-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#
44
# Beginning of multi stage Dockerfile
55
################################################################################
6+
67
<% /*
78
This file is passed through Groovy's SimpleTemplateEngine, so dollars and backslashes
89
have to be escaped in order for them to appear in the final Dockerfile. You
@@ -13,17 +14,31 @@
1314
We use control-flow tags in this file to conditionally render the content. The
1415
layout/presentation here has been adjusted so that it looks reasonable when rendered,
1516
at the slight expense of how it looks here.
17+
18+
Note that this file is also filtered to squash together newlines, so we can
19+
add as many newlines here as necessary to improve legibility.
1620
*/ %>
1721
################################################################################
1822
# Build stage 0 `builder`:
1923
# Extract Elasticsearch artifact
2024
################################################################################
2125
26+
${build_args}
27+
2228
FROM ${base_image} AS builder
29+
2330
<% if (docker_base == 'ubi') { %>
2431
# Install required packages to extract the Elasticsearch distribution
2532
RUN ${package_manager} install -y tar gzip
2633
<% } %>
34+
35+
<% if (docker_base == 'iron_bank') { %>
36+
# `tini` is a tiny but valid init for containers. This is used to cleanly
37+
# control how ES and any child processes are shut down.
38+
COPY tini /bin/tini
39+
RUN chmod 0755 /bin/tini
40+
<% } else { %>
41+
2742
# `tini` is a tiny but valid init for containers. This is used to cleanly
2843
# control how ES and any child processes are shut down.
2944
#
@@ -41,19 +56,23 @@ RUN set -eux ; \\
4156
curl --retry 8 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\
4257
sha256sum -c \${tini_bin}.sha256sum ; \\
4358
rm \${tini_bin}.sha256sum ; \\
44-
mv \${tini_bin} /tini ; \\
45-
chmod +x /tini
59+
mv \${tini_bin} /bin/tini ; \\
60+
chmod +x /bin/tini
61+
62+
<% } %>
4663
4764
RUN mkdir /usr/share/elasticsearch
4865
WORKDIR /usr/share/elasticsearch
4966
5067
${source_elasticsearch}
5168
52-
RUN tar zxf /opt/elasticsearch.tar.gz --strip-components=1
69+
RUN tar -zxf /opt/elasticsearch.tar.gz --strip-components=1
70+
71+
# Configure the distribution for Docker
5372
RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' /usr/share/elasticsearch/bin/elasticsearch-env
54-
RUN mkdir -p config config/jvm.options.d data logs
73+
RUN mkdir -p config config/jvm.options.d data logs plugins
5574
RUN chmod 0775 config config/jvm.options.d data logs plugins
56-
COPY config/elasticsearch.yml config/log4j2.properties config/
75+
COPY ${config_dir}/elasticsearch.yml ${config_dir}/log4j2.properties config/
5776
RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties
5877
5978
################################################################################
@@ -65,7 +84,17 @@ RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties
6584
6685
FROM ${base_image}
6786
68-
ENV ELASTIC_CONTAINER true
87+
<% if (docker_base == "iron_bank") { %>
88+
<%
89+
/* Reviews of the Iron Bank Dockerfile said that they preferred simpler */
90+
/* scripting so this version doesn't have the retry loop featured below. */
91+
%>
92+
RUN ${package_manager} update --setopt=tsflags=nodocs -y && \\
93+
${package_manager} install --setopt=tsflags=nodocs -y \\
94+
nc shadow-utils zip unzip && \\
95+
${package_manager} clean all
96+
97+
<% } else { %>
6998

7099
RUN for iter in {1..10}; do \\
71100
${package_manager} update --setopt=tsflags=nodocs -y && \\
@@ -76,14 +105,18 @@ RUN for iter in {1..10}; do \\
76105
done; \\
77106
(exit \$exit_code)
78107

108+
<% } %>
109+
79110
RUN groupadd -g 1000 elasticsearch && \\
80111
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
81112
chmod 0775 /usr/share/elasticsearch && \\
82113
chown -R 1000:0 /usr/share/elasticsearch
83114

115+
ENV ELASTIC_CONTAINER true
116+
84117
WORKDIR /usr/share/elasticsearch
85118
COPY --from=builder --chown=1000:0 /usr/share/elasticsearch /usr/share/elasticsearch
86-
COPY --from=builder --chown=0:0 /tini /tini
119+
COPY --from=builder --chown=0:0 /bin/tini /bin/tini
87120

88121
# Replace OpenJDK's built-in CA certificate keystore with the one from the OS
89122
# vendor. The latter is superior in several ways.
@@ -92,7 +125,7 @@ RUN ln -sf /etc/pki/ca-trust/extracted/java/cacerts /usr/share/elasticsearch/jdk
92125

93126
ENV PATH /usr/share/elasticsearch/bin:\$PATH
94127

95-
COPY bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
128+
COPY ${bin_dir}/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
96129

97130
# 1. The JDK's directories' permissions don't allow `java` to be executed under a different
98131
# group to the default. Fix this.
@@ -128,7 +161,8 @@ LABEL org.label-schema.build-date="${build_date}" \\
128161
org.opencontainers.image.url="https://www.elastic.co/products/elasticsearch" \\
129162
org.opencontainers.image.vendor="Elastic" \\
130163
org.opencontainers.image.version="${version}"
131-
<% if (docker_base == 'ubi') { %>
164+
165+
<% if (docker_base == 'ubi' || docker_base == 'iron_bank') { %>
132166
LABEL name="Elasticsearch" \\
133167
maintainer="[email protected]" \\
134168
vendor="Elastic" \\
@@ -141,10 +175,14 @@ RUN mkdir /licenses && \\
141175
cp LICENSE.txt /licenses/LICENSE
142176
<% } %>
143177

144-
ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
178+
ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
145179
# Dummy overridable parameter parsed by entrypoint
146180
CMD ["eswrapper"]
147181

182+
<% if (docker_base == 'iron_bank') { %>
183+
HEALTHCHECK --interval=10s --timeout=5s --start-period=1m --retries=5 CMD curl -I -f --max-time 5 http://localhost:9200 || exit 1
184+
<% } %>
185+
148186
################################################################################
149187
# End of multi-stage Dockerfile
150188
################################################################################
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore any locally downloaded or dropped releases
2+
*.tar.gz
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@Library('DCCSCR@master') _
2+
dccscrPipeline(version: '${version}')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Elasticsearch
2+
3+
**Elasticsearch** is a distributed, RESTful search and analytics engine capable of
4+
solving a growing number of use cases. As the heart of the Elastic Stack, it
5+
centrally stores your data so you can discover the expected and uncover the
6+
unexpected.
7+
8+
For more information about Elasticsearch, please visit
9+
https://www.elastic.co/products/elasticsearch.
10+
11+
### Installation instructions
12+
13+
Please follow the documentation on [how to install Elasticsearch with Docker](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html).
14+
15+
### Where to file issues and PRs
16+
17+
- [Issues](https://github.com/elastic/elasticsearch/issues)
18+
- [PRs](https://github.com/elastic/elasticsearch/pulls)
19+
20+
### Where to get help
21+
22+
- [Elasticsearch Discuss Forums](https://discuss.elastic.co/c/elasticsearch)
23+
- [Elasticsearch Documentation](https://www.elastic.co/guide/en/elasticsearch/reference/master/index.html)
24+
25+
### Still need help?
26+
27+
You can learn more about the Elastic Community and also understand how to get more help
28+
visiting [Elastic Community](https://www.elastic.co/community).
29+
30+
31+
This software is governed by the [Elastic
32+
License](https://github.com/elastic/elasticsearch/blob/${major_minor_version}/licenses/ELASTIC-LICENSE.txt),
33+
and includes the full set of [free
34+
features](https://www.elastic.co/subscriptions).
35+
36+
View the detailed release notes
37+
[here](https://www.elastic.co/guide/en/elasticsearch/reference/${major_minor_version}/es-release-notes.html).

0 commit comments

Comments
 (0)