Docker指令詳解&最佳實踐&面試問題

2021-12-24 二蛋實驗室

收錄於話題 #docker 1個

docker已成為現在流行的一個開源容器引擎,我們在工作中也經常要與其打交道。這篇文章將主要從以下四個大方面闡述作者對docker的理解,希望能幫助大家由淺入深的或者是查漏補缺的更好的使用docker,歡迎大家在評論區留言交流。

docker裡的一些基本概念

docker常用指令詳解

Dockerfile最佳實踐

docker面試題

Docker裡的一些基本概念

鏡像和容器的關係

由圖中可以看出,紅色部分的只讀層為鏡像,而容器就是多層可讀層+一層讀寫層。

image鏡像

鏡像是由一堆可讀層組成,除了最下邊一層,每層都有一個指向下層的指針。image由Dockerfile構建而來,每層都對應一個dockerfile指令,例如下邊的Dockerfile,每個命令會創建一層鏡像。

FROM ubuntu:18.04COPY . /appRUN make /appCMD python /app/app.py

container容器

容器=鏡像+讀寫層。可以通過docker run [image]從一個鏡像運行一個容器。

Dockerfile

指定鏡像的生成規則,可以通過docker build -f [Dockerfile]生成一個鏡像。

容器是怎麼來的

Docker指令

根據上一部分我們知道了一個容器是怎麼來的,下邊列出的是作者在平時工作中經常會使用到的一些指令以及詳解。

 Dockerfile

假如我們有如下Dockerfile,關於Dockerfile的詳解會在下一部分。

# syntax=docker/dockerfile:1FROM node:12-alpinecopy hello.js ./hello.js      #hello.js內容為console.log('hello world');CMD ["node", "hello.js"]

build
我們可以通過build指令從Dockerfile生成一個image。

docker build -t hello:v1 .

-t 表示我想要生成image的name和一個可選的tag,格式為name:tag。. 注意:這裡的 .並不是表示Dockerfile的位置,而是此次構建的context,後邊會詳解。那麼大家可能注意到我們並沒有指定Dockerfile的路徑,因為build默認會從當前目錄尋找一個叫 Dockerfile的文件。如果你的Dockerfile不在當前路徑呢,我們可以使用-f [path]來指定其路徑。

其他常用的和image相關的指令還有:

docker images:查看當前宿主機上所有的image

docker tag hello:v1 hello:v2:把image的tag從v1變成v2

docker rmi [image-id]:刪除一個指定的image

run
docker run hello:v1我們可以把容器運行起來了。

        當然docker run有很多參數,我經常用的有:
         a. -d: 以detached mode運行

         b. -p: 後加3000:3000即表示把容器內的3000埠發布到宿主機的3000端                  口上

         c. -v: /src:/src,把宿主機的src目錄綁定到容器內

         d. -it: 以交互模式運行container

        其他常用的和container相關的指令還有:

         a. docker ps -a:查看所有宿主機上的container以及其狀態等信息

         b. docker stop [container-id]:停止一個容器

         c. docker rm [container-id]:刪除一個容器,-f表示強制。

Dockerfile常用指令詳解

下圖列出的是筆者記錄的需要常用的一些指令及其特點。

其中包括的要點有:

ADD和COPY的區別,分別在什麼時候使用:

所有的文件複製都使用copy命令

add <src>的src可以是一個遠程url,如果是可解壓文件如 tar.gz, 會自動解壓

數據的兩種綁定方式,使用 docker inspect [container-id] 可以找到Mounts欄位查看

目錄綁定,type=bind

數據卷綁定,type=volume

dockerfile裡設置的VOLUME欄位的作用:
注意:dockerfile裡的volume只能指定容器內的目錄,不能指定主機的目錄,可以在docker run的時候通過-v 進行覆蓋,如果不覆蓋,會默認在/var/lib/docker/volumes/xxx創建匿名volume。所以dockerfile裡的VOLUME欄位的作用是為了防止用戶忘記在運行容器時忘記將動態文件掛在為卷。


CMD和ENTRYPOINT的區別:
    a. CMD可以作為ENTRYPOINT的參數,docker把CMD命令拼接到ENTRYPOINT之後
    b. CMD可以在run時候被參數覆蓋,而ENTRYPOINT不可以
    c. ENTRYPOINT可以執行一個腳本,這樣CMD的參數會被腳本接收
    d. 一般最佳實踐是將ENTRYPOINT設置為容器的main command
    e. 多個CMD只有最後一個生效,同理ENTRYPOINT也一樣

CMD和ENTRYPOINT的兩種語法:
    a. EXEC寫法:CMD ["executable","param1","param2"]
    b. EXEC寫法:CMD ["executable","param1","param2"]
    c. 永遠使用EXEC寫法

ENV和RUN export的區別:
    a. 通過ENV設置的會持續存在,包括multiStage的dockerfile
    b. RUN export只會在當前image層有效,如下例子:

build這個image,最後一步的輸出結果為:


我們可以看到,FOO 是輸出來了,因為ENV可以跨多層生效,BAR 沒有輸出,而BAZ輸出了,是因為RUN export只存在於當前層。

ARG命令:
    a. ARG指定的參數不能在容器運行時讀取
    b. ARG指定的參數可以在docker build時候通過—build-arg覆蓋
    c. 對於multiStage的dockerfile,需要在FROM之後使用如下指令重新指定            一下,這是因為ARG只在一當前它所指定的stage中生效

ARG DARSHAN_VER=3.1.6
FROM fedora:29 as buildARG DARSHAN_VERRUN curl -O "<ftp://ftp.mcs.anl.gov/pub/darshan/releases/darshan-${DARSHAN_VER}.tar.gz>" \\ && tar ..
FROM fedora:29ARG DARSHAN_VERCOPY --from=build "/usr/local/darshan-${DARSHAN_VER}" "/usr/local/darshan-${DARSHAN_VER}"..

WORKDIR

WORKDIR aWORKDIR bWORKDIR cRUN pwd #a/b/c

multiStage多階段構建

docker從17.5開始支持多階段構建,是為了解決:

鏡像層次多,體積過大,部署時間長

原始碼可能洩露

通過多個FROM指定多個構建階段,前邊階段可能是多個builder,把最終需要delivery的binary或其他file 複製到最終鏡像。

FROM golang:alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as pr
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]

可以通過如下指令build某個stage的image:

docker build --target builder -t alexellis2/href-counter:latest .

Dockerfile最佳實踐

keep small

使用合適的base image,例如openJDK代替Ubuntu + jdk;常用的linux鏡像一般有Ubuntu,CentOS,Alpine,一般更推薦使用Alpine,其大小分別如下:

如果你要運行的包包含所有的依賴,那麼可以使用空的image:scratch

...
# 運行:使用scratch作為基礎鏡像FROM scratch as prod# 在build階段複製可執行的go二進位文件appCOPY --from=builder /go/release/app /# 啟動服務CMD ["/app"]

     c. 多階段構建,使用maven build java app,然後tomcat deploy

高效利用緩存,儘量把變化最小的放在前邊。例如把需要下載的依賴和業務代碼分開:
    a. 對於前端項目,可以

ADD package.json ./RUN npm iADD src ./RUN npm run build

    b. 對於golang項目,可以

ADD go.mod go.sum ./RUN go mod downloadADD server ./RUN go build app.go 

利用.dockerignore來減小build context,可以提高docker load context的速度,例如:

指令串聯,一方面可以減少image分層,縮小體積;另一方面避免達到最大層數(127)。例如多個RUN合併為一個:

FROM debian:stretch
RUN set -x; buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps

執行yum install -y 的時候可以一次install多個工具,例如:

yum install -y gcc gcc-c++ make

執行apt-get install -y的時候增加選項— no-install-recommends,去除非必須的依賴

使用RUN echo $(ls -al) 代替 RUN ls
在實際使用的過程中發現ls不會輸出內容,加上echo後可以了。不知道大家有沒有遇到這種情況。

優先使用copy over add。

Docker面試題
Docker和虛擬機的區別
傳統虛擬機是虛擬化出來一套硬體,在其上運行作業系統,而docker是直接運行在宿主的內核,沒有自己的內核,也沒有硬體虛擬化,更加輕便。

ADD和COPY的區別,見上邊。

docker-compose vs k8s vs docker swarm

docker-compose:單機node集群編排

k8s:多主機集群

swarm:多主機集群

還有其他一些,基本上文都覆蓋到了,也歡迎大家補充。

總結

本文主要介紹了筆者在工程中的一些常見docker用法,可能需要有一些docker基礎之後食用更佳,歡迎評論區留言討論。

相關焦點

  • 雲計算核心技術Docker教程:Dockerfile指令詳解
    CMD類似於 RUN 指令,用於運行程序,但二者運行的時間點不同:CMD 在docker run 時運行。RUN 是在 docker build。CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋。注意:如果 Dockerfile 中如果存在多個 CMD 指令,僅最後一個生效。格式:CMDCMD ["","","",...]
  • Dockerfile最佳實踐
    本文分為三個部分,首先會直接給出一份 Dockerfile 的參考模板,然後說明如和構建高效的鏡像並解釋這個模板這樣組織的原因,最後會補充說明一些編寫過程中的常見問題。一份簡單的Dockerfile參考模板 docker 官方給出的參考文檔中給出的 Dockerfile 指令接近 20 個,而我們平時在編寫的時候,經常用到的不超過 10 個。
  • 學習Docker就應該掌握的dockerfile語法與指令
    以下是 Dockerfile 中一些常用欄位的含義:todo:下面命令太抽象,而且只是介紹,沒有demo,可以給每個命令引入demo,比如解釋 FROM ubuntu:16.04FROM:基礎鏡像,FROM 命令必須是 Dockerfile 的首個命令。LABEL:為鏡像生成元數據標籤信息。
  • docker容器dockerfile詳解
    1.3s => exporting to image 0.5s => => exporting layers
  • Dockerfile 文件全面詳解
    如果您省略其中任何一個,構建器默認使用一個 latest 標籤。如果找不到該 tag 值,構建器將返回錯誤。--platform 標誌可用於在 FROM 引用多平臺鏡像的情況下指定平臺。例如,linux/amd64、linux/arm64、 或 windows/amd64。
  • 運維 - 實踐篇(二)- 基礎 Docker 開發環境搭建
    運維 - 實踐篇(二)- 基礎 Docker 開發環境搭建一、Linux 搭建基礎 Docker 環境1.
  • Dockerfile常用使用方法
    Docker從Dockerfile中讀取指令,以自動執行原本手動執行的步驟來創建映像。要構建鏡像,請創建一個名為Dockerfile的文件。Dockerfile描述了組裝映像所需的步驟。創建Dockerfile後,調用docker build命令,使用包含Dockerfile的目錄的路徑作為參數。
  • 從安全到鏡像流水線,Docker 最佳實踐與反模式一覽
    在本文中,我們將探討Docker的最佳實踐和反模式。反模式是人們對於反覆出現的問題的一般解決方案,這些方案沒有效率,甚至會完全抵消Docker技術棧帶來的好處。下面我們來看看我們的哪些做法不可取。我們需要的標籤標籤是必不可少的,我們需要通過標籤傳達有關Docker鏡像的信息。
  • Dockerfile文件全面詳解
    將在當前鏡像之上的新層中執行命令,在 docker build時運行。設置的環境變量將持續存在,您可以使用 docker inspect 來查看。使用 docker run --env <key>=<value> 來更改環境變量的值。
  • 怎樣構建Golang Dockerfiles?
    Docker 在 Dockerfile 文檔中一上來就強調:儘量減少層數是一個最佳實踐!這是一個重要的概念,必須從一開始就做好。你很容易就能寫一個包含很多層的 Dockerfile——它的語法就有這個傾向——結果你不知不覺中就會寫出很多效率低下的內容。最佳實踐是將構建的相關階段分組和連結在一起,例如下載依賴項、供應商文件夾集成或使用 RUN 命令設置構建環境等階段。
  • Dockerfile 常用指令 - 每天5分鐘玩轉 Docker 容器技術(16)
    VOLUMEWORKDIR為後面的 RUN, CMD, ENTRYPOINT, ADD 或 COPY 指令設置鏡像中的當前工作目錄。RUNCMD 可以被 docker run 之後的參數替換。目錄 bunch:由 ADD 指令從 build context 複製的歸檔文件 bunch.tar.gz,已經自動解壓。
  • Docker問答錄系列——Docker引擎相關問題(一)
    本系列文章總結了一些初學Docker時比較常見問題的解決方法,解決思路大多遵循Docker官方的最佳實踐的原則而進行的解答。
  • dockerfile中ENTRYPOINT與CMD的結合
    一、寫在前面我們在上篇小作文docker容器dockerfile詳解[1]對中dockerfile有了比較全面的認識,我們也提到ENTRYPOINT和CMD
  • 雲計算核心技術Docker教程:Dockerfile文件EXPOSE命令詳解
    Dockerfile文件EXPOSE 指令是聲明運行時容器提供服務埠,這只是一個聲明,在運行時並不會因為這個聲明應用就會開啟這個埠的服務。在 Dockerfile 中寫入這樣的聲明有兩個好處,一個是幫助鏡像使用者理解這個鏡像服務的守護埠,以方便配置映射;另一個用處則是在運行時使用隨機埠映射時,也就是 docker run -P 時,會自動隨機映射 EXPOSE 的埠。
  • 雲計算核心技術Docker教程:Dockerfile文件CMD命令詳解
    Dockerfile文件ADD指令是用於指定默認的容器主進程的啟動命令。Docker 不是虛擬機,容器就是進程。既然是進程,那麼在啟動容器的時候,需要指定所運行的程序及參數。
  • Docker —— 從入門到實踐
    同時,本書中給出的實踐案例,可供在進行實際部署時借鑑。在線閱讀:https://www.gitbook.io/book/yeasy/docker_practice。維護本書的Github項目: https://github.com/yeasy/docker_practice。 歡迎大家參與。本書發布時,Docker的最新版本為1.20。
  • 雲計算核心技術Docker教程:info/version命令詳解
    來源:TechWeb.com.cn在docker客戶端命令行中我們可以使用info命令 顯示 Docker 系統信息,包括鏡像和容器數,通過version命令顯示 Docker 版本信息。示例docker info : 顯示 Docker 系統信息,包括鏡像和容器數語法docker info [OPTIONS]OPTIONS說明:-f :指定返回值的模板文件
  • 雲計算核心技術Docker教程:build 命令詳解
    來源:TechWeb.com.cnDockerfile是一個用來構建鏡像的文本文件,在docker客戶端命令行中我們可以通過docker build 命令使用Dockerfile文件來創建鏡像。在構建期間設置RUN指令的網絡模式例如,使用當前目錄的 Dockerfile 創建鏡像,標籤為 runoob/ubuntu:v1:docker build -t runoob/ubuntu:v1 .
  • 雲計算核心技術Docker教程:Docker Compose yml常用配置指令簡介
    Docker Compose 默認使用文件名 docker-compose.yml,例如以下就是一個docker-compose.yml文件示例:version:指定本 yml 依從的 compose 哪個版本制定的services:用於定義不同的應用服務,上例中分別定義了兩個服務
  • 雲計算核心技術Docker教程:rm/rmi命令詳解
    在docker客戶端命令行中我們可以使用rm刪除一個或多個容器,使用rmi刪除本地一個或多少鏡像。示例:docker rm :刪除一個或多個容器。例如,強制刪除容器 db01、db02,命令如下:$ docker rm -f db01 db02移除容器 nginx01 對容器 db01 的連接,連接名 db:$ docker rm -l db刪除容器 nginx01, 並刪除容器掛載的數據卷:$ docker rm -v nginx01刪除所有已經停止的容器