1. 程式人生 > >01 . Docker原理部署及常用操作命令

01 . Docker原理部署及常用操作命令

#### Docker的來源及構造: > 容器是一種基礎工具:泛指任何用於容納其他物品的工具,可以部分或完全封閉,被用於容納,儲存,運輸物品: 物品可以被放置在容器中,而容器可以保護內容物: > > > > 人類使用容器的歷史有十萬年,甚至可能有數百萬年的歷史: ```shell # 容器型別: # 瓶,罐,箱,籃,桶,袋 ``` > 但我們重點在LXC這裡; > > 虛擬化和容器的關係: > > 主機級虛擬化:虛擬化的是整個完整的物理硬體平臺,比如VMware,KVM.裡面跑的是與宿主機不一樣的系統 > > > > 兩種型別: > 型別一: 直接在硬體上裝一個虛擬機器管理器,不裝宿主機,在虛擬機器管理器上跑需要的作業系統: > 型別二: Vmware,kvm > **完整意義的作業系統:** 我們自己在上面裝一個核心,核心上面有一個使用者空間,使用者空間裡面跑程序。 > > > > 但是執行核心並不是我們主要目的,核心核心作用在於資源分配和管理。 > > > > 就是說真正在應用空間跑的應用程式才是能產生生產力的,就好比我們跑一個web服務,核心其實很少提供服務。 > > 而出於通用目的的而設計的資源管理平臺,作業系統管理核心。我們並不太需要: > > > > 但是核心也不得不有,原因在於要想在一組硬體平臺跑軟體程式,而現在的軟體都是針對於核心呼叫和系統呼叫,庫呼叫研發的,而不安裝這些沒法執行程式,所以核心看上去沒多大用,但是也必不可少; > > 更何況,我們一旦有了使用者空間後,裡面執行很多程序,這些程序為了協調也需要核心。 > > > > 但假設我們建立一個虛擬機器,只為了執行一個nginx,tomcat,卻不得不去安裝核心,使用者空間,再去跑tomcat,代價就有點大了。 > > > > 而在這裡如果我們用二級虛擬化來分析的話: > > 如果為了執行一個程序,就要實現兩級排程和資源分派。 > > 第一,自己虛擬機器有一個核心,已經實現了一次記憶體虛擬化,cpu排程以及IO排程和io 管理。 > > 但是真正虛擬機器也是被宿主機核心管理一次,這個中間浪費可以想一下。 > > 傳統的主機虛擬化技術,確實能讓我們一組硬體平臺之上實現跨系統傳進的隔離和試驗,除錯,資源高效利用。而帶來的資源開銷不容忽視的,而很多時候,我們建立虛擬機器只是為了執行一個和幾個附有生產責任的程序而已,為此; > > > > 減少中間層就是一個好的辦法, > > > > 比如在二級虛擬化我們把虛擬機器那一層給抽掉,只保留程序,但是我們加虛擬機器就是為了環境隔離,比如說一臺機器跑兩個或者五個nginx,我們一臺機器沒有那麼多套接字和80埠。 > > > > 而我們虛擬機器就是為了資源隔離,就算把裡面的nginx翻江倒海,壞的也是哪一個虛擬機器。 > > > > 所以這裡的隔離才是我們的目標,我們雖然想抽掉這一層核心,但是不能讓他回到在一個鍋裡吃飯那個環境,所以要讓每一個組程序互相不可干擾的,只不過共享一個底層資源而已。 > > > > 因此我們想要的環境就是: > > 建立一個又一個隔離環境,我們要執行隔離的程式就讓他跑在隔離環境中,核心提供的是核心空間,程序試執行在使用者空間,而這裡就能實現將使用者空間隔離成多個使用者空間,互不干擾,這裡一般都有一個空間是有特權的,一般第一個。而這裡眾多使用者空間執行的都是同一個核心,被同一個核心管理的。但是執行時候能看到的邊界只能是當前使用者空間的邊界,雖然沒有主機級虛擬化隔離那麼徹底。 > > 更重要是,這個使用者空間放程序,給程序提供執行環境,並且保護內部程序不被其他程序所幹擾.叫做容器. > > > > 虛擬的隔離環境管理器 > > 一層硬體平臺容器不是什麼新概念,最早出現在2000年,當年叫jail,監獄,監禁之意,裡面就像一個沙箱,就算裡面那個程式bug,有了故障,異常行為,也無法影響這個容器之外的程式執行,這也是jail初衷。 > > > > 容器並非起源於 Linux,但開源世界的最精彩之處就在於借鑑、修改和改進,容器也不例外. > > 但這個概念非常有吸引力。 > > > > 2001 年,通過 Jacques Gélinas 的 [VServer 專案,]隔離環境的實施進入了 Linux 領域。正如 Gélinas 所說,這項工作的目的是“**在高度獨立且安全的單一環境中執行多個通用 Linux 伺服器 [sic]。” 在完成了這項針對 Linux 中多個受控制使用者空間的基礎性工作後,Linux 容器開始逐漸成形並最終發展成了現在的模樣。** > > > > 一般來講,就算一個程式被遠端劫持在裡面搞起破壞,他的邊界也僅僅是哪個容器的邊界,最初只是為了應用的安全執行。 > > > > 後來有人把這個技術山寨到Linux平臺上,叫Vserver(chroot),在一個子目錄定義一個根,讓裡面的程式以為他就是根。 > > chroot他能實現的你看上去的那層空間,底層還是同一個核心,程序執行是特權模式還是普通模式,表面很簡單,真正實現起來涉及東西至少要有; > > > > 在一個單獨使用者空間當中,它主要目的是隔離使用,要讓裡面程序以為他就執行在底層核心之上,有六點: ```shell # 1. 主機名和域名: UTS # 2. 根檔案系統 : MOUNT # 3. 應該有自己的IPC,程序間的專用通道,如果能讓兩個使用者空間程序能互相通訊,也沒有意義了,共享記憶體,拒絕跨使用者空間。 IPC # 4. 給每個使用者空間做一個假的init,只能有一個,pid,1,每個系統正常來說只有一個,為了隔離,給每個空間偽裝一個。 # 5. 必須給每個使用者偽裝一個root,實現效果就是在真正系統只是普通使用者,在他所屬使用者空間裡面可以為所欲為,讓程序看起來是root. # 6. IP地址,互相通訊,而在核心級別,tcp協議棧只有一個, ``` > 為了容器機制的實現,Linux核心對著六種明成空間原生支援,namespaces。直接通過系統呼叫對外進行輸出, > > > > 直到今天,所謂的容器就是靠六個namespaces和chroot來實現的。 > > > > 聽上去容易,這六種名成空間有一個是到核心3.8才實現的,所以要想完美使用容器,要核心版本3.8以上。centos6天然排除在外. | namespace | 系統呼叫引數 | 隔離內容 | 核心版本 | | --------- | ------------- | -------------------------- | -------- | | UTS | clone_newuts | 主機名和域名 | 2.6.19 | | IPC | clone_newipc | 訊號量、訊息佇列和共享記憶體 | 2.6.19 | | PID | clone_newpid | 程序編號 | 2.6.24 | | Network | clon_newnet | 網路裝置、網路棧、埠等 | 2..29 | | Mount | clone_newns | 掛載點(檔案系統) | 2.4.19 | | User | clone_newuser | 使用者和使用者組 | 3.8 | **容器級虛擬化:** > 資源限制: 我們可以在整體資源做比例性分配,也可以在單一使用者空間做核心繫結,你只能使用幾個核;不然的話一個程序記憶體洩露整個系統都蹦了;或者一個程序佔用了絕大部分CPU. > > > > 記憶體是壓縮不了的,多一點都不能,一給就收不回來了,因為記憶體是壓縮不了的; > > > > 實現這個Control Cgroups ```shell blkio: # 塊裝置IO cpu: # CPU cpuacct: # CPU資源使用報告 cpuset: # 多處理器平臺上的cpu集合 devices: # 裝置訪問 freezer: # 掛起或恢復任務 memory: # 記憶體用量及報告 perf_event: # 對cgroup中的任務進行統一效能測試 net_cls: # cgroup中的任務建立的資料報文的類別; ``` > 劃分成組後,在不同組內進行資源分配,組還可以再分。 > > > > 即使有了chroot,nameespaces, control cgroups,容器的隔離能力比起主機級別虛擬化依然差很多,因為容器畢竟屬於同一個核心,而主機級虛擬化那麼好,因為核心本來就是天然的隔離,因此為了加強安全性,防止裡面的程序通過漏洞繞過邊界伸到別的使用者空間,通過selinux各種安全機制加強容器的邊界,為了支撐容器做的更加完善。 > > > > 容器實現就是靠chroot,nameespaces,cgroups核心技術實現,而這些在核心已經實現了。 > > > > 容器本來是靠jail啟發有了vserver,後來為了讓這種容器更加易用, > > > > 因為建立容器要自己寫程式碼,系統呼叫,克隆等來實現的,但麻煩程度很大。 > > > > 所以我們最好把實現容器的這種功能做成一種工具,極大的簡化容器的使用,於是就有了LXC的解決方案, > > > > LXCx container,最早一批真正把完整的容器技術用一組簡易使用的工具和摸板來極大的簡化了容器的使用方案。 > > > > lxc-create : 建立一個容器,template,摸板一組指令碼, > > > > 建立一個明成空間後,這指令碼自動執行後,自動實現安裝過程,指向了你所打算建立哪一類明成空間的系統發行版所屬的倉庫,從倉庫中映象下載過來,安裝,生成這個新的名成空間,就可以像虛擬機器一樣使用。 > > > > 所有的明成空間都這樣實現,而lxc就靠這樣這一組工具包快速實現建立空間,利用模板完成內部所需要各種檔案的安裝,同時還有些工具可以通過chroot切換來切換去,於是我們就可以愉快的使用了,跟虛擬機器沒多大區別; > > > > lxc在容器技術推廣絕對功不可沒,但依然有很高門檻: ```shell # 1. 學好lxc命令 # 2. 必要時候需要定製模板, # 3. 如果在裡面執行Mysql,如果服務在裡面出現故障,如何遷移資料; # 4. 批量建立也不容易 ``` > 雖然極大的簡化了容器使用,但是在複雜程度沒有多大降低的,更何況他的隔離性沒有虛擬機器那麼好, > > > > 好處是效能資源方面的節約,讓每一個程序直接使用宿主機的效能。 > > > > 於是後來出現了docker: > > lxc增強版,也不是容器,容器的一個易用前端工具。 > > > > lxc批量建立挺難,docker就在這個上面找突破點,docker早期核心就是lxc,他是lxc的二次封裝發行版。 > > > > 功能上利用lxc做容器管理引擎,建立容器時不再使用模板安裝,而是使用一種映象技術, > > > > 我們嘗試著把一個作業系統使用者空間所需要用到的所需要所有元件事先準備編排好,編排好後整體打包成一個檔案,叫做映象檔案,這個映象檔案是放在一個集中統一的倉庫中的,我把大家都會用到的最小化centos,ubuntu分別放在倉庫內; > > > > 而在這裡我們在這個最小化系統裡面先裝好原始碼nginx,再打包成一個映象,也放入這個倉庫中,當啟動建立容器時候,需用映象,把映象拖到本地,基於映象啟動容器。所以docker極大的簡化了容器使用程度; > > 比如說你想跑一個tomcat,nginx直接docker run就完成了 > > 為了易於管理,docker還採用另外一種方式,在一個使用者空間當中,我們嘗試只執行一組程序或一個程序,我們目的就是為了執行一個有生產力的程式,比如我們在主機上要跑tomcat,nginx,nginx執行在nginx容器中,tomcat執行在tomcat容器中,二者用容器間通訊邏輯進行通訊,所以以後一個容器只執行一個程序,這也是docker的目的; > > > > lxc就是當虛擬機器使用,到時候管理極為不便; > > > > 而docker這裡實現功能: > > 不用容器,一個核心之上執行各種程序 ,大家屬於同一個使用者空間,共享同一組主機名,使用者,ipc,pid,在同一個可視範圍內,如果一個黑客劫持一個程序以他做跳板可以威脅到其他程序 > > > > 而docker,把他們給圈起來了,彼此間不可見,而且這個容器只為了這一個程序,最小化定義的; > 壞處,佔用更多空間,如果服務壞了,除錯工具只針對一個容器有效,而如果加上除錯工具違反了唯一程序目的, > > > > 所以帶來問題:本來除錯一個程序極為簡單,可能沒有工具: > > > > 而要想除錯得突破他的邊界: > > 好處;刪除了不影響別人: > > 給運維帶來極大不便利,給開發帶來巨大好處,分發容易了,一次編寫到處執行。現在環境都是異構的, > > centos5,6,7/Ubuntu/deepin/suse/kali/redhat/AIX > > > > 要開發一個軟體,要開發每一種平臺的軟體,各自組織一個團隊開發,只需要打包一個映象,不管你是windows,linux,unix跟核心沒關係,跟作業系統沒關係,他有自己的明成空間 > > > > 跟java類似這種效果,但是java有很多版本,6,7,8 > > 以前部署需要釋出,變更,故障處理 > > 有了映象,直接one,就行了,但是還需要接路由器和排程器,如果有一個容器編排工具,之間run結束,甚至連run都不需要你手動執行; > > > > 像以前的java容器只能支援java一種語言; > > 而docker不會管你是什麼語言; > > 隨之帶來運維的問題,釋出操作用編排工具來實現,docker必須要使用編排工具來管理,不用的話手動管理比我們直接管理應用程式更麻煩,增加我們運維環境複雜度;但確實是降低開發壓力; > > > > 運維工作就是維穩,以往除錯很容易,而容器可能沒有除錯工具,這麼一來就意味著,我以後做映象需要為每一個映象自帶除錯工具,這麼一來意味著做映象需要自帶一些工具,以前能共享的,現在不能,但他們卻是隔離的; > > > > docker還有一個好處: 批量建立,他建立容器採用 > > 分層構建,聯合掛載;使得我們以後映象分發沒有那麼龐大,比如說我在一個系統上執行三個容器,都是基於底層centos構建,在這裡只用一個centos基礎映象,三個上層nginx,tomcat,mariadb,底層映象是共享的,底層映象是隻讀的,要想改,在每一層聯合掛載映象棧最頂層額外附加一個新層,這個層才是能讀能寫的; > > > > 遷移很困難,如果要把容器遷移到其他地方,但這裡是有狀態的,真正在使用容器時,不會在本地儲存有效資料,他會在檔案系統上共享儲存,而使用者儲存資料,這個服務不小心宕掉了,再找一個主機重新資料載入就行了,再訪問資料還在,所以資料脫離宿主機,脫離容器而持久,容器可以丟到任何主機上; > > > > 儲存需要一個外接永續性儲存,程式是由指令+資料組成, > > 把容器當程序使用,啟動一個容器,就是為了執行一個程序,程序一終止,把容器刪了,不要了,下次重新建立,重新掛載 > > 容器不需要持久,容器和程序一樣有生命週期,從建立而開始,從停止到結束,跟我們主機都沒多大關係,可以執行在任何主機上; > > > > 在docker之上建設一個層次,看那個主機更閒,來個排程法則,如果需要持久資料,給個web儲存空間,掛載上去儲存資料,一旦任務結束直接容器一刪,結束,這個元件能幫我們把要啟動容器排程於整個底層叢集環境中某一個docker主機之上,當要構建lnmp,誰先啟動,docker就解決不來這種功能,我們需要一個docker基礎之上,能夠把這種應用程式依賴關係,從屬關係,隸屬關係反映在啟動關閉時的次序和管理邏輯中,這種功能叫容器編排工具; ```shell # 在docker之後出現了各種編排工具, machine+swarm+compose: # compose只能單機編排, mesos: # 統一資源排程和分配的 + 實現編排需要加上 marathon kubernetes -- > k8s ``` > google使用容器有十幾年歷史了,據說每一週銷燬和新建容器多達幾十億。 > > > > docker因緣巧合摸到了這個門道,並且做成開源軟體,谷歌就坐不住了,自己本來做為獨門武器,那小子居然找到一種辦法還公開所有人使用,本來我最有話語權,祕而不宣藏起來,這樣才能稱為獨門武器,必要時候必殺技; > > > > 但是docker已然獨霸話語權了,好在docker也不是鐵板一塊,後來出來出來另一個公司,谷歌就在後面大力扶植反對派,後來發現難以跟docker抗衡,上不來臺面; > > 容器編排工具在谷歌已經跑了十幾年了,該踩的坑都踩的差不多了; > > > > docker三兩年做不到; > > 於是kubemetes橫空出世,佔據了百分八十的市場,成為市場的標準,還成立了CNCF 容器標準組織,docker有一個問題,docker在編排上沒有經驗,義無建樹,技術沒走好,沒有吸引更多的土豪進來投資,上市做獨角獸,雖然網際網路上火的不要不要的,但是無法變現,決定,把開源版拆分為企業版和社群版,將社群改名,把docker流量引入企業版,後來改名叫moby,把所有docker引入企業版, > > > > 之所以這樣 docker是因為一家商業公司,谷歌在做k8s時候向大家表明我是沒有任何意圖的,於是把k8s原始碼捐給了cncf,cncf是由很多組織聯合成立的,主導權不再屬於谷歌,屬於IMB,微軟等等,不會被人說想把k8s私有化,一年四個版本釋出,go研發的 > > > > 後來docker研發了一個容器引擎,libcontainer,替換了lxc,docker已經被cncf挾持了,cncf自己玩,把docker排除在外,如果以後容器要走下去肯定要開源,標準化,誰來負責標準化,cncf就可以做,定義標準,但這樣太欺負docker了,所以給docker個機會,你來定標準化,同時做一款開源軟體。 > > > > > > 現在新的docker引擎已經是runC了, > > 雖然k8s支援很多種容器,但常見的還是docker+k8s; #### 映象管理基礎 > docker架構形式:只考慮單機之上,整體架構是這樣的: > > > > 整體是一個dockerhosts: docker server端 > > > > dockerhost 就是執行有docker程序(守護程序)的主機,被稱為docerhost,dockerserver; > > > > docker接收到建立和啟動容器命令後,將在本地建立容器,一個dockerhost上可以啟動多個容器,此處我們可能執行的分別不屬於各種不同程式的容器,而容器取決於映象來實現,如果本地沒有,dockerdaemon自動連結到registries上,而後從中獲得映象後,先儲存到本地一個能夠專門儲存所謂映象儲存空間中,要求得是特殊並且特殊的檔案系統,overlay2, > > ![1](/Users/youmen/Documents/blog/z/docker/1.png) > > 這裡面可能有多個映象檔案,映象本身是隻讀的,而且映象在registries放的時候倉庫名就是應用程式名,而後倉庫內可以放多個映象,而且這些映象通常屬於同一個應用程式不同版本,我們用標籤來識別; **docker映象:** 含有啟動容器所需要的檔案系統及其內容,因此,其用於建立並啟動docker容器: 採用分層構建機制,大體分為兩層,最底層為bootfs,其之為rootfs 真正讓使用者拿來去構建使用者空間並執行程序容器的是rootfs; bootfs: 用於系統引導的檔案系統,包括bootloader和kernel ,容器啟動完成後會被解除安裝以節約記憶體資源 這裡的kernel僅僅用於引導並啟動一個使用者空間,啟動完之後就沒有了以節約記憶體資源,畢竟很有可能我們使用者空間跟底層核心還是有一點不同之處的,向上就是rootfs了; ![2](/Users/youmen/Documents/blog/z/docker/2.png) ```shell # rootfs: 位於bootfs之上,表現為docker容器的根檔案系統; # rootfs: 位於bootfs之上,表現為docker容器的根檔案系統 ``` > 傳統模式中,系統啟動時,核心掛載bootfs時會首先將其掛載為只讀模式,完整性自檢完成後將其重新掛載為讀寫模式; > > > > docker中,rootfs由核心掛載為“只讀”模式,而後通過“聯合掛載技術”額外掛載一個“可寫層”; `這裡的分層構建` > 我們做一個apache映象,執行httpd映象,我們很有可能在一個底層的非常基礎系統映象之上一個純淨最小化centos版本,在他之上新增一個編輯器,相當於vim,除此之外新增一個httpd,每新增一個軟體都是一個獨立的層次,這裡是三層,底下bootfs那一層在容器啟動時,一旦被引導了完了rootfs時候再解除安裝並移除,不是刪除檔案,而是從記憶體中移除; > > > > 而這時候底層只有三層,base image 用來構建一個系統基本組成,/bin;如果要用到額外一個工具,需要額外安裝; > > 但是對於我們映象來講,底層映象是不會動的,額外安裝一個vim 他會在裡面額外生成一個vim 層,再裝個nginx,生成個nginx層;疊加在一起掛載的,這三層都是隻讀的,因此,對於一個容器來講,僅能在writoble上能寫,而且如果刪除容器,writable也會被刪除; > > > > 含有啟動容器所需要的檔案系統及其內容,因此,其用於建立並啟動docker容器: > > 採用分層構建機制,大體分為兩層,最底層為bootfs,其之為rootfs > > 真正讓使用者拿來去構建使用者空間並執行程序容器的是rootfs; > > bootfs: 用於系統引導的檔案系統,包括bootloader和kernel ,容器啟動完成後會被解除安裝以節約記憶體資源 > > 這裡的kernel僅僅用於引導並啟動一個使用者空間,啟動完之後就沒有了以節約記憶體資源,畢竟很有可能我們使用者空間跟底層核心還是有一點不同之處的,向上就是rootfs了; > > > > 映象分層構建和聯合掛載依賴於檔案系統的支撐 > > 早起用到的是Aufs,高階多層統一檔案系統: > > 最早被docker拿來用於實現聯合掛載的Linux檔案系統, > > aufs是之前的unionfs重新實現,重寫後依然很爛,三萬行程式碼,一個ext4才四五千程式碼,這是要被整合進核心的,因此被申請時,次次被拒絕,一直到不申請,aufs一直都不是核心中自有的檔案系統,想用需要向核心打補丁,centos不會幹這種事情,因為他們以保守穩定為初衷,ubuntu是很早一批把aufs打包進核心,早些時候想要使用docker需使用ubuntu. > > aufs的競爭產品是overlayfs(),後者自從3.18版本才開始被合併到linux核心; > > docker的分層映象,除了aufs,docker還支援btrfs,devicemapper和vsf等; > > docker預設是aufs; centos7用的是devicemapper;在試用聯合掛載很差,不穩定,因為它使用target driver; > > 比較成熟的支援的檔案系統必須要能夠是docker info當中的overlay2,xfs,overlay2是一種抽象的二級檔案系統,他需要建立在本地檔案系統之上; > > 構建映象時,映象做好之後,應該有一個統一儲存的位置,叫做doceker registry > > 啟動容器時,docker daemon會試圖從本地獲取相關的映象: 本地映象不存在時,將其從registry中下載該映象並儲存到本地; > > 如果我們沒有特別指定,那麼他就是registry,如果要指向別的registry我們必須在映象的訪問路徑當中給明伺服器地址;否則訪問預設的registry,除非我們修改預設; > ![3](/Users/youmen/Documents/blog/z/docker/3.png) ##### Docker Registry分類 ```shell # Registry用於儲存docker映象,包括映象的層次結構和元資料 # 使用者可自建Registry,也可以使用官方的docker hub 分類: # Sponsor Registry 第三方的registry,供客戶和docker社群使用 # Mirror Registry 第三方的registry,只讓客戶使用 # Vendor Registry 由釋出docker 映象的供應商提供的registry提供給買了紅帽系統使用 # Private Registry # 通過設有防火牆和額外的安全層的私有實體提供的registry 不消耗網際網路頻寬,尤其是本地大規模容器自建本地registry; ``` `OCI` > 由linux基金會於2015年6月創立 > 旨在圍繞容器格式和執行時制定一個開放的工業化標準 > RunC: 無論是客戶端還是服務端,都由docker一個程式提供,他有很多子程式,他可以監聽在一個套件字之上; > docker有三種類型套接字, > docker啟動容器就是基於映象啟動,在映象基礎之上,為一個容器建立一個專用可寫層; > containers:容器, > > > > lmages: 映象  映象來自於Registry,登錄檔,可以稱為docker的映象倉庫,預設就是docker hub,預設本地是沒有的,映象是分層構建的,所以下載到本地後,可以共享多個上層映象使用,因為映象是隻讀的,所以啟動容器就是基於映象來啟動,在映象基礎上為一個容器建立一個專用的可寫層,從而來啟動這個容器。 > > > > 所以這裡映象也需要在docker本地儲存,因此這有專門的倉庫來放映象,而映象擁有幾十萬之多,所以放到一個公共的倉庫,需要時候拉取過來載入到本地,這裡的協議是http/https,預設是加密的,需要明確定義成不安全才可以使用; > > > > docker的執行過程中尤其是建立容器時可能有一點慢,原因是他要下載一次映象,取決於他的寬頻; > > > > 因為伺服器在國外, 為了能使加速訪問,docker在大陸這邊做了一個docker映象伺服器,docker.cn,加速不太好,可以使用阿里,科大,所以要想使用docker,必須要能接入到網際網路。 > > > > docker映象是分層構建的 > > > > 倉庫:  一個docker擁有兩重功能,第一,他提供映象提供的倉庫,第二,他還提供使用者來獲取映象時的認證等功能,還提供了當前伺服器所有可用映象的索引; > > > > 所以映象也會有應用到不同程式版本的映象,為了讓映象跟應用程式版本有一定的關聯,給映象外面加了一個標籤,倉庫名+標籤才能唯一標識一個映象。如果只給了倉庫名,那就是預設最新版;一個映象可以有多個標籤,在倉庫名+標籤外面加上 stable最新版,穩定版什麼的; > > > > 映象是靜態的; ```shell # 容器: 動態,生命週期,類似於程式; #任何images,networks,volumes,plugins可以支援增刪改查的,因為他們都是物件; # 依賴的基礎環境: # 64 bits CPU # Linux KERNEL 3.10+  CentOS6也支援docker,2.6.32,打了補丁; ``` > 如果我們要是 就使用docker,如果我們要使用在這的倉庫就下載docker,區別EE和CE; > > > > Docker 從 1.13 版本之後採用時間線的方式作為版本號,分為社群版 CE 和企業版 EE,社群版是免費提供給個人開發者和小型團體使用的,企業版會提供額外的收費服務,比如經過官方測試認證過的基礎設施、容器、外掛等。 > 社群版按照 stable 和 edge 兩種方式釋出,每個季度更新 stable 版本,如 17.06,17.09;每個月份更新 edge 版本,如17.09,17.10。 #### Docker的部署 ##### 初始化環境 ```shell init_security() { systemctl stop firewalld systemctl disable firewalld &>/dev/null setenforce 0 sed -i '/^SELINUX=/ s/enforcing/disabled/' /etc/selinux/config sed -i '/^GSSAPIAu/ s/yes/no/' /etc/ssh/sshd_config sed -i '/^#UseDNS/ {s/^#//;s/yes/no/}' /etc/ssh/sshd_config systemctl enable sshd crond &> /dev/null rpm -e postfix --nodeps echo -e "\033[32m [安全配置] ==> OK \033[0m" } init_security init_yumsource() { if [ ! -d /etc/yum.repos.d/backup ];then mkdir /etc/yum.repos.d/backup fi mv /etc/yum.repos.d/* /etc/yum.repos.d/backup 2>/dev/null if ! ping -c2 www.baidu.com &>/dev/null then echo "您無法上外網,不能配置yum源" exit fi curl -o /etc/yum.repos.d/163.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo &>/dev/null curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo &>/dev/null yum clean all timedatectl set-timezone Asia/Shanghai echo "nameserver 114.114.114.114" > /etc/resolv.conf echo "nameserver 8.8.8.8" >> /etc/resolv.conf chattr +i /etc/resolv.conf echo -e "\033[32m [YUM Source] ==> OK \033[0m" } init_yumsource ``` ##### 安裝必要系統工具和軟體源 ```shell # 安裝一些必要的系統工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 新增軟體源資訊 # docker 官方源 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 阿里雲源 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo ``` ##### 安裝並啟動Docker-ce ```shell # 安裝前可以先更新 yum 快取: sudo yum makecache fast # CentOS7安裝 Docker-ce yum -y install docker-ce # CentOS 中安裝 apt-get install docker-ce # Ubuntu 中安裝 pacman -S docker # Arch 中安裝 emerge --ask docker # Gentoo 中安裝 # 如果想安裝特定版本的Docker-ce版本,先列出repo中可用版本,然後選擇安裝 yum list docker-ce --showduplicates |sort -r Loading mirror speeds from cached hostfile Loaded plugins: fastestmirror Installed Packages docker-ce.x86_64 3:19.03.4-3.el7 docker-ce-stable docker-ce.x86_64 3:19.03.4-3.el7 @docker-ce-stable docker-ce.x86_64 3:19.03.3-3.el7 docker-ce-stable docker-ce.x86_64 3:19.03.2-3.el7 docker-ce-stable docker-ce.x86_64 3:19.03.1-3.el7 docker-ce-stable yum install docker-ce- # 選擇安裝 docker-ce-18.06.1.ce yum install docker-ce-18.06.1.ce # Docker映象加速 # 沒有啟動/etc/docker目錄不存在,需要自己建立,docker啟動也會自己建立 # 為了期望我們的映象下載快一點,應該定義一個映象加速器,加速器在國內 mkdir /etc/docker vim /etc/docker/daemon.json { "registry-mirrors": ["https://registry.docker-cn.com"] } # 啟動Docker後臺服務 systemctl start docker && systemctl enable docker systemctl daemon-reload # 守護程序重啟 # 通過執行hello-world映象,驗證是否正確安裝了docker,或者通過檢視版本 docker run hello-world docker version Client: Docker Engine - Community Version: 19.03.4 API version: 1.40 Go version: go1.12.10 Git commit: 9013bf583a Built: Fri Oct 18 15:52:22 2019 OS/Arch: linux/amd64 Experimental: false ``` #### CentOS/RHEL的使用者需要注意的事項 > 在 Ubuntu/Debian 上有 UnionFS 可以使用,如 aufs 或者 overlay2 ,而 CentOS 和 RHEL 的核心中沒有相關驅動。因此對於這類系統,一般使用 devicemapper 驅動利用 LVM 的一些 機制來模擬分層儲存。這樣的做法除了效能比較差外,穩定性一般也不好,而且配置相對復 雜。Docker 安裝在 CentOS/RHEL 上後,會預設選擇 devicemapper ,但是為了簡化配置, 其 devicemapper 是跑在一個稀疏檔案模擬的塊裝置上,也被稱為 loop-lvm 。這樣的選擇是 因為不需要額外配置就可以執行 Docker,這是自動配置唯一能做到的事情。但是 loop-lvm 的做法非常不好,其穩定性、效能更差,無論是日誌還是 docker info 中都會看到警告信 息。官方文件有明確的文章講解了如何配置塊裝置給 devicemapper 驅動做儲存層的做法,這 類做法也被稱為配置 direct-lvm 。 > > > > 除了前面說到的問題外, devicemapper + loop-lvm 還有一個缺陷,因為它是稀疏檔案,所 以它會不斷增長。使用者在使用過程中會注意到 /var/lib/docker/devicemapper/devicemapper/data 不斷增長,而且無法控制。很多人會希望刪 除映象或者可以解決這個問題,結果發現效果並不明顯。原因就是這個稀疏檔案的空間釋放 後基本不進行垃圾回收的問題。因此往往會出現即使刪除了檔案內容,空間卻無法回收,隨 著使用這個稀疏檔案一直在不斷增長。 所以對於 CentOS/RHEL 的使用者來說,在沒有辦法使用 UnionFS 的情況下,一定要配置 direct-lvm 給 devicemapper ,無論是為了效能、穩定性還是空間利用率。 或許有人注意到了 CentOS 7 中存在被 backports 回來的 overlay 驅動,不過 CentOS 裡的 這個驅動達不到生產環境使用的穩定程度,所以不推薦使用。 #### 標準化開發測試和生產環境 > 對於大部分企業來說,搭建PaaS既沒有那個精力,也沒那個必要,用Docker做個人的sandbox用處又小了點。可以用Docker來標準化開發、測試、生產環境。 > > > > Docker佔用資源小,在一臺E5128G記憶體的伺服器上部署100個容器都綽綽有餘,可以單獨抽一個容器或者直接在宿主物理主機上部署samba,利用samba的home分享方案將每個使用者的home目錄對映到開發中心和測試部門的Windows機器上。 > > > > 針對某個專案組,由架構師搭建好一個標準的容器環境供專案組和測試部門使用,每個開發工程師可以擁有自己單獨的容器,通過 docker run -v 將使用者的home 目錄對映到容器中。需要提交測試時,只需要將程式碼移交給測試部門,然後分配一個容器使用 -v載入測試部門的 home目錄啟動即可。這樣,在公司內部的開發、測試基本就統一了,不會出現開發部門提交的程式碼,測試部門部署不了的問題。 > > > > 測試部門釋出測試通過的報告後,架構師再次檢測容器環境,就可以直接交由部署工程師將程式碼和容器分別部署到生產環境中了。這種方式的部署橫向效能的擴充套件性也極好。 #### Docker映象使用 #### 管理命令 ```shell Docker --help container 管理容器 image 管理映象 network 管理網路 命令: attach 介入到一個正在執行的容器 build 根據 Dockerfile 構建一個映象 commit 根據容器的更改建立一個新的映象 cp 在本地檔案系統與容器中複製 檔案/資料夾 create 建立一個新容器 exec 在容器中執行一條命令 images 列出映象 kill 殺死一個或多個正在執行的容器 logs 取得容器的日誌 pause 暫停一個或多個容器的所有程序 ps 列出所有容器 pull 拉取一個映象或倉庫到 registry push 推送一個映象或倉庫到 registry rename 重新命名一個容器 restart 重新啟動一個或多個容器 rm 刪除一個或多個容器 rmi 刪除一個或多個映象 run 在一個新的容器中執行一條命令 search 在 Docker Hub 中搜索映象 start 啟動一個或多個已經停止執行的容器 stats 顯示一個容器的實時資源佔用 stop 停止一個或多個正在執行的容器 tag 為映象建立一個新的標籤 top 顯示一個容器內的所有程序 unpause 恢復一個或多個容器內所有被暫停的程序 ``` #### Docker服務管理 ```shell service docker start # 啟動 docker 服務,守護程序 service docker stop # 停止 docker 服務 service docker status # 檢視 docker 服務狀態 chkconfig docker on # 設定為開機啟動 systemctl stop docker systemctl start docker systemctl enable docker systemctl daemon-reload # 守護程序重啟 ``` #### 映象管理 ##### 獲取映象 ```shell # 映象可以看做我們平時裝系統的映象,裡面就是一個執行環境 # Docker Hub上有大量的高質量映象可以用,這裡就說一下怎麼獲取這些映象, # 從映象倉庫獲取映象的命令是docker pull,其命令格式為: docker pull [選項] [Docker Registry 地址[:埠號]/]倉庫名[:標籤] docker search centos # 搜尋docker官方提供的centos映象 docker search centos --filter=stars=100 # 查詢stars數至少為100的映象 docker pull centos # 預設從官網拉取 docker pull centos:7.7.1908 # 預設拉取centos8,需要指定版本才能下載7. docker pull daocloud.io/library/centos # 從daocloud拉取 docker dao pull centos # 從daocloud拉取,國內倉庫 ``` `注意` > 我們使用docker image ls時候會發現,映象體積的所佔用空間在Docker Hub上看到的映象大小不同,比如nginx映象在docker hub官網上是50多兆,而把他pull下來就變成一百多兆了,這是因為docker hub所顯示大小是網路傳輸中更關心的流量大小,而docker image ls顯示的是映象下載到本地展開後的各層所佔空間的綜合,因為映象下載到本地後,更關心的是磁碟空間佔用的大小. > > > > 另一個需要注意問題是,docker image ls列表中的映象體積綜合並非是所有映象實際硬碟消耗,由於Docker映象是多層儲存結構,並且可以繼承、複用,因此不同映象可能因為使用相同的基礎映象,從而擁有共同的層,由於Docker使用UnionFS,相同的層只需要儲存一份即可,因此實際佔用硬碟空間很可能比這個列表映象大小的總和小的多. ##### 檢視映象 ```shell docker system df # 檢視映象、容器、資料卷所佔用的空間. docker images # 檢視已下載的映象 docker rm image_id # 刪除映象,指定映象id docker images # 檢視已下載的映象 docker images -q # 只檢視所有映象的id docker inspect imageID # 檢視映象詳情 ``` ##### 刪除映象 ```dockerfile docker rm image_id # 刪除映象,指定映象id docker rmi RepositoryName --force # 刪除映象,指定映象名,<倉庫名>:<標籤> --force映象在使用中強制刪除 # 如果映象正在被未執行的容器使用,則需要強制刪除,但是如果正在被執行的容器使用,則強制刪除也無法刪除 docker image ls -a # 這樣會看到很多無標籤的映象,這些無標籤映象很多都是中間層映象, # 是其他映象所需要依賴的映象,這些無標籤映象不應該刪除,否則會導致上層映象因為缺失依賴而出錯, # 實際上也沒必要刪除,因為相同的層只會存一遍,而這些映象是別的映象的依賴, # 因此並不會因為他們被列出來而多存了一份,無論如何你也會需要他們, # 刪除那些依賴他們的映象,這些中間層映象也會被連帶刪除. # 刪除所有倉庫名為redis的映象: docker image rm $(docker image ls -q redis) # 刪除所有映象 # none 預設為 docker.io docker rmi $(docker images | grep none | awk '{print $3}' | sort -r) docker rmi $(docker images -q) ``` #### 容器管理 > 啟動容器有兩種方式,一種基於映象新建一個容器並啟動,另外一個是將在終止狀態(stopped)的容器重新啟動. > > > > 因為docker的容器太輕量級了,很多時候使用者都是隨時刪除和重建. ##### 建立容器 ```shell # 容器就像是一個類的例項(比如一個基於CentOS7映象啟動的虛擬機器) # 連線進行進入命令列模式,exit命令退出。 docker run -t -i nginx:latest /bin/bash -i # 互動式操作,讓容器的標準輸入保持開啟. -t # 讓docker分配一個偽終端(pseudo-tty)並繫結到容器的標準輸入上 nginx:latest # 基於centos構建的nginx映象 /bin/bash # 放在映象後的命令,這裡我們希望有個互動式shell,因此用的是/bin/bash # 當我們基於映象啟動一個例項的時候,此時他就是容器了. # 就好比CentOS7.iso映象和已經運行了的CentOS7虛擬機器一樣. # 同一個映象可以啟動多個容器 # 建立執行容器且連線到容器 docker run -it --rm -d --cidfile="id.txt" centos /bin/bash -i # 捕獲標準輸入輸出,保持互動式的意思 -t # 分配一個終端或控制檯,每一個控制檯都要伴隨一個shell --rm # 退出時就刪除該容器,預設情況下,每個容器在退出時,他的檔案系統會儲存下來,這樣一方面有利於除錯, # 因為可以通過檢視日誌等方式來確定最終狀態;另一方面,也可以報錯容器所產生的資料, # 如果僅僅需要短暫的執行一個容器,且不需要儲存容器中的資料, # 就可以在exit容器時自動清理掉容器及產生的資料,但此選項不能與-d共用 /bin/bash # 容器執行起來之後執行的程式,也可以是任何的命令,/bin/echo hello --cidfile # 指定容器執行之後container長id的存放檔案位置 -d # 如果不使用-d引數執行容器,容器會把輸出的結果(STDOUT)列印到宿主機上面, # 如果使用了-d引數會返回一個id,也可以通過docker ps 檢視容器資訊. # 要獲取容器的輸出資訊,可以通過docker container logs命令檢視 # 容器能否長久執行是和docker run指定的命令有關,和-d引數無關. ``` `當利用docker run來建立容器時,Docker在後臺執行的標準操作包括:` ```shell # 1. 檢查本地是否有指定的映象,不存在就從公有倉庫下載 # 2. 利用映象建立並啟動一個容器 # 3. 分配一個檔案系統,並在只讀的映象層外面掛載一層可讀寫層. # 4. 從宿主主機配置的網橋介面中橋接一個虛擬介面道容器中去. # 5. 從地址池中配置一個ip地址給容器. # 6. 執行使用者指定的應用程式. # 7. 執行完畢後容器被終止. ``` ##### Docker容器服務管理 ```shell docker start my-nginx # 啟動一個已經存在的容器 docker restart my-nginx # 重啟容器 docker stop my-nginx # 停止執行一個容器 docker kill my-nginx # 殺死一個執行中的容器 docker rename my-nginx new-nginx # 重新命名容器 docker rm new-nginx # 刪除容器 docker stop $(docker ps -q) & docker rm $(docker ps -aq) # 停掉所有容器並刪除 docker container prune # 刪除所有處於終止狀態的容器. docker logs [containerID/Names] # 檢視日誌 docker logs my-nginx # 檢視 my-nginx 容器日誌 # 使用docker exec命令進入一個已經在執行的容器 docker exec -it [containerID/Names] /bin/bash # 進入容器 docker attach 7968b44369 # 會附加該容器的標準輸出到當前命令列 # 啟動狀態的容器,執行任務 # 通過exec命令可以建立兩種任務:後臺型任務和互動型任務 # 後臺型任務:docker exec -it test /bin/bash # 互動型任務:docker attach 7968 docker run centos echo "hello world" # 在docker容器中執行hello world! docker run centos yum install -y wget # 在docker容器中,安裝wget軟體 docker ps # 列出包括未執行的容器 docker ps -a # 檢視所有容器(包括正在執行和已停止的) docker ps -a -q # 檢視所有容器的ID docker ps -s # 檢視容器使用了多少記憶體 docker ps -qf status=running # 檢視某種狀態的容器ID docker ps -l # 列出最近一次啟動的容器 docker inspect 7657b3785bcf # 檢視容器詳細配置資訊,包含容器名,環境變數,執行命令,主機配置,網路配置,資料卷配置等,json格式; docker inspect -f {{.State.Pid}} 44fc0f0582d9 # 獲取id為 44fc0f0582d9 的PID程序編號 docker inspect --format '{{.Config.Image}}' 7657b3485 # 獲取當前執行映象版本 docker inspect --format='{{.NetworkSettings.IPAddress}}' 7657b3485 # 獲取當前執行映象的IP地址 ``` ```shell # 列印該容器輸出 docker run -it -d --name test centos /bin/bash -c "while true; do echo hello world;sleep 2;done" docker logs test # 監控容器執行 docker logs container_id/container_name --tail: # 選項可以指定檢視最後幾條日誌 -t: # 選項則可以對日誌條目附加時間戳 -f: # 選項可以跟蹤日誌的輸出,直到手動停止 # 執行遠端機器上的容器 docker run -it -d -h 39.108.140.0 daocloud.io/centos:7 # 斷開容器 # 斷開與容器的連線,並且關閉容器 [root@7968b4436989 /]# exit [root@7968b4436989 /]# docker stop 7968b443 # 只斷開和容器的連線而不關閉容器 # 快捷鍵: ctrl+p+q # 關閉執行中的容器 # 如果此時有其他終端正在對他進行互動會自動中斷 # docker stop contrainer_id/name //傳送SIGTERM訊號,可忽略,15訊號 # docker kill contrainer_id/name //傳送SIGKILL訊號,9訊號 ``` ##### 容器的匯入匯出 ```shell # 匯出容器 # 映象打包 # 方案一: export # 利用export把正在執行的容器直接匯出為tar包的映象檔案,可以用-o或> docker run --name my-nginx -d -p 8080:80 some-centent-nginx:1.2 docker export my-nginx > youmen_nginx.tar && docker export -o youmen_nginx.tar my-nginx scp youmen_nginx.tar 120.77.248.31: docker import youmen_nginx.tar docker tag 121d8 mynginx:1 # 設定映象名字 docker import youmen_nginx.tar mynginx:1.1 # 匯入時即設定映象名字 方案二: 利用save直接把映象打包出來 docker save -o suibian.tar library/centos:latest scp suibian.tar 192.168.135.161: docker load < suibian.tar # 匯入之後使用原名 # 匯入也可以通過指定URL或者某個目錄來匯入 docker import http://example.com/exampleimage.tgz example/imagerepo ------------------------------------區別介紹------------------------------------- # docker save:將一個映象匯出為檔案,儲存的是該映象的所有歷史記錄; # docker export:將一個容器匯出為檔案,儲存的是容器當時的狀態,即容器快照; # docker load:將映象儲存檔案匯入到本地映象庫; # docker import:匯入一個容器快照到本地映象庫; docker save和docker export之間的區別: 1>
docker save是將映象儲存為tar包,且會儲存該映象的父層、標籤、所有歷史等資訊; docker export是將容器檔案系統儲存為tar包,僅僅儲存的是容器當時的狀態(快照); 2> docker save可以同時指定多個映象名稱;docker export只能指定一個容器名稱; 3> docker save儲存的映象檔案tar包使用docker load命令載入還原; docker export儲存的容器快照tar包使用docker import命令匯入還原; 4> docker save儲存的tar包檔案通常比docker export匯出的檔案要大; docker load和docker import之間的區別: 1)docker load將映象儲存檔案匯入到本地映象庫;docker import將容器快照檔案匯入到本地映象庫; 2)docker load不能指定url;而docker import可以指定url來進行匯入; ``` #### 容器服務管理及開機啟動設定 ```shell # Docker容器開機啟動設定 sudo docker run --restart=always -it centos /bin/bash --restart=always # 預設情況下docker重啟之後所有容器會被關閉,這個選項的意思是容器隨docker engine自啟動 # 如果建立時候未指定--restart=always,可通過docker update命令設定: docker update --restart=always 7b5f30fe77c0 # 注意Docker服務開啟啟動 # restart引數介紹 # no:容器退出時候,不重啟容器 # on-failure: 只有在非0狀態退出時才重新啟動容器 # always:無論退出狀態是如何,都重啟容器 # unless-stopped: 在容器退出時總是重啟容器,但是不考慮在Docker守護程序啟動時就已經停止了的容器 # 在使用on-failure策略時,指定Docker將嘗試重新啟動容器的最大次數; # 預設情況下,Docker將嘗試永遠重新啟動容器 # sudo docker run --restart=on-failure:5