Skip to content

Commit 11dd412

Browse files
authored
Introduce Docker images build (#36246)
This commit introduces the building of the Docker images as bonafide packaging formats alongside our existing archive and packaging distributions. This build is migrated from a dedicated repository, and converted to Gradle in the process.
1 parent 3e04a90 commit 11dd412

File tree

7 files changed

+400
-0
lines changed

7 files changed

+400
-0
lines changed

buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import org.gradle.api.tasks.compile.JavaCompile
5151
import org.gradle.api.tasks.javadoc.Javadoc
5252
import org.gradle.internal.jvm.Jvm
5353
import org.gradle.process.ExecResult
54+
import org.gradle.process.ExecSpec
5455
import org.gradle.util.GradleVersion
5556

5657
import java.nio.charset.StandardCharsets
@@ -232,6 +233,95 @@ class BuildPlugin implements Plugin<Project> {
232233
project.ext.java9Home = project.rootProject.ext.java9Home
233234
}
234235

236+
static void requireDocker(final Task task) {
237+
final Project rootProject = task.project.rootProject
238+
if (rootProject.hasProperty('requiresDocker') == false) {
239+
/*
240+
* This is our first time encountering a task that requires Docker. We will add an extension that will let us track the tasks
241+
* that register as requiring Docker. We will add a delayed execution that when the task graph is ready if any such tasks are
242+
* in the task graph, then we check two things:
243+
* - the Docker binary is available
244+
* - we can execute a Docker command that requires privileges
245+
*
246+
* If either of these fail, we fail the build.
247+
*/
248+
final boolean buildDocker
249+
final String buildDockerProperty = System.getProperty("build.docker")
250+
if (buildDockerProperty == null || buildDockerProperty == "true") {
251+
buildDocker = true
252+
} else if (buildDockerProperty == "false") {
253+
buildDocker = false
254+
} else {
255+
throw new IllegalArgumentException(
256+
"expected build.docker to be unset or one of \"true\" or \"false\" but was [" + buildDockerProperty + "]")
257+
}
258+
rootProject.rootProject.ext.buildDocker = buildDocker
259+
rootProject.rootProject.ext.requiresDocker = []
260+
rootProject.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
261+
// check if the Docker binary exists and record its path
262+
final List<String> maybeDockerBinaries = ['/usr/bin/docker2', '/usr/local/bin/docker2']
263+
final String dockerBinary = maybeDockerBinaries.find { it -> new File(it).exists() }
264+
265+
int exitCode
266+
String dockerErrorOutput
267+
if (dockerBinary == null) {
268+
exitCode = -1
269+
dockerErrorOutput = null
270+
} else {
271+
// the Docker binary executes, check that we can execute a privileged command
272+
final ByteArrayOutputStream output = new ByteArrayOutputStream()
273+
final ExecResult result = LoggedExec.exec(rootProject, { ExecSpec it ->
274+
it.commandLine dockerBinary, "images"
275+
it.errorOutput = output
276+
it.ignoreExitValue = true
277+
})
278+
if (result.exitValue == 0) {
279+
return
280+
}
281+
exitCode = result.exitValue
282+
dockerErrorOutput = output.toString()
283+
}
284+
final List<String> tasks =
285+
((List<Task>)rootProject.requiresDocker).findAll { taskGraph.hasTask(it) }.collect { " ${it.path}".toString()}
286+
if (tasks.isEmpty() == false) {
287+
/*
288+
* There are tasks in the task graph that require Docker. Now we are failing because either the Docker binary does not
289+
* exist or because execution of a privileged Docker command failed.
290+
*/
291+
String message
292+
if (dockerBinary == null) {
293+
message = String.format(
294+
Locale.ROOT,
295+
"Docker (checked [%s]) is required to run the following task%s: \n%s",
296+
maybeDockerBinaries.join(","),
297+
tasks.size() > 1 ? "s" : "",
298+
tasks.join('\n'))
299+
} else {
300+
assert exitCode > 0 && dockerErrorOutput != null
301+
message = String.format(
302+
Locale.ROOT,
303+
"a problem occurred running Docker from [%s] yet it is required to run the following task%s: \n%s\n" +
304+
"the problem is that Docker exited with exit code [%d] with standard error output [%s]",
305+
dockerBinary,
306+
tasks.size() > 1 ? "s" : "",
307+
tasks.join('\n'),
308+
exitCode,
309+
dockerErrorOutput.trim())
310+
}
311+
throw new GradleException(
312+
message + "\nyou can address this by attending to the reported issue, "
313+
+ "removing the offending tasks from being executed, "
314+
+ "or by passing -Dbuild.docker=false")
315+
}
316+
}
317+
}
318+
if (rootProject.buildDocker) {
319+
rootProject.requiresDocker.add(task)
320+
} else {
321+
task.enabled = false
322+
}
323+
}
324+
235325
private static String findCompilerJavaHome() {
236326
String compilerJavaHome = System.getenv('JAVA_HOME')
237327
final String compilerJavaProperty = System.getProperty('compiler.java')

distribution/docker/build.gradle

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import org.elasticsearch.gradle.BuildPlugin
2+
import org.elasticsearch.gradle.LoggedExec
3+
import org.elasticsearch.gradle.MavenFilteringHack
4+
import org.elasticsearch.gradle.VersionProperties
5+
6+
apply plugin: 'base'
7+
8+
configurations {
9+
dockerPlugins
10+
dockerSource
11+
ossDockerSource
12+
}
13+
14+
dependencies {
15+
dockerPlugins project(path: ":plugins:ingest-geoip", configuration: 'zip')
16+
dockerPlugins project(path: ":plugins:ingest-user-agent", configuration: 'zip')
17+
dockerSource project(path: ":distribution:archives:tar")
18+
ossDockerSource project(path: ":distribution:archives:oss-tar")
19+
}
20+
21+
ext.expansions = { oss ->
22+
return [
23+
'elasticsearch' : oss ? "elasticsearch-oss-${VersionProperties.elasticsearch}.tar.gz" : "elasticsearch-${VersionProperties.elasticsearch}.tar.gz",
24+
'jdkUrl' : 'https://download.java.net/java/GA/jdk11/13/GPL/openjdk-11.0.1_linux-x64_bin.tar.gz',
25+
'jdkVersion' : '11.0.1',
26+
'license': oss ? 'Apache-2.0' : 'Elastic License',
27+
'ingest-geoip' : "ingest-geoip-${VersionProperties.elasticsearch}.zip",
28+
'ingest-user-agent' : "ingest-user-agent-${VersionProperties.elasticsearch}.zip",
29+
'version' : VersionProperties.elasticsearch
30+
]
31+
}
32+
33+
private static String files(final boolean oss) {
34+
return "build/${ oss ? 'oss-' : ''}docker"
35+
}
36+
37+
private static String taskName(final String prefix, final boolean oss, final String suffix) {
38+
return "${prefix}${oss ? 'Oss' : ''}${suffix}"
39+
}
40+
41+
void addCopyDockerContextTask(final boolean oss) {
42+
task(taskName("copy", oss, "DockerContext"), type: Sync) {
43+
into files(oss)
44+
45+
into('bin') {
46+
from 'src/docker/bin'
47+
}
48+
49+
into('config') {
50+
from 'src/docker/config'
51+
}
52+
53+
if (oss) {
54+
from configurations.ossDockerSource
55+
} else {
56+
from configurations.dockerSource
57+
}
58+
59+
from configurations.dockerPlugins
60+
}
61+
}
62+
63+
void addCopyDockerfileTask(final boolean oss) {
64+
task(taskName("copy", oss, "Dockerfile"), type: Copy) {
65+
mustRunAfter(taskName("copy", oss, "DockerContext"))
66+
into files(oss)
67+
68+
from('src/docker/Dockerfile') {
69+
MavenFilteringHack.filter(it, expansions(oss))
70+
}
71+
}
72+
}
73+
74+
void addBuildDockerImage(final boolean oss) {
75+
final Task buildDockerImageTask = task(taskName("build", oss, "DockerImage"), type: LoggedExec) {
76+
dependsOn taskName("copy", oss, "DockerContext")
77+
dependsOn taskName("copy", oss, "Dockerfile")
78+
List<String> tags
79+
if (oss) {
80+
tags = [ "docker.elastic.co/elasticsearch/elasticsearch-oss:${VersionProperties.elasticsearch}" ]
81+
} else {
82+
tags = [
83+
"elasticsearch:${VersionProperties.elasticsearch}",
84+
"docker.elastic.co/elasticsearch/elasticsearch:${VersionProperties.elasticsearch}",
85+
"docker.elastic.co/elasticsearch/elasticsearch-full:${VersionProperties.elasticsearch}"
86+
]
87+
}
88+
executable 'docker'
89+
final List<String> dockerArgs = ['build', files(oss), '--pull']
90+
for (final String tag : tags) {
91+
dockerArgs.add('--tag')
92+
dockerArgs.add(tag)
93+
}
94+
args dockerArgs.toArray()
95+
}
96+
BuildPlugin.requireDocker(buildDockerImageTask)
97+
}
98+
99+
for (final boolean oss : [false, true]) {
100+
addCopyDockerContextTask(oss)
101+
addCopyDockerfileTask(oss)
102+
addBuildDockerImage(oss)
103+
}
104+
105+
assemble.dependsOn "buildOssDockerImage"
106+
assemble.dependsOn "buildDockerImage"
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
################################################################################
2+
# This Dockerfile was generated from the template at distribution/src/docker/Dockerfile
3+
#
4+
# Beginning of multi stage Dockerfile
5+
################################################################################
6+
7+
################################################################################
8+
# Build stage 0 `builder`:
9+
# Extract elasticsearch artifact
10+
# Install required plugins
11+
# Set gid=0 and make group perms==owner perms
12+
################################################################################
13+
14+
FROM centos:7 AS builder
15+
16+
ENV PATH /usr/share/elasticsearch/bin:$PATH
17+
ENV JAVA_HOME /opt/jdk-${jdkVersion}
18+
19+
RUN curl -s ${jdkUrl} | tar -C /opt -zxf -
20+
21+
# Replace OpenJDK's built-in CA certificate keystore with the one from the OS
22+
# vendor. The latter is superior in several ways.
23+
# REF: https://github.com/elastic/elasticsearch-docker/issues/171
24+
RUN ln -sf /etc/pki/ca-trust/extracted/java/cacerts /opt/jdk-${jdkVersion}/lib/security/cacerts
25+
26+
RUN yum install -y unzip which
27+
28+
RUN groupadd -g 1000 elasticsearch && \
29+
adduser -u 1000 -g 1000 -d /usr/share/elasticsearch elasticsearch
30+
31+
WORKDIR /usr/share/elasticsearch
32+
33+
COPY ${elasticsearch} ${ingest-geoip} ${ingest-user-agent} /opt/
34+
RUN tar zxf /opt/${elasticsearch} --strip-components=1
35+
RUN elasticsearch-plugin install --batch file:///opt/${ingest-geoip}
36+
RUN elasticsearch-plugin install --batch file:///opt/${ingest-user-agent}
37+
RUN mkdir -p config data logs
38+
RUN chmod 0775 config data logs
39+
COPY config/elasticsearch.yml config/log4j2.properties config/
40+
41+
42+
################################################################################
43+
# Build stage 1 (the actual elasticsearch image):
44+
# Copy elasticsearch from stage 0
45+
# Add entrypoint
46+
################################################################################
47+
48+
FROM centos:7
49+
50+
ENV ELASTIC_CONTAINER true
51+
ENV JAVA_HOME /opt/jdk-${jdkVersion}
52+
53+
COPY --from=builder /opt/jdk-${jdkVersion} /opt/jdk-${jdkVersion}
54+
55+
RUN yum update -y && \
56+
yum install -y nc unzip wget which && \
57+
yum clean all
58+
59+
RUN groupadd -g 1000 elasticsearch && \
60+
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \
61+
chmod 0775 /usr/share/elasticsearch && \
62+
chgrp 0 /usr/share/elasticsearch
63+
64+
WORKDIR /usr/share/elasticsearch
65+
COPY --from=builder --chown=1000:0 /usr/share/elasticsearch /usr/share/elasticsearch
66+
ENV PATH /usr/share/elasticsearch/bin:$PATH
67+
68+
COPY --chown=1000:0 bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
69+
70+
# Openshift overrides USER and uses ones with randomly uid>1024 and gid=0
71+
# Allow ENTRYPOINT (and ES) to run even with a different user
72+
RUN chgrp 0 /usr/local/bin/docker-entrypoint.sh && \
73+
chmod g=u /etc/passwd && \
74+
chmod 0775 /usr/local/bin/docker-entrypoint.sh
75+
76+
EXPOSE 9200 9300
77+
78+
LABEL org.label-schema.schema-version="1.0" \
79+
org.label-schema.vendor="Elastic" \
80+
org.label-schema.name="elasticsearch" \
81+
org.label-schema.version="${version}" \
82+
org.label-schema.url="https://www.elastic.co/products/elasticsearch" \
83+
org.label-schema.vcs-url="https://github.com/elastic/elasticsearch" \
84+
license="${license}"
85+
86+
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
87+
# Dummy overridable parameter parsed by entrypoint
88+
CMD ["eswrapper"]
89+
90+
################################################################################
91+
# End of multi-stage Dockerfile
92+
################################################################################

0 commit comments

Comments
 (0)