批量安裝操作系統(tǒng) by Cobbler

當(dāng)我們需要部署某集群時,往往需要為數(shù)十臺服務(wù)器安裝操作系統(tǒng)蛀醉,為了節(jié)省時間和人力著摔,可以使用PXE技術(shù),實(shí)現(xiàn)批量無人安裝操作系統(tǒng)管闷。

預(yù)啟動執(zhí)行環(huán)境(Preboot eXecution Environment粥脚,PXE)提供了一種使用網(wǎng)絡(luò)接口(Network Interface)啟動計(jì)算機(jī)的機(jī)制。

普通的從硬盤啟動Linux系統(tǒng)最初是BIOS將MBR加載入內(nèi)存包个,然后將控制權(quán)交給MBR中的bootloader程序(如GRUB)刷允,bootloader程序經(jīng)過幾個stage的加載后,最后將vmlinuz加載入內(nèi)存碧囊,開始內(nèi)核引導(dǎo)树灶;而PXE的啟動過程在內(nèi)核引導(dǎo)之前,是由BIOS中的PXE固件開啟NBP程序(比如DHCP的網(wǎng)絡(luò)通信)糯而,然后下載vmlinuz和initrd天通,之后再進(jìn)入內(nèi)核啟動過程。在內(nèi)核引導(dǎo)完成之后熄驼,仍然會通過網(wǎng)絡(luò)的方式(但不是TFTP協(xié)議像寒,而是其他更加健壯的協(xié)議如NFS、iSCSI等)瓜贾,加載真正的完整操作系統(tǒng)诺祸。

具體請搜索關(guān)鍵詞:pxe、kickstart祭芦、cobbler筷笨。

我們要做的大概流程是:在與集群網(wǎng)絡(luò)連通的某臺機(jī)器上部署cobbler服務(wù),集群其他服務(wù)器以pxe方式boot实束,通過cobbler服務(wù)器上的dhcp服務(wù)獲取ip奥秆,然后通過tftp、nfs等方式從cobbler服務(wù)器上拉取鏡像和配置文件咸灿,進(jìn)行操作系統(tǒng)安裝构订。

部署cobbler服務(wù)器

部署cobbler的服務(wù)器操作系統(tǒng)為centos 7,內(nèi)核版本3.10.
請保證部署cobbler的機(jī)器的/var目錄至少有10GB的空間避矢。

關(guān)閉防火墻&selinux

之后的安裝過程中需要使用http/dhcp/tftp等服務(wù)悼瘾,需要開啟對應(yīng)的端口囊榜。在內(nèi)網(wǎng)環(huán)境中,為了方便亥宿,可以直接關(guān)閉防火墻和selinux卸勺。

執(zhí)行下面腳本

# 臨時關(guān)閉selinux,不必重啟就可生效
setenforce 0  
# 永久關(guān)閉selinux烫扼。reboot后生效
[ -e /etc/sysconfig/selinux ] || cp /etc/sysconfig/selinux /etc/sysconfig/selinux.orig
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/sysconfig/selinux
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
# 關(guān)閉防火墻
systemctl stop firewalld
systemctl disable firewalld

安裝cobbler及其他需要的軟件包

yum install -y epel-release
yum install -y cobbler
yum install -y httpd dhcp rsync bind
yum install -y tftp xinetd
yum install -y pykickstart

配置cobbler

cobbler的配置文件放在/etc/cobbler曙求。
主要配置文件/etc/cobbler/settings,要修改的位置主要有:

Default Encrypted Password

如注釋所說映企,運(yùn)行
openssl passwd -1
并用結(jié)果替換原有的密碼悟狱。

# /etc/cobbler/settings

# cobbler has various sample kickstart templates stored
# in /var/lib/cobbler/kickstarts/.  This controls
# what install (root) password is set up for those
# systems that reference this variable.  The factory
# default is "cobbler" and cobbler check will warn if
# this is not changed.
# The simplest way to change the password is to run 
# openssl passwd -1
# and put the output between the "" below.
default_password_crypted: "$1$mF86/UHC$WvcIcX2t6crBz2onWxyac."

Server

cobbler server的ip,用于對外提供dhcp和http等服務(wù)堰氓,必須為一個固定內(nèi)網(wǎng)ip地址挤渐。

# /etc/cobbler/settings

# this is the address of the cobbler server -- as it is used
# by systems during the install process, it must be the address
# or hostname of the system as those systems can see the server.
# if you have a server that appears differently to different subnets
# (dual homed, etc), you need to read the --server-override section
# of the manpage for how that works.
server: 127.0.0.1

Next_Server

next_server為tftp服務(wù)所在ip,通常是需要和server保持一致

# /etc/cobbler/settings

# if using cobbler with manage_dhcp, put the IP address
# of the cobbler server here so that PXE booting guests can find it
# if you do not set this correctly, this will be manifested in TFTP open timeouts.
next_server: 127.0.0.1

DHCP Management and DHCP Server Template

manage_dhcp: 0修改為manage_dhcp: 1

# /etc/cobbler/settings

# set to 1 to enable Cobbler's DHCP management features.
# the choice of DHCP management engine is in /etc/cobbler/modules.conf
manage_dhcp: 0

同時需要修改/etc/cobbler/dhcp.template
一般情況下双絮,只需要修改下面這部分內(nèi)容中的dhcp網(wǎng)段浴麻。要與本機(jī)ip在同一網(wǎng)段。

 # /etc/cobbler/dhcp.template

 subnet 192.168.1.0 netmask 255.255.255.0 {
      option routers             192.168.1.1;
      option subnet-mask         255.255.255.0;
      range dynamic-bootp        192.168.1.100 192.168.1.254;
      default-lease-time         21600;
      max-lease-time             43200;
      next-server                $next_server;
      class "pxeclients" {
           match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
           if option pxe-system-type = 00:02 {
                   filename "ia64/elilo.efi";
           } else if option pxe-system-type = 00:06 {
                   filename "grub/grub-x86.efi";
           } else if option pxe-system-type = 00:07 {
                   filename "grub/grub-x86_64.efi";
           } else if option pxe-system-type = 00:09 {
                   filename "grub/grub-x86_64.efi";
           } else {
                   filename "pxelinux.0";
           }
      }
 
 }

假設(shè)本機(jī)ip為192.168.122.226/24囤攀,那么修改后的配置為:

 # /etc/cobbler/dhcp.template
 
 subnet 192.168.122.0 netmask 255.255.255.0 {
      option routers             192.168.122.1;
      option subnet-mask         255.255.255.0;
      range dynamic-bootp        192.168.122.100 192.168.122.254;
      default-lease-time         21600;
      max-lease-time             43200;
      next-server                $next_server;
      class "pxeclients" {
           match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
           if option pxe-system-type = 00:02 {
                   filename "ia64/elilo.efi";
           } else if option pxe-system-type = 00:06 {
                   filename "grub/grub-x86.efi";
           } else if option pxe-system-type = 00:07 {
                   filename "grub/grub-x86_64.efi";
           } else if option pxe-system-type = 00:09 {
                   filename "grub/grub-x86_64.efi";
           } else {
                   filename "pxelinux.0";
           }
      }
 
 }

啟動服務(wù)

tftp

sed -i '/disable/c\\tdisable\t\t\t= no' /etc/xinetd.d/tftp
systemctl start xinetd
systemctl enable xinetd

rsyncd

systemctl start rsyncd
systemctl enable rsyncd

httpd

systemctl start httpd
systemctl enable httpd

dhcpd
這個我們配置成是由cobbler管理的软免,留到后面通過cobbler 啟動。略過

cobblerd

systemctl start cobblerd
systemctl enable cobblerd

問題自檢

cobbler提供了問題檢查的命令

cobbler check

根據(jù)輸出解決必須解決的問題抚岗。有些問題可以忽略或杠。
第一次運(yùn)行cobbler時,肯定會缺少boot-loaders宣蔚,需要run一次

cobbler get-loaders

根據(jù)check的輸出向抢,解決所有必須解決的問題后,重啟cobblerd

# 修改配置后胚委,需要重啟cobblerd
systemctl restart cobblerd

然后運(yùn)行一次挟鸠,啟動被cobbler管理的service,確保這一步?jīng)]有報錯后亩冬,才能繼續(xù)后面的步驟

# 啟動被cobbler管理的service艘希,修改模版配置后也需要執(zhí)行這個命令
cobbler sync

導(dǎo)入系統(tǒng)鏡像

In order to import a distribution, you will need a DVD ISO for your distribution. NOTE: You must use a full DVD, and not a "Live CD" ISO.

根據(jù)文檔,我們需要使用DVD版本的鏡像(通過kickstart定制一些基礎(chǔ)軟件包的安裝硅急,要求DVD版本)覆享,下載CentOS-7-x86_64-DVD版本的鏡像。

wget http://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-1708.iso

下載完成后营袜,把鏡像mount到某個目錄撒顿,然后import到cobbler

# 掛載iso
mkdir /mnt/iso1
mount -t iso9660 -o loop,ro CentOS-7-x86_64-DVD-1708.iso /mnt/iso1
# 導(dǎo)入到cobbler
cobbler import --name=centos7 --arch=x86_64 --path=/mnt/iso1

如果沒有報錯,import過程會生成至少一對distro/profile對象荚板。通過list命令查看凤壁。還可以通過report命令查看某個對象的詳細(xì)信息吩屹。另外要注意,cobbler中的對象配置是繼承關(guān)系:distro <- profile <- system拧抖,后面的配置會覆蓋前面的煤搜。

cobbler distro list
cobbler profile list
# 可以通過report命令查看對象的details
cobbler distro report --name=centos7-x86_64
cobbler profile report --name=centos7-x86_64

一切正常后,現(xiàn)在已經(jīng)不需要iso

umount /mnt/iso1
rmdir /mnt/iso1

這里需要解釋下cobbler的幾個對象的含義唧席。我們可能用到的命令主要有cobbler distro/profile/system擦盾,分別對應(yīng)了三種對象:

  • distro:表示某個發(fā)行版的對象,不同的鏡像導(dǎo)入后對應(yīng)不同的distro袱吆,比如centos7-x86_64
  • profile:distro的配置文件厌衙,一個distro可對應(yīng)一個或多個profile距淫。默認(rèn)導(dǎo)入時會為distro生成一個profile绞绒。每個profile背后關(guān)聯(lián)一個ks文件,用于決定kickstart安裝操作系統(tǒng)時的配置榕暇。當(dāng)需要為不同的機(jī)器指定不同的操作系統(tǒng)配置時蓬衡,需要為distro創(chuàng)建多個profile文件,對應(yīng)多種配置選項(xiàng)彤枢。
  • system:system對象不是必須要創(chuàng)建的狰晚,但它可以指導(dǎo)cobbler進(jìn)行更定制化的操作。比如說缴啡,我們有多個profile文件壁晒,需要為不同的機(jī)器安裝不同配置的操作系統(tǒng),沒有system對象時业栅,需要手動選擇profile文件秒咐,而通過system對象可以使指定機(jī)器使用指定的profile配置。

暫時我們沒有使用system對象碘裕。

修改profile

假設(shè)我們的需求是這樣的:

  1. 安裝一個標(biāo)準(zhǔn)的Centos7
  2. 安裝一些必要的軟件包
  3. 安裝時只對系統(tǒng)盤進(jìn)行分區(qū)和格式化携取,其他磁盤不動
  4. 為了方便使用虛擬機(jī)測試整個安裝流程,需要在磁盤分區(qū)時自動適配磁盤名如vda/sda
  5. 安裝完成后對一些基礎(chǔ)配置進(jìn)行設(shè)置

下面針對上面的需求帮孔,進(jìn)行profile修改雷滋。

修改ks templates

cobbler基于kickstart進(jìn)行操作系統(tǒng)的安裝,kickstart使用ks文件完成安裝過程的配置文兢。如前文所說晤斩,ks文件與profile綁定,我們需要對其進(jìn)行自定義來滿足不同的部署要求姆坚。當(dāng)系統(tǒng)通過PXE引導(dǎo)至profile選擇菜單后澳泵,一旦選定了需要部署的系統(tǒng),接下來就會按照該profile所對應(yīng)的ks文件來執(zhí)行一系列的安裝操作旷偿。

在cobbler中ks文件的實(shí)例是通過cgi動態(tài)生成的烹俗,所以我們需要修改的是ks templates文件爆侣,而不是raw ks文件。

通過cobbler profile report --name=centos7-x86_64命令幢妄,得到原始的ks tempate文件兔仰。

Kickstart       :/var/lib/cobbler/kickstarts/sample_end.ks

我們要做的,就是基于原始的ks模版文件蕉鸳,進(jìn)行修改乎赴。修改完成后,修改profile中的Kickstart文件路徑潮尝,使其指向我們自己的ks模版文件榕吼。

# 復(fù)制原始文件
cp /var/lib/cobbler/kickstarts/sample_end.ks /var/lib/cobbler/kickstarts/customize_end.ks
# 修改模版文件
vim /var/lib/cobbler/kickstarts/customize_end.ks
# 修改profile指向的ks模版文件
cobbler profile edit --name=centos7-x86_64 --kickstart=/var/lib/cobbler/kickstarts/customize_end.ks 

ks template和ks文件的簡單介紹:

  • ks template文件
    ks模版文件使用 Cheetah作為模版引擎,使用Snippets進(jìn)行代碼片的復(fù)用勉失。

文檔:
http://cobbler.github.io/manuals/2.8.0/3/5_-Kickstart_Templating.html
http://cobbler.github.io/manuals/2.8.0/3/6
-_Snippets.html

  • ks文件
    ks文件包含下面四部分:
    • 命令部分:這里應(yīng)該包括必需的選項(xiàng)羹蚣,包含語言的選擇,防火墻乱凿,密碼顽素,網(wǎng)絡(luò),分區(qū)的設(shè)置等徒蟆;
    • %packages部分:這部分選擇需要安裝的軟件包胁出,可以是 @core 這樣的group的形式,也可以是這樣 vim-* 包的形式段审;
    • %pre部分:安裝前解析的腳本全蝶,通常用來生成特殊的ks配置,比如由一段程序決定磁盤分區(qū)等寺枉;
    • %post部分:安裝后執(zhí)行的腳本抑淫,通常用來做系統(tǒng)的初始化設(shè)置。比如啟動的服務(wù)型凳,相關(guān)的設(shè)定等丈冬。

文檔:
https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/6/html/installation_guide/s1-kickstart2-file

修改之后的customize_end.ks文件完整如下:

# This kickstart file should only be used with EL > 5 and/or Fedora > 7.
# For older versions please use the sample.ks kickstart file.

#platform=x86, AMD64, or Intel EM64T
# System authorization information
auth  --useshadow  --enablemd5
# System bootloader configuration
#bootloader --location=mbr
# Partition clearing information
#clearpart --all --initlabel
# Use text mode install
text
# Firewall configuration
firewall --disabled
# Run the Setup Agent on first boot
firstboot --disabled
# System keyboard
keyboard us
# System language
lang en_US
# Use network installation
url --url=$tree
# If any cobbler repo definitions were referenced in the kickstart profile, include them here.
$yum_repo_stanza
# Network information
$SNIPPET('network_config')
# Reboot after installation
reboot

#Root password
rootpw --iscrypted $default_password_crypted
# SELinux configuration
selinux --disabled
# Do not configure the X Window System
skipx
# System timezone
# Customized
timezone  Asia/Shanghai
# Install OS instead of upgrade
install
# Clear the Master Boot Record
zerombr
# Allow anaconda to partition the system as needed
#autopart
# To support part manually
$SNIPPET('main_partition_select')

%pre
$SNIPPET('log_ks_pre')
$SNIPPET('kickstart_start')
$SNIPPET('pre_install_network_config')
$SNIPPET('pre_partition_select_vda_or_sda')
# Enable installation monitoring
$SNIPPET('pre_anamon')
%end

%packages
$SNIPPET('func_install_if_enabled')
@^minimal
@core
chrony
wget
net-tools
python-setuptools
rsync
lrzsz
expect
tcl
ntpdate
-selinux-policy*
-NetworkManager*
-kexec-tools
-snappy
-wpa_supplicant
-ppp
%end

%post --nochroot
$SNIPPET('log_ks_post_nochroot')
%end

%post
$SNIPPET('log_ks_post')
# Start yum configuration
$yum_config_stanza
# End yum configuration
$SNIPPET('post_install_kernel_options')
$SNIPPET('post_install_network_config')
$SNIPPET('func_register_if_enabled')
$SNIPPET('download_config_files')
$SNIPPET('cobbler_register')
# Enable post-install boot notification
$SNIPPET('post_anamon')
# Start final steps
$SNIPPET('kickstart_done')
# End final steps
%end

為了自動識別虛擬機(jī)和物理機(jī),我們增加了一個snippet:pre_partition_select_vda_or_sda甘畅。

所以埂蕊,還需要在/var/lib/cobbler/snippets/路徑下,增加我們的snippet疏唾。

vim /var/lib/cobbler/snippets/pre_partition_select_vda_or_sda

將下面內(nèi)容蓄氧,輸入到/var/lib/cobbler/snippets/pre_partition_select_vda_or_sda文件內(nèi)。

# Determine architecture-specific partitioning needs
if [ -b /dev/vda ]; then
  cat >/tmp/partinfo << EOF
clearpart --initlabel --all
ignoredisk --only-use=vda
bootloader --location=mbr --boot-drive=vda --driveorder=vda
clearpart --initlabel --drives=vda
part /boot --fstype=ext3 --ondisk=vda --size=500
part / --fstype=xfs --size=1024 --grow --ondisk=vda --asprimary
EOF
elif [ -b /dev/sda ]; then
  cat >/tmp/partinfo << EOF
clearpart --initlabel --all
ignoredisk --only-use=sda
bootloader --location=mbr --boot-drive=sda --driveorder=sda
part /boot --fstype=ext3 --ondisk=sda --size=500
part / --fstype=xfs --size=100000 --ondisk=sda --asprimary
part /data --fstype=xfs --grow --ondisk=sda
EOF
fi

校驗(yàn)ks 模版文件

運(yùn)行如下命令槐脏,校驗(yàn)ks喉童,這個命令會輸出一些建議。如果輸出all kickstarts seem to be ok表示一切正常顿天,堂氯。

cobbler validateks

可以通過getks命令查看通過我們模版渲染出的完整ks文件

cobbler profile getks --name=centos7-x86_64

虛擬機(jī)測試

使用vmware虛擬機(jī)進(jìn)行測試蔑担。創(chuàng)建一臺centos 7虛擬機(jī),部署cobbler服務(wù)器咽白。

創(chuàng)建一臺空白虛擬機(jī)啤握,保證網(wǎng)絡(luò)與之前創(chuàng)建的centos 7虛擬機(jī)在同一環(huán)境。啟動虛擬機(jī)晶框。
一切正常的話排抬,會出現(xiàn)如下界面,選擇第二項(xiàng)授段,開始安裝蹲蒲。


image.png

從啟動到安裝完成,只需要人工干預(yù)一次----選擇profile侵贵。其他步驟完全自動化届搁。安裝一臺機(jī)器大概需要5-10分鐘,受網(wǎng)絡(luò)環(huán)境影響模燥。

進(jìn)行安裝

現(xiàn)在我們的cobbler服務(wù)器已經(jīng)就緒了咖祭。下一步,需要將集群中的其他機(jī)器與cobbler保證網(wǎng)絡(luò)連通蔫骂,然后進(jìn)入bios選擇以PXE方式啟動,進(jìn)行安裝牺汤。

參考資料:

http://cobbler.github.io/manuals/quickstart/
http://cobbler.github.io/manuals/2.8.0/3/5_-Kickstart_Templating.html
http://cobbler.github.io/manuals/2.8.0/3/6
-_Snippets.html
https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/6/html/installation_guide/s1-kickstart2-file

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辽旋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子檐迟,更是在濱河造成了極大的恐慌补胚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件追迟,死亡現(xiàn)場離奇詭異溶其,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)敦间,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門瓶逃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人廓块,你說我怎么就攤上這事厢绝。” “怎么了带猴?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵昔汉,是天一觀的道長。 經(jīng)常有香客問我拴清,道長靶病,這世上最難降的妖魔是什么会通? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮娄周,結(jié)果婚禮上渴语,老公的妹妹穿的比我還像新娘。我一直安慰自己昆咽,他們只是感情好驾凶,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掷酗,像睡著了一般调违。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泻轰,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天技肩,我揣著相機(jī)與錄音,去河邊找鬼浮声。 笑死虚婿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泳挥。 我是一名探鬼主播然痊,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屉符!你這毒婦竟也來了剧浸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤矗钟,失蹤者是張志新(化名)和其女友劉穎唆香,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吨艇,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躬它,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了东涡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冯吓。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖软啼,靈堂內(nèi)的尸體忽然破棺而出桑谍,到底是詐尸還是另有隱情,我是刑警寧澤祸挪,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布锣披,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雹仿。R本人自食惡果不足惜增热,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胧辽。 院中可真熱鬧峻仇,春花似錦、人聲如沸邑商。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽人断。三九已至吭从,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恶迈,已是汗流浹背涩金。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暇仲,地道東北人步做。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像奈附,于是被迫代替她去往敵國和親全度。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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