容器Docker詳解

redhatxl發表於2019-01-24

一、概述

1.1 基本概念:

Docker 是一個開源的應用容器引擎,基於 Go 語言 並遵從Apache2.0協議開源。Docker 可以讓開發者打包他們的應用以及依賴包到一個輕量級、可移植的容器中,然後釋出到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何介面(類似 iPhone 的 app),更重要的是容器效能開銷極低。

1.2 優勢:

簡化程式:
Docker 讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後釋出到任何流行的 Linux 機器上,便可以實現虛擬化。Docker改變了虛擬化的方式,使開發者可以直接將自己的成果放入Docker中進行管理。方便快捷已經是 Docker的最大優勢,過去需要用數天乃至數週的 任務,在Docker容器的處理下,只需要數秒就能完成。

節省開支:
一方面,雲端計算時代到來,使開發者不必為了追求效果而配置高額的硬體,Docker 改變了高效能必然高價格的思維定勢。Docker 與雲的結合,讓雲空間得到更充分的利用。不僅解決了硬體管理的問題,也改變了虛擬化的方式。

1.3 與傳統VM特性對比:

作為一種輕量級的虛擬化方式,Docker在執行應用上跟傳統的虛擬機器方式相比具有顯著優勢:

Docker容器很快,啟動和停止可以在秒級實現,這相比傳統的虛擬機器方式要快得多。

Docker容器對系統資源需求很少,一臺主機上可以同時執行數千個Docker容器。

Docker通過類似Git的操作來方便使用者獲取、分發和更新應用映象,指令簡明,學習成本較低。

Docker通過Dockerfile配置檔案來支援靈活的自動化建立和部署機制,提高工作效率。

Docker容器除了執行其中的應用之外,基本不消耗額外的系統資源,保證應用效能的同時,儘量減小系統開銷。

Docker利用Linux系統上的多種防護機制實現了嚴格可靠的隔離。從1.3版本開始,Docker引入了安全選項和映象簽名機制,極大地提高了使用Docker的安全性。

特性容器虛擬機器
啟動速度秒級分鐘級
硬碟使用一般為MB一般為GB
效能接近原生弱於原生
系統支援量單機支援上千個容器一般幾十個
隔離性安全隔離完全隔離

1.4 基礎架構

Docker 使用客戶端-伺服器 (C/S) 架構模式,使用遠端API來管理和建立Docker容器。

Docker 容器通過 Docker 映象來建立。

容器與映象的關係類似於物件導向程式設計中的物件與類。

Docker物件導向
容器 物件
映象

de4146cd809a9f320512b316ed5e4570.png

1.5 Docker技術的基礎:

  • namespace,容器隔離的基礎,保證A容器看不到B容器. 6個名空間:User,Mnt,Network,UTS,IPC,Pid

  • cgroups,容器資源統計和隔離。主要用到的cgroups子系統:cpu,blkio,device,freezer,memory

  • unionfs,典型:aufs/overlayfs,分層映象實現的基礎

1.6 Docker元件:

  • docker Client客戶端————>向docker伺服器程式發起請求,如:建立、停止、銷燬容器等操作

  • docker Server伺服器程式—–>處理所有docker的請求,管理所有容器

  • docker Registry映象倉庫——>映象存放的中央倉庫,可看作是存放二進位制的scm

二、安裝部署

2.1 準備條件

目前,CentOS 僅發行版本中的核心支援 Docker。

Docker 執行在 CentOS 7 上,要求系統為64位、系統核心版本為 3.10 以上。

Docker 執行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系統為64位、系統核心版本2.6.32-431 或者更高版本。

2.2 安裝docker

yum install docker -y          #安裝
systemctl start docker         #啟動    
systemctl enable docker        #設定開機自啟動
複製程式碼

2.3 基本命令

docker search centos   #搜尋映象
複製程式碼

預設從國外拉去,速度很慢,可以使用daocloud配置加速

 curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://d6f11267.m.daocloud.io
指令碼是寫入
echo "{\"registry-mirrors\": [\"http://d6f11267.m.daocloud.io\"]}"> /etc/docker/daemon.json
systemctl restart docker              #重啟失效
複製程式碼

584a343d165a20c2f995d00e63b73342.png

根據需求拉取映象:

docker pull docker.io/ansible/centos7-ansible
複製程式碼

拉去search到的全部映象:

for i in `docker search centos|awk '!/NAME/{print $2}'`;do docker pull $i;done
複製程式碼

檢視本地映象:

docker images
複製程式碼

a5970eaa0596c0531afaadc340b34cff.png

2.4 命令整理:

容器操作:

docker create # 建立一個容器但是不啟動它
docker run # 建立並啟動一個容器
docker stop # 停止容器執行,傳送訊號SIGTERM
docker start # 啟動一個停止狀態的容器
docker restart # 重啟一個容器
docker rm # 刪除一個容器
docker kill # 傳送訊號給容器,預設SIGKILL
docker attach # 連線(進入)到一個正在執行的容器
docker wait # 阻塞一個容器,直到容器停止執行
複製程式碼

獲取容器資訊:

docker ps # 顯示狀態為執行(Up)的容器
docker ps -a # 顯示所有容器,包括執行中(Up)的和退出的(Exited)
docker inspect # 深入容器內部獲取容器所有資訊
docker logs # 檢視容器的日誌(stdout/stderr)
docker events # 得到docker伺服器的實時的事件
docker port # 顯示容器的埠對映
docker top # 顯示容器的程式資訊
docker diff # 顯示容器檔案系統的前後變化
複製程式碼

匯出容器:

docker cp # 從容器裡向外拷貝檔案或目錄
docker export # 將容器整個檔案系統匯出為一個tar包,不帶layers、tag等資訊
複製程式碼

執行:

docker exec # 在容器裡執行一個命令,可以執行bash進入互動式
複製程式碼

映象操作:

docker images # 顯示本地所有的映象列表
docker import # 從一個tar包建立一個映象,往往和export結合使用
docker build # 使用Dockerfile建立映象(推薦)
docker commit # 從容器建立映象
docker rmi # 刪除一個映象
docker load # 從一個tar包建立一個映象,和save配合使用
docker save # 將一個映象儲存為一個tar包,帶layers和tag資訊
docker history # 顯示生成一個映象的歷史命令
docker tag # 為映象起一個別名
複製程式碼

映象倉庫(registry)操作:

docker login # 登入到一個registry
docker search # 從registry倉庫搜尋映象
docker pull # 從倉庫下載映象到本地
docker push # 將一個映象push到registry倉庫中
複製程式碼

2.5 簡單實踐操作

執行並進入容器操作:

docker run -i -t docker.io/1832990/centos6.5  /bin/bash
複製程式碼

-t 表示在新容器內指定一個偽終端或終端;

-i表示允許我們對容器內的 (STDIN) 進行互動;

-d表示將容器在後臺執行;

/bin/bash 。這將在容器內啟動 bash shell;

所以當容器(container)啟動之後,我們會獲取到一個命令提示符:

de50febadf6ddcbce18fc348b1ab0c0e.png

在容器內我們安裝mysql並設定開機自啟動,將修改後的映象提交:

docker ps -l 查詢容器ID
docker commit -m "功能" -a "使用者資訊" ID tag 提交修改後的映象
複製程式碼

492c5f98644dc2e9b0a230bf69068311.png

docker inspect ID 檢視詳細資訊
docker push ID 上傳docker映象
複製程式碼

利用DockerFile建立映象

使用命令 docker build , 需要建立一個 Dockerfile 檔案,其中包含一組指令來告訴 Docker 如何構建映象。

mkdir DockerFile
cd DockerFile
cat > Dockerfile <<EOF
FROM 603dd3515fcc
MAINTAINER Docker xuel
RUN yum install mysql mysql-server -y
RUN mddir /etc/sysconfig/network
RUN /etc/init.d/mysqld start
EOF
複製程式碼

370397346f81e7b732c4ee2b9279def4.png

docker build -t "centos6.8:mysqld" .
複製程式碼

-t 制定repository 與tag

. 指定Dockerfile的路徑

注意一個映象不能超過 127 層

此外,還可以利用 ADD 命令複製本地檔案到映象;

用 EXPOSE 命令來向外部開放埠;

用 CMD 命令來描述容器啟動後執行的程式等。

CMD ["/usr/sbin/apachectl", "-D", "FOREGROUND"]

2.6 Dockerfile詳解

Dockerfile的指令是忽略大小寫的,建議使用大寫,使用 # 作為註釋,每一行只支援一條指令,每條指令可以攜帶多個引數。

Dockerfile的指令根據作用可以分為兩種,構建指令和設定指令。

構建指令:用於構建image,其指定的操作不會在執行image的容器上執行;

設定指令:用於設定image的屬性,其指定的操作將在執行image的容器中執行。

  • FROM(指定基礎image)

構建指令,必須指定且需要在Dockerfile其他指令的前面。後續的指令都依賴於該指令指定的image。FROM指令指定的基礎image可以是官方遠端倉庫中的,也可以位於本地倉庫。

該指令有兩種格式:

FROM <image>                  #指定基礎image為該image的最後修改的版本
FROM <image>:<tag>              #指定基礎image為該image的一個tag版本。
複製程式碼
  • MAINTAINER(用來指定映象建立者資訊)

構建指令,用於將image的製作者相關的資訊寫入到image中。當我們對該image執行docker inspect命令時,輸出中有相應的欄位記錄該資訊。

MAINTAINER <name>
複製程式碼
  • RUN(安裝軟體用)

構建指令,RUN可以執行任何被基礎image支援的命令。如基礎image選擇了ubuntu,那麼軟體管理部分只能使用ubuntu的命令。

RUN <command> (the command is run in a shell - `/bin/sh -c`)  
RUN ["executable", "param1", "param2" ... ]  (exec form)
複製程式碼
  • CMD(設定container啟動時執行的操作)

設定指令,用於container啟動時指定的操作。該操作可以是執行自定義指令碼,也可以是執行系統命令。該指令只能在檔案中存在一次,如果有多個,則只執行最後一條。

CMD ["executable","param1","param2"] (like an exec, this is the preferred form)  
CMD command param1 param2 (as a shell)
複製程式碼

ENTRYPOINT指定的是一個可執行的指令碼或者程式的路徑,該指定的指令碼或者程式將會以param1和param2作為引數執行。所以如果CMD指令使用上面的形式,那麼Dockerfile中必須要有配套的ENTRYPOINT。當Dockerfile指定了ENTRYPOINT,那麼使用下面的格式:

CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
複製程式碼
  • ENTRYPOINT(設定container啟動時執行的操作)

設定指令,指定容器啟動時執行的命令,可以多次設定,但是隻有最後一個有效。

ENTRYPOINT ["executable", "param1", "param2"] (like an exec, the preferred form)  
ENTRYPOINT command param1 param2 (as a shell)
複製程式碼

該指令的使用分為兩種情況,一種是獨自使用,另一種和CMD指令配合使用。
當獨自使用時,如果你還使用了CMD命令且CMD是一個完整的可執行的命令,那麼CMD指令和ENTRYPOINT會互相覆蓋只有最後一個CMD或者ENTRYPOINT有效。

# CMD指令將不會被執行,只有ENTRYPOINT指令被執行  
CMD echo “Hello, World!”  
ENTRYPOINT ls -l
複製程式碼

另一種用法和CMD指令配合使用來指定ENTRYPOINT的預設引數,這時CMD指令不是一個完整的可執行命令,僅僅是引數部分;ENTRYPOINT指令只能使用JSON方式指定執行命令,而不能指定引數。

FROM ubuntu  
CMD ["-l"]  
ENTRYPOINT ["/usr/bin/ls"]
複製程式碼
  • USER(設定container容器的使用者)

設定指令,設定啟動容器的使用者,預設是root使用者

# 指定memcached的執行使用者  
ENTRYPOINT ["memcached"]  
USER daemon  
或  
ENTRYPOINT ["memcached", "-u", "daemon"]
複製程式碼
  • EXPOSE(指定容器需要對映到宿主機器的埠)

設定指令,該指令會將容器中的埠對映成宿主機器中的某個埠。當你需要訪問容器的時候,可以不是用容器的IP地址而是使用宿主機器的IP地址和對映後的埠。要完成整個操作需要兩個步驟,首先在Dockerfile使用EXPOSE設定需要對映的容器埠,然後在執行容器的時候指定-p選項加上EXPOSE設定的埠,這樣EXPOSE設定的埠號會被隨機對映成宿主機器中的一個埠號。也可以指定需要對映到宿主機器的那個埠,這時要確保宿主機器上的埠號沒有被使用。EXPOSE指令可以一次設定多個埠號,相應的執行容器的時候,可以配套的多次使用-p選項。

# 對映一個埠  
EXPOSE port1  
# 相應的執行容器使用的命令  (主機(宿主)埠:容器埠)
docker run -p port1 image  
  
# 對映多個埠  
EXPOSE port1 port2 port3  
# 相應的執行容器使用的命令  
docker run -p port1 -p port2 -p port3 image  
# 還可以指定需要對映到宿主機器上的某個埠號  
docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image
複製程式碼

埠對映是docker比較重要的一個功能,原因在於我們每次執行容器的時候容器的IP地址不能指定而是在橋接網路卡的地址範圍內隨機生成的。宿主機器的IP地址是固定的,我們可以將容器的埠的對映到宿主機器上的一個埠,免去每次訪問容器中的某個服務時都要檢視容器的IP的地址。對於一個執行的容器,可以使用docker port加上容器中需要對映的埠和容器的ID來檢視該埠號在宿主機器上的對映埠。

  • ENV(用於設定環境變數)

構建指令,在image中設定一個環境變數。

ENV <key> <value>
複製程式碼

設定了後,後續的RUN命令都可以使用,container啟動後,可以通過docker inspect檢視這個環境變數,也可以通過在docker run --env key=value時設定或修改環境變數。
假如你安裝了JAVA程式,需要設定JAVA_HOME,那麼可以在Dockerfile中這樣寫:

ENV JAVA_HOME /path/to/java/dirent
複製程式碼
  • ADD(從src複製檔案到container的dest路徑)

構建指令,所有拷貝到container中的檔案和資料夾許可權為0755,uid和gid為0;如果是一個目錄,那麼會將該目錄下的所有檔案新增到container中,不包括目錄;如果檔案是可識別的壓縮格式,則docker會幫忙解壓縮(注意壓縮格式);如果<src>是檔案且<dest>中不使用斜槓結束,則會將<dest>視為檔案,<src>的內容會寫入<dest>;如果<src>是檔案且<dest>中使用斜槓結束,則會<src>檔案拷貝到<dest>目錄下。

ADD <src> <dest>
複製程式碼

<src> 是相對被構建的源目錄的相對路徑,可以是檔案或目錄的路徑,也可以是一個遠端的檔案url;
<dest> 是container中的絕對路徑

  • VOLUME(指定掛載點)

設定指令,使容器中的一個目錄具有持久化儲存資料的功能,該目錄可以被容器本身使用,也可以共享給其他容器使用。我們知道容器使用的是AUFS,這種檔案系統不能持久化資料,當容器關閉後,所有的更改都會丟失。當容器中的應用有持久化資料的需求時可以在Dockerfile中使用該指令。

FROM base  
VOLUME ["/tmp/data"]
複製程式碼
  • WORKDIR(切換目錄)

設定指令,可以多次切換(相當於cd命令),對RUN,CMD,ENTRYPOINT生效。

# 在 /p1/p2 下執行 vim a.txt  
WORKDIR /p1 WORKDIR p2 RUN vim a.txt
複製程式碼

2.7 映象匯入匯出

7cf8917e844e1e50e23757cf08714b5a.png

匯出映象到本地:

98c9b4e84d6cfd09a8521039e0e5e9ad.png

docker save -o centos6.5.tar centos6.5 或
docker export f9c99092063c >centos6.5.tar
複製程式碼

從本地將映象匯入:

docker load --input centos6.5.tar 或  
docker load < centos6.5.tar
複製程式碼

d2bc592a3168c50ea6dfd809dad3c58c.png

docker rm刪除已經終止的容器
docker -f rm 可以刪除正在執行的容器
複製程式碼

修改已經執行的後臺容器:

docker exec -it CONTAINER ID /bin/bash
複製程式碼

9e258035ff6c1c09fd8a87410b77c316.png

三、儲存

3.1資料盤

docker的映象使用一層一層檔案組成的,docker的一些儲存引擎可以處理怎麼樣儲存這些檔案。

docker inspect centos            #檢視容器詳細資訊
複製程式碼

資訊下方的Layers,就是centos的檔案,這些東西都是隻讀的不能去修改,我們基於這個映象去建立的映象和容器也會共享這些檔案層,而docker會在這些層上面去新增一個可讀寫的檔案層。如果需要修改一些檔案層裡面的東西的話,docker會複製一份到這個可讀寫的檔案層裡面,如果刪除容器的話,那麼也會刪除它對應的可讀寫的檔案層的檔案。

如果有些資料你想一直儲存的話,比如:web伺服器上面的日誌,資料庫管理系統裡面的資料,那麼我們可以把這些資料放到data volumes資料盤裡面。它上面的資料,即使把容器刪掉,也還是會永久保留。建立容器的時候,我們可以去指定資料盤。其實就是去指定一個特定的目錄。

docker run -i -t -v /mnt  --name nginx docker.io/nginx /bin/bash
複製程式碼

-v:制定掛載到容器內的目錄

272c9f983d98694569447ad89df78dc2.png

使用docker inspect 容器ID可以檢視掛載目錄對應於宿主機的物理檔案路徑

2a6510c525616589c661aef41b3e985a.png

同樣,我們可以使用將制定物理宿主機的目錄掛載到容器的制定目錄下:

將宿主機目錄掛載到容器內:

docker run -d -p 80:80 --name nginx -v /webdata/wordpress:/usr/share/nginx/html docker.io/sergeyzh/centos6-nginx
複製程式碼

-d 後臺執行

--name 給執行的容器命名

-v 宿主機目錄:容器目錄 將宿主機目錄掛載在容器內

-p 宿主機埠:容器監聽埠 將容器內應用監聽埠對映到物理宿主機的特定埠上

1577f28ec87c4e75ea9d71a9563c8417.png

f8aee4bc4051778ba59e0c00d3f2bdf0.png

對映多個物理目錄:(多寫幾個-v即可)

720666caee8fa6edac22ef6179febb65.png

7d506a6000fd3bf06c0c3226fd9094ea.png

3.2 資料容器:

可以建立一個資料容器,也就是再建立容器是指定這個容器的資料盤,然後讓其他容器可以使用這個容器作為他們的資料盤,有點像繼承了這個資料容器指定的資料盤作為資料盤。

首先建立一個資料容器命名為newnginx

docker create -v /mnt -it --name newnginx docker.io/nginx /bin/bash
複製程式碼

利用此資料容器容器執行一個容器nginx1,在資料目錄/mnt 下建立一個檔案

docker run --volumes-from newnginx --name nginx1 -it docker.io/nginx /bin/bash
複製程式碼

利用資料容器在建立一個容器nginx2,檢視資料目錄下容器nginx1建立的檔案依舊存在,同理在nginx2的/mnt下建立檔案,其他基於資料容器執行的新容器也可以看到檔案

ae9d1b83d7cabde598433ef444c65daf.png

3.3 資料盤管理:

在刪除容器時,docker預設不會刪除其資料盤。

docker volume ls                    #檢視資料盤
docker volume ls -f dangling=true        #檢視未被容器使用的資料盤
docker volume rm VOLUME NAME        #刪除資料盤
複製程式碼

6babe72eac1d5deda7b6baa5d408ae89.png

如果想要刪除容器時,同時刪除掉其資料盤,那麼可以使用-v引數。

docker rm -v newnginx
複製程式碼

四、網路

docker提供幾種網路,它決定容器之間和外界和容器之間如何去相互通訊。

docker network ls        #檢視網路
複製程式碼

3c3b558dc81e98f69dab30c0a9d50cec.png

當Docker程式啟動時,會在主機上建立一個名為docker0的虛擬網橋,此主機上啟動的Docker容器會連線到這個虛擬網橋上。虛擬網橋的工作方式和物理交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網路中。從docker0子網中分配一個IP給容器使用,並設定docker0的IP地址為容器的預設閘道器。在主機上建立一對虛擬網路卡veth pair裝置,Docker將veth pair裝置的一端放在新建立的容器中,並命名為eth0(容器的網路卡),另一端放在主機中,以vethxxx這樣類似的名字命名,並將這個網路裝置加入到docker0網橋中。

4.1 bridge橋接網路

除非建立容器的時候指定網路,不然容器就會預設的使用橋接網路。屬於這個網路的容器之間可以相互通訊,不過外界想要訪問到這個網路的容器呢,需使用橋接網路,有點像主機和容器之間的一座橋,對容器有一點隔離作用。實際是在iptables做了DNAT規則,實現埠轉發功能。可以使用iptables -t nat -vnL檢視。

4.2 host主機網路

如果啟動容器的時候使用host模式,那麼這個容器將不會獲得一個獨立的Network Namespace,而是和宿主機共用一個Network Namespace。容器將不會虛擬出自己的網路卡,配置自己的IP等,而是使用宿主機的IP和埠。但是,容器的其他方面,如檔案系統、程式列表等還是和宿主機隔離的。只用這種網路的容器會使用主機的網路,這種網路對外界是完全開放的,能夠訪問到主機,就能訪問到容器。

4.3 使用none模式

Docker容器擁有自己的Network Namespace,但是,並不為Docker容器進行任何網路配置。也就是說,這個Docker容器沒有網路卡、IP、路由等資訊。需要我們自己為Docker容器新增網路卡、配置IP等。使用此種網路的容器會完全隔離。

4.4 簡單演示:

啟動兩個容器,檢視其容器內部IP地址

for i in `docker ps |grep -v "CONTAINER"|awk '{print $1}'`;do docker inspect $i|grep 'IPAddress';done
複製程式碼

檢視橋接模式下主機內部容器之間和容器與宿主機直接均可正常通訊

e1623e4a2f17c581957424a4aa4f2995.png

docker inspect 容器ID
複製程式碼

135cdfff43e9a32e96819c69c2a18840.png

檢視host建立的容器內部沒有IP地址,它使用的為宿主機的地址

docker run -d --net host docker.io/sergeyzh/centos6-nginx
複製程式碼

a3d6239b861d62172cf7b8235a2d57a1.png

1965c24d718ca5d6437c4991110176f4.png

檢視host建立的容器內部沒有IP地址,它使用的為宿主機的地址

docker run -d --net none docker.io/sergeyzh/centos6-nginx
複製程式碼

002f07ebb53e60920fdaa25e59ffea5d.png

4.5 容器埠:

如果想讓外界可以訪問到,基於bridge網路建立的容器提供的服務,那你可以告訴Docker你要使用哪些介面。如果想檢視映象會使用哪些埠,ExposedPorts,可以獲悉映象使用哪些埠。

docker run -d -p 80 docker.io/sergeyzh/centos6-nginx        
docker port 09648b2ff7f6
複製程式碼

-p 引數會在宿主機隨機對映一個高階口到容器內的指定埠

a0025b5366276690b9d24b79e3d2a49c.png

docker run -d -p 80:80 docker.io/sergeyzh/centos6-nginx    #將宿主機的80埠對映到容器的80埠
複製程式碼

3010e7d100c43e8692ca25e7cc4c986b.png





相關文章