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一次構建、隨處運行
- 更快速的應用交付和部署
- 更便捷的升級和擴縮容
- 更簡單的系統運維
- 更高效的計算資源利用
- 相關網站
- 官網://www.docker.com
- docker-hub://hub.docker.com/
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解決方式
- 更新yum:yum update
- 安裝依賴環境:yum install -y yum-utils device-mapper-persistent-data lvm2
- 添加docker源:sudo yum-config-manager –add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 安裝新版containerd.io:dnf install //download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.6-3.3.el7.x86_64.rpm
- 再次安裝: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
- 檢測:docker info
Registry Mirrors:
//reg-mirror.qiniu.com
- sudo yum install docker-ce docker-ce-cli containerd.io
開機自啟
- 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”
}
],
- “Mounts”: [
-
測試
-
測試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體系的基石
- Dockerfile是軟件的原材料
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 build -f dockerfile路徑 -t 鏡像名:版本 .
-
列出鏡像的變更歷史: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 倉庫地址:[鏡像版本號]