去年中在 Hacker News 上有篇熱帖 「Goodbye Docker: Purging is Such Sweet Sorrow」 這篇文章內容其實很常規,無非是本身使用 Docker 的時候遇到了一些問題,最後切換到了 Podman
buildah
和 Skopeo
組合的工具集,以做爲 Docker 的一種替代方案。git
這樣的文章在近一年左右應該算是比較常見了,但爲什麼會成爲 Hacker News 上的熱帖呢?主要有兩方面的緣由: 1. 仍是由於此文的做者是 Ian Miell 他是 Docker in Practice (中譯本叫作 《Docker 實踐》)的做者,一個 Docker 相關技術書籍的做者將本身的 Docker 給替換掉,會讓人比較好奇;2. 替換 Docker 貌似是一種方向,多數人也想要了解這種技術變遷會帶來什麼優點或者有什麼樣的坑。github
背景介紹完了,咱們來開始正文。docker
Docker 在 2013 年 PyCon 上首次亮相,隨後開源。因爲其簡單易用,以及切實解決了因環境不一致致使的問題,迅速得到到一大批粉絲。shell
接下來的幾年中,Docker 改變了軟件的交付方式,更多的人爲之着迷。隨之而來的是 Docker 生態的蓬勃發展。json
Docker 在大多數人眼中幾乎是容器(container)的代名詞,即便是如今我也會常聽到有人說「我有幾個 docker 跑 xx 服務」 相似這樣的話,無疑 Docker 引領了容器的時代。安全
一直在提容器,咱們不如深刻點先來探究下容器究竟是什麼?bash
在 Docker 官網上對容器的描述是: 「A standardized unit of software」 -- 軟件的標準單元,並無什麼更詳細的內容了。多數人對容器的見解也都停留在很淺顯的認識:認爲容器是輕量級的虛擬機,因此後來也就有一段時間有人推「富容器」技術。網絡
這裏我想更深刻一些,可能會涉及一些容器的歷史,但我認爲這些內容有助於讀者理解個人角度和觀點。架構
說白了 容器實際上是在某臺機器上的「一組」進程,固然這組進程可能只有一個;它們有相同的特性,固然所受的限制也是相同的;既然叫作容器,很天然的咱們認爲它們與外界能夠進行隔離/應該有一個分界線。工具
談完了它的基本特性,那咱們來看看如何來建立一個容器。
若是你對 Linux 相對熟悉的話,你可能知道 chroot
,咱們有時會使用 chroot
改變某進程的根目錄,而且它不能訪問該目錄以外的其餘目錄。
這個和咱們在一個容器內的感受很像了。事實上在幾年前確實有人用一百多行的 bash shell 利用 chroot
寫了一個模擬 Docker 建立容器的實現,稱之爲 bocker , 有興趣的朋友能夠去看看該項目的代碼。
接下來咱們使用 chroot
建立一個隔離環境:
首先它須要有一個 rootfs
的根文件系統,咱們能夠很簡單的使用 Docker 得到咱們須要的內容。
(MoeLove) ➜ ~ mkdir chroot-dir
(MoeLove) ➜ ~ cd chroot-dir
(MoeLove) ➜ chroot-dir docker save -o debian.tar debian:buster
(MoeLove) ➜ chroot-dir ls
debian.tar
(MoeLove) ➜ chroot-dir tar -xf debian.tar
(MoeLove) ➜ chroot-dir ls
098963abf3c3b87b8114ff67d164097dfac2d5659e39f9beb5604db91585f375.json debian.tar repositories
0f28619fe69181d3af529d56692f1362b7a7c8a6bf8dc9ab0d6d4f9ef9b0004d manifest.json
(MoeLove) ➜ chroot-dir mkdir -p debian
(MoeLove) ➜ chroot-dir tar -C debian -xf 0f28619fe69181d3af529d56692f1362b7a7c8a6bf8dc9ab0d6d4f9ef9b0004d/layer.tar
(MoeLove) ➜ chroot-dir ls debian
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
複製代碼
能夠看到咱們經過對鏡像文件的解壓,獲得了咱們所需的 rootfs
的所有內容,接下來咱們看看 chroot
的能力:
(MoeLove) ➜ chroot sudo chroot debian /bin/bash -i
[sudo] tao 的密碼:
root@localhost:/# whoami
root
root@localhost:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
複製代碼
咱們目前就已經在一個 "容器" 內了,咱們來看下使用這個容器咱們能作些什麼。
首先咱們看看當前「容器」內的路由表:
root@localhost:/# mkdir -p /sys
root@localhost:/# mount -t sysfs sys /sys
root@localhost:/# ip r
default via 192.168.0.1 dev wlp2s0 proto dhcp metric 600
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.0.0/24 dev wlp2s0 proto kernel scope link src 192.168.0.108 metric 600
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 linkdown
複製代碼
很天然的,咱們發現它能夠訪問全部的網絡設備
root@localhost:/# ls /sys/class/net/
docker0 enp1s0 lo vboxnet0 virbr0 virbr0-nic wlp2s0
複製代碼
另外,咱們還能夠將 /proc
也掛載進去
root@localhost:/# mkdir -p /proc
root@localhost:/# mount -t proc proc /proc
root@localhost:/# ls -al /proc/31730/ns/pid
lrwxrwxrwx. 1 1000 1000 0 Jul 29 16:47 /proc/31730/ns/pid -> 'pid:[4026531836]'
複製代碼
能夠看到咱們在這個「容器」內,能夠訪問到主機上的進程及網絡等信息,這表示沒有任何的進程或者網絡隔離,這帶來的危險是咱們甚至能夠在容器內殺掉容器外的進程,或者經過容器來攻擊主機。
爲了能更好的解決這個問題,接下來出現了另外一個技術:namespace 。
Namespace 是在 2002 年由 Linux 2.4.19 開始加入內核的特性,它主要的做用就作了一層抽象和隔離,使得在 namespace 中的進程/進程組能夠看起來擁有本身的獨立資源,具體的「資源」表現形式取決於給它賦予了哪些 namespace 。
隨着 2013 年 Linux 3.8 中 user namespace 的引入,對於咱們如今所熟知的容器所需的所有 namespace 就都實現了:mnt
pid
net
ipc
uts
user
和 cgroup
對於這些 namespace 和 Docker 的關係,咱們稍後會逐步來看,這裏先對 namespace 作下介紹,而後繼續前面"容器"的內容。
咱們能夠經過三個系統調用直接操做 namespace ,這三個系統調用分別是:
知道了這些基礎知識後,咱們回到前面"容器"的內容中。
咱們在前面 chroot
的例子中看到沒能作到進程或網絡隔離,如今咱們來試試看用 namespace 完成該需求。
(MoeLove) ➜ ~ sudo unshare -fp --mount-proc -n
[sudo] tao 的密碼:
[root@localhost]/home/tao# ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@localhost]/home/tao# ps -a
PID TTY TIME CMD
1 pts/15 00:00:00 zsh
33 pts/15 00:00:00 ps
[root@localhost]/home/tao#
複製代碼
這裏很明顯,咱們當前所在進程的 PID 爲 1 而且看不到宿主機上的其餘進程,咱們達到了基礎的隔離效果。網絡也一樣,如今只包含一個 lo
接口。關於網絡部分的內容其實能聊的東西還有不少,我在專欄《Docker 核心知識必知必會》中,用 8 節內容來深刻的聊了聊 Docker 容器網絡相關的內容,感興趣的讀者能夠看看。
上面聊了這麼多,無非是想說明容器的發展歷程,以及它是什麼。它其實就是用了各類 Linux 內核提供的特性/功能協同實現的對進程組的資源隔離。用 Docker 或者 namespace 或者 chroot 之類的均可以造出來一個 「容器」 ,那咱們爲何要用 Docker 呢?
想必你們對 cgroups 不會太陌生,它在 2008 年進入 Linux 2.6.24 後,基於它而且瞄準容器世界的一個項目誕生了。
Linux Container (LXC)結合了 namespace 和 cgroups 等技術,目標就是要創造出運行在 Linux 系統中,而且隔離性良好的容器環境。
LXC 的事情發生在 2008 年,但值得注意的是 cgroups 最初是由 Google 的工程師開發的,最先的記錄是在 2006 年,事實上當時 Google 確實也在作相似的容器化項目。
時間一晃而過,就到了 2013 年的 PyCon 上,在此次大會上 Docker 正式面世。而它當時其實也只是構建在 LXC 之上的一個工具,屏蔽掉了 LXC 的使用細節,讓用戶能夠一句 docker run
便建立出本身的容器環境。 同時,它容許用戶將容器環境打包成爲一個 Docker 鏡像進行分發,這也大大下降了用戶使用的門檻。 Docker 鏡像分發能夠說是 Docker 成功的一個關鍵要素了。
2014 年 Docker 發佈 1.0 正式進入生產就緒的狀態。在此以前它也將 LXC 逐步從它的底層移除,換成了本身實現的 libcontainer
,幸運的是我也在 0.9 版本時開始了個人 Docker 之路。
此後 Docker 便成爲了風靡技術界的新熱潮。
瞭解了 Docker 的發展背景後,咱們來看看前面咱們提到的問題:爲何要用 Docker 呢?
2008 年到 2013 年這之間大約 5 年左右的時間,以 LXC 爲首的容器技術並無獲得相似 Docker 出現後那麼普遍的普及,我在以前的線下演講中也提到過這個點,最主要的緣由在於 LXC 太偏向技術了,使用 LXC 有必定的門檻,致使了好多人的退卻。
Docker 則提供了簡單易用的 CLI , 優雅靈活的容器生命週期管理,以及鏡像的構建,分發等配套設施,這爲後期的推動提供了不少的便利。
再加上 Docker 的策略很好,以及在 Docker 公司內部也有大量的實踐經驗,因此這些事情作起來也都很順暢了。(關於 Docker 公司的實踐經驗能夠說是信息量巨大了,之後再看機會分享吧)
前面分別聊了容器的發展歷程,以及 Docker 的發展歷程。咱們要正視 Docker 是一個已經 7 歲的項目,自它 1.0 發佈以來已經 6 年之久了,它在生產環境中已經獲得了大量的實踐和驗證。固然不可避免的它也會存在一些歷史遺留問題或者是軟件 bug 。
尤爲是隨着 K8S 的發展,國內外大量公司都在落地 K8S。其中有超過半數的公司都是使用 Docker 做爲容器運行的。
當發生故障或者異常時,有些人可能會一籌莫展,或是隻從上層進行問題的排查,卻不知問題極可能發生在底層的容器運行時。比較典型的例子:好比說容器 Hang 住,或者是沒法建立容器等。
或者有人因爲對 Docker 網絡相關的方面不瞭解,因此在學習和使用 K8S 時,也會走很多的彎路。比較典型的例子:不理解 K8S 中數據包的流向。
最後一個很重要的方面就是安全相關的了。在企業中使用 Docker 尤爲須要注意。這裏包含着不少信息:好比容器運行時安全,鏡像安全,容器策略安全等,我在專欄的安全篇也都進行了介紹。
Docker 的上手使用很是簡單,這是 Docker 的一大優點,旨在下降開發者的使用門檻。尤爲是 Docker Desktop 提供了交互式 UI,用戶能夠經過鼠標點擊就完成容器的相關操做和管理。
可是 Docker 做爲一個正在被大量使用,且日後會被應用更多的技術,若是要用在生產環境中,我建議讀者去更深刻的學習和掌握它。
以便在遇到問題時,能夠更快速的定位和解決;構建鏡像時,能夠更加高效;使用 Docker 時,能爲企業提供更適宜的安全策略。
固然,還有一個最重要的,當你對 Docker 的瞭解越深刻時,你也會越開心,你能接觸到更多有趣的知識和技能。
推薦我耗時6個月完成的專欄: 《Docker 核心知識必知必會》, 從容器,鏡像,Docker 架構,安全,網絡等方面深刻介紹 Docker 核心知識及應用實踐。