Docker的數據管理、網絡通信和dockerfile
一、Docker的數據管理
管理Docker容器中數據主要有兩種方式:數據卷(Data Volumes)和數據卷容器(Data Volumes Containers)。
1. 數據卷
1.1 數據卷定義
數據卷是一個供容器使用的特殊目錄,位於容器中。可將宿主機的目錄掛載到數據卷上,對數據卷的修改操作立刻可見,並且更新數據不會影響鏡像,從而實現數據在宿主機與容器之間的遷移。數據卷的使用類似於Linux下對目錄進行的mount操作。
1.2 數據卷配置
宿主機目錄/var/www掛載到容器中的/data1。
注意:宿主機本地目錄的路徑必須是使用絕對路徑。如果路徑不存在,Docker會自動創建相應的路徑。
[root@docker ~]# cd /var/www
[root@docker www]# ls
cgi-bin html
[root@docker www]# docker run -it -v /var/www:/test --name test centos:7 bash
[root@704c227d8f3a /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys test tmp usr var
[root@704c227d8f3a /]# cd test
[root@704c227d8f3a test]# ls
cgi-bin html
[root@704c227d8f3a test]# echo 'test' > test.txt
[root@704c227d8f3a test]# exit
exit
[root@docker www]# ls
cgi-bin html test.txt
[root@docker www]# cat test.txt
test
2. 數據卷容器
如果需要在容器之間共享一些數據,最簡單的方法就是使用數據卷容器。數據卷容器是一個普通的容器,專門提供數據卷給其他容器掛載使用。
2.1 創建數據卷容器
[root@docker www]# docker run -it --name test1 -v /test1 -v /test2 centos:7 bash
[root@c2e17eaba020 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys test1 test2 tmp usr var
[root@c2e17eaba020 /]# ls test1
[root@c2e17eaba020 /]# ls test2
[root@c2e17eaba020 /]# echo "this is test1" > test1/test1.txt
[root@c2e17eaba020 /]# echo "this is test2" > test2/test2.txt
2.2 使用--volume-from來掛載test1
[root@docker www]# docker run -it --name test1 -v /test1 -v test2 centos:7 bash
[root@c2e17eaba020 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys test1 test2 tmp usr var
[root@c2e17eaba020 /]# ls test1
[root@c2e17eaba020 /]# ls test2
[root@c2e17eaba020 /]# echo "this is test1" > test1/test1.txt
[root@c2e17eaba020 /]# echo "this is test2" > test2/test2.txt
[root@c2e17eaba020 /]# exit
exit
[root@docker www]# docker run -it --volumes-from test1 --name test2 centos:7 bash
[root@ad08168f508d /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys test1 test2 tmp usr var
[root@ad08168f508d /]# ls test1
test1.txt
[root@ad08168f508d /]# cat test1/test1.txt
this is test1
[root@ad08168f508d /]# ls test2
test2.txt
[root@ad08168f508d /]# cat test2/test2.txt
this is test2
二、端口映射
在啟動容器的時候,如果不指定對應的端口,在容器外是無法通過網絡來來訪問容器內的服務。端口映射機制將容器內的服務提供給外部網絡訪問,實際上就是將宿主機的端口映射到容器中,使得外部網絡訪問宿主機的端口便可訪問容器內的服務。
[root@docker www]# docker run -d --name test1 -P nginx
0a07519f54de9552a37d076613f9f5af4ff4b80d1d3c258762c45f876a29500b
[root@docker www]# docker run -d --name test2 -p 11111:80 nginx
9316ffd9e2aa98e8d1ea679128d308fc0f1842d854e8b772c7cd5c07647251a8
[root@docker www]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9316ffd9e2aa nginx "/docker-entrypoint.…" 1 second ago Up 1 second 0.0.0.0:11111->80/tcp, :::11111->80/tcp test2
0a07519f54de nginx "/docker-entrypoint.…" 19 seconds ago Up 18 seconds 0.0.0.0:49154->80/tcp, :::49154->80/tcp test1
curl測試
[root@docker www]# curl http://192.168.122.10:11111
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@docker www]# curl http://192.168.122.10:49154
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
三、容器互聯
容器互聯是通過容器的名稱在容器間建立一條專門的網絡通信隧道。簡單點說,就是會在源容器和接收容器之間建立一條隧道,接收容器可以看到源容器指定的信息。
1. 創建容器互聯
[root@docker www]# docker run -itd -P --name test1 centos:7 bash
d83da111bcb2307629c6a5f0614dd1697b1be8c26725ce50377627814f13d00b
[root@docker www]# docker run -itd -P --name test2 --link test1:TEST centos:7 bash
##--link test1:TEST指定互聯容器名為test1,別名為TEST
2adcd159aba40a293f2a8385b2c3dbaceaaa99c6fa289bf556a6f03bb1e1d170
2. 進入test2測試(ping 容器名/別名)
[root@docker www]# docker exec -it test2 bash
[root@2adcd159aba4 /]# ping test1
PING TEST (172.17.0.2) 56(84) bytes of data.
64 bytes from TEST (172.17.0.2): icmp_seq=1 ttl=64 time=0.061 ms
64 bytes from TEST (172.17.0.2): icmp_seq=2 ttl=64 time=0.050 ms
^C
--- TEST ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.050/0.055/0.061/0.009 ms
[root@2adcd159aba4 /]# ping TEST
PING TEST (172.17.0.2) 56(84) bytes of data.
64 bytes from TEST (172.17.0.2): icmp_seq=1 ttl=64 time=0.075 ms
64 bytes from TEST (172.17.0.2): icmp_seq=2 ttl=64 time=0.048 ms
^C
--- TEST ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.048/0.061/0.075/0.015 ms
四、Docker鏡像的創建
創建鏡像有三種方法,分別為基於已有鏡像創建、基於本地模板創建以及基於Dockerfile創建。
1. 基於現有鏡像創建
1.1 首先啟動一個鏡像,在容器里做修改
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 87a94228f133 28 hours ago 133MB
centos 7 eeb6ee3f44bd 3 weeks ago 204MB
[root@docker ~]# docker create -it --name test1 centos:7 bash
ae03a782eb8fed80190bfee1c5e459c1e82e1406a55627553e93edc943645217
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ae03a782eb8f centos:7 "bash" 2 seconds ago Created test1
1.2 然后將修改后的容器提交為新的鏡像,需要使用該容器的ID號創建新鏡像
[root@docker ~]# docker commit -m "new" -a "centos" test1 centos:test1
sha256:3e6e91b628effd49a7fa8fea422057cb392c76e852b2d60f9d7287bfa172ff88
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos test1 3e6e91b628ef 10 seconds ago 204MB
nginx latest 87a94228f133 28 hours ago 133MB
centos 7 eeb6ee3f44bd 3 weeks ago 204MB
commit常用選項:
-m:說明信息
-a:作者信息
-p:生成過程中停止容器的運行
2. 基於本地模板創建
通過導入操作系統模板文件可以生成鏡像,模板可以從OPENVZ開源項目下載,下載地址為http://openvz.org/Download/template/precreated
[root@docker ~]# wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
[root@docker ~]# cat debian-7.0-x86-minimal.tar.gz | docker import - debian:test
sha256:f7fd702b88cc14fbcc011dc0883edbf45dca3de99b0aef3fc8d184544c07fb5b
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
debian test f7fd702b88cc 36 seconds ago 215MB
centos test1 3e6e91b628ef About an hour ago 204MB
nginx latest 87a94228f133 29 hours ago 133MB
centos 7 eeb6ee3f44bd 3 weeks ago 204MB
2.1 補充:URL下載方式
方法一:
wget 下載路徑 -O 保存路徑
方法二:
curl -L 下載路徑 -o 保存路徑
2.2 補充:載入選項
通過docker save保存的鏡像,使用docker load加載入docker
通過docker export保存的鏡像,使用docker export加載入docker
3. 基於dockerfile創建
3.1 聯合文件系統(UnionFS)
UnionFS(聯合文件系統):Union文件系統(UnionFS)是一種分層、輕量級並且高性能的文件系統,它支持對文件系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下。AUFS、OverlayFS及Devicemapper都是一種UnionFS。
Union文件系統是Docker鏡像的基礎。鏡像可以通過分層來進行集成,基於基礎鏡像(沒有父鏡像),可以制作各種具體的應用鏡像。
特性:一次同時加載多個文件系統,但從外面看起來,只能看到一個文件系統,聯合加載會把各層文件系統疊加起來,這樣最終的文件系統還會包含所有底層的文件和目錄。
我們下載的時候看到的一層層的就是聯合文件系統。
3.2 鏡像加載原理
3.2.1 bootfs
Docker的鏡像實際上由一層一層的文件系統組成,這種層級的文件系統就是UnionFS。
bootfs主要包含bootloader和kernel,bootloader主要是引導加載kernel,Linux剛啟動時會加載bootfs文件系統。
在Docker鏡像的最底層是bootfs,這一層與我們典型的Linux/Unix系統是一樣的,包含boot加載完成之后整個內核就都在內存中了,此時內存的使用權已由bootfs轉交給內核,此時系統也會卸載bootfs。
3.2.2 rootfs
rootfs,在bootfs之上。包含的就是典型Linux系統中的/dev,/proc,/etc等標准目錄和文件。rootfs就是各種不同的操作系統的發行版,比如Ubuntu,Centos等等。
我們可以理解成一開始內核里什么都沒有,操作一個命令下載debian,這是就會在內核上面加了一層基礎鏡像;再安裝一個emacs,會在基礎鏡像上疊加一層image;接着再安裝一個apache,又會在images上面再疊加一層image。最后他們看起來就像一個文件系統即容器的rootfs。在Docker的體系里把這些rootfs叫做Docker的鏡像。但是,此時的每一層rootfs都是read-only的,我們此時還不能對其進行操作。當我們創建一個容器,也就是將Docker鏡像進行實例化,系統會在一層或是多層read-only的rootfs之上分配一層空的read-write的rootfs。
3.2.3 Docker的輕量化原因
因為對於精簡的OS,rootfs可以很小,只需要包含最基本的命令、工具和程序庫就可以了,因為底層直接用宿主機的kernel,自己只需要提供rootfs就可以了,由此可見對於不同的linux發行版,bootfs基本是一致的,rootfs會有差別,因此不同的發行版可以公用bootfs。
3.3 Dockerfile
3.3.1 Dockerfile定義
Docker鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行准備的一些配置參數(如匿名卷、環境變量、用戶等)。鏡像不包含任何動態數據,其內容在構建之后也不會被改變。
鏡像的定期實際上就是定期每一層所添加的配置、文件。如果我們可以把每一層修改、安裝、構建、操作的命令都寫入一個腳本,用這個腳本來構建、定制鏡像,那么鏡像構建透明性的問題、體積的問題就都會解決。這個腳本就是Dockerfile。
Dockerfile是一個文本文件,其內包含了一條條的指令(Instruction),每一條指令構建一層,因此每一條執行的內容,就是描述該層應當如何構建。有了Dockerfile,當我們需要定制自己額外的需求時,只需在Dockerfile上添加或者修改指令,重新生成image即可,省去了敲命令的麻煩。
除了手動生成Docker鏡像之外,可以使用Dockerfile自動生成鏡像。Dockerfile是由多條的指令組成的文件,其中每條指定對應Linux中的一條命令,Docker程序將讀取Dockerfile中的指令生成指定鏡像。
3.3.2 Docker鏡像結構的分層
鏡像不是一個單一的文件,而是有多層構成。容器其實是在鏡像的最上面加了一層讀寫層,在運行容器里做的任何文件改動,都會寫到這個讀寫層。如果刪除了容器,也就刪除了其最上面的讀寫層,文件改動也就丟失了。Docker使用存儲驅動管理鏡像每層內容及可讀寫層的容器層。
(1)Dockerfile中的每個指令都會創建一個新的鏡像層;
(2)鏡像層江北緩存和復用;
(3)當Dockerfile的指令修改了,復制的文件變化了,或者構建鏡像時指定的變量不同了,對應的鏡像層緩存就會失效;
(4)某一層的鏡像緩存失效,它之后的鏡像層緩存都會失效;
(5)鏡像層是不可變的,如果在某一層中添加一個文件,然后在下一層中刪除它,則鏡像中依然會包含該文件,只是這個文件在Docker容器中不可見了。
3.4 Docker鏡像結構的分層
3.4.1 FROM 鏡像
指定新鏡像所基於的基礎鏡像,第一條指令必須為FROM指令,每創建一個鏡像就需要一條FROM指令。
3.4.2 MAINTAINER 名字
說明新鏡像的維護人信息
3.4.3 RUN 命令
在所基於的鏡像上執行命令,並提交到新的鏡像中
3.4.4 ENTRYPOINT ["要運行的程序","參數1","參數2"]
設定容器啟動時第一個運行的命令機器參數。
可以通過使用命令docker run --entrypoint來覆蓋鏡像中的ENTRYPOINT指令的內容。
3.4.5 CMD ["要運行的程序","參數1","參數2"]
上面的是exec形式,shell形式:CMD 命令 參數1 參數2
啟動容器時默認執行的命令或者腳本,Dockerfile只能有一條CMD命令。如果指定多條命令,只執行最后一條命令,可使用;或&&。
如果在docker run 時制定了命令或者鏡像中有ENTRYPOINT,那么CMD就會被覆蓋。
CMD可以為ENTRYPOINT指令提供默認參數。
3.4.6 EXPOSE 端口號
指定新鏡像加載到Docker時要開啟的端口
3.4.7 ENV 環境變量 變量值
設置一個環境變量的值,會被后面的RUN使用
3.4.8 ADD 源文件/目錄 目標文件/目錄
將源文件復制到鏡像中,源文件要與Dockerfile位於相同目錄中,或者是一個URL
有如下注意事項:
如果原路徑是個文件,且目標路徑是以/結尾,則docker會把目標路徑當做一個目錄,會把源文件拷貝到該目錄下。
如果目標路徑不存在,則會自動創建目標路徑。
如果源路徑是個文件,且目標路徑不是以/結尾,則docker會把目標路徑當作一個文件。
如果目標路徑不存在,會以目標路徑為名創建一個文件,內容同源文件;
如果目標文件是個存在的文件,會用源文件覆蓋它,當然只是內容覆蓋,文件名還是目標文件名。
如果目標文件實際是個存在的目錄,則會源文件拷貝到該目錄下。注意,這種情況下,最好顯示的以/結尾,以避免混淆。
如果源路徑是個目錄,且目標路徑不存在,則docker會自動以目標路徑創建一個目錄,把源路徑目錄下的文件拷貝進來。
如果目標路徑是個已經存在的目錄,則docker會把源路徑目錄下的文件拷貝到該目錄下。
如果源文件是個歸檔文件(壓縮文件),則docker會自動幫解壓。
URL下載和解壓特性不能一起使用。任何壓縮文件通過URL拷貝,都不會自動解壓。
3.4.9 COPY 源文件/目錄 目標文件/目錄
只復制本地主機上的文件/目錄復制到目標地點,源文件/目錄要與Dockerfile在相同的目錄中。
3.4.10 VOLUME ["目錄"]
在容器中創建一個掛載點
3.4.11 USER 用戶名/UID
指定運行容器時的用戶
3.4.12 WORKDIR 路徑
為后續的RUN、CMD、ENTRYPOINT指定工作目錄
3.4.13 ONBUILD 命令
指定所生成的鏡像作為一個基礎鏡像時所要運行的命令。
當在一個Dockerfile文件中加上ONBUILD指令,該指令對利用該Dockerfile構建鏡像(比如為A鏡像)不會產生實質性影響。
但是當編寫一個新的Dockerfile文件來基於A鏡像構建一個鏡像(比如為B鏡像)時,這時構建A鏡像的Dockerfile文件中的ONBUILD指令就生效了,在構建B鏡像的過程中,首先會執行ONBUILD指令指定的指令,然后才會執行其他指令。
3.4.14 HEALTHCHECK
健康檢查
3.5 編寫Dockerfile格式
在編寫Dockerfile時,有嚴格的格式需要遵循:
● 第一行必須使用FROM指令知名所基於的鏡像名稱;
● 之后使用MAINTAINER指令說明維護該鏡像的用戶信息;
● 然后是鏡像操作相關指令,如RUN指令。沒運行一條指令,都會給基礎鏡像添加新的一層;
● 最后使用CMD指令指定啟動容器時要運行的命令操作。
3.6 Dockerfile案例-httpd
3.6.1 建立工作目錄
[root@docker ~]# mkdir /opt/apache
[root@docker ~]# cd /opt/apache/
[root@docker apache]# vim Dockerfile
FROM centos:7
#基於centos:7的基礎鏡像
MAINTAINER this is apache image <test>
#定義鏡像的用戶信息
RUN yum -y update;yum install -y httpd
#鏡像操作指令安裝apche軟件
EXPOSE 80
#開啟80端口
ADD index.html /var/www/index.html
#復制網站首頁文件
##前台啟動方法一(需准備執行腳本)
ADD run.sh /run.sh
#將執行腳本復制到鏡像中
RUN chmod 755 /run.sh
CMD ["/run.sh"]
#啟動容器時執行腳本
##前台啟動方法二(無需其他腳本)
ENTRYPOINT ["/usr/sbin/apachectl"]
CMD ["-D","FOREGROUND"]
#前台啟動apache,CMD為ENTRYPOINT傳遞參數
3.6.2 准備執行腳本(前台啟動方法一)
[root@docker apache]# vim run.sh
#!/bin/bash
rm -rf /run/httpd/*
#清理httpd的緩存
/usr/sbin/apachectl -D FOREGROUND
#指定為前台運行
因為Docker容器僅在它的1號進程(PID為1)運行時,會保持運行。如果1號進程退出了,Docker容器也就退出了。
3.6.3 准備網頁頁面
[root@docker apache]# echo "this is test web" > index.html
[root@docker apache]# ls
Dockerfile index.html run.sh
3.6.4 生成鏡像
方法一:
[root@docker apache]# docker build -t http:test1 .
方法二:
[root@docker apache]# docker build -t http:test2 .
[root@docker apache]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd test2 6440a6fa57e0 56 seconds ago 623MB
httpd test1 5ebb94751288 3 minutes ago 623MB
debian test f7fd702b88cc 4 hours ago 215MB
centos test1 3e6e91b628ef 5 hours ago 204MB
nginx latest 87a94228f133 33 hours ago 133MB
centos 7 eeb6ee3f44bd 3 weeks ago 204MB
3.6.5 新鏡像運行容器
[root@docker apache]# docker run -itd --name test1 -p 11111:80 httpd:test1
[root@docker apache]# docker run -itd --name test2 -p 22222:80 httpd:test2
[root@docker apache]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4ff1f0b80978 httpd:test2 "/usr/sbin/apachectl…" 3 seconds ago Up 2 seconds 0.0.0.0:22222->80/tcp, :::22222->80/tcp test2
2609fc4c8c14 httpd:test1 "/run.sh" 2 minutes ago Up 2 minutes 0.0.0.0:11111->80/tcp, :::11111->80/tcp test1
3.6.6 測試
[root@docker apache]# curl http://192.168.122.10:11111
this is test web
[root@docker apache]# curl http://192.168.122.10:22222
this is test web
3.6.7 如果有網絡報錯提示
WARNING: IPv4 forwarding is disabled. Networking will not work.
解決方法:
copy
[root@docker ~]# echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
[root@docker ~]# sysctl -p
net.ipv4.ip_forward = 1
[root@docker ~]# systemctl restart network
[root@docker ~]# systemctl restart docker