1.IaaS--基礎(chǔ)設(shè)施即服務(wù)之--Docker中構(gòu)建鏡像

本文轉(zhuǎn)載自:https://www.funtl.com/zh/docker/Docker-使用-Dockerfile-定制鏡像.html

鏡像的定制實(shí)際上就是定制每一層所添加的配置悬蔽、文件憨琳。如果我們可以把每一層修改蛔六、安裝、構(gòu)建塘辅、操作的命令都寫入一個(gè)腳本,用這個(gè)腳本來構(gòu)建式廷、定制鏡像休偶,那么之前提及的無法重復(fù)的問題、鏡像構(gòu)建透明性的問題暗挑、體積的問題就都會(huì)解決笋除。這個(gè)腳本就是 Dockerfile。

Dockerfile 是一個(gè)文本文件炸裆,其內(nèi)包含了一條條的指令(Instruction)垃它,每一條指令構(gòu)建一層,因此每一條指令的內(nèi)容烹看,就是描述該層應(yīng)當(dāng)如何構(gòu)建国拇。

還以之前定制 nginx 鏡像為例,這次我們使用 Dockerfile 來定制惯殊。

在一個(gè)空白目錄中酱吝,建立一個(gè)文本文件,并命名為 Dockerfile

$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile

其內(nèi)容為:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

這個(gè) Dockerfile 很簡(jiǎn)單土思,一共就兩行务热。涉及到了兩條指令毕源,FROMRUN

#FROM 指定基礎(chǔ)鏡像

所謂定制鏡像陕习,那一定是以一個(gè)鏡像為基礎(chǔ),在其上進(jìn)行定制址愿。就像我們之前運(yùn)行了一個(gè) nginx 鏡像的容器该镣,再進(jìn)行修改一樣,基礎(chǔ)鏡像是必須指定的响谓。而 FROM 就是指定基礎(chǔ)鏡像损合,因此一個(gè) DockerfileFROM 是必備的指令,并且必須是第一條指令娘纷。

Docker Store 上有非常多的高質(zhì)量的官方鏡像嫁审,有可以直接拿來使用的服務(wù)類的鏡像,如 nginx赖晶、redis律适、mongomysql遏插、httpd捂贿、phptomcat 等胳嘲;也有一些方便開發(fā)厂僧、構(gòu)建、運(yùn)行各種語言應(yīng)用的鏡像了牛,如 node颜屠、openjdkpython鹰祸、ruby甫窟、golang 等「]可以在其中尋找一個(gè)最符合我們最終目標(biāo)的鏡像為基礎(chǔ)鏡像進(jìn)行定制蕴坪。

如果沒有找到對(duì)應(yīng)服務(wù)的鏡像,官方鏡像中還提供了一些更為基礎(chǔ)的操作系統(tǒng)鏡像敬锐,如 ubuntu背传、debiancentos台夺、fedora径玖、alpine 等,這些操作系統(tǒng)的軟件庫(kù)為我們提供了更廣闊的擴(kuò)展空間颤介。

除了選擇現(xiàn)有鏡像為基礎(chǔ)鏡像外梳星,Docker 還存在一個(gè)特殊的鏡像赞赖,名為 scratch。這個(gè)鏡像是虛擬的概念冤灾,并不實(shí)際存在前域,它表示一個(gè)空白的鏡像。

FROM scratch
...

如果你以 scratch 為基礎(chǔ)鏡像的話韵吨,意味著你不以任何鏡像為基礎(chǔ)匿垄,接下來所寫的指令將作為鏡像第一層開始存在。

不以任何系統(tǒng)為基礎(chǔ)归粉,直接將可執(zhí)行文件復(fù)制進(jìn)鏡像的做法并不罕見椿疗,比如 swarmcoreos/etcd糠悼。對(duì)于 Linux 下靜態(tài)編譯的程序來說届榄,并不需要有操作系統(tǒng)提供運(yùn)行時(shí)支持,所需的一切庫(kù)都已經(jīng)在可執(zhí)行文件里了倔喂,因此直接 FROM scratch 會(huì)讓鏡像體積更加小巧铝条。使用 Go 語言 開發(fā)的應(yīng)用很多會(huì)使用這種方式來制作鏡像,這也是為什么有人認(rèn)為 Go 是特別適合容器微服務(wù)架構(gòu)的語言的原因之一滴劲。

#RUN 執(zhí)行命令

RUN 指令是用來執(zhí)行命令行命令的攻晒。由于命令行的強(qiáng)大能力,RUN 指令在定制鏡像時(shí)是最常用的指令之一班挖。其格式有兩種:

  • shell 格式:RUN <命令>鲁捏,就像直接在命令行中輸入的命令一樣。剛才寫的 Dockerfile 中的 RUN 指令就是這種格式萧芙。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

  • exec 格式:RUN ["可執(zhí)行文件", "參數(shù)1", "參數(shù)2"]给梅,這更像是函數(shù)調(diào)用中的格式。
    既然 RUN 就像 Shell 腳本一樣可以執(zhí)行命令双揪,那么我們是否就可以像 Shell 腳本一樣把每個(gè)命令對(duì)應(yīng)一個(gè) RUN 呢动羽?比如這樣:
FROM debian:jessie

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install

之前說過,Dockerfile 中每一個(gè)指令都會(huì)建立一層渔期,RUN 也不例外运吓。每一個(gè) RUN 的行為,就和剛才我們手工建立鏡像的過程一樣:新建立一層疯趟,在其上執(zhí)行這些命令拘哨,執(zhí)行結(jié)束后,commit 這一層的修改信峻,構(gòu)成新的鏡像倦青。

而上面的這種寫法,創(chuàng)建了 7 層鏡像盹舞。這是完全沒有意義的产镐,而且很多運(yùn)行時(shí)不需要的東西隘庄,都被裝進(jìn)了鏡像里,比如編譯環(huán)境癣亚、更新的軟件包等等丑掺。結(jié)果就是產(chǎn)生非常臃腫、非常多層的鏡像述雾,不僅僅增加了構(gòu)建部署的時(shí)間吼鱼,也很容易出錯(cuò)。 這是很多初學(xué) Docker 的人常犯的一個(gè)錯(cuò)誤绰咽。

Union FS 是有最大層數(shù)限制的,比如 AUFS地粪,曾經(jīng)是最大不得超過 42 層取募,現(xiàn)在是不得超過 127 層。

上面的 Dockerfile 正確的寫法應(yīng)該是這樣:

FROM debian:jessie

RUN buildDeps='gcc libc6-dev make' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

首先蟆技,之前所有的命令只有一個(gè)目的玩敏,就是編譯、安裝 redis 可執(zhí)行文件质礼。因此沒有必要建立很多層旺聚,這只是一層的事情。因此眶蕉,這里沒有使用很多個(gè) RUN 對(duì)一一對(duì)應(yīng)不同的命令砰粹,而是僅僅使用一個(gè) RUN 指令,并使用 && 將各個(gè)所需命令串聯(lián)起來造挽。將之前的 7 層碱璃,簡(jiǎn)化為了 1 層。在撰寫 Dockerfile 的時(shí)候饭入,要經(jīng)常提醒自己嵌器,這并不是在寫 Shell 腳本,而是在定義每一層該如何構(gòu)建谐丢。

并且爽航,這里為了格式化還進(jìn)行了換行。Dockerfile 支持 Shell 類的行尾添加 \ 的命令換行方式乾忱,以及行首 # 進(jìn)行注釋的格式讥珍。良好的格式,比如換行饭耳、縮進(jìn)串述、注釋等,會(huì)讓維護(hù)寞肖、排障更為容易纲酗,這是一個(gè)比較好的習(xí)慣衰腌。

此外,還可以看到這一組命令的最后添加了清理工作的命令觅赊,刪除了為了編譯構(gòu)建所需要的軟件右蕊,清理了所有下載、展開的文件吮螺,并且還清理了 apt 緩存文件饶囚。這是很重要的一步,我們之前說過鸠补,鏡像是多層存儲(chǔ)萝风,每一層的東西并不會(huì)在下一層被刪除,會(huì)一直跟隨著鏡像紫岩。因此鏡像構(gòu)建時(shí)规惰,一定要確保每一層只添加真正需要添加的東西,任何無關(guān)的東西都應(yīng)該清理掉泉蝌。

很多人初學(xué) Docker 制作出了很臃腫的鏡像的原因之一歇万,就是忘記了每一層構(gòu)建的最后一定要清理掉無關(guān)文件。

#構(gòu)建鏡像

好了勋陪,讓我們?cè)倩氐街岸ㄖ频?nginx 鏡像的 Dockerfile 來√盎牵現(xiàn)在我們明白了這個(gè) Dockerfile 的內(nèi)容,那么讓我們來構(gòu)建這個(gè)鏡像吧诅愚。

Dockerfile 文件所在目錄執(zhí)行:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
 ---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in 9cdc27646c7b
 ---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

從命令的輸出結(jié)果中寒锚,我們可以清晰的看到鏡像的構(gòu)建過程。在 Step 2 中违孝,如同我們之前所說的那樣壕曼,RUN 指令啟動(dòng)了一個(gè)容器 9cdc27646c7b,執(zhí)行了所要求的命令等浊,并最后提交了這一層 44aa4490ce2c腮郊,隨后刪除了所用到的這個(gè)容器 9cdc27646c7b

這里我們使用了 docker build 命令進(jìn)行鏡像構(gòu)建筹燕。其格式為:

docker build [選項(xiàng)] <上下文路徑/URL/->

在這里我們指定了最終鏡像的名稱 -t nginx:v3轧飞,構(gòu)建成功后,我們可以像之前運(yùn)行 nginx:v2 那樣來運(yùn)行這個(gè)鏡像撒踪,其結(jié)果會(huì)和 nginx:v2 一樣过咬。

#鏡像構(gòu)建上下文(Context)

如果注意,會(huì)看到 docker build 命令最后有一個(gè) .制妄。. 表示當(dāng)前目錄掸绞,而 Dockerfile 就在當(dāng)前目錄,因此不少初學(xué)者以為這個(gè)路徑是在指定 Dockerfile 所在路徑,這么理解其實(shí)是不準(zhǔn)確的衔掸。如果對(duì)應(yīng)上面的命令格式烫幕,你可能會(huì)發(fā)現(xiàn),這是在指定上下文路徑敞映。那么什么是上下文呢较曼?

首先我們要理解 docker build 的工作原理。Docker 在運(yùn)行時(shí)分為 Docker 引擎(也就是服務(wù)端守護(hù)進(jìn)程)和客戶端工具振愿。Docker 的引擎提供了一組 REST API捷犹,被稱為 Docker Remote API,而如 docker命令這樣的客戶端工具冕末,則是通過這組 API 與 Docker 引擎交互萍歉,從而完成各種功能。因此档桃,雖然表面上我們好像是在本機(jī)執(zhí)行各種 docker 功能翠桦,但實(shí)際上,一切都是使用的遠(yuǎn)程調(diào)用形式在服務(wù)端(Docker 引擎)完成胳蛮。也因?yàn)檫@種 C/S 設(shè)計(jì),讓我們操作遠(yuǎn)程服務(wù)器的 Docker 引擎變得輕而易舉丛晌。

當(dāng)我們進(jìn)行鏡像構(gòu)建的時(shí)候仅炊,并非所有定制都會(huì)通過 RUN 指令完成,經(jīng)常會(huì)需要將一些本地文件復(fù)制進(jìn)鏡像澎蛛,比如通過 COPY 指令抚垄、ADD 指令等。而 docker build 命令構(gòu)建鏡像谋逻,其實(shí)并非在本地構(gòu)建呆馁,而是在服務(wù)端,也就是 Docker 引擎中構(gòu)建的毁兆。那么在這種客戶端/服務(wù)端的架構(gòu)中浙滤,如何才能讓服務(wù)端獲得本地文件呢?

這就引入了上下文的概念气堕。當(dāng)構(gòu)建的時(shí)候纺腊,用戶會(huì)指定構(gòu)建鏡像上下文的路徑,docker build 命令得知這個(gè)路徑后茎芭,會(huì)將路徑下的所有內(nèi)容打包揖膜,然后上傳給 Docker 引擎。這樣 Docker 引擎收到這個(gè)上下文包后梅桩,展開就會(huì)獲得構(gòu)建鏡像所需的一切文件壹粟。

如果在 Dockerfile 中這么寫:

COPY ./package.json /app/

這并不是要復(fù)制執(zhí)行 docker build 命令所在的目錄下的 package.json,也不是復(fù)制 Dockerfile 所在目錄下的 package.json宿百,而是復(fù)制 上下文(context) 目錄下的 package.json趁仙。

因此洪添,COPY 這類指令中的源文件的路徑都是相對(duì)路徑。這也是初學(xué)者經(jīng)常會(huì)問的為什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 無法工作的原因幸撕,因?yàn)檫@些路徑已經(jīng)超出了上下文的范圍薇组,Docker 引擎無法獲得這些位置的文件。如果真的需要那些文件坐儿,應(yīng)該將它們復(fù)制到上下文目錄中去律胀。

現(xiàn)在就可以理解剛才的命令 docker build -t nginx:v3 . 中的這個(gè) .,實(shí)際上是在指定上下文的目錄貌矿,docker build 命令會(huì)將該目錄下的內(nèi)容打包交給 Docker 引擎以幫助構(gòu)建鏡像炭菌。

如果觀察 docker build 輸出,我們其實(shí)已經(jīng)看到了這個(gè)發(fā)送上下文的過程:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...

理解構(gòu)建上下文對(duì)于鏡像構(gòu)建是很重要的逛漫,避免犯一些不應(yīng)該的錯(cuò)誤黑低。比如有些初學(xué)者在發(fā)現(xiàn) COPY /opt/xxxx /app 不工作后,于是干脆將 Dockerfile 放到了硬盤根目錄去構(gòu)建酌毡,結(jié)果發(fā)現(xiàn) docker build 執(zhí)行后克握,在發(fā)送一個(gè)幾十 GB 的東西,極為緩慢而且很容易構(gòu)建失敗枷踏。那是因?yàn)檫@種做法是在讓 docker build 打包整個(gè)硬盤菩暗,這顯然是使用錯(cuò)誤。

一般來說旭蠕,應(yīng)該會(huì)將 Dockerfile 置于一個(gè)空目錄下停团,或者項(xiàng)目根目錄下。如果該目錄下沒有所需文件掏熬,那么應(yīng)該把所需文件復(fù)制一份過來佑稠。如果目錄下有些東西確實(shí)不希望構(gòu)建時(shí)傳給 Docker 引擎,那么可以用 .gitignore 一樣的語法寫一個(gè) .dockerignore旗芬,該文件是用于剔除不需要作為上下文傳遞給 Docker 引擎的舌胶。

那么為什么會(huì)有人誤以為 . 是指定 Dockerfile 所在目錄呢?這是因?yàn)樵谀J(rèn)情況下疮丛,如果不額外指定 Dockerfile 的話辆琅,會(huì)將上下文目錄下的名為 Dockerfile 的文件作為 Dockerfile。

這只是默認(rèn)行為这刷,實(shí)際上 Dockerfile 的文件名并不要求必須為 Dockerfile婉烟,而且并不要求必須位于上下文目錄中,比如可以用 -f ../Dockerfile.php 參數(shù)指定某個(gè)文件作為 Dockerfile暇屋。

當(dāng)然似袁,一般大家習(xí)慣性的會(huì)使用默認(rèn)的文件名 Dockerfile,以及會(huì)將其置于鏡像構(gòu)建上下文目錄中。

#其它 docker build 的用法

#直接用 Git repo 進(jìn)行構(gòu)建

或許你已經(jīng)注意到了昙衅,docker build 還支持從 URL 構(gòu)建扬霜,比如可以直接從 Git repo 中構(gòu)建:

$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14
docker build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM gitlab/gitlab-ce:8.14.0-ce.0
8.14.0-ce.0: Pulling from gitlab/gitlab-ce
aed15891ba52: Already exists
773ae8583d14: Already exists
...

這行命令指定了構(gòu)建所需的 Git repo,并且指定默認(rèn)的 master 分支而涉,構(gòu)建目錄為 /8.14/著瓶,然后 Docker 就會(huì)自己去 git clone 這個(gè)項(xiàng)目、切換到指定分支啼县、并進(jìn)入到指定目錄后開始構(gòu)建材原。

#用給定的 tar 壓縮包構(gòu)建

$ docker build http://server/context.tar.gz

1

如果所給出的 URL 不是個(gè) Git repo,而是個(gè) tar 壓縮包季眷,那么 Docker 引擎會(huì)下載這個(gè)包余蟹,并自動(dòng)解壓縮,以其作為上下文子刮,開始構(gòu)建威酒。

#從標(biāo)準(zhǔn)輸入中讀取 Dockerfile 進(jìn)行構(gòu)建

docker build - < Dockerfile

cat Dockerfile | docker build -

如果標(biāo)準(zhǔn)輸入傳入的是文本文件,則將其視為 Dockerfile挺峡,并開始構(gòu)建葵孤。這種形式由于直接從標(biāo)準(zhǔn)輸入中讀取 Dockerfile 的內(nèi)容,它沒有上下文橱赠,因此不可以像其他方法那樣可以將本地文件 COPY 進(jìn)鏡像之類的事情尤仍。

#從標(biāo)準(zhǔn)輸入中讀取上下文壓縮包進(jìn)行構(gòu)建

$ docker build - < context.tar.gz

如果發(fā)現(xiàn)標(biāo)準(zhǔn)輸入的文件格式是 gzipbzip2 以及 xz 的話病线,將會(huì)使其為上下文壓縮包,直接將其展開鲤嫡,將里面視為上下文送挑,并開始構(gòu)建。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暖眼,一起剝皮案震驚了整個(gè)濱河市惕耕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诫肠,老刑警劉巖司澎,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異栋豫,居然都是意外死亡挤安,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門丧鸯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛤铜,“玉大人,你說我怎么就攤上這事∥Х剩” “怎么了剿干?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)穆刻。 經(jīng)常有香客問我置尔,道長(zhǎng),這世上最難降的妖魔是什么氢伟? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任榜轿,我火速辦了婚禮,結(jié)果婚禮上腐芍,老公的妹妹穿的比我還像新娘差导。我一直安慰自己,他們只是感情好猪勇,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布设褐。 她就那樣靜靜地躺著,像睡著了一般泣刹。 火紅的嫁衣襯著肌膚如雪助析。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天椅您,我揣著相機(jī)與錄音外冀,去河邊找鬼。 笑死掀泳,一個(gè)胖子當(dāng)著我的面吹牛雪隧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播员舵,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼脑沿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了马僻?” 一聲冷哼從身側(cè)響起庄拇,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎韭邓,沒想到半個(gè)月后措近,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡女淑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年瞭郑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸭你。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凰浮,死狀恐怖我抠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情袜茧,我是刑警寧澤菜拓,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站笛厦,受9級(jí)特大地震影響纳鼎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裳凸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一贱鄙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧姨谷,春花似錦逗宁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捌议,卻和暖如春哼拔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓣颅。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工倦逐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宫补。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓檬姥,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親粉怕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子健民,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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