原生引入了不少新的概念和思維方式,也影響了應用所采用的實現技術。容器是云原生應用的基礎性技術,其顛覆了應用的開發、交付和運行模式,在云計算、互聯網等領域得到了廣泛應用。
本文選自《Harbor權威指南》一書,下面我們一同了解下容器的發展進程及基本原理。
容器技術的發展背景
在電子計算機剛出現時,由于硬件成本高昂,人們試圖尋找能夠多用戶共享計算資源的方式,以提高資源利用率和降低成本。在20世紀60年代,基于硬件技術的主機虛擬化技術出現了。一臺物理主機可以被劃分為若干個小的機器,每個機器的硬件互不共享,并可以安裝各自的操作系統來使用。20世紀90年代后期,X86架構的硬件虛擬化技術逐漸興起,可在同一臺物理機上隔離多個操作系統實例,帶來了很多的優點,目前絕大多數的數據中心都采用了硬件虛擬化技術。
雖然硬件虛擬化提供了分隔資源的能力,但是采用虛擬機方式隔離應用程序時,效率往往較低,畢竟還要在每個虛擬機中安裝或復制一個操作系統實例,然后把應用部署到其中。因此人們探索出一種更輕量的方案——操作系統虛擬化,使面向應用的管理更便捷。所謂操作系統虛擬化,就是由操作系統創建虛擬的系統環境,使應用感知不到其他應用的存在,仿佛在獨自占有全部的系統資源,從而實現應用隔離的目的。在這種方式中不需要虛擬機,也能夠實現應用彼此隔離,由于應用是共享同一個操作系統實例的,因此比虛擬機更節省資源,性能更好。操作系統虛擬化在不少系統里面也被稱為容器(Container),下面也會以容器來指代操作系統虛擬化。
操作系統虛擬化最早出現在2000年,FreeBSD 4.0推出了Jail。Jail加強和改進了用于文件系統隔離的chroot環境。到了2004年,Sun公司發布了Solaris 10的Containers,包括Zones和Resource management兩部分。Zones實現了命名空間隔離和安全訪問控制,Resource management實現了資源分配控制。2007年,Control Groups(簡稱cgroups)進入Linux內核,可以限定和隔離一組進程所使用的資源(包括CPU、內存、I/O和網絡等)。
2013年,Docker公司發布Docker開源項目,提供了一系列簡便的工具鏈來使用容器。毫不夸張地說,Docker公司率先點燃了容器技術的火焰,拉開了云原生應用變革的帷幕,促進容器生態圈一日千里地發展。截至2020年,Docker Hub中的鏡像累計下載了1300億次,用戶創建了約600萬個容器鏡像庫。從這些數據可以看到,用戶正在以驚人的速度從傳統模式切換到基于容器的應用發布和運維模式。
2015年,OCI(Open Container Initiative)作為Linux基金會項目成立,旨在推動開源技術社區制定容器鏡像和運行時規范,使不同廠家的容器解決方案具備互操作能力。同年還成立了CNCF,目的是促進容器技術在云原生領域的應用,降低用戶開發云原生應用的門檻。創始會員包括谷歌、紅帽、Docker、VMware等多家公司和組織。
CNCF成立之初只有一個開源項目,就是后來大名鼎鼎的Kubernetes。Kubernetes是一個容器應用的編排工具,最早由谷歌的團隊研發,后來開源并捐贈給了CNCF成為種子項目。由于Kubernetes是廠家中立的開源項目,開源后得到了社區用戶和開發者的廣泛參與和支持。到了2018年,Kubernetes已成為容器編排領域事實上的標準,并成為首個CNCF的畢業(graduated)項目。2020年8月,CNCF旗下的開源項目增加到了63個,包括原創于中國的Harbor等項目。
從容器的發展歷程可以看到,容器在出現的早期并沒有得到人們的廣泛關注,主要原因是當時開放的云計算環境還沒出現或者未成為主流。2010年之后,隨著IaaS、PaaS和SaaS等云平臺逐漸成熟,用戶對云端應用開發、部署和運維的效率不斷重視,重新發掘了容器的價值,最終促成了容器技術的盛行。
容器的基本原理
下面我們以Linux容器為例,講解容器的實現原理,主要包括命名空間(Namespace)和控制組(cgroups)。
▊ 命名空間
命名空間是Linux操作系統內核的一種資源隔離方式,使不同的進程具有不同的系統視圖。系統視圖就是進程能夠感知到的系統環境,如主機名、文件系統、網絡協議棧、其他用戶和進程等。使用命名空間后,每個進程都具備獨立的系統環境,進程間彼此感覺不到對方的存在,進程之間相互隔離。目前,Linux中的命名空間共有6種,可以嵌套使用。
◎ Mount
隔離了文件系統的掛載點(mount points),處于不同“mount”命名空間中的進程可以看到不同的文件系統。
◎ Network
隔離進程網絡方面的系統資源,包括網絡設備、IPv4和IPv6的協議棧、路由表、防火墻等。
◎ IPC
進程間相互通信的命名空間,不同命名空間中的進程不能通信。
◎ PID
進程號在不同的命名空間中是獨立編號的,不同的命名空間中的進程可以有相同的編號。當然,這些進程在操作系統中的全局(命名空間外)編號是唯一的。
◎ UTS
系統標識符命名空間,在每個命名空間中都可以有不同的主機名和NIS域名。
◎ User
命名空間中的用戶可以有不同于全局的用戶ID和組ID,從而具有不同的特權。
命名空間實現了在同一操作系統中隔離進程的方法,幾乎沒有額外的系統開銷,所以是非常輕量的隔離方式,進程啟動和運行的過程在命名空間中和外面幾乎沒有差別。
▊ 控制組
命名空間實現了進程隔離功能,但由于各個命名空間中的進程仍然共享同樣的系統資源,如CPU、磁盤I/O、內存等,所以如果某個進程長時間占用某些資源,其他命名空間里的進程就會受到影響,這就是“吵鬧的鄰居(noisy neighbors)”現象。因此,命名空間并沒有完全達到進程隔離的目的。為此,Linux內核提供了控制組(Control Groups,cgroups)功能來處理這個問題。
Linux把進程分成控制組,給每組里的進程都設定資源使用規則和限制。在發生資源競爭時,系統會根據每個組的定義,按照比例在控制組之間分配資源。控制組可設定規則的資源包括CPU、內存、磁盤I/O和網絡等。通過這種方式,就不會出現某些進程無限度搶占其他進程資源的情況。
Linux系統通過命名空間設置進程的可見且可用資源,通過控制組規定進程對資源的使用量,這樣隔離進程的虛擬環境(即容器)就建立起來了。
容器運行時
Linux 提供了命名空間和控制組兩大系統功能,它們是容器的基礎。但是,要把進程運行在容器中,還需要有便捷的SDK或命令來調用Linux的系統功能,從而創建出容器。容器的運行時(runtime)就是容器進程運行和管理的工具。
容器運行時分為低層運行時和高層運行時,功能各有側重。低層運行時主要負責運行容器,可在給定的容器文件系統上運行容器的進程;高層運行時則主要為容器準備必要的運行環境,如容器鏡像下載和解壓并轉化為容器所需的文件系統、創建容器的網絡等,然后調用低層運行時啟動容器。主要的容器運行時的關系如下。
▊ OCI運行時規范
成立于2015年的OCI是Linux基金會旗下的合作項目,以開放治理的方式制定操作系統虛擬化(特別是Linux容器)的開放工業標準,主要包括容器鏡像格式和容器運行時(runtime)。初始成員包括Docker、亞馬遜、CoreOS、谷歌、微軟和VMware等公司。OCI成立之初,Docker公司為其捐贈了容器鏡像格式和運行時的草案及相應的實現代碼。原來屬于Docker的libcontainer項目被捐贈給OCI,成為獨立的容器運行時項目runC。
OCI運行時規范定義了容器配置、運行時和生命周期的標準,主流的容器運行時都遵循OCI運行時的規范,從而提高系統的可移植性和互操作性,用戶可根據需要進行選擇。
首先,容器啟動前需要在文件系統中按一定格式存放所需的文件。OCI運行時規范定義了容器文件系統包(filesystem bundle)的標準,在OCI運行時的實現中通常由高層運行時下載OCI鏡像,并將OCI鏡像解壓成OCI運行時文件系統包,然后OCI運行時讀取配置信息和啟動容器里的進程。OCI運行時文件系統包主要包括以下兩部分。
◎ config.json
這是必需的配置文件,存放于文件系統包的根目錄下。OCI運行時規范對Linux、Windows、Solaris和虛擬機4種平臺的運行時做了相應的配置規范。
◎ 容器的根文件系統
容器啟動后進程所使用的根文件系統,由 config.json 中的root.path屬性確定該文件系統的路徑,通常是“rootfs/”。
然后,在定義文件系統包的基礎上,OCI運行時規范制定了運行時和生命周期管理規范。生命周期定義了容器從創建到刪除的全過程,可用以下三條命令說明。
◎“create”命令
在調用該命令時需要用到文件系統包的目錄位置和容器的唯一標識。在創建運行環境時需要使用config.json里面的配置。在創建的過程中,用戶可加入某些事件鉤子(hook)來觸發一些定制化處理,這些事件鉤子包括prestart、createRuntime和createContainer。
◎“start”命令
在調用該命令時需要運行容器的唯一標識。用戶可在 config.json 的process 屬性中指明運行程序的詳細信息。“start”命令包括兩個事件鉤子:startContainer和poststart。
◎“delete”命令
在調用該命令時需要運行容器的唯一標識。在用戶的程序終止后(包括正常和異常退出),容器運行時執行“delete”命令以清除容器的運行環境。“delete”命令有一個事件鉤子:poststop。
-----
除了上述生命周期命令,OCI運行時還必須支持另外兩條命令。
(1)“state”命令
在調用該命令時需要運行容器的唯一標識。該命令查詢某個容器的狀態,必須包括的狀態屬性有ociVersion、id、status、pid和bundle,可選屬性有annotation。不同的運行時實現可能會有一些差異。下面是一個容器狀態的例子:
1{
2 "ociVersion": "1.0.1",
3 "id": "oci-container001",
4 "status": "running",
5 "pid": 8080,
6 "bundle": "/containers/nginx",
7 "annotations": {
8 "key1": "value1"
9 }
10}
(2)“kill”命令
在調用該命令時需要運行容器的唯一標識和信號(signal)編號。該命令給容器進程發送信號,如Linux操作系統的信號9表示立即終止進程。
▊ runC
runC是OCI運行時規范的參考實現,也是最常用的容器運行時,被其他多個項目使用,如containerd和CRI-O等。runC也是低層容器運行時,開發人員可通過runC實現容器的生命周期管理,避免繁瑣的操作系統調用。根據OCI運行時規范,runC不包括容器鏡像的管理功能,它假定容器的文件包已經從鏡像里解壓出來并存放于文件系統中。runC創建的容器需要手動配置網絡才能與其他容器或者網絡節點連通,為此可在容器啟動之前通過OCI定義的事件鉤子來設置網絡。
由于runC提供的功能比較單一,復雜的環境需要更高層的容器運行時來生成,所以runC常常成為其他高層容器運行時的底層實現基礎。
▊ containerd
在OCI成立時,Docker公司把其Docker項目拆分為runC的低層運行時及高層運行時功能。2017年,Docker公司把這部分高層容器運行時的功能集中到containerd項目里,捐贈給云原生計算基金會。
containerd 已經成為多個項目共同使用的高層容器運行時,提供了容器鏡像的下載和解壓等鏡像管理功能,在運行容器時,containerd先把鏡像解壓成OCI的文件系統包,然后調用runC運行容器。containerd提供了API,其他應用程序可以通過API與containerd交互。“ctr”是containerd的命令行工具,和“docker”命令很相像。但作為容器運行時,containerd只注重在容器運行等方面,因而不包含開發者使用的鏡像構建和鏡像上傳鏡像倉庫等功能。
▊ Docker
Docker引擎是最早流行也是最廣泛使用的容器運行時之一,是一個容器管理工具,架構如下圖。
Docker的客戶端(命令行CLI工具)通過API調用容器引擎Docker Daemon(dockerd)的功能,完成各種容器管理任務。
Docker引擎在發布時是一個單體應用,所有功能都集中在一個可執行文件里,后來按功能分拆成runC和containerd兩個不同層次的運行時,分別捐獻給了OCI和CNCF。上面兩節已經分別介紹了runC和containerd的主要特點,剩下的dockerd就是Docker公司維護的容器運行時。
dockerd同時提供了面向開發者和面向運維人員的功能。其中,面向開發者的命令主要提供鏡像管理功能。容器鏡像一般可由Dockerfile構建(build)而來。Dockerfile是一個文本文件,通過一組命令關鍵字定義了容器鏡像所包含的基礎鏡像(base image)、所需的軟件包及有關應用程序。在Dockerfile編寫完成以后,就可以用“docker build”命令構建鏡像了。下面是一個Dockerfile的簡單例子:
1FROM ubuntu:18.04
2EXPOSE 8080
3CMD ["nginx", "-g", "daemon off;"]
容器的鏡像在構建之后被存放在本地鏡像庫里,當需要與其他節點共享鏡像時,可上傳鏡像到鏡像倉庫(Registry)以供其他節點下載。
Docker還提供了容器存儲和網絡映射到宿主機的功能,大部分由containerd實現。應用的數據可以被保存在容器的私有文件系統里面,這部分數據會隨著容器一起被刪除。對需要數據持久化的有狀態應用來說,可用數據卷Volume的方式導入宿主機上的文件目錄到容器中,對該目錄的所有寫操作都將被保存到宿主機的文件系統中。Docker可以把容器內的網絡映射到宿主機的網絡上,并且可以連接外部網絡。
▊ CRI和CRI-O
Kubernetes是當今主流的容器編排平臺,為了適應不同場景的需求,Kubernetes需要有使用不同容器運行時的能力。為此,Kubernetes從1.5版本開始,在kubelet中增加了一個容器運行時接口CRI(Container Runtime Interface),需要接入Kubernetes的容器運行時必須實現CRI接口。由于kubelet的任務是管理本節點的工作負載,需要有鏡像管理和運行容器的能力,因此只有高層容器運行時才適合接入CRI。CRI和容器運行時的關系如下圖。
CRI和容器運行時之間需要有個接口層,通常稱之為shim(墊片),用以匹配相應的容器運行時。CRI接口由shim實現,定義如下,分為RuntimeService和ImageServiceManager(代碼參見GitHub上kubernetes/cri-api的項目文件“pkg/apis/services.go”):
1// RuntimeService接口必須由容器運行時實現
2// 以下方法必須是線程安全的
3type RuntimeService interface {
4RuntimeVersioner
5ContainerManager
6PodSandboxManager
7ContainerStatsManager
8
9// UpdateRuntimeConfig更新運行時配置
10UpdateRuntimeConfig(runtimeConfig *runtimeapi.RuntimeConfig) error
11
12// Status返回運行時的狀態
13Status() (*runtimeapi.RuntimeStatus, error)
14}
15
16// ImageManagerService接口必須由容器管理器實現
17// 以下方法必須是線程安全的
18type ImageManagerService interface {
19// ListImages列出現有鏡像
20ListImages(filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error)
21
22// ImageStatus返回鏡像狀態
23ImageStatus(image *runtimeapi.ImageSpec) (*runtimeapi.Image, error)
24
25// PullImage用認證配置拉取鏡像
26PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi.AuthConfig, podSandboxConfig *runtimeapi.PodSandboxConfig) (string, error)
27
28// RemoveImage刪除鏡像
29RemoveImage(image *runtimeapi.ImageSpec) error
30
31// ImageFsInfo返回存儲鏡像的文件系統信息
32ImageFsInfo() ([]*runtimeapi.FilesystemUsage, error)
33}
Docker運行時被普遍使用,它的CRI shim被稱為dockershim,內置在Kubernetes的kubelet中,由Kubernetes項目組開發和維護。其他運行時則需要提供外置的shim。containerd從1.1版本開始內置了CRI plugin,不再需要外置shim來轉發請求,因此效率更高。在安裝Docker的最新版本時,會自動安裝containerd,所以在一些系統中,Docker和Kubernetes可以同時使用containerd來運行容器,但是二者的鏡像用了命名空間隔離,彼此是獨立的,即鏡像不可以共用。因為Docker和containerd常常同時存在,因此在不需要使用Docker的系統中只安裝containerd即可。
containerd最早是為Docker設計的代碼,包含一些用戶相關的功能。相比之下,CRI-O是替代Docker或者containerd的高效且輕量級的容器運行時方案,是CRI的一個實現,能夠運行符合OCI規范的容器,所以被稱為CRI-O。CRI-O是原生為生產系統運行容器設計的,有個簡單的命令行工具供測試用,但并不能進行容器管理。CRI-O支持OCI的容器鏡像格式,可以從容器鏡像倉庫中下載鏡像。CRI-O支持runC和Kata Containers這兩種低層容器運行時。
新書上市
▊《Harbor權威指南:容器鏡像、Helm Chart等云原生制品的管理與實踐》
張海寧 等 著
在云原生生態中,容器鏡像和其他云原生制品的管理與分發是至關重要的一環。本書對開源云原生制品倉庫Harbor展開全面講解,由Harbor開源項目維護者和貢獻者傾力撰寫,內容涵蓋Harbor的架構、原理、功能、部署與配置、運維、定制化開發、API、項目治理和成功案例等,還有很多未公開發表的內容在本書中都有詳盡講解!
Docker 是一個開源的應用容器引擎 誕生于 2013 年初,基于 Go 語言實現, dotCloud 公司出品(后改名為Docker Inc) Docker 可以讓開發者打包他們的應用以及依賴包到一個輕量級、可移植的容器中,然后發布到任何流行的 Linux 機器上。 容器是完全使用沙箱機制,相互隔離 容器性能開銷極低。 Docker 從 17.03 版本之后分為 CE(Community Edition: 社區版) 和 EE(Enterprise Edition: 企業版)
鏡像(Image):Docker 鏡像(Image),就相當于是一個 root 文件系統。比如官方鏡像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系統的 root 文件系統。
容器(Container):鏡像(Image)和容器(Container)的關系,就像是面向對象程序設計中的類和對象一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等。
倉庫(Repository):倉庫可看成一個代碼控制中心,用來保存鏡像。
操作系統組成部分:
進程調度子系統 進程通信子系統 內存管理子系統 設備管理子系統 文件管理子系統 網絡通信子系統 作業控制子系統
Linux文件系統由bootfs和rootfs兩部分組成 bootfs:包含bootloader(引導加載程序)和 kernel(內核) rootfs: root文件系統,包含的就是典型 Linux 系統中的/dev,/proc,/bin,/etc等標準目錄和文件 不同的linux發行版,bootfs基本一樣,而rootfs不同,如ubuntu,centos等
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳
Docker容器組成
Docker鏡像是由特殊的文件系統疊加而成 最底端是 bootfs,并使用宿主機的bootfs 第二層是 root文件系統rootfs,稱為base image 然后再往上可以疊加其他的鏡像文件 統一文件系統(Union File System)技術能夠將不同的層整合成一個文件系統,為這些層提供了一個統一的視角,這樣就隱藏了多層的存在,在用戶的角度看來,只存在一個文件系統。 一個鏡像可以放在另一個鏡像的上面。位于下面的鏡像稱為父鏡像,最底部的鏡像成為基礎鏡像。 當從一個鏡像啟動容器時,Docker會在最頂層加載一個讀寫文件系統作為容器
Docker可以運行在MAC、Windows、CentOS、UBUNTU等操作系統上
這里以CentOS7為例:
1.安裝yum工具
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
2.更新本地鏡像源
# 設置docker鏡像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
yum makecache fast
3.執行安裝
yum install -y docker-ce
等待命令執行完成,docker安裝完成
查看Docker版本
docker -v
配置鏡像加速
docker官方鏡像倉庫網速較差,我們這里使用阿里云鏡像加速
打開阿里云官網,并登陸
在產品控制臺搜索 容器與鏡像服務 并打開
在鏡像工具選擇鏡像加速器
復制示例代碼到linux執行:
如上的加速地址:https://twcgr1t0.mirror.aliyuncs.com 不同的阿里云用戶賬戶不同,可以安裝如上步驟進行操作獲取加速地址
Docker 進程相關命令
啟動docker服務:
systemctl start docker
停止docker服務:
systemctl stop docker
重啟docker服務:
systemctl restart docker
查看docker服務狀態:
systemctl status docker
設置開機啟動docker服務:
systemctl enable docker
Docker鏡像操作命令
查看鏡像:
docker images
docker images –q # 查看所用鏡像的id
搜索鏡像:
docker search 鏡像名稱
拉取鏡像:
docker pull 鏡像名稱
從Docker倉庫下載鏡像到本地,鏡像名稱格式為 名稱:版本號,如果版本號不指定則是最新的版本。 如果不知道鏡像版本,可以去docker hub 搜索對應鏡像查看。
刪除鏡像:
docker rmi 鏡像id # 刪除指定本地鏡像
docker rmi `docker images -q` # 刪除所有本地鏡像
Docker容器操作命令
查看容器
docker ps # 查看正在運行的容器
docker ps –a # 查看所有容器
創建并啟動容器
docker run 參數
參數說明:
進入容器
docker exec 參數 # 退出容器,容器不會關閉
停止容器
docker stop 容器名稱/id
啟動容器
docker start 容器名稱
刪除容器:如果容器是運行狀態則刪除失敗,需要停止容器才能刪除
docker rm 容器名稱
查看容器信息
docker inspect 容器名稱
復制宿主機文件到容器
docker cp 宿主機目錄 容器:容器目錄
數據卷 數據卷是宿主機中的一個目錄或文件 當容器目錄和數據卷目錄綁定后,對方的修改會立即同步 一個數據卷可以被多個容器同時掛載 一個容器也可以被掛載多個數據卷
數據卷作用
配置數據卷
創建啟動容器時,使用 –v 參數 設置數據卷
docker run ... –v 宿主機目錄(文件):容器內目錄(文件) ...
注意事項:
1. 目錄必須是絕對路徑
2. 如果目錄不存在,會自動創建
3. 可以掛載多個數據卷
4. 掛載的容器目錄如果存在子目錄或文件也會同步到宿主機的目錄
多容器進行數據交換
數據卷容器
1.創建數據卷容器
docker run –it --name=c3 –v /root/mydata:/mydata centos:7 /bin/bash
2.使用 –-volumes-from 參數 設置數據卷
docker run –it --name=c1 --volumes-from c3 centos:7 /bin/bash
docker run –it --name=c2 --volumes-from c3 centos:7 /bin/bash
這里以部署MySQL為例:
1.搜索mysql鏡像
docker search mysql
2.拉取mysql鏡像
docker pull mysql:5.7
3.創建容器,設置端口映射、目錄映射
docker run -id \
-p 3307:3306 \
--name=mysql \
-v /root/docker/mysql/conf:/etc/mysql \
-v /root/docker/mysql/logs:/var/log/mysql \
-v /root/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:5.7
參數說明:
-p 3307:3306:將容器的 3306 端口映射到宿主機的 3307 端口。
-v /mydata/mysql/log:/var/log/mysql:將宿主機目錄下的 /mydata/mysql/log目錄掛載到容器的 var/log/mysql; 日志目錄
-v /mydata/mysql/data:/var/lib/mysql:將宿主機目錄下的/mydata/mysql/data目錄掛載到容器的 /var/lib/mysql ; 數據目錄
/root/docker/mysql/conf:/etc/mysql: 將**/root/docker/mysql/conf目錄掛載到容器/etc/mysql ;配置目錄
4.進入容器
docker exec –it mysql bash
5.登錄mysql
mysql -uroot -p123456
部署redis:
docker search redis
docker pull redis:5.0
docker run -id --name=redis -p 6379:6379 redis:5.0
./redis-cli.exe -h 虛擬機或服務器ip -p 6379
1.容器轉鏡像
目的:啟動容器后,對容器進行定制化修改,在導出成為鏡像
相關命令:
##提交鏡像到本地倉庫 可通過docker images 查看
docker commit 容器id 鏡像名稱:版本號
## 將鏡像導出為壓縮包文件
docker save -o 壓縮文件名稱 鏡像名稱:版本號
## 加載鏡像到本地倉庫
docker load –i 壓縮文件名稱
2.dockerfile
概述
Dockerfile 相關指令:
關鍵字 | 作用 | 備注 |
FROM | 指定父鏡像 | 指定dockerfile基于那個image構建 |
MAINTAINER | 作者信息 | 用來標明這個dockerfile誰寫的 |
LABEL | 標簽 | 用來標明dockerfile的標簽 可以使用Label代替Maintainer 最終都是在docker image基本信息中可以查看 |
RUN | 執行命令 | 執行一段命令 默認是/bin/sh 格式: RUN command 或者 RUN ["command" , "param1","param2"] |
CMD | 容器啟動命令 | 提供啟動容器時候的默認命令 和ENTRYPOINT配合使用.格式 CMD command param1 param2 或者 CMD ["command" , "param1","param2"] |
ENTRYPOINT | 入口 | 一般在制作一些執行就關閉的容器中會使用 |
COPY | 復制文件 | build的時候復制文件到image中 |
ADD | 添加文件 | build的時候添加文件到image中 不僅僅局限于當前build上下文 可以來源于遠程服務 |
ENV | 環境變量 | 指定build時候的環境變量 可以在啟動的容器的時候 通過-e覆蓋 格式ENV name=value |
ARG | 構建參數 | 構建參數 只在構建的時候使用的參數 如果有ENV 那么ENV的相同名字的值始終覆蓋arg的參數 |
VOLUME | 定義外部可以掛載的數據卷 | 指定build的image那些目錄可以啟動的時候掛載到文件系統中 啟動容器的時候使用 -v 綁定 格式 VOLUME ["目錄"] |
EXPOSE | 暴露端口 | 定義容器運行的時候監聽的端口 啟動容器的使用-p來綁定暴露端口 格式: EXPOSE 8080 或者 EXPOSE 8080/udp |
WORKDIR | 工作目錄 | 指定容器內部的工作目錄 如果沒有創建則自動創建 如果指定/ 使用的是絕對地址 如果不是/開頭那么是在上一條workdir的路徑的相對路徑 |
USER | 指定執行用戶 | 指定build或者啟動的時候 用戶 在RUN CMD ENTRYPONT執行的時候的用戶 |
HEALTHCHECK | 健康檢查 | 指定監測當前容器的健康監測的命令 基本上沒用 因為很多時候 應用本身有健康監測機制 |
ONBUILD | 觸發器 | 當存在ONBUILD關鍵字的鏡像作為基礎鏡像的時候 當執行FROM完成之后 會執行 ONBUILD的命令 但是不影響當前鏡像 用處也不怎么大 |
STOPSIGNAL | 發送信號量到宿主機 | 該STOPSIGNAL指令設置將發送到容器的系統調用信號以退出。 |
SHELL | 指定執行腳本的shell | 指定RUN CMD ENTRYPOINT 執行命令的時候 使用的shell |
案例
將springboot項目打包成為docker鏡像
實現步驟:
創建文件Dockerfile 內容如下:
FROM java:8
MAINTAINER muzidong
ADD springboot.jar app.jar
CMD java –jar app.jar
執行腳本:
docker bulid -t myapp:1.0 . ## . 代表當前目錄的Dockerfile
鏡像創建完成
基于鏡像啟動容器 將容器8080端口映射到宿主機8080
docker run -it -p 8081:8080 --name=myapp myapp:1.0
訪問測試
疑惑:
1.Docker 鏡像本質是什么? 是一個分層文件系統 2.Docker 中一個centos鏡像為什么只有200MB,而一個centos操作系統的iso文件要幾個個G? Centos的iso鏡像文件包含bootfs和rootfs,而docker的centos鏡像復用操作系統的bootfs,只有rootfs和其他鏡像層 3.Docker 中一個tomcat鏡像為什么有500MB,而一個tomcat安裝包只有70多MB? 由于docker中鏡像是分層的,tomcat雖然只有70多MB,但他需要依賴于父鏡像和基礎鏡像,所有整個對外暴露的tomcat鏡像大小500多MB
概念
微服務架構的應用系統中一般包含若干個微服務,每個微服務一般都會部署多個實例,如果每個微服務都要手動啟停,維護的工作量會很大。
簡單理解: 按照一定的業務規則批量管理容器
使用步驟
Docker Compose是一個編排多容器分布式部署的工具,提供命令集管理容器化應用的完整開發周期,包括服務構建,啟動和停止。
使用步驟: 利用 Dockerfile 定義運行環境鏡像 使用 docker-compose.yml 定義組成應用的各服務 運行 docker-compose up 啟動應用
Docker Compose安裝
# Compose目前已經完全支持Linux、Mac OS和Windows,在我們安裝Compose之前,需要先安裝Docker。下面我 們以編譯好的二進制包方式安裝在Linux系統中。
curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 設置文件可執行權限
chmod +x /usr/local/bin/docker-compose
# 查看版本信息
docker-compose -version
卸載
# 二進制包方式安裝的,刪除二進制文件即可
rm /usr/local/bin/docker-compose
真實案例
使用docker compose編排springboot+redis+mysql+nginx項目
以真實電商服務后臺為例:
1.將java jar包打包為docker鏡像
# 該鏡像需要依賴的基礎鏡像
FROM java:8
# 將當前目錄下的jar包復制到docker容器的/目錄下
ADD api.jar /api.jar
# 運行過程中創建一個.jar文件
RUN bash -c 'touch /api.jar'
# 聲明服務運行在8080端口
EXPOSE 8998
# 指定docker容器啟動時運行jar包
CMD java -jar /api.jar
# 指定維護者的名字
MAINTAINER muzidong
2.編寫docker-compose.yml
version: '3'
services:
##mysql容器
mysql:
image: mysql:5.7
container_name: mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
restart: always
environment:
MYSQL_ROOT_PASSWORD: root #設置root帳號密碼
ports:
- 3307:3306
volumes:
- /root/docker/mysql/conf:/etc/mysql
- /root/docker/mysql/logs:/var/log/mysql
- /root/docker/mysql/data:/var/lib/mysql
##redis容器
redis:
image: redis:5
container_name: redis
command: redis-server --appendonly yes
volumes:
- /mydata/redis/data:/data #數據文件掛載
ports:
- 6399:6379
# 指定服務名稱
adminapi:
# 指定服務使用的鏡像
image: admin-api
# 指定容器名稱
container_name: adminapi
# 指定服務運行的端口
ports:
- 8998:8998
# 指定容器中需要掛載的文件
volumes:
- /root/docker/shop-api/logs:/logs
- /root/docker/nginx/data/:/root/docker/nginx/data/
links:
- mysql:mysql
- redis:redis
nginx:
image: nginx:1.10
container_name: nginx
volumes:
- /root/docker/nginx/nginx.conf:/etc/nginx/nginx.conf #配置文件掛載
- /root/docker/nginx/html:/usr/share/nginx/html #靜態資源根目錄掛載
- /root/docker/nginx/data:/usr/share/nginx/data #靜態資源根目錄掛載
- /root/docker/nginx/log:/var/log/nginx #日志文件掛載
ports:
- 81:80
links:
- adminapi:adminapi
3.啟動容器:
docker-compose -f docker-compose.yml up -d
docker-compose.yml常用指令:
image:指定運行的鏡像名稱
container_name:配置容器名稱
ports:指定宿主機和容器的端口映射(HOST:CONTAINER)
volumes:將宿主機的文件或目錄掛載到容器中(HOST:CONTAINER)
environment:配置環境變量
links:連接其他容器的服務(SERVICE:ALIAS)
links 訪問注意:
這里mysql:mysql 代表訪問mysql域名映射到對應的mysql容器
在java代碼里面 jdbc:mysql://mysql:3306/admin-api 我們的mysql容器端口3306是映射到主機的3307端口
我們在訪問的時候要注意端口不要寫成 mysql:3307了 3307 是宿主機的端口,我們容器間訪問應該使用容器內的端口
docker默認容器間通信模式
具體可參考文章:
https://www.cnblogs.com/kevingrace/p/6590319.html
Docker官方的Docker hub(https://hub.docker.com)是一個用于管理公共鏡像的倉庫,我們可以從上面拉取鏡像 到本地,也可以把我們自己的鏡像推送上去。但是,有時候我們的服務器無法訪問互聯網,或者你不希望將自己的鏡 像放到公網當中,那么我們就需要搭建自己的私有倉庫來存儲和管理自己的鏡像。
私有倉庫搭建
# 1、拉取私有倉庫鏡像
docker pull registry
# 2、啟動私有倉庫容器
docker run -id --name=registry -p 5000:5000 registry
# 3、打開瀏覽器 輸入地址http://私有倉庫服務器ip:5000/v2/_catalog,看到{"repositories":[]} 表示私有倉庫 搭建成功
# 4、修改daemon.json
vim /etc/docker/daemon.json
# 在上述文件中添加一個key,保存退出。此步用于讓 docker 信任私有倉庫地址;注意將私有倉庫服務器ip修改為自己私有倉庫服務器真實ip
{"insecure-registries":["私有倉庫服務器ip:5000"]}
# 5、重啟docker 服務
systemctl restart docker
docker start registry
將鏡像上傳至私有倉庫
# 1、標記鏡像為私有倉庫的鏡像
docker tag 鏡像名稱:版本號 私有倉庫服務器IP:5000/鏡像名稱:版本號
# 2、上傳標記的鏡像
docker push 私有倉庫服務器IP:5000/鏡像名稱:版本號
從私有倉庫拉取鏡像
#拉取鏡像
docker pull 私有倉庫服務器ip:5000/centos:7
相同: 容器和虛擬機具有相似的資源隔離和分配優勢
不同: 容器虛擬化的是操作系統,虛擬機虛擬化的是硬件。 傳統虛擬機可以運行不同的操作系統,容器只能運行同一類型操作系統
架構圖:
以上就是本期的全部內容了