Docker 鏡像的創建
創建鏡像有三種方法,分別為基於已有鏡像創建、基於本地模板創建以及基於Dockerfile創建。
1. 基於現有鏡像創建
(1)首先啟動一個鏡像,在容器里做修改
docker run -idt --name No1 centos:7 /bin/bash
docker ps-a
(2)然后將修改后的容器提交為新的鏡像,需要使用該容器的 ID 號創建新鏡像
docker commit -m "new" -a "centos" 208181f8f689 centos:dd
#常用選項∶
-m:說明信息
-a:作者信息
-p:生成過程中停止容器的運行
docker images
2. 基於本地模板創建
通過導入操作系統模板文件可以生成鏡像, 模板可以從 OPENVZ 開源項目下載,下載地址為http://openvz.org/Download/template/precreated
#格式:wget [下載路徑] -o [保存路徑]、curl -L [下載路徑] -o [保存路徑]
wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
#導入為鏡像
cat debian-7.0-x86-minimal.tar.gz | docker import - debian:dd
3. 基於Dockerfile 創建鏡像
聯合文件系統(UnionFS)
UnionFS(聯合文件系統):Union文件系統(UnionFS)是一種分層、輕量級並且高性能的文件系統,它支持對文件系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下。AUFS、OverlayFS 及 Devicemapper 都是一種 UnionFS(現在用的overlay2聯合文件系統)。
Union文件系統是Docker鏡像的基礎。鏡像可以通過分層來進行繼承,基於基礎鏡像(沒有父鏡像),可以制作各種具體的應用鏡像。
特性∶一次同時加載多個文件系統,但從外面看起來,只能看到一個文件系統,聯合加載會把各層文件系統疊加起來,這樣最終的文件系統會包含所有底層的文件和目錄。
我們下載的時候看到的一層層的就是聯合文件系統。
鏡像加載原理
Docker的鏡像實際上由一層一層的文件系統組成,這種層級的文件系統就是UnionFS。
bootfs主要包含bootloader和kernel,bootloader主要是引導加載kernel,Linux剛啟動時會加載bootfs文件系統。
在Docker鏡像的最底層是bootfs,這一層與我們典型的Linux/Unix系統是一樣的,包含boot加載器和內核。當boot加載完成之后整個內核就都在內存中了,此時內存的使用權己由bootfs轉交給內核,此時系統也會卸載bootfs(bootfs加載宿主機內核並提供與鏡像共享)
rootfs在bootfs之上。包含的就是典型Linux系統中的/dey、/proc、/bin、/etc等標准目錄和文件。rootfs就是各種不同的操作系統發行版,比如Ubuntu、Centos等等(rootfs提供相同的運行環境,不同發行的操作系統和目錄環境)
簡答來說一開始bootfs引導加載宿主機內核,所有鏡像的運行創建基於共享宿主機的內核,操作一個命令下載debian(通用操作系統),這時就會在內核上面加了一層基礎鏡像,再安裝一個emacs, 會在基礎鏡像上疊加一層image,接着再安裝一個apache,又會在images上面再疊加一層image。最后它們看起來就像一個文件系統即容器的rootfs。在Docker的體系里把這些rootfs叫做Docker的鏡像。但是,此時的每一層rootfs都是read-only的,我們此時還不能對其進行操作。當我們創建一個容器,也就是將Docker鏡像進行實例化,系統會在一層或是多層read-only的rootfs(只讀層)之上分配一層空的read-write的rootfs(可讀可寫層)。
在鏡像實例化運行就會附加可讀可寫層,也就是容器化運行,用戶會對可讀可寫層進行操作,數據會只會存儲在讀寫層,伴隨着容器的生命周期。不會對鏡像本身進行操作,如果刪除容器,那之前在讀寫層進行的操作數據也會消失,讀寫層之前累計的層數都為只讀層
Dockerfile
Docker鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時准備的一些配置參數(如匿名卷、環境變量、用戶等)。鏡像不包含任何動態數據,其內容在構建之后也不會被改變。
鏡像的定制實際上就是定制每一層所添加的配置、文件。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入一個腳本,用這個腳本來構建、定制鏡像,那么鏡像構建透明性的問題、體積的問題就都會解決。這個腳本就是Dockerfile。
Dockerfile是一個文本文件,其內包含了一條條的指令(Instruction),每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。有了Dockerfile,當我們需要定制自己額外的需求時,只需在Dockerfile上添加或者修改指令,重新生成 image即可,省去了敲命令的麻煩。
除了手動生成Docker鏡像之外,可以使用Dockerfile自動生成鏡像。Dockerfile是由多條的指令組成的文件,其中每條指令對應 Linux 中的一條命令,Docker 程序將讀取Dockerfile 中的指令生成指定鏡像。
Dockerfile結構大致分為四個部分∶基礎鏡像信息、維護者信息、鏡像操作指令和容器啟動時執行指令。Dockerfile每行支持一條指令,每條指令可攜帶多個參數,支持使用以"#"號開頭的注釋。
Docker 鏡像結構的分層
鏡像不是一個單一的文件,而是有多層構成。容器其實是在鏡像的最上面加了一層讀寫層,在運行容器里做的任何文件改動,都會寫到這個讀寫層。如果刪除了容器,也就刪除了其最上面的讀寫層,文件改動也就丟失了。Docker使用存儲驅動管理鏡像每層內容及可讀寫層的容器層。
(1)Dockerfile 中的每個指令都會創建一個新的鏡像層,后面疊加的每一次鏡像都由overlay2聯合文件系統管理
(2)鏡像層將被緩存和復用
(3)當Dockerfile的指令修改了,復制的文件變化了,或者構建鏡像時指定的變量不同了,對應的鏡像層緩存就會失效
(4)某一層的鏡像緩存失效,它之后的鏡像層緩存都會失效(之后的鏡像層都在該層基礎上疊加)
(5)鏡像層是不可變的,如果在某一層中添加一個文件,然后在下一層中刪除它,則鏡像中依然會包含該文件,只是這個文件在 Docker 容器中用戶不可見了。
Dockerfile 操作常用的指令
(1)FROM鏡像名
指定新鏡像所基於的基礎鏡像,第一條指令必須為EROM 指令,每創建一個鏡像就需要一條 FROM 指令
(2)MAINTAINER 名字
說明新鏡像的維護人信息
(3)RUN 命令
在所基於的鏡像上執行命令,並提交到新的鏡像中
(4)ENTRYPOINT ["要運行的程序", "參數 1", "參數 2"]
設定容器啟動時第一個運行的命令及其參數。
可以通過使用命令docker run --entrypoint 來覆蓋鏡像中的ENTRYPOINT指令的內容。
docker run指定的命令優先級最高,其次為ENTRYPOINT指定的命令最后為CMD
例如:
ENTRYPOINT [ "ls", "-lf", "*"]
(5)CMD ["要運行的程序", "參數1", "參數2"]
上面的是exec形式,shell形式: CMD命令 參數1 參數2
啟動容器時默認執行的命令或者腳本,Dockerfile只能有一條CMD命令。如果指定多條命令,只執行最后一條命令
如果在docker run時指定了命令或者鏡像中有ENTRYPOINT,那么CDM就會被覆蓋。
CMD 可以為 ENTRYPOINT 指令提供默認參數,相當於傳參的作用。
例如:
ENTRYPOINT ["ls"]
CMD ["-lf", "*"]
(6)EXPOSE 端口號
指定新鏡像加載到 Docker 時要開啟的端口
(7)ENV 環境變量 變量值
設置一個環境變量的值,會被后面的 RUN 使用
例如:
ENV PATH=$PATH:/opt #中間的 = 號可以使用空格替代
(8)ADD 源文件/目錄 目標文件/目錄
將源文件復制到鏡像中,源文件要與 Dockerfile 位於相同目錄中,或者是一個URL
有如下注意事項∶
1、如果源路徑是個文件,且目標路徑是以 / 結尾,則docker會把目標路徑當作一個目錄,會把源文件拷貝到該目錄下。如果目標路徑不存在,則會自動創建目標路徑。
2、如果源路徑是個文件,且目標路徑是不是以/結尾,則docker會把目標路徑當作一個文件。
如果目標路徑不存在,會以目標路徑為名創建一個文件,內容同源文件
如果目標文件是個存在的文件,會用源文件覆蓋它, 當然只是內容覆蓋,文件名還是目標文件名。
如果目標文件實際是個存在的目錄,則會源文件拷貝到該目錄下。注意,這種情況下,最好顯示的以 / 結尾,以避免混淆。
3、如果源路徑是個目錄,且目標路徑不存在,則docker會自動以目標路徑創建一個目錄,把源路徑目錄下的文件拷貝進來。如果目標路徑是個已經存在的目錄,則docker會把源路徑目錄下的文件拷貝到該目錄下。
4、如果源文件是個歸檔文件(使用tar命令壓縮的文件),則docker會自動幫解壓。
URL下載和解壓特性不能一起使用。任何壓縮文件通過URL拷貝,都不會自動解壓。
(9)COPY 源文件/目錄 目標文件/目錄
只復制本地主機上的文件/目錄復制到目標地點,源文件/目錄要與Dockerfile 在相同的目錄中
(10)VOLUME ["目錄"]
在容器中創建一個掛載點
(11)USER用戶名/UID
指定運行容器時的用戶
(12)WORKDIR 路徑
為后續的 RUN、CMD、ENTRYPOINT 指定工作目錄
(13)ONBUILD 命令
指定所生成的鏡像作為一個基礎鏡像時所要運行的命令。
當在一個Dockerfile文件中加上ONBUILD指令,該指令對利用該Dockerfile構建鏡像(比如為A鐿像)不會產生實質性影響。
但是當編寫一個新的Dockerfile文件來基於A鏡像構建一個鏡像(比如為B鏡像)時,這時構造A鏡像的Dockerfile文件中的ONBUILD指令就生效了,在構建B鏡像的過程中,首先會執行ONBUILD指令指定的指令,然后才會執行其它指令。
(14)HEALTHCHECK
健康檢查
注意:Dockerfile中ENTRYPOINT和CMD命令的作用和區別
ENTRYPOINT和CMD都是容器啟動時默認執行的命令,ENTRYPOINT需要嚴格遵守exec形式,CMD命令可以使用shell形式
ENTRYPOINT的優先級比CMD命令更高,如果同時存在會覆蓋CMD指定的命令,docker run指定的命令優先級最高
Dockerfile中只能有一條CMD命令,多條CMD只會執行最后一條
注意:Dockerfile中ADD和COPY命令的作用和區別
他們都具有復制本地文件、目錄到鏡像的功能
ADD在復制tar壓縮包的歸檔文件時候自動支持解壓縮,並且支持使用url路徑拉取文件
COPY只能復制本地文件/目錄到鏡像
編寫 Dockerfile 時需要嚴格遵循的格式
●第一行必須使用 FROM 指令指明所基於的鏡像名稱
●之后使用 MAINTAINER 指令說明維護該鏡像的用戶信息(可以忽略)
●然后是鏡像操作相關指令,如 RUN 指令。每運行一條指令,都會給基礎鏡像添加新的一層(因為RUN指令會不斷疊加鏡像,增大整個鏡像的大小,盡量減少多條RUN命令)。
●最后使用 CMD 指令指定啟動容器時要運行的命令操作。
●指令名都需要大寫
Dockerfile使用
一、Apache 服務yum安裝配置
#建立工作目錄 mkdir /opt/apache cd /opt/apache vim Dockerfile FROM centos:7 #基於的基礎鏡像 MAINTAINER this is apache image <dd> #維護鏡像的用戶信息 RUN yum -y update #鏡像操作更新yum倉庫 RUN yum -y install httpd #鏡像操作指令yum安裝apache軟件 EXPOSE 80 #開啟 80 端口 ADD index.html /var/www/html/ #復制網站首頁文件 #方法一: ADD run.sh /run.sh #將服務啟動腳本復制到鏡像中 RUN chmod 755 /run.sh CMD ["/run.sh"] #創建容器后啟動該腳本 #方法二: ENTRYPOINT [ "/usr/sbin/apachectl" ] CMD [ "-D", "FOREGROUND" ] #ENTRYPOINT指定的命令用來控制Apache HTTP服務器的程序,CMD指定的是傳給ENTRYPOINT命令的參數,使apache服務前台啟動,因為Docker容器僅在它的1號進程(PID為1)運行時,會保持運行。
如果1號進程退出了,Docker容器也就退出了,所以為了保證該apache容器創建運行后不直接關閉將服務放在前台一直進行執行 #如果使用方法一需要准備執行腳本 vim run.sh #!/bin/bash rm -rf /run/httpd/* #清理httpd的緩存 /usr/sbin/apachectl -D FOREGROUND #指定為前台運行 echo "this is web1">index.html #准備網站頁面 #生成鏡像 docker build -t httpd:centos . #注意別忘了末尾有".",表示使用當前路徑下的文件構建鏡像
#新鏡像運行容器,並進行web測試 docker run -it --name No1 -p 5555:80 httpd:centos http://192.168.150.30:5555 #如果有網絡報錯提示# [Warning] IPv4 forwarding is disabled.Networking will not work. 解決方法∶ vim /etc/sysctl.conf #配置內核轉發 net.ipv4.ip forward=1 sysctl -p #重載內核配置,重啟網卡和docker服務 systemctl restart network systemctl restart docker
二、Nginx服務源碼安裝配置
mkdir /opt/nginx cd /opt/nginx vim Dockerfile FROM centos:7 MAINTAINER this is nginx image <dd> RUN yum -y install pcre-devel zlib-devel gcc gcc-c++ make;useradd -M -s /sbin/nologin nginx #nginx源碼編譯安裝需要的工具和環境,創建nginx運行的用戶,兩條命令中間使用 ; 號間隔 ADD nginx-1.12.0.tar.gz /opt/ #將nginx源碼包復制到鏡像中的opt目錄下 WORKDIR /opt/nginx-1.12.0 #指定默認執行命令的路徑 RUN ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_stub_status_module;make -j 2 && make install #安裝模塊並且編譯安裝 EXPOSE 80 #開啟80端口 ADD index.html /usr/local/nginx/html/index.html #復制首頁文件到鏡像中 CMD /usr/local/nginx/sbin/nginx -g "daemon off;" #前台啟動nginx的命令 echo "this is nginx" > index.html #准備首頁文件 docker build -t ngnix:dd . #讀取Dockerfile文件創建鏡像 docker run -itd --name No1 -p 5333:80 nginx:dd #使用該鏡像創建容器 docker ps -a http://192.168.150.30:5333