自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

四個超實用的 Docker 鏡像構(gòu)建技巧

系統(tǒng) Linux
這篇文章我們來介紹一下做 Docker Image 的一些技巧。一起來看一下吧。

最近做了一個好玩的工具,叫 xbin.io[1]  。其中有一項工作是為不同的工具來構(gòu)建 Docker 鏡像,讓他們都運行在 Docker 中(實際上,是兼容 Docker image 的其他  sandbox 系統(tǒng),沒有直接用 Docker)。支持的工具越來越多,為了節(jié)省資源,Build 的 Docker image  就越小越好,文件越少,其實啟動速度也會略微快一些,也會更安全一些。

這篇文章來介紹一下做 Docker Image 的一些技巧。

在之前的博客 Docker (容器) 的原理[2] 中介紹過 Docker image 是如何工作的。簡單來說,就是使用 Linux 的 overlayfs[3], overlay file system 可以做到,將兩個 file system merge  在一起,下層的文件系統(tǒng)只讀,上層的文件系統(tǒng)可寫。如果你讀,找到上層就讀上層的,否則的話就找到下層的給你讀。然后寫的話會寫入到上層。這樣,其實對于最終用戶來說,可以認(rèn)為只有一個 merge 之后的文件系統(tǒng),用起來和普通文件系統(tǒng)沒有什么區(qū)別。

有了這個功能,Docker 運行的時候,從最下層的文件系統(tǒng)開始,merge 兩層,得到新的 fs 然后再 merge 上一層,然后再 merge 最上一層,最后得到最終的 directory,然后用 chroot[4] 改變進程的 root 目錄,啟動 container。

了解了原理之后,你會發(fā)現(xiàn),這種設(shè)計對于 Docker 來說非常合適:

  1. 如果 2 個 image 都是基于 Ubuntu,那么兩個 Image 可以共用 Ubuntu 的 base image,只需要存儲一份;
  2. 如果 pull 新的 image,某一層如果已經(jīng)存在,那么這一層之前的內(nèi)容其實就不需要 pull 了;

后面 build image 的技巧其實都是基于這兩點。

另外稍微提一下,Docker image 其實就是一個 tar 包[5]。一般來說我們通過 Dockerfile 用 docker built 命令來構(gòu)建,但是其實也可以用其他工具構(gòu)建,只要構(gòu)建出來的 image 符合 Docker 的規(guī)范[6],就可以運行。比如,之前的博文 Build 一個最小的 Redis Docker Image[7] 就是用 Nix 構(gòu)建出來的。

技巧1:刪除緩存

一般的包管理器,比如 apt, pip 等,下載包的時候,都會下載緩存,下次安裝同一個包的時候不必從網(wǎng)絡(luò)上下載,直接使用緩存即可。

但是在 Docker Image 中,我們是不需要這些緩存的。所以我們在 Dockerfile 中下載東西一般會使用這種命令:

RUN dnf install -y --setopt=tsflags=nodocs \
httpd vim && \
systemctl enable httpd && \
dnf clean all

在包安裝好之后,去刪除緩存。

一個常見的錯誤是,有人會這么寫:

FROM fedora
RUN dnf install -y mariadb
RUN dnf install -y wordpress
RUN dnf clean all

Dockerfile 里面的每一個 RUN 都會創(chuàng)建一層新的 layer,如上所說,這樣其實是創(chuàng)建了 3 層  layer,前 2 層帶來了緩存,第三層刪除了緩存。如同 git 一樣,你在一個新的 commit 里面刪除了之前的文件,其實文件還是在 git 歷史中的,最終的 docker image 其實沒有減少。

但是 Docker 有了一個新的功能,docker build --squash。squash 功能會在 Docker 完成構(gòu)建之后,將所有的 layers 壓縮成一個 layer,也就是說,最終構(gòu)建出來的 Docker image 只有一層。所以,如上在多個 RUN 中寫 clean 命令,其實也可以。我不太喜歡這種方式,因為前文提到的,多個 image 共享 base image 以及加速 pull 的 feature 其實就用不到了。

一些常見的包管理器刪除緩存的方法:

yum

yum clean all

dnf

dnf clean all

rvm

rvm cleanup all

gem

gem cleanup

cpan

rm -rf ~/.cpan/{build,sources}/*

pip

rm -rf ~/.cache/pip/*

apt-get

apt-get clean

另外,上面這個命令其實還有一個缺點。因為我們在同一個 RUN 中寫多行,不容易看出這個 dnf 到底安裝了什么。而且,第一行和最后一行不一樣,如果修改,diff 看到的會是兩行內(nèi)容,很不友好,容易出錯。

可以寫成這種形式,比較清晰。

RUN true \
&& dnf install -y --setopt=tsflags=nodocs \
httpd vim \
&& systemctl enable httpd \
&& dnf clean all \
&& true

技巧2:改動不頻繁的內(nèi)容往前放

通過前文介紹過的原理,可以知道,對于一個 Docker image 有 ABCD 四層,B 修改了,那么 BCD 會改變。

根據(jù)這個原理,我們在構(gòu)建的時候可以將系統(tǒng)依賴往前寫,因為像 apt, dnf 這些安裝的東西,是很少修改的。然后寫應(yīng)用的庫依賴,比如 pip install,最后 copy 應(yīng)用。

比如下面這個 Dockerfile,就會在每次代碼改變的時候都重新 Build 大部分 layers,即使只改了一個網(wǎng)頁的標(biāo)題。

FROM python:3.7-buster
# copy source
RUN mkdir -p /opt/app
COPY myapp /opt/app/myapp/
WORKDIR /opt/app
# install dependencies nginx
RUN apt-get update && apt-get install nginx
RUN pip install -r requirements.txt
RUN chown -R www-data:www-data /opt/app
# start server
EXPOSE 8020
STOPSIGNAL SIGTERM
CMD ["/opt/app/start-server.sh"]

我們可以改成,先安裝 Nginx,再單獨 copy requirements.txt,然后安裝 pip 依賴,最后 copy 應(yīng)用代碼。


FROM python:3.7-buster
# install dependencies nginx
RUN apt-get update && apt-get install nginx
COPY myapp/requirements.txt /opt/app/myapp/requirements.txt
RUN pip install -r requirements.txt
# copy source
RUN mkdir -p /opt/app
COPY myapp /opt/app/myapp/
WORKDIR /opt/app
RUN chown -R www-data:www-data /opt/app
# start server
EXPOSE 8020
STOPSIGNAL SIGTERM
CMD ["/opt/app/start-server.sh"]

技巧3:構(gòu)建和運行 Image 分離

我們在編譯應(yīng)用的時候需要很多構(gòu)建工具,比如 gcc, golang 等。但是在運行的時候不需要。在構(gòu)建完成之后,去刪除那些構(gòu)建工具是很麻煩的。

我們可以這樣:使用一個 Docker 作為 builder,安裝所有的構(gòu)建依賴,進行構(gòu)建,構(gòu)建完成后,重新選擇一個 Base image,然后將構(gòu)建的產(chǎn)物復(fù)制到新的 base image,這樣,最終的 image 只含有運行需要的東西。

比如,這是安裝一個 golang 應(yīng)用 pup 的代碼:

FROM golang as build
ENV CGO_ENABLED 0
RUN go install github.com/ericchiang/pup@latest
FROM alpine:3.15.4 as run
COPY --from=build /go/bin/pup /usr/local/bin/pup

我們使用 golang 這個 1G 多大的 image 來安裝,安裝完成之后將 binary 復(fù)制到 alpine, 最終的產(chǎn)物只有 10M 左右。這種方法特別適合一些靜態(tài)編譯的編程語言,比如 golang 和 rust.

技巧4:檢查構(gòu)建產(chǎn)物

這是最有用的一個技巧了。

dive 是一個 TUI,命令行的交互式 App,它可以讓你看到 docker 每一層里面都有什么。

dive ubuntu:latest 命令可以看到 ubuntu image 里面都有什么文件。內(nèi)容會顯示為兩側(cè),左邊顯示每一層的信息,右邊顯示當(dāng)前層(會包含之前的所有層)的文件內(nèi)容,本層新添加的文件會用黃色來顯示。通過 tab 鍵可以切換左右的操作。

一個非常有用的功能是,按下 ctrl + U 可以只顯示當(dāng)前層相比于前一層增加的內(nèi)容,這樣,就可以看到增加的文件是否是預(yù)期的了。

按 ctrl + Space 可以折疊起來所有的目錄,然后交互式地打開他們查看,就像是 Docker 中的 ncdu。

責(zé)任編輯:龐桂玉 來源: 奇妙的Linux世界
相關(guān)推薦

2024-03-27 14:16:48

Docker鏡像RUN

2024-02-23 18:17:57

Python腳本開發(fā)

2011-08-29 18:37:47

Ubuntu11.04

2017-07-10 14:00:04

Python命令行

2022-08-24 14:42:51

Linux技巧

2022-03-21 08:00:00

網(wǎng)絡(luò)安全影子IT數(shù)據(jù)泄露

2022-05-04 12:44:57

Python編程語言

2018-04-18 21:55:59

多云架構(gòu)云計算數(shù)據(jù)

2020-10-29 08:35:06

Pandas函數(shù)Python

2023-11-13 10:00:09

數(shù)據(jù)中心服務(wù)器

2023-02-19 15:22:22

React技巧

2020-08-25 08:47:15

開源軟件技巧

2021-03-12 10:01:33

Sudo命令Linux

2011-07-25 14:39:06

組策略

2020-10-26 08:06:59

網(wǎng)絡(luò)技巧CSS

2021-10-19 09:53:38

智能建筑數(shù)據(jù)中心邊緣計算

2023-10-26 07:47:35

JavaScript代碼變量

2022-10-08 15:32:24

Python開發(fā)技巧

2014-10-16 09:50:41

2024-11-14 09:00:00

Python編程元編程
點贊
收藏

51CTO技術(shù)棧公眾號