Cris 的 Docker 學習筆記

Author:Cris

1. Docker 基礎知識

1.1 什麼是 Docker ?

這裏引用維基百科php

Docker是一個開放源代碼軟件項目,讓應用程序部署在軟件貨櫃下的工做能夠自動化進行,藉此在Linux操做系統上,提供一個額外的軟件抽象層,以及操做系統層虛擬化的自動管理機制html

依據行業分析公司「451研究」:「Dockers是有能力打包應用程序及其虛擬容器,能夠在任何Linux服務器上運行的依賴性工具,這有助於實現靈活性和便攜性,應用程序在任何地方均可以運行,不管是公有云、私有云、單機等。」java

中文官網地址mysql

簡單來講,Docker 經過對應用組件的封裝、分發、部署、運行等生命週期的管理,使用戶的APP(能夠是一個WEB應用或數據庫應用等等)及其運行環境可以作到「一次封裝,處處運行」。linux

1.2 爲何 Docker 如此流行?

一款產品從開發到上線,從操做系統,到運行環境,再到應用配置。做爲開發+運維之間的協做咱們須要關心不少東西,這也是不少互聯網公司都不得不面對的問題,特別是各類版本的迭代以後,不一樣版本環境的兼容,對運維人員都是考驗nginx

Docker之因此發展如此迅速,也是由於它對此給出了一個標準化的解決方案。git

環境配置如此麻煩,換一臺機器,就要重來一次,費力費時。不少人想到,能不能從根本上解決問題,軟件能夠帶環境安裝?也就是說,安裝的時候,把原始環境如出一轍地複製過來。開發人員利用 Docker 能夠消除協做編碼時「在個人機器上可正常工做」的問題。web

以前在服務器配置一個應用的運行環境,要安裝各類軟件,隨便拿一個 Java 項目來講,Java/Tomcat/MySQL/JDBC驅動包基本是必不可少的。安裝和配置這些東西有多麻煩就不說了,它還不能跨平臺。假如咱們是在 Windows 上安裝的這些環境,到了 Linux 又得從新裝。何況就算不跨操做系統,換另外一臺一樣操做系統的服務器,要移植應用也是很是麻煩的。redis

傳統上認爲,軟件編碼開發/測試結束後,所產出的成果便是程序或是可以編譯執行的二進制字節碼等(java爲例)。而爲了讓這些程序能夠順利執行,開發團隊也得準備完整的部署文件,讓維運團隊得以部署應用程式,開發須要清楚的告訴運維部署團隊,用的所有配置文件+全部軟件環境。不過,即使如此,仍然經常發生部署失敗的情況。sql

Docker鏡像的設計,使得Docker得以打破過去「程序即應用」的觀念。透過鏡像(images)將做業系統核心除外,運做應用程式所須要的系統環境,由下而上打包,達到應用程式跨平臺間的無縫接軌運做。

1.3 Docker 的理念

Docker是基於Go語言實現的雲開源項目。

Docker的主要目標是「Build,Ship and Run Any App,Anywhere」,也就是經過對應用組件的封裝、分發、部署、運行等生命週期的管理,使用戶的APP(能夠是一個WEB應用或數據庫應用等等)及其運行環境可以作到「一次封裝,處處運行」。

Linux 容器技術的出現就解決了這樣一個問題,而 Docker 就是在它的基礎上發展過來的。將應用運行在 Docker 容器上面,而 Docker 容器在任何操做系統上都是一致的,這就實現了跨平臺、跨服務器。只須要一次配置好環境,換到別的機子上就能夠一鍵部署好,大大簡化了操做(能夠參考一下 Java 的一次編譯,到處運行特性)

簡單來講,Docker 的出現解決了運行環境和配置問題軟件容器,方便作持續集成並有助於總體發佈的容器虛擬化技術

1.4 Docker 虛擬化

首先咱們簡單瞭解一下 Docker 的實現原理

  • 虛擬機技術

    虛擬機(virtual machine)就是帶環境安裝的一種解決方案。 它能夠在一種操做系統裏面運行另外一種操做系統,好比在Windows 系統裏面運行Linux 系統。應用程序對此毫無感知,由於虛擬機看上去跟真實系統如出一轍,而對於底層系統來講,虛擬機就是一個普通文件,不須要了就刪掉,對其餘部分毫無影響。這類虛擬機完美的運行了另外一套系統,可以使應用程序,操做系統和硬件三者之間的邏輯不變。

    虛擬機的缺點:

    1. 資源佔用多
    2. 冗餘步驟多
    3. 啓動慢
  • 容器虛擬化技術

    因爲前面虛擬機存在這些缺點,Linux 發展出了另外一種虛擬化技術:Linux 容器(Linux Containers,縮寫爲 LXC)。 Linux 容器不是模擬一個完整的操做系統,而是對進程進行隔離。有了容器,就能夠將軟件運行所需的全部資源打包到一個隔離的容器中。容器與虛擬機不一樣,不須要捆綁一整套操做系統,只須要軟件工做所需的庫資源和設置。系統所以而變得高效輕量並保證部署在任何環境中的軟件都能始終如一地運行。

比較 Docker 和傳統虛擬化方式的不一樣之處:

  • 傳統虛擬機技術是虛擬出一套硬件後,在其上運行一個完整操做系統,在該系統上再運行所需應用進程;
  • 而容器內的應用進程直接運行於宿主的內核,容器內沒有本身的內核,並且也沒有進行硬件虛擬。所以容器要比傳統虛擬機更爲輕便。
  • 每一個容器之間互相隔離,每一個容器有本身的文件系統 ,容器之間進程不會相互影響,能區分計算資源。

1.5 DevOps

經過 Docker 一次構建,到處運行的特性, DevOps 獲得了極大的發展, 關於 DevOps,能夠參考知乎的這個問答

或者這個系列的文章

我的理解: DevOps 是一種優化軟件開發和運營部署之間理念,經過自動化的工具協做和溝通來完成軟件的生命週期管理,從而更快、更頻繁地交付更穩定的軟件

1.6 Docker 的優點

  1. 更快速的應用交付和部署

    傳統的應用開發完成後,須要提供一堆安裝程序和配置說明文檔,安裝部署後需根據配置文檔進行繁雜的配置才能正常運行。Docker化以後只須要交付少許容器鏡像文件,在正式生產環境加載鏡像並運行便可,應用安裝配置在鏡像裏已經內置好,大大節省部署配置和測試驗證時間。

  2. 更便捷的升級和擴縮容

    隨着微服務架構和Docker的發展,大量的應用會經過微服務方式架構,應用的開發構建將變成搭樂高積木同樣,每一個Docker容器將變成一塊「積木」,應用的升級將變得很是容易。當現有的容器不足以支撐業務處理時,可經過鏡像運行新的容器進行快速擴容,使應用系統的擴容從原先的天級變成分鐘級甚至秒級。

  3. 更簡單的系統運維

    應用容器化運行後,生產環境運行的應用可與開發、測試環境的應用高度一致,容器會將應用程序相關的環境和狀態徹底封裝起來,不會由於底層基礎架構和操做系統的不一致性給應用帶來影響,產生新的BUG。當出現程序異常時,也能夠經過測試環境的相同容器進行快速定位和修復。

  4. 更高效的計算資源利用

    Docker是內核級虛擬化,其不像傳統的虛擬化技術同樣須要額外的Hypervisor支持,因此在一臺物理機上能夠運行不少個容器實例,可大大提高物理服務器的CPU和內存的利用率。

1.7 當前企業使用 Docker 現狀

  • 美團

  • 微博

  • 蘑菇街

ps: 若是軟件開發人員對自動化運維一樣熟悉,那麼不只能夠大大下降開發和運維之間的扯皮,還能夠提升開發人員的議價能力

1.8 Docker 官網和倉庫

docker官網:www.docker.com

docker中文網站:www.docker-cn.com/

Docker Hub官網: hub.docker.com/

1.9 我的總結

以 Java 爲例,之前開發人員只須要將開發好的代碼交給運維部署便可,可是隨着敏捷開發等開發模式的流行以及愈來愈高速化的軟件開發-測試-交付-部署等流程,爲了減低運維壓力,調和開發人員和運維人員之間的關係,容器虛擬化技術(Docker)的應運而生

之前咱們僅僅是是將 Java 代碼進行交付,如今咱們是將開發環境整套進行交付.經過對開發環境(例如 Redis, MySQL 以及各類配置等)和開發代碼打包,不只更加快速,便捷,也很容易的對應用部署進行彈性擴容,也大大減小了運維壓力

相似於搬家,爲了營造一個和以前同樣的生活環境,的咱們還須要將舊家的東西搬運到新家去,假若有一天,你頗有錢,能夠將舊家所在的整棟樓搬運到新家所在地;或者說你有了超能力,能夠將舊家的環境直接複製到新家.那麼比起之前累死累活的搬運東西這種方式,哪一個更方便不就是一目瞭然的事情了~~~

2. Docker 安裝

2.1 前提

當前 Docker 基本都裝在 Linux 環境下,以 CentOS 爲例,建議 CentOS6.5 以上版本,目前主流 CentOS6.8 和 CentOS7.x 都支持

Cris 的 Linux 環境以下

2.2 Docker 三要素

  • 鏡像(Image)

    Docker 鏡像(Image)就是一個只讀的模板。鏡像能夠用來建立 Docker 容器,一個鏡像能夠建立不少容器。

  • 容器(Container)

    Docker 利用容器(Container)獨立運行一個或一組應用。容器是用鏡像建立的運行實例。

    它能夠被啓動、開始、中止、刪除。每一個容器都是相互隔離的、保證安全的平臺。

    能夠把容器看作是一個簡易版的 Linux 環境(包括root用戶權限、進程空間、用戶空間和網絡空間等)和運行在其中的應用程序。

    容器的定義和鏡像幾乎如出一轍,也是一堆層的統一視角,惟一區別在於容器的最上面那一層是可讀可寫的。

  • 倉庫(Repository)

    倉庫(Repository)是集中存放鏡像文件的場所。

    倉庫(Repository)和倉庫註冊服務器(Registry)是有區別的。倉庫註冊服務器上每每存放着多個倉庫,每一個倉庫中又包含了多個鏡像,每一個鏡像有不一樣的標籤(tag)。

    倉庫分爲公開倉庫(Public)和私有倉庫(Private)兩種形式。 最大的公開倉庫是 Docker Hub(hub.docker.com/), 存放了數量龐大的鏡像供用戶下載。國內的公開倉庫包括阿里雲 、網易雲等

須要正確的理解倉儲/鏡像/容器這幾個概念:

Docker 自己是一個容器運行載體或稱之爲管理引擎。咱們把應用程序和配置依賴打包好造成一個可交付的運行環境,這個打包好的運行環境就是 image鏡像文件。只有經過這個鏡像文件才能生成容器。image 文件能夠看做是容器的模板。Docker 根據 image 文件生成容器的實例。同一個 image 文件,能夠生成多個同時運行的容器實例。

  • image 文件生成容器實例,稱爲鏡像文件。

  • 一個容器運行一種服務,當咱們須要的時候,就能夠經過docker客戶端建立一個對應的運行實例,也就是咱們的容器

  • 至於倉儲,就是放了一堆鏡像的地方,咱們能夠把鏡像發佈到倉儲中,須要的時候從倉儲中拉下來就能夠了。

Docker 簡易流程圖

2.3 安裝 Docker

以 Cris 的CentOS6.8 爲例

  1. Docker使用EPEL發佈,RHEL系的OS首先要確保已經持有EPEL倉庫,不然先檢查OS的版本,而後安裝相應的EPEL包。

    [cris@hadoop104 ~]$ sudo yum install -y epel-release
    複製代碼
  2. 而後執行 Docker 安裝命令

    當前在 CentOS6.8 版本若是直接執行如下命令,極可能沒法安裝 Docker

    [cris@hadoop104 ~]$ sudo yum install -y docker-io
    複製代碼

    因此須要執行另外的命令

    [root@hadoop104 cris]# yum install https://get.docker.com/rpm/1.7.1/centos-6/RPMS/x86_64/docker-engine-1.7.1-1.el6.x86_64.rpm
    複製代碼

    參考博客

  3. 安裝後的配置文件:/etc/sysconfig/docker

  4. Docker 版本

  5. 啓動 Docker

ps: 若是要在 CentOS7.x 上安裝 Docker, 請直接參考官網文檔便可

2.4 Docker 鏡像加速

以阿里云爲例,首先註冊一個阿里雲帳號,而後進入鏡像加速頁面

配置本機的 Docker 文件

[cris@hadoop104 ~]$ sudo vim /etc/sysconfig/docker
複製代碼

重啓 Docker 服務

[cris@hadoop104 ~]$ sudo service docker restart
中止 docker:                                              [肯定]
Starting docker:	                                   [肯定]
複製代碼

而後檢測 Docker 鏡像加速地址是不是咱們的阿里雲鏡像地址

ps: 若是是 CentOS7.x 版本,直接參考上面的阿里雲操做文檔

網易雲鏡像加速這裏直接略過~~~:cowboy_hat_face:

2.5 hello world

直接經過 docker run hello-world 命令,咱們能夠直接從阿里雲拉取 hello-world 鏡像並建立容器自動運行(在本地沒有找到 hello-world 的鏡像時)

docker run 命令運行流程圖

2.6 Docker 和 VM 比較

  • Docker 工做原理

    Docker是一個Client-Server結構的系統,Docker守護進程運行在主機上,而後經過Socket鏈接從客戶端訪問,守護進程從客戶端接受命令並管理運行在主機上的容器。容器,是一個運行時環境,就是咱們前面說到的集裝箱。

    例以下面 Docker 圖標(一隻鯨魚背上拖着不少個集裝箱, 鯨魚相似於 Docker,一個個的集裝箱就是軟件開發環境中的各類軟件)

    如下爲 Docker 運行架構圖

  • 爲何 Docker 運行速度遠大於 VM?

    (1)Docker有着比虛擬機更少的抽象層。因爲docker不須要Hypervisor實現硬件資源虛擬化,運行在docker容器上的程序直接使用的都是實際物理機的硬件資源。所以在CPU、內存利用率上docker將會在效率上有明顯優點。

    (2)Docker利用的是宿主機的內核,而不須要CentOS。所以,當新建一個容器時,docker不須要和虛擬機同樣從新加載一個操做系統內核。從而避免加載操做系統內核這個比較費時費資源的過程,當新建一個虛擬機時,虛擬機軟件須要加載CentOS,整個新建過程是分鐘級別的。而docker因爲直接利用宿主機的操做系統,所以新建一個docker容器只須要幾秒鐘。

  • Docker 和 VM 對比圖

  • Docker 和 VM 特色對比圖

3. Docker 經常使用命令

3.1 幫助命令

  • docker version

  • docker info

  • docker --help(-h)

3.2 鏡像命令

  1. docker images

列出本地主機上的鏡像

  • REPOSITORY:表示鏡像的倉庫源
  • TAG:鏡像的標籤
  • MAGE ID:鏡像ID
  • CREATED:鏡像建立時間
  • SIZE:鏡像大小

同一倉庫源能夠有多個 TAG,表明這個倉庫源的不一樣個版本,咱們使用 REPOSITORY:TAG 來定義不一樣的鏡像。 若是你不指定一個鏡像的版本標籤,例如你只使用 ubuntu,docker 將默認使用 ubuntu:latest 鏡像

  • images 命令 的 option 參數

    • -a:列出本地全部的鏡像(含中間映像層)

    • -q :只顯示鏡像ID

    • --digests :顯示鏡像的摘要信息

    • --no-trunc :顯示完整的鏡像信息

  1. docker search 鏡像名

從 Docker hub 官網搜索鏡像

官網地址: hub.docker.com

以 tomcat 爲例

和在官網搜索的結果如出一轍

  • 參數說明

    • -s : 列出收藏數不小於指定值的鏡像

      只搜索點贊數大於30的鏡像

    • --no-trunc : 顯示完整的鏡像描述

    • --automated : 只列出 automated build類型的鏡像

  1. docker pull 鏡像名:tag

    拉取鏡像(從前面咱們已經設置的阿里雲的鏡像加速地址)

    若是 docker pull 鏡像名 後面不加參數,默認下載最新版本

    即 docker pull tomcat 等價於 docker pull tomcat:latest

    [cris@hadoop104 ~]$ sudo docker pull tomcat
    複製代碼

  2. docker rmi 鏡像名/鏡像id

    刪除 Docker 鏡像

    ps: docker rmi 鏡像名 默認會刪除標籤爲 :latest 的鏡像,若是要刪除指定標籤的鏡像,在鏡像名後面指定 tag 便可

    若是沒法刪除,出現如上提示,表示咱們的鏡像正在使用中,可使用 -f 強制刪除

    若是想要刪除多個鏡像

    docker rmi -f 鏡像名1:tag 鏡像名2:tag

    sudo docker rmi -f hello-world tomcat
    複製代碼

    刪除所有鏡像

    sudo docker rmi -f $(docker images -qa)
    複製代碼

結合 git 知識,想一想 Docker 是否有 docker pull 和 docker commit 命令?

3.3 容器命令

  1. 以 CentOS 爲例,這裏 Cris 從阿里雲下載一個 CentOS 的鏡像

  2. 新建並啓動容器(以 Docker 裏面的 CentOS 鏡像爲例)

    docker run [options] image [command] [arg...]

    options 這裏經常使用的有:

    --name="容器新名字": 爲容器指定一個名稱; -d: 後臺運行容器,並返回容器ID,也即啓動守護式容器; -i:以交互模式運行容器,一般與 -t 同時使用; -t:爲容器從新分配一個僞輸入終端,一般與 -i 同時使用; -P: 隨機端口映射; -p: 指定端口映射,有如下四種格式 ip:hostPort:containerPort ip::containerPort hostPort:containerPort containerPort

    咱們使用 -it 參數來啓動 CentOS 容器

    root@ 後面跟着的就是該容器的 id

  3. 查看全部運行的容器命令

    docker ps [options]

    參數說明:

    -a :列出當前全部正在運行的容器+歷史上運行過的容器 -l :顯示最近運行的容器。 -n number:顯示最近 number 個建立的容器。 -q :靜默模式,只顯示容器編號。 --no-trunc :不截斷輸出。

  4. 退出容器

    exit: 容器中止並退出

    ctrl+p+q: 容器不中止退出

  5. 啓動已經建立的容器

    docker start 容器id/容器名

  6. 重啓容器

    docker restart 容器id/容器名

  7. 中止容器

    docker stop 容器id/容器名

  8. 強制關閉容器

    docker kill 容器id/容器名

  9. 刪除已中止的容器

    docker rm 容器id/容器名

    一次性刪除多個已經中止的容器

    docker rm -f $(docker ps -qa) 或者 docker ps -a -q | xargs docker rm

  10. 交互式容器和守護式容器

    前面啓動 CentOS 容器使用的 -it 參數就是表示交互式命令,經過終端來保持和容器的交互

    若是要啓動守護式容器,那麼須要加上 -d 參數

    可是此時查詢正在運行的容器

    沒有發現之後臺模式運行的 Docker 容器

    發現該容器已經自動退出了

    問題:docker ps -a 進行查看, 會發現容器已經退出 很重要的一點: Docker容器後臺運行,就必須有一個前臺進程. 容器運行的命令若是不是那些一直掛起的命令(好比運行top,tail),是會自動退出的。

    這個是docker的機制問題,好比你的web容器,咱們以nginx爲例,正常狀況下,咱們配置啓動服務只須要啓動響應的service便可。例如 service nginx start 可是,這樣作,nginx爲後臺進程模式運行,就致使docker前臺沒有運行的應用, 這樣的容器後臺啓動後,會當即自殺由於他以爲他沒事可作了. 因此,最佳的解決方案是,將你要運行的程序之前臺進程的形式運行

  11. 容器日誌

    對於後臺運行的容器,能夠如下面的方式來啓動

    [cris@hadoop104 ~]$ sudo  docker run -d --name centos004 centos /bin/sh -c "while true;do echo hello zzyy;sleep 2;done"
    複製代碼

    查看 Docker 容器

    即使是後臺啓動,可是由於前臺一直打印日誌,Docker 容器也不會自動關閉

    若是此時咱們想去查看 Docker 容器的日誌,能夠經過如下命令

    docker logs -f -t --tail 容器ID

    • -t 是加入時間戳
    • -f 跟隨最新的日誌打印
    • --tail 數字 顯示最後多少條

  12. 查看容器內運行的進程

    docker top 容器ID

  13. 查看容器內部細節

    docker inspect 容器ID

  14. 進入正在運行的容器並以命令行交互

    從新進入正在運行的容器

    docker attach 容器ID

    還有一種方式是

    docker exec -it 容器ID bashShell(功能更增強大,能夠直接返回結果到客戶端)

    區別在於:

    attach 命令直接進入容器啓動命令的終端,不會啓動新的進程

    exec 命令是在容器中打開新的終端,而且能夠啓動新的進程

  15. 從容器內拷貝文件到主機上

    docker cp 容器ID:容器內路徑 目的主機路徑

    示例以下

3.4 經常使用命令總結

attach    Attach to a running container                 # 當前 shell 下 attach 鏈接指定運行鏡像

build     Build an image from a Dockerfile              # 經過 Dockerfile 定製鏡像

commit    Create a new image from a container changes   # 提交當前容器爲新的鏡像

cp        Copy files/folders from the containers filesystem to the host path   #從容器中拷貝指定文件或者目錄到宿主機中

create    Create a new container                        # 建立一個新的容器,同 run,但不啓動容器

diff      Inspect changes on a container's filesystem # 查看 docker 容器變化 events Get real time events from the server # 從 docker 服務獲取容器實時事件 exec Run a command in an existing container # 在已存在的容器上運行命令 export Stream the contents of a container as a tar archive # 導出容器的內容流做爲一個 tar 歸檔文件[對應 import ] history Show the history of an image # 展現一個鏡像造成歷史 images List images # 列出系統當前鏡像 import Create a new filesystem image from the contents of a tarball # 從tar包中的內容建立一個新的文件系統映像[對應export] info Display system-wide information # 顯示系統相關信息 inspect Return low-level information on a container # 查看容器詳細信息 kill Kill a running container # kill 指定 docker 容器 load Load an image from a tar archive # 從一個 tar 包中加載一個鏡像[對應 save] login Register or Login to the docker registry server # 註冊或者登錄一個 docker 源服務器 logout Log out from a Docker registry server # 從當前 Docker registry 退出 logs Fetch the logs of a container # 輸出當前容器日誌信息 port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT # 查看映射端口對應的容器內部源端口 pause Pause all processes within a container # 暫停容器 ps List containers # 列出容器列表 pull Pull an image or a repository from the docker registry server # 從docker鏡像源服務器拉取指定鏡像或者庫鏡像 push Push an image or a repository to the docker registry server # 推送指定鏡像或者庫鏡像至docker源服務器 restart Restart a running container # 重啓運行的容器 rm Remove one or more containers # 移除一個或者多個容器 rmi Remove one or more images # 移除一個或多個鏡像[無容器使用該鏡像纔可刪除,不然需刪除相關容器纔可繼續或 -f 強制刪除] run Run a command in a new container # 建立一個新的容器並運行一個命令 save Save an image to a tar archive # 保存一個鏡像爲一個 tar 包[對應 load] search Search for an image on the Docker Hub # 在 docker hub 中搜索鏡像 start Start a stopped containers # 啓動容器 stop Stop a running containers # 中止容器 tag Tag an image into a repository # 給源中鏡像打標籤 top Lookup the running processes of a container # 查看容器中運行的進程信息 unpause Unpause a paused container # 取消暫停容器 version Show the docker version information # 查看 docker 版本號 wait Block until a container stops, then print its exit code # 截取容器中止時的退出狀態值 複製代碼

4. Docker 鏡像

鏡像是一種輕量級、可執行的獨立軟件包,用來打包軟件運行環境和基於運行環境開發,它包含運行某個軟件所需的全部內容,包括代碼、運行時、庫、環境變量和配置文件。

而 Docker 鏡像的底層實現原理是 UnionFS 聯合文件系統

4.1 UnionFS 聯合文件系統

UnionFS(聯合文件系統):Union文件系統(UnionFS)是一種分層、輕量級而且高性能的文件系統,它支持對文件系統的修改做爲一次提交來一層層的疊加,同時能夠將不一樣目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)。Union 文件系統是 Docker 鏡像的基礎。鏡像能夠經過分層來進行繼承,基於基礎鏡像(沒有父鏡像),能夠製做各類具體的應用鏡像。

相似於花捲這種常見的早餐,文件系統能夠經過一層一層的嵌套,對外暴露統一的"表面層"來供使用者操做

特性:一次同時加載多個文件系統,但從外面看起來,只能看到一個文件系統,聯合加載會把各層文件系統疊加起來,這樣最終的文件系統會包含全部底層的文件和目錄

4.2 Docker 鏡像加載原理

docker的鏡像實際上由一層一層的文件系統組成,這種層級的文件系統UnionFS。

bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引導加載kernel, Linux剛啓動時會加載bootfs文件系統,在Docker鏡像的最底層是bootfs。這一層與咱們典型的Linux/Unix系統是同樣的,包含boot加載器和內核。當boot加載完成以後整個內核就都在內存中了,此時內存的使用權已由bootfs轉交給內核,此時系統也會卸載bootfs。

rootfs (root file system) ,在bootfs之上。包含的就是典型 Linux 系統中的 /dev, /proc, /bin, /etc 等標準目錄和文件。rootfs就是各類不一樣的操做系統發行版,好比Ubuntu,Centos等等。

平時咱們安裝進虛擬機的CentOS都是好幾個G,爲何docker這裏才200M??

對於一個精簡的OS,rootfs能夠很小,只須要包括最基本的命令、工具和程序庫就能夠了,由於底層直接用Host的kernel,本身只須要提供 rootfs 就好了。因而可知對於不一樣的linux發行版, bootfs基本是一致的, rootfs會有差異, 所以不一樣的發行版能夠公用bootfs。

經過 docker pull 命令再來感覺一下鏡像分層

因此在下載的過程當中咱們能夠看到docker的鏡像好像是在一層一層的在下載

爲何 Docker 鏡像要採用這種分層結構呢?

最大的一個好處就是 - 共享資源

好比:有多個鏡像都從相同的 base 鏡像構建而來,那麼宿主機只需在磁盤上保存一份base鏡像, 同時內存中也只需加載一份 base 鏡像,就能夠爲全部容器服務了。並且鏡像的每一層均可以被共享。

因此,Docker鏡像都是隻讀的,當容器啓動時,一個新的可寫層被加載到鏡像的頂部。這一層一般被稱做「容器層」,「容器層」之下的都叫「鏡像層」。

4.3 Docker 鏡像 commit 操做

基礎命令

docker commit 用於提交容器副本使之成爲一個新的鏡像

完整格式以下

docker commit -m=「提交的描述信息」 -a=「做者」 容器ID 要建立的目標鏡像名:[標籤名]

以 Tomcat 爲例

  1. 啓動 Tomcat 容器

  • -p 主機端口:docker容器端口
  • -P 隨機分配主機端口
  • i:交互
  • t:終端
  1. 以交互模式進入到 Docker 中的 Tomcat 中
[cris@hadoop104 ~]$ sudo docker exec -it 8db42f30a60a /bin/bash
root@8db42f30a60a:/usr/local/tomcat# 
複製代碼

咱們刪除掉 tomcat 下的 doc 文檔

  1. 也即當前的tomcat運行實例是一個沒有文檔內容的容器,以它爲模板commit一個沒有doc的tomcat新鏡像 cris/tomcat

    至此,新的沒有 doc 的 Tomcat 鏡像已經生成好了

  2. 運行基於新鏡像的 Tomcat 容器

    [cris@hadoop104 ~]$ sudo docker run -it -p 8118:8080 cris/tomcat:1.1
    複製代碼

    能夠發現是沒有 doc 文檔的

    ps: 以上都是之前臺方式啓動 Tomcat 容器(不斷打印日誌),若是想要之後臺守護方式啓動,直接加上 -d 參數便可

    控制檯也就沒有日誌打印了

5. Docker容器數據卷

5.1 什麼是 Docker 容器數據卷

需求:

  • Docker 能夠將運行的環境打包造成容器運行,可是咱們對 Docker 容器的數據的要求但願是持久化的
  • 容器之間但願共享數據

Docker容器產生的數據,若是不經過docker commit生成新的鏡像,使得數據作爲鏡像的一部分保存下來, 那麼當容器刪除後,數據天然也就沒有了。

爲了能保存數據在docker中咱們使用數據卷。

相似 Redis裏面的rdb和aof文件或者咱們平時使用的移動硬盤

5.2 數據卷的用處

數據卷就是目錄或文件,存在於一個或多個容器中,由docker掛載到容器,但不屬於聯合文件系統,所以可以繞過Union File System提供一些用於持續存儲或共享數據的特性:

數據卷的設計目的就是數據的持久化,徹底獨立於容器的生存週期,所以Docker不會在容器刪除時刪除其掛載的數據卷

特色:

  1. 數據卷可在容器之間共享或重用數據
  2. 卷中的更改能夠直接生效
  3. 數據卷中的更改不會包含在鏡像的更新中
  4. 數據卷的生命週期一直持續到沒有容器使用它爲止

5.3 數據卷使用

一. 直接經過命令添加數據卷

docker run -it -v /宿主機絕對路徑目錄:/容器內目錄 鏡像名

驗證數據卷是否掛載成功

[cris@hadoop104 ~]$ sudo docker inspect 4b02b2727058
複製代碼

宿主機和容器之間的數據交互

同理, 容器能夠對數據進行修改並同步到宿主機

容器中止退出後,主機修改後數據是否同步

經過 exit 命令中止容器並退出終端

而後在宿主機對數據進行修改

[cris@hadoop104 hostDataVolume]$ sudo vim dog.txt
複製代碼

從新開啓容器

能夠發現,即使是容器關閉,宿主機依然能夠對數據捲進行數據操做,當容器從新開啓時,數據卷會自動進行同步

若是想要設置權限,例如容器只能對數據捲進行讀和同步,宿主機能夠操做數據卷,那麼只須要添加一個參數便可

docker run -it -v /宿主機絕對路徑目錄:/容器內目錄:ro 鏡像名

ro 就表示 read-only 權限(針對容器)

二. DockerFile添加數據卷

DockerFile 簡單來講,就是描述 Docker 鏡像的描述文件

流程簡單梳理以下

  1. 宿主機新建一個 DockerFile

    可在Dockerfile中使用VOLUME指令來給鏡像添加一個或多個數據卷

    ps: 出於可移植和分享的考慮,用-v 主機目錄:容器目錄這種方法不可以直接在Dockerfile中實現。 因爲宿主機目錄是依賴於特定宿主機的,並不可以保證在全部的宿主機上都存在這樣的特定目錄

  2. build 構建鏡像

  3. 根據鏡像運行容器

  4. 測試數據卷

    根據 inspect 命令查看對應的宿主機數據卷目錄

    [cris@hadoop104 ~]$ sudo docker ps
    CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS               NAMES
    cd98e9b2c94b        cris/centos         "/bin/sh -c /bin/bas   2 minutes ago       Up 2 minutes                            jovial_goodall      
    [cris@hadoop104 ~]$ sudo docker inspect cd98e9b2c94b
    複製代碼

    默認宿主機掛載地址須要經過 inspect 命令查看

三. 數據卷容器

主要用於容器和容器之間的數據共享

命令: --volumes-from

示例以下:

  1. 啓動一個父容器

    啓動後咱們在指定目錄下建立一個文件

  2. 新建兩個子容器,繼承自父容器

    能夠發現成功同步了父容器的數據

    同時修改任意一個子容器的數據卷數據,都會同步到其餘容器

    即使是刪除任意一個容器,數據卷的數據同步不會中止

  3. 結論

    容器之間共享數據的傳遞,數據卷的生命週期一直持續到沒有容器使用它爲止

6. DockerFile解析

6.1 什麼是 DockerFile

Dockerfile是用來構建Docker鏡像的構建文件,是由一系列命令和參數構成的腳本。

一般使用 DockerFile 的三個步驟都是:

  1. 編寫 DockerFile 文件
  2. 執行 docker build 編譯命令
  3. 執行docker run 啓動容器命令

以 CentOS 爲例, Docker Hub 上的 CentOS 的 DockerFile 文件以下

6.2 DockerFile 構建過程解析

DockerFile 基礎知識

以上面的 CentOS DockerFile 文件爲例

  1. 每條保留字指令(紅色字體)都必須爲大寫字母且後面要跟隨至少一個參數
  2. 指令按照從上到下,順序執行
  3. #表示註釋
  4. 每條指令都會建立一個新的鏡像層,並對鏡像進行提交

Docker執行Dockerfile的大體流程

  1. docker從基礎鏡像運行一個容器
  2. 執行一條指令並對容器做出修改
  3. 執行相似docker commit的操做提交一個新的鏡像層
  4. docker再基於剛提交的鏡像運行一個新容器
  5. 執行dockerfile中的下一條指令直到全部指令都執行完成

總結:

從應用軟件的角度來看,Dockerfile、Docker鏡像與Docker容器分別表明軟件的三個不一樣階段,

  • Dockerfile是軟件的原材料
  • Docker鏡像是軟件的交付品
  • Docker容器則能夠認爲是軟件的運行態。 Dockerfile面向開發,Docker鏡像成爲交付標準,Docker容器則涉及部署與運維,三者缺一不可,協力充當Docker體系的基石。

Dockerfile,須要定義一個Dockerfile,Dockerfile定義了進程須要的一切東西。Dockerfile涉及的內容包括執行代碼或者是文件、環境變量、依賴包、運行時環境、動態連接庫、操做系統的發行版、服務進程和內核進程(當應用進程須要和系統服務和內核進程打交道,這時須要考慮如何設計namespace的權限控制)等等;

Docker鏡像,在用Dockerfile定義一個文件以後,docker build時會產生一個Docker鏡像,當運行 Docker鏡像時,會真正開始提供服務;

Docker容器,直接提供服務.

6.3 DockerFile 體系結構(保留字)

  • FROM : 基礎鏡像,當前新鏡像是基於哪一個鏡像的

  • MAINTAINER : 鏡像維護者的姓名和郵箱地址

  • RUN : 容器構建時須要運行的命令

  • EXPOSE : 當前容器對外暴露出的端口

  • WORKDIR : 指定在建立容器後,終端默認登錄的進來工做目錄,一個落腳點

  • ENV : 用來在構建鏡像過程當中設置環境變量

    ENV MY_PATH /usr/mytest 這個環境變量能夠在後續的任何RUN指令中使用,這就如同在命令前面指定了環境變量前綴同樣; 也能夠在其它指令中直接使用這些環境變量,

    好比:WORKDIR $MY_PATH

  • ADD : 將宿主機目錄下的文件拷貝進鏡像且ADD命令會自動處理URL和解壓tar壓縮包

  • COPY : 相似ADD,拷貝文件和目錄到鏡像中。 將從構建上下文目錄中 <源路徑> 的文件/目錄複製到新的一層的鏡像內的 <目標路徑> 位置

  • VOLUME : 容器數據卷,用於數據保存和持久化工做

  • CMD : 指定一個容器啓動時要運行的命令

    注意: Dockerfile 中能夠有多個 CMD 指令,但只有最後一個生效,CMD 會被 docker run 以後的參數替換

  • ENTRYPOINT : 指定一個容器啓動時要運行的命令;ENTRYPOINT 的目的和 CMD 同樣,都是在指定容器啓動程序及參數,可是不會被 docker run 後面的參數替換,而是追加

  • ONBUILD : 當構建一個被繼承的Dockerfile時運行命令,父鏡像在被子繼承後父鏡像的onbuild被觸發

總結:

6.4 DockerFile 實戰案例

一. 自定義 CentOS 鏡像案例

首先,咱們須要知道Docker Hub 中 99% 的鏡像都是經過在 base 鏡像中安裝和配置須要的軟件構建出來的

而這個 base 鏡像就是scratch 鏡像

首先中止全部正在運行的 Docker 容器

而後看看從阿里雲下載的基礎版 CentOS 缺失了哪些功能

如今咱們的目標就是經過編寫 DockerFile 爲基礎版本的 CentOS 鏡像加上這些缺失的功能

DockerFile 以下

FROM centos
MAINTAINER cris<cris163@111.com>
 
ENV MYPATH /usr/local
WORKDIR $MYPATH
 
RUN yum -y install vim
RUN yum -y install net-tools
 
EXPOSE 80
 
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
複製代碼

開始構建

[cris@hadoop104 dockerFile]$ sudo docker build -f centosDockerFile -t cris/centos:1.2 .
複製代碼

構建成功,開始運行容器

除此以外,還能夠查看 Docker 鏡像的修改歷史

二. CMD/ENTRYPOINT 鏡像案例

CMD 示例:

Dockerfile 中能夠有多個 CMD 指令,但只有最後一個生效,CMD 會被 docker run 以後的參數替換

以 Tomcat 爲例

緣由就在於咱們的 ls -l 參數替換掉了原來的啓動參數,以下

至關於在這行參數後面又添加了 CMD ls -l

那麼容器啓動時就會執行最後的 ls -l 命令

若是是 ENTRYPOINT

docker run 以後的參數會被當作參數傳遞給 ENTRYPOINT,以後造成新的命令組合

示例以下

編寫 DockerFile

FROM centos
RUN yum install -y curl
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
複製代碼

而後構建

[cris@hadoop104 dockerFile]$ sudo docker build -f centosDockerFile2 -t cris/ipcentos .
複製代碼

以添加參數的形式啓動容器

若是咱們把上面 DockerFile 文件中的 ENTRYPOINT 改成 CMD 並從新編譯鏡像

那麼一樣的命令啓動容器,會出現如下報錯

ONBUILD 示例

相似於觸發器,在鏡像編譯以及子鏡像編譯的時候觸發

新建一個 DockerFile , centosDockerFileFather

FROM centos
RUN yum install -y curl
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
ONBUILD RUN echo "father is building -------------->"
複製代碼

進行編譯後,又新建一個子 DockerFile, centosDockerFileSon

FROM cris/ipcentos_father
RUN yum install -y curl
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
複製代碼

cris/ipcentos_father 就是上面編譯完成的父鏡像

當咱們開始編譯子鏡像時,就會觸發 ONBUILD 操做

三. 自定義 Tomcat9 鏡像(重要)

在新建目錄,而且添加如下文件

Dockerfile 內容以下

FROM         centos
MAINTAINER    cris<cris@1222.com>
#把宿主機當前上下文的c.txt拷貝到容器/usr/local/路徑下
COPY copy.txt /usr/local/cincontainer.txt
#把java與tomcat添加到容器中
ADD jdk-8u172-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.19.tar.gz /usr/local/
#安裝vim編輯器
RUN yum -y install vim
#設置工做訪問時候的WORKDIR路徑,登陸落腳點
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java與tomcat環境變量
ENV JAVA_HOME /usr/local/jdk1.8.0_172
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.19
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.19
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器運行時監聽的端口
EXPOSE  8080
#啓動時運行tomcat
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.8/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.8/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.19/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.19/bin/logs/catalina.out
複製代碼

而後開始編譯

[cris@hadoop104 mytomcat]$ sudo docker build -t cris_tomcat9 .
複製代碼

若是不加 -f 參數,默認從當前目錄下的 Dockerfile 文件開始編譯

編譯成功後,直接運行

[cris@hadoop104 mytomcat]$ sudo docker run -d -p 9090:8080 --name cristomcat9 -v /home/cris/dockerFile/mytomcat/test:/usr/local/apache-tomcat-9.0.19/webapps/test -v \
> /home/cris/dockerFile/mytomcat/logs:/usr/local/apache-tomcat-9.0.19/logs --privileged=true cris_tomcat9
複製代碼

查看數據卷對應的目錄

[cris@hadoop104 mytomcat]$ sudo docker inspect cristomcat9
複製代碼

驗證 Tomcat 是否啓動

測試web 工程發佈

咱們在宿主機的目錄上新建一個簡單的 web 工程

test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
  </head>
  <body>
    -----------welcome------------
    <%="i am in docker tomcat self "%>
    <br>
    <br>
    <% System.out.println("=============docker tomcat self");%>
  </body>
</html>
複製代碼

而後是 WEB-INF 目錄下的 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  
  <display-name>test</display-name>
 
</web-app>
複製代碼

最後重啓容器

[cris@hadoop104 test]$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                    NAMES
e39ccfda5b58        cris_tomcat9        "/bin/sh -c '/usr/lo   18 minutes ago      Up 18 minutes       0.0.0.0:9090->8080/tcp   cristomcat9         
[cris@hadoop104 test]$ sudo docker restart cristomcat9
複製代碼

測試

咱們在宿主機修改 jsp

實時反映到 Docker 容器中的 Tomcat

在查看對應的日誌

四. 總結

7. 拓展

Docker 安裝常規步驟

  • 搜索鏡像
  • 拉取鏡像
  • 查看鏡像
  • 啓動鏡像
  • 中止容器
  • 移除容器
  • 移除鏡像

7.1 安裝 Tomcat

  1. docker search tomcat
  2. docker pull tomcat
  3. docker image tomcat
  4. docker run -it -p 8080:8080 tomcat
    • -p 主機端口:docker容器端口
    • -P 隨機分配端口
    • i:交互
    • t:終端

7.2 安裝 MySQL

docker pull mysql:5.6

驗證

運行容器

[cris@hadoop104 ~]$ sudo docker run -p 12345:3306 --name mysql -v /home/cris/docker_mysql/conf:/etc/mysql/conf.d -v /home/cris/docker_mysql/logs:/logs -v /home/cris/docker_mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
複製代碼

數據卷已創建

[cris@hadoop104 ~]$ sudo docker ps 
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                     NAMES
029d8863fb17        mysql:5.6           "docker-entrypoint.s   7 seconds ago       Up 6 seconds        0.0.0.0:12345->3306/tcp   mysql    
複製代碼

容器已經成功跑起來了

宿主機測試是否能夠鏈接

數據備份

docker exec myql服務容器ID sh -c ' exec mysqldump --all-databases -uroot -p"123456" ' > /home/cris/docker_mysql/all-databases.sql
複製代碼

7.3 安裝 Redis

先拉取: docker pull redis:3.2

而後執行如下命令運行容器

[cris@hadoop104 ~]$ sudo docker run -p 6379:6379 -v /home/cris/myredis/data:/data -v /home/cris/myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf  -d redis:3.2 redis-server /usr/local/etc/redis/redis.conf --appendonly yes
複製代碼

若是想要修改 Redis 的配置文件,能夠直接在宿主機上對應目錄修改 Redis 的配置文件便可,自動映射到Docker 容器中的 Redis

測試 redis-cli 鏈接

測試持久化文件生成

7.4 測試本地和阿里雲倉庫的鏡像發佈和拉取

首先,咱們知道了 image 的生成方式有兩種,一種是根據 DockerFile 構建;一種是根據容器 commit 新的 image

示例

首先運行一個 Docker 容器

$ sudo docker run -it cris/centos:1.2
複製代碼

而後提交一個新的 image

$ sudo docker commit -a cris -m "new centos image by cris with vim and ifconfig features" 3325338e5c43 cris/centos:1.3
複製代碼

登陸阿里雲,進行測試

建立鏡像倉庫

而後根據指示推送本地 image 到阿里雲的 repository

$ sudo docker login --username=990435014@qq.com registry.cn-hangzhou.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/cris_cool/mycentos:[鏡像版本號]
$ sudo docker push registry.cn-hangzhou.aliyuncs.com/cris_cool/mycentos:[鏡像版本號]
複製代碼

ps: 這裏 Cris 從本機登陸阿里雲倉庫的時候一直沒法登陸,顯示以下錯誤

Error response from daemon: no successful auth challenge for https://registry.cn-hangzhou.aliyuncs.com/v2/ - errors: [token auth attempt for registry https://registry.cn-hangzhou.aliyuncs.com/v2/: https://dockerauth.cn-hangzhou.aliyuncs.com/auth?account=990435014%40qq.com&service=registry.aliyuncs.com%3Acn-hangzhou%3A26842 request failed with status: 401 Unauthorized]
複製代碼

網上搜索了後發現登陸密碼不對,不是登陸阿里雲帳號的密碼,須要從新設置登陸 repository 的密碼

而後開始推送

想要拉取也很簡單

[cris@hadoop104 data]$ sudo docker pull registry.cn-hangzhou.aliyuncs.com/cris_cool/mycentos:1.3
複製代碼

7.5 CentOS 7 下的 Docker 安裝

參考 Docker 官網便可

相關文章
相關標籤/搜索