上一期我们介绍了几种常见的构建镜像方式,并给出了功能对比、决策树等作为选型参考。本期我们将演示如何使用 Alpine 构建一个 Redis 镜像。
Alpine 系统使用 apk 包管理工具,文中相关 apk 使用技巧不再赘述。
我们将构建镜像 Dockerfile 中的几个部分单独讲解,最后提供一个完整的 Demo。重点在如何使用 Alpine,Redis 镜像构建步骤此文不详细介绍。
文章大纲:
首先,为了在本地更流畅的部署,可以更换更流畅的镜像源。我们这里将 apk 的软件包镜像源修改为中科大镜像源。
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
构建镜像时,只一些只有构建时才需要的文件/软件。比如 gcc 只有编译时候才会用到;wget 用于下载文件;传统 yum/apt 需要用完后手动卸载软件和依赖,还要自己清理临时文件。而 Alpine 的 apk 可以用起来更方便。
- –no-cache:不使用缓存目录缓存,装完也不保留缓存。
- –virtual .build-deps:不真实安装,将软件安装到虚拟包 .build-deps 中,方便 del 时一口气卸载所有包。
- –no-network:不使用网络。
RUN set -eux; \
\
apk add --no-cache --virtual .build-deps \
coreutils \
wget \
dpkg-dev dpkg \
gcc \
linux-headers \
make \
musl-dev \
openssl-dev ;\
#########################################
#
# 构建部分
#
#########################################
apk del --no-network .build-deps;
通常我们启动镜像前需要使用 shell 调整下启动状态,做准备。比如:生成配置文件,检查路径挂载等等。然后再拉起服务,但会遇到两个问题:
解决方案
说明: 这里使用 su-exec 而不使用 sudo 的原因是传统 sudo 会新创建出一个进程运行服务,导致 PID 不能为 1,进而导致无法接收到 signal 信号而正常关闭服务。
Dockerfile 内容如下:
FROM alpine:3.16
创建用户
RUN addgroup -S -g 1000 redis && adduser -S -G redis -u 999 redis
安装 su-exec 命令
RUN apk add --no-cache 'su-exec>=0.2'
############################################################
#
其它部署 内容
#
############################################################
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
/usr/bin/entrypoint.sh
内容如下:
#!/bin/sh
set -e
############################################################
#
启动准备脚本内容
#
############################################################
最终启动命令
exec su-exec redis redis-server "$@"
由于 Alpine 系统的时区是 UTC,作者需要设置为上海。可以用如下方式调整时区。
RUN apk add --no-cache tzdata ;\
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
如果对镜像简化要求较高,可以在调整时区后删除时区包,这会给以后修改时区带来困难。
[En]
If there is a high requirement for image simplification, you can delete the time zone package after adjusting the time zone, which will make it difficult to modify the time zone in the future.
RUN apk add --no-cache --virtual .build-tzdata tzdata ; \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\
apk del .build-tzdata;
我们将以上部分整理为一个完整的 Redis 镜像构建 Demo (Redis 版本 6.2.7)。
Dockerfile 文件
FROM alpine:3.16
RUN addgroup -S -g 1000 redis && adduser -S -G redis -u 999 redis
改为中科大镜像源
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
调整时区
RUN apk add --no-cache --virtual .build-tzdata tzdata ; \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\
apk del .build-tzdata;
RUN apk add --no-cache \
'su-exec>=0.2' \
tzdata
ENV REDIS_VERSION 6.2.7
ENV REDIS_DOWNLOAD_URL http://download.redis.io/releases/redis-6.2.7.tar.gz
ENV REDIS_DOWNLOAD_SHA b7a79cc3b46d3c6eb52fa37dde34a4a60824079ebdfb3abfbbfa035947c55319
编译 Redis 相关镜像
RUN set -eux; \
\
# 使用 --virtual 将软件包虚拟安装到 .build-deps 空间下, 之后会被打包
# 使用 --no-cache 不会留下缓存文件
apk add --no-cache --virtual .build-deps \
coreutils \
dpkg-dev dpkg \
gcc \
linux-headers \
make \
musl-dev \
openssl-dev \
wget \
; \
\
wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL"; \
echo "$REDIS_DOWNLOAD_SHA *redis.tar.gz" | sha256sum -c -; \
mkdir -p /usr/src/redis; \
tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1; \
rm redis.tar.gz; \
\
# 禁用 Redis 的保护模式
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' /usr/src/redis/src/config.c; \
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' /usr/src/redis/src/config.c; \
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' /usr/src/redis/src/config.c; \
\
# 获取CPU架构
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
extraJemallocConfigureFlags="--build=$gnuArch"; \
dpkgArch="$(dpkg --print-architecture)"; \
case "${dpkgArch##*-}" in \
amd64 | i386 | x32) extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-page=12" ;; \
*) extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-page=16" ;; \
esac; \
extraJemallocConfigureFlags="$extraJemallocConfigureFlags --with-lg-hugepage=21"; \
# 根据架构调整 Makefile 文件
grep -F 'cd jemalloc && ./configure ' /usr/src/redis/deps/Makefile; \
sed -ri 's!cd jemalloc && ./configure !&'"$extraJemallocConfigureFlags"' !' /usr/src/redis/deps/Makefile; \
grep -F "cd jemalloc && ./configure $extraJemallocConfigureFlags " /usr/src/redis/deps/Makefile; \
\
# 编译 Redis
export BUILD_TLS=yes; \
make -C /usr/src/redis -j "$(nproc)" all; \
make -C /usr/src/redis install; \
\
# Redis
serverMd5="$(md5sum /usr/local/bin/redis-server | cut -d' ' -f1)"; export serverMd5; \
find /usr/local/bin/redis* -maxdepth 0 \
-type f -not -name redis-server \
-exec sh -eux -c ' \
md5="$(md5sum "$1" | cut -d" " -f1)"; \
test "$md5" = "$serverMd5"; \
' -- '{}' ';' \
-exec ln -svfT 'redis-server' '{}' ';' \
; \
\
rm -r /usr/src/redis; \
\
runDeps="$( \
scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \
| tr ',' '\n' \
| sort -u \
| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
)"; \
apk add --no-network --virtual .redis-rundeps $runDeps; \
# 基于前面安装卸载 .build-deps 虚拟包
apk del --no-network .build-deps; \
\
redis-cli --version; \
redis-server --version
RUN mkdir /data && chown redis:redis /data
VOLUME /data
WORKDIR /data
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD ["redis-server"]
Dockerfile 中 ENTRYPOINT 所使用的 docker-entrypoint.sh
文件内容如下。
#!/bin/sh
set -e
当第一个参数使用 -f
or --some-option
这样参数时
或以 .conf
结尾,
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
# 将第一个参数设为 redis-server, 方便后面启动
set -- redis-server "$@"
fi
当启动程序是 'redis-server' 且 uid = 0 时, 即 Root 启动
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
# 1. 调整数据目录属主为 Redis
find . \! -user redis -exec chown redis '{}' +
# 2. 使用 exec su-exec 组合启动redis-server, 会将当前 PID 传递给 redis-server, 并切换用户为 redis
# 注意: 使用 exec 后, 后面脚本将不会执行
exec su-exec redis "$0" "$@"
fi
当用户使用其它用户启动, 或者非 redis-server 服务启动适时会执行如下代码
修改默认权限 为 700
um="$(umask)"
if [ "$um" = '0022' ]; then
umask 0077
fi
启动服务
exec "$@"
我们从几个案例中可以看出,Alpine 为容器做了很多量身打造的特性。比如 apk 的不使用缓存/虚拟包特性,容器构建带来极大方便。而 apk 所包含 su-exec 包也比 sudo 更适合容器。
不仅如此,传统镜像精简版镜像的 ps/netstat/ifconfig/ip/vi
等命令会被删掉,导致后期运维困难。而仅 5M 的 Alpine 却都自带。传统镜像去安装任何一个命令可能都要比 Alpine 本身都要大,还会增添 cve 漏洞风险,而 Alpine 几乎无需考虑这个问题。
所以,如果没有用到 glibc 的服务 Alpine 将是一个非常不错的选择。而用了 glibc 测试过兼容性问题后,Alpine 也是非常不错选择。
作者:安树博 青云科技 PaaS 中间件开发工程师
从事 PaaS 中间件服务(Redis/Memcached 等)开发工作,热衷对 NoSQL 数据库领域内技术的学习与研究Original: https://www.cnblogs.com/radondb/p/16627585.html
Author: RadonDB
Title: 容器化 | 使用 Alpine 构建 Redis 镜像
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/504938/
转载文章受原作者版权保护。转载请注明原作者出处!