Skip to content
This repository was archived by the owner on Apr 12, 2022. It is now read-only.

Use multi-stage build and replace CMD with ENTRYPOINT #125

Merged
merged 18 commits into from
Oct 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ else
endif

# Build different images tagged as :version-<flavor>
IMAGE_FLAVORS ?= basic platinum
IMAGE_FLAVORS ?= oss basic platinum

# Which image flavor will additionally receive the plain `:version` tag
DEFAULT_IMAGE_FLAVOR ?= basic
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
#!/bin/bash
set -e

# Run Elasticsearch and allow setting default settings via env vars
# Files created by Elasticsearch should always be group writable too
umask 0002

run_as_other_user_if_needed() {
if [[ "$(id -u)" == "0" ]]; then
# If running as root, drop to specified UID and run command
exec chroot --userspec=1000 / "${@}"
else
# Either we are running in Openshift with random uid and are a member of the root group
# or with a custom --user
exec "${@}"
fi
}

# Allow user specify custom CMD, maybe bin/elasticsearch itself
# for example to directly specify `-E` style parameters for elasticsearch on k8s
# or simply to run /bin/bash to check the image
if [[ "$1" != "eswrapper" ]]; then
if [[ "$(id -u)" == "0" ]] && [[ "$1" == *elasticsearch* ]]; then
# centos:7 chroot doesn't have `--skip-chdir` option and
# changes our CWD. Rewrite CMD args to replace $1 with
# `elasticsearch` explicitly, so that we are backwards
# compatible with the docs from previous Elasticsearch
# versions<6 and configuration option D:
# https://www.elastic.co/guide/en/elasticsearch/reference/5.6/docker.html#_d_override_the_image_8217_s_default_ulink_url_https_docs_docker_com_engine_reference_run_cmd_default_command_or_options_cmd_ulink
# Without this, user could specify `elasticsearch -E x.y=z` but not
# `bin/elasticsearch -E x.y=z`
set -- "elasticsearch" "${@:2}"
# Use chroot to switch to UID 1000
exec chroot --userspec=1000 / "$@"
else
# User probably wants to exec something else, like /bin/bash
exec "$@"
fi
fi

# Parse Docker env vars to customize Elasticsearch
#
# e.g. Setting the env var cluster.name=testcluster
#
Expand Down Expand Up @@ -42,8 +79,8 @@ if bin/elasticsearch-plugin list -s | grep -q x-pack; then
# node at this step, we can't enforce the presence of this env
# var.
if [[ -n "$ELASTIC_PASSWORD" ]]; then
[[ -f config/elasticsearch.keystore ]] || bin/elasticsearch-keystore create
echo "$ELASTIC_PASSWORD" | bin/elasticsearch-keystore add -x 'bootstrap.password'
[[ -f config/elasticsearch.keystore ]] || run_as_other_user_if_needed "bin/elasticsearch-keystore" "create"
run_as_other_user_if_needed echo "$ELASTIC_PASSWORD" | bin/elasticsearch-keystore add -x 'bootstrap.password'
fi

# ALLOW_INSECURE_DEFAULT_TLS_CERT=true permits the use of a
Expand All @@ -59,4 +96,11 @@ if bin/elasticsearch-plugin list -s | grep -q x-pack; then
fi
fi

exec bin/elasticsearch "${es_opts[@]}"
if [[ "$(id -u)" == "0" ]]; then
# If requested and running as root, mutate the ownership of bind-mounts
if [[ -n "$TAKE_FILE_OWNERSHIP" ]]; then
chown -R 1000:0 /usr/share/elasticsearch/{data,logs}
fi
fi

run_as_other_user_if_needed /usr/share/elasticsearch/bin/elasticsearch "${es_opts[@]}"
127 changes: 75 additions & 52 deletions templates/Dockerfile.j2
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
################################################################################
# This Dockerfile was generated from the template at templates/Dockerfile.j2
#
# Beginning of multi stage Dockerfile
################################################################################

{% set ingest_plugins = 'ingest-user-agent ingest-geoip' %}
{% set version_tag = elastic_version -%}
{% set tarball = 'elasticsearch-%s.tar.gz' % elastic_version -%}
Expand All @@ -16,22 +21,26 @@
{% set url_root = 'https://artifacts.elastic.co/downloads/elasticsearch' -%}
{% endif -%}

{# Use a centos base that has openjdk-1.8.0.b141 pre-installed and excluded from yum.conf,
until potential issues with build 144 have been troubleshooted #}
FROM docker.elastic.co/elasticsearch/centos-base:7.3-jvm141
LABEL maintainer "Elastic Docker Team <docker@elastic.co>"
################################################################################
# Build stage 0 `prep_es_files`:
# Extract elasticsearch artifact
# Install required plugins
# Set gid=0 and make group perms==owner perms
################################################################################

FROM docker.elastic.co/elasticsearch/centos-base:7.3-jvm141 AS prep_es_files

ENV ELASTIC_CONTAINER true
ENV PATH /usr/share/elasticsearch/bin:$PATH
ENV JAVA_HOME /usr/lib/jvm/jre-1.8.0-openjdk

RUN yum update -y && yum install -y java-1.8.0-openjdk-headless unzip wget which && yum clean all
RUN yum install -y unzip

RUN groupadd -g 1000 elasticsearch && adduser -u 1000 -g 1000 -d /usr/share/elasticsearch elasticsearch
RUN groupadd -g 1000 elasticsearch && \
adduser -u 1000 -g 1000 -d /usr/share/elasticsearch elasticsearch

WORKDIR /usr/share/elasticsearch

USER elasticsearch
USER 1000

# Download and extract defined ES version.
RUN curl -fsSL {{ url_root }}/{{ tarball }} | \
Expand Down Expand Up @@ -62,60 +71,74 @@ RUN for PLUGIN in {{ ingest_plugins }}; do \
RUN for PLUGIN in x-pack {{ ingest_plugins }}; do \
{% else -%}
RUN for PLUGIN in {{ ingest_plugins }}; do \
{%- endif %}
elasticsearch-plugin install --batch "$PLUGIN"; \
done
{%- endif -%}
elasticsearch-plugin install --batch "$PLUGIN"; done
{%- endif %}

USER root

{# A fair bit of duplication here, but making line breaks and '\' consistent
coupled with jinja2 conditionals is complicated.
-#}
COPY --chown=1000:0 elasticsearch.yml log4j2.properties config/

{% if image_flavor == 'oss' -%}
COPY elasticsearch.yml log4j2.properties config/
COPY bin/es-docker bin/es-docker
{% if image_flavor == 'platinum' -%}
COPY --chown=1000:0 certgen_instances.yml log4j2.properties config/
COPY --chown=1000:0 x-pack/log4j2.properties config/x-pack/
RUN echo 'xpack.license.self_generated.type: trial' >>config/elasticsearch.yml
RUN bin/x-pack/certgen -s -in config/certgen_instances.yml --out config/certs.zip && \
unzip -d config/x-pack/ config/certs.zip && \
rm config/certs.zip config/certgen_instances.yml

RUN chown elasticsearch:elasticsearch \
config/elasticsearch.yml \
config/log4j2.properties \
bin/es-docker && \
chmod 0750 bin/es-docker
{% elif image_flavor == 'basic' -%}
COPY elasticsearch.yml log4j2.properties config/
RUN echo 'xpack.license.self_generated.type: basic' >>config/elasticsearch.yml
COPY x-pack/log4j2.properties config/x-pack/
COPY bin/es-docker bin/es-docker

RUN chown elasticsearch:elasticsearch \
config/elasticsearch.yml \
config/log4j2.properties \
config/x-pack/log4j2.properties \
bin/es-docker && \
chmod 0750 bin/es-docker
{% elif image_flavor == 'platinum' -%}
COPY elasticsearch.yml certgen_instances.yml log4j2.properties config/
COPY x-pack/log4j2.properties config/x-pack/
COPY bin/es-docker bin/es-docker
{% endif -%}

RUN echo 'xpack.license.self_generated.type: trial' >>config/elasticsearch.yml
USER 0

RUN chown elasticsearch:elasticsearch \
config/certgen_instances.yml \
config/elasticsearch.yml \
config/log4j2.properties \
config/x-pack/log4j2.properties \
bin/es-docker && \
chmod 0750 bin/es-docker
{% endif -%}
# Set gid to 0 for elasticsearch and make group permission similar to that of user
# This is needed, for example, for Openshift Open: https://docs.openshift.org/latest/creating_images/guidelines.html
# and allows ES to run with an uid
RUN chown -R elasticsearch:0 . && \
chmod -R g=u /usr/share/elasticsearch

USER elasticsearch
################################################################################
# Build stage 1 (the actual elasticsearch image):
# Copy elasticsearch from stage 0
# Add entrypoint
################################################################################

{% if image_flavor == 'platinum' -%}
RUN bin/x-pack/certgen -s -in config/certgen_instances.yml --out config/certs.zip && unzip -d config/x-pack/ config/certs.zip && rm config/certs.zip config/certgen_instances.yml
{% endif -%}
# Use a centos base that has openjdk-1.8.0.b141 pre-installed and excluded from yum.conf
# until potential issues with build 144 have been troubleshooted
FROM docker.elastic.co/elasticsearch/centos-base:7.3-jvm141
LABEL maintainer "Elastic Docker Team <docker@elastic.co>"

ENV ELASTIC_CONTAINER true
ENV PATH /usr/share/elasticsearch/bin:$PATH
ENV JAVA_HOME /usr/lib/jvm/jre-1.8.0-openjdk

RUN yum update -y && \
yum install -y java-1.8.0-openjdk-headless unzip wget which && \
yum clean all

CMD ["/bin/bash", "bin/es-docker"]
RUN groupadd -g 1000 elasticsearch && \
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \
chmod 0775 /usr/share/elasticsearch && \
chgrp 0 /usr/share/elasticsearch

WORKDIR /usr/share/elasticsearch

COPY --from=prep_es_files --chown=1000:0 /usr/share/elasticsearch /usr/share/elasticsearch
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay!!!!!!!


COPY --chown=1000:0 bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

# Openshift overrides USER and uses ones with randomly uid>1024 and gid=0
# Allow ENTRYPOINT (and ES) to run even with a different user
RUN chgrp 0 /usr/local/bin/docker-entrypoint.sh && \
chmod g=u /etc/passwd && \
chmod 0775 /usr/local/bin/docker-entrypoint.sh

EXPOSE 9200 9300

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
# Dummy overridable parameter parsed by entrypoint
CMD ["eswrapper"]

################################################################################
# End of multi-stage Dockerfile
################################################################################
2 changes: 1 addition & 1 deletion templates/docker-compose-fragment.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# running the test suite. Some of the settings are useful as "canaries", for
# making assertions about the configuration.
---
version: '2'
version: '2.1'
services:
elasticsearch1:
# Let Testinfra infer the container name by making the hostname the same.
Expand Down
11 changes: 8 additions & 3 deletions templates/docker-compose.yml.j2
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
# This file was generated from the template at templates/docker-compose.yml.j2
version: '2'
version: '2.1'
services:
elasticsearch1:
container_name: elasticsearch1
Expand All @@ -12,12 +12,16 @@ services:
esnet:
aliases:
- elasticsearch
# If unset, uses empty string which Docker ignores
# Defined in tests/conftest.py to test starting with a random uid
user: ${PROCESS_UID}
ulimits:
memlock:
soft: -1
hard: -1
# DATA_VOLUME{1,2} are defined in tests/conftest.py
volumes:
- esdata1:/usr/share/elasticsearch/data
- ${DATA_VOLUME1}:/usr/share/elasticsearch/data

elasticsearch2:
container_name: elasticsearch2
Expand All @@ -30,12 +34,13 @@ services:
mem_limit: 4g
networks:
- esnet
user: ${PROCESS_UID}
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata2:/usr/share/elasticsearch/data
- ${DATA_VOLUME2}:/usr/share/elasticsearch/data

volumes:
esdata1:
Expand Down
1 change: 1 addition & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
__pycache/
.cache/
.datadir*/
32 changes: 31 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from subprocess import run
import os
import pytest


Expand All @@ -13,14 +14,43 @@ def pytest_addoption(parser):
parser.addoption('--image-flavor', action='store',
help='Docker image flavor; the suffix used in docker-compose-<flavor>.yml')

# Bind-mount a user specified dir for the data dir
parser.addoption('--mount-datavolume1', action='store',
help='The host dir to be bind-mounted on /usr/share/elasticsearch/data for the first node')
# Bind-mount a user specified dir for the data dir
parser.addoption('--mount-datavolume2', action='store',
help='The host dir to be bind-mounted on /usr/share/elasticsearch/data for the second node')

# Let us override the Dockerfile's USER; akin to specifying `--user` in the docker run.
parser.addoption('--process-uid', action='store',
help='Used to override the Dockerfile\'s USER')


def pytest_configure(config):
# Named volumes used by default for persistence of each container
(datavolume1, datavolume2) = ("esdata1", "esdata2")
# Our default is not to override uid; empty strings for --user are ignored by Docker.
process_uid = ''
image_flavor = config.getoption('--image-flavor')
compose_flags = ('-f docker-compose-{0}.yml -f tests/docker-compose-{0}.yml up -d'.format(image_flavor)).split(' ')

if config.getoption('--single-node'):
compose_flags.append('elasticsearch1')

run(['docker-compose'] + compose_flags)
# Use a host dir for the data volume of Elasticsearch, if specified
if config.getoption('--mount-datavolume1'):
datavolume1 = config.getoption('--mount-datavolume1')
if config.getoption('--mount-datavolume2'):
datavolume2 = config.getoption('--mount-datavolume2')
if config.getoption('--process-uid'):
process_uid = config.getoption('--process-uid')

env_vars = os.environ
env_vars['DATA_VOLUME1'] = datavolume1
env_vars['DATA_VOLUME2'] = datavolume2
env_vars['PROCESS_UID'] = process_uid

run(['docker-compose'] + compose_flags, env=env_vars)


def pytest_unconfigure(config):
Expand Down
Loading