基於Dockerfile構建容器鏡像的最佳實踐


1、背景概述

容器鏡像是容器化落地轉型的第一步,總結幾點需要做鏡像優化的原因

隨着應用容器化部署的大規模遷移以及版本迭代的加快,優化基礎設施之docker鏡像主要有以下目的

  • 縮短部署時的鏡像下載時間

  • 提升安全性,減少可供攻擊的目標

  • 減少故障恢復時間

  • 節省存儲開銷

2、為什么鏡像會這么大

這里簡要分析了幾個典型的Repo,總結了現有Docker鏡像較大的幾個原因

2.1 基礎鏡像過大

舉例:倉庫A,制作出來的鏡像大小9.67GB

用到的基礎鏡像: 鏡像大小8.72GB

逆向分析了一下,為啥基礎鏡像還這么大?結果就不用多說了0.0

2.2 基礎鏡像過大,而且找不到了

舉例:倉庫B,制作出來的鏡像大小22.7GB

用到的基礎鏡像: 404 not found,沒錯,找不到了0.0

2.3 .git目錄(非必要目錄)

這個問題更多內容可以參考我之前的文章 Git目錄為什么這么大

舉例:倉庫C,代碼大小795MB

其中.git目錄大小225MBdockerfile中的指令如下(全部添加到了鏡像中)

ADD . /app/startapp/

其中還包含了d目錄大小約300MB,是否需要使用不得而知,但目測不需要使用,僅為測試數據

d
├── [ 503]  test_421.json
├── [ 483]  test_havalB9.json
...
├── [ 484]  test_144.json
├── [ 104]  .gitmodules
├── [ 122]  .idea
├── [   0]  __init__.py
├── [ 11M]  164103.zip
├── [108M]  test_180753.csv
├── [ 68M]  test_180753.txt
...
└── [ 335]  README.md

以上其實都不需要提交到鏡像中制作成鏡像

2.4 Dockerfile本身有其他問題

這個原因不言而喻,不是專業的人寫的Dockerfile可能都有一定的優化空間,只是暫時沒關注這些細節而已

例如,放任各路repo研發自行寫Dockerfile,沒有一定的標准,前期可能無所謂,到后期問題就慢慢浮現了

正所謂《能用就行》~

3、Dockerfile如何優化

3.1 從哪里入手

優化docker鏡像應該從鏡像分層概念入手

3.1.1 舉個栗子

一個實際的例子

nginx:alpine鏡像 23.2MB

# docker history nginx:alpine
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
b46db85084b8   9 days ago    /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B        
<missing>      9 days ago    /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B        
<missing>      9 days ago    /bin/sh -c #(nop)  EXPOSE 80                    0B        
<missing>      9 days ago    /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B        
<missing>      9 days ago    /bin/sh -c #(nop) COPY file:09a214a3e07c919a…   4.61kB    
<missing>      9 days ago    /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB    
<missing>      9 days ago    /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0…   1.96kB    
<missing>      9 days ago    /bin/sh -c #(nop) COPY file:65504f71f5855ca0…   1.2kB     
<missing>      9 days ago    /bin/sh -c set -x     && addgroup -g 101 -S …   17.6MB    
<missing>      9 days ago    /bin/sh -c #(nop)  ENV PKG_RELEASE=1            0B        
<missing>      9 days ago    /bin/sh -c #(nop)  ENV NJS_VERSION=0.7.0        0B        
<missing>      9 days ago    /bin/sh -c #(nop)  ENV NGINX_VERSION=1.21.4     0B        
<missing>      9 days ago    /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      10 days ago   /bin/sh -c #(nop) ADD file:762c899ec0505d1a3…   5.61MB

python:alpine鏡像 45.5MB

# docker history python:alpine
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
382a63bb2f25   10 days ago   /bin/sh -c #(nop)  CMD ["python3"]              0B        
<missing>      10 days ago   /bin/sh -c set -ex;   wget -O get-pip.py "$P…   8.31MB    
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PYTHON_GET_PIP_SHA256…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PYTHON_GET_PIP_URL=ht…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PYTHON_SETUPTOOLS_VER…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PYTHON_PIP_VERSION=21…   0B        
<missing>      10 days ago   /bin/sh -c cd /usr/local/bin  && ln -s idle3…   32B       
<missing>      10 days ago   /bin/sh -c set -ex  && apk add --no-cache --…   29.8MB    
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PYTHON_VERSION=3.10.0    0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV GPG_KEY=A035C8C19219B…   0B        
<missing>      10 days ago   /bin/sh -c set -eux;  apk add --no-cache   c…   1.82MB    
<missing>      10 days ago   /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PATH=/usr/local/bin:/…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      10 days ago   /bin/sh -c #(nop) ADD file:762c899ec0505d1a3…   5.61MB

實際存儲

# docker inspect nginx:alpine| jq '.[0]|{GraphDriver}'             
{
  "GraphDriver": {
    "Data": {
      "LowerDir": "/data/docker-overlay2/overlay2/3d.../diff:/data/docker-overlay2/overlay2/ae.../diff:/data/docker-overlay2/overlay2/ea.../diff:/data/docker-overlay2/overlay2/29.../diff:/data/docker-overlay2/overlay2/5e.../diff",
      "MergedDir": "/data/docker-overlay2/overlay2/b7.../merged",
      "UpperDir": "/data/docker-overlay2/overlay2/b7.../diff",
      "WorkDir": "/data/docker-overlay2/overlay2/b7.../work"
    },
    "Name": "overlay2"
  }
}

分層概念的描述

鏡像解決了應用運行及環境的打包問題,實際應用中應用都是基於同一個rootfs來打包和迭代的,但並不是每個rootfs都會多份,實際上docker利用了存儲驅動AUFSdevicemapperoverlayoverlay2的存儲技術實現了分層

例如上面查看一個docker鏡像會發現這些層

  • LowerDir:鏡像層

  • MergedDir:整合了lower層和upper讀寫層顯示出來的視圖

  • UpperDir:讀寫層

  • WorkDir:中間層,對Upper層的寫入,先寫入WorkDir,再移入UpperDir

3.1.2 Copy on write

Docker第一次啟動一個容器時,初始的讀寫層是空的,當文件系統發生變化時,這些變化都會應用到這一層之上。比如,如果想修改一個文件,這個文件首先會從該讀寫層下面的只讀層復制到該讀寫層。由此,該文件的只讀版本依然存在於只讀層,只是被讀寫層的該文件副本所隱藏,該機制則被稱之為寫時復制

3.1.3 UnionFS

把多個目錄(也叫分支)內容聯合掛載到同一個目錄下,而目錄的物理位置是分開的

一個直觀的效果,第一次拉取一個nginx:1.15版本鏡像,再次拉取nginx:1.16鏡像,速度要快很多

3.2 方案

了解了鏡像大小的主要構成之后,就很容易知道從哪些方向入手減少鏡像大小了

3.2.1 減少鏡像層數

鏡像層數的增加,對Dockerfile來說主要在於RUN指令出現的次數,因此,合並RUN指令可以大大減少鏡像層數

舉個栗子:

合並前,三層

RUN apk add tzdata
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo "Asia/Shanghai" > /etc/timezone

合並后,一層

RUN apk add tzdata \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
    && echo "Asia/Shanghai" > /etc/timezone

3.2.2 減少每層鏡像大小

3.2.2.1 選用更小的基礎鏡像
  • scratch:空鏡像,又叫鏡像之父!任何鏡像都需要有一個基礎鏡像,那么問題來了,就好比是先有雞還是先有蛋的問題,基礎鏡像的“祖宗”是什么呢?能不能在構建時不以任何鏡像為基礎呢?答案是肯定的,可以選用scratch,具體就不展開了,可以參考:baseimages,使用scratch鏡像的例子pause
  • busybox:對比scratch,多了常用的linux工具等
  • alpine:多了包管理工具apk
3.3.2.2 多階段構建

多階段構建非常適用於編譯性語言,簡單來說就是允許一個Dockerfile中出現多條FROM指令,只有最后一條FROM指令中指定的基礎鏡像作為本次構建鏡像的基礎鏡像,其它的階段都可以認為是只為中間步驟

FROM … AS …COPY --from組合使用

例如java鏡像,鏡像大小812MB

FROM centos AS jdk
COPY jdk-8u231-linux-x64.tar.gz /usr/local/src
RUN cd /usr/local/src && \
    tar -xzvf jdk-8u231-linux-x64.tar.gz -C /usr/local

使用多階段構建,鏡像大小618MB

FROM centos AS jdk
COPY jdk-8u231-linux-x64.tar.gz /usr/local/src
RUN cd /usr/local/src && \
    tar -xzvf jdk-8u231-linux-x64.tar.gz -C /usr/local

FROM centos
COPY --from=jdk /usr/local/jdk1.8.0_231 /usr/local
3.3.2.3 忽略文件

構建上下文build context,“上下文” 意為和現在這個工作相關的周圍環境

docker build時當前的工作目錄,不管構建時有沒有用到當前目錄下的某些文件及目錄,默認情況下這個上下文中的文件及目錄都會作為構建上下文內容發送給Docker Daemon

docker build開始執行時,控制台會輸出Sending build context to Docker daemon xxxMB,這就表示將當前工作目錄下的文件及目錄都作為了構建上下文

前面提到可以在RUN指令中添加--no-cache不使用緩存,同樣也可以在執行docker build命令時添加該指令以在鏡像構建時不使用緩存

構建上下文中,使用.dockerignore 文件在構建時就可以避免將本地模塊以及調試日志被拷貝進入到Docker鏡像中,這和git版本控制的.gitignore很類似

3.3.2.4 遠程下載

使用遠程下載代替ADD可以減少鏡像大小

RUN curl -s http://192.168.1.1/repository/tools/jdk-8u241-linux-x64.tar.gz | tar -xC /opt/
3.3.2.5 拆分COPY

例如一個COPY指令的目錄下A4個子目錄AA/BB/CC/DDCOPY,但常變化的只有一個BB

這個時候拆分COPY會更快

COPY A/AA /app/A/AA
COPY A/BB /app/A/BB
COPY A/CC /app/A/CC
COPY A/DD /app/A/DD
3.3.2.6 構建時掛載

構建時掛載(擴展功能

配置

  • 修改docker啟動參數,添加--experimental
  • dockerfile頭部添加# syntax=docker/dockerfile:1.1.1-experimental

使用

  • 掛載本地golang緩存
# syntax = docker/dockerfile:experimental
FROM golang
...
RUN --mount=type=cache,target=/root/.cache/go-build go build ...
  • 掛載cache目錄
# syntax = docker/dockerfile:experimental
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \
  apt update && apt install -y gcc
  • 掛載某些憑據
# syntax = docker/dockerfile:experimental
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials aws s3 cp s3://... ...

等等

3.3.2.7 構建后清理
  • 刪除壓縮包
  • 清理安裝緩存
    • --no-cache
    • rm -rf /var/lib/apt/lists/*
    • rm -rf /var/cache/yum/*
3.3.2.8 鏡像壓縮

exportimport組合進行壓縮鏡像(壓縮效果不是很明顯)

這種方法不好的就是會丟失一部分鏡像信息

# docker run -d --name nginx nginx:alpine
# docker export nginx |docker import - nginx:alpine2
sha256:dd6a3cf822ac3c3ad3e7f7b31675cd8cd99a6f80e360996e04da6fc2f3b98cb5
# docker history nginx:alpine
IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
b46db85084b8   10 days ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  STOPSIGNAL SIGQUIT           0B        
<missing>      10 days ago   /bin/sh -c #(nop)  EXPOSE 80                    0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENTRYPOINT ["/docker-entr…   0B        
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:09a214a3e07c919a…   4.61kB    
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7…   1.04kB    
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0…   1.96kB    
<missing>      10 days ago   /bin/sh -c #(nop) COPY file:65504f71f5855ca0…   1.2kB     
<missing>      10 days ago   /bin/sh -c set -x     && addgroup -g 101 -S …   17.6MB    
<missing>      10 days ago   /bin/sh -c #(nop)  ENV PKG_RELEASE=1            0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV NJS_VERSION=0.7.0        0B        
<missing>      10 days ago   /bin/sh -c #(nop)  ENV NGINX_VERSION=1.21.4     0B        
<missing>      10 days ago   /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B        
<missing>      10 days ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      10 days ago   /bin/sh -c #(nop) ADD file:762c899ec0505d1a3…   5.61MB    
# docker history nginx:alpine2
IMAGE          CREATED          CREATED BY   SIZE      COMMENT
dd6a3cf822ac   40 seconds ago                23MB      Imported from -
# docker images|grep nginx
nginx                                                                                                               alpine2                     dd6a3cf822ac   54 seconds ago   23MB
nginx                                                                                                               alpine                      b46db85084b8   10 days ago      23.2MB

3.3 樣例

3.3.1 go 樣例

樣例一

kubeadm安裝的k8s集群,kube-apiserver鏡像的Dockerfile是利用bazel編譯工具編譯的

bazel build ...
LABEL maintainers=Kubernetes Authors
LABEL description=go based runner for distroless scenarios
WORKDIR /
COPY /workspace/go-runner . # buildkit
ENTRYPOINT ["/go-runner"]
COPY file:2e904ea733ba0ded2a99947847de31414a19d83f8495dd8c1fbed3c70bf67a22 in /usr/local/bin/kube-apiserver

代碼目錄28M(包含.git目錄20.5M)

鏡像大小122MB

樣例二

開源編排引擎CadenceDockerfile

ARG TARGET=server

# Can be used in case a proxy is necessary
ARG GOPROXY

# Build tcheck binary
FROM golang:1.17-alpine3.13 AS tcheck

WORKDIR /go/src/github.com/uber/tcheck

COPY go.* ./
RUN go build -mod=readonly -o /go/bin/tcheck github.com/uber/tcheck

# Build Cadence binaries
FROM golang:1.17-alpine3.13 AS builder

ARG RELEASE_VERSION

RUN apk add --update --no-cache ca-certificates make git curl mercurial unzip

WORKDIR /cadence

# Making sure that dependency is not touched
ENV GOFLAGS="-mod=readonly"

# Copy go mod dependencies and build cache
COPY go.* ./
RUN go mod download

COPY . .
RUN rm -fr .bin .build

ENV CADENCE_RELEASE_VERSION=$RELEASE_VERSION

# bypass codegen, use committed files.  must be run separately, before building things.
RUN make .fake-codegen
RUN CGO_ENABLED=0 make copyright cadence-cassandra-tool cadence-sql-tool cadence cadence-server cadence-bench cadence-canary


# Download dockerize
FROM alpine:3.11 AS dockerize

RUN apk add --no-cache openssl

ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && echo "**** fix for host id mapping error ****" \
    && chown root:root /usr/local/bin/dockerize


# Alpine base image
FROM alpine:3.11 AS alpine

RUN apk add --update --no-cache ca-certificates tzdata bash curl

# set up nsswitch.conf for Go's "netgo" implementation
# https://github.com/gliderlabs/docker-alpine/issues/367#issuecomment-424546457
RUN test ! -e /etc/nsswitch.conf && echo 'hosts: files dns' > /etc/nsswitch.conf

SHELL ["/bin/bash", "-c"]


# Cadence server
FROM alpine AS cadence-server

ENV CADENCE_HOME /etc/cadence
RUN mkdir -p /etc/cadence

COPY --from=tcheck /go/bin/tcheck /usr/local/bin
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin
COPY --from=builder /cadence/cadence-cassandra-tool /usr/local/bin
COPY --from=builder /cadence/cadence-sql-tool /usr/local/bin
COPY --from=builder /cadence/cadence /usr/local/bin
COPY --from=builder /cadence/cadence-server /usr/local/bin
COPY --from=builder /cadence/schema /etc/cadence/schema

COPY docker/entrypoint.sh /docker-entrypoint.sh
COPY config/dynamicconfig /etc/cadence/config/dynamicconfig
COPY config/credentials /etc/cadence/config/credentials
COPY docker/config_template.yaml /etc/cadence/config
COPY docker/start-cadence.sh /start-cadence.sh

WORKDIR /etc/cadence

ENV SERVICES="history,matching,frontend,worker"

EXPOSE 7933 7934 7935 7939
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD /start-cadence.sh


# All-in-one Cadence server
FROM cadence-server AS cadence-auto-setup

RUN apk add --update --no-cache ca-certificates py-pip mysql-client
RUN pip install cqlsh

COPY docker/start.sh /start.sh

CMD /start.sh


# Cadence CLI
FROM alpine AS cadence-cli

COPY --from=tcheck /go/bin/tcheck /usr/local/bin
COPY --from=builder /cadence/cadence /usr/local/bin

ENTRYPOINT ["cadence"]

# Cadence Canary
FROM alpine AS cadence-canary

COPY --from=builder /cadence/cadence-canary /usr/local/bin
COPY --from=builder /cadence/cadence /usr/local/bin

CMD ["/usr/local/bin/cadence-canary", "--root", "/etc/cadence-canary", "start"]

# Cadence Bench
FROM alpine AS cadence-bench

COPY --from=builder /cadence/cadence-bench /usr/local/bin
COPY --from=builder /cadence/cadence /usr/local/bin

CMD ["/usr/local/bin/cadence-bench", "--root", "/etc/cadence-bench", "start"]

# Final image
FROM cadence-${TARGET}

代碼目錄85.4M(包含.git目錄57.7M)

鏡像大小135.69MB

3.3.2 py 樣例

FROM python:3.4

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        postgresql-client \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .

EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

代碼目錄275M(包含.git目錄222M)

鏡像大小436MB

4、除了這些優化還可以做什么

4.1 設置字符集

Dockerfile中設置通用的字符集

# Set lang
ENV LANG "en_US.UTF-8"

4.2 時區校正

這個問題更多內容可以參考我之前的文章 k8s環境下處理容器時間問題的多種姿勢

Dockerfile中設置通用的時區

# Set timezone
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
		 && echo "Asia/Shanghai" > /etc/timezone

4.3 進程管理

docker容器運行時,默認會以Dockerfile中的ENTRYPOINTCMD作為PID1的主進程,這個進程存在的目的,通俗來說需要做的就是將容器"夯住",一旦這個進程不存在了,那么容器就會退出

除此之外,這個主進程還有一個重要的作用就是管理“僵屍進程”

一個比較官方的定義,“僵屍進程”是指完成執行(通過exit系統調用,或運行時發生致命錯誤或收到終止信號所致),但在操作系統的進程表中仍然存在其進程控制塊,處於"終止狀態"的進程。

清理“僵屍進程”的思路主要有

  • 將父進程中對SIGCHLD信號的處理函數設為SIG_IGN(忽略信號);
  • fork兩次並殺死一級子進程,令二級子進程成為孤兒進程而被init所“收養”、清理

目前可以實現的開源方案

  • Tini
    tini容器init是一個最小化的init系統,運行在容器內部,用於啟動一個子進程,並等待進程退出時清理僵屍和執行信號轉發

    優點

    • tini可以避免應用程序生成僵屍進程

    • tini可以處理Docker進程中運行的程序的信號,通過TiniSIGTERM 可以終止進程,不需要你明確安裝一個信號處理器

    示例

# Add Tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# Run your program under Tini
CMD ["/your/program", "-and", "-its", "arguments"]
# or docker run your-image /your/program ...
  • dumb-init

    dumb-init會向子進程的進程組發送其收到的信號。例如 bash 接收到信號之后,不會向子進程發送信號

    dumb-init也可以通過設置環境變量DUMB_INIT_SETSID=0來控制只向它的直接子進程發送信號

    另外dumb-init也會接管失去父進程的進程,確保其能正常退出

    示例

FROM alpine:3.11.5
RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories \
    && apk add --no-cache dumb-init

# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT ["dumb-init", "--"]

# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]

CMD ["/my/script", "--with", "--args"]

4.4 降權啟動

很多情況下,容器中的進程需要降權啟動以保證安全性,這就和我們在vm上運行一個nginx服務一樣,最好通過特定的降權用戶去運行

舉例,tomcat鏡像

...
USER tomcat
WORKDIR /usr/local/tomcat
EXPOSE 8080
ENTRYPOINT ["catalina.sh","run"]

如果在某些情況下需要使用sudo權限,在docker官方避免安裝或使用sudosudo因為它具有不可預測的TTY和可能導致問題的信號轉發行為。如果必須,例如將守護進程初始化為 root但將其作為非運行root,推薦使用gosu

例如,Postgres 官方鏡像 使用以下腳本作為其ENTRYPOINT

#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

4.5 底層庫依賴

很多時候,服務依賴一些底層庫的支持,這里以基於alpine基礎鏡像構建java鏡像舉個栗子

alpine為了精簡本身並沒有安裝太多的常用軟件,所以如果要使用jdk/jre的話就需要glibc,而glibc需要先得到ca-certificates證書服務(安裝glibc前置依賴)才能安裝

alpine跑了jdk8的鏡像結果發現jdk無法執行。究其原因,java是基於GUN Standard C library(glibc)alpine是基於MUSL libc(mini libc),所以alpine需要安裝glibc的庫

5、小結

本文簡要分析了Dockerfile為什么這么大的幾個主要原因,並且根據生產經驗羅列了一些優化鏡像大小的措施以及其他方面常用的處理辦法,很多技巧性的內容,比較雜亂,就不一一提及了 ~

參考
https://github.com/docker-library/official-images#init
https://wiki.alpinelinux.org/wiki/Running_glibc_programs

See you ~

關注公眾號加群,更多原創干貨與你分享 ~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM