Push arch-specific logic from Gradle into Docker (#56503)

Docker informed us that for official multi-arch Docker builds, there
needs to be a single Dockerfile and build context that can be used for
each supported architecture. Therefore, rework the build to move the
relevant architecture logic into the Dockerfile, and merge the aarch64
/ x64 docker context builds.
This commit is contained in:
Rory Hunter 2020-05-12 11:10:05 +01:00 committed by Rory Hunter
parent dfdd7e4fce
commit bf97679854
6 changed files with 93 additions and 88 deletions

View File

@ -28,32 +28,47 @@ dependencies {
} }
ext.expansions = { architecture, oss, local -> ext.expansions = { architecture, oss, local ->
String base_image = null String classifier
String tini_arch = null if (local) {
String classifier = null switch (architecture) {
switch (architecture) { case "aarch64":
case "aarch64": classifier = "linux-aarch64"
base_image = "arm64v8/centos:7" break
tini_arch = "arm64" case "x64":
classifier = "linux-aarch64" classifier = "linux-x86_64"
break; break
case "x64": default:
base_image = "amd64/centos:7" throw new IllegalArgumentException("Unrecognized architecture [" + architecture + "], must be one of (aarch64|x64)")
tini_arch = "amd64" }
classifier = "linux-x86_64" } else {
break; /* When sourcing the Elasticsearch build remotely, the same Dockerfile needs
default: * to be able to fetch the artifact for any supported platform. We can't make
throw new IllegalArgumentException("unrecongized architecture [" + architecture + "], must be one of (aarch64|x64)") * the decision here. Bash will interpolate the `arch` command for us. */
classifier = "linux-\$(arch)"
} }
final String elasticsearch = oss ? "elasticsearch-oss-${VersionProperties.elasticsearch}-${classifier}.tar.gz" : "elasticsearch-${VersionProperties.elasticsearch}-${classifier}.tar.gz"
final String elasticsearch = "elasticsearch-${oss ? 'oss-' : ''}${VersionProperties.elasticsearch}-${classifier}.tar.gz"
/* Both the following Dockerfile commands put the resulting artifact at
* the same location, regardless of classifier, so that the commands that
* follow in the Dockerfile don't have to know about the runtime
* architecture. */
String sourceElasticsearch
if (local) {
sourceElasticsearch = "COPY $elasticsearch /opt/elasticsearch.tar.gz"
} else {
sourceElasticsearch = """
RUN curl --retry 8 -S -L \\
--output /opt/elasticsearch.tar.gz \\
https://artifacts.elastic.co/downloads/elasticsearch/$elasticsearch
"""
}
return [ return [
'base_image' : base_image,
'build_date' : BuildParams.buildDate, 'build_date' : BuildParams.buildDate,
'elasticsearch' : elasticsearch,
'git_revision' : BuildParams.gitRevision, 'git_revision' : BuildParams.gitRevision,
'license' : oss ? 'Apache-2.0' : 'Elastic-License', 'license' : oss ? 'Apache-2.0' : 'Elastic-License',
'source_elasticsearch': local ? "COPY $elasticsearch /opt/" : "RUN cd /opt && curl --retry 8 -s -L -O https://artifacts.elastic.co/downloads/elasticsearch/${elasticsearch} && cd -", 'source_elasticsearch': sourceElasticsearch,
'tini_arch' : tini_arch,
'version' : VersionProperties.elasticsearch 'version' : VersionProperties.elasticsearch
] ]
} }

View File

@ -1,11 +0,0 @@
apply plugin: 'base'
task buildDockerBuildContext(type: Tar) {
extension = 'tar.gz'
compression = Compression.GZIP
archiveClassifier = "docker-build-context-aarch64"
archiveBaseName = "elasticsearch"
with dockerBuildContext("aarch64", false, false)
}
assemble.dependsOn buildDockerBuildContext

View File

@ -5,7 +5,9 @@ task buildDockerBuildContext(type: Tar) {
compression = Compression.GZIP compression = Compression.GZIP
archiveClassifier = "docker-build-context" archiveClassifier = "docker-build-context"
archiveBaseName = "elasticsearch" archiveBaseName = "elasticsearch"
with dockerBuildContext("x64", false, false) // Non-local builds don't need to specify an architecture.
// Make this explicit via the string value.
with dockerBuildContext("<remote>", false, false)
} }
assemble.dependsOn buildDockerBuildContext assemble.dependsOn buildDockerBuildContext

View File

@ -1,11 +0,0 @@
apply plugin: 'base'
task buildOssDockerBuildContext(type: Tar) {
extension = 'tar.gz'
compression = Compression.GZIP
archiveClassifier = "docker-build-context-aarch64"
archiveBaseName = "elasticsearch-oss"
with dockerBuildContext("aarch64", true, false)
}
assemble.dependsOn buildOssDockerBuildContext

View File

@ -5,7 +5,9 @@ task buildOssDockerBuildContext(type: Tar) {
compression = Compression.GZIP compression = Compression.GZIP
archiveClassifier = "docker-build-context" archiveClassifier = "docker-build-context"
archiveBaseName = "elasticsearch-oss" archiveBaseName = "elasticsearch-oss"
with dockerBuildContext("x64", true, false) // Non-local builds don't need to specify an architecture.
// Make this explicit via the string value.
with dockerBuildContext("<remote>", true, false)
} }
assemble.dependsOn buildOssDockerBuildContext assemble.dependsOn buildOssDockerBuildContext

View File

@ -3,45 +3,53 @@
# #
# Beginning of multi stage Dockerfile # Beginning of multi stage Dockerfile
################################################################################ ################################################################################
<% /*
This file is passed through Groovy's SimpleTemplateEngine, so dollars and backslashes
have to be escaped in order for them to appear in the final Dockerfile. You
can also comment out blocks, like this one. See:
https://docs.groovy-lang.org/latest/html/api/groovy/text/SimpleTemplateEngine.html
*/ %>
################################################################################ ################################################################################
# Build stage 0 `builder`: # Build stage 0 `builder`:
# Extract elasticsearch artifact # Extract elasticsearch artifact
# Install required plugins
# Set gid=0 and make group perms==owner perms # Set gid=0 and make group perms==owner perms
################################################################################ ################################################################################
FROM ${base_image} AS builder FROM centos:7 AS builder
RUN for iter in {1..10}; do yum update --setopt=tsflags=nodocs -y && \
yum install --setopt=tsflags=nodocs -y wget gzip shadow-utils tar && \
yum clean all && exit_code=0 && break || exit_code=\$? && echo "yum error: retry \$iter in 10s" && sleep 10; done; \
(exit \$exit_code)
# `tini` is a tiny but valid init for containers. This is used to cleanly # `tini` is a tiny but valid init for containers. This is used to cleanly
# control how ES and any child processes are shut down. # control how ES and any child processes are shut down.
# #
# The tini GitHub page gives instructions for verifying the binary using # 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 # gpg, but the keyservers are slow to return the key and this can fail the
# build. Instead, we check the binary against a checksum that they provide. # build. Instead, we check the binary against the published checksum.
RUN wget --no-cookies --quiet https://github.com/krallin/tini/releases/download/v0.19.0/tini-${tini_arch} \ RUN set -eux ; \\
&& wget --no-cookies --quiet https://github.com/krallin/tini/releases/download/v0.19.0/tini-${tini_arch}.sha256sum \ \\
&& sha256sum -c tini-${tini_arch}.sha256sum \ tini_bin="" ; \\
&& mv tini-${tini_arch} /tini \ case "\$(arch)" in \\
&& chmod +x /tini aarch64) tini_bin='tini-arm64' ;; \\
x86_64) tini_bin='tini-amd64' ;; \\
*) echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ;; \\
esac ; \\
curl --retry 8 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\
curl --retry 8 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\
sha256sum -c \${tini_bin}.sha256sum ; \\
rm \${tini_bin}.sha256sum ; \\
mv \${tini_bin} /tini ; \\
chmod +x /tini
ENV PATH /usr/share/elasticsearch/bin:\$PATH ENV PATH /usr/share/elasticsearch/bin:\$PATH
RUN groupadd -g 1000 elasticsearch && \ RUN groupadd -g 1000 elasticsearch && \\
adduser -u 1000 -g 1000 -d /usr/share/elasticsearch elasticsearch adduser -u 1000 -g 1000 -d /usr/share/elasticsearch elasticsearch
WORKDIR /usr/share/elasticsearch WORKDIR /usr/share/elasticsearch
${source_elasticsearch} ${source_elasticsearch}
RUN tar zxf /opt/${elasticsearch} --strip-components=1 RUN tar zxf /opt/elasticsearch.tar.gz --strip-components=1
RUN grep ES_DISTRIBUTION_TYPE=tar /usr/share/elasticsearch/bin/elasticsearch-env \ RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' /usr/share/elasticsearch/bin/elasticsearch-env
&& sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' /usr/share/elasticsearch/bin/elasticsearch-env
RUN mkdir -p config config/jvm.options.d data logs RUN mkdir -p config config/jvm.options.d data logs
RUN chmod 0775 config config/jvm.options.d data logs RUN chmod 0775 config config/jvm.options.d data logs
COPY config/elasticsearch.yml config/log4j2.properties config/ COPY config/elasticsearch.yml config/log4j2.properties config/
@ -53,20 +61,20 @@ RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties
# Add entrypoint # Add entrypoint
################################################################################ ################################################################################
FROM ${base_image} FROM centos:7
ENV ELASTIC_CONTAINER true ENV ELASTIC_CONTAINER true
COPY --from=builder /tini /tini COPY --from=builder /tini /tini
RUN for iter in {1..10}; do yum update --setopt=tsflags=nodocs -y && \ RUN for iter in {1..10}; do yum update --setopt=tsflags=nodocs -y && \\
yum install --setopt=tsflags=nodocs -y nc shadow-utils zip unzip && \ yum install --setopt=tsflags=nodocs -y nc shadow-utils zip unzip && \\
yum clean all && exit_code=0 && break || exit_code=\$? && echo "yum error: retry \$iter in 10s" && sleep 10; done; \ yum clean all && exit_code=0 && break || exit_code=\$? && echo "yum error: retry \$iter in 10s" && sleep 10; done; \\
(exit \$exit_code) (exit \$exit_code)
RUN groupadd -g 1000 elasticsearch && \ RUN groupadd -g 1000 elasticsearch && \\
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \ adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
chmod 0775 /usr/share/elasticsearch && \ chmod 0775 /usr/share/elasticsearch && \\
chgrp 0 /usr/share/elasticsearch chgrp 0 /usr/share/elasticsearch
WORKDIR /usr/share/elasticsearch WORKDIR /usr/share/elasticsearch
@ -81,7 +89,7 @@ ENV PATH /usr/share/elasticsearch/bin:\$PATH
COPY bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh COPY bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod g=u /etc/passwd && \ RUN chmod g=u /etc/passwd && \\
chmod 0775 /usr/local/bin/docker-entrypoint.sh chmod 0775 /usr/local/bin/docker-entrypoint.sh
# Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks. # Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks.
@ -89,24 +97,24 @@ RUN find / -xdev -perm -4000 -exec chmod ug-s {} +
EXPOSE 9200 9300 EXPOSE 9200 9300
LABEL org.label-schema.build-date="${build_date}" \ LABEL org.label-schema.build-date="${build_date}" \\
org.label-schema.license="${license}" \ org.label-schema.license="${license}" \\
org.label-schema.name="Elasticsearch" \ org.label-schema.name="Elasticsearch" \\
org.label-schema.schema-version="1.0" \ org.label-schema.schema-version="1.0" \\
org.label-schema.url="https://www.elastic.co/products/elasticsearch" \ org.label-schema.url="https://www.elastic.co/products/elasticsearch" \\
org.label-schema.usage="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \ org.label-schema.usage="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\
org.label-schema.vcs-ref="${git_revision}" \ org.label-schema.vcs-ref="${git_revision}" \\
org.label-schema.vcs-url="https://github.com/elastic/elasticsearch" \ org.label-schema.vcs-url="https://github.com/elastic/elasticsearch" \\
org.label-schema.vendor="Elastic" \ org.label-schema.vendor="Elastic" \\
org.label-schema.version="${version}" \ org.label-schema.version="${version}" \\
org.opencontainers.image.created="${build_date}" \ org.opencontainers.image.created="${build_date}" \\
org.opencontainers.image.documentation="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \ org.opencontainers.image.documentation="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\
org.opencontainers.image.licenses="${license}" \ org.opencontainers.image.licenses="${license}" \\
org.opencontainers.image.revision="${git_revision}" \ org.opencontainers.image.revision="${git_revision}" \\
org.opencontainers.image.source="https://github.com/elastic/elasticsearch" \ org.opencontainers.image.source="https://github.com/elastic/elasticsearch" \\
org.opencontainers.image.title="Elasticsearch" \ org.opencontainers.image.title="Elasticsearch" \\
org.opencontainers.image.url="https://www.elastic.co/products/elasticsearch" \ org.opencontainers.image.url="https://www.elastic.co/products/elasticsearch" \\
org.opencontainers.image.vendor="Elastic" \ org.opencontainers.image.vendor="Elastic" \\
org.opencontainers.image.version="${version}" org.opencontainers.image.version="${version}"
ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]