Docker的鏡像和容器

我們?cè)诶斫?docker 之前爷绘,首先我們得先區(qū)分清楚兩個(gè)概念,容器虛擬機(jī)岗钩。

可能很多讀者朋友都用過(guò)虛擬機(jī)胞谈,而對(duì)容器這個(gè)概念比較的陌生尘盼。

我們用的傳統(tǒng)虛擬機(jī)如 VMware 憨愉, VisualBox 之類的需要模擬整臺(tái)機(jī)器包括硬件,每臺(tái)虛擬機(jī)都需要有自己的操作系統(tǒng)卿捎,虛擬機(jī)一旦被開啟配紫,預(yù)分配給它的資源將全部被占用。每一臺(tái)虛擬機(jī)包括應(yīng)用午阵,必要的二進(jìn)制和庫(kù)躺孝,以及一個(gè)完整的用戶操作系統(tǒng)。

而容器技術(shù)是和我們的宿主機(jī)共享硬件資源及操作系統(tǒng)底桂,可以實(shí)現(xiàn)資源的動(dòng)態(tài)分配植袍。容器包含應(yīng)用和其所有的依賴包,但是與其他容器共享內(nèi)核籽懦。容器在宿主機(jī)操作系統(tǒng)中于个,在用戶空間以分離的進(jìn)程運(yùn)行。

容器技術(shù)是實(shí)現(xiàn)操作系統(tǒng)虛擬化的一種途徑猫十,可以讓您在資源受到隔離的進(jìn)程中運(yùn)行應(yīng)用程序及其依賴關(guān)系览濒。通過(guò)使用容器,我們可以輕松打包應(yīng)用程序的代碼拖云、配置和依賴關(guān)系,將其變成容易使用的構(gòu)建塊应又,從而實(shí)現(xiàn)環(huán)境一致性运翼、運(yùn)營(yíng)效率惑朦、開發(fā)人員生產(chǎn)力和版本控制等諸多目標(biāo)。容器可以幫助保證應(yīng)用程序快速、可靠您朽、一致地部署,其間不受部署環(huán)境的影響卵皂。容器還賦予我們對(duì)資源更多的精細(xì)化控制能力肾胯,讓我們的基礎(chǔ)設(shè)施效率更高。通過(guò)下面這幅圖我們可以很直觀的反映出這兩者的區(qū)別所在旬蟋。

image

Docker 屬于 Linux 容器的一種封裝油昂,提供簡(jiǎn)單易用的容器使用接口。它是目前最流行的 Linux 容器解決方案倾贰。

Linux 容器是 Linux 發(fā)展出了另一種虛擬化技術(shù)冕碟,簡(jiǎn)單來(lái)講, Linux 容器不是模擬一個(gè)完整的操作系統(tǒng)匆浙,而是對(duì)進(jìn)程進(jìn)行隔離安寺,相當(dāng)于是在正常進(jìn)程的外面套了一個(gè)保護(hù)層。對(duì)于容器里面的進(jìn)程來(lái)說(shuō)首尼,它接觸到的各種資源都是虛擬的挑庶,從而實(shí)現(xiàn)與底層系統(tǒng)的隔離言秸。

Docker 將應(yīng)用程序與該程序的依賴,打包在一個(gè)文件里面迎捺。運(yùn)行這個(gè)文件井仰,就會(huì)生成一個(gè)虛擬容器。程序在這個(gè)虛擬容器里運(yùn)行破加,就好像在真實(shí)的物理機(jī)上運(yùn)行一樣俱恶。有了 Docker ,就不用擔(dān)心環(huán)境問(wèn)題范舀。

總體來(lái)說(shuō)合是, Docker 的接口相當(dāng)簡(jiǎn)單,用戶可以方便地創(chuàng)建和使用容器锭环,把自己的應(yīng)用放入容器聪全。容器還可以進(jìn)行版本管理、復(fù)制辅辩、分享难礼、修改,就像管理普通的代碼一樣玫锋。

Docker的優(yōu)勢(shì)

Docker相比于傳統(tǒng)虛擬化方式具有更多的優(yōu)勢(shì):

  • docker 啟動(dòng)快速屬于秒級(jí)別蛾茉。虛擬機(jī)通常需要幾分鐘去啟動(dòng)
  • docker 需要的資源更少, docker 在操作系統(tǒng)級(jí)別進(jìn)行虛擬化撩鹿, docker 容器和內(nèi)核交互谦炬,幾乎沒(méi)有性能損耗,性能優(yōu)于通過(guò) Hypervisor 層與內(nèi)核層的虛擬化
  • docker 更輕量节沦, docker 的架構(gòu)可以共用一個(gè)內(nèi)核與共享應(yīng)用程序庫(kù)键思,所占內(nèi)存極小。同樣的硬件環(huán)境甫贯, Docker 運(yùn)行的鏡像數(shù)遠(yuǎn)多于虛擬機(jī)數(shù)量吼鳞,對(duì)系統(tǒng)的利用率非常高
  • 與虛擬機(jī)相比, docker 隔離性更弱叫搁, docker 屬于進(jìn)程之間的隔離赔桌,虛擬機(jī)可實(shí)現(xiàn)系統(tǒng)級(jí)別隔離
  • 安全性: docker 的安全性也更弱。 Docker 的租戶 root 和宿主機(jī) root 等同常熙,一旦容器內(nèi)的用戶從普通用戶權(quán)限提升為root權(quán)限纬乍,它就直接具備了宿主機(jī)的root權(quán)限,進(jìn)而可進(jìn)行無(wú)限制的操作裸卫。虛擬機(jī)租戶 root 權(quán)限和宿主機(jī)的 root 虛擬機(jī)權(quán)限是分離的仿贬,并且虛擬機(jī)利用如 IntelVT-dVT-xring-1 硬件隔離技術(shù),這種隔離技術(shù)可以防止虛擬機(jī)突破和彼此交互墓贿,而容器至今還沒(méi)有任何形式的硬件隔離茧泪,這使得容器容易受到攻擊
  • 可管理性: docker 的集中化管理工具還不算成熟蜓氨。各種虛擬化技術(shù)都有成熟的管理工具,例如 VMware vCenter 提供完備的虛擬機(jī)管理能力
  • 高可用和可恢復(fù)性: docker 對(duì)業(yè)務(wù)的高可用支持是通過(guò)快速重新部署實(shí)現(xiàn)的队伟。虛擬化具備負(fù)載均衡穴吹,高可用,容錯(cuò)嗜侮,遷移和數(shù)據(jù)保護(hù)等經(jīng)過(guò)生產(chǎn)實(shí)踐檢驗(yàn)的成熟保障機(jī)制港令, VMware 可承諾虛擬機(jī) 99.999% 高可用,保證業(yè)務(wù)連續(xù)性
  • 快速創(chuàng)建锈颗、刪除:虛擬化創(chuàng)建是分鐘級(jí)別的顷霹, Docker 容器創(chuàng)建是秒級(jí)別的, Docker 的快速迭代性击吱,決定了無(wú)論是開發(fā)淋淀、測(cè)試、部署都可以節(jié)約大量時(shí)間
  • 交付覆醇、部署:虛擬機(jī)可以通過(guò)鏡像實(shí)現(xiàn)環(huán)境交付的一致性朵纷,但鏡像分發(fā)無(wú)法體系化。 DockerDockerfile 中記錄了容器構(gòu)建過(guò)程永脓,可在集群中實(shí)現(xiàn)快速分發(fā)和快速部署

我們可以從下面這張表格很清楚地看到容器相比于傳統(tǒng)虛擬機(jī)的特性的優(yōu)勢(shì)所在:

特性 容器 虛擬機(jī)
啟動(dòng) 秒級(jí) 分鐘級(jí)
硬盤使用 一般為MB 一般為GB
性能 接近原生 弱于
系統(tǒng)支持量 單機(jī)支持上千個(gè)容器 一般是幾十個(gè)

Docker的三個(gè)基本概念

image.png

從上圖我們可以看到袍辞,Docker 中包括三個(gè)基本的概念:

  • Image(鏡像)
  • Container(容器)
  • Repository(倉(cāng)庫(kù))
    鏡像是 Docker 運(yùn)行容器的前提,倉(cāng)庫(kù)是存放鏡像的場(chǎng)所憨奸,可見(jiàn)鏡像更是 Docker 的核心革屠。

Image (鏡像)

那么鏡像到底是什么呢?

Docker 鏡像可以看作是一個(gè)特殊的文件系統(tǒng)排宰,除了提供容器運(yùn)行時(shí)所需的程序、庫(kù)那婉、資源板甘、配置等文件外,還包含了一些為運(yùn)行時(shí)準(zhǔn)備的一些配置參數(shù)(如匿名卷详炬、環(huán)境變量盐类、用戶等)。鏡像不包含任何動(dòng)態(tài)數(shù)據(jù)呛谜,其內(nèi)容在構(gòu)建之后也不會(huì)被改變在跳。

鏡像(Image)就是一堆只讀層(read-only layer)的統(tǒng)一視角,也許這個(gè)定義有些難以理解隐岛,下面的這張圖能夠幫助讀者理解鏡像的定義猫妙。

image

從左邊我們看到了多個(gè)只讀層,它們重疊在一起聚凹。除了最下面一層割坠,其它層都會(huì)有一個(gè)指針指向下一層齐帚。這些層是Docker 內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),并且能夠在主機(jī)的文件系統(tǒng)上訪問(wèn)到彼哼。統(tǒng)一文件系統(tǒng) (union file system) 技術(shù)能夠?qū)⒉煌膶诱铣梢粋€(gè)文件系統(tǒng)对妄,為這些層提供了一個(gè)統(tǒng)一的視角,這樣就隱藏了多層的存在敢朱,在用戶的角度看來(lái)剪菱,只存在一個(gè)文件系統(tǒng)。我們可以在圖片的右邊看到這個(gè)視角的形式拴签。

Container (容器)

容器 (container) 的定義和鏡像 (image) 幾乎一模一樣孝常,也是一堆層的統(tǒng)一視角,唯一區(qū)別在于容器的最上面那一層是可讀可寫的篓吁。

image

由于容器的定義并沒(méi)有提及是否要運(yùn)行容器茫因,所以實(shí)際上,容器 = 鏡像 + 讀寫層杖剪。

Repository (倉(cāng)庫(kù))

Docker 倉(cāng)庫(kù)是集中存放鏡像文件的場(chǎng)所冻押。鏡像構(gòu)建完成后,可以很容易的在當(dāng)前宿主上運(yùn)行盛嘿,但是洛巢, 如果需要在其它服務(wù)器上使用這個(gè)鏡像,我們就需要一個(gè)集中的存儲(chǔ)次兆、分發(fā)鏡像的服務(wù)稿茉,Docker Registry (倉(cāng)庫(kù)注冊(cè)服務(wù)器)就是這樣的服務(wù)。有時(shí)候會(huì)把倉(cāng)庫(kù) (Repository) 和倉(cāng)庫(kù)注冊(cè)服務(wù)器 (Registry) 混為一談芥炭,并不嚴(yán)格區(qū)分漓库。Docker 倉(cāng)庫(kù)的概念跟 Git 類似,注冊(cè)服務(wù)器可以理解為 GitHub 這樣的托管服務(wù)园蝠。實(shí)際上渺蒿,一個(gè) Docker Registry 中可以包含多個(gè)倉(cāng)庫(kù) (Repository) ,每個(gè)倉(cāng)庫(kù)可以包含多個(gè)標(biāo)簽 (Tag)彪薛,每個(gè)標(biāo)簽對(duì)應(yīng)著一個(gè)鏡像茂装。所以說(shuō),鏡像倉(cāng)庫(kù)是 Docker 用來(lái)集中存放鏡像文件的地方類似于我們之前常用的代碼倉(cāng)庫(kù)善延。

通常少态,一個(gè)倉(cāng)庫(kù)會(huì)包含同一個(gè)軟件不同版本的鏡像,而標(biāo)簽就常用于對(duì)應(yīng)該軟件的各個(gè)版本 易遣。我們可以通過(guò)<倉(cāng)庫(kù)名>:<標(biāo)簽>的格式來(lái)指定具體是這個(gè)軟件哪個(gè)版本的鏡像彼妻。如果不給出標(biāo)簽,將以 latest 作為默認(rèn)標(biāo)簽.训挡。

倉(cāng)庫(kù)又可以分為兩種形式:

  • public(公有倉(cāng)庫(kù))
  • private(私有倉(cāng)庫(kù))

Docker Registry 公有倉(cāng)庫(kù)是開放給用戶使用澳骤、允許用戶管理鏡像的 Registry 服務(wù)歧强。一般這類公開服務(wù)允許用戶免費(fèi)上傳、下載公開的鏡像为肮,并可能提供收費(fèi)服務(wù)供用戶管理私有鏡像摊册。

除了使用公開服務(wù)外,用戶還可以在本地搭建私有 Docker Registry 颊艳。Docker 官方提供了 Docker Registry鏡像茅特,可以直接使用做為私有 Registry 服務(wù)。當(dāng)用戶創(chuàng)建了自己的鏡像之后就可以使用 push 命令將它上傳到公有或者私有倉(cāng)庫(kù)棋枕,這樣下次在另外一臺(tái)機(jī)器上使用這個(gè)鏡像時(shí)候白修,只需要從倉(cāng)庫(kù)上 pull 下來(lái)就可以了。

我們主要把 Docker 的一些常見(jiàn)概念如 Image 重斑, Container 兵睛, Repository 做了詳細(xì)的闡述,也從傳統(tǒng)虛擬化方式的角度闡述了 docker 的優(yōu)勢(shì)窥浪,我們從下圖可以直觀地看到 Docker 的架構(gòu):

framework

Docker 使用 C/S 結(jié)構(gòu)祖很,即客戶端/服務(wù)器體系結(jié)構(gòu)。 Docker 客戶端與 Docker 服務(wù)器進(jìn)行交互漾脂,Docker服務(wù)端負(fù)責(zé)構(gòu)建假颇、運(yùn)行和分發(fā) Docker 鏡像。 Docker 客戶端和服務(wù)端可以運(yùn)行在一臺(tái)機(jī)器上骨稿,也可以通過(guò) RESTful 笨鸡、 stock 或網(wǎng)絡(luò)接口與遠(yuǎn)程 Docker 服務(wù)端進(jìn)行通信。

docker-framework

這張圖展示了 Docker 客戶端坦冠、服務(wù)端和 Docker 倉(cāng)庫(kù)(即 Docker HubDocker Cloud )形耗,默認(rèn)情況下Docker 會(huì)在 Docker 中央倉(cāng)庫(kù)尋找鏡像文件,這種利用倉(cāng)庫(kù)管理鏡像的設(shè)計(jì)理念類似于 Git 辙浑,當(dāng)然這個(gè)倉(cāng)庫(kù)是可以通過(guò)修改配置來(lái)指定的趟脂,甚至我們可以創(chuàng)建我們自己的私有倉(cāng)庫(kù)。

Docker的安裝

Centos7上安裝docker

Docker Platform

  • Docker提供了一個(gè)開發(fā)例衍、打包、運(yùn)行app的平臺(tái)
  • Docker把a(bǔ)pp和底層infrastructure隔離開來(lái)


    image.png

Docker Engine

Docker Engine是一個(gè)C/S架構(gòu)的應(yīng)用程序已卸,主要包含下面幾個(gè)組件佛玄;

  • 常駐后臺(tái)進(jìn)程Dockerd
  • 一個(gè)用來(lái)和Dockerd交互的REST API Server
  • 命令行CLI接口,通過(guò)和REST API進(jìn)行交互


    image.png

Docker架構(gòu)

image.png

Docker 的核心組件包括:

  • Docker Client
  • Docker daemon
  • Docker Image
  • Docker Registry
  • Docker Container

Docker Damon
Docker Damon來(lái)監(jiān)聽(tīng)Docker API的請(qǐng)求和管理Docker對(duì)象累澡,比如鏡像梦抢、容器、網(wǎng)絡(luò)和Volume愧哟。

Docker Client
docker client是我們和Docker進(jìn)行交互的最主要的方式方法奥吩,比如可以通過(guò)docker run來(lái)運(yùn)行一個(gè)容器哼蛆,然后我們的這個(gè)client會(huì)把命令發(fā)送給上面的Docker。

Docker Registry
Docker Registry 用來(lái)存儲(chǔ)Docker鏡像的倉(cāng)庫(kù)霞赫,Docker Hub是Docker官方提供的一個(gè)公共倉(cāng)庫(kù)腮介,而且Docker默認(rèn)也是從Docker Hub上查找鏡像的,當(dāng)然你也可以很方便的運(yùn)行一個(gè)私有倉(cāng)庫(kù)端衰,當(dāng)我們使用docker pull或者docker run命令時(shí)叠洗,就會(huì)從我們配置的Docker鏡像倉(cāng)庫(kù)中去拉取鏡像,使用docker push命令時(shí)旅东,會(huì)將我們構(gòu)建的鏡像推送到對(duì)應(yīng)的鏡像倉(cāng)庫(kù)中灭抑。

Images 鏡像
鏡像是一個(gè)制度模板,帶有Docker容器的說(shuō)明抵代。一般來(lái)說(shuō)的腾节,鏡像會(huì)基于另外的一些基礎(chǔ)鏡像上面安裝一個(gè)Nginx服務(wù)器,這樣就可以構(gòu)建一個(gè)屬于我們自己的鏡像了荤牍。

Containers 容器
容器是一個(gè)鏡像的可運(yùn)行的實(shí)例案腺,可以使用Docker REST API或者CLI來(lái)操作容器,容器的實(shí)質(zhì)是進(jìn)程参淫,但與直接在宿主執(zhí)行的實(shí)例進(jìn)程不同救湖,容器進(jìn)程屬于自己的獨(dú)立的命名空間。因此容器可以擁有自己的root文件系統(tǒng)涎才、自己的網(wǎng)絡(luò)配置鞋既、自己的進(jìn)程空間、甚至自己的用戶ID耍铜。容器內(nèi)的經(jīng)常是運(yùn)行在一個(gè)隔離的環(huán)境里邑闺,使用起來(lái),就好像在一個(gè)獨(dú)立于宿主的系統(tǒng)下操作一樣棕兼。這種特性使得容器封裝的應(yīng)用比直接在宿主運(yùn)行更加安全陡舅。

Docker底層的技術(shù)支持

Namespaces:做隔離pid、net伴挚、ipc靶衍、mnt、uts
Control groups:做資源限制
Union file systems:Container和image的分層

Docker Image概述

  • 文件和meta data的集合(root filesystem)
  • 分層的茎芋,并且每一層都可以添加颅眶、改變和刪除文件,成為一個(gè)新的image
  • 不同的image可以共享相同的layer
  • Image本身是read-only的


Docker技術(shù)里最為基礎(chǔ)的兩大概念:鏡像和容器田弥。

鏡像的獲取方式:

  • 從registry拉取
  • 從Dockerfile構(gòu)建

搜索鏡像

docker search <image> # 在docker index中搜索image
    --automated=false 僅顯示自動(dòng)創(chuàng)建的鏡像
    --no-trunc=false 輸出信息不截?cái)囡@示
    -s 0 指定僅顯示評(píng)價(jià)為指定星級(jí)的鏡像

下載鏡像

docker pull <image> # 從docker registry server 中下拉image
還可通過(guò)指定標(biāo)簽下載某鏡像
    docker pull [:TAG]
    docker pull centos:7

查看鏡像/刪除

docker images: # 列出images
docker images -a # 列出所有的images(包含歷史)
docker ps -a #列出本機(jī)所有容器
docker rmi <image ID>: # 刪除一個(gè)或多個(gè)image

存出和載入鏡像

    存出本地鏡像文件為.tar
    docker save -o ubuntu_14.04.tar ubuntu:14.04
    導(dǎo)入鏡像到本地鏡像庫(kù)
    docker load --input ubuntu_14.04.tar或者
    docker load < ubuntu_14.04.tar

上傳鏡像

    用戶在dockerhub網(wǎng)站注冊(cè)后涛酗,即可上傳自制的鏡像。
    docker push NAME[:TAG]

Container容器

  • 通過(guò)Image創(chuàng)建(copy)
  • 在Image layer之上建立一個(gè)container layer(可讀寫)
  • 類比Java面向?qū)ο螅侯惡蛯?shí)例
  • Image負(fù)責(zé)app的存儲(chǔ)和分發(fā),Container負(fù)責(zé)運(yùn)行app


    image.png

創(chuàng)建(使用鏡像創(chuàng)建容器):

    首先得查看鏡像的REPOSITORY和TAG
docker run -i -t REPOSITORY:TAG (等價(jià)于先執(zhí)行docker create 再執(zhí)行docker start 命令)
其中-t選項(xiàng)讓docker分配一個(gè)偽終端并綁定到容器的標(biāo)準(zhǔn)輸入上商叹, -i則讓容器的標(biāo)準(zhǔn)輸入保持打開燕刻。若要在后臺(tái)以守護(hù)態(tài)(daemonized)形式運(yùn)行,可加參數(shù)-d

在執(zhí)行docker run來(lái)創(chuàng)建并啟動(dòng)容器時(shí)剖笙,后臺(tái)運(yùn)行的標(biāo)準(zhǔn)包括:

  • 檢查本地是否存在指定的鏡像卵洗,不存在就從公有倉(cāng)庫(kù)下載
  • 利用鏡像創(chuàng)建并啟動(dòng)一個(gè)容器
  • 分配一個(gè)文件系統(tǒng),并在只讀的鏡像層外面掛載一層可讀可寫層
  • 從宿主機(jī)配置的網(wǎng)橋接口中橋接一個(gè)虛擬接口到容器
  • 從地址池配置一個(gè)ip地址給容器
  • 執(zhí)行用戶指定的應(yīng)用程序
  • 執(zhí)行完畢后容器被終止
docker start/stop/restart <container> #:開啟/停止/重啟container

進(jìn)入容器:

docker attach [container_id] #連接一個(gè)正在運(yùn)行的container實(shí)例(即實(shí)例須為start狀態(tài)枯途,可以多個(gè) 窗口同時(shí)attach 一個(gè)container實(shí)例)忌怎,但當(dāng)某個(gè)窗口因命令阻塞時(shí),其它窗口也無(wú)法執(zhí)行了酪夷。

exec可直接在容器內(nèi)運(yùn)行的命令榴啸。docker exec -ti [container_id] /bin/bash

刪除容器:

docker rm <container...> #:刪除一個(gè)或多個(gè)container
docker rm `docker ps -a -q` #:刪除所有的container
docker ps -a -q | xargs docker rm #:同上, 刪除所有的container

docker -rm
    -f 強(qiáng)制中止并運(yùn)行的容器
    -l 刪除容器的連接,但保留容器
    -v 刪除容器掛載的數(shù)據(jù)卷

修改容器:

docker commit <container> [repo:tag] # 將一個(gè)container固化為一個(gè)新的image晚岭,后面的repo:tag可選鸥印。

導(dǎo)入和導(dǎo)出容器:

    導(dǎo)出到一個(gè)文件,不管是否處于運(yùn)行狀態(tài)坦报。
    docker export CONTAINER > test.tar

    導(dǎo)入為鏡像:
cat test.tar | docker import - centos:latest

Dockerfile語(yǔ)法梳理及最佳實(shí)踐

一库说、 FROM

Syntax:

FROM  <image>[:<tag> | @<digest>] [AS <name>]
  • FROM指定一個(gè)基礎(chǔ)鏡像,且必須為Dockerfile文件開篇的每個(gè)非注釋行,至于image則可以是任何合理存在的image鏡像

  • FROM可以在一個(gè)Dockerfile中出現(xiàn)多次片择,以便于創(chuàng)建混合的images潜的。如果沒(méi)有指定tag,latest將會(huì)被指定為要使用的基礎(chǔ)鏡像版本。

  • AS name,可以給新的構(gòu)建階段賦予名稱字管。該名稱可用于后續(xù)FROM 和 COPY --from=<name | index>說(shuō)明可以引用此階段中構(gòu)建的鏡像

image.png
  • 為了安全啰挪,盡量使用官方的image作為base image

二、LABEL

為鏡像生成元數(shù)據(jù)標(biāo)簽信息
Syntax:

LABEL <KEY>=<VALUE> \
    <KEY>="XXXX"

多個(gè)標(biāo)簽寫成一行嘲叔,避免在鏡像中額外增加layer


image.png

三亡呵、MAINTAINER

作者信息,寫在FROM后
Syntax:

MAINTAINER "auth <email>"

四、COPY

當(dāng)復(fù)制一個(gè)目錄時(shí)硫戈,并不會(huì)復(fù)制目錄本身锰什,而是會(huì)遞歸復(fù)制其下子目錄 至目標(biāo)目錄下


image.png

Syntax:

COPY data /data/

文件復(fù)制準(zhǔn)則

  • <src>必須是build上下言文中的路徑,不能是其父目錄中的文件
  • 如果<src>是目錄丁逝,則其內(nèi)部文件或子目錄會(huì)被遞歸復(fù)制汁胆,但<src>目錄自身不會(huì)被復(fù)制
  • 如果指定了多個(gè)<src>,或在<src>中使用了通配符,則<dest>必須是一個(gè)目錄霜幼,且必須以/結(jié)尾
  • 如果<dest>事先不存在沦泌,它將會(huì)被自動(dòng)創(chuàng)建,這包括其父目錄路徑辛掠。

五、ADD

ADD指令類似于COPY指令,ADD支持使用TAR文件和URL路徑


image.png

Syntax:

ADD <src>...<dest>
ADD ["<src>",..."<dest>"]

操作準(zhǔn)則:

  • 如果<src>為URL且<dest>不以/結(jié)尾萝衩,則<src>指定的文件將被下載并直接被創(chuàng)建為<dest>;如果<dest>以/結(jié)尾回挽,則文件名URL指定的文件將被直接下載并保存為<dest>/<filename>
  • 如果<src>是一個(gè)本地文件系統(tǒng)上的壓縮格式的tar文件,它將被展開為一個(gè)目錄猩谊,其行為類似于"tar -x"命令;然而千劈,通過(guò)URL獲取到的tar文件將不會(huì)自動(dòng)展開。
  • 如果<src>有多個(gè)牌捷,或其間接或直接使用了通配符墙牌,則<dest>必須是一個(gè)以/結(jié)尾的目錄路徑;如果<dest>不以/結(jié)尾,則其被視作一個(gè)普通文件暗甥,<src>內(nèi)容將被直接寫入到<dest>
  • 為了讓鏡像盡量小喜滨,最好不要使用 ADD 指令從遠(yuǎn)程 URL 獲取包,而是使用 curl 和 wget撤防。這樣你可以在文件提取完之后刪掉不再需要的文件來(lái)避免在鏡像中額外添加一層虽风。
    示例:
    額外操作:
ADD http://example.com/1.tar.gz /apps/
RUN tar xf /apps/1.tar.gz -C /apps/ && \
    /bin/sh -c /apps/***.sh
     
簡(jiǎn)單操作:
RUN mkdir -p /iyunwen/server/ && \
        curl -SL http://example.com/1.tar.gz \
        | tar -xzC /iyunwen/server/ && \
        /bin/sh -c /apps/***.sh

注意:

大部分情況下,COPY由于ADD
ADD除了COPY外還有額外解壓縮功能
添加遠(yuǎn)程文件 / 目錄寄月,請(qǐng)使用curl或者wget

六辜膝、WORKDIR

用于為Dockerfile中所有RUN、CMD漾肮、ENTRYPOINT厂抖、COPY和ADD指令設(shè)定工作目錄


image.png

注意:

  • 用WORKDIR,不要用RUN cd
  • 盡量使用絕對(duì)目錄
    Syntax:
WORKDIR <dirpath>

在Dockerfile文件中克懊,WORKDIR指令可以出現(xiàn)多次忱辅,其路徑也可以為相對(duì)路徑,不過(guò)保檐,其是相對(duì)此前一個(gè)WORKDIR指令指定的路徑
另外耕蝉,WORKDIR也可調(diào)用由ENV指定定義的變量

ex:

    WORKDIR /var/log
    WORKDIR $STATEPATH

七、RUN

接受命令作為參數(shù)并用于創(chuàng)建鏡像,在之前的commit層上形成新的層夜只。


image.png

注意:

  • 為了美觀垒在,復(fù)雜的RUN請(qǐng)用反斜線換行
  • 避免無(wú)用分層,合并多條命令成一行

Syntax:

RUN \<command\>(如同執(zhí)行shell命令 /bin/sh -c)
RUN ["executable","param1","param2"]
  • RUN 指令將在當(dāng)前image中執(zhí)行任意合法命令并提交執(zhí)行結(jié)果扔亥。命令執(zhí)行提交后场躯,就會(huì)自動(dòng)執(zhí)行Dockerfile中的下一個(gè)指令。
  • 分層RUN指令和生成提交符合Docker的核心概念旅挤,其中提交很輕量踢关,可以從image將用于Dockerfile中的下一步。
  • exec形式使得可以避免shell字符串變化粘茄,以及使用不包含指定的shell可執(zhí)行文件的基本image來(lái)運(yùn)行RUN命令签舞。
  • 在shell形式中秕脓,可以使用\(反斜杠)將單個(gè)RUN指令繼續(xù)到下一行。例如:
RUN yum install -y \ openssl \ pcre-devel \ zlib
  • 第二種語(yǔ)法格式中的參數(shù)是一個(gè)JSON格式的數(shù)組儒搭,其中<executable>為要運(yùn)行的命令吠架,后面的<paramN>為傳遞給命令的選項(xiàng)或?qū)?shù);然而,此種格式指定的命令不會(huì)以"/bin/sh -c"來(lái)發(fā)起搂鲫,因此常見(jiàn)的shell操作如變量替換以及通配符(?,*等)替換將不會(huì)進(jìn)行;不過(guò)傍药,如果要運(yùn)行的命令依賴于此shell特性的話,可以將其替換為類似下面的格式魂仍。
RUN ["/bin/bash","-c","<executable>","<param1>"]

RUN 指令的緩存在下一次構(gòu)建期間不會(huì)自動(dòng)失效拐辽。用于諸如:yum repolist 之類的指令的緩存將在下一次構(gòu)建期間被重用〔磷茫可以通過(guò)--no-cache 參數(shù)來(lái)使RUN指令的緩存無(wú)效俱诸,例如: docker build --no-cache

管理命令
某些RUN 命令依賴于使用管道字符( | )將管道輸出到另一個(gè)命令功能

RUN wget -O - http://www.baidu.com/index.html | wc -l > /app/html/baidu.html

Docker使用 /bin/sh -c 解釋執(zhí)行這些命令,解釋器只評(píng)估管道中最后一個(gè)操作的退出代碼以確定成功仑氛。在上面的例子中乙埃,只要wc -l 命令成功,即使wget 命令失敗锯岖,該構(gòu)建步驟也會(huì)成功并生成新的鏡像介袜。

由于管道中任何階段的錯(cuò)誤而導(dǎo)致命令失敗,請(qǐng)預(yù)先 set -o pipefail && 確保意外錯(cuò)誤可防止構(gòu)建無(wú)意中成功出吹。例如:
set -o pipefail : 表示在管道連接的命令序列中遇伞,只要有任何一個(gè)命令返回非0值,則整個(gè)管道返回非0值捶牢,即使最后一個(gè)命令返回0.

RUN set -o pipefail && wget -O - http://www.baidu.com/index.html | wc -l > /app/html/baidu.html

注意:

并非所有的shell都支持 -o pipefail 選項(xiàng)鸠珠。在這種情況下(例如 dash shell,這是基于Debian的映像上的默認(rèn)shell)秋麸,請(qǐng)考慮使用exec形式RUN來(lái)明確選擇一個(gè)支持該pipefail選項(xiàng)的shell渐排。如:

RUN ["/bin/bash","-c","set -o pipefail && wget -O - http://www.baidu.com/index.html | wc -l > /app/html/baidu.html"]

八、CMD

類似于RUN指令灸蟆,CMD指令也可用于運(yùn)行任何命令或應(yīng)用程序驯耻,不過(guò),二者的運(yùn)行時(shí)間點(diǎn)不同

  • RUN 指令運(yùn)行于映像文件構(gòu)建過(guò)程中炒考,而CMD指令運(yùn)行于基于Dockerfile構(gòu)建出的新鏡像文件啟動(dòng)一個(gè)容器時(shí)可缚。
  • CMD指令的首要目的在于為啟動(dòng)的容器指定默認(rèn)要運(yùn)行的程序,且其運(yùn)行結(jié)束后斋枢,容器也將終止;不過(guò)帘靡,CMD指定的命令其可以被docker run的命令行選項(xiàng)所覆蓋
  • 在Dockerfile中可以存在多個(gè)CMD指令,但僅最后一個(gè)生效
    Syntax:
CMD <command>   //支持命令展開瓤帚,但是不支持傳遞信號(hào) 
CMD ["<executable>","<param1>","<param2>"]  //相當(dāng)于容器的第一個(gè)命令描姚,可以接受信號(hào)
CMD ["param1","param2"]
前兩種語(yǔ)法格式的意義同RUN
第三種則用于為ENTRYPOINT指令提供默認(rèn)參數(shù)

CMD會(huì)在啟動(dòng)容器的時(shí)候執(zhí)行涩赢,build時(shí)不執(zhí)行,而RUN只是在構(gòu)建鏡像的時(shí)候執(zhí)行轰胁,后續(xù)鏡像構(gòu)建完成之后谒主,啟動(dòng)容器就與RUN無(wú)關(guān)了。這個(gè)命令就相當(dāng)于在/etc/rc.d/rc.local中寫命令

九赃阀、ENTRYPOINT

類似CMD指令的功能,用于為容器指定默認(rèn)運(yùn)行程序擎颖,從而使得容器像是一具單獨(dú)的可執(zhí)行程序
與CMD不同的是榛斯,由ENTRYPOINT啟動(dòng)的程序不會(huì)被docker run命令行指定的參數(shù)所覆蓋,而且搂捧,這些命令行參數(shù)會(huì)被當(dāng)作參數(shù)傳遞給ENTRYPOINT指定的程序驮俗。不過(guò),docker run 命令的--entrypoint 選項(xiàng)的參數(shù)可覆蓋ENTRYPOINT指令指定的程序

Syntax:

ENTRYPOINT <command>    //這種方式能接受shell命令行展開
ENTRYPOINT ["<executable>","param1"]  //展開不了允跑,但能接收到信號(hào)

docker run命令傳入的命令參數(shù)會(huì)覆蓋CMD指令的內(nèi)容并且附加到ENTRYPOINT命令最后做為其參數(shù)使用王凑。Dockerfile文件中也可以存在多個(gè)ENTRYPOINT指令,但僅有最后一個(gè)會(huì)生效

image.png

十聋丝、EXPOSE

用來(lái)指定端口索烹,使容器內(nèi)的應(yīng)用可以通過(guò)端口和外界交互。
Syntax:

EXPOSE <port> [<port>...]

告訴Docker服務(wù)端容器對(duì)外映射的本地端口弱睦,需要在docker run 的時(shí)候使用-p 或者 -P 選項(xiàng)生效百姓。

EXPOSE 80/tcp

十一齐媒、ENV

ENV指令可以用于docker容器設(shè)置環(huán)境變量

Syntax:

ENV <key> <value>
ENV <key>=<value> ...

指定一個(gè)環(huán)境變量蛤奢,會(huì)被后續(xù)RUN指令使用,并在容器運(yùn)行時(shí)保留荣恐。
ENV設(shè)置的環(huán)境變量火惊,可以使用 docker inspect 命令來(lái)查看求类。同時(shí)還可以使用 docker run --env <key>=<value>來(lái)修改環(huán)境變量
盡量使用ENV增加可維護(hù)性


image.png

十二、USER

用于指定運(yùn)行image時(shí)的或運(yùn)行Dockerfile中任何RUN屹耐、CMD或ENTRYPOINT指令指定的程序時(shí)的用戶名或UID
默認(rèn)情況下尸疆,container的運(yùn)行身份為root用戶
Syntax:

USER <UID>|<UserName> 

需要注意的是,<UID>可以為任意數(shù)字,但實(shí)踐中其必須為/etc/passwd中某用戶的有效UID张症,否則,docker run命令將運(yùn)行失敗

十三仓技、ONBUILD

用于在Dockerfile中定義一個(gè)觸發(fā)器
Dockerfile用于build映像文件,此映像文件亦可作為base image被另一個(gè)Dockerfile用作FROM指令的參數(shù)俗他,并以之構(gòu)建新的映像文件
在后的這個(gè)Dockerfile中的FROM指令在build過(guò)程中被執(zhí)行時(shí)脖捻,將會(huì)“觸發(fā)”創(chuàng)建其base image的Dockerfile文件中的ONBUILD指令定義的觸發(fā)器

Syntax:

ONBUILD <INSTRUCTION>

注意:
盡管任何指令都可注冊(cè)成為觸發(fā)器指令,但ONBUILD不能自我嵌套兆衅,且不會(huì)觸發(fā)FROM和MAINTAINER指令
使用包含ONBUILD指令的Dockerfile構(gòu)建的鏡像應(yīng)該使用特殊的標(biāo)簽地沮,例如ruby:2.0-onbuild
在ONBUILD指令中使用ADD或COPY指令應(yīng)該格外小心嗜浮,因?yàn)樾聵?gòu)建過(guò)程和上下文在缺少指定的源文件時(shí)會(huì)失敗。

十四摩疑、HEALTHCHECK

Docker 1.12版本后引入的判斷容器狀態(tài)是否正常

Syntax:

HEALTHCHECK [OPTION] CMD <command>  //設(shè)置檢查容器健康狀況的命令 
HEALTHCHECK NONE   //如果基礎(chǔ)鏡像有健康檢查指令危融,使用這行可屏蔽掉其健康檢查指令

在沒(méi)HEALTHCHECK指令前,Docker只能通過(guò)容器內(nèi)主進(jìn)程是否退出來(lái)判斷容器是否狀態(tài)異常雷袋。很多情況下這沒(méi)問(wèn)題吉殃,但是如果程序進(jìn)入死鎖狀態(tài),或者死循環(huán)狀態(tài)楷怒,應(yīng)用進(jìn)程并不退出蛋勺,但是該容器已經(jīng)無(wú)法提供服務(wù)了。雖然后端的程序可以通過(guò)前端的檢測(cè)工具來(lái)檢查狀態(tài)信息鸠删。但是最前端的服務(wù)就需要本身的檢測(cè)機(jī)制加上監(jiān)控抱完,就可以做到出現(xiàn)問(wèn)題解決問(wèn)題。

當(dāng)在一個(gè)鏡像指定了 HEALTHCHECK 指令后刃泡,用其啟動(dòng)容器巧娱,初始狀態(tài)會(huì)為 starting,在 HEALTHCHECK 指令檢查成功后變?yōu)?healthy烘贴,如果連續(xù)一定次數(shù)失敗禁添,則會(huì)變?yōu)?unhealthy。

HEALTHCHECK支持下列選項(xiàng):

  • --interval=<間隔> : 兩次健康檢查間隔庙楚,默認(rèn)30秒
  • --timeout=<時(shí)長(zhǎng)> : 健康檢查命令運(yùn)行超時(shí)時(shí)間上荡,如果超過(guò)這個(gè)時(shí)間,本次健康檢查就被視為失敗馒闷,默認(rèn)為30秒
  • --retries=<次數(shù)> :當(dāng)連續(xù)失敗指定次數(shù)后酪捡,則將容器狀態(tài)視為unhealthy,默認(rèn)3次。

和CMD纳账、ENTRYPOINT一樣逛薇,HEALTHCHECK只可以出現(xiàn)一次,如果寫了多個(gè)疏虫,只有最后一個(gè)生效永罚。CMD 后面的命令也分為shell和exec格式。命令的返回值決定了該次檢查的成功與否: 0表示成功;1表示失敗;2保留卧秘。

ex:

HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -fs http://localhost/ || exit 1

命令收集:

容器生命周期管理 — docker [run|start|stop|restart|kill|rm|pause|unpause]
容器操作運(yùn)維 — docker [ps|inspect|top|attach|exec|events|logs|wait|export|import|port]
容器rootfs命令 — docker [commit|cp|diff]
鏡像倉(cāng)庫(kù) — docker [login|pull|push|search]
本地鏡像管理 — docker [images|rmi|tag|build|history|save|import]
其他命令 — docker [info|version]

查看docker信息:

$ docker version
# 或者
$ docker info</pre>

Docker 需要用戶具有 sudo 權(quán)限呢袱,為了避免每次命令都輸入sudo,可以把用戶加入 Docker 用戶組(官方文檔

$ sudo usermod -aG docker $USER

服務(wù)啟動(dòng)翅敌,重啟羞福,狀態(tài)

[hongdada@localhost home]$ systemctl stop docker.service

[hongdada@localhost home]$ systemctl restart docker.service

[hongdada@localhost home]$ systemctl status docker.service</pre>

images相關(guān)命令:

以鏡像centos為例

登錄倉(cāng)庫(kù) docker login
查找鏡像docker search centos
下載鏡像docker pull centos
上傳鏡像docker push centos
刪除鏡像docker rmi centos  說(shuō)明:如果有多個(gè)tag,則指定tag只會(huì)刪除tag蚯涮,而不會(huì)刪除鏡像本身治专。
查看鏡像docker images
查看具體某一個(gè)鏡像的詳細(xì)信息:docker inspect  centos
更改tag: docker tag  docker.io/centos  21yunwei:latest
創(chuàng)建鏡像docker commit  容器ID 鏡像名稱   注:創(chuàng)建鏡像有三種方式卖陵,基于容器創(chuàng)建,基于本地模板創(chuàng)建张峰,基于dockerfile創(chuàng)建
保存鏡像docker save -o testcentos.tar 21yunwei:latest
載入鏡像docker  load < tesetcentos.tar
# 列出本機(jī)的所有 image 文件泪蔫。
$ docker image ls

# 刪除 image 文件
$ docker image rm [imageName]

# 搜索鏡像
$ docker search mysql (輸出信息包括鏡像名字、描述喘批、星級(jí)撩荣、是否為官方創(chuàng)建、是否自動(dòng)創(chuàng)建)

# image 文件從倉(cāng)庫(kù)抓取到本地饶深。
$ docker image pull library/hello-world

# 運(yùn)行image文件
$ docker container run hello-world

container相關(guān)命令:

列出本機(jī)正在運(yùn)行的容器:docker container ls
列出本機(jī)所有容器婿滓,包括終止運(yùn)行的容器:docker container ls --all
創(chuàng)建容器docker create -ti image  容器ID:cid
啟動(dòng)容器docker start  cid
運(yùn)行容器docker run -dit cid 等同于docker create+docker start
停止容器服務(wù) docker container kill cid
關(guān)閉容器docker stop  cid
重啟容器docker restart  cid
刪除容器docker rm cid    #注意數(shù)據(jù)卷
刪除所有容器docker rm `docker ps -a -q`  docker kill `docker ps -q`

阻塞對(duì)容器的其他調(diào)用方法,直到容器停止后退出 docker wait  cid
查看容器docker ps 或者docker ps -a
列出容器ID docker ps -q (docker ps  -q  -a)
容器文件拷貝 docker  cp cid:路徑 宿主機(jī)路徑或docker  cp 宿主機(jī)路徑你 cid:路徑
查看容器進(jìn)程docker top cid
查看容器日志docker logs cid
查看容器變化 docker diff  cid
進(jìn)入容器docker exec -ti cid /bin/bash或者 docker attach cid(不推薦粥喜,終端顯示相同,顯示不安全且容易卡组偃)
查看容器詳細(xì)信息 docker inspect cid 包括配置信息额湘,名稱,命令旁舰、網(wǎng)路配置以及很多有用數(shù)據(jù)
查看容器端口 docker port  cid

導(dǎo)出容器docker export 3ad>21yunwei.tar
導(dǎo)入容器cat 21yunwei.tar | docker import -test/centos:latest

參考:
https://www.cnblogs.com/dance-walter/p/9581508.html

https://www.cnblogs.com/edisonchou/p/dockerfile_inside_introduction.html

https://www.cnblogs.com/hongdada/p/8906967.html

https://baijiahao.baidu.com/s?id=1641433332828402192&wfr=spider&for=pc

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锋华,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子箭窜,更是在濱河造成了極大的恐慌毯焕,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磺樱,死亡現(xiàn)場(chǎng)離奇詭異纳猫,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)竹捉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門芜辕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人块差,你說(shuō)我怎么就攤上這事侵续。” “怎么了憨闰?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵状蜗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鹉动,道長(zhǎng)轧坎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任训裆,我火速辦了婚禮眶根,結(jié)果婚禮上蜀铲,老公的妹妹穿的比我還像新娘。我一直安慰自己属百,他們只是感情好记劝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著族扰,像睡著了一般厌丑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渔呵,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天怒竿,我揣著相機(jī)與錄音,去河邊找鬼扩氢。 笑死耕驰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的录豺。 我是一名探鬼主播朦肘,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼双饥!你這毒婦竟也來(lái)了媒抠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤咏花,失蹤者是張志新(化名)和其女友劉穎趴生,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昏翰,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苍匆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矩父。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锉桑。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖窍株,靈堂內(nèi)的尸體忽然破棺而出民轴,到底是詐尸還是另有隱情,我是刑警寧澤球订,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布后裸,位于F島的核電站,受9級(jí)特大地震影響冒滩,放射性物質(zhì)發(fā)生泄漏微驶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望因苹。 院中可真熱鬧苟耻,春花似錦、人聲如沸扶檐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)款筑。三九已至智蝠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奈梳,已是汗流浹背杈湾。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留攘须,地道東北人漆撞。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像于宙,于是被迫代替她去往敵國(guó)和親叫挟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容