精簡(jiǎn)Docker鏡像的好處很多,不僅可以節(jié)省存儲(chǔ)空間和帶寬,還能減少安全隱患。優(yōu)化鏡像大小的手段多種多樣,因服務(wù)所使用的基礎(chǔ)開(kāi)發(fā)語(yǔ)言不同而有差異。本文將介紹精簡(jiǎn)Docker鏡像的幾種通用方法。
精簡(jiǎn)Docker鏡像的好處:
減少構(gòu)建時(shí)間
減少磁盤使用量
減少下載時(shí)間
因?yàn)榘募?,攻擊面減小,提高了安全性
提高部署速度
使用精簡(jiǎn)版的基礎(chǔ)鏡像
這里我們使用alpine版本的基礎(chǔ)鏡像,alpine是一個(gè)高度精簡(jiǎn)又包含了基本工具的輕量級(jí)linux發(fā)行版,本身的Docker鏡像只有四到五兆大小。使用alpine基礎(chǔ)鏡像來(lái)減小鏡像體積,以保證部署和擴(kuò)容速度。各開(kāi)發(fā)語(yǔ)言和框架都有基于alpine制作的基礎(chǔ)鏡像,在開(kāi)發(fā)自己應(yīng)用的鏡像時(shí),選擇這些鏡像作為基礎(chǔ)鏡像,可以大大減小鏡像的體積。
各種語(yǔ)言對(duì)應(yīng)的基礎(chǔ)鏡像如下:
JAVA(Spring Boot): - openjdk:8-jdk-alpine,openjdk:8-jre-alpine等
Java(Tomcat) - tomcat:8.5-alpine等
Nodejs - node:9-alpine, node:8-alpine等
Python - python:3-alpine, python:2-alpine等
php - 基于php:7-fpm-alpine,php:5-fpm-alpine等鏡像添加Nginx,參考https://hub.docker.com/r/trafex/alpine-nginx-php7/
Ruby:ruby:2-alpine等
Go/可執(zhí)行文件 - 直接基于alpine鏡像,把編譯后的可執(zhí)行文件打入鏡像。因?yàn)閍lpine不同于普通的Ubuntu/centos等發(fā)行版,需要靜態(tài)編譯和鏈接應(yīng)用代碼,例如Go需要關(guān)閉cgo: CGO_ENABLED=0 go build ...
靜態(tài)頁(yè)面 - nginx:1-alpine等
相同的命令放在一行
在使用dockerfile的時(shí)候,使用最多的命令是RUN指令,但是使用太多的RUN指令,會(huì)導(dǎo)致鏡像有很多的層,因?yàn)槊恳粋€(gè)指令就會(huì)導(dǎo)致寫入一個(gè)層,這樣鏡像就非常臃腫,甚至有可能會(huì)超過(guò)最大數(shù)限制,這個(gè)時(shí)候我們可以將多個(gè)命令串聯(lián)起來(lái)合并為一個(gè)RUN指令(運(yùn)算符&&和/)。
下面對(duì)其進(jìn)行一下對(duì)比:
FROM centos:7.5.1804 RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo RUN yum -y install httpd RUN yum -y install bind RUN yum clean all CMD testv1
FROM centos:7.5.1804 RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup && curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && yum -y install httpd bind && yum clean all CMD nginx
對(duì)比結(jié)果:
使用多階段構(gòu)建
Dockerfile中每條指令都會(huì)為鏡像增加一個(gè)鏡像層,并且你需要在移動(dòng)到下一個(gè)鏡像層之前清理不需要的組件。實(shí)際上,有一個(gè)Dockerfile用于開(kāi)發(fā)(其中包含構(gòu)建應(yīng)用程序所需的所有內(nèi)容)以及一個(gè)用于生產(chǎn)的瘦客戶端,它只包含你的應(yīng)用程序以及運(yùn)行它所需的內(nèi)容。這被稱為“建造者模式”。Docker 17.05.0-ce版本以后支持多階段構(gòu)建。使用多階段構(gòu)建,你可以在Dockerfile中使用多個(gè)FROM語(yǔ)句,每條FROM指令可以使用不同的基礎(chǔ)鏡像,這樣您可以選擇性地將服務(wù)組件從一個(gè)階段COPY到另一個(gè)階段,在最終鏡像中只保留需要的內(nèi)容。
下面是一個(gè)使用COPY --from 和 FROM … AS … 的Dockerfile:
# Compile FROM golang:1.9.0 AS builder WORKDIR /go/src/v9.git...com/.../k8s-monitor COPY . . WORKDIR /go/src/v9.git...com/.../k8s-monitor RUN make build RUN mv k8s-monitor /root Package Use scratch image FROM scratch WORKDIR /root/ COPY --from=builder /root . EXPOSE 8080 CMD ["/root/k8s-monitor"]
構(gòu)建鏡像,你會(huì)發(fā)現(xiàn)生成的鏡像只有上面COPY 指令指定的內(nèi)容,鏡像大小只有2M。這樣在以前使用兩個(gè)Dockerfile(一個(gè)Dockerfile用于開(kāi)發(fā)和一個(gè)用于生產(chǎn)的瘦客戶端),現(xiàn)在使用多階段構(gòu)建就可以搞定。
構(gòu)建業(yè)務(wù)代碼的技巧
在打包業(yè)務(wù)代碼的時(shí)候,通常我們都是直接將jar包或者是war包放進(jìn)docker中,但是這樣的話會(huì)導(dǎo)致打包速度很慢。怎么解決呢?我們可以先將jar包或者是war包解壓出來(lái)然后多執(zhí)行幾個(gè)COPY,這樣我們的打包速度就可以快很多。以下以jeecg-boot為例說(shuō)明:
1、解壓我們的jar包
unzip jeecg-boot-module-system-2.1.1.jar -d App
2、我們將應(yīng)用分成四個(gè)部分打進(jìn)docker鏡像中,最后一行是解壓縮后,啟動(dòng)spring boot應(yīng)用的方式。
FROM openjdk:8-jre-alpine COPY app/BOOT-INF/lib/ /app/BOOT-INF/lib/ COPY app/org /app/org COPY app/META-INF /app/META-INF COPY app/BOOT-INF/classes /app/BOOT-INF/classes EXPOSE 8888 CMD ["/usr/bin/java", "-cp", "/app", "org.springframework.boot.loader.JarLauncher"]
其他優(yōu)化
如果在RUN命令中執(zhí)行apt、apk或者yum類工具,可以借助這些工具提供的一些小技巧來(lái)減少鏡像層數(shù)量及鏡像大小。舉幾個(gè)例子:
(1)在執(zhí)行apt-get install -y 時(shí)增加選項(xiàng)—no-install-recommends ,可以不用安裝建議性(非必須)的依賴,也可以在執(zhí)行apk add 時(shí)添加選項(xiàng)--no-cache 達(dá)到同樣效果;
(2)執(zhí)行yum install -y 時(shí)候, 將所有yum install 任務(wù)放在一條RUN命令上執(zhí)行,從而減少鏡像層的數(shù)量;
(3)組件的安裝和清理要串聯(lián)在一條指令里面,如 apk --update add php7 && rm -rf /var/cache/apk/* 。 Ubuntu或Debian可以使用 rm -rf /var/lib/apt/lists/* 清理鏡像中緩存文件;CentOS等系統(tǒng)使用yum clean all 命令清理。