1.......................................................
搜索鏡像 mysql 為例
Docker search mysql
拉取鏡像
Docker pull mysql:5.7
查看鏡像
docker images
啟動(dòng)鏡像
docker run -p 12345:3306 \
--name mysql-01 \
-v /tmp/mysql/conf:/etc/mysql/conf.d \
-v /tmp/mysql/logs:/logs \
-v /tmp/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
進(jìn)入容器
docker exec -it 4787a6d530ad /bin/bash
2.......................................................
在docker鏡像的幫助下,你只能部署一個(gè)容器。然而,如果你需要從同一個(gè)鏡像部署多個(gè)容器(每個(gè)容器用于不同的任務(wù)),那么會(huì)發(fā)生什么?你可以借助dockerfile來解決這個(gè)問題。
Dockerfile是一個(gè)簡(jiǎn)單的文本文件,它包含了用戶可以在命令行上調(diào)用的所有命令來組裝或構(gòu)建一個(gè)鏡像。在docker build的幫助下,你可以很容易地自動(dòng)構(gòu)建,連續(xù)運(yùn)行dockerfile中定義的多個(gè)命令。
例如,你想從docker hub下載docker鏡像,以滿足你的特定開發(fā)需求。然后,你想更新鏡像,為你的開發(fā)過程安裝一些包。在這種情況下,你可以創(chuàng)建一個(gè)dockerfile,包含所有的參數(shù),以滿足你的特定需求,并構(gòu)建你的自定義鏡像。在創(chuàng)建dockerfile后,你可以反復(fù)使用它來構(gòu)建相同的鏡像,而不需要手動(dòng)安裝你的開發(fā)過程所需的所有包。
Dockerfile基本功能
在使用dockerfile之前,如果知道如何制作dockerfile是很重要的。Dockerfile包括特定的關(guān)鍵字,可以用來構(gòu)建特定的鏡像。下面列出了dockerfile中使用的所有關(guān)鍵字的簡(jiǎn)要解釋。
FROM:用于定義基礎(chǔ)鏡像,我們將在此基礎(chǔ)上構(gòu)建鏡像。
ADD:它用于向正在構(gòu)建的容器中添加文件。簡(jiǎn)單來說,RUN用于運(yùn)行命令并提交結(jié)果。
RUN:它用于通過安裝組件向基礎(chǔ)鏡像添加鏡像。
CMD:用于在容器開始時(shí)運(yùn)行命令。這些命令只有在運(yùn)行容器時(shí)沒有指定參數(shù)時(shí)才會(huì)運(yùn)行。
ENTRYPOINT:它用于在容器初始化過程中運(yùn)行命令。如果要在構(gòu)建鏡像后自動(dòng)啟動(dòng)容器,必須在 dockefile 中使用 ENTRYPOINT。
ENV:它用于在容器運(yùn)行時(shí)定義環(huán)境變量。
EXPOSE:它用于指定監(jiān)聽端口,以便在運(yùn)行時(shí)啟用網(wǎng)絡(luò)。
MAINTAINER:它用于指定鏡像創(chuàng)建者的名稱和電子郵件 ID。
USER:它用于指定用于運(yùn)行容器的用戶名。
VOLUME:它用于允許從容器訪問 Docker 主機(jī)上的目錄。
WORKDIR:它用于指定運(yùn)行時(shí)要執(zhí)行的命令的路徑。
LABEL:用來給docker鏡像添加標(biāo)簽。
創(chuàng)建Dockerfile
在本節(jié)中,我們將創(chuàng)建一個(gè)dockerfile,以從Ubuntu基本鏡像構(gòu)建LAMP服務(wù)器鏡像。
首先,您將需要?jiǎng)?chuàng)建一個(gè)目錄來存儲(chǔ)dockerfile。您可以使用以下命令創(chuàng)建它:
mkdir LAMP
接下來,在目錄內(nèi)創(chuàng)建一個(gè)名為Dockerfile的目錄:
nano LAMP/Dockerfile
添加以下行:
FROM ubuntu:latest
MAINTAINER Hitesh Jethva
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && apt-get install apache2 mariadb-server
libapache2-mod-php php php-cli php-common php-mysql php-json php-curl -y
CMD ["apachectl","-D","FOREGROUND"]
EXPOSE 8080
完成后,保存并關(guān)閉文件。
使用Dockerfile構(gòu)建鏡像
創(chuàng)建Dockerfile之后,您可以在Dockerfile的幫助下輕松創(chuàng)建自定義LAMP鏡像。
首先,將目錄更改為L(zhǎng)AMP并運(yùn)行以下命令以從該文件構(gòu)建鏡像:
cd LAMP
docker build -t "lamp:Dockerfile".
DEBIAN_FRONTEND這個(gè)環(huán)境變量,告知操作系統(tǒng)應(yīng)該從哪兒獲得用戶輸入。如果設(shè)置為”noninteractive”,你就可以直接運(yùn)行命令,而無需向用戶請(qǐng)求輸入(所有操作都是非交互式的)。這在運(yùn)行apt-get命令的時(shí)候格外有用,因?yàn)樗鼤?huì)不停的提示用戶進(jìn)行到了哪步并且需要不斷確認(rèn)。非交互模式會(huì)選擇默認(rèn)的選項(xiàng)并以最快的速度完成構(gòu)建。請(qǐng)確保只在Dockerfile中調(diào)用的RUN命令中設(shè)置了該選項(xiàng),而不是使用ENV命令進(jìn)行全局的設(shè)置。因?yàn)镋NV命令在整個(gè)容器運(yùn)行過程中都會(huì)生效,所以當(dāng)你通過BASH和容器進(jìn)行交互時(shí),如果進(jìn)行了全局設(shè)置那就會(huì)出問題。
正確的做法 – 只為這個(gè)命令設(shè)置ENV變量
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3
錯(cuò)誤地做法 – 為接下來的任何命令都設(shè)置ENV變量,包括正在運(yùn)行地容器
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y python3
我的示例如下:
FROM ubuntu:trusty
MAINTAINER mryqu
RUN \
DEBIAN_FRONTEND=noninteractive apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get -y install wget curl && \
DEBIAN_FRONTEND=noninteractive apt-get -y autoremove && \
DEBIAN_FRONTEND=noninteractive apt-get clean
3.......................................................
一、關(guān)于 Dockerfile
我們知道 Docker 鏡像的生成方式有兩種,一種是基于現(xiàn)有容器進(jìn)行 commit 生成一個(gè)鏡像,另外一種就是通過 Docker 提供的 Dockerfile 構(gòu)建一個(gè)鏡像。
Dockerfile 其實(shí)就是一個(gè)包含了 build image 過程中需要執(zhí)行的所有命令的文本文件,這些指令就是 Dockerfile 構(gòu)建時(shí)規(guī)定的一些指令,不過區(qū)區(qū)不到二十個(gè)指令。另外,也可以理解為 Dockfile 是一種被 Docker 程序解釋的腳本,由一條一條的指令組成,每條指令對(duì)應(yīng) Linux 系統(tǒng)下面的一條命令,由 Docker 程序?qū)⑦@些 Dockerfile 指令翻譯成真正的 Linux 命令。
我們通過一定的格式和指令編寫 Dockerfile 文件,然后通過 docker build 命令來讀取 Dockerfile 文件中的內(nèi)容從而構(gòu)建一個(gè)完整的鏡像。相比 image 這種黑盒子,Dockerfile 這種顯而易見的腳本更容易被使用者接受,它明確的表明這個(gè) image 是怎么產(chǎn)生的。有了 Dockerfile,當(dāng)我們需要定制自己額外的需求時(shí),只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻煩。
二、編寫 Dockerfile
其中 Dockerfile 的格式其實(shí)很簡(jiǎn)單,就兩類,一類是注釋信息,以 # 號(hào)作為注釋;另外一類就是一條條的指令了。其中指令是忽略大小寫的,但建議使用大寫,每一行只支持一條指令,每條指令可以攜帶多個(gè)參數(shù)。
而 Dockerfile 的指令根據(jù)作用可以分為兩種:構(gòu)建指令和設(shè)置指令。構(gòu)建指令用于構(gòu)建 image,其指定的操作不會(huì)在運(yùn)行 image 的容器上執(zhí)行;設(shè)置指令用于設(shè)置 image 的屬性,其指定的操作將在運(yùn)行 image 的容器中執(zhí)行。
在通過 Dockerfile 構(gòu)建鏡像時(shí)是按照指令的順序運(yùn)行的,所以需要自己根據(jù)相互的依賴關(guān)系調(diào)整好指令的相應(yīng)順序。但需要特別注意的一點(diǎn)就是,在 Dockerfile 中的第一個(gè)指令必須是 FROM 指令,除注釋信息。FROM 指令是用來引入一個(gè)基礎(chǔ)鏡像,我們后續(xù)的所有指令操作都是基于這個(gè)基礎(chǔ)鏡像而進(jìn)行的,如果沒有,那么 Dockerfile 是做不了的。
FROM(指定基礎(chǔ) image)
屬于構(gòu)建指令,用來加載一個(gè)基礎(chǔ)鏡像,比如 CentOS。此指令必須指定且需要是 Dockerfile 中的第一個(gè)指令,除注釋信息外;后續(xù)的指令都依賴于該指令引入的基礎(chǔ)鏡像而工作。
實(shí)踐中,F(xiàn)ROM 指令指定的基礎(chǔ)鏡像可以是官方遠(yuǎn)程倉庫中的,也可以位于本地倉庫。默認(rèn)情況下,docker build 會(huì)在 docker 主機(jī)上查找指定的鏡像文件,在其不存在時(shí),則會(huì)從 Docker Hub Registry 上拉取所需的鏡像文件。如果找不到指定的鏡像文件,docker build 會(huì)返回一個(gè)錯(cuò)誤信息。
該指令有兩種格式:
1,FROM指定基礎(chǔ) image 的名稱。
FROM ubuntu
2,FROM指定基礎(chǔ) image 的名稱和標(biāo)簽,tag 為可選選項(xiàng),省略時(shí)默認(rèn)為 latest。
FROM ubuntu:latest
MAINTAINER(用來指定鏡像創(chuàng)建者信息)
屬于構(gòu)建指令,用于將 image 的制作者相關(guān)的信息寫入到 image 中。當(dāng)我們對(duì)該 image 執(zhí)行 docker inspect 命令時(shí),輸出中有相應(yīng)的字段記錄該信息。
Dockerfile 并不限制 MAINTAINER 指令可在出現(xiàn)的位置,但推薦將其放置于 FROM 指令之后。格式:
MAINTAINER mryqu
此指令在新版本 Docker 中已經(jīng)被標(biāo)識(shí)為即將被廢棄,由 LABEL 指令替代,LABEL 指令相比較 MAINTAINER 更加豐富。
LABEL(鏡像標(biāo)簽信息)
屬于構(gòu)建指令,LABEL 相比于 MAINTAINER 有了更寬泛的適用領(lǐng)域。用來指令一個(gè)鏡像的各種元數(shù)據(jù),當(dāng)然其中就包括 MAINTAINER 信息。
LABEL==
可以看出 LABEL 支持的都是鍵值對(duì),比如 author=’dkey’。
ENV(用于設(shè)置環(huán)境變量)
屬于構(gòu)建指令,用于為鏡像定義所需的環(huán)境變量,并可被 Dockerfile 文件中位于其后的其它指令(如 ENV、ADD、COPY 等)所調(diào)用。語法格式如下:
ENV
或
ENV=
第一種格式中, 之后的所有內(nèi)容均會(huì)被視作其 的組成部分,包括空格,因此,一次只能設(shè)置一個(gè)變量。
第二種格式中,可用一次設(shè)置多個(gè)變量,每個(gè)變量為一個(gè)“=”的鍵值對(duì),如果 中包含空格,可以以反斜線 () 進(jìn)行轉(zhuǎn)義,也可通過對(duì) 加引號(hào)進(jìn)行標(biāo)識(shí);另外,反斜線也可用于續(xù)行。
在定義多個(gè)變量時(shí),建議使用第二種方式,以便構(gòu)建鏡像時(shí)在同一層中完成所有功能。
需要注意一點(diǎn),ENV 定義的變量是在 docker build 時(shí)替換的,但同時(shí)也會(huì)注入到以此鏡像啟動(dòng)的容器中,這是兩個(gè)不同的階段。容器啟動(dòng)后,可以通過 docker inspect 查看這個(gè)環(huán)境變量。如果想修改容器中的此環(huán)節(jié)變量,可以通過在啟動(dòng)容器時(shí)修改,比如使用 docker run –env key=value 命令修改環(huán)境變量,如果變量名稱相同,那么就會(huì)替換掉容器中對(duì)應(yīng)的環(huán)境變量,這個(gè)很好理解。
假如你安裝了 JAVA 程序,需要設(shè)置 JAVA_HOME,那么可以在 Dockerfile 中這樣寫:
ENV JAVA_HOME=/path/to/java/dirent
那么在 Dockerfile 中引用這個(gè)變量時(shí),其實(shí)跟 shell 中使用方式一樣了,比如引用方式是 ${variable_name},其中 {} 可省略,同樣可以使用默認(rèn)值,比如 ${variable_name: – WORD} 就是變量沒有值時(shí)就顯示 WORD 值,${variable_name: + WORD} 表示變量有值時(shí)就顯示 WORD 值,而 ${variable_name: = WORD} 表示變量沒有值時(shí)就把 WORD 賦值給變量。
同樣,這個(gè)變量會(huì)被注入到以這個(gè)鏡像啟動(dòng)的容器中,就是這個(gè)容器啟動(dòng)后就會(huì)有 JAVA_HOME 變量及對(duì)應(yīng)的值。
COPY(從 src 復(fù)制文件到容器的 dest 路徑)
用于從 Docker 主機(jī)復(fù)制文件至創(chuàng)建的新鏡像。
COPY
:要復(fù)制的源文件或目錄,支持使用通配符。
:目標(biāo)路徑,即正在創(chuàng)建的 image 的文件系統(tǒng)路徑;建議為 使用絕對(duì)路徑,否則 COPY 指定則以 WORKDIR 為其其實(shí)路徑。
注意:在路徑中有空白字符時(shí),通常使用第二種格式。另外, 必須是 build 上下文中的路徑,不能是其父目錄中的文件。如果 是目錄,則其內(nèi)部文件或子目錄會(huì)被遞歸復(fù)制,但 目錄自身不會(huì)被復(fù)制。如果指定了多個(gè) ,或在 中使用了通配符,則 必須是一個(gè)目錄,且必須以 / 結(jié)尾,否則被視為一個(gè)普通文件, 內(nèi)容直接被寫入到 中。如果 事先不存在,它將會(huì)被自動(dòng)創(chuàng)建,這包括其父目錄路徑。
ADD(從 src 復(fù)制文件到容器的 dest 路徑)
屬于構(gòu)建指令,ADD 指令類似于 COPY 指令,包括指令格式,但 ADD 支持使用 TAR 文件和 URL 路徑。如果 是一個(gè)可識(shí)別的壓縮格式,則 docker 會(huì)幫忙解壓縮,它將會(huì)被展開為一個(gè)目錄,其行為類似于 “tar -x” 命令。然而通過 URL 獲取到的 tar 文件將不會(huì)自動(dòng)展開。
RUN(鏡像構(gòu)建時(shí)的執(zhí)行命令)
屬于構(gòu)建指令,RUN 是用來運(yùn)行命令的,但是屬于構(gòu)建鏡像階段,RUN 可以運(yùn)行任何被基礎(chǔ)鏡像支持的命令。比如基礎(chǔ)鏡像選擇了 centos,那么軟件管理部分只能使用 centos 的命令,就可以使用 RUN 來運(yùn)行 yum 命令安裝一些軟件包。
該指令有兩種格式:
RUN (the command is run in a shell - `/bin/sh -c`)
RUN ["", "", "" ... ] (exec form)
第一種格式中, 通常是一個(gè) shell 命令,系統(tǒng)會(huì)自動(dòng)以“/bin/sh -c”來運(yùn)行此 ,這意味著 屬于 shell 的子進(jìn)程,那么此進(jìn)程在容器中的 PID 不為 1,不能接收 Unix 信號(hào),因此,當(dāng)使用 docker stop 命令停止容器時(shí),此進(jìn)程接收不到 SIGTERM 信號(hào)。
Tips:bash -c STRING 的含義,如果存在 -c 選項(xiàng),則表示從字符串中讀取命令。如果字符串有多個(gè)空格,第一個(gè)空格前面的字符串是要執(zhí)行的命令,也就是 $0,后面的是參數(shù),即$1,$2….。比如執(zhí)行命令 bash -c “./test hello world”,那么 ./test 就是 $0,hello 就是 $1,world 就是 $2,以此類推。
第二種語法格式中的參數(shù)是一個(gè) JSON 格式的數(shù)組(數(shù)組中的元素要使用雙引號(hào),不能使用單引號(hào),不然會(huì)報(bào)錯(cuò)),其中 為要運(yùn)行的命令,后面的 為傳遞給命令的選項(xiàng)或參數(shù);然而,此種格式指定的命令系統(tǒng)不會(huì)以“/bin/sh -c”來發(fā)起,由內(nèi)核創(chuàng)建,因此常見的 shell 操作,如變量替換以及通配符(?,*)替換將不會(huì)進(jìn)行;不過,如果要運(yùn)行的命令依賴于此 shell 特性的話,可以將其替換為類似下面的格式。
RUN ["/bin/bash", "-c", "executable", "param1", "param2" ... ]
比如我需要構(gòu)建一個(gè) web 鏡像,運(yùn)行一個(gè) httpd 服務(wù)并設(shè)置一個(gè)默認(rèn)頁面,如下:
FROM Busybox
ENV WEB_DOC_ROOT=“/opt/web/"
RUN mkdir -p ${DIR} && \
echo "
CMD(容器啟動(dòng)時(shí)的執(zhí)行命令)
屬于設(shè)置指令,CMD 跟 RUN 類似,也是用來運(yùn)行命令的。但兩者運(yùn)行的時(shí)間點(diǎn)不同,RUN 用于構(gòu)建鏡像時(shí)運(yùn)行命令,而 CMD 用于容器啟動(dòng)時(shí)運(yùn)行的命令,可以是一個(gè)服務(wù)啟動(dòng)命令,也可以是一個(gè)系統(tǒng)命令或腳本程序,且其運(yùn)行結(jié)束后,容器也將終止。但該指令只能在文件中存在一次,如果有多個(gè),則只執(zhí)行最后一條。這主要是因?yàn)?Docker 容器的啟動(dòng)只能運(yùn)行一個(gè)進(jìn)程,并且這個(gè)進(jìn)程 ID 號(hào)為 1 在容器中。至于為什么,這就涉及到了 namespaces 了,也就是容器的原理部分了。
我們知道在 Linux 中,如果我們?cè)诋?dāng)前 shell 下啟動(dòng)了一個(gè)前臺(tái)進(jìn)程,那么這個(gè)進(jìn)程就屬于這個(gè) shell 的子進(jìn)程了。當(dāng)我們退出或 kill 了這個(gè) shell 進(jìn)程時(shí),此 shell 進(jìn)程會(huì)一并關(guān)閉掉它的相關(guān)子進(jìn)程。注意,就算我們使用 & 把這個(gè)前臺(tái)進(jìn)程放置到后臺(tái)運(yùn)行,它還是屬于當(dāng)前 shell 進(jìn)程的。所以,大多數(shù)時(shí)候我們可能都是會(huì)使用一個(gè)叫 nohup 的命令配合 & 符號(hào)使用,nohub 其實(shí)主要就是把我們要運(yùn)行的進(jìn)行剝離當(dāng)前 shell,也就是不歸屬于 shell 的子進(jìn)程了,這個(gè)時(shí)候我們退出 shell 時(shí),那么自然就不會(huì)關(guān)閉我們使用 nohub 運(yùn)行的進(jìn)程了。
比如說我們使用 RUN 命令運(yùn)行一個(gè) CentOS 系統(tǒng)的容器,并且啟動(dòng)時(shí)執(zhí)行一個(gè) bash 程序,如下:
$ docker run -ti centos /bin/bash
正常情況下,我們就會(huì)進(jìn)入到這個(gè)容器的 shell 交互式模式,此時(shí)這個(gè) shell 進(jìn)程就是一個(gè) ID 號(hào)為 1 的進(jìn)程。如果說我們?cè)诋?dāng)前 shell 下啟動(dòng)一個(gè) nginx 服務(wù),那么這個(gè) nginx 服務(wù)就屬于這個(gè) shell 的子進(jìn)程。當(dāng)我們使用 exit 命令退出這個(gè) shell 時(shí),可以想到此時(shí)整個(gè)容器都會(huì)退出,父進(jìn)程退出子進(jìn)程也會(huì)退出,這個(gè)容器都沒有進(jìn)程存在了,自然也就退出了。
該指令有三種格式:
CMD command
CMD ["executable","param1","param2"] (like an exec, this is the preferred form)
CMD ["" ""] (as a shell)
前兩種語法格式的意義同 RUN 指令。
然后,我們接著 RUN 命令時(shí)用到的 Dockerfile 信息,運(yùn)行起來這個(gè)容器,就需要使用 CMD 指令來啟動(dòng) httpd 服務(wù),如下:
FROM Busybox
ENV WEB_DOC_ROOT="/opt/web"
RUN mkdir -p ${DIR} && \
echo "
EXPOSE(暴露容器端口)
EXPOSE 可以用來暴露端口,或者在 docker run 時(shí)指定--expose=1234,這兩種方式作用相同。但是,--expose可以接受端口范圍作為參數(shù),比如--expose=2000-3000。EXPOSE 和--expose都不依賴于宿主機(jī)器。默認(rèn)狀態(tài)下,這些規(guī)則并不會(huì)使這些端口可以通過宿主機(jī)來訪問。
基于 EXPOSE 指令的上述限制,Dockerfile 的作者一般在包含 EXPOSE 規(guī)則時(shí)都只將其作為哪個(gè)端口提供哪個(gè)服務(wù)的提示。使用時(shí),還要依賴于容器的操作人員進(jìn)一步指定網(wǎng)絡(luò)規(guī)則,需要配合docker run -p PORT:EXPORT使用,這樣 EXPOSE 設(shè)置的端口號(hào)會(huì)被指定需要映射到宿主機(jī)器的端口,這時(shí)要確保宿主機(jī)器上的端口號(hào)沒有被使用。如果直接指定 docker run -p EXPORT,這樣 EXPOSE 設(shè)置的端口號(hào)會(huì)被隨機(jī)映射成宿主機(jī)器中的一個(gè)端口號(hào)。不過通過 EXPOSE 命令文檔化端口的方式十分有用。
本質(zhì)上說,EXPOSE 或者--expose只是為其他命令提供所需信息的元數(shù)據(jù)(比如容器間 link 操作就依賴 EXPOSE 元數(shù)據(jù)),或者只是告訴容器操作人員有哪些已知選擇。
語法格式:
EXPOSE [...]
EXPOSE 指令可以一次設(shè)置多個(gè)端口號(hào),相應(yīng)的運(yùn)行容器的時(shí)候,可以配套的多次使用 -p 選項(xiàng)。
暴露一個(gè)端口;
EXPOSE port1
如果想代理EXPOSE端口, 相應(yīng)的運(yùn)行容器使用的命令;
docker run -p port1 image
暴露多個(gè)端口;
EXPOSE port1 port2 port3
如果想代理EXPOSE端口, 相應(yīng)的運(yùn)行容器使用的命令;
docker run -p port1 -p port2 -p port3 image
還可以指定需要映射到宿主機(jī)器上的某個(gè)端口號(hào);
docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image
注意,EXPOSE 僅僅是暴露一個(gè)端口,一個(gè)標(biāo)識(shí),在沒有定義任何端口映射時(shí),外部是無法訪問到容器提供的服務(wù)。而端口映射(-p)是 docker 比較重要的一個(gè)功能,原因在于我們每次運(yùn)行容器的時(shí)候容器的 IP 地址不能指定,而是在橋接網(wǎng)卡的地址范圍內(nèi)隨機(jī)生成的。宿主機(jī)器的 IP 地址是固定的,我們可以將容器的端口的映射到宿主機(jī)器上的一個(gè)端口,免去每次訪問容器中的某個(gè)服務(wù)時(shí)都要查看容器的IP的地址。對(duì)于一個(gè)運(yùn)行的容器,可以使用 docker port 加上容器 ID 和 EXPOSE 暴露的端口來查看該端口號(hào)在宿主機(jī)器上的映射端口。
$ docker port redis 6379
0.0.0.0:6380
VOLUME(掛載卷)
屬于設(shè)置指令,使容器中的一個(gè)目錄具有持久化存儲(chǔ)數(shù)據(jù)的功能,該目錄可以被容器本身使用,也可以共享給其他容器使用。我們知道容器使用的是 AUFS 聯(lián)合文件系統(tǒng),這種文件系統(tǒng)不能持久化數(shù)據(jù),當(dāng)容器刪除后,所有讀寫層的數(shù)據(jù)都會(huì)丟失。當(dāng)容器中的應(yīng)用有持久化數(shù)據(jù)的需求時(shí)可以在 Dockerfile 中使用該指令。格式:
######VOLUME ["<mountpoint>"]
比如我們?cè)?Dockerfile 中添加 VOLUME /data,然后通過該 Dockerfile 生成 image 的容器,/data 目錄中的數(shù)據(jù)在容器關(guān)閉后,里面的數(shù)據(jù)還存在。當(dāng)我們使用 docker inspect 命令查看已經(jīng)啟動(dòng)的容器信息時(shí),可以看到 Mounts 信息,如下:
"Mounts": [
{
"Type": "volume",
"Name": "c3e3bda4e64cc51af272bb7bb4a75f45b07216b6b34e87f24261813caf4e0347",
"Source": "/var/lib/docker/volumes/c3e3bda4e64cc51af272bb7bb4a75f45b07216b6b34e87f24261813caf4e0347/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
可以看到容器的 /data 目錄掛載在了 docker host 的具體路徑,如果使用 docker rm -f 命令刪除容器,此數(shù)據(jù)目錄也還是存在的。但如果使用 docker rm -fv 命令刪除容器,那么數(shù)據(jù)目錄會(huì)被一并刪除,這是需要注意的。
另外,另一個(gè)容器也有持久化數(shù)據(jù)的需求,且想使用上面容器共享的 /data 目錄,那么可以運(yùn)行下面的命令啟動(dòng)一個(gè)容器:
$ docker run -ti -volumes-from container_name image_name /bin/bash
其中 container_name 為 /data 目錄所在的容器名稱,image_name 為第二個(gè)容器運(yùn)行 image 的名字。
WORKDIR(切換目錄)
屬于設(shè)置指令,可以多次切換(相當(dāng)于 cd 命令),對(duì) RUN、CMD、ENTRYPOINT 生效。格式:
WORKDIR /path/to/workdir
在/p1/p2下執(zhí)行vim a.txt;
WORKDIR /p1 WORKDIR p2 RUN vim a.txt
USER(設(shè)置容器的用戶)
屬于設(shè)置指令,用于指定運(yùn)行鏡像時(shí)的或運(yùn)行 Dockerfile 中任何 RUN、CMD或ENTRYPOINT 指令指定的程序時(shí)的用戶名或 UID。默認(rèn)容器的運(yùn)行身份是 root 用戶。語法格式如下:
USER |
需要注意的是,可以為任意數(shù)字,但實(shí)踐中其必須為 /etc/passwd 中某用戶的有效 UID,否則,docker run 命令將運(yùn)行失敗。
HEALTHCHECK(健康檢查)
我們知道容器就是被隔離的進(jìn)程,當(dāng)一個(gè)容器中的進(jìn)程退出,那么意味著容器也就退出了。最常見的場(chǎng)景就是一個(gè)運(yùn)行一個(gè)容器的時(shí)候,如果進(jìn)程起不來,那么容器自然也起不來。Docker 引擎判斷一個(gè)容器是否能正常工作,是根據(jù)進(jìn)程的狀態(tài),而不是根據(jù)進(jìn)程運(yùn)行的服務(wù)是否正常來判斷的。比如我們運(yùn)行一個(gè) nginx,進(jìn)程都沒正常,但是 nginx 里面的靜態(tài)資源沒有,自然對(duì)用戶來說訪問就是有問題的,但 Docker 引擎并知道。
那么 Docker 就提供了 HEALTHCHECK 指令,讓我們可以定義服務(wù)層面的健康狀況檢查。一個(gè)簡(jiǎn)單的示例如下:
HEALTHCHECK --interval=5m --timeout=3s --start-period=1m --retries=3 CMD curl -f http://localhost || exit 1
其中--interval用來指定每次檢查的間隔時(shí)間,默認(rèn) 30s; --timeout是指檢測(cè)一次的等待超時(shí)時(shí)間,默認(rèn) 30s;--start-period表示容器啟動(dòng)后多久開始檢測(cè),有的服務(wù)啟動(dòng)時(shí)間較長(zhǎng),默認(rèn) 0s;--retries表示檢測(cè)重試的次數(shù),避免誤殺,默認(rèn) 3 次。
當(dāng)檢測(cè)命令發(fā)出后,我們可以定義返回檢測(cè)碼,0 表示健康,1 表示不健康,2 暫時(shí)是預(yù)留的。
STOPSIGNAL(定義停止容器的信號(hào))
一個(gè)容器就是運(yùn)行了一個(gè) PID 為 1 的進(jìn)程,也只有 PID 為 1 的進(jìn)程才可以接收信號(hào)。當(dāng)我們執(zhí)行 docker stop 命令時(shí),其實(shí)就是給進(jìn)程發(fā)送了一個(gè) -15 的信號(hào),進(jìn)程接收到了之后就退出了,進(jìn)程退出容器自然也就退出了,從而實(shí)現(xiàn)關(guān)閉容器的功能。
如果我們想使用別的信號(hào)來殺死進(jìn)程也可以的,比如 -9 信號(hào),強(qiáng)制殺死進(jìn)程。就可以使用 STOPSIGNAL 來指定,大多數(shù)情況下也不需要改。
ARG(構(gòu)建鏡像傳參)
ENV 指令可以用來定義變量,其定義的變量可以在構(gòu)建鏡像和容器運(yùn)行時(shí)使用。但是細(xì)心就會(huì)發(fā)現(xiàn),ENV 定義的變量在 Dockerfile 中都是固定的值,如果我們想在構(gòu)建鏡像時(shí)傳參,根據(jù)同一個(gè) Dockerfile 可以構(gòu)建不同的鏡像,那么就需要 ARG 指令了。ARG 指令就是用來定義鏡像構(gòu)建時(shí)傳參,然后 docker build --build-arg key="value" 指定要傳給鏡像構(gòu)建時(shí)的變量即可。
但需要注意,ARG 指令也沒有辦法放在 FROM 指令的前面,也就是說如果使用傳參的方式來構(gòu)建不同版本的 nginx,是不支持的。因?yàn)?ARG 沒辦法放在 FROM 指令前面,但 ARG 放在 FROM 后面時(shí),F(xiàn)ROM 指令又無法找到它要引用的變量,這就成了一個(gè)死循環(huán)了。
ONBUILD(子鏡像中執(zhí)行操作)
用于在 Dockerfile 中定義一個(gè)觸發(fā)器,Dockerfile 用于 build 映象文件,此映象文件亦可作為 base image 被另一個(gè) Dockerfile 用作 FROM 指令的參數(shù),并以之構(gòu)建新的映象文件。
在后面的這個(gè) Dockerfile 中的 FROM 指令在 build 過程中被執(zhí)行時(shí),將會(huì)“觸發(fā)”創(chuàng)建其 base image 的 Dockerfile 文件中的 ONBUILD 指令定義的觸發(fā)器。簡(jiǎn)單說就是,你定義了 Dockerfile,并使用了 ONBUILD 指令,在你進(jìn)行鏡像構(gòu)建時(shí),這個(gè) ONBUILD 指令并不會(huì)執(zhí)行;然后,其他人以你這個(gè)鏡像作為他編寫 Dockerfile 時(shí)的 base image,也就是 FROM 引用,他在進(jìn)行構(gòu)建時(shí)會(huì)執(zhí)行你的 ONBUILD 指令定義的動(dòng)作。
ONBUILD
盡管任何指令都可注冊(cè)成為觸發(fā)器指令,但 ONBUILD 不能自我嵌套,且不會(huì)觸發(fā) FROM 和 MAINTAINER 指令。
在 ONBUILD 指令中使用 ADD 或 COPY 指令時(shí)應(yīng)該格外小心,因?yàn)樾聵?gòu)建過程的上下文在缺少指定的源文件時(shí)會(huì)報(bào)錯(cuò)。但如果你使用 ADD 添加一個(gè)互聯(lián)網(wǎng)的 URL 就沒啥問題了。
構(gòu)建Dockerfile文件
創(chuàng)建一個(gè)構(gòu)建 nginx 鏡像的 Dockerfile 文件,Git地址:https://github.com/dongwenpeng/nginx。
FROM nginx
LABEL MAINTAINER="dkey"
ENV RUN_USER nginx
ENV RUN_GROUP nginx
ENV DATA_DIR /data/web
ENV LOG_DIR /data/log/nginx
RUN mkdir /data/log/nginx -p
RUN chown nginx.nginx -R /data/log/nginx
ADD nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
ENTRYPOINT nginx -g "daemon off;"
此 Dockerfile 很簡(jiǎn)單,做了這么幾件事:
拉取一個(gè) nginx 鏡像。
設(shè)置了幾個(gè)變量。
創(chuàng)建了幾個(gè)需要的目錄。
把當(dāng)前目錄下的 web 程序復(fù)制到鏡像的 /data/web 目錄。
把 nginx.conf 配置文件和 default.conf 配置文件復(fù)制到鏡像中。
設(shè)置一個(gè)默認(rèn)端口。
最后設(shè)置了容器啟動(dòng)時(shí)執(zhí)行的命令,我用來啟動(dòng)nginx程序,注意這個(gè)命令不能錯(cuò),不然容器啟動(dòng)不了。這樣設(shè)置后,當(dāng)你docker run運(yùn)行此鏡像時(shí)不需要在后面再次執(zhí)行需要執(zhí)行的命令了。
如果我能提前把這個(gè) nginx 容器可能需要更改的配置都進(jìn)行變量化,那么對(duì)于用戶來說是不是就可以不用關(guān)心這個(gè)容器的構(gòu)建過程,下載容器之后想改什么配置都可以通過傳遞變量的方式來進(jìn)行,是不是就方便很多。我們可以簡(jiǎn)單模擬一下這個(gè)場(chǎng)景,當(dāng)然,不會(huì)把所有參數(shù)都進(jìn)行變量化,只是作為演示。
FROM nginx
LABEL MAINTAINER="dkey"
ENV RUN_USER nginx
ENV RUN_GROUP nginx
ENV NGX_DOC_ROOT="/data/web/html/"
ADD entrypoint.sh /bin
EXPOSE 80
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]
entrypoint.sh 腳本的內(nèi)容:
#!/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 "$@"
最后這個(gè) exec 特別關(guān)鍵了,其中 $@ 是用來取所有參數(shù)的,也就是 CMD 指令指定的容器運(yùn)行程序。使用 exec 來執(zhí)行命令,表示替換執(zhí)行 entrypoint.sh 的進(jìn)程,從而把 nginx 進(jìn)程變成 1 號(hào)進(jìn)程,只有成為 1 號(hào)進(jìn)程才能接受信號(hào),比如 stop、kill 等指令。
構(gòu)建nginx鏡像
Dockerfile 編寫好了之后就可以通過 docker build 來進(jìn)行構(gòu)建了,需要注意 docker build 命令的執(zhí)行需要在 dockerfile 文件所在目錄,會(huì)自動(dòng)找到以 dockerfile 命令的文件,然后就會(huì)開始進(jìn)行鏡像構(gòu)建??梢允褂?-t 參數(shù)指定鏡像的名稱或名稱加 tag。
$ docker build -t nginx_01 .
Sending build context to Docker daemon 3.43 MB
Step 1 : FROM nginx
latest: Pulling from library/nginx
64f0219ba3ea: Pull complete
325b624bee1c: Pull complete
Digest: sha256:2a07a07e5bbf62e7b583cbb5257357c7e0ba1a8e9650e8fa76d999a60968530f
Status: Downloaded newer image for nginx:latest
---> 19146d5729dc
Step 2 : MAINTAINER dkey
---> Running in a9d12e4fa972
---> 3e6bcc394986
Removing intermediate container a9d12e4fa972
Step 3 : ENV RUN_USER nginx
---> Running in 55e165c28124
---> 024ba866269e
Removing intermediate container 55e165c28124
Step 4 : ENV RUN_GROUP nginx
---> Running in 577cb4556709
---> 95f5b6de5080
Removing intermediate container 577cb4556709
Step 5 : ENV DATA_DIR /data/web
---> Running in e472aaa991f2
---> d50decbaf7bc
Removing intermediate container e472aaa991f2
Step 6 : ENV LOG_DIR /data/log/nginx
---> Running in 5ccf9955685d
---> 00f1deefcc1b
Removing intermediate container 5ccf9955685d
Step 7 : RUN mkdir /data/log/nginx -p
---> Running in 63a61c463ad4
---> 467d1548998c
Removing intermediate container 63a61c463ad4
Step 8 : RUN chown nginx.nginx -R /data/log/nginx
---> Running in 00d559b2de7b
---> a550d1639d98
Removing intermediate container 00d559b2de7b
Step 9 : ADD web /data/web
---> e75181ff11e0
Removing intermediate container b4c03ff5df61
Step 10 : ADD nginx.conf /etc/nginx/nginx.conf
---> 00dcb2ec895f
Removing intermediate container 4a75f67c4f1c
Step 11 : ADD default.conf /etc/nginx/conf.d/default.conf
---> 9b2668d59294
Removing intermediate container d57c3e297464
Step 12 : EXPOSE 80
---> Running in 076209139acf
---> fa3247254200
Removing intermediate container 076209139acf
Step 13 : ENTRYPOINT nginx -g "daemon off;"
---> Running in e0a4845172cb
---> fa37625af3ac
Removing intermediate container e0a4845172cb
Successfully built fa37625af3ac
從執(zhí)行過程可以看出一共十三個(gè)步驟,都完成了。
下面可以看看鏡像了。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_01 latest fa37625af3ac 38 seconds ago 182.7 MB
nginx latest 19146d5729dc 6 days ago 181.6 MB
可以看到從官方拉取了一個(gè) nginx 鏡像,然后在此鏡像的基礎(chǔ)上構(gòu)建了 nginx_01 鏡像。注意,由于 nginx_01 是在 nginx 鏡像的基礎(chǔ)上構(gòu)建出來的,所以如果你要?jiǎng)h除 nginx 鏡像是不允許的,只有先刪除 nginx_01 鏡像后才可以刪除 nginx 鏡像。
同理,你可以接著創(chuàng)建第二個(gè)鏡像,這個(gè)時(shí)候你會(huì)發(fā)現(xiàn)構(gòu)建速度非常之快。
$ docker build -t nginx_02 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_02 latest fa37625af3ac 54 seconds ago 182.7 MB
nginx_01 latest fa37625af3ac 54 seconds ago 182.7 MB
nginx latest 19146d5729dc 6 days ago 181.6 MB
但你也應(yīng)該發(fā)現(xiàn),這兩個(gè)鏡像的 IMAGE ID 是相同的,因?yàn)?docker 檢測(cè)出你的 dockerfile 文件沒有任何內(nèi)容改變(包括要復(fù)制文件的內(nèi)容),所以只是做了一個(gè)鏈接。這個(gè)時(shí)候你可以運(yùn)行這個(gè)鏡像看看。
$ docker run --name nginx_01 -d -p 80:80 nginx_01
$ docker exec -ti nginx_01 bash
root@0dbeb4d3852f:/# ss -nplt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:80 :
root@0dbeb4d3852f:/# cat /data/log/nginx/access.log
172.28.24.96 - - [27/Dec/2016:08:37:58 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) Chrome/55.0.2883.87 Safari/537.36" "-"
從上面的結(jié)果可以看出,這個(gè) nginx 容器運(yùn)行一切正常。你可以在瀏覽器訪問看看。
這個(gè)時(shí)候你可以選擇刪除一個(gè)鏡像看看。
$ docker rmi nginx_02
Untagged: nginx_02:latest
成功刪除,不管你刪除哪個(gè)鏡像都可以刪除,但是只能刪除一個(gè)。再次刪除另一個(gè)時(shí)就會(huì)報(bào)錯(cuò)。
$ docker rmi nginx_01
Error response from daemon: conflict: unable to remove repository reference "nginx_01"
如果我們改變了網(wǎng)站代碼或者改變了配置文件,再次構(gòu)建鏡像時(shí)會(huì)怎么樣呢?
其實(shí) dockerfile 中有任何的改動(dòng),再次構(gòu)建鏡像時(shí)都會(huì)重新構(gòu)建,但是只會(huì)從改動(dòng)的地方開始重新構(gòu)建,速度很快,這就是鏡像分層的好處。
$ docker build -t nginx_02 .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_02 latest 9868ca64a680 9 seconds ago 182.7 MB
使用commit命令提交新鏡像
通過上面我們知道,通過 dockerfile 可以構(gòu)建一個(gè)鏡像。同樣使用 commit 也可以提交一個(gè)新鏡像。跟 build 不同的是,commit 只能從現(xiàn)有的容器之上提交出一個(gè)新的鏡像。比如,你對(duì) nginx 鏡像做好了基本配置,然后就可以把這個(gè)鏡像提交為一個(gè)新的鏡像。
commit 語法:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
dockercommit[OPTIONS]CONTAINER[REPOSITORY[:TAG]]
提交一個(gè)新的鏡像。
$ docker commit -a pengdongwen nginx_01 nginx_10
sha256:2cdae15e08fd9160fc24998a4f6cae270cd2130980fd7c48cc9da70b1b67671e
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx_10 latest 2cdae15e08fd 6 seconds ago 182.7 MB
原文鏈接:https://blog.csdn.net/weixin_29065015/article/details/112045524
dockerfile文件內(nèi)容
FROM ubuntu:16.04
MAINTAINER wangyaoheng@ysstech.com
ENV REFRESHED_AT 2018-12-18 16:57
ADD jdk8.tar.gz /usr/local/
ADD apache-tomcat-9.0.8.tar.gz /usr/local/
ADD entrypoint.sh /usr/local
RUN apt-get update && apt-get install -yqq net-tools telnet localepurge
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV JAVA_HOME /usr/local/jdk1.8.0_111
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.8
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
ENV ORACCONFIG_HOME /usr/local/docker/orac_home
ENV JAVA_OPTS="$JAVA_OPTS -server -Xms1024m -Xmx2048m -XX:PermSize=512M -XX:MaxNewSize=1024m -XX:MaxPermSize=1024m"
EXPOSE 8080
EXPOSE 1090
ENTRYPOINT ["sh","/usr/local/entrypoint.sh"]
編譯并生成鏡像 (. 表示當(dāng)前路徑)
docker build -t tomcat9.0 .
(docker rm fb93e677d775)刪除容器
(docker rmi 鏡像ID)刪除指令
查看生成鏡像
docker images
創(chuàng)建容器并運(yùn)行
docker run -di -u root -p 8080:8080 --name=orac tomcat9.0
進(jìn)入容器
docker exec -it 77ba19db7129 /bin/bash
entrypoint.sh內(nèi)容
!/bin/bash
/usr/local/apache-tomcat-9.0.8/bin/startup.sh
tail -f /usr/local/apache-tomcat-9.0.8/logs/catalina.out