Docker進階-Dockerfile建立一個自定義的映象執行自定義程序

2022-12-12 06:00:38

前言

docker對我來說是一個很方便的工具,,上一篇文章也寫了docker基本的一些使用,這篇文章重點描述一下Dockerfile的使用,從零建立一個自己客製化化的映象,並可以執行我們需要的任務。

作者:良知猶存

轉載授權以及圍觀:歡迎關注微信公眾號:羽林君

或者新增作者個人微信:become_me


命令列表

FROM 指定基礎映象:所謂客製化映象,那一定是以一個映象為基礎,在其上進行客製化。就像我們之前執行了一個 nginx 映象的容器,再進行修改一樣,基礎映象是必須指定的。而 FROM 就是指定 基礎映象,因此一個 Dockerfile 中 FROM 是必備的指令,並且必須是第一條指令

RUN 執行命令:RUN 指令是用來執行命令列命令的。其格式有兩種:

  • shell 格式:RUN <命令>,就像直接在命令列中輸入的命令一樣。剛才寫的 Dockerfile 中的 RUN 指令就是這種格式
  • exec 格式:RUN ["可執行檔案", "引數1", "引數2"],這更像是函數呼叫中的格式。

COPY 指令將從構建上下文目錄中 <源路徑> 的檔案/目錄複製到新的一層的映象內的 <目標路徑> 位置

ADD 指令和 COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。因此在 COPY 和 ADD 指令中選擇的時候,可以遵循這樣的原則,所有的檔案複製均使用 COPY 指令,僅在需要自動解壓縮的場合使用 ADD。

CMD 指令的格式和 RUN 相似,也是兩種格式:

  • shell 格式:CMD <命令>

  • exec 格式:CMD ["可執行檔案", "引數1", "引數2"...]

參數列格式:CMD ["引數1", "引數2"...]。在指定了 ENTRYPOINT 指令後,用 CMD 指定具體的引數。Docker 不是虛擬機器器,容器就是程序。既然是程序,那麼在啟動容器的時候,需要指定所執行的程式及引數。CMD 指令就是用於指定預設的容器主程序的啟動命令的。

ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式和 shell 格式。

LABEL:你可以給映象新增標籤來幫助組織映象、記錄許可資訊、輔助自動化構建等。每個標籤一行,由 LABEL 開頭加上一個或多個標籤對。下面的範例展示了各種不同的可能格式。# 開頭的行是註釋內容。

EXPOSE:EXPOSE 指令用於指定容器將要監聽的埠。因此,你應該為你的應用程式使用常見的埠。例如,提供 Apache web 服務的映象應該使用 EXPOSE 80,而提供 MongoDB 服務的映象使用 EXPOSE 27017。 對於外部存取,使用者可以在執行 docker run 時使用一個標誌來指示如何將指定的埠對映到所選擇的埠。

ENV:為了方便新程式執行,你可以使用 ENV 來為容器中安裝的程式更新 PATH 環境變數。例如使用 ENV PATH /usr/local/nginx/bin:$PATH 來確保 CMD ["nginx"] 能正確執行。 ENV 指令也可用於為你想要容器化的服務提供必要的環境變數,比如 Postgres 需要的 PGDATA。 最後,ENV 也能用於設定常見的版本號,比如下面的範例:

VOLUME:VOLUME 指令用於暴露任何資料庫儲存檔案,組態檔,或容器建立的檔案和目錄。強烈建議使用 VOLUME 來管理映象中的可變部分和使用者可以改變的部分。

USER:如果某個服務不需要特權執行,建議使用 USER 指令切換到非 root 使用者。先在 Dockerfile 中使用類似 RUN groupadd -r postgres && useradd -r -g postgres postgres 的指令建立使用者和使用者組。

WORKDIR:為了清晰性和可靠性,你應該總是在 WORKDIR 中使用絕對路徑。另外,你應該使用 WORKDIR 來替代類似於 RUN cd ... && do-something 的指令,後者難以閱讀、排錯和維護。

https://yeasy.gitbook.io/docker_practice/appendix/best_practices

命令驗證執行

從docker build 開發

先做個簡單編譯demo:

FROM ubuntu:18.04

USER root

COPY sources.list /etc/apt/sources.list

docker build . 當前目錄執行 可以看到執行情況,一共分為三步

每條指令建立一層:

此時通過docker images就可以看到我們編好的映象了,好了正式進入正題了。

from命令幫助我們找尋原始映象

第一行:FROM ubuntu:18.04 from本質上等效於 docker pull命令,我們可以使用本地映象,也可以指定映象源,用如下

FROM registry.hub.docker.com/library/ubuntu:18.04

執行效果:

對於國內映象源大家可以從此文獲取: https://segmentfault.com/a/1190000023117518

使用RUN命令安裝工具代替我們在容器執行命令:

RUN apt update 不要在指令碼中使用apt命令,如果在指令碼中使用apt命令,有可能會得到"WARNING: apt does not have a stable CLI interface. Use with caution in scripts." 提示。請使用apt-get、apt-cache等命令進行替換。apt命令不適合在指令碼中執行,因為apt命令是為使用者(人)而設計的,它會有顏色的顯示、進度條顯示等一些友好的互動介面。而在指令碼中,對於這些「特性」是不穩定(不支援或者是輸出錯亂等)的。

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

修改為RUN apt-get update

此外我們可以進行命令一次執行完成

RUN apt-get update
RUN apt-get  --fix-broken install
RUN  apt-get install -y gcc \
    zip\
    curl\
    python\
    kmod\
    openssh-server\
    sudo

安裝之後進行 設定一個賬戶

RUN groupadd -g 1011 lyn
RUN useradd -d /home/lyn -m -s /bin/bash -u 1010 -g lyn lyn

RUN mkdir -p /home/lyn/work\
&&chown -R lyn:lyn /home/lyn

RUN echo "lyn ALL=(ALL)    ALL" > /etc/sudoers

RUN echo 'root:root' | chpasswd && echo 'lyn:lyn' | chpassswd

WORKDIR /home/lyn/work

USER lyn
COPY .bashrc /home/lyn/.bashrc

設定工具目錄,可以看到進去之後工具目錄被設定為/home/lyn,通過WORKDIR /home/lyn/work執行。

使用

docker images 看一下打包好的映象

看到有個沒有命名的包,就是我們剛剛編出來的

這個時候我們進行改個名字

docker tag IMAGEID(映象id) REPOSITORY:TAG(倉庫:標籤)

docker tag 025673a91e65 lyn_image:v1

啟動使用 這個時候給大家介紹 volume命令

docker使用volume實現資料的持久化,不僅如此volume還能幫助容器和容器之間,容器和host之間共用資料。

我們可以使用dockerfile的VOLUME或者 docker run -v引數 ,直接設定需要掛載的目錄。

在Dockerfile增加 VOLUME /home/lyn/work,開始編譯。

編譯完成後,首先通過docker inspect檢視我們編譯好的映象資訊或者容器資訊:

docker inspect 47920709b10c 映象id進行檢視是否設定掛載目錄

docker inspect f4c2449431c5 啟動之後的容器id

docker volume ls 可以看到當前所有的volume

修改對映的資料夾內容

sudo touch /var/lib/docker/volumes/567f9c362f6067b3b354bea8b0b370bf304b845ae18e38b125fbdaaface09cfb/_data/lyn.log

可以看到檔案已經同步過來了

同樣我們也可以使用 docker run -v進行控制,首先註釋掉這句VOLUME /home/lyn/work,重新編譯映象

docker run -v /home/lyn/docker_share:/home/lyn/work [imageid] -v A:B A是在主機上的地址,B是在容器中的地址,這兩個地址如果不存在都會建立,一旦容器執行,AB的會完全同步。

具體執行為: docker run -it -v /home/lyn/docker_share:/home/lyn/work 208aca0306ab /bin/bash

關於volume更詳細的介紹大家可以看此文:https://docs.docker.com/engine/reference/commandline/volume_create/

寫了一個迴圈執行的程式碼,編譯成韌體,用dockerfile 編譯讓映象自動執行

COPY hello_world /home/lyn/work

CMD ./hello_world

docker run -it 47920709b10c

最終的Dockerfile檔案

FROM ubuntu:18.04

USER root

COPY sources.list /etc/apt/sources.list

RUN apt-get update
RUN  apt-get install -y gcc \
    zip\
    curl\
    python\
    kmod\
    openssh-server\
    sudo


RUN groupadd -g 1011 lyn
RUN useradd -d /home/lyn -m -s /bin/bash -u 1010 -g lyn lyn

RUN mkdir -p /home/lyn/work \
   &&chown -R lyn:lyn /home/lyn

RUN echo "lyn ALL=(ALL)    ALL" > /etc/sudoers

RUN echo 'root:root' | chpasswd && echo 'lyn:lyn' | chpasswd

WORKDIR /home/lyn/work

USER lyn
COPY .bashrc /home/lyn/.bashrc


VOLUME /home/lyn/work

COPY hello_world /home/lyn/work

CMD ./hello_world

docker build執行的log:因為有過編譯了,所以這裡好多執行就是Using cache,很少的列印了

lyn@lyn:~/Documents/lyn_test/docker_build_ubuntu$ docker build .
Sending build context to Docker daemon  28.16kB
Step 1/16 : FROM ubuntu:18.04
 ---> 71eaf13299f4
Step 2/16 : USER root
 ---> Using cache
 ---> d3fe45bd0e46
Step 3/16 : COPY sources.list /etc/apt/sources.list
 ---> Using cache
 ---> d4f825c3fc77
Step 4/16 : RUN apt-get update
 ---> Using cache
 ---> 3863a99d6e2b
Step 5/16 : RUN  apt-get install -y gcc     zip    curl    python    kmod    openssh-server    sudo
 ---> Using cache
 ---> 9b77c43d6709
Step 6/16 : RUN groupadd -g 1011 lyn
 ---> Using cache
 ---> bbba5f18057a
Step 7/16 : RUN useradd -d /home/lyn -m -s /bin/bash -u 1010 -g lyn lyn

 ---> Using cache
 ---> 47e999f10256
Step 8/16 : RUN mkdir -p /home/lyn/work    &&chown -R lyn:lyn /home/lyn
 ---> Using cache
 ---> 36faf04c6390
Step 9/16 : RUN echo "lyn ALL=(ALL)    ALL" > /etc/sudoers
 ---> Using cache
 ---> 0422bf50db6b
Step 10/16 : RUN echo 'root:root' | chpasswd && echo 'lyn:lyn' | chpasswd
 ---> Using cache
 ---> 68da5bb15877
Step 11/16 : WORKDIR /home/lyn/work
 ---> Using cache
 ---> b30d4dcd99f8
Step 12/16 : USER lyn
 ---> Using cache
 ---> 5dfa565d0c6a
Step 13/16 : COPY .bashrc /home/lyn/.bashrc
 ---> Using cache
 ---> f2b39d61f05b
Step 14/16 : VOLUME /home/lyn/work
 ---> Running in 5482493ab221
Removing intermediate container 5482493ab221
 ---> 0e359e093a4f
Step 15/16 : COPY hello_world /home/lyn/work
 ---> ac326932752c
Step 16/16 : CMD ./hello_world
 ---> Running in 430cfd90ad30
Removing intermediate container 430cfd90ad30
 ---> fc180e4919da
Successfully built fc180e4919da

補充操作:

發現兩個映象的id相同,如果用docker rmi [映象id]它就不知道該如何刪除,我們可以用:

Error response from daemon: conflict: unable to delete 71eaf13299f4 (must be forced) - image is referenced in multiple repositories

docker rmi 映象名:版本號 當我建立錯誤的映象之後,使用rmi進行刪除

結語

這就是我自己的一些Dockerfile使用分享。如果大家有更好的想法和需求,也歡迎大家加我好友交流分享哈。

此外對於想要更加細節的dockerfile使用可以官網的文章: https://docs.docker.com/engine/reference/builder/ https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ 這篇文章:https://yeasy.gitbook.io/docker_practice/image/build


作者:良知猶存,白天努力工作,晚上原創公號號主。公眾號內容除了技術還有些人生感悟,一個認真輸出內容的職場老司機,也是一個技術之外豐富生活的人,攝影、音樂 and 籃球。關注我,與我一起同行。

                              ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

推薦閱讀

【1】jetson nano開發使用的基礎詳細分享

【2】Linux開發coredump檔案分析實戰分享

【3】CPU中的程式是怎麼執行起來的 必讀

【4】cartographer環境建立以及建圖測試

【5】設計模式之簡單工廠模式、工廠模式、抽象工廠模式的對比

本公眾號全部原創乾貨已整理成一個目錄,回覆[ 資源 ]即可獲得。