1.docker介紹、命令、容器、鏡像、數據卷、Dockerfile、常用軟件安裝、推送阿里雲

一、docker介紹

1、docker是什麼

  • 一款產品從開發到上線,從操作系統,到運行環境,再到應用配置。作為開發+運維之間的協作我們需要關心很多東西,這也是很多互聯網公司都不得不面對的問題,特別是各種版本的迭代之後,不同版本環境的兼容,對運維人員都是考驗,環境配置如此麻煩,換一台機器,就要重來一次,費力費時。很多人想到,能不能從根本上解決問題,軟件可以帶環境安裝?也就是說,安裝的時候,把原始環境一模一樣地複製過來。開發人員利用Docker可以消除協作編碼時「在我的機器上可正常工作」的問題。原始的部署方式是開發人員只將程序打包好發送給運維人員,造成了開發環境和生產環境有差異,docker可以將運行環境進行打包,到達將開發環境和生產環境一致
  • Docker是基於Go語言實現的雲開源項目
  • Docker的主要目標是「Build,Ship and Run Any App,Anywhere」,也就是通過對應用組件的封裝、分發、部署、運行等生命周期的管理,使用戶的APP(可以是一個WEB應用或數據庫應用等等)及其運行環境能夠做到「一次封裝,到處運行」
  • Linux容器技術的出現就解決了這樣一個問題,而Docker就是在它的基礎上發展過來的。將應用運行在Docker容器上面,而Docker容器在任何操作系統上都是一致的,這就實現了跨平台、跨服務器。只需要一次配置好環境,換到別的機子上就可以一鍵部署好,大大簡化了操作
  • 解決了運行環境和配置問題軟件容器,方便做持續集成並有助於整體發佈的容器虛擬化技術

2、docker優點

  • 虛擬機就是帶環境安裝的一種解決方案,在一個操作系統上模擬出另外一個操縱系統,在模擬的操作系統上再運行程序,應用程序毫無感知,對底層操作系統來說,虛擬機就是一個文件不需要可以刪掉,完美的模擬硬件,操作系統和應用之間的關係
  • Docker和虛擬機對比
    • 虛擬機和docker都需要運行在底層操作系統之上,虛擬機利用虛擬機管理軟件(例如:VMware),而docker依賴其守護進程
    • 虛擬機管理軟件會虛擬出一個完整的機器(包含操作系統,硬件存儲等),在該系統上再運行所需應用進程;而docker內的應用進程直接運行於宿主的內核,容器內沒有自己的內核,而且也沒有進行硬件虛擬,因此容器要比傳統虛擬機更為輕便只會虛擬出一個容器應用,底層使用的還是宿主機的硬件,每個容器之間互相隔離,每個容器有自己的文件系統 ,容器之間進程不會相互影響,能區分計算資源
    • 虛擬機資源佔用多、冗餘步驟多、啟動慢,而docker資源佔用少,操作簡單,啟動快
    • docker一次構建、隨處運行
      • 更快速的應用交付和部署
      • 更便捷的升級和擴縮容
      • 更簡單的系統運維
      • 更高效的計算資源利用
  • 相關網站

3、docker安裝

3.1、前提條件

  • centos安裝docker需要6.5版本以上
  • centos僅發行版中的內容支持,centos7要求64位,內核版本3.10以上,centos6.5要求64位,內核版本3.6.32-431以上
  • uname -r 查看內核版本
  • cat /etc/redhat-release 查看系統

3.2、docker架構

  • 說明

    • docker的客戶端都是和docker的守護進程進行交互
    • Docker鏡像(Image)就是一個只讀的模板。鏡像可以用來創建Docker容器,一個鏡像可以創建很多容器
    • Docker利用容器(Container)獨立運行的一個或一組應用。容器是用鏡像創建的運行實例。它可以被啟動、開始、停止、刪除。每個容器都是相互隔離的、保證安全的平台。可以把容器看做是一個簡易版的Linux環境(包括root用戶權限、進程空間、用戶空間和網絡空間等)和運行在其中的應用程序。容器的定義和鏡像幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
    • 倉庫(Repository)是集中存放鏡像文件的場所。倉庫(Repository)和倉庫註冊服務器(Registry)是有區別的。倉庫註冊服務器上往往存放着多個倉庫,每個倉庫中又包含了多個鏡像,每個鏡像有不同的標籤(tag),倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。最大的公開倉庫是 Docker Hub(//hub.docker.com/),存放了數量龐大的鏡像供用戶下載。國內的公開倉庫包括阿里雲 、網易雲等
    • 通俗理解:鏡像就是類,容器就是對象,一個類可以創建多個對象,互不影響,倉庫和maven一樣,存放鏡像的地方

3.3、docker安裝

  • docker的安裝參考官網,有兩種方式,自動安裝手動安裝
卸載
  • sudo yum remove docker
    docker-client
    docker-client-latest
    docker-common
    docker-latest
    docker-latest-logrotate
    docker-logrotate
    docker-engine
自動安裝
  • yum install docker
  • 驗證安裝是否成功:docker version
手動安裝
  • 1.安裝gcc相關:yum -y install gcc,yum -y install gcc-c++

  • 2.安裝所需的軟件包yum-utils提供了yum-config-manager ,並且device mapper 存儲驅動程序需要device-mapper-persistent-data和lvm2

    • sudo yum install -y yum-utils device-mapper-persistent-data lvm2
  • 3.設置倉庫(注意選擇國內,國外很慢)sudo yum-config-manager –add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

  • 4.安裝最新版本的Docker Engine-Community和containerd,或者安裝特定版本

    • sudo yum install docker-ce docker-ce-cli containerd.io
      • centos8安裝報錯Problem: package docker-ce-3:19.03.14-3.el7.x86_64 requires containerd.io >= 1.2.2-3, but none of the providers can be installed解決方式
    • 5.啟動docker:sudo systemctl start docker
    • 6.運行Hello world:sudo docker run hello-world 本地找不見不自動去庫中下載
    • 7.配置阿里雲鏡像加速器

      • 獲取加速器鏈接地址://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
      • 創建目錄:mkdir -p /etc/docker
      • 創建配置文件:vim /etc/docker/daemon.json 並寫入{“registry-mirrors”: [“//{自已的編碼}.mirror.aliyuncs.com”]}
      • 重啟docker:sudo systemctl daemon-reload,sudo systemctl restart docker
開機自啟
  • systemctl start docker.service
  • systemctl enable docker.service
  • systemctl grep docker查看docker進程的狀態

4、docker原理

  • Docker是一個Client-Server結構的系統,Docker守護進程運行在主機上, 然後通過Socket連接從客戶端訪問,守護進程從客戶端接受命令並管理運行在主機上的容器。 容器,是一個運行時環境,就是我們前面說到的集裝箱。
  • docker有着比虛擬機更少的抽象層。由亍docker不需要Hypervisor實現硬件資源虛擬化,運行在docker容器上的程序直接使用的都是實際物理機的硬件資源。因此在CPU、內存利用率上docker將會在效率上有明顯優勢。
  • docker利用的是宿主機的內核,而不需要Guest OS。因此,當新建一個容器時,docker不需要和虛擬機一樣重新加載一個操作系統內核。仍而避免引尋、加載操作系統內核返個比較費時費資源的過程,當新建一個虛擬機時,虛擬機軟件需要加載Guest OS,返個新建過程是分鐘級別的。而docker由於直接利用宿主機的操作系統,則省略了返個過程,因此新建一個docker容器只需要幾秒鐘。

二、docker常用命令

1、幫助命令

  • 查看版本:docker version
  • 查看詳細信息:docker info
  • 幫助 docker –help

2、鏡像命令

2.1、列出本地主機上的鏡像

  • docker images
    • REPOSITORY:表示鏡像的倉庫源
    • TAG:鏡像的標籤 同一倉庫源可以有多個TAG,代表這個倉庫源的不同個版本,我們使用REPOSITORY:TAG來定義不同的鏡像。如果你不指定一個鏡像的版本,你只寫ubuntu,docker 將默認使用ubuntu:latest 鏡像
    • IMAGE ID:鏡像ID
    • CREATED:鏡像創建時間
    • SIZE:鏡像大小
  • 參數
    • -a:列出本地所有鏡像(包含中間映像層)
    • -q:只顯示鏡像id
    • –digests:只顯示摘要信息
    • –no-trunc:顯示完整的鏡像信息(列出完整的id)
  • 常用
    • docker images -qa

2.2、在庫中搜索鏡像

  • docker search 鏡像名
  • 參數
    • –no-trunc:顯示完整的鏡像信息
    • -s:列出收藏數不小於指定值的鏡像
    • –automated:只列出automated build類型的鏡像(自己構建的)

2.3、拉取指定鏡像

  • docker pull 鏡像名:TAG,不寫TAG默認latest

2.4、刪除指定鏡像

  • docker rmi -f 鏡像名或id
  • 常用
    • 批量刪除:docker rmi -f 鏡像名1:TAG 鏡像名2:TAG
    • 刪除全部:docker rmi -f $(docker images -qa)

3、容器命令

3.1、新建並啟動容器

  • docker run [options] 鏡像 [command] [arg…]
  • 參數
    • –name=”容器新名字”: 為容器指定一個名稱,不寫隨機分配
    • -d: 後台運行容器,並返回容器ID,也即啟動守護式容器
    • -i:以交互模式運行容器,通常與-t同時使用
    • -t:為容器重新分配一個偽輸入終端,通常與-i同時使用
    • -P: 隨機端口映射
    • -p: 指定端口映射,有以下四種格式
      • ip:hostPort:containerPort
      • ip::containerPort
      • hostPort:containerPort
      • containerPort
  • 注意:如果啟動一個守護式容器,沒有前台(終端也算)進行,啥也不做,剛啟動就會被自動關閉

3.2、列出當前正在運行的容器

  • docker ps [option]
  • 參數
    • -a:列出當前所有正在運行的容器+歷史上運行過的
    • -l:顯示最近創建的容器
    • -n:顯示最近n個創建的容器 例如-n 10
    • -q:靜默模式,只顯示容器編號
    • –no-trunc :不截斷輸出(完整id)

3.3、退出容器

  • exit:退出並停止容器
  • ctrl+P+Q:退出不停止退出(推薦)

3.4、啟動容器

  • docker start 容器id或容器名

3.5、重啟容器

  • docker restart 容器id或容器名

3.6、停止容器

  • 停止:docker stop 容器id或容器名
  • 強制停止:docker kill 容器id或容器名

3.7、刪除已經停止的

  • 刪除一個:docker rm 容器id
  • 刪除所有:docker rm -f $(docker ps -a -q)或者docker ps -a -q | xargs docker rm(linux管道的形式,上一個命令的返回值作為下一個命令的輸入)

3.8、啟動守護式容器

  • docker run -d 容器名(例如:docker run -d centos)返回一個容器id
  • 然後docker ps -a 進行查看, 會發現容器已經退出
    • 很重要的要說明的一點: Docker容器後台運行,就必須有一個前台進程,容器運行的命令如果不是那些一直掛起的命令(比如運行top,tail),就是會自動退出的
    • 這個是docker的機制問題,比如你的web容器,我們以nginx為例,正常情況下,我們配置啟動服務只需要啟動響應的service即可。例如service nginx start但是,這樣做,nginx為後台進程模式運行,就導致docker前台沒有運行的應用,這樣的容器後台啟動後,會立即自殺因為他覺得他沒事可做了.所以,最佳的解決方案是,將你要運行的程序以前台進程的形式運行

3.9、查看容器日誌

  • docker logs [option] 容器id (也就是容器內部終端顯示的內容) 按ctrl+c可以退出
  • 參數
    • -t:是加入時間戳
    • -f:跟隨最新的日誌打印
    • –tail 數字:顯示最後多少條
  • 例如在容器內運行shell腳本:docker run -d centos /bin/sh -c “while true;do echo hello zzyy;sleep 2;done”

3.10、查看容器內部運行的進程

  • docker top 容器id

3.11、查看容器內部細節

  • docker inspect 容器id

3.12、與容器交互

  • docker attach 容器id
    • 直接進入容器啟動命令終端,不會創建新的進程(容器裏面原來存在的終端),若使用的exit退出到宿主機,會停止容器
  • docker exec -it 容器id shell命令(必須)
    • 是在容器中打開新的終端,並且可以啟動新的進程,使用exit退出到宿主機,容器也不會停止
    • 例如:docker exec -it 容器id /bin/bash——>進入容器打開一個新的終端
    • 例如:docker exec -it 容器id ls——>返回進入容器默認目錄的文件列表(隔空取物:不需要進入容器)
    • 例如:docker exec -it 容器id ls /tmp——>返回容器tmp目錄的文件列表(隔空取物:不需要進入容器)

3.13、拷貝文件

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

三、鏡像原理

1、鏡像結構

  • 鏡像是一種輕量級、可執行的獨立軟件包,用來打包軟件運行環境和基於運行環境開發的軟件,它包含運行某個軟件所需的所有內容,包括代碼、運行時、庫、環境變量和配置文件
  • UnionFS(聯合文件系統)
    • Union文件系統(UnionFS)是一種分層、輕量級並且高性能的文件系統,它支持對文件系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)。Union文件系統是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中的tomcat很大,幾百兆?
      • 因為tomcat鏡像中還包含了所依賴的其他庫文件,kernel內核、centos、jdk8、tomcat,所以可以說一個鏡像像一個微型的linux系統,包含了一個應用的運行環境,多層文件進行疊加,最終暴露出一個鏡像文件
      • 下載時分層下載
  • 分層的優點
    • 最大的一個好處就是 – 共享資源
    • 比如:有多個鏡像都從相同的 base 鏡像構建而來,那麼宿主機只需在磁盤上保存一份base鏡像,同時內存中也只需加載一份 base 鏡像,就可以為所有容器服務了。而且鏡像的每一層都可以被共享
  • 結構特點
    • docker鏡像都是只讀的
    • 當容器啟動時,一個新的可寫層被加載到鏡像的頂部,這一層通常被稱作容器層,容器層之下的都叫鏡像層

2、docker commit

  • 將一個容器提交成一個新的容器
  • docker commit -m=”提交的描述信息” -a=”作者” 容器id 要創建的目標鏡像名:[TAG] 不寫默認latest
  • 案例
    • 拉取tomcat8.5.31鏡像,運行後刪除webapps目錄下的doc目錄(rm -rf 目錄名)(其他版本的tomcat的webapps目錄下為空,啟動之後直接訪問報404是正常的)
    • 將該容器commit提交成一個新的鏡像
    • 使用新鏡像運行一個容器,點擊document是否是404

四、容器數據卷

1、概述

  • 遇到的問題
    • 將運用與運行的環境打包形成容器運行 ,運行可以伴隨着容器,數據不能持久化,容器之間不能共享數據
    • 持久化傳統方式:通過docker commit生成新的鏡像,使得數據做為鏡像的一部分保存下來
  • 解決方式
    • 容器數據卷:有點類似rdb和aof,持久化容器中的數據和容器間繼承+共享數據

2、數據卷

  • 解決容器中數據的持久化

  • 解決方式:將容器中重要的數據保存到宿主機上

  • 方式一:啟動容器時指定映射關係

    • 命令:docker run -it -v /宿主機目錄:/容器內目錄 鏡像 /bin/bash (-v:指定映射關係,可以有多個,如果目錄不存在,會創建目錄)

    • 查看是否成功:docker inspect 容器id (RW:代表容器對該目錄可讀可寫,Source為主機目錄,Destination為容器目錄)

      • “Mounts”: [
        {
        “Type”: “bind”,
        “Source”: “/tmp/hosttest”,
        “Destination”: “/tmp/containertest”,
        “Mode”: “”,
        “RW”: true,
        “Propagation”: “rprivate”
        }
        ],
    • 測試

      • 測試1:兩邊都修改,看是否生效

        • 1.主機寫入文件
          [root@localhost hosttest]# echo "I'm host" > a.txt
          [root@localhost hosttest]# cat a.txt
          I'm host
          2.容器修改文件
          [root@51037b519837 containertest]# vi a.txt
          [root@51037b519837 containertest]# cat a.txt
          I'm host
          container update
          3.主機查看文件
          [root@localhost hosttest]# cat a.txt
          I'm host
          container update
          
          
      • 測試2:關閉容器,主機修改文件,看是否生效

        • 1.關閉容器
          [root@localhost hosttest]# docker kill 51037b519837
          51037b519837
          2.主機上修改文件
          [root@localhost hosttest]# vim a.txt
          [root@localhost hosttest]# cat a.txt
          I'm host
          container update
          host update
          3.啟動容器
          [root@localhost hosttest]# docker start 51037b519837
          51037b519837
          4.查看文件
          [root@localhost hosttest]# docker exec -it 51037b519837 /bin/bash
          [root@51037b519837 /]# cd /tmp/containertest/
          [root@51037b519837 containertest]# cat a.txt
          I'm host
          container update
          host update
          
      • 結論:不管容器是否關閉,主機修改文件,容器啟動後都會更新,相當於java中兩個引用指向的是同一個內存地址

    • 帶權限的命令:docker run -it -v /宿主機目錄:/容器內目錄:ro 鏡像 /bin/bash (-v:指定映射關係,可以有多個,如果目錄不存在,會創建目錄;ro:代表read only只讀,容器中不可修改

      • 1.創建容器
        [root@localhost hosttest]# docker run -it -v /tmp/hosttestro:/tmp/containertestro:ro --name ctro centos
        [root@9d0eef947060 /]# 
        2.查看是否成功 rw已經變成了false
        [root@localhost hosttest]# docker inspect 9d0eef947060
        "Mounts": [
                    {
                        "Type": "bind",
                        "Source": "/tmp/hosttestro",
                        "Destination": "/tmp/containertestro",
                        "Mode": "ro",
                        "RW": false,
                        "Propagation": "rprivate"
                    }
                ],
        3.在host上新建文件並寫入
        [root@localhost hosttestro]# echo "I'm read only" > rotest.txt
        [root@localhost hosttestro]# cat rotest.txt
        I'm read only
        4.進入容器修改
        [root@localhost hosttestro]# docker exec -it 9d0eef947060 /bin/bash
        [root@9d0eef947060 /]# cd tmp/containertestro/
        [root@9d0eef947060 containertestro]# ls
        rotest.txt
        [root@9d0eef947060 containertestro]# vi rotest.txt
        5.發現vi不能編輯該文件
        -- INSERT -- W10: Warning: Changing a readonly file
        6.也不能自己新建文件
        [root@9d0eef947060 containertestro]# echo "I'm container" > aa.txt
        bash: aa.txt: Read-only file system
        
        
  • 方式二:使用DockerFile在構建鏡像時指定

    • DockerFile指令:VOLUME[“容器目錄1″,”容器目錄2″,”容器目錄3”,…]

      • 出於可移植和分享的考慮,用-v 主機目錄:容器目錄這種方法不能夠直接在Dockerfile中實現。由於宿主機目錄是依賴於特定宿主機的,並不能夠保證在所有的宿主機上都存在這樣的特定目錄
    • 創建DockerFile文件,並寫入內容

      • [root@localhost mydocker]# vim dockerfile
        [root@localhost mydocker]# cat dockerfile
        # volume test
        
        FROM centos
        
        VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"]
        
        CMD echo "finished,--------success1"
        
        CMD /bin/bash
        [root@localhost mydocker]#
        
        
    • 命令構建鏡像

      • docker build -f /test/mydocker/dockerfile -t ma/centos:1.0 . (-f:指定dockfile文件路徑;.:最後的點不能省略,指定的是構建的上下文環境)

      • [root@localhost mydocker]# docker images
        REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
        ma/centos           1.0                 6c074087077c        37 seconds ago      209MB
        mytomcat            1.0                 ca4cffd1de8f        15 hours ago        463MB
        centos              latest              300e315adb2f        26 hours ago        209MB
        tomcat              8.5.31              df50c9d355cf        2 years ago         463MB
        
        
    • 啟動鏡像並查看數據卷在主機的位置

      • [root@localhost mydocker]# docker run -it ma/centos:1.0
        [root@c86811d3480e /]# [root@localhost mydocker]#
        [root@localhost mydocker]# docker inspect c86811d3480e
         "Mounts": [
                    {
                        "Type": "volume",
                        "Name": "bf9c3b442d1f754f6a80c3496a8f251b31e6ae962871c123f8142a253cc1904c",
                        "Source": "/var/lib/docker/volumes/bf9c3b442d1f754f6a80c3496a8f251b31e6ae962871c123f8142a253cc1904c/_data",
                        "Destination": "/dataVolumeContainer1",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    },
                    {
                        "Type": "volume",
                        "Name": "2e26e661586d11af53ab8d322016458e392c2e1aa4d9727d84b11a3da1e03f99",
                        "Source": "/var/lib/docker/volumes/2e26e661586d11af53ab8d322016458e392c2e1aa4d9727d84b11a3da1e03f99/_data",
                        "Destination": "/dataVolumeContainer2",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    }
                ],
        
        
    • Docker掛載主機目錄Docker訪問出現cannot open directory .: Permission denied 解決辦法:在掛載目錄後多加一個–privileged=true參數即可

3、數據卷容器

  • 命名的容器掛載數據卷,其它容器通過掛載這個(父容器)實現數據共享,掛載數據卷的容器,稱之為數據卷容器

  • 實現方式

    • 啟動一個父容器

      • docker run -it –name dc1 ma/centos:1.0
    • 掛載兩個子容器 使用–volumes-from參數

      • docker run -it –name dc2 –volumes-from dc1 ma/centos:1.0
      • docker run -it –name dc3 –volumes-from dc1 ma/centos:1.0
    • 測試

      • 在dc1中添加文件,dc2,dc3也添加
      • 刪除dc1,修改dc2中文件,dc3也修改了
      • 刪除dc2,dc3仍舊可以訪問
      • 新建dc4繼承dc3,刪除dc3,dc4仍舊可以訪問
    • 容器之間配置信息的傳遞,數據卷的生命周期一直持續到沒有容器使用它為止

    • 本質原因:配置信息的傳遞

      • 1.創建dc1,dc2、dc3、dc4容器卷繼承dc1,dc4使用原始鏡像,不包含VOLUME指令
        docker run -it --name dc1 ma/centos:1.0
        docker run -it --name dc2 --volumes-from dc1 ma/centos:1.0
        docker run -it --name dc3 --volumes-from dc1 ma/centos:1.0
        docker run -it --name dc4 --volumes-from dc1 centos (dc4使用原始鏡像,不包含VOLUME指令)
        2.根據容器id查看各自的信息
        [root@localhost _data]# docker inspect dc1
        "Mounts": [
                    {
                        "Type": "volume",
                        "Name": "ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0",
                        "Source": "/var/lib/docker/volumes/ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0/_data",
                        "Destination": "/dataVolumeContainer1",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    },
                    {
                        "Type": "volume",
                        "Name": "160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892",
                        "Source": "/var/lib/docker/volumes/160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892/_data",
                        "Destination": "/dataVolumeContainer2",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    }
                ],
        [root@localhost _data]# docker inspect dc2
        "Mounts": [
                    {
                        "Type": "volume",
                        "Name": "160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892",
                        "Source": "/var/lib/docker/volumes/160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892/_data",
                        "Destination": "/dataVolumeContainer2",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    },
                    {
                        "Type": "volume",
                        "Name": "ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0",
                        "Source": "/var/lib/docker/volumes/ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0/_data",
                        "Destination": "/dataVolumeContainer1",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    }
                ],
        
        [root@localhost _data]# docker inspect dc3
        "Mounts": [
                    {
                        "Type": "volume",
                        "Name": "ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0",
                        "Source": "/var/lib/docker/volumes/ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0/_data",
                        "Destination": "/dataVolumeContainer1",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    },
                    {
                        "Type": "volume",
                        "Name": "160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892",
                        "Source": "/var/lib/docker/volumes/160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892/_data",
                        "Destination": "/dataVolumeContainer2",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    }
                ],
        [root@localhost _data]# docker inspect dc4
        "Mounts": [
                    {
                        "Type": "volume",
                        "Name": "ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0",
                        "Source": "/var/lib/docker/volumes/ce66ad2a242a771b9735717ec305521dfcbad324adbee58bd6dea2795fc1b4f0/_data",
                        "Destination": "/dataVolumeContainer1",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    },
                    {
                        "Type": "volume",
                        "Name": "160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892",
                        "Source": "/var/lib/docker/volumes/160ec2ca48392b73c53c2dbe709d414c81ca6be1dfeb85d70e4f7dc37c3fc892/_data",
                        "Destination": "/dataVolumeContainer2",
                        "Driver": "local",
                        "Mode": "",
                        "RW": true,
                        "Propagation": ""
                    }
                ],
        
        
      • 發現dc1,dc2,dc3,dc4宿主機的容器卷是同一個位置,所以才會達到各個容器共享,即使dc4的鏡像本身沒有指定容器卷,也會通過繼承的形式將容器卷從dc1上繼承過來

五、Dockerfile

1、概述

  • Dockfile用來構建鏡像文件,由一系列命令和參數構成的腳本
  • 構建三步驟
    • 編寫dockerfile
    • docker build
    • docker run
  • 基本規則
    • 每個保留字指令都要大寫並且後面至少跟一個參數
    • 指令從上到下順序執行
    • ‘#’表示注釋
    • 每條指令都會創建一個新的鏡像層,並對鏡像提交
  • 執行流程
    • docker從基礎鏡像運行一個容器
    • 執行一條指令對容器進行修改
    • 執行類似docker commit 的操作提交一個新的鏡像層
    • docker再基於剛才提交新的鏡像啟動一個新容器
    • 執行dockerfile的下一條指令直到所有指令執行完成
  • 從應用軟件的角度來看,Dockerfile、Docker鏡像與Docker容器分別代表軟件的三個不同階段,
    • Dockerfile是軟件的原材料
      • 需要定義一個Dockerfile,Dockerfile定義了進程需要的一切東西。Dockerfile涉及的內容包括執行代碼或者是文件、環境變量、依賴包、運行時環境、動態鏈接庫、操作系統的發行版、服務進程和內核進程(當應用進程需要和系統服務和內核進程打交道,這時需要考慮如何設計namespace的權限控制)等等;
    • Docker鏡像是軟件的交付品
      • 在用Dockerfile定義一個文件之後,docker build時會產生一個Docker鏡像,當運行 Docker鏡像時,會真正開始提供服務
    • Docker容器則可以認為是軟件的運行態
      • 容器是直接提供服務的
    • Dockerfile面向開發,Docker鏡像成為交付標準,Docker容器則涉及部署與運維,三者缺一不可,合力充當Docker體系的基石

2、dockerfile指令

  • FROM:指定基礎鏡像,DockerHub中99%的鏡像通過scratch鏡像構建出來的
  • MAINTAINER:指定作者和郵箱 例如:作者名<郵箱>
  • RUN:容器構建時運行的命令
  • EXPOSE:當前容器對外暴露的端口
  • WORKDIR:在創建容器後,終端默認登錄進來的工作目錄,一個落腳點
  • ENV:用來在構建鏡像過程中設置環境變量,例如:ENV MY_PATH /tmp 其他地方使用$MY_PATH
  • ADD:將主機的文件並解壓,自動處理URL和tar
  • COPY:類似ADD,但不解壓 源路徑(宿主機內)/目標路徑(容器內)
  • VOLUME:指定容器數據卷
  • CMD:容器啟動是要運行的命令
    • dockerfile可以有多個CMD,但只會有最後一個生效,會覆蓋,如果在啟動容器時指定了CMD,則會執行指定的CMD
    • 支持的格式
      • shell格式:CMD <命令>
      • exec格式:CMD[“可執行文件”,”參數1″,”參數2″…]
  • ENTRYPOINT:指定容器啟動要運行的命令和CMD作用一樣,但是不會覆蓋,會追加,如果在啟動容器時指定了CMD,追加指定的CMD 指定了ENTRYPOINT 就不會執行CMD
  • ONBUILD:當構建一個被繼承dockfile是運行命令,當子鏡像被build時會觸發父鏡像的ONBUILD指令

3、dockerfile案例

3.1、自定義mycentos

  • 編寫dockerfile

    • # 指定基礎鏡像
      FROM centos
      # 設置作者郵箱
      MAINTAINER ms<[email protected]>
      # 設置環境變量
      ENV MYPATH /use/local
      # 設置落腳點
      WORKDIR $MYPATH
      # 安裝vim 和 ifconfig等命令 多個RUN 可以用&&連接只創建一個鏡像層,否則一個RUN一個鏡像層
      # RUN yum -y install vim
      # RUN yum -y install net-tools
      RUN yum -y install vim && yum -y install net-tools
      # 向外的暴露端口
      EXPOSE 80
      # 容器啟動時執行的命令 只有最後一個生效
      CMD echo $MYPATH
      CMD echo "success"
      CMD /bin/bash
      
  • 根據dockerfile構建鏡像

    • 命令:docker build -f dockerfile路徑 -t 鏡像名:版本 .
      • 注意:-t 需要在 -f 之後,應該只能是最後一個參數
      • -f:指定dockerfile路徑,不寫默認當前路徑尋找Dockerfile
      • .:最後一個點代表了上下文路徑
        • 上下文路徑,是指 docker 在構建鏡像,有時候想要使用到本機的文件(比如複製),docker build 命令得知這個路徑後,會將路徑下的所有內容打包
        • 解析:由於 docker 的運行模式是 C/S。我們本機是 C,docker 引擎是 S。實際的構建過程是在 docker 引擎下完成的,所以這個時候無法用到我們本機的文件。這就需要把我們本機的指定目錄下的文件一起打包提供給 docker 引擎使用
        • 如果未說明最後一個參數,那麼默認上下文路徑就是 Dockerfile 所在的位置
        • 注意:上下文路徑下不要放無用的文件,因為會一起打包發送給 docker 引擎,如果文件過多會造成過程緩慢
    • 執行過程中報錯Errors during downloading metadata for repository ‘appstream’:的解決方式
      • wget -O /etc/yum.repos.d/CentOS-Base.repo //mirrors.aliyun.com/repo/Centos-8.repo
      • 清除緩存:yum clean all
      • 安裝新依賴(緩存):yum makecache
      • 重啟docker(很重要):systemctl restart docker
      • 重新構建即可
  • 列出鏡像的變更歷史:docker history 鏡像

3.2、CMD和ENTRYPOINT指令區別

CMD指令
  • 為啟動的容器指定默認要運行的程序,程序運行結束,容器也就結束,Dockerfile 中如果存在多個 CMD 指令,僅最後一個生效,CMD 指令指定的程序可被 docker run 命令行參數中指定要運行的程序所覆蓋
  • 格式
    • CMD <shell 命令>
    • CMD [“<可執行文件或命令>”,”“,”“,…]
    • CMD [““,”“,…] # 該寫法是為 ENTRYPOINT 指令指定的程序提供默認參數
    • 推薦使用第二種格式,執行過程比較明確。第一種格式實際上在運行的過程中也會自動轉換成第二種格式運行,並且默認可執行文件是 sh
ENTRYPOINT指令
  • 編寫dockerfile

    • # 1.實際執行的是 curl -s //ip.cn      curl命令可以下載網頁的html -i參數下載頭信息
      FROM centos
      CMD echo "success"
      ENTRYPOINT ["curl","-s","//ip.cn"]
      # 即使啟動時攜帶了CMD命令 還是會執行ENTRYPOINT
      
      # 2.實際執行的是 curl -s //ip.cn 與CMD和ENTRYPOINT的順序無關
      FROM centos
      ENTRYPOINT ["curl","-s","//ip.cn"]
      CMD echo "success"
      
      # 3.實際執行的是 curl //ip.cn 只有ENTRYPOINT後面最後一個CMD才能拼接到ENTRYPOINT指令上(後面有多個就覆蓋) 前面的不行
      FROM centos
      CMD ["-i"]
      ENTRYPOINT ["curl"]
      CMD ["-s"]
      CMD ["//ip.cn"]
      # 啟動時攜帶了CMD命令 例如有 -i 實際執行的是 curl -i 就會報錯(run 攜帶的參數也是一個CMD命令)
      
      # 4.實際執行的是curl -s //ip.cn 如果有多個ENTRYPOINT 也只有最後一個生效
      FROM centos
      CMD ["-i"]
      ENTRYPOINT ["curl"]
      CMD ["-s"]
      CMD ["//ip.cn"]
      ENTRYPOINT ["curl","-s","//ip.cn"]
      # 啟動時攜帶了CMD命令 例如有 -i 就會拼接 實際執行的是curl -s //ip.cn -i
      
      # 5.如果ENTRYPOINT沒有使用[] 形式 則不會追加 實際執行的是curl -s //ip.cn
      FROM centos
      ENTRYPOINT curl -s //ip.cn
      CMD ["-i"]
      
  • 注意:如果有ENTRYPOINT存在,容器啟動之後會優先執行ENTRYPOINT,不會執行CMD,如果沒有ENTRYPOINT,則會執行最後一個CMD,與CMD和ENTRYPOINT的順序無關

4、自定義tomcat

  • 準備工作

    • 在目錄下創建a.txt(用於測試COPY)
    • 上傳apache-tomcat-8.5.61.tar.gz至該目錄
    • 上傳jdk-8u11-linux-x64.tar.gz至該目錄
  • 編寫dockerfile

    • FROM centos
      MAINTAINER ms<[email protected]>
      # 把宿主機當前上下文的c.txt拷貝到容器/usr/local/路徑下
      COPY a.txt /usr/local/cincontainer.txt
      # 把java與tomcat添加到容器中並解壓 具體解壓出來的目錄是什麼樣子 要看tar.gz內部
      ADD jdk-8u11-linux-x64.tar.gz /usr/local/
      ADD apache-tomcat-8.5.61.tar.gz /usr/local/
      # 安裝vim編輯器
      RUN yum -y install vim
      # 設置工作訪問時候的WORKDIR路徑,登錄落腳點
      ENV MYPATH /usr/local
      WORKDIR $MYPATH
      # 配置java與tomcat環境變量 具體參照linux下配置 具體解壓出來的目錄名字 要看tar.gz內部
      ENV JAVA_HOME /usr/local/jdk1.8.0_11
      ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
      ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.61
      ENV CATALINA_BASE /usr/local/apache-tomcat-8.5.61
      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-8.5.61/bin/startup.sh && tail -F /usr/local/apache-tomcat-8.5.61/logs/catalina.out
       
      
  • 構建鏡像

    • docker build -f /test/mydocker/mytomcat8/Dockerfile -t mytomcat8:1.0 .
  • 啟動容器並創建數據卷 –privileged=true 解決 Docker掛載主機目錄Docker訪問出現cannot open directory .: Permission denied

    • docker run -it -P -v /test/mydocker/mytomcat8/tomcatwebapps:/usr/local/apache-tomcat-8.5.61/webapps -v /test/mydocker/mytomcat8/tomcatlog:/usr/local/apache-tomcat-8.5.61/logs –privileged=true mytomcat8:1.0
  • 通過數據卷在宿主機中就可以修改容器中tomcat中的內容

5、ONBUILD指令

  • ONBUILD指令用於子鏡像被build時,父鏡像觸發

  • 編寫Dockerfile並構建鏡像運行

    • # 1.父鏡像
      FROM centos
      ONBUILD RUN echo "I'm parent-image"
      # docker build -f /test/mydocker/parent-children/parentfile -t parent-image .
      # 2.子鏡像
      FROM parent-image
      CMD echo "I'm child-image"
      # docker build -f /test/mydocker/parent-children/childfile -t child-image .
      
  • 構建子鏡像時查看結果

    • [root@localhost parent-children]# docker build -f /test/mydocker/parent-children/childfile -t child-image .
      Sending build context to Docker daemon  3.072kB
      Step 1/2 : FROM parent-image
      
      此處說明了執行了一個觸發器 也就是父鏡像中ONBUILD指令
      # Executing 1 build trigger
       ---> Running in 49d2db5530c2
      I'm parent-image
      
      Removing intermediate container 49d2db5530c2
       ---> 184b598876a3
      Step 2/2 : CMD echo "I'm child-image"
       ---> Running in 6b6359d232c3
      Removing intermediate container 6b6359d232c3
       ---> ca71e2df6c07
      Successfully built ca71e2df6c07
      Successfully tagged child-image:latest
      

六、常用軟件的安裝

  • docker軟件安裝流程
    • 搜索鏡像
    • 拉取鏡像
    • 查看鏡像
    • 啟動容器
    • 停止容器
    • 移除容器

1、安裝tomcat

  • 上面已經安裝過,此處省略

2、安裝mysql

  • 拉取mysql5.7.20鏡像
  • 啟動容器
    • docker run -p 12345:3306 –name mysql -v /test/mysql/conf:/etc/mysql/mysql.conf.d -v /test/mysql/logs:/logs -v /test/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7.20
    • 命令說明:
      • -p 12345:3306:將主機的12345端口映射到docker容器的3306端口。
      • –name mysql:運行容器的名字
      • -v /test/mysql/conf:/etc/mysql/mysql.conf.d :將主機/test/mysql/conf目錄掛載到容器的/etc/mysql/conf.d或者mysql.conf.d(這兩個目錄根據版本的不同而不同,裏面的mysqld.cnf為mysql的配置文件)
      • -v /test/mysql/logs:/logs:將主機/test/mysql目錄下的logs目錄掛載到容器的/logs。
      • -v /test/mysql/data:/var/lib/mysql :將主機/test/mysql目錄下的data目錄掛載到容器的/var/lib/mysql 也就是mysql的data目錄
      • -e MYSQL_ROOT_PASSWORD=123456:初始化root用戶的密碼。
      • -d mysql:5.7.20 : 後台程序運行mysql:5.7.20
  • 在容器外面連接即可

3、安裝redis

  • 拉取redis3.2鏡像
  • 編寫配置文件並放入自己創建的目錄下(啟動時需要對應數據卷)
    • 在該目錄/test/redis/conf下創建redis.conf
    • 將官網上的配置文件內容寫入,將bind注釋掉
    • 在docker中啟動redis一定要把:daemonize 設置為 no,這個很重要,如果不是no docker會一直啟動失敗,原因是docker本身需要後台運行,而這個配置選項也是以守護進程啟動,兩者會衝突
      • 不設置也可以
  • 啟動容器
    • docker run -p 6379:6379 -v /test/redis/data:/data -v /test/redis/conf:/usr/local/etc/redis/conf -d redis:3.2 redis-server /usr/local/etc/redis/conf –appendonly yes
    • 命令說明
      • -v /test/redis/data:/data :持久化文件存儲的地方
      • -v /test/redis/conf:/usr/local/etc/redis/conf :配置文件存放的地方
      • redis-server /usr/local/etc/redis/conf :指定配置文件啟動
      • –appendonly yes :開啟持久化
  • 在容器外面連接即可

七、將鏡像推至阿里雲

  • 推送拉取流程

  • 在阿里雲鏡像倉庫創建倉庫

    • 以下具體地址在阿里雲鏡像倉庫查看
  • 在linux中登錄阿里雲鏡像倉庫

    • sudo docker login –username=xxxxxx registry.cn-hangzhou.aliyuncs.com
  • 在linux推送鏡像至倉庫

    • sudo docker tag [ImageId] 倉庫地址:[鏡像版本號]
    • sudo docker push 倉庫地址:[鏡像版本號]
    • 注意:這兩個[鏡像版本號]要保持一致
  • 推送成功後可拉取到本地

    • sudo docker pull 倉庫地址:[鏡像版本號]
Tags: