在 eclipse-temurin:17-jre 基础上构建支持 Maven 和 Docker 的镜像
下面是一个详细的步骤指南,用于在 eclipse-temurin:17-jre
基础镜像上构建一个支持 Maven 和 Docker 的镜像,并处理相关的权限和配置问题。
Dockerfile 内容
FROM eclipse-temurin:17-jre
RUN apt-get update && \ apt-get install -y --no-install-recommends \ curl \ gnupg \ apt-transport-https \ ca-certificates \ software-properties-common && \ rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \ apt-get update && \ apt-get install -y --no-install-recommends docker-ce-cli && \ rm -rf /var/lib/apt/lists/*
ARG MAVEN_VERSION=3.8.6 ARG USER_HOME_DIR="/root" ARG SHA=f790857f3b1f90ae8d16281f902c689e4f136ebe584aba45e4b1fa66c80cba826d3e0e52fdd04ed44b4c66f6d3fe3584a057c26dfcac544a60b301e6d0f91c26 ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries
RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ && echo "${SHA} /tmp/apache-maven.tar.gz" | sha512sum -c - \ && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ && rm -f /tmp/apache-maven.tar.gz \ && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
ARG USER_ID=1000 ARG GROUP_ID=1000 RUN addgroup --gid ${GROUP_ID} appgroup && \ adduser --disabled-password --gecos '' --uid ${USER_ID} --gid ${GROUP_ID} appuser && \ addgroup appuser docker
RUN mkdir -p /home/appuser/.m2 && \ mkdir -p /home/appuser/project && \ chown -R appuser:appgroup /home/appuser/.m2 && \ chown -R appuser:appgroup /home/appuser/project
WORKDIR /home/appuser/project
USER appuser
CMD ["/bin/bash"]
|
构建和使用说明
构建镜像
docker build -t temurin-maven-docker:17 .
|
运行容器并挂载外部配置
docker run -it --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /path/to/host/m2:/home/appuser/.m2 \ -v /path/to/project:/home/appuser/project \ -v /path/to/docker/config.json:/home/appuser/.docker/config.json \ temurin-maven-docker:17
|
使用示例
在容器内执行 Maven 和 Docker 命令:
mvn clean install docker build -t my-app .
|
关键注意事项
Docker 套接字挂载
- 挂载
/var/run/docker.sock
允许容器内使用宿主机的 Docker 守护进程
- 这意味着容器内的 Docker 客户端实际上是在宿主机上执行命令
用户和组权限处理
- 创建了非 root 用户
appuser
和组 appgroup
- 用户可以自定义用户和组的 ID(通过构建参数
USER_ID
和 GROUP_ID
)
- 用户被添加到
docker
组以获得 Docker 套接字的访问权限
- 确保挂载的目录有正确的所有权,避免权限问题
Maven 配置挂载
- 挂载宿主机的 Maven 配置目录(
~/.m2
)到容器内
- 这样可以重用本地 Maven 缓存,加速构建过程
- 同时保持一致的 Maven 配置
构建时可以自定义用户ID和组ID,以匹配宿主机用户:
docker build \ --build-arg USER_ID=$(id -u) \ --build-arg GROUP_ID=$(id -g) \ -t temurin-maven-docker:17 .
|
替代方案:使用 Docker-in-Docker (DinD)
如果需要完全隔离的 Docker 环境,可以考虑使用 Docker-in-Docker:
FROM docker:dind
RUN apk add --no-cache openjdk17-jre
|
但这种方案会增加镜像大小和运行时资源消耗。
这个方案提供了一个灵活的环境,既可以使用宿主机 Docker 守护进程的效率,又保持了适当的用户权限隔离和配置管理。
在 Alpine 基础镜像上安装完整 Docker 引擎
使用 apk add
方式安装完整的 Docker 引擎,而不是只安装 Docker CLI。这需要在基于 Alpine Linux 的镜像上进行,因为 eclipse-temurin:17-jre
是基于 Debian 的,不支持 apk
包管理器。使用 eclipse-temurin:17-jre-alpine
作为基础镜像,并安装完整的 Docker 引擎。
下面是一个完整的 Dockerfile,它在 eclipse-temurin:17-jre-alpine
基础上构建支持 Maven 和 Docker 的镜像,并允许挂载外部 Maven 仓库和配置文件。
Dockerfile 内容
FROM eclipse-temurin:17-jre-alpine
RUN apk update && \ apk add --no-cache \ curl \ bash \ git \ openssh-client \ shadow
RUN apk add --no-cache docker openrc && \ rc-update add docker boot
ARG MAVEN_VERSION=3.8.6 ARG USER_HOME_DIR="/home/appuser" ARG SHA=f790857f3b1f90ae8d16281f902c689e4f136ebe584aba45e4b1fa66c80cba826d3e0e52fdd04ed44b4c66f6d3fe3584a057c26dfcac544a60b301e6d0f91c26 ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries
RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ && echo "${SHA} /tmp/apache-maven.tar.gz" | sha512sum -c - \ && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ && rm -f /tmp/apache-maven.tar.gz \ && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
ARG USER_ID=1000 ARG GROUP_ID=1000 RUN addgroup -g ${GROUP_ID} appgroup && \ adduser -D -u ${USER_ID} -G appgroup appuser && \ adduser appuser docker
RUN mkdir -p /home/appuser/.m2 && \ mkdir -p /home/appuser/project && \ chown -R appuser:appgroup /home/appuser/.m2 && \ chown -R appuser:appgroup /home/appuser/project
RUN mkdir -p /mnt/m2/repository && \ mkdir -p /mnt/m2/conf && \ chown -R appuser:appgroup /mnt/m2
RUN mkdir -p /etc/docker && \ echo '{"storage-driver": "vfs"}' > /etc/docker/daemon.json
WORKDIR /home/appuser/project
COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh
USER appuser
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/bin/bash"]
|
Entrypoint 脚本
创建一个 entrypoint.sh
文件来处理外部挂载的 Maven 配置:
#!/bin/bash
if [ -d "/mnt/m2/conf" ] && [ "$(ls -A /mnt/m2/conf)" ]; then echo "Using external Maven configuration..." ln -sf /mnt/m2/conf/settings.xml /home/appuser/.m2/settings.xml ln -sf /mnt/m2/conf/settings-security.xml /home/appuser/.m2/settings-security.xml 2>/dev/null || true fi
if [ -d "/mnt/m2/repository" ]; then echo "Using external Maven repository..." ln -sf /mnt/m2/repository /home/appuser/.m2/repository fi
if [ "$1" = "dockerd" ]; then echo "Starting Docker daemon..." sudo dockerd > /var/log/docker.log 2>&1 & while ! docker info > /dev/null 2>&1; do echo "Waiting for Docker to start..." sleep 1 done echo "Docker daemon started." fi
exec "$@"
|
构建和使用说明
构建镜像
docker build -t temurin-maven-docker:17-alpine .
|
准备外部 Maven 配置和仓库
在宿主机上准备 Maven 配置和仓库目录:
mkdir -p /path/to/m2/conf mkdir -p /path/to/m2/repository
cp ~/.m2/settings.xml /path/to/m2/conf/
|
运行容器
docker run -it --rm --privileged \ -v /path/to/m2/conf:/mnt/m2/conf \ -v /path/to/m2/repository:/mnt/m2/repository \ -v /path/to/project:/home/appuser/project \ temurin-maven-docker:17-alpine
|
在容器内使用 Maven 和 Docker
容器启动后,Maven 会自动使用外部挂载的配置和仓库:
mvn clean install
docker version
|
关键注意事项
Maven 配置和仓库挂载
- 使用专门的挂载点
/mnt/m2/conf
和 /mnt/m2/repository
来挂载外部配置
- Entrypoint 脚本会创建符号链接,将外部配置连接到 Maven 的默认位置
- 这样可以保持容器内路径的一致性,同时使用外部配置
权限处理
- 容器内创建了非 root 用户
appuser
,UID 和 GID 可以通过构建参数自定义
- 所有相关目录都设置了正确的所有权
- 使用
sudo
来执行需要特权的操作(如启动 Docker 守护进程)
Docker 守护进程管理
- Entrypoint 脚本可以自动启动 Docker 守护进程(当第一个参数是 “dockerd” 时)
- 使用
vfs
存储驱动以避免在容器内使用 OverlayFS 的复杂性
自定义构建参数
构建时可以自定义用户 ID 和组 ID,以匹配宿主机用户:
docker build \ --build-arg USER_ID=$(id -u) \ --build-arg GROUP_ID=$(id -g) \ -t temurin-maven-docker:17-alpine .
|
安全考虑
- 虽然使用了非 root 用户,但容器仍然需要特权模式来运行 Docker 守护进程
- 考虑使用更细粒度的权限控制,如
--cap-add
选项
- 谨慎处理挂载的 Maven 配置,特别是如果包含敏感信息(如凭据)
使用示例
简单使用(只使用 Maven)
docker run -it --rm \ -v /path/to/m2/conf:/mnt/m2/conf \ -v /path/to/m2/repository:/mnt/m2/repository \ -v /path/to/project:/home/appuser/project \ temurin-maven-docker:17-alpine mvn clean install
|
完整使用(包括 Docker 守护进程)
docker run -it --rm --privileged \ -v /path/to/m2/conf:/mnt/m2/conf \ -v /path/to/m2/repository:/mnt/m2/repository \ -v /path/to/project:/home/appuser/project \ temurin-maven-docker:17-alpine dockerd mvn clean install
|
交互式使用
docker run -it --rm --privileged \ -v /path/to/m2/conf:/mnt/m2/conf \ -v /path/to/m2/repository:/mnt/m2/repository \ -v /path/to/project:/home/appuser/project \ temurin-maven-docker:17-alpine dockerd bash
|
这个方案提供了一个完整的开发环境,支持外部挂载的 Maven 配置和仓库,同时包含了完整的 Docker 引擎。通过符号链接和入口点脚本的巧妙组合,确保了容器内路径的一致性和外部配置的正确使用。
交互式构建容器镜像的方法
您希望先进入一个基础容器,然后在容器内交互式地安装所需软件,最后将容器保存为镜像。这是一个非常实用的方法,特别适合调试和测试不同的软件配置。以下是详细的步骤:
方法一:使用交互式会话手动安装
启动基础容器
docker run -it --name builder-container \ --privileged \ -v /tmp/m2:/mnt/m2 \ -v $(pwd):/mnt/project \ eclipse-temurin:17-jre-alpine /bin/sh
|
在容器内安装所需软件
进入容器后,您可以交互式地安装和配置软件:
apk update
apk add curl bash git openssh-client shadow vim
apk add docker openrc rc-update add docker boot
MAVEN_VERSION=3.8.6 cd /tmp curl -O https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz tar xzf apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven ln -s /opt/maven/bin/mvn /usr/bin/mvn
echo 'export MAVEN_HOME=/opt/maven' >> /etc/profile echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> /etc/profile
addgroup -g 1000 appgroup adduser -D -u 1000 -G appgroup appuser adduser appuser docker
mkdir -p /home/appuser/.m2 mkdir -p /home/appuser/project chown -R appuser:appgroup /home/appuser
mkdir -p /etc/docker echo '{"storage-driver": "vfs"}' > /etc/docker/daemon.json
|
将容器提交为镜像
在另一个终端中执行:
docker ps
docker commit builder-container my-custom-builder:latest
docker stop builder-container
docker rm builder-container
|
方法二:使用 Dockerfile 和交互式构建
创建基础 Dockerfile
FROM eclipse-temurin:17-jre-alpine
RUN apk update && \ apk add --no-cache \ curl \ bash \ git \ openssh-client \ shadow \ vim
WORKDIR /root
CMD ["tail", "-f", "/dev/null"]
|
构建基础镜像并运行
docker build -t interactive-builder .
docker run -it --name interactive-builder \ --privileged \ -v /tmp/m2:/mnt/m2 \ -v $(pwd):/mnt/project \ interactive-builder
|
在容器内安装软件
在容器内执行安装命令(与方法一相同)。
保存更改
docker commit interactive-builder my-final-builder:latest
docker stop interactive-builder docker rm interactive-builder
|
方法三:使用脚本记录安装过程
创建安装脚本
创建 install-software.sh
文件:
#!/bin/sh
apk add docker openrc rc-update add docker boot
MAVEN_VERSION=3.8.6 cd /tmp curl -O https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz tar xzf apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven ln -s /opt/maven/bin/mvn /usr/bin/mvn
echo 'export MAVEN_HOME=/opt/maven' >> /etc/profile echo 'export PATH=$MAVEN_HOME/bin:$PATH' >> /etc/profile
addgroup -g 1000 appgroup adduser -D -u 1000 -G appgroup appuser adduser appuser docker
mkdir -p /home/appuser/.m2 mkdir -p /home/appuser/project chown -R appuser:appgroup /home/appuser
mkdir -p /etc/docker echo '{"storage-driver": "vfs"}' > /etc/docker/daemon.json
|
启动容器并执行脚本
docker run -it --name scripted-builder \ --privileged \ -v $(pwd):/mnt \ eclipse-temurin:17-jre-alpine /bin/sh
chmod +x /mnt/install-software.sh /mnt/install-software.sh
|
提交镜像
docker commit scripted-builder scripted-builder:latest
|
方法四:使用多阶段构建和保存点
创建 Dockerfile 并设置保存点
FROM eclipse-temurin:17-jre-alpine AS base
RUN apk update && \ apk add --no-cache \ curl \ bash \ git \ openssh-client \ shadow \ vim
FROM base AS intermediate
RUN apk add docker openrc && \ rc-update add docker boot
FROM intermediate AS final
ARG MAVEN_VERSION=3.8.6 RUN cd /tmp && \ curl -O https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz && \ tar xzf apache-maven-${MAVEN_VERSION}-bin.tar.gz -C /opt && \ ln -s /opt/apache-maven-${MAVEN_VERSION} /opt/maven && \ ln -s /opt/maven/bin/mvn /usr/bin/mvn && \ rm apache-maven-${MAVEN_VERSION}-bin.tar.gz
ENV MAVEN_HOME=/opt/maven ENV PATH=$MAVEN_HOME/bin:$PATH
RUN addgroup -g 1000 appgroup && \ adduser -D -u 1000 -G appgroup appuser && \ adduser appuser docker && \ mkdir -p /home/appuser/.m2 /home/appuser/project && \ chown -R appuser:appgroup /home/appuser
RUN mkdir -p /etc/docker && \ echo '{"storage-driver": "vfs"}' > /etc/docker/daemon.json
WORKDIR /home/appuser/project USER appuser
|
构建到特定阶段
docker build --target base -t my-builder:base .
docker build --target intermediate -t my-builder:intermediate .
docker build --target final -t my-builder:final .
|
使用最终镜像
无论使用哪种方法,最终都可以使用构建的镜像:
docker run -it --rm --privileged \ -v /path/to/m2/conf:/mnt/m2/conf \ -v /path/to/m2/repository:/mnt/m2/repository \ -v /path/to/project:/home/appuser/project \ my-builder:final /bin/sh
|
注意事项
权限问题:确保挂载的目录有正确的权限,特别是当使用非 root 用户时。
容器状态:使用 docker commit
时,容器中所有更改(包括临时文件)都会被保存。建议在提交前清理不必要的文件。
可重复性:交互式方法不适合生产环境,因为它不可重复。对于生产环境,建议使用 Dockerfile。
镜像大小:交互式安装可能会留下不必要的缓存文件,增加镜像大小。可以在安装完成后运行清理命令。
Docker 守护进程:在容器内运行 Docker 守护进程需要特权模式,这有安全风险,只应在开发环境中使用。
保存工作:如果您在容器中做了很多配置工作,定期提交容器为镜像,以免丢失工作进度。
这种方法非常适合探索和测试不同的软件配置,然后再将确认的配置转换为可重复的 Dockerfile。