本文會介紹一個Docker啟動MySQL實例的例子,,借此來簡單說明下Docker的核心原理。
1. Docker基本用法
下面來看一個基本用法的例子,,對于例子中所講的daemon、容器、鏡像等術(shù)語我們會在下文進行說明,。我們的例子是需要啟動一個運行MySQL的容器。命令如下:
#啟動docker daemon:[root@dev ~]# service docker restart#查看目前所擁有的鏡像:[root@dev ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZElibnetwork-build latest c99f5cf5eb23 12 days ago 587.5 MBdocker.io/golang 1.4 124e2127157f 2 weeks ago 517.2 MBdocker.io/berngp/docker-zabbix latest ad689c775bbf 6 weeks ago 1.134 GBdocker.io/centos 7 7322fbe74aa5 6 weeks ago 172.2 MBdocker.io/centos latest 7322fbe74aa5 6 weeks ago 172.2 MB#下載一個MySQL鏡像,,其源托管在https://registry.hub./_/mysql/上:[root@dev ~]# docker pull mysqllatest: Pulling from docker.io/mysqlc8cbfd2973e: Downloading [=> ] 1.129 MB/37.21 MBc52dbe9d91: Download complete c8cbfd2973e: Pull complete c52dbe9d91: Pull complete c2b0136be90f: Pull complete cd71eacf0: Pull complete ff72402d8: Pull complete aa3022270c68: Pull complete 0042665d: Pull complete e4d19227c16: Pull complete f877cc70688: Pull complete e6d170eec04: Pull complete 4b223269: Pull complete b95dfc449f80: Pull complete d84ed3b24d: Pull complete bcf7334ef42a: Pull complete a128139aadf2: Already exists docker.io/mysql:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.Digest: sha256:3e633be4546d5549c35f9731280e7c7ef0a272c23dfbe241e08a655920c2ffa1Status: Downloaded newer image for docker.io/mysql:latest#從上面給出的連接中可以看出這個鏡像提供了一些參數(shù)可以在啟動的時候使用,,包括:MYSQL_ROOT_PASSWORD、MYSQL_DATABASE,、MYSQL_USER,、MYSQL_PASSWORD等等,。這里我們使用MYSQL_DATABASE、MYSQL_USER,、MYSQL_PASSWORD這三個操作來讓我們的鏡像啟動的時候自動建立一個數(shù)據(jù)庫并建立有權(quán)限操作該數(shù)據(jù)庫的相關(guān)用戶,。同時我們映射啟動后容器中的3306端口對應(yīng)物理機的6033端口:[root@dev ~]# docker run --name NeutronMySQL -p 6033:3306 -e MYSQL_USER=neutron -e MYSQL_PASSWORD=password -e MYSQL_DATABASE=neutron -e MYSQL_ROOT_PASSWORD=password docker.io/mysqlRunning mysql_install_db-08-02 15:06:58 0 [Note] /usr/sbin/mysqld (mysqld 5.6.26) starting as process 33 ...-08-02 15:06:58 33 [Note] InnoDB: Using atomics to ref count buffer pool pages-08-02 15:06:58 33 [Note] InnoDB: The InnoDB memory heap is disabled-08-02 15:06:58 33 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins-08-02 15:06:58 33 [Note] InnoDB: Memory barrier is not used......#現(xiàn)在我們在物理機上連接一下我們的neutron數(shù)據(jù)庫試試:[root@dev ~]# mysql -uneutron -ppassword -P6033 -h127.0.0.1 neutronWelcome to the MariaDB monitor. Commands end with ; or \g.Your MySQL connection id is 2Server version: 5.6.26 MySQL Community Server (GPL)Copyright (c) 2000, 2014, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MySQL [neutron]>
可以看到通過docker run這么一個命令我們就運行了一個完整的數(shù)據(jù)庫,并且這個運行的時候可以指定某些參數(shù)的值,,這一點如果要通過虛擬機來實現(xiàn)那么是比較麻煩的,。
2. Docker實現(xiàn)原理
上面看過一個例子后,這里我們來講一下Docker的實現(xiàn)原理,。Docker的實現(xiàn)依賴兩個東西:
1. namespace
2. CGroup
namespace在前面我們講過,,主要用于提供名字空間的隔離。我們之前分析過網(wǎng)絡(luò)namespace的實現(xiàn),,因此這里就不多介紹了,。CGroup的作用大家可以參考這個連接的介紹:
* http:///articles/17049.html
簡單的說,CGroup的作用就是限制某個進程可以使用的系統(tǒng)資源,。比如限制一個進程只可以使用1個G的內(nèi)存,,或者限制這個進程的IO速率等等。
當我們運行docker XXX命令的時候,,大部分命令發(fā)送給了一個叫做daemon的進程,。這個daemon進程的啟動、停止方式也是通過docker命令完成的:
# 啟動daemon[root@dev ~]# docker -d
接下來當我們執(zhí)行如docker ps這種查看當前有哪些正在運行的容器的命令的時候,,docker ps會的轉(zhuǎn)成一個滿足RESTful要求的HTTP請求,,然后這個HTTP請求會通過TCP或者本地套接字的方式發(fā)送給我們的daemon進程。daemon守護進程進行相關(guān)操作后會返回結(jié)果給我們運行docker ps的客戶端,,接著docker ps會的輸出結(jié)果,。也就是說docker其實就是在我們的本機啟動了一個server,然后docker ps這類命令就是和我們的server進行普通的交互,,且這種交互是基于HTTP的請求,。
現(xiàn)在來解釋下什么是容器。在上面的MySQL例子執(zhí)行后,,我們在系統(tǒng)中可以看到如下進程:
[root@dev ~]# ps -elf | grep NeutronMySQL S root 2535 2204 0 80 0 - 51809 ep_pol 23:06 pts/0 00:00:00 docker run --name NeutronMySQL -p 6033:3306 -e MYSQL_USER=neutron -e MYSQL_PASSWORD=password -e MYSQL_DATABASE=neutron -e MYSQL_ROOT_PASSWORD=password docker.io/mysql
對于Docker來說,,一個容器其實就是一個進程。這個進程運行在自己獨立的namespace下面,,所以這個pid為2535進程其看到的namespace都是獨立的,。當我們執(zhí)行docker run命令后,docker首先會啟動一個進程,,然后設(shè)置這個進程使用新的namespace(如果對namespace不熟悉的話,,建議去看下小秦博客中namespace的相關(guān)知識)。接著會在這個namespace中運行預(yù)定義的一些指令,比如啟動一個MySQL進程,,然后建立一個數(shù)據(jù)庫等等,。可以看到這個進程之所以能和我們的物理機隔離的關(guān)鍵正是在于namespace,。那么CGroup有什么用呢,?我們的例子里并沒有限制我們的容器(也就是我們的進程)能使用多少系統(tǒng)資源,加入我希望我的MySQL容器只能使用10個G的系統(tǒng)內(nèi)存,,那么這里可以通過CGroup實現(xiàn)資源限制,。
接著我們來解釋下鏡像。上面說了容器其實就是一個進程,,生活在自己獨立的namespace中,。那么這個容器進程所看到的文件系統(tǒng)是什么呢?這個容器進程所看到的文件系統(tǒng)是DOcker通過chroot來實現(xiàn)的,。chroot是Linux很早就提供的一個命令,,用于修改某個進程所看到的根文件系統(tǒng)路徑。比如在進程A的執(zhí)行代碼中執(zhí)行類似chroot /tmp/a的命令,,則在此之后A執(zhí)行l(wèi)s /命令看到的就是/tmp/a下面的內(nèi)容了,。因此在Docker中每個容器都是通過chroot命令獲取自己獨立的文件系統(tǒng)的。但是如果每次啟動容器都需要安裝MySQL等等才能使用的話會很麻煩,,所以有人會事先建立好類似/tmp/a之類的目錄,,在這個目錄下會建立如/tmp/a/bin/mysqld、/tmp/a/etc/my.cnf之類的文件并進行配置,,接著容器啟動的時候設(shè)置/tmp/a為根目錄后,,其就能執(zhí)行mysqld命令,并且獲取到預(yù)先設(shè)置的my.cnf配置文件了,。這樣的一個/tmp/a所打包生成的一個文件其實就是一個鏡像的雛形,。那么實際上的鏡像和這個雛形有什么區(qū)別呢?如果按照我們剛剛說的,,每個容器啟動后都需要chroot自己的根文件系統(tǒng),,則如果有100個容器則會有100個類似于/tmp/a的目錄存在,這樣對于資源的利用以及容器的啟動速度(也就是這個進程的初始化速度)是很不利的,。因為類似于mysqld這樣的文件大家都是公用的,,完全沒有必要在/tmp/a/bin下有一份,同時在/tmp/b/bin下也有一份,。為了解決這個問題Docker使用了支持多層次的文件系統(tǒng),,比如AUFS或者Device Mapper,。他們的作用簡單的說就是對于容器A,,啟動的時候系統(tǒng)不會從/tmp/mysql復(fù)制一個完整的目錄到/tmp/a下,而是建立一個空的/tmp/A。然后通過AUFS或Device Mapper的技術(shù)掛載出一個/tmp/a目錄,,對該目錄的所有讀操作會先在/tmp/A下進行,,如果/tmp/A下沒有找到對應(yīng)的文件則會去/tmp/mysql下讀取。對于該目錄的所有寫操作則都只會發(fā)生在/tmp/A,。比如用戶修改了my.cnf,,則實際上的操作是從/tmp/mysql/etc/my.cnf復(fù)制一份到/tmp/A/etc/my.cnf,然后再對/tmp/A/etc/my.cnf進行修改,。Docker在啟動容器的時候,,會先設(shè)置好這些,然后再chroot /tmp/a為根目錄,??梢钥吹酵ㄟ^這種多層次的文件系統(tǒng)用戶可以對底層的/tmp/mysql進行定制后再提供服務(wù)給其他人使用,比如用戶在進行上面的操作后,,安裝了一個Apache+PHP在容器中,,此時我們的/tmp/mysql + /tmp/A合并而成的/tmp/a就能提供一個完整的LAMP服務(wù)了。于是可以將/tmp/mysql + /tmp/A一起打包成一個文件提供出去,。當某個用戶需要一個LAMP的容器B在上面運行一個wordpress應(yīng)用的時候,,其解壓這個文件,然后Docker建立空的/tmp/B,,并通過上面說的AUFS或Device Mapper將/tmp/mysql,、/tmp/A、/tmp/B掛載為/tmp/b,,然后啟動的新容器chroot到/tmp/b后就能直接使用LAMP環(huán)境了,。這里說到的/tmp/mysql加上/tmp/A以及一些元信息所共同打包二層的一個文件就是我們所說的鏡像??梢钥吹界R像是分層的,。
以上這些就是Docker的實現(xiàn)原理,說實話從原理上看真的很簡單,,因為Docker使用的技術(shù)都是一些現(xiàn)有的技術(shù),。