Docker學習之六:基於Dockerfile構建鏡像

鏡像製作

一般鏡像的製作,通常需要修改鏡像的配置文件,比如nginx的配置文件,可以通過以下的方式:

  • 將配置文件做成存儲卷,從宿主機編輯好之後,啓動容器時應用程序加載配置文件的路徑並和宿主機的目錄建立關聯關係。容器內也能修改好配置文件
  • 一般的docker exec CONTAINER,然後編輯配置文件,然後reload nginx
  • 通過自定義鏡像

鏡像自定義,一般是需要針對不同的環境,定義符合實際需求的鏡像環境。比如可以基於容器,將配置文件直接寫死到鏡像中,但是如果需要修改配置時十分不便,而且如果應用容器的環境很多,如開發,測試,業務等環境,則需要爲每個環境都需要製作鏡像,如此就需要修改對應環境的配置文件,十分繁瑣。

因此,最好通過docker file來定義鏡像。

假如nginx啓動爲容器,僅僅配置一個虛擬主機,提供一個服務。其配置文件的格式是固定的,如果將配置文件做成類似模板,並且其值通過變量來獲取,比如server_name $NGINX_SERVER_NAME; Listen $NGX_IP:$NGX_PORT; root $DOC_ROOT;這樣的形式。

當用戶拿到鏡像,啓動爲容器時,在容器主進程啓動前,先啓動一個輔助程序,該程序根據鏡像中的文件和用戶啓動鏡像爲容器時,向容器傳遞的環境變量,程序自動將環境變量傳遞到主程序中,然後程序啓動主程序,輔助程序退出。如此可以基於一個鏡像啓動多個容器,並可直接向容器傳遞環境變量。如此一來,便可以準備好一個模板,設計好處理程序,由處理程序來替換主程序,以實現傳遞環境變量的方式來傳遞配置信息。

雲原生的應用也符合這種形式,程序啓動時甚至可以不讀取配置文件,而是直接讀取環境變量來作爲配置並啓動。

Docker file

Docker file是構建docker鏡像的源碼,裏面是基本的文本指令,包含用戶可以裝配生成一個image。

Docker file由兩類語句組成:註釋信息 + 指令和參數

指令一般純大寫字母,但是它本身不區分字符大小寫。根據約定俗成的慣例,都應該使用大寫字母。

Docker file是順序執行的指令,因此指令間的依賴關係很重要

第一個非註釋行,必須是FROM,表示必須基於某個基礎鏡像來製作。實際上,如今已經無此限制。

要求

  • 必須在某個特定目錄下進行,一個專用目錄放進docker file
  • Docker file的首字母必須大寫
  • 如果需要打包文件,必須將文件放入到工作目錄或者子目錄
  • Docker file支持目錄內做隱藏文件,docker ignore文件,寫入文件路徑後,打包時將不會打包該路徑的文
  • 命令:docker build

基於docker file構建鏡像時,無需啓用容器,而是由docker build完成。它可以基於基礎鏡像來執行命令,該命令是基礎鏡像所包含的而非宿主機包含的命令。因此,製作環境是底層鏡像啓動爲容器時所能提供的環境。

變量

Docker build能使用的環境變量,和shell的環境變量十分類似

${variable:-world}:如果變量爲空,則引用world爲變量的默認值

${variable:+world}:如果變量設定了值,不爲空,則顯示world

Docker file構成

FROM

FROM指令是最重要的一個指令,用於爲鏡像文件構建過程中指定基準鏡像。後續的指令運行於此基準鏡像所提供的運行環境。

實際中,基準鏡像可以是任何可用的鏡像文件。默認情況下,docker build會在docker主機上查找指定的鏡像文件。如果不存在,則從Docker Hub Registry上拉取所需的鏡像文件。

如果都找不到指定的鏡像文件,docker build會返回一個錯誤信息。

FROM <repository>[:<tag>] 或
FROM <repository>@<digest>

比如:

root@eto:~# mkdir lab1
root@eto:~# cd lab1/
root@eto:~/lab1# vim Dockerfile
root@eto:~/lab1# cat Dockerfile 
# Description: test image
FROM busybox:latest

MAINTAINER

用於讓Docker file製作者提供本人的詳細信息。

該字段已經廢棄,替換爲LABEL。Label可以提供各種鍵值信息,包含的信息更加寬泛。

LABEL

提供鍵值信息。

LABEL <key>=<value> <key>=<value>...

信息爲鍵值對,一個鏡像可以使用多個LABEL。

COPY

用於從Docker主機複製文件至創建的新映像文件

COPY <src> ... <dest> 或
COPY ["<src>"..."<dest>"]

<src>:要複製的源文件或目錄,支持使用通配符
<dest>:目標路徑,即正在創建的image文件的系統路徑。建議使用絕對路徑,否則COPY指定則以WORKDIR爲起始路徑

文件複製準則:

  • 必須是build上下文中的路徑,不能是父目錄中的文件
  • 如果是目錄,則其內部文件或者子目錄會被遞歸複製,但目錄自身不會被複制
  • 如果指定了多個,或在中使用了通配符,到必須是一個目錄,且必須以/結尾
  • 如果事先不存在,它將被自動創建,這包括其父目錄路徑

比如:

將宿主機的當前工作目錄的文件打包複製到提供的目標鏡像文件。

root@eto:~/lab1# cat Dockerfile 
# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>
COPY index.html /data/web/html/

root@eto:~/lab1# cat index.html 
my first image

root@eto:~/lab1# docker build -t tinyhttpd:v0.1-1 ./
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM busybox:latest
 ---> be5888e67be6
Step 2/3 : LABEL <mail>=<[email protected]>
 ---> Running in b6efaa79d695
Removing intermediate container b6efaa79d695
 ---> 07515529f3c5
Step 3/3 : COPY index.html /data/web/html/
 ---> 423253017bfe
Successfully built 423253017bfe
Successfully tagged tinyhttpd:v0.1-1

驗證:運行自定義命令,命令運行結束後容器消失。

root@eto:~/lab1# docker run --name tinyweb1 --rm tinyhttpd:v0.1-1 cat /data/web/html/index.html
my first image

複製目錄:

root@eto:~/lab1# mkdir mydir
root@eto:~/lab1# touch mydir/{test1,test2}
root@eto:~/lab1# vim Dockerfile 
root@eto:~/lab1# docker build -t tinyhttpd:v0.1-2 ./
Sending build context to Docker daemon  4.608kB
Step 1/4 : FROM busybox:latest
 ---> be5888e67be6
Step 2/4 : LABEL <mail>=<[email protected]>
 ---> Using cache
 ---> 07515529f3c5
Step 3/4 : COPY index.html /data/web/html/
 ---> Using cache
 ---> 423253017bfe
Step 4/4 : COPY mydir /tmp/
 ---> 4c98b55d0226
Successfully built 4c98b55d0226
Successfully tagged tinyhttpd:v0.1-2
root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-2 ls /tmp/
test1
test2

注意:

docker file文件中,每條指令都會生成一層。因此如果能合併爲一條指令,必須合併爲一條指令。否則多層的聯合掛載回導致讀寫效率低下。

ADD

ADD指令類似於COPY指令,ADD支持使用TAR文件和URL路徑

ADD <src> ... <dest> 或
ADD ["<src>" ... "<dest>"]

操作準則:

  • 同COPY指令
  • 如果爲URL且不以/結尾,則指定的文件將被下載並且直接被創建爲;如果以/結尾,則文件名URL指定的文件將被直接下載且保存爲/
  • 如果是一個本地系統上的壓縮格式的tar文件,它將被展開爲一個目錄,其行爲類似於"tar -x"命令;然而,通過URL獲取到的tar文件將不會自動展開;
  • 如果有多個,或其間接或直接使用通配符,則必須是一個以/結尾的目錄路徑;否則,其將被視作爲一個普通文件,的內容將被直接寫入到;

比如:

# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>
COPY index.html /data/web/html/
COPY mydir /tmp/
ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/
root@eto:~/lab1# docker build -t tinyhttpd:v0.1-3 ./

打包並驗證:

root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-3 ls /usr/local/src
nginx-1.17.10.tar.gz

如果直接從本地ADD,則會自動展開到鏡像中.

root@eto:~/lab1# cat Dockerfile 
# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>
COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD nginx-1.17.10.tar.gz ./

VOLUME

用於在image中創建一個掛載點目錄,以掛載Docker host上的卷或其他容器上的卷

VOLUME <mountpoint> 或
VOLUME ["<mountpoint>"]

如果掛載點目錄路徑下此前的文件存在,則docker run命令會在卷掛載完畢後將此前的所有文件複製到新掛載的卷中。

# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>
COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD nginx-1.17.10.tar.gz ./

VOLUME /data/mysql/
root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-3 mount | grep mysql
/dev/sda2 on /data/mysql type ext4 (rw,relatime,data=ordered)

EXPOSE

用於爲容器打開指定要監聽的端口以實現與外部通信

EXPOSE <port>[/<protocol>][<port>[/<protocol>]...]

用於指定傳輸層協議,可以是tcp或者udp,默認爲TCP協議。

EXPOSE 指令可一次指定多個端口,例如

EXPOSE 11211/udp 11211/tcp 

端口爲動態綁定,無法指定宿主機的哪個地址和端口。並且,還能指定容器要暴露的端口。

# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>
COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD nginx-1.17.10.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp
root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-3 /bin/httpd -f -h /data/web/html

root@eto:~/lab1# curl 172.17.0.2
my first image

注意:即使指定了暴露端口,未必默認就暴露:

root@eto:~/lab1# docker port tinyweb2
root@eto:~/lab1#

因此,要暴露到宿主機外部,使用-P選項

root@eto:~/lab1# docker run --name tinyweb2 -P --rm tinyhttpd:v0.1-3 /bin/httpd -f -h /data/web/html

root@eto:~/lab1# docker port tinyweb2
80/tcp -> 0.0.0.0:32768

ENV

用於爲鏡像定義所需的環境變量,並且可被Dockfile文件中位於其後的其他指令(如ENV, ADD, COPY等)所調用

調用格式爲$variable_name 或 ${variable_name}

ENV <key><value> 或
ENV <key>=<value>...

第一種格式中,之後的所有內容都會視作其的組成部分,因此一次只能設置一個變量;

第二種格式,可以用一次設置多個變量,每個變量爲一個"="的鍵值對。如果包含空格,則可以以反斜線進行轉義,也可以通過對加引號標識;另外,反斜線也可用於續行;

定義多個變量時,建議使用第二種方式,以便在同一層中完成所有功能

# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>

ENV DOC_ROOT=/data/web/html \
   WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp
root@eto:~/lab1# docker run --name myweb -P --rm tinyhttpd:v0.1-3 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=fd80bf4feaed
DOC_ROOT=/data/web/html
WEB_SERVICE_PACKAGE=nginx-1.17.10
HOME=/root

在docker run時,可傳遞變量的值。環境變量可以在容器啓動後,注入到容器中。因此,此時在容器運行時,重新賦值環境變量是可行的,比如:

root@eto:~/lab1# docker run --name myweb -e WEB_SERVICE_PACKAGE=nginx-1.17.11  -P --rm tinyhttpd:v0.1-3 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=7786f8b481e2
WEB_SERVICE_PACKAGE=nginx-1.17.11
DOC_ROOT=/data/web/html
HOME=/root

儘管在docker run中重新定義了環境變量的值,然而docker build時的值已經定義好了,image中的值無法修改的。

因此,docker build和docker run是兩個不同的過程。

CMD & RUN

鏡像從開始製作到運行,經過docker build和docker run兩個階段。這個兩個階段都允許運行shell命令,默認的命令是由CMD定義的。

而RUN是基於docker file構建鏡像時,要運行的命令,在docker build中運行。

比如,製作鏡像時,可在docker file中將tar.gz解壓

# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>

ENV DOC_ROOT=/data/web/html \
    WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp

RUN cd /usr/local/src && \
    tar xf ${WEB_SERVICE_PACKAGE}.tar.gz

RUN可以寫多個,並且建議多條命令使用續行符的方式,寫在同一行。

注意:

一般自定義鏡像,都使用編譯安裝。因爲yum安裝會生成許多緩存,會增大鏡像體積。因此,如果yum安裝完畢後,必須yum clean all

FROM centos
RUN yum -y install epel-release && yum makecache && yum install nginx && yum clean all

容器應該運行什麼樣的應用?一般而言,擁有生產能力的應用,都是運行在後臺的守護進程,比如nginx,redis,mysql等。這些應用,也可以運行在容器中。

假如手動運行nginx,直接在命令行激活而非通過systemctl管理,則nginx屬於shell的子進程。Shell是用戶創建並運行的進程的接口,因此任何進程都是shell的子進程,並且會佔據當前sh的終端設備。即使加&,僅僅是將進程送到後臺執行,而無法脫離sh這個父進程而存在。一旦退出sh,則其下的任何子進程將被回收。因此,需要使用nohup command &,才能將進程送到後臺並剝離和sh的關係。此時意味着父進程從sh改變爲init。

容器中的main process是否委託由內核啓動?而不是由shell啓動。比如ls /var/* 這個進程是在shell中能正常運行並解析的,一旦該進程不是sh啓動,則命令會運行失敗。因爲命令行展開,管道,通配符,輸入輸出重定向等都是屬於sh的特性。如果不是基於sh啓動,將全部失效。但是如果一旦基於sh啓動,main process將不再是容器中的pid=1的進程。

綜上所述,容器中啓動進程的方式有兩種:

  • 直接在容器中啓動進程,讓其pid=1
  • 容器中啓動sh後,再啓動主進程。但是不能違背主進程的pid=1這個原則,通過exec COMMAND實現

CMD

類似RUN指令,CMD指令可用於運行任何命令或應用程序。不過二者的運行時間點不同。

  • RUN指令運行於鏡像文件的構建過程中,而CMD指令運行於基於Dockfile構建出來的新鏡像文件啓動一個容器時
  • CMD指令的首要目的在於爲啓動的容器指定默認的要運行的程序,且其運行結束後,容器也將終止;不過,CMD指定的命令可以被docker run的命令選項所覆蓋
  • 在Dockerfile中可以存在多個CMD指令,但僅最後一個生效
CMD <command> 或
CMD ["<executable>","<param1>","<param2>"] 或
CMD ["<param1>","<param2>"]

前兩種語法格式的意義同RUN
第三種則用於ENTRYPOINT指令提供默認參數

Cmd指令用於定義一個容器,啓動時默認要運行的程序。Docker默認只運行一個程序,因此CMD只能給一個(docker file中可以給多個,但實際生效只有最後一個)

RUN

用於指定docker build過程中運行的程序,可以是任意命令

RUN <command> 或
RUN ["<executable>","<param1>","<param2>"]
  • 第一種格式中,通常是一個shell命令,並且以"/bin/sh -c"來運行它。這意味着此進程在容器中的PID不爲1,不能接收unix信號。因此,當使用docker stop 命令停止容器時,此進程接收不到SIGTERM信號;

  • 第二種語法格式中的參數是一個JSON格式的數組,其中爲要運行的命令,後面的爲傳遞給命令的選項或參數;然而,這種格式指定的命令不會以"/bin/sh -c"來發起,而是以內核啓動。因此常見的shell操作如變量替換以及通配符替換將不會進行;不過,如果要運行的命令依賴於shell特性,可以將其替換爲類似下面的格式

    RUN ["/bin/bash","-c","<executable>","<param1"]
    
# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>

ENV WEB_DOC_ROOT=/data/web/html \
    WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp

#RUN cd /usr/local/src && \
#    tar xf ${WEB_SERVICE_PACKAGE}.tar.gz

CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

構建完畢後運行:

root@eto:~/lab1# docker run --name myweb --rm -P tinyhttpd:v0.1-3

默認啓動後,會直接以httpd –f –h運行。因此終端會卡住。可以使用命令直接顯式運行sh,而且默認使用exec來將主進程啓動爲pid=1以接收unix信號。如此可以使用docker kill來停止容器。

            "Cmd": [
                "/bin/sh",
                "-c",
                "/bin/httpd -f -h ${WEB_DOC_ROOT}"

連接到容器終端裏:

root@eto:~/lab1# docker exec -it myweb /bin/sh
/usr/local/src # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html
    6 root      0:00 /bin/sh
   11 root      0:00 ps

在docker run時,RUN定義的環境變量也會被調用

/usr/local/src # printenv
WEB_DOC_ROOT=/data/web/html
HOSTNAME=b74030022d2c
SHLVL=1
HOME=/root
WEB_SERVICE_PACKAGE=nginx-1.17.10
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/usr/local/src

使用json格式來定義CMD:

由於cmd的這種定義方式是由內核執行的指令,無法識別shell變量。因爲kernel不會識別變量,直接識別爲目錄路徑。因此,需要定義爲:

#CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]

ENTRYPOINT

  • 類似CMD指令的功能,用於爲容器指定默認運行的程序,從而使得容器像一個單獨運行的可執行程序。
  • 與CMD不同的是,由ENTRYPOINT啓動的程序不會被docker run命令行指定的參數所覆蓋。而且,這些命令行參數會被當做參數傳遞給ENTRYPOINT指定的程序
  • 不過,docker run命令的–entrypoint選項的參數可覆蓋ENTRYPOINT指令指定的程序
ENTRYPOINT <command> 
ENTRYPOINT ["<executable>","<param1>","<param2>"]
  • docker run命令傳入的命令參數會覆蓋CMD指令的內容,並且附加到ENTRYPOINT命令最後做爲其參數使用
  • dockerfile可存在多個ENTRYPOINT指令,但僅有最後一個生效
  • 一般,不希望用戶運行非容器默認啓動時使用的命令,則使用ENTRYPOINT
# Description: test image
FROM busybox:latest
LABEL <mail>=<[email protected]>

ENV WEB_DOC_ROOT=/data/web/html \
    WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp

#RUN cd /usr/local/src && \
#    tar xf ${WEB_SERVICE_PACKAGE}.tar.gz

#CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

#CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]

ENTRYPOINT /bin/httpd -f -h ${WEB_DOC_ROOT}

運行

root@eto:~/lab1# docker run --name myweb --rm -P  tinyhttpd:v0.1-3  ls /dat/web/html

實際上並沒有運行ls命令,而是將ls等字符串識別爲位置參數傳遞給httpd程序。

如果一定要運行自己的命令,必須明確自己的需求。使用專門的選項來指定:–entrypoint string

root@eto:~/lab1# docker run --name myweb --rm -P --entrypoint "date" tinyhttpd:v0.1-3  
Tue May  5 09:08:45 UTC 2020

如果定義了多個ENTRYPOINT,則最後一個生效。如果ENTRYPOINT和CMD同時定義,則CMD定義的內容會被當做參數傳遞給ENTRYPOINT指定的命令。

比如:

CMD ["/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
ENTRYPOINT ["/bin/sh","-c"]

啓動:

root@eto:~/lab1# docker run --name myweb -it -P --rm tinyhttpd:v0.1-3  "sleep 60"
            "Cmd": [
                "sleep 60"
            ],
            "Image": "tinyhttpd:v0.1-3",
            "Volumes": {
                "/data/mysql/": {}
            },
            "WorkingDir": "/usr/local/src",
            "Entrypoint": [
                "/bin/sh",
                "-c"
            ],

此時傳遞的參數,能正常運行。

CMD定位默認的參數,傳遞給ENTRYPOINT。而命令行傳遞參數時,會覆蓋CMD定義的參數。如此就相當於運行了/bin/sh –c ”sleep 60”

實戰

定義一個nginx鏡像,能通過環境變量來接收參數,以定義監聽的端口,地址和doc_root。

  • 定義腳本初始化文件,然後運行nginx,並頂替sh進程(exec命令實現):

    root@eto:~/lab1# cat entrypoint.sh 
    #!/bin/sh
    #
    cat > /etc/nginx/conf.d/www.conf << EOF
    server {
        server_name $HOSTNAME;
        listen ${IP:-0.0.0.0}:${PORT:-80};
        root ${NGX_DOC_ROOT:-/usr/share/nginx/html};
    }
    EOF
    
    exec "$@"
    
    
    root@eto:~/lab1# chmod +x entrypoint.sh 
    
  • 定義dockerfile

    root@eto:~/lab1# cat Dockerfile
    FROM nginx:stable-alpine
    LABEL maintainer="jaywin"
    ENV NGX_DOC_ROOT="/data/web/html/"
    
    ADD index.html ${NGX_DOC_ROOT}
    ADD entrypoint.sh /bin/
    
    CMD ["/usr/sbin/nginx","-g","daemon off;"]
    ENTRYPOINT ["/bin/entrypoint.sh"]
    
  • 構建鏡像並運行

    root@eto:~/lab1# docker build -t tinyhttpd:v0.1-4 ./
    root@eto:~/lab1# docker run --name myweb --rm -P -it tinyhttpd:v0.1-4
    
    
  • 查看配置文件

    root@eto:~/lab1# docker container exec myweb cat /etc/nginx/conf.d/www.conf
    server {
        server_name c772ee1312f3;
        listen 0.0.0.0:80;
        root /data/web/html/;
    }
    
    root@eto:~/lab1# docker container exec myweb netstat -tanlup
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1
    
  • 修改環境變量

    root@eto:~/lab1# docker run --name myweb -e "PORT=8080"  --rm -P -it tinyhttpd:v0.1-4
    
  • 檢查端口

    root@eto:~/lab1# docker container exec myweb netstat -tanlup
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1/nginx -g daemon o
    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1/nginx -g daemon o
    

USER

用於指定運行image時,或運行Dockerfile中任何RUN,CMD或ENTRYPOINT指令指定的程序時的用戶名或UID

默認情況下,container的運行身份爲root

USER <UID>|<UserName>

注意:可以是任意數字,但實踐中必須是/etc/passwd中某用戶的有效UID,否則docker run命令將運行失敗

HEALTH CHECK

基於某個鏡像啓動容器時,如果主進程沒有停止,則容器一直是存在的。如果nginx進程運行正確,但是doc_root指向錯誤,客戶端訪問失敗,但容器正常運行。因此,docker判斷進程的健康狀態,僅僅是判斷進程運行與否,而不會判斷進程的健康與否。

因此,可使用CMD參數,用於檢測主進程工作狀態健康狀態,可以定義定義週期性任務計劃。或者使用NONE參數,不檢測健康狀態。

選項:
  --interval=DURATION(default: 30s)
  --timeout=DURATION(default: 30s)
  --start-period=DURATION(default: 30s)
  --retries=N(default: 3)
  
狀態碼:
  0:健康
  1:不健康
  2:保留,不使用

eg: HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1 

當容器啓動時,nginx可能並沒有馬上啓動。假設nginx啓動需要5s,如果測試馬上檢測健康狀態,則會失敗的。一旦失敗,將kill容器。

因此,可以設置爲,等待主進程初始化完畢的一段時間後,纔開始檢測。默認等待時長爲0s。

比如:

FROM nginx:stable-alpine
LABEL maintainer="jaywin"
ENV NGX_DOC_ROOT="/data/web/html/"

ADD index.html ${NGX_DOC_ROOT}
ADD entrypoint.sh /bin/

EXPOSE 80/tcp

HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${PORT:-80}/

CMD ["/usr/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]

構建並運行鏡像,正常運行

root@eto:~/lab1# docker run --name myweb -e "PORT=8080"  --rm -P -it tinyhttpd:v0.1-4
127.0.0.1 - - [05/May/2020:10:07:54 +0000] "GET / HTTP/1.1" 200 15 "-" "Wget" "-"

如果手動將index.html刪除了,則已經警告

root@eto:~/lab1# docker exec -it myweb /bin/sh
/ # rm -rf /data/web/html/index.html

2020/05/05 10:14:27 [error] 8#8: *14 directory index of "/data/web/html/" is forbidden, client: 127.0.0.1, server: 9a00fefe0364, request: "GET / HTTP/1.1", host: "0.0.0.0:8080"
127.0.0.1 - - [05/May/2020:10:14:27 +0000] "GET / HTTP/1.1" 403 153 "-" "Wget" "-" 

檢測失敗,超過3次嘗試後,宣告失敗。

SHELL

定義運行程序默認使用的shell。

在Linux中,爲["/bin/sh","-c"];在windows中,爲[“cmd”,"/S","/C"]

STOPSIGNAL

設定系統傳遞給容器的信號,一般無需變化。

ARG

只能在docker build時期使用,能在過程中使用並傳遞。可以讓一個docker file適用較多的場景,比如傳遞版本參數。

ARG <name>[=<defalut value>]

比如:

FROM nginx:stable-alpine
ARG author="jaywin <[email protected]>"
LABEL maintainer="$author" 

也能在build的過程中,傳遞ARG,以實現變量的傳遞。如此可解決ENV無法在build過程中傳遞參數的問題。

root@eto:~/lab1# docker build --build-arg author="hanna <[email protected]>" -t myweb:v0.3-1 ./

ONBUILD

用於在Dockerfile中定義一個觸發器。

dockerfile用於build鏡像文件,此鏡像文件也可作爲base image被另一個dockerfile用作FROM 指令的參數,並以之構建新的鏡像文件。

在後面這個dockerfile中的FROM指令在build過程中被執行時,將會觸發創建其base image的dockerfile文件中的 ONBUILD指令定義的觸發器

ONBUILD <INSTRUCTION>

儘管任何指令都可註冊成爲觸發器指令,但ONBUILD不能自我嵌套,且不會觸發FROM和MAINTAINER指令。

使用包含ONBUILD指令的Dockerfile構建的鏡像應該使用特殊的標籤,例如ruby:2.0-onbuild

在ONBUILD指令中使用ADD和COPY指令應該格外小心,因爲新構建過程的上下文在缺少指定源文件時會失敗

在dockerfile定義觸發器,不是在當前的build執行,而是在生產image後,再次基於image from後再執行。也就是被當做基礎鏡像後,製作鏡像後執行。也可理解爲延遲實行。

比如:

ONBUILD ADD http://mirrors.aliyun.com/repo/Centos-7.repo

Build後,實際並沒有執行onbuild執行。

基於新鏡像,定義dockerfile後,將執行此指令。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章