現在正是雲計算『容器化』的潮流。Docker越來越成為雲計算和分布式系統的寵兒和基石。
我們可以從 Docker Hub 或其他registry,如 DockerPool 和 阿里雲Docker鏡像庫, pull下已有的鏡像,也可以自己寫Dockerfile文件,自己創建鏡像。有了鏡像,就可以去RUN它。下面依次介紹了RUN一個鏡像(docker run),自己創建鏡像(Dockerfile語法 和 docker build)。在介紹它的最基本用法之後,開始初步深入它的原理和內核技術,不求理解,只求一個印象 :-)。深入部分會越來越細緻,不斷完善。之後也會增加Docker Runtime metrics的介紹和命令,這樣對Performance的分析也會有幫助。
感謝大家一起幫助博主完善這篇blog。
大將們合個影先粗略介紹docker常用的幾個基本命令:
run
運行容器,如果鏡像不存在則先下載
pull
從鏡像庫上下載容器鏡像
start/stop
啟動/停止一個container
rm
刪除容器
rmi
刪除容器鏡像
commit
將容器中的修改提交至鏡像中
logs
顯示容器運行的控制臺輸出
build
從 Dockerfile 構建一個鏡像
inspect
顯示容器運行參數
images
顯示當前宿主機上的所有鏡像
docker run 灑灑水拉$ sudo docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]1docker run命令有兩個參數,一個是鏡像名,一個是要在鏡像中運行的命令。
正確的命令:$ docker run learn/tutorial echo "hello word"1–link (name or id): alias
$ docker run -d -P --name web --link db:db training/webapp python app.py1上面命令連接了web和db兩個container,注意link的參數 db:db,前一個db是容器名,後一個db是alias。
如果一個名為web的container被連接到db container上, –link db:webdb,那麼Docker就會在web這個container中創建環境變量 WEBDB_NAME=/web/webdb。其中<alias>_NAME = WEBDB_NAME。
docker run --name mongo_001 -d -P mongo1映射所有接口地址
使用 hostPort:containerPort 格式本地的 5000 埠映射到容器的 5000 埠,可以執行
$ sudo docker run -d -p 5000:5000 training/webapp python app.py
此時默認會綁定本地所有接口上的所有地址。
映射到指定地址的指定埠
可以使用 ip:hostPort:containerPort 格式指定映射使用一個特定地址,比如 localhost 地址
127.0.0.1$ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
映射到指定地址的任意埠
使用 ip::containerPort 綁定 localhost 的任意埠到容器的 5000 埠,本地主機會自動分配一個埠。
$ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py
還可以使用 udp 標記來指定 udp 埠
$ sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
查看映射埠配置
使用 docker port 來查看當前映射的埠配置,也可以查看到綁定的地址
$ docker port nostalgic_morse 5000
Dockerfile語法:事兒多
127.0.0.1:49155.VOLUME [「mountpoint」] 將本地文件夾或者其他container的文件夾掛載到container中
# Define mountable directories.VOLUME ["/data/db"]12WORKDIR /path/to/workdir 切換目錄用,可以多次切換(相當於cd命令)
# Define working directory.WORKDIR /data12CMD [「executable」,」param1」,」param2」] container啟動時執行的命令,但是一個Dockerfile中只能有一條CMD命令,多條則只執行最後一條CMD,用於在構建過程中執行命令
# Define default command.CMD ["mongod"]12EXPOSE port 把這個埠暴露在外,這樣容器外可以看到這個埠並與其通信
# Expose ports.# - 27017: process# - 28017: httpEXPOSE 27017EXPOSE 2801712345ENV key value 設置環境變量
ENV APP_NAME app.js1ADD <源文件> <目標文件> 用來將一個文件或目錄添加到 Docker 鏡像中,前面是源文件,後面是目標文件(源文件必須使用相對路徑)
ADD device.jar /device.jar1ENTRYPOINT 【運行命令】 用來指定運行 Docker 容器時,在容器中執行的命令是什麼。如果需要運行多個命令,可以通過 Supervisor 來執行
ENTRYPOINT java -jar /device.jar1示例(構建mongodb鏡像的Dockerfile)
## MongoDB Dockerfile## Pull base imageFROM ubuntu:latestMAINTAINER LIU Qiu Shan <qsliubj@cn.ibm.com># Install MongoDB# The real logic# Add 10gen official apt source to the sources listRUN \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && \
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' > /etc/apt/sources.list.d/mongodb.list && \
apt-get update && \
apt-get install -y mongodb-org && \
rm -rf /var/lib/apt/lists/*# Define mountable directoriesVOLUME ["/data/db"]# Define working directoryWORKDIR /data# Define default mongodb commandCMD ["mongod"]# Expose the process port of mongodbEXPOSE 27017# Expose the http port of mongodb#EXPOSE 2801712345678910111213141516171819202122232425262728293031323334
docker build 如此easydocker build -t="dockerfile/mongodb" .1在當前目錄下有名為Dockerfile的文件,執行上述命令,構建名為dockerfile/mongodb的鏡像。
鏡像:等主人完善…查看所有鏡像 $ docker images
刪除指定鏡像 $ docker rmi image_name
注意:
清空/var/lib/docker/devicemapper這個目錄後,需要重新build所有用到的鏡像,包括用到的作業系統鏡像,如ubuntu:latestdocker pull ubuntu1
/var/lib/docker 啥玩意Docker用/var/lib/docker作為默認的目錄,所有docker相關的文件,包括images和掛在卷(volumes)都存在這個目錄下。
注意:
Docker Daemon 牛
在使用mongodb時,創建多個mongodb的containers後,volumes目錄下就會存滿mongodb的掛在卷,如果過多,會佔用大量磁碟空間,如果出現空間不足的錯誤,可以把不用的volumes刪掉。在Docker架構中,Docker Client通過特定的協議與Docker Daemon進行通信,而Docker Daemon主要承載了Docker運行過程中的大部分工作。Docker Daemon是Docker架構中運行在後臺的守護進程,大致可以分為Docker Server、Engine和Job三部分。
Docker Client先通知Docker Daemon創建container,創建出後Docker Daemon會通知Docker Client已經創建好,這時Docker Client會再次發出start container的請求。收到請求的Docker Daemon會使用以下的Start函數來完成容器啟動的所有過程。
if err := container.Start(); err != nil { return job.Errorf("Cannot start container %s: %s", name, err)
}123「Start函數實現了容器的啟動。更為具體的描述是:Start函數實現了進程的啟動,另外在啟動進程的同時為進程設定了命名空間(namespace),啟動完畢之後為進程完成了資源使用的控制,從而保證進程以及之後進程的子進程都會在同一個命名空間內,且受到相同的資源控制。如此一來,Start函數創建的進程,以及該進程的子進程,形成一個進程組,該進程組處於資源隔離和資源控制的環境,我們習慣將這樣的進程組環境稱為容器,也就是這裡的Docker Container。」(《Docker源碼分析》)
深入大山坡軟體職業技術學院」Docker有兩方面的技術非常重要,第一是Linux容器方面的技術,第二是Docker鏡像的技術。從技術本身來講,兩者的可複製性很強,不存在絕對的技術難點,然而Docker Hub由於存在大量的數據的原因,導致Docker Hub的可複製性幾乎不存在,這需要一個生態的營造。「
容器這種系統級的虛擬化運用了一項技術叫namespace isolation:Namespace isolation使主機能夠給每個容器一個虛擬的namespace,容器在這個虛擬的namespace內,只能看到它應該看到的資源。但為了提升效率,許多作業系統文件、目錄和運行的服務在容器間共享,並映射到每個容器的namespace。僅當應用在它的容器內改變這些資源時,比如修改一個已存在的文件或創建新文件,容器會從宿主作業系統得到一份副本——利用Docker的「copy-on-write」優化,僅僅複製發生變化的部分。這一共享特性,是在一臺主機上高效部署多個容器的技術之一。還有一項技術叫cgroup,利用它可以實現對資源的限制和配置,如限制CPU的使用率。
Dockerfile 是軟體的原材料,Docker 鏡像是軟體的交付品,而 Docker 容器則可以認為是軟體的運行態。
Dockerfile中的四條命令 FROM, ADD, VOLUME, CMD, 這四條命令可以構建出一個鏡像來,分別對應四個鏡像層,見下圖。
下圖的出處是http://www.csdn.net/article/2015-08-21/2825511
Docker最大的創新點在於Docker鏡像的設計,下面是張Docker鏡像的層次圖,分層的文件系統,一層層地搭建出一個完整的容器運行環境:
Cgroups可以限制、記錄、隔離進程組所使用的物理資源(包括:CPU、memory、IO等),為容器實現虛擬化提供了基本保證,是構建Docker等一系列虛擬化管理工具的基石,最初由Google工程師(Paul Menage和Rohit Seth)於2006年提出。
Cgroups可以對進程組使用的資源總額進行限制,如設定應用運行時使用內存的上限,一旦超過這個配額就發出OOM(Out of Memory)。通過分配的CPU時間片數量及硬碟IO帶寬大小,實際上就相當於控制了進程運行的優先級。 cgroups可以統計系統的資源使用量,如CPU使用時長、內存用量等等,這個功能非常適用於計費。cgroups可以對進程組執行掛起、恢復等操作。
Cgroups也是LXC為實現虛擬化所使用的資源管理手段,可以說沒有cgroups就沒有LXC。從單個進程的資源控制,到實現作業系統層次的虛擬化(OS Level Virtualization)。
「根據Docker布道師Jerome Petazzoni的說法,Docker約等於LXC+AUFS(之前只支持ubuntu時)。其中LXC負責資源管理,AUFS負責鏡像管理;而LXC又包括cgroup、namespace、chroot等組件,並通過cgroup進行資源管理。所以只從資源管理這條線來看的話,Docker、LXC、CGroup三者的關係是:Cgroup在最底層落實資源管理,LXC在cgroup上封裝了一層,Docker又在LXC封裝了一層,關係圖如圖1.b所示。」
(出自http://speakingbaicai.blog.51cto.com/5667326/1352962)
(a)
(b)圖1 Docker-LXC-CGroup結構圖
Docker的本質實際上是宿主機上的一個進程,通過namespace實現了資源隔離,通過cgroup實現了資源限制,通過UnionFS實現了Copy on Write的文件操作。
以下來自http://www.sel.zju.edu.cn/?p=573
術語表
task(任務):cgroups的術語中,task就表示系統的一個進程。
cgroup(控制組):cgroups 中的資源控制都以cgroup為單位實現。cgroup表示按某種資源控制標準劃分而成的任務組,包含一個或多個子系統。一個任務可以加入某個cgroup,也可以從某個cgroup遷移到另外一個cgroup。
subsystem(子系統):cgroups中的subsystem就是一個資源調度控制器(Resource Controller)。比如CPU子系統可以控制CPU時間分配,內存子系統可以限制cgroup內存使用量。
hierarchy(層級樹):hierarchy由一系列cgroup以一個樹狀結構排列而成,每個hierarchy通過綁定對應的subsystem進行資源調度。hierarchy中的cgroup節點可以包含零或多個子節點,子節點繼承父節點的屬性。整個系統可以有多個hierarchy規則1: 同一個hierarchy可以附加一個或多個subsystem。如下圖1,cpu和memory的subsystem附加到了一個hierarchy。
圖1 同一個hierarchy可以附加一個或多個subsystem規則2: 一個subsystem可以附加到多個hierarchy,若且唯若這些hierarchy只有這唯一一個subsystem。如下圖2,小圈中的數字表示subsystem附加的時間順序,CPU subsystem附加到hierarchy A的同時不能再附加到hierarchy B,因為hierarchy B已經附加了memory subsystem。如果hierarchy B與hierarchy A狀態相同,沒有附加過memory subsystem,那麼CPU subsystem同時附加到兩個hierarchy是可以的。
圖2 一個已經附加在某個hierarchy上的subsystem不能附加到其他含有別的subsystem的hierarchy上規則3: 系統每次新建一個hierarchy時,該系統上的所有task默認構成了這個新建的hierarchy的初始化cgroup,這個cgroup也稱為root cgroup。對於你創建的每個hierarchy,task只能存在於其中一個cgroup中,即一個task不能存在於同一個hierarchy的不同cgroup中,但是一個task可以存在在不同hierarchy中的多個cgroup中。如果操作時把一個task添加到同一個hierarchy中的另一個cgroup中,則會從第一個cgroup中移除。在下圖3中可以看到,httpd進程已經加入到hierarchy A中的/cg1而不能加入同一個hierarchy中的/cg2,但是可以加入hierarchy B中的/cg3。實際上不允許加入同一個hierarchy中的其他cgroup野生為了防止出現矛盾,如CPU subsystem為/cg1分配了30%,而為/cg2分配了50%,此時如果httpd在這兩個cgroup中,就會出現矛盾。
圖3 一個task不能屬於同一個hierarchy的不同cgroup規則4: 進程(task)在fork自身時創建的子任務(child task)默認與原task在同一個cgroup中,但是child task允許被移動到不同的cgroup中。即fork完成後,父子進程間是完全獨立的。如下圖4中,小圈中的數字表示task 出現的時間順序,當httpd剛fork出另一個httpd時,在同一個hierarchy中的同一個cgroup中。但是隨後如果PID為4840的httpd需要移動到其他cgroup也是可以的,因為父子任務間已經獨立。總結起來就是:初始化時子任務與父任務在同一個cgroup,但是這種關係隨後可以改變。
While namespaces are responsible for isolation between host and container, control groups implement resource accounting and limiting. In addition to the above components, Docker has been using AuFS (Advanced Multi-Layered Unification Filesystem) as a filesystem for containers. AuFS is a layered filesystem that can transparently overlay one or more existing filesystems. When a process needs to modify a file, AuFS creates a copy of that file. AuFS is capable of merging multiple layers into a single representation of a filesystem. This process is called copy-on-write.
Docker既可以使用LXC,也可以使用libcontainer,後者是新的而且是默認的。通過它們我們可以對container進行限制。比如,我們要將container鎖定在第一個core上,在 docker run 命令上加上 –cpuset-cpus=0。
另外 –cpu-shares 參數會定下 share 一個CPU的百分比。下面是CloudSigma上的一個示例:$ docker run -d \
--name='low_prio' \ --cpuset-cpus=0 \ --cpu-shares=20 \ busybox md5sum /dev/urandom
$ docker run -d \
--name='high_prio' \ --cpuset-cpus=0 \ --cpu-shares=80 \ busybox md5sum /dev/urandom12345678910如果在一個host上管理很多個Docker container,就要藉助Cgroups,Docker支持兩個Cgroup driver,分別是LXC,libcontainer,各有優勢。
工業界對Docker使用:虛心學習,how low I can do騰訊萬臺規模的Docker應用實踐
「... Docker在資源管理緯度方面只有CPU和內存兩個維度,這對於共享的雲環境下需要完善,也是目前相對於虛擬機不足的地方。Gaia引入磁碟容量管理,網絡出入帶寬控制以及磁碟IO的控制維護。 ...」基於容器的自動構建——Docker在美團的應用
「... 該應用只利用了Docker最核心的容器功能,並沒有使用Docker集群管理、調度、自動擴容等高級的功能。 ...」本文為CSDN博客Qianyu-OS原創文章,未經允許不得轉載。