Docker FAQ

Codcodog發表於2019-02-23
  1. Dockerfile 中 COPY 指令和 ADD 指令有什麼區別

    簡單來說,最主要的區別是 ADD 功能更為強大一些:

    • ADD 允許 <src> 是一個 URL
    • 如果 ADD<src> 引數是可以識別的壓縮格式存檔檔案,則對其進行解壓

      如果 ADD 帶一個 URL 是 xxx.tar.gz 的檔案的話,是不會對其進行解壓的

    「書寫 Dockerfiles 的最佳實踐」推薦使用 COPY 指令而不是使用 ADD 指令.

  2. Dockerfile 中 CMD 指令和 ENTRYPOINT 指令有什麼區別

    Docker 有一個預設的「入口點」 /bin/sh -c,但沒有一個預設的命令.

    這裡論述,感覺不對,主要看 Dockerfile 中的 CMDENTRYPOINT 是基於 exec 還是 shell 模式
    若是 shell 模式則是利用 /bin/sh -c 調起命令執行的,exec 模式則是直接執行命令

    當你這樣執行 docker 的時候:docker run -i -t ubuntu bash,「入口點」就是預設的 /bin/sh -c,映象是 ubuntu,命令是bash.
    該命令是透過「入口點」執行的,實際上執行的是 /bin/sh -c bash. 這允許 Docker 依靠 shell 的解析器快速實現 RUN.
    後來,使用者要求能夠自定義它,因此引入了 ENTRYPOINT--entrypoint.

    在上面的示例中,ubuntu 之後的內容都是命令並且傳遞給了「入口點」.
    當使用 CMD 指令的時候,實際上就跟 docker run -i -t ubuntu <cmd> 一樣,<cmd> 就是「入口點」的引數.
    你仍然會在容器中啟動一個 bash shell 是因為 ubuntuDockerfile 指定了一個預設的 CMD: CMD ["bash"]

    因為所有內容都會傳遞到「入口點」,所以你可以從「映象」獲取到非常好的行為.
    例如,把「映象」用作「二進位制」命令.
    當把 ["/bin/cat"] 作為「入口點」並執行 docker run img /etc/passwd 的時候,你將會得到,
    由於 /etc/passwd 是命令並傳遞給「入口點」,因此最終執行的只是 /bin/cat /etc/passwd.

    另外一個例子是將任何 cli 作為「入口點」.
    假設有一個 redis 映象,不用執行 docker run redisimg redis -H something -u toto get key,而是簡單定義
    ENTRYPOINT ["reids", "-H", "something", "-u", "toto"],然後執行類似這樣的命令將會得到相同的結果:
    docker run redisimg get key.

  3. 從主機複製檔案到 Docker 容器

    cp 命令能夠用來複制檔案. 可以複製一個特定的檔案,如

    docker cp foo.txt mycontainer:/foo.txt
    docker cp mycontainer:/foo.txt foo.txt

    特別強調,mycontainer 是「容器」ID 而不是「映象」ID.

    資料夾 src 包含多個檔案可以使用以下方法複製到 target 資料夾中

    docker cp src/. mycontainer:/target
    docker cp mycontainer:/src/. target

    參考:Docker CLI docs for cp

    小於 Docker 1.8 的版本,只能將檔案從容器複製到主機,而不能從主機複製到容器.

  4. 如何移除舊的 Docker 容器

    使用 docker container prune.

    也可以使用 docker system prune, 將會在一個命令中移除「容器」(containers),「映象」(images),「卷」(volumes)以及「網路」(networks).

  5. 如何從主機獲取 Docker 容器的 IP 地址

    inspect--format 選項可以獲取到.
    現代 Docker 客戶端語法

    docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id

    古代 Docker 客戶端語法

    docker inspect --format '{{ .NetworkSettings.IPAddress }}' container_name_or_id

    若是 windows 系統,則需要雙引號(“)而不是單引號(‘)

  6. 在不使用倉庫的情況下,如何將 Docker 映象從一臺主機複製到另一臺

    首先,把映象儲存為一個 tar 檔案.

    docker save -o < path for generated tar file > < image name >

    然後使用常規檔案傳輸工具(如 cpscp)將映象複製到系統.
    之後再將映象匯入到 Docker 中.

    docker load -i < path to image tar file >
  7. 如何處理 Docker 中的持久儲存(例如:資料庫)

    使用 volume API

    docker volume create --name hello
    docker run -d -v hello:/container/path/for/volume container_image my_command

    這意味著必須放棄僅資料容器模式以支援新卷.

    實際上 volume API 只是實現資料容器模式的更好的方法.

    如果使用 -v volume_name:/container/fs/path 建立容器,Docker 將自動為你建立一個命名卷,它可以

    1. 透過 docker volume ls 列出來
    2. 透過 docker volume inspect volume_name 識別
    3. 備份為普通目錄
    4. 像以前一樣透過 --volumes-from 連線備份

    新的 volume API 增加了一個有用的命令,能夠讓你識別出懸空的卷

    docker volume ls -f dangling=true

    然後可以透過卷名移除

    docker volume rm < volume name >

    可以只透過一條命令移除所有懸空的卷

    docker volume rm $(docker volume ls -f dangling=true -q)
    
    # Or using 1.13.x
    docker volume prune
  8. 如何在 Docker 中列出容器

    只顯示正在執行的「容器」

    docker ps

    顯示所有的「容器」

    docker ps -a

    顯示最新建立的「容器」(包括所有狀態)

    docker ps -l

    顯示 N 個最新建立的「容器」(包括所有狀態)

    docker ps -n=-1

    顯示總的檔案大小

    docker ps -s

    在新版本的 Docker, 命令有更新,新增了一些管理命令

    列出正在執行的「容器」

    docker container ls

    列出所有的容器(不管狀態)

    docker container ls -a
  9. 如何刪除 Docker 中的映象

    使用 docker rmi node

    列出所有建立的「容器」 docker ps -a

    移除所有現有的「容器」(不是「映象」),執行 docker rm $(docker ps -aq)

  10. 如何進入 Docker 容器的 shell

    使用 docker exec, 例如

    docker exec -it < mycontainer > bash
  11. Docker 映象儲存在主機那個位置

    /var/lib/docker 目錄的內容主要依賴於 driver Docker is using for storage

    預設是 aufs,但也可以為 overlay, overlay2, btrfs, devicemapper 或者 zfs,主要是取決於你的「核心」支援.
    大多數情況下都會是 aufs,但是 RedHats 的是 devicemapper.

    可以使用 -s 或者 --storage-dirver= 選項為 Docker daemon 手動設定儲存的驅動.

    • /var/lib/docker/{dirver-name} 包含了指定儲存驅動的映象內容
    • /var/lib/docker/graph/{id} 僅包含映象的後設資料,儲存在 jsonlayersize 檔案

    若是 aufs

    • /var/lib/docker/aufs/diff/{id} 包含了映象檔案內容
    • /var/lib/docker/repositories-aufs 是一個 JSON 檔案,包含了本地映象資訊. 可以使用 docker images 命令檢視

    若是 devicemapper

    • /var/lib/docker/devicemapper/devicemapper/data 儲存了映象
    • /var/lib/docker/devicemapper/devicemapper/metadata 儲存了後設資料

      這些檔案是精簡配置的’稀疏‘檔案,因此不像它們看起來那麼大

  12. Docker 容器和映象的區別

    「映象」的執行例項被稱作「容器」.

    一個「映象」,實質是一組 layer 層的描述. 當你啟動此「映象」,則會有一個執行此「映象」的「容器」. 可以建立許多同一個「映象」的執行「容器」.

    docker images 可以檢視所有的「映象」, 而 docker ps 可以檢視所有執行中的「容器」, docker ps -a 檢視所有的「容器」.

    因此,「映象」的執行例項就是「容器」.

  13. 如何移除舊的不用的 Docker 映象

    docker system prune 會刪除所有的懸空資料(例如:停止執行的「容器」,沒有「容器」的「卷」,沒有「容器」的「映象」),甚至不使用的資料,使用 -a 選項.

    還可以使用:

    • docker container prune
    • docker image prune
    • docker network prune
    • docker volume prune

    對於不使用的「映象」,使用 docker image prune -a (刪除懸空和不使用的「映象」).

    不使用的「映象」是指未被任何「容器」引用

    --filter 選項與 docker xxx prune 組合使用,是一種限制 prune 的好方法.

  14. 如何將環境變數傳遞給 Docker 容器

    使用 -e 選項.

    sudo docker run -d -t -i -e REDIS_NAMESPACE='staging' \
    -e POSTGRES_ENV_POSTGRES_PASSWORD='foo' \
    -e POSTGRES_ENV_POSTGRES_USER='bar' \
    -e POSTGRES_ENV_DB_NAME='mysite_staging' \
    -e POSTGRES_PORT_5432_TCP_ADDR='docker-db-1.hidden.us-east-1.rds.amazonaws.com' \
    -e SITE_URL='staging.mysite.com' \
    -p 80:80 \
    --link redis:redis \
    --name container_name dockerhub_id/image_name

    或者,不想在 ps 命令列看到這樣賦值的引數,則可以這樣

    sudo PASSWORD='foo' docker run  [...] -e PASSWORD [...]

    如果有很多環境變數特別是它們出於安全考慮不能暴露的話,可以使用 env-file.

    $ docker run --env-file ./env.list ubuntu bash

    env.list 檔案每一行要符合 VAR=VAL 這樣的格式

  15. 強制 Docker 不使用快取構建映象

    使用 --no-cache 選項.

    docker build --no-cache -t u12_core .
  16. Docker 如何修改倉庫名或者映象名

    docker tag server:latest myname/server:latest

    或者

    docker tag d583c3ac45fd myname/server:latest

    如果不要舊的名字,在重新標記之後可以移除它

    docker rmi server

    這僅僅移除 alias/tag. 因為 d583c3ac45fd 有其他名字,真實的「映象」不會被移除.

  17. 在 Docker 中 expose 和 publish 有什麼不同

    expose 是一種文件化的方式,與 Dockerfile 相關.

    expose 只是定義了那些埠被使用,是一種文件化定義,實際上不進行埠對映或開啟埠的操作

    --publish(-p) 是一種將主機埠對映到正在執行的容器埠的方法,與 docker run 相關.

    -p 會告訴容器在網路介面開啟那些埠

  18. 如何 attach 和 detach Docker 程式

    使用 CTRL-c detach 「容器」,會導致 Docker 「容器」退出.

    使用 --sig-proxy 選項,可以避免「容器」退出.

    docker attach --sig-proxy=false 304f5db405ec

    使用 CTRL-p CTRL-q 也可以 detach 而「容器」不退出, 不過只有在「容器」是使用 -t-i 選項啟動的時候,才會生效.

    $ docker run -i -t -d nginx
    af203c3e997b94c7a519463c26c9290c777942556be4682738a8892af1f7062b
    
    $ docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    af203c3e997b        nginx               "nginx -g 'daemon of…"   3 seconds ago       Up 2 seconds        80/tcp              keen_lumiere
    
    $ docker attach keen_lumiere
    read escape sequence # 這裡使用 CTRL-p CTRL-q detach 「容器」
    
    $ docker ps # 「容器」沒有退出
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    af203c3e997b        nginx               "nginx -g 'daemon of…"   20 seconds ago      Up 19 seconds       80/tcp              keen_lumiere

    也可以自定義 detach 鍵:--detach-keys
    詳情參考:set your own detach sequence

  19. Docker daemon 日誌的位置

    這主要取決於你的系統,以下是幾個位置以及包含幾個作業系統的命令.

    • Ubuntu(使用 upstart) - /var/log/upstart/docker.log
    • Ubuntu(使用 systemd) - journalctl -fu docker.service
    • Amazon Linux AMI - /var/log/docker
    • Boot2Docker - /var/log/docker.log
    • Debian GNU/Linux - /var/log/daemon.log
    • CentOS - /var/log/daemon.log | grep docker
    • CoreOS - journalctl -u docker.service
    • Fedora - journalctl -u docker.service
    • Red Hat Enterprise Linux Server - /var/log/messages | grep docker
    • OpenSuSE - journalctl -u docker.service
  20. Dockerfile 中如何更新 PATH 環境變數

    使用 Environment replacement

    ENV PATH="/opt/gtk/bin:${PATH}"
  21. 如何檢查 docker build 失敗的檔案系統

    Docker 每次從 Dockerfile 成功執行一個 RUN 指令,都會提交一個新的 layer 層到「映象」檔案系統.
    你可以方便地使用這些 layer 層 ID 作為一個「映象」啟動一個新的「容器」.

    使用這樣的 Dockerfile

    FROM busybox
    RUN echo 'foo' > /tmp/foo.txt
    RUN echo 'bar' >> /tmp/foo.txt

    構建一個「映象」

    $ docker build -t so-2622957 .
    Sending build context to Docker daemon 47.62 kB
    Step 1/3 : FROM busybox
    ---> 00f017a8c2a6
    Step 2/3 : RUN echo 'foo' > /tmp/foo.txt
    ---> Running in 4dbd01ebf27f
    ---> 044e1532c690
    Removing intermediate container 4dbd01ebf27f
    Step 3/3 : RUN echo 'bar' >> /tmp/foo.txt
    ---> Running in 74d81cb9d2b1
    ---> 5bd8172529c1
    Removing intermediate container 74d81cb9d2b1
    Successfully built 5bd8172529c1

    你可以使用 00f017a8c2a6, 044e1532c6905bd8172529c1 啟動一個新的「容器」.

    $ docker run --rm 00f017a8c2a6 cat /tmp/foo.txt
    cat: /tmp/foo.txt: No such file or directory
    
    $ docker run --rm 044e1532c690 cat /tmp/foo.txt
    foo
    
    $ docker run --rm 5bd8172529c1 cat /tmp/foo.txt
    foo
    bar

    Dockerfile 中的一個指令執行失敗時,你需要做的是查詢上一個 layer 層的 ID, 並在從該 ID 建立的「容器」中執行 shell

    docker run --rm -it <id_last_working_layer> bash -il

    一旦進入到「容器」

    • 嘗試失敗的命令,重現該問題
    • 然後修復該命令並測試它
    • 最後使用修復之後的命令更新你的 Dockerfile

    若是想直接在失敗的 layer 層排查問題而不是在最後一個工作的 layer 層,則可以:
    首先,找到失敗的「容器」

    $ docker ps -a
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                          PORTS               NAMES
    6934ada98de6        42e0228751b3        "/bin/sh -c './utils/"   24 minutes ago      Exited (1) About a minute ago                       sleepy_bell

    然後,把它提交到一個「映象」

    $ docker commit 6934ada98de6
    sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83

    之後,執行這個「映象」

    $ docker run -it 7015687976a4 [bash -il]

    這樣,就處於構建失敗時的狀態了.

說明

文章主要是參考了 StackOverflow docker 標籤的問題列表

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章