前段时间对基于elasticsearch-5.6.4-alpine镜像创建容器时的CMD机制作了认真的研究与实践,这里记录下研究中的收获。
elasticsearch-5.6.4-alpine镜像Dockerfile内容
FROM openjdk:8-jre-alpine
# ensure elasticsearch user exists
RUN addgroup -S elasticsearch && adduser -S -G elasticsearch elasticsearch
# grab su-exec for easy step-down from root
# and bash for "bin/elasticsearch" among others
RUN apk add --no-cache 'su-exec>=0.2' bash
# https://artifacts.elastic.co/GPG-KEY-elasticsearch
ENV GPG_KEY 46095ACC8548582C1A2699A9D27D666CD88E42B4
WORKDIR /usr/share/elasticsearch
ENV PATH /usr/share/elasticsearch/bin:$PATH
ENV ELASTICSEARCH_VERSION 5.6.10
ENV ELASTICSEARCH_TARBALL="https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.10.tar.gz" \
ELASTICSEARCH_TARBALL_ASC="https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.10.tar.gz.asc" \
ELASTICSEARCH_TARBALL_SHA1="c4df8b240635484f09487a66707a9192bf97d3f9"
RUN set -ex; \
\
apk add --no-cache --virtual .fetch-deps \
ca-certificates \
gnupg \
openssl \
tar \
; \
\
wget -O elasticsearch.tar.gz "$ELASTICSEARCH_TARBALL"; \
\
if [ "$ELASTICSEARCH_TARBALL_SHA1" ]; then \
echo "$ELASTICSEARCH_TARBALL_SHA1 *elasticsearch.tar.gz" | sha1sum -c -; \
fi; \
\
if [ "$ELASTICSEARCH_TARBALL_ASC" ]; then \
wget -O elasticsearch.tar.gz.asc "$ELASTICSEARCH_TARBALL_ASC"; \
export GNUPGHOME="$(mktemp -d)"; \
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$GPG_KEY"; \
gpg --batch --verify elasticsearch.tar.gz.asc elasticsearch.tar.gz; \
rm -rf "$GNUPGHOME" elasticsearch.tar.gz.asc; \
fi; \
\
tar -xf elasticsearch.tar.gz --strip-components=1; \
rm elasticsearch.tar.gz; \
\
apk del .fetch-deps; \
\
mkdir -p ./plugins; \
for path in \
./data \
./logs \
./config \
./config/scripts \
; do \
mkdir -p "$path"; \
chown -R elasticsearch:elasticsearch "$path"; \
done; \
\
# we shouldn't need much RAM to test --version (default is 2gb, which gets Jenkins in trouble sometimes)
export ES_JAVA_OPTS='-Xms32m -Xmx32m'; \
if [ "${ELASTICSEARCH_VERSION%%.*}" -gt 1 ]; then \
elasticsearch --version; \
else \
# elasticsearch 1.x doesn't support --version
# but in 5.x, "-v" is verbose (and "-V" is --version)
elasticsearch -v; \
fi
COPY config ./config
VOLUME /usr/share/elasticsearch/data
COPY docker-entrypoint.sh /
EXPOSE 9200 9300
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["elasticsearch"]
可见Dockerfile中指定的ENTRYPOINT为/docker-entrypoint.sh
脚本docker-entrypoint.sh内容
#!/bin/bash
set -e
# Add elasticsearch as command if needed
if [ "${1:0:1}" = '-' ]; then
set -- elasticsearch "$@"
fi
# Drop root privileges if we are running elasticsearch
# allow the container to be started with `--user`
if [ "$1" = 'elasticsearch' -a "$(id -u)" = '0' ]; then
# Change the ownership of user-mutable directories to elasticsearch
for path in \
/usr/share/elasticsearch/data \
/usr/share/elasticsearch/logs \
; do
chown -R elasticsearch:elasticsearch "$path"
done
set -- su-exec elasticsearch "$@"
#exec su-exec elasticsearch "$BASH_SOURCE" "$@"
fi
# As argument is not related to elasticsearch,
# then assume that user wants to run his own process,
# for example a `bash` shell to explore this image
exec "$@"
可见,docker-entrypoint.sh脚本支持接收参数,比如/docker-entrypoint.sh xxx
脚本docker-entrypoint.sh内容解读
set -e:设定当脚本中执行的命令的返回值不等于0(返回值不等于0,表示命名执行出错),则退出shell,比如:@表示脚本接收的所有参数
{parameter:offset:length}的操作,假设
{1:0:1}结果为h
/docker-entrypoint.sh --help
执行后,此时$@值则为:elasticsearch --help
id -u:显示当前系统登录的用户IDelasticsearch:elasticsearch
,最后设置位置参数$@的值。
su-exec:切换用户和组后执行命令,Usage: su-exec user-spec command [args]
su-exec elasticsearch "@"的值所代表的命令
Docker的ENTRYPOINT机制是当基于镜像运行容器时,ENTRYPOINT指定的执行脚本是执行的起点,CMD是作为该执行脚本的位置参数传入,比如elasticsearch:5.6.4.-alpine镜像的Dockerfile为:
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["elasticsearch"]
docker run xxx elasticsearch:5.6.4-alpine
本质上是执行/docker-entrypoint.sh elasticsearch
若在docker run 时执行cmd,比如docker run xxx elasticsearch:5.6.4-alpine cmds
本质上是执行/docker-entrypoint.sh cmds
实践
-
docker run -itd --name es 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine elasticsearch
本质上等同于docker run -itd --name es 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine
-
docker run -itd --name es2 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine /usr/share/elasticsearch/bin/elasticsearch
/docker-entrypoint.sh /usr/share/elasticsearch/bin/elasticsearch
,由脚本内容可知,在启动的容器中以root用户运行了elasticsearch服务,因此会报上述错误。 -
比如想要映射elasticsearch的config目录,则此时需要更改config目录的属主与属组信息
docker run -itd --name elasticsearch -v /root/elasticsearch/config:/usr/share/elasticsearch/config 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine elasticsearch && chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config
这样写的方式是不合适的,run后面的命令本来是elasticsearch && chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config
,但此时则变成了以&&作为连接符的两个shell命令了。若以这种方式执行docker run -itd --name elasticsearch -v /root/elasticsearch/config:/usr/share/elasticsearch/config 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine "elasticsearch && chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config"
分析docker-entrypoint.sh内容可知,该脚本中有处理机制将root用户切换到elasticsearch用户来执行elasticsearch命令,因此在sh -c "$cmd" 中,cmd最好还是以docker-entrypoint.sh为执行起点。
docker run -itd --name elasticsearch -v /root/elasticsearch/config:/usr/share/elasticsearch/config 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine sh -c "chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config && /docker-entrypoint.sh elasticsearch"
- 首先更改config目录权限
chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config
- 然后运行elasticsearch服务
/docker-entrypoint.sh elasticsearch
-
docker run -itd --name elasticsearch -v /root/elasticsearch/config:/usr/share/elasticsearch/config -v /root/elasticsearch/data:/usr/share/elasticsearch/data -v /root/elasticsearch/logs:/usr/share/elasticsearch/logs -v /root/elasticsearch/plugins:/usr/share/elasticsearch/plugins 192.168.84.23:5000/library/elasticsearch:5.6.4-alpine sh -c "chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config /usr/share/elasticsearch/plugins && /docker-entrypoint.sh elasticsearch"
sh -c "chown -R elasticsearch:elasticsearch /usr/share/elasticsearch/config /usr/share/elasticsearch/plugins && /docker-entrypoint.sh elasticsearch"
, 命令中并没有显示的为data、logs目录更改属主与属组,其实这个操作已经在docker-entrypoint.sh脚本中完成了。
结语
上述为研究的过程与记录,这里创建容器的参数配置仅是为了研究CMD的需要,在实际的使用中创建elasticsearch容器的参数则需要根据自身情况来定义;欢迎批评、指正,望与大家一起学习,进步。