Skip to content

Dockerfile

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明,支持以#注释。Dockerfile由一行行命令语句组成。docker是按顺序执行dockerfile里的指令,每一个dockerfile的第一个非注释行的指令,必须是FROM指令,用于为文件构建过程中指定基准镜像,后续的指令运行于此基准镜像所提供的运行环境中。实践中,基准镜像可以是任何课用的镜像文件,默认情况下,docker不存在时,则会从docker registry(远端)上拉去镜像文件

指令

指令说明示例
FROM指定基础镜像FROM ubuntu:20.04
RUN执行命令(构建时)RUN apt-get update && apt-get install -y python3
COPY复制本地文件到镜像COPY ./app /app
ADD类似 COPY,支持 URL 和解压ADD https://example.com/file.tar.gz /tmp/
WORKDIR设置工作目录WORKDIR /app
ENV设置环境变量ENV PYTHON_VERSION=3.8
ARG定义构建时变量(构建后可被覆盖)ARG VERSION=latest
EXPOSE声明容器运行时监听的端口(不实际发布)EXPOSE 8080
CMD容器启动时的默认命令(可被覆盖)CMD ["python3", "app.py"]
ENTRYPOINT容器启动时的主要命令(不易被覆盖)ENTRYPOINT ["python3"]
VOLUME创建挂载点VOLUME /data
USER指定运行命令的用户USER appuser
LABEL添加元数据LABEL maintainer="dev@example.com"
HEALTHCHECK定义容器健康检查`HEALTHCHECK --interval=30s CMD curl -f http://localhost/
ONBUILD设置当本镜像被用作基础镜像时执行的指令ONBUILD COPY . /app
STOPSIGNAL设置停止容器时发送的系统信号STOPSIGNAL SIGTERM
SHELL覆盖默认的 shellSHELL ["/bin/bash", "-c"]

Dockerfile基本用法

dockerfile
FROM alpine
WORKDIR /
LABEL kevinchen
RUN apk add httpd
EXPOSE 80
ENTRYPOINT ["nginx"]


#FROM:定制的镜像都是基于FROM 的镜像,这里的 ngi需要的基础镜像。后续的操作都是基于 nginx。
#RUN:用于执行后面跟着的命令行命令。
#COPY 复制指令,从上下文目录中复制文件或者目录到容器里指定路径
#ADD 指令和 COPY 的使用格式一致.不同之处如下:
#ADD 的优点:在执行 <源文件> 为tar压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
#ADD 的缺点:在不解压的前提下,无法复制 tar 压令镜像构建缓存失效,从而可能会令镜像构建变得

构建

bash
# 基本构建
docker build -t myapp:latest .
# 指定Dockerfile
docker build -t myapp -f dockerfiles/Dockerfile.prod .

镜像优化

基础镜像优化

dockerfile
# 不推荐 - 过大
FROM ubuntu:latest

# 推荐 - 轻量级
FROM alpine:3.14
# 或
FROM debian:buster-slim
# 或
FROM gcr.io/distroless/static

多阶段构建

dockerfile
# 第一阶段:构建环境。可以是基础运行环境如编译安装Nginx等。
FROM golang:1.16 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# 第二阶段:运行环境
FROM alpine:3.14
WORKDIR /app
# 引入第一阶段基础环境
COPY --from=builder /app/myapp .
CMD ["./myapp"]

构建过程优化

dockerfile
# 合并 RUN 指令
RUN apt-get update && \
    apt-get install -y package1 package2 && \
    rm -rf /var/lib/apt/lists/*
    
# 不常变化的放前面
COPY package.json .
RUN npm install

# 常变化的放后面
COPY . .

# 清理不必要的文件
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        build-essential \
        && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
    
# 删除缓存和临时文件
RUN npm install && \
    npm cache clean --force

安全优化

dockerfile
# 使用非 root 用户
RUN addgroup -S appgroup && \
    adduser -S appuser -G appgroup

USER appuser

扫描镜像漏洞

bash
docker scan <image-name>

排除不需要的文件

.dockerignore 文件用于在构建 Docker 镜像时,指定哪些文件和目录应当被排除在镜像之外。它类似于 .gitignore 文件的作用,用来控制哪些文件不应被包含在 Docker 镜像中,从而减少镜像的大小,提高构建效率,并避免不必要的文件暴露。

tex
node_modules
.git
*.log
Dockerfile
.dockerignore

经典案例

构建Nginx镜像

配置文件

nginx.conf
ini
worker_processes  auto;
worker_rlimit_nofile 65535;
worker_cpu_affinity auto;

events {
    worker_connections  1024;
    use epoll;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
	log_format main '$remote_addr - $remote_user [$time_iso8601] '
                     '"$request_method $request_uri $server_protocol" '
                     '$status $body_bytes_sent $request_length '
                     '"$http_referer" "$http_user_agent" '
                     'rt=$request_time uct=$upstream_connect_time '
                     'uht=$upstream_header_time urt=$upstream_response_time '
                     'upstream=$upstream_addr upstream_status=$upstream_status '
                     'host=$host conn=$connection req=$connection_requests';
    sendfile        on;
    keepalive_timeout  65;
    client_header_timeout 20;
    client_body_timeout 20;
    server_tokens off;
    
    gzip  on;
    gzip_types text/plain text/javascript application/x-javascrip t text/css text/xml application/xml application/xml+rss;
    include conf.d/*.conf;
}
conf.d/www.conf
ini
server {
    listen 80;
    server_name example.com www.example.com;
    
    root /data/wwwroot/www;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;

        error_page 404 /404.html;
        error_page 403 /404.html;
    }
    location ~ \.(css|js|gif|jpg|jpeg|png|bmp|ico)$ {
        proxy_cache_valid 200 304 1h;
        proxy_cache_valid 404 1m;
        expires 3h;
    }
}

多阶段构建

dockerfile
FROM alpine:3.22.0 AS nginx-base
LABEL AUTHER="kevinchen"

# 设置变量
ENV NGINX_VERSION=1.26.3
ARG CONFIG="\
    --prefix=/usr/local/nginx \
    --conf-path=/usr/local/nginx/conf/nginx.conf \
    --sbin-path=/usr/local/nginx/sbin/nginx \
    --error-log-path=/usr/local/nginx/logs/error.log \
    --http-log-path=/usr/local/nginx/logs/access.log \
    --user=www \
    --group=www \
    --with-http_ssl_module \
    --with-http_realip_module \
    --with-http_addition_module \
    --with-http_sub_module \
    --with-http_dav_module \
    --with-http_flv_module \
    --with-http_mp4_module \
    --with-http_gunzip_module \
    --with-http_gzip_static_module \
    --with-http_random_index_module \
    --with-http_secure_link_module \
    --with-http_stub_status_module \
    --with-http_auth_request_module \
    --with-http_xslt_module=dynamic \
    --with-http_image_filter_module=dynamic \
    --with-http_geoip_module=dynamic \
    --with-threads \
    --with-stream \
    --with-stream_ssl_module \
    --with-stream_ssl_preread_module \
    --with-stream_realip_module \
    --with-stream_geoip_module=dynamic \
    --with-http_slice_module \
    --with-mail \
    --with-mail_ssl_module \
    --with-compat \
    --with-file-aio \
    --with-http_v2_module \
"
# 编译安装
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
	apk update && apk upgrade && apk add --no-cache \
    build-base \
    linux-headers \
    pcre-dev \
    zlib-dev \
    openssl-dev \
    libxml2-dev \
    libxslt-dev \
    gd-dev \
    geoip-dev \
    wget \
    tar && \
	wget https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz -O nginx.tar.gz && \
    tar -zxf nginx.tar.gz && \
    rm -f nginx.tar.gz && \
    cd nginx-$NGINX_VERSION && \
    ./configure $CONFIG --with-debug && \
    make -j$(getconf _NPROCESSORS_ONLN) && \
    make install && \
    rm -rf /nginx-$NGINX_VERSION && \
    rm -rf /var/cache/apk/*
    
COPY conf /usr/local/nginx/conf

# 第二阶段
FROM alpine:3.22.0

COPY --from=nginx-base /usr/local/nginx /usr/local/nginx
# COPY nginx.conf /usr/local/nginx/conf/nginx.conf

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
	apk add --no-cache \
    openssl-dev \
    pcre-dev \
   # libxml2-dev \
   # libxslt-dev \
   # gd-dev \
   # geoip-dev \
    zlib-dev && \
    ln -s /usr/local/nginx/sbin/nginx /usr/sbin/nginx && \
    addgroup -S www && \
	adduser -s /sbin/nologin -H -D -G www www && \
	chown -R www:www /usr/local/nginx && \
	rm -rf /var/cache/apk/* && \
	mkdir -p /data/wwwroot/www && \
	chown www:www /data/wwwroot/www

# 注意系统端口范围限制(1-999)
# USER www
WORKDIR /data/wwwroot/www
EXPOSE 80
CMD ["nginx","-g","daemon off;"]

构建和运行命令

bash
# 构建镜像
docker build -t nginx-web:alpine-1.0.0 .

# 运行容器
docker run -d \
  -p 8080:80 \
  -v /path/to/your/html:/data/wwwroot/www \
  --name my-nginx \
  nginx-web:v1.0.1

构建JDK镜像

Dockerfile

bash
cat > jdk-dockerfile << 'EOF'
FROM alpine:3.10
ARG JDK_VERSION=22.0.1

ADD https://download.java.net/java/GA/jdk${JDK_VERSION}/c7ec1332f7bb44aeba2eb341ae18aca4/8/GPL/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz /tmp/
RUN cd /tmp && \
	tar xf openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz -C /usr/local/ && \
	ln -s /usr/local/jdk-${JDK_VERSION}/bin/* /usr/local/bin/ && \
	rm -rf openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz
	
ENV JAVA_HOME=/usr/local/jdk-${JDK_VERSION}
ENV JRE_HOME=${JAVA_HOME}/jre
ENV CLASSPATH=.:${JAVA_HOME}/lib
ENV JAVA_PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin
ENV PATH=$PATH:${JAVA_PATH}
ENV TZ=Asia/Shanghai

WORKDIR /app
EXPOSE 8080

EOF

docker build -t openjdk:alpine-1.0 -f jdk-dockerfile .

多阶段构建

bash
# 构建阶段
FROM maven:3.6-openjdk-8 AS builder
WORKDIR /build
COPY . .
RUN mvn clean package -DskipTests

# 运行阶段
FROM openjdk:8-jdk-alpine
ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
COPY --from=builder /build/target/admin.jar app.jar
ENV JAVA_OPTS="-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom"

EXPOSE 8001
CMD ["sh", "-c", "java $JAVA_OPTS -jar yshop-admin-3.3.jar --spring.profiles.active=docker"]