Docker Devicemapper存儲(chǔ)介紹

Device Mapper簡(jiǎn)介

DeviceMapper自Linux 2.6被引入熄捍。它在內(nèi)核中支持邏輯卷管理的通用設(shè)備映射機(jī)制荚醒,它為實(shí)現(xiàn)用于存儲(chǔ)資源管理的塊設(shè)備驅(qū)動(dòng)提供了一個(gè)高度模塊化的內(nèi)核和架構(gòu)千劈,包含三個(gè)重要的對(duì)象概念饰躲,MapperDevice翘贮,Mapping Table, Target device嘴纺。

Mapped Device 是一個(gè)邏輯抽象椎瘟,可以理解成為內(nèi)核向外提供的邏輯設(shè)備承璃,它通過(guò)Mapping Table描述的映射關(guān)系和Target Device建立映射。Target device表示的是Mapped Device所映射的物理空間段械蹋,對(duì)Mapped Device所表示邏輯設(shè)備來(lái)收出皇,就是該邏輯設(shè)備映射到的一個(gè)物理設(shè)備。

Mapping Table里有 Mapped Device 邏輯的起始地址哗戈、范圍郊艘、和表示在 Target Device 所在物理設(shè)備的地址偏移量以及Target 類(lèi)型等信息(注:這些地址和偏移量都是以磁盤(pán)的扇區(qū)為單位的,即 512 個(gè)字節(jié)大小唯咬,所以纱注,當(dāng)你看到128的時(shí)候,其實(shí)表示的是128*512=64K)胆胰。

DeviceMapper 中的邏輯設(shè)備Mapped Device不但可以映射一個(gè)或多個(gè)物理設(shè)備Target Device狞贱,還可以映射另一個(gè)Mapped Device,于是蜀涨,就是構(gòu)成了一個(gè)迭代或遞歸的情況瞎嬉,就像文件系統(tǒng)中的目錄里除了文件還可以有目錄,理論上可以無(wú)限嵌套下去厚柳。

devicemapper驅(qū)動(dòng)將每一個(gè)Docker鏡像和容器存儲(chǔ)在它自身的具有精簡(jiǎn)置備(thin-provisioned)氧枣、寫(xiě)時(shí)拷貝(copy-on-write)和快照功能(snapshotting)的虛擬設(shè)備上。由于Device Mapper技術(shù)是在塊(block)層面而非文件層面别垮,所以Docker Engine的devicemapper存儲(chǔ)驅(qū)動(dòng)使用的是塊設(shè)備來(lái)存儲(chǔ)數(shù)據(jù)而非文件系統(tǒng)便监。

Thin Provisioning 精簡(jiǎn)配置

Thinprovisioning.jpg

Docker使用了Thin Provisioning的Snapshot的技術(shù)實(shí)現(xiàn)分層鏡像,

Thin Provisioning Snapshot 演示

首先碳想,我們需要先建兩個(gè)文件烧董,一個(gè)是data.img,一個(gè)是meta.data.img:

[root@localhost ~]#  dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.000172451 s, 5.9 MB/s
[root@localhost ~]# dd if=/dev/zero of=/tmp/meta.data.img bs=1K count=1 seek=1G
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.000164882 s, 6.2 MB/s

注意命令中seek選項(xiàng)胧奔,表示略過(guò)of選項(xiàng)指定得輸出文件得前10M個(gè)output的blocksize的空間后再寫(xiě)入內(nèi)容逊移。因?yàn)閎s是1個(gè)字節(jié),所以是10G的大小龙填,但其實(shí)在硬盤(pán)上沒(méi)有占用空間的螟左,占有空間只有1k的內(nèi)容啡浊。直到寫(xiě)入內(nèi)容時(shí)觅够,才會(huì)在硬盤(pán)上分配空間胶背。
用ls命令查看

[root@localhost tmp]# ls -lsh /tmp/data.img
4.0K -rw-r--r--. 1 root root 11G Jan 25 20:27 /tmp/data.img
[root@localhost tmp]# ls -lsh /tmp/meta.data.img
4.0K -rw-r--r--. 1 root root 1.1T Jan 25 20:27 /tmp/meta.data.img

創(chuàng)建loopback設(shè)備。

[root@localhost tmp]# losetup /dev/loop2015 /tmp/data.img
[root@localhost tmp]# losetup /dev/loop2016 /tmp/meta.data.img

[root@localhost tmp]# losetup -a
/dev/loop2015: [64768]:16788486 (/tmp/data.img)
/dev/loop2016: [64768]:16788157 (/tmp/meta.data.img)

為這個(gè)設(shè)備建一個(gè)Thin Provisioning的Pool喘先, 用dmsetup命令:

[root@localhost tmp]# dmsetup create test-thin-pool \
    --table "0 20971522 thin-pool /dev/loop2016 /dev/loop2015 \
    128 65536 1 skip_block_zeroing"

參數(shù)解釋如下(更多信息參看man pnage):

  • dmsetup create 創(chuàng)建thin pool的命令
  • test-thin-pool是自定義的一個(gè)pool名钳吟,不沖突就好。
  • table是這個(gè)pool的參數(shù)設(shè)置
    • 0代表起的sector位置
    • 20971522代碼結(jié)句的sector號(hào)窘拯,前面說(shuō)過(guò)红且,一個(gè)sector是512字節(jié),所以涤姊,20971522個(gè)正好是10GB
    • /dev/loop2016是meta文件的設(shè)備
    • /dev/loop2015是data文件的設(shè)備
    • 128是最小的可分配的sector數(shù)
    • 65536是最少可用sector的water mark暇番,也就是一個(gè)threshold
    • 1 代表有一個(gè)附加參數(shù)
    • skip_block_zeroing是個(gè)附加參數(shù),表示略過(guò)用0填充的塊

然后思喊,就可以看到一個(gè)Device Mapper的設(shè)備:

[root@localhost tmp]# ll /dev/mapper/test-thin-pool
lrwxrwxrwx. 1 root root 7 Jan 25 20:30 /dev/mapper/test-thin-pool -> ../dm-2

接下來(lái)壁酬,創(chuàng)建一個(gè)Thin Provisioning的Volume:

[root@localhost tmp]# dmsetup message /dev/mapper/test-thin-pool 0 "create_thin 0"
[root@localhost tmp]# dmsetup create test-thin-volumn-001 --table "0 2097152 thin /dev/mapper/test-thin-pool 0"

期中:

  • 第一個(gè)命令中的create_thin是關(guān)鍵字,后面的0表示這個(gè)Volume的device的id
  • 第二個(gè)命令恨课,是真正的為這個(gè)Volumn創(chuàng)建一個(gè)可以mount的設(shè)備舆乔,名字叫test-thin-volumn-001。 2091512只有1GB剂公。

在mount前希俩,格式化一下:

[root@localhost tmp]# mkfs.ext4 /dev/mapper/test-thin-volumn-001
mke2fs 1.42.9 (28-Dec-2013)
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=16 blocks, Stripe width=16 blocks
65536 inodes, 262144 blocks
13107 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=268435456
8 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376

Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

可以掛載了

[root@localhost tmp]# mkdir /mnt/base
[root@localhost tmp]# mount /dev/mapper/test-thin-volumn-001 /mnt/base
[root@localhost tmp]# echo "hello, im am a base" > /mnt/base/id.txt
[root@localhost tmp]# cat /mnt/base/id.txt
hello, im am a base

創(chuàng)建snapshot:

[root@localhost tmp]# dmsetup message /dev/mapper/test-thin-pool 0 "create_snap 1 0"
[root@localhost tmp]# dmsetup create mysnap1 --table "0 2097152 thin /dev/mapper/test-thin-pool 1"

掛載snapshot:

[root@localhost tmp]# ll /dev/mapper/mysnap1
lrwxrwxrwx. 1 root root 7 Jan 25 20:37 /dev/mapper/mysnap1 -> ../dm-4
[root@localhost tmp]# mkdir /mnt/mysnap1
[root@localhost tmp]# mount /dev/mapper/mysnap1 /mnt/mysnap1/
[root@localhost tmp]# ls /mnt/mysnap1/
id.txt  lost+found
[root@localhost tmp]# cat /mnt/mysnap1/id.txt
hello, im am a base
[root@localhost tmp]# echo >> i am snap1 >> /mnt/mysnap1/id.txt
[root@localhost tmp]# echo i am snap1 >> /mnt/mysnap1/id.txt
[root@localhost tmp]# cat /mnt/mysnap1/id.txt
hello, im am a base
am snap1
i am snap1

我們?cè)倏聪?mnt/base,沒(méi)有新加的內(nèi)容纲辽。

[root@localhost tmp]# cat /mnt/base/id.txt
hello, im am a base

我們能看到分層鏡像的樣子了颜武。

Docker存儲(chǔ)驅(qū)動(dòng)devicemapper

devicemapper是RHEL的Docker Engine的默認(rèn)存儲(chǔ)驅(qū)動(dòng),有兩種配置模式:loop-lvm和direct-lvm拖吼。
loop-lvm是默認(rèn)的模式鳞上,它使用OS層面離散的文件來(lái)構(gòu)建精簡(jiǎn)池(thin pool)。該模式主要是設(shè)計(jì)出來(lái)讓Docker能夠簡(jiǎn)單的被”開(kāi)箱即用(out-of-the-box)”而無(wú)需額外的配置绿贞。但如果是在生產(chǎn)環(huán)境的部署Docker因块,官方明文不推薦使用該模式。
direct-lvm是Docker推薦的生產(chǎn)環(huán)境的推薦模式籍铁,他使用塊設(shè)備來(lái)構(gòu)建精簡(jiǎn)池來(lái)存放鏡像和容器的數(shù)據(jù)涡上。

自動(dòng)配置

自動(dòng)配置docker的devicemapper的存儲(chǔ)驅(qū)動(dòng),需要一塊獨(dú)立的塊設(shè)備拒名,比如/dev/sdb吩愧。

[root@localhost ~]# fdisk -l
...
...
Disk /dev/sdb: 53.7 GB, 53687091200 bytes, 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x0007eabf

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048   104857599    52427776   8e  Linux LVM

全新安裝的docker,啟動(dòng)服務(wù)前增显,修改配置文件/etc/sysconfig/docker-storage-setup

DEVS="/dev/sdb"
VG=docker-vg
DATA_SIZE=100%FREE

說(shuō)明一下:

  • DEVS是獨(dú)立的快設(shè)備名
  • VG是vg的名字
  • DATA_SIZE默認(rèn)50%雁佳,這里設(shè)定是100%FREE

啟動(dòng)docker后,devicemapper的存儲(chǔ)卷就創(chuàng)建成功了。

[root@node1 ~]# lvs
  LV          VG        Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  docker-pool docker-vg twi-aot---  49.89g             24.35  6.20
  lv_01       root_vg01 -wi-ao---- <41.00g

手動(dòng)配置

如果沒(méi)有獨(dú)立的磁盤(pán)塊設(shè)備糖权,可以在系統(tǒng)中的磁盤(pán)設(shè)備上空閑的塊空間配置堵腹。

  1. 查看設(shè)備名,我們假定是/dev/xvdf
  2. 停止docker
# systemctl stop docker
  1. 安裝包
yum install device-mapper-persistent-data lvm2 -y
  1. 創(chuàng)建pv
# pvcreate /dev/xvdf
Physical volume "/dev/xvdf" successfully created.
  1. 創(chuàng)建dockervg
# vgcreate docker /dev/xvdf
Volume group "docker" successfully created
  1. 創(chuàng)建兩個(gè)lv thinpoolthinpoolmeta
# sudo lvcreate --wipesignatures y -n thinpool docker -l 95%VG

Logical volume "thinpool" created.

# sudo lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG

Logical volume "thinpoolmeta" created.
  1. 把lv轉(zhuǎn)換成thin pool和metadata
# sudo lvconvert -y \
--zero n \
-c 512K \
--thinpool docker/thinpool \
--poolmetadata docker/thinpoolmeta

WARNING: Converting logical volume docker/thinpool and docker/thinpoolmeta to
thin pool's data and metadata volumes with metadata wiping.
THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)
Converted docker/thinpool to thin pool.
  1. 通過(guò)lvm profile配置自動(dòng)擴(kuò)容
# vi /etc/lvm/profile/docker-thinpool.profile
  1. 指定thin_pool_autoextend_thresholdthin_pool_autoextend_percent值星澳。

當(dāng)磁盤(pán)使用率達(dá)到80%疚顷,增加20%的容量.

activation {
  thin_pool_autoextend_threshold=80
  thin_pool_autoextend_percent=20
}
  1. 應(yīng)用LVM profile.
sudo lvchange --metadataprofile docker-thinpool docker/thinpool

Logical volume docker/thinpool changed.
  1. 啟用LV的監(jiān)控
# sudo lvs -o+seg_monitor

LV       VG     Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert Monitor
thinpool docker twi-a-t--- 95.00g             0.00   0.01                             monitored
  1. 如果之前運(yùn)行過(guò)docker,先備份
# mv /var/lib/docker /var/lib/docker.bk
  1. 編輯/etc/docker/daemon.json配置devicemapper存儲(chǔ)驅(qū)動(dòng)需要的參數(shù)
{
    "storage-driver": "devicemapper",
    "storage-opts": [
    "dm.thinpooldev=/dev/mapper/docker-thinpool",
    "dm.use_deferred_removal=true",
    "dm.use_deferred_deletion=true"
    ]
}
  1. 啟動(dòng)docker
# systemctl start docker
  1. 驗(yàn)證
# docker info

Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.03.1-ce
Storage Driver: devicemapper
 Pool Name: docker-thinpool
 Pool Blocksize: 524.3 kB
 Base Device Size: 10.74 GB
 Backing Filesystem: xfs
 Data file:
 Metadata file:
 Data Space Used: 19.92 MB
 Data Space Total: 102 GB
 Data Space Available: 102 GB
 Metadata Space Used: 147.5 kB
 Metadata Space Total: 1.07 GB
 Metadata Space Available: 1.069 GB
 Thin Pool Minimum Free Space: 10.2 GB
 Udev Sync Supported: true
 Deferred Removal Enabled: true
 Deferred Deletion Enabled: true
 Deferred Deleted Device Count: 0
 Library Version: 1.02.135-RHEL7 (2016-11-16)
<output truncated>

Data fileMetadata file是空的腿堤,pool名字是docker-thinpool笆檀。

device mapper在Docker中的性能表現(xiàn)

device mapper的性能主要受“需要時(shí)分配”策略和“寫(xiě)時(shí)復(fù)制”策略影響酗洒,下面分別介紹:

需要時(shí)分配(allocate-on-demand)

device mapperdriver通過(guò)allocate-on-demand策略為需要寫(xiě)入的數(shù)據(jù)分配數(shù)據(jù)塊寝蹈。也就是說(shuō)登淘,每當(dāng)容器中的進(jìn)程需要向容器寫(xiě)入數(shù)據(jù)時(shí)黔州,device mapper就從資源池中分配一些數(shù)據(jù)塊并將其映射到容器流妻。
當(dāng)容器頻繁進(jìn)行小數(shù)據(jù)的寫(xiě)操作時(shí),這種機(jī)制非常影響影響性能涣达。
一旦數(shù)據(jù)塊被分配給了容器度苔,對(duì)它進(jìn)行的讀寫(xiě)操作都直接對(duì)塊進(jìn)行操作了浑度。

寫(xiě)時(shí)復(fù)制(copy-on-write)

與aufs一樣,device mapper也支持寫(xiě)時(shí)復(fù)制策略甩骏。容器中第一次更新某個(gè)文件時(shí),device mapper調(diào)用寫(xiě)時(shí)復(fù)制策略饮笛,將數(shù)據(jù)塊從鏡像快照中復(fù)制到容器快照中缎浇。
device mapper的寫(xiě)時(shí)復(fù)制策略以64KB作為粒度,意味著無(wú)論是對(duì)32KB的文件還是對(duì)1GB大小的文件的修改都僅復(fù)制64KB大小的文件素跺。這相對(duì)于在文件層面進(jìn)行的讀操作具有很明顯的性能優(yōu)勢(shì)指厌。
但是踩验,如果容器頻繁對(duì)小于64KB的文件進(jìn)行改寫(xiě)箕憾,device mapper的性能是低于aufs的袭异。

存儲(chǔ)空間使用效率

device mapper不是最有效使用存儲(chǔ)空間的storage driver御铃,啟動(dòng)n個(gè)相同的容器就復(fù)制了n份文件在內(nèi)存中上真,這對(duì)內(nèi)存的影響很大羹膳。所以device mapper并不適合容器密度高的場(chǎng)景。

參考

本文參考和節(jié)選了以下文章:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末就珠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蠢壹,更是在濱河造成了極大的恐慌嗓违,老刑警劉巖图贸,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹂季,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡偿洁,警方通過(guò)查閱死者的電腦和手機(jī)撒汉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)涕滋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)睬辐,“玉大人,你說(shuō)我怎么就攤上這事宾肺。” “怎么了锨用?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵增拥,是天一觀的道長(zhǎng)掌栅。 經(jīng)常有香客問(wèn)我猾封,道長(zhǎng)澄耍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任枚钓,我火速辦了婚禮铅搓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘搀捷。我一直安慰自己星掰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布嫩舟。 她就那樣靜靜地躺著氢烘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪家厌。 梳的紋絲不亂的頭發(fā)上播玖,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音饭于,去河邊找鬼蜀踏。 笑死维蒙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的果覆。 我是一名探鬼主播颅痊,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼局待!你這毒婦竟也來(lái)了斑响?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤钳榨,失蹤者是張志新(化名)和其女友劉穎舰罚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體重绷,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沸停,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昭卓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瘟滨,死狀恐怖候醒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杂瘸,我是刑警寧澤倒淫,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站败玉,受9級(jí)特大地震影響敌土,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜运翼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一返干、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧血淌,春花似錦矩欠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沦补,卻和暖如春乳蓄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夕膀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工虚倒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留美侦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓裹刮,卻偏偏與公主長(zhǎng)得像音榜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捧弃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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

  • Device Mapper 簡(jiǎn)介 DeviceMapper自Linux 2.6被引入成為了Linux最重要的一個(gè)技...
    51reboot閱讀 3,673評(píng)論 0 3
  • devicemapper驅(qū)動(dòng)將每一個(gè) Docker鏡像 和容器存儲(chǔ)在它自身的具有精簡(jiǎn)置備(thin-provisi...
    jianweixs閱讀 4,867評(píng)論 0 0
  • 一赠叼、Docker 簡(jiǎn)介 Docker 兩個(gè)主要部件:Docker: 開(kāi)源的容器虛擬化平臺(tái)Docker Hub: 用...
    R_X閱讀 4,386評(píng)論 0 27
  • Docker Docker這兩年可謂大紅大紫,仿佛一夜之間违霞,街坊鄰居茶余飯后都在說(shuō)Docker嘴办,我這也掰扯掰扯Do...
    jony456123閱讀 571評(píng)論 0 1
  • 作者:記渡——夭夭 1 星期五的晚上,7點(diǎn)鐘买鸽,如往常一樣涧郊,我和方素相約在他們公司附近的“茶馬天下”吃云南菜,犒勞這...
    記渡閱讀 376評(píng)論 0 0