容器介紹
Docker 是世界領先的軟件容器平臺,所以想要搞懂 Docker 的概念我們必須先從容器開始說起。
什么是容器?
先來看看容器較為官方的解釋
一句話概括容器:容器就是將軟件打包成標準化單元,以用于開發、交付和部署。
- 容器鏡像是輕量的、可執行的獨立軟件包 ,包含軟件運行所需的所有內容:代碼、運行時環境、系統工具、系統庫和設置。
- 容器化軟件適用于基于 Linux 和 Windows 的應用,在任何環境中都能夠始終如一地運行。
- 容器賦予了軟件獨立性,使其免受外在環境差異(例如,開發和預演環境的差異)的影響,從而有助于減少團隊間在相同基礎設施上運行不同軟件時的沖突。
再來看看容器較為通俗的解釋
如果需要通俗地描述容器的話,我覺得容器就是一個存放東西的地方,就像書包可以裝各種文具、衣柜可以放各種衣服、鞋架可以放各種鞋子一樣。我們現在所說的容器存放的東西可能更偏向于應用比如網站、程序甚至是系統環境。
圖解物理機,虛擬機與容器
關于虛擬機與容器的對比在后面會詳細介紹到,這里只是通過網上的圖片加深大家對于物理機、虛擬機與容器這三者的理解(下面的圖片來源于網絡)。
物理機:
虛擬機:
容器:
通過上面這三張抽象圖,我們可以大概通過類比概括出:容器虛擬化的是操作系統而不是硬件,容器之間是共享同一套操作系統資源的。虛擬機技術是虛擬出一套硬件后,在其上運行一個完整操作系統。因此容器的隔離級別會稍低一些。
容器 VS 虛擬機
每當說起容器,我們不得不將其與虛擬機做一個比較。就我而言,對于兩者無所謂誰會取代誰,而是兩者可以和諧共存。
簡單來說:容器和虛擬機具有相似的資源隔離和分配優勢,但功能有所不同,因為容器虛擬化的是操作系統,而不是硬件,因此容器更容易移植,效率也更高。
傳統虛擬機技術是虛擬出一套硬件后,在其上運行一個完整操作系統,在該系統上再運行所需應用進程;而容器內的應用進程直接運行于宿主的內核,容器內沒有自己的內核,而且也沒有進行硬件虛擬。因此容器要比傳統虛擬機更為輕便。
容器和虛擬機的對比:
-
容器是一個應用層抽象,用于將代碼和依賴資源打包在一起。 多個容器可以在同一臺機器上運行,共享操作系統內核,但各自作為獨立的進程在用戶空間中運行 。與虛擬機相比, 容器占用的空間較少(容器鏡像大小通常只有幾十兆),瞬間就能完成啟動 。
-
虛擬機 (VM) 是一個物理硬件層抽象,用于將一臺服務器變成多臺服務器。管理程序允許多個 VM 在一臺機器上運行。每個 VM 都包含一整套操作系統、一個或多個應用、必要的二進制文件和庫資源,因此 占用大量空間 。而且 VM 啟動也十分緩慢 。
通過 Docker 官網,我們知道了這么多 Docker 的優勢,但是大家也沒有必要完全否定虛擬機技術,因為兩者有不同的使用場景。虛擬機更擅長于徹底隔離整個運行環境。例如,云服務提供商通常采用虛擬機技術隔離不同的用戶。而 Docker 通常用于隔離不同的應用 ,例如前端,后端以及數據庫。
就我而言,對于兩者無所謂誰會取代誰,而是兩者可以和諧共存。
Docker 介紹
什么是 Docker?
說實話關于 Docker 是什么并不太好說,下面我通過四點向你說明 Docker 到底是個什么東西。
- Docker 是世界領先的軟件容器平臺。
- Docker 使用 Google 公司推出的 Go 語言 進行開發實現,基于 Linux 內核 提供的 CGroup 功能和 namespace 來實現的,以及 AUFS 類的 UnionFS 等技術,對進程進行封裝隔離,屬于操作系統層面的虛擬化技術。 由于隔離的進程獨立于宿主和其它的隔離的進程,因此也稱其為容器。
- Docker 能夠自動執行重復性任務,例如搭建和配置開發環境,從而解放了開發人員以便他們專注在真正重要的事情上:構建杰出的軟件。
- 用戶可以方便地創建和使用容器,把自己的應用放入容器。容器還可以進行版本管理、復制、分享、修改,就像管理普通的代碼一樣。
Docker 思想:
- 集裝箱:就像海運中的集裝箱一樣,Docker 容器包含了應用程序及其所有依賴項,確保在任何環境中都能以相同的方式運行。
- **標準化:**運輸方式、存儲方式、API 接口。
- 隔離:每個 Docker 容器都在自己的隔離環境中運行,與宿主機和其他容器隔離。
Docker 容器的特點
- 輕量 : 在一臺機器上運行的多個 Docker 容器可以共享這臺機器的操作系統內核;它們能夠迅速啟動,只需占用很少的計算和內存資源。鏡像是通過文件系統層進行構造的,并共享一些公共文件。這樣就能盡量降低磁盤用量,并能更快地下載鏡像。
- 標準 : Docker 容器基于開放式標準,能夠在所有主流 Linux 版本、Microsoft Windows 以及包括 VM、裸機服務器和云在內的任何基礎設施上運行。
- 安全 : Docker 賦予應用的隔離性不僅限于彼此隔離,還獨立于底層的基礎設施。Docker 默認提供最強的隔離,因此應用出現問題,也只是單個容器的問題,而不會波及到整臺機器。
為什么要用 Docker ?
- Docker 的鏡像提供了除內核外完整的運行時環境,確保了應用運行環境一致性,從而不會再出現 “這段代碼在我機器上沒問題啊” 這類問題;——一致的運行環境
- 可以做到秒級、甚至毫秒級的啟動時間。大大的節約了開發、測試、部署的時間。——更快速的啟動時間
- 避免公用的服務器,資源會容易受到其他用戶的影響。——隔離性
- 善于處理集中爆發的服務器使用壓力;——彈性伸縮,快速擴展
- 可以很輕易的將在一個平臺上運行的應用,遷移到另一個平臺上,而不用擔心運行環境的變化導致應用無法正常運行的情況。——遷移方便
- 使用 Docker 可以通過定制應用鏡像來實現持續集成、持續交付、部署。——持續交付和部署
Docker 基本概念
Docker 中有非常重要的三個基本概念:鏡像(Image)、容器(Container)和倉庫(Repository)。
理解了這三個概念,就理解了 Docker 的整個生命周期。
鏡像(Image):一個特殊的文件系統
操作系統分為內核和用戶空間。對于 Linux 而言,內核啟動后,會掛載 root 文件系統為其提供用戶空間支持。而 Docker 鏡像(Image),就相當于是一個 root 文件系統。
Docker 鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時準備的一些配置參數(如匿名卷、環境變量、用戶等)。 鏡像不包含任何動態數據,其內容在構建之后也不會被改變。
Docker 設計時,就充分利用 Union FS 的技術,將其設計為分層存儲的架構 。鏡像實際是由多層文件系統聯合組成。
鏡像構建時,會一層層構建,前一層是后一層的基礎。每一層構建完就不會再發生改變,后一層上的任何改變只發生在自己這一層。 比如,刪除前一層文件的操作,實際不是真的刪除前一層的文件,而是僅在當前層標記為該文件已刪除。在最終容器運行的時候,雖然不會看到這個文件,但是實際上該文件會一直跟隨鏡像。因此,在構建鏡像的時候,需要額外小心,每一層盡量只包含該層需要添加的東西,任何額外的東西應該在該層構建結束前清理掉。
分層存儲的特征還使得鏡像的復用、定制變的更為容易。甚至可以用之前構建好的鏡像作為基礎層,然后進一步添加新的層,以定制自己所需的內容,構建新的鏡像。
容器(Container):鏡像運行時的實體
鏡像(Image)和容器(Container)的關系,就像是面向對象程序設計中的 類 和 實例 一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等 。
容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行于屬于自己的獨立的 命名空間。前面講過鏡像使用的是分層存儲,容器也是如此。
容器存儲層的生存周期和容器一樣,容器消亡時,容器存儲層也隨之消亡。因此,任何保存于容器存儲層的信息都會隨容器刪除而丟失。
按照 Docker 最佳實踐的要求,容器不應該向其存儲層內寫入任何數據 ,容器存儲層要保持無狀態化。所有的文件寫入操作,都應該使用數據卷(Volume)、或者綁定宿主目錄,在這些位置的讀寫會跳過容器存儲層,直接對宿主(或網絡存儲)發生讀寫,其性能和穩定性更高。數據卷的生存周期獨立于容器,容器消亡,數據卷不會消亡。因此, 使用數據卷后,容器可以隨意刪除、重新 run ,數據卻不會丟失。
倉庫(Repository):集中存放鏡像文件的地方
鏡像構建完成后,可以很容易的在當前宿主上運行,但是, 如果需要在其它服務器上使用這個鏡像,我們就需要一個集中的存儲、分發鏡像的服務,Docker Registry 就是這樣的服務。
一個 Docker Registry 中可以包含多個倉庫(Repository);每個倉庫可以包含多個標簽(Tag);每個標簽對應一個鏡像。所以說:鏡像倉庫是 Docker 用來集中存放鏡像文件的地方類似于我們之前常用的代碼倉庫。
通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標簽就常用于對應該軟件的各個版本 。我們可以通過<倉庫名>:<標簽>
的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標簽,將以 latest 作為默認標簽.。
這里補充一下 Docker Registry 公開服務和私有 Docker Registry 的概念:
Docker Registry 公開服務 是開放給用戶使用、允許用戶管理鏡像的 Registry 服務。一般這類公開服務允許用戶免費上傳、下載公開的鏡像,并可能提供收費服務供用戶管理私有鏡像。
最常使用的 Registry 公開服務是官方的 Docker Hub ,這也是默認的 Registry,并擁有大量的高質量的官方鏡像,網址為:https://hub.docker.com/ 。官方是這樣介紹 Docker Hub 的:
Docker Hub 是 Docker 官方提供的一項服務,用于與您的團隊查找和共享容器鏡像。
比如我們想要搜索自己想要的鏡像:
在 Docker Hub 的搜索結果中,有幾項關鍵的信息有助于我們選擇合適的鏡像:
- OFFICIAL Image:代表鏡像為 Docker 官方提供和維護,相對來說穩定性和安全性較高。
- Stars:和點贊差不多的意思,類似 GitHub 的 Star。
- Downloads:代表鏡像被拉取的次數,基本上能夠表示鏡像被使用的頻度。
當然,除了直接通過 Docker Hub 網站搜索鏡像這種方式外,我們還可以通過 docker search
這個命令搜索 Docker Hub 中的鏡像,搜索的結果是一致的。
? ~ docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 8763 [OK]
mariadb MariaDB is a community-developed fork of MyS… 3073 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 650
在國內訪問 Docker Hub 可能會比較慢國內也有一些云服務商提供類似于 Docker Hub 的公開服務。比如 時速云鏡像庫、網易云鏡像服務、DaoCloud 鏡像市場、阿里云鏡像庫等。
除了使用公開服務外,用戶還可以在 本地搭建私有 Docker Registry 。Docker 官方提供了 Docker Registry 鏡像,可以直接使用做為私有 Registry 服務。開源的 Docker Registry 鏡像只提供了 Docker Registry API 的服務端實現,足以支持 Docker 命令,不影響使用。但不包含圖形界面,以及鏡像維護、用戶管理、訪問控制等高級功能。
Image、Container 和 Repository 的關系
下面這一張圖很形象地展示了 Image、Container、Repository 和 Registry/Hub 這四者的關系:
- Dockerfile 是一個文本文件,包含了一系列的指令和參數,用于定義如何構建一個 Docker 鏡像。運行
docker build
命令并指定一個 Dockerfile 時,Docker 會讀取 Dockerfile 中的指令,逐步構建一個新的鏡像,并將其保存在本地。 docker pull
命令可以從指定的 Registry/Hub 下載一個鏡像到本地,默認使用 Docker Hub。docker run
命令可以從本地鏡像創建一個新的容器并啟動它。如果本地沒有鏡像,Docker 會先嘗試從 Registry/Hub 拉取鏡像。docker push
命令可以將本地的 Docker 鏡像上傳到指定的 Registry/Hub。
上面涉及到了一些 Docker 的基本命令,后面會詳細介紹大。
Build Ship and Run
Docker 的概念基本上已經講完,我們再來談談:Build, Ship, and Run。
如果你搜索 Docker 官網,會發現如下的字樣:“Docker - Build, Ship, and Run Any App, Anywhere”。那么 Build, Ship, and Run 到底是在干什么呢?
- Build(構建鏡像):鏡像就像是集裝箱包括文件以及運行環境等等資源。
- Ship(運輸鏡像):主機和倉庫間運輸,這里的倉庫就像是超級碼頭一樣。
- Run (運行鏡像):運行的鏡像就是一個容器,容器就是運行程序的地方。
Docker 運行過程也就是去倉庫把鏡像拉到本地,然后用一條命令把鏡像運行起來變成容器。所以,我們也常常將 Docker 稱為碼頭工人或碼頭裝卸工,這和 Docker 的中文翻譯搬運工人如出一轍。
Docker 常見命令
基本命令
docker version # 查看docker版本
docker images # 查看所有已下載鏡像,等價于:docker image ls 命令
docker container ls # 查看所有容器
docker ps #查看正在運行的容器
docker image prune # 清理臨時的、沒有被使用的鏡像文件。-a, --all: 刪除所有沒有用的鏡像,而不僅
拉取鏡像
docker pull
命令默認使用的 Registry/Hub 是 Docker Hub。當你執行 docker pull 命令而沒有指定任何 Registry/Hub 的地址時,Docker 會從 Docker Hub 拉取鏡像。
docker search mysql # 查看mysql相關鏡像
docker pull mysql:5.7 # 拉取mysql鏡像
docker image ls # 查看所有已下載鏡像
構建鏡像
運行 docker build
命令并指定一個 Dockerfile 時,Docker 會讀取 Dockerfile 中的指令,逐步構建一個新的鏡像,并將其保存在本地。
#
# imageName 是鏡像名稱,1.0.0 是鏡像的版本號或標簽
docker build -t imageName:1.0.0 .
需要注意:Dockerfile 的文件名不必須為 Dockerfile,也不一定要放在構建上下文的根目錄中。使用 -f
或 --file
選項,可以指定任何位置的任何文件作為 Dockerfile。當然,一般大家習慣性的會使用默認的文件名 Dockerfile
,以及會將其置于鏡像構建上下文目錄中。
刪除鏡像
比如我們要刪除我們下載的 mysql 鏡像。
通過 docker rmi [image]
(等價于docker image rm [image]
)刪除鏡像之前首先要確保這個鏡像沒有被容器引用(可以通過標簽名稱或者鏡像 ID 刪除)。通過我們前面講的docker ps
命令即可查看。
? ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c4cd691d9f80 mysql:5.7 "docker-entrypoint.s…" 7 weeks ago Up 12 days 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
可以看到 mysql 正在被 id 為 c4cd691d9f80 的容器引用,我們需要首先通過 docker stop c4cd691d9f80
或者 docker stop mysql
暫停這個容器。
然后查看 mysql 鏡像的 id
? ~ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 f6509bac4980 3 months ago 373MB
通過 IMAGE ID 或者 REPOSITORY 名字即可刪除
docker rmi f6509bac4980 # 或者 docker rmi mysql
鏡像推送
docker push
命令用于將本地的 Docker 鏡像上傳到指定的 Registry/Hub。
# 將鏡像推送到私有鏡像倉庫 Harbor
# harbor.example.com是私有鏡像倉庫的地址,ubuntu是鏡像的名稱,18.04是鏡像的版本標簽
docker push harbor.example.com/ubuntu:18.04
鏡像推送之前,要確保本地已經構建好需要推送的 Docker 鏡像。另外,務必先登錄到對應的鏡像倉庫。
Docker 數據管理
在容器中管理數據主要有兩種方式:
- 數據卷(Volumes)
- 掛載主機目錄 (Bind mounts)
數據卷是由 Docker 管理的數據存儲區域,有如下這些特點:
- 可以在容器之間共享和重用。
- 即使容器被刪除,數據卷中的數據也不會被自動刪除,從而確保數據的持久性。
- 對數據卷的修改會立馬生效。
- 對數據卷的更新,不會影響鏡像。
# 創建一個數據卷
docker volume create my-vol
# 查看所有的數據卷
docker volume ls
# 查看數據卷的具體信息
docker inspect web
# 刪除指定的數據卷
docker volume rm my-vol
在用 docker run
命令的時候,使用 --mount
標記來將一個或多個數據卷掛載到容器里。
還可以通過 --mount
標記將宿主機上的文件或目錄掛載到容器中,這使得容器可以直接訪問宿主機的文件系統。Docker 掛載主機目錄的默認權限是讀寫,用戶也可以通過增加 readonly
指定為只讀。
Docker Compose
什么是 Docker Compose?有什么用?
Docker Compose 是 Docker 官方編排(Orchestration)項目之一,基于 Python 編寫,負責實現對 Docker 容器集群的快速編排。通過 Docker Compose,開發者可以使用 YAML 文件來配置應用的所有服務,然后只需一個簡單的命令即可創建和啟動所有服務。
Docker Compose 是開源項目,地址:https://github.com/docker/compose。
Docker Compose 的核心功能:
- 多容器管理:允許用戶在一個 YAML 文件中定義和管理多個容器。
- 服務編排:配置容器間的網絡和依賴關系。
- 一鍵部署:通過簡單的命令,如
docker-compose up
和docker-compose down
,可以輕松地啟動和停止整個應用程序。
Docker Compose 簡化了多容器應用程序的開發、測試和部署過程,提高了開發團隊的生產力,同時降低了應用程序的部署復雜度和管理成本。
Docker Compose 文件基本結構
Docker Compose 文件是 Docker Compose 工具的核心,用于定義和配置多容器 Docker 應用。這個文件通常命名為 docker-compose.yml
,采用 YAML(YAML Ain't Markup Language)格式編寫。
Docker Compose 文件基本結構如下:
- 版本(version): 指定 Compose 文件格式的版本。版本決定了可用的配置選項。
- 服務(services): 定義了應用中的每個容器(服務)。每個服務可以使用不同的鏡像、環境設置和依賴關系。
- 鏡像(image): 從指定的鏡像中啟動容器,可以是存儲倉庫、標簽以及鏡像 ID。
- 命令(command): 可選,覆蓋容器啟動后默認執行的命令。在啟動服務時運行特定的命令或腳本,常用于啟動應用程序、執行初始化腳本等。
- 端口(ports): 可選,映射容器和宿主機的端口。
- 依賴(depends_on): 依賴配置的選項,意思是如果服務啟動是如果有依賴于其他服務的,先啟動被依賴的服務,啟動完成后在啟動該服務。
- 環境變量(environment): 可選,設置服務運行所需的環境變量。
- 重啟(restart): 可選,控制容器的重啟策略。在容器退出時,根據指定的策略自動重啟容器。
- 服務卷(volumes): 可選,定義服務使用的卷,用于數據持久化或在容器之間共享數據。
- 構建(build): 指定構建鏡像的 dockerfile 的上下文路徑,或者詳細配置對象。
- 網絡(networks): 定義了容器間的網絡連接。
- 卷(volumes): 用于數據持久化和共享的數據卷定義。常用于數據庫存儲、配置文件、日志等數據的持久化。
Docker Compose 常見命令
啟動
docker-compose up
會根據?docker-compose.yml
?文件中定義的服務來創建和啟動容器,并將它們連接到默認的網絡中。
# 在當前目錄下尋找 docker-compose.yml 文件,并根據其中定義的服務啟動應用程序
docker-compose up
# 后臺啟動
docker-compose up -d
# 強制重新創建所有容器,即使它們已經存在
docker-compose up --force-recreate
# 重新構建鏡像
docker-compose up --build
# 指定要啟動的服務名稱,而不是啟動所有服務
# 可以同時指定多個服務,用空格分隔。
docker-compose up service_name
另外,如果 Compose 文件名稱不是 docker-compose.yml
也沒問題,可以通過 -f
參數指定。
docker-compose -f docker-compose.prod.yml up
暫停
docker-compose down
用于停止并移除通過 docker-compose up
啟動的容器和網絡。
# 在當前目錄下尋找 docker-compose.yml 文件
# 根據其中定義移除啟動的所有容器,網絡和卷。
docker-compose down
# 停止容器但不移除
docker-compose down --stop
# 指定要停止和移除的特定服務,而不是停止和移除所有服務
# 可以同時指定多個服務,用空格分隔。
docker-compose down service_name
同樣地,如果 Compose 文件名稱不是 docker-compose.yml
也沒問題,可以通過 -f
參數指定。
docker-compose -f docker-compose.prod.yml down
查看
docker-compose ps
用于查看通過 docker-compose up
啟動的所有容器的狀態信息。
# 查看所有容器的狀態信息
docker-compose ps
# 只顯示服務名稱
docker-compose ps --services
# 查看指定服務的容器
docker-compose ps service_name
其他
Docker 底層原理
首先,Docker 是基于輕量級虛擬化技術的軟件,那什么是虛擬化技術呢?
簡單點來說,虛擬化技術可以這樣定義:
虛擬化技術是一種資源管理技術,是將計算機的各種實體資源)(CPU、內存、磁盤空間、網絡適配器等),予以抽象、轉換后呈現出來并可供分割、組合為一個或多個電腦配置環境。由此,打破實體結構間的不可切割的障礙,使用戶可以比原本的配置更好的方式來應用這些電腦硬件資源。這些資源的新虛擬部分是不受現有資源的架設方式,地域或物理配置所限制。一般所指的虛擬化資源包括計算能力和數據存儲。
Docker 技術是基于 LXC(Linux container- Linux 容器)虛擬容器技術的。
LXC,其名稱來自 Linux 軟件容器(Linux Containers)的縮寫,一種操作系統層虛擬化(Operating system–level virtualization)技術,為 Linux 內核容器功能的一個用戶空間接口。它將應用軟件系統打包成一個軟件容器(Container),內含應用軟件本身的代碼,以及所需要的操作系統核心和庫。通過統一的名字空間和共用 API 來分配不同軟件容器的可用硬件資源,創造出應用程序的獨立沙箱運行環境,使得 Linux 用戶可以容易的創建和管理系統或應用容器。
LXC 技術主要是借助 Linux 內核中提供的 CGroup 功能和 namespace 來實現的,通過 LXC 可以為軟件提供一個獨立的操作系統運行環境。
cgroup 和 namespace 介紹:
-
namespace 是 Linux 內核用來隔離內核資源的方式。 通過 namespace 可以讓一些進程只能看到與自己相關的一部分資源,而另外一些進程也只能看到與它們自己相關的資源,這兩撥進程根本就感覺不到對方的存在。具體的實現方式是把一個或多個進程的相關資源指定在同一個 namespace 中。Linux namespaces 是對全局系統資源的一種封裝隔離,使得處于不同 namespace 的進程擁有獨立的全局系統資源,改變一個 namespace 中的系統資源只會影響當前 namespace 里的進程,對其他 namespace 中的進程沒有影響。
(以上關于 namespace 介紹內容來自Linux Namespace : 簡介 - sparkdev - 博客園 ,更多關于 namespace 的內容可以查看這篇文章 )。
-
CGroup 是 Control Groups 的縮寫,是 Linux 內核提供的一種可以限制、記錄、隔離進程組 (process groups) 所使用的物理資源 (如 cpu memory i/o 等等) 的機制。
(以上關于 CGroup 介紹內容來自 IBM Developer ,更多關于 CGroup 的內容可以查看這篇文章 )。
cgroup 和 namespace 兩者對比:
兩者都是將進程進行分組,但是兩者的作用還是有本質區別。namespace 是為了隔離進程組之間的資源,而 cgroup 是為了對一組進程進行統一的資源監控和限制。