哥哥带你学Jenkins自动化打包一(持续更新中……)

                                                                                                 互联网开发流程已经形成一套完整的模式

                                                                                                                                                                        编码 --> 构建 --> 集成 --> 测试 --> 交付 --> 部署

但是这不并不意味这是一个这是一套一次成型的系统,因为随着不同项目的不同交付周期以及交付版本,整个系统其实随时处于动态调整状态,这也就是图中双箭头所指出的我们要谈的重点,持续集成Continuous integration,持续交付Continuous delivery,以及持续部署continuous deployment。总的来说,持续集成,持续交付,持续部署,对整个开发测试流程自动化都将成为未来软件工程的重要组成部分。

Jenkins 介绍

Jenkins是一个开源的,可扩展的持续集成、交付、部署的基于web界面的平台、允许持续集成和持续交付项目,无论是用什么平台。可以处理任何类型的构建或者是持续集成。包含了上面所说的:自动构建,自动编译,自动发布。还能监控集成中存在的错误,提供详细的日志和提醒功能

官网:https://jenkins.io/     官方文档:https://jenkins.io/doc/ 教程: https://www.yiibai.com/jenkins

开源的java语言开发持续集成工具,支持CI,CD;

易于安装部署配置:可通过yum安装,或下载war包以及通过docker容器等快速实现安装部署,可方便web界面配置管理;

消息通知及测试报告:集成RSS/E-mail通过RSS发布构建结果或当构建完成时通过e-mail通知,生成JUnit/TestNG测试报告;

分布式构建:支持Jenkins能够让多台计算机一起构建/测试;

文件识别:Jenkins能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等;

丰富的插件支持:支持扩展插件,你可以开发适合自己团队使用的工具,如git,svn,maven,docker等。

  解决的用户痛点:

开发人员需经常集成自己的工作成果,每次集成都是通过自动化的构建来完成,包括:自动拉取仓库代码、自动编译、自动发布、自动测试。为了更快更好的发现问题,提高团队开发效率。各个开发人员的工作成果不需要在工作一段时间(可能几个星期)后进行将代码集成,jenkins会很频繁的集成代码然后 自动发布测试,能够尽快发现问题。(如果人工集成,集成很耗时耗力,并且集成时间会耗费在开发时间里)。


核心价值

1,集成的任意环节都是自动完成,无需人工太多干预

2,能尽快发现软件的集成问题,因为频繁集成,每一次集成都有可能发现集成问题

3,能很快适应需求变化,因为经常变换需求真的很烦

4,减少管理风险

应用场景

你写了一个月代码,然后合并,合并,花了3天时间解决冲突,又花了一周解决合并后的bug,大多数时间都耽误在了繁琐的问题上,而不能专心于业务开发和技术提升。而现在是很频繁的去集成,比方说10分钟,那么解决10分钟写的代码冲突会比解决一个月的代码冲突简单的多,并且集成这个事情还不是你做,而是CI来做,大大提高了开发效率。


使用原则

 需要使用版本控制工具 如:SVN / Git

 需及时向版本控制库里提交代码和从版本控制库里更新代码

 开发环境得统一

 集成频率得当

为什么要使用jenkins

是所有持续集成产品中安装和配置最简单的

 基于浏览器web访问界面,用户界面简洁友好

拥有非常多和强大的插件

简单点来说:jenkins是一个web应用程序,这个web应用程序能做持续集成的一切功能

部署代码上线流程

1.代码获取(直接了拉取)

2.编译(可选)

3.配置文件放进去

4.打包

5.scp到目标服务器

6.将目标服务器移除集群

7.解压并放置到Webroot

8.Scp差异文件

9.重启(可选)

10.测试

11.加入集群

Jenkins学习流程

Jenkins安装

Tomat安装设置

GIt安装设置

Maven安装设置

Jenkins配置

Jenkins管理

Jenkins安装程序生成作业

Jenkins单元测试

Jenkins自动化测试

Jenkins邮件通知

Jenkins报表

Jenkins代码分析

Jenkins分布式构建

Jenkins自动部署

Jenkins指标与趋势

Jenkins服务器维护

Jenkins持续部署

Jenkins管理插件

Jenkins安全

Jenkins备份插件

Jenkins远程测试

初始化

第一次访问的时候会有一个重置密码界面,我们按照他的要求进行即可。

自主选择插件安装。

我们先什么插件也不安装,稍后我们自主进行安装,需要什么插件就当场安装什么插件。

最后一步,我们设置管理员账户密码就可以了。

三、配置 jenkins 全局工具

1、配置 jdk 工具

jdk1.8我们已经事先安装好了,路径是 /usr/java/default/。点击系统管理--->全局工具配置。

2、配置 maven 工具

去 maven 官网下载软件,直接解压在 /usr/local 路径下面,我下载的地址是 http://mirrors.shuosc.org/apache/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.tar.gz 。

3、配置 Git 工具

jenkins 服务器首先要安装 git

yuminstall git

安装 Git client(系统管理--->插件管理)

安装完成之后重启 jenkins,然后再配置全局工具。

基本也就用到这几个工具,后面如果遇到其他工具我们可以再进行添加。

四、创建 java 任务

1、安装 Maven 插件

目前我们创建项目还是没有 maven 的,因为我们还没有安装插件。

安装 Maven Integration

现在我们点击新建,就可以看到 maven 项目的构建啦。


依赖环境

SSH,远程机开启SSH服务,允许Jenkins所在机器通过SSH服务登录到远程机执行脚本;可以设置SSH使用用户名/密码或通过key登录,SSH配置请查看我之前的jenkins配置文章。

JDK,本文部署的是JAVA应用,远程机上要用jdk环境,如非Java应用请忽略。

Tomcat或其他web容器,本文是以Tomcat为例,将应用部署到Tomcat中运行,远程机上安装Tomcat是必须的,如非Java应用请忽略,如用其他web容器请查找容器的安装资料。


前期准备

1、安装 Publish Over SSH 插件

由于后面我们还需要把打好的包分发到远程的服务器.所以这里还要再安装一个Publish Over SSH插件.这样的话,我们就可以在jenkins配置中直接使用远程拷贝了,把包直接拷贝到远程服务器。我这里是已经安装过了。

2、配置 Publish over SSH

我们添加一台服务器,后面打好的包将推送到这台服务器的 tomcat 上面。

系统管理--->系统设置--->Publish over SSH。

添加对应服务器的账号信息。

SSH Servers:远程服务器相关配置,也就是我们要把程序发布到的那一台机器的相关配置。

Name:起一个名字,方便后面配置发布的时候,选择服务器。

Hostname:远程服务器的IP地址,如果写远程的主机名的话,要在jenkins的hosts文件中加映射。

Username:服务器账号用户名。

Remote Directory:要把文件发布到远程机器的哪一个目录。

我们针对单一的服务器进行配置,只需要填写好特定服务器的账号密码即可,我们也可以配置统一的无密码通信,即使用公钥通信。

测试成功,点击保存即可,我们在后面构建项目的时候可以直接选择被构建的服务器。

3、安装 git 插件

因为我们的 jenkins 需要从 gitlab 获取代码,需要安装 git 插件。

创建 java 任务

1、创建一个 maven 任务

任务名字可以随便填写。

2、General

描述:我们可以随便填写,大概介绍一下项目的情况,以及发送到哪个机器。

丢弃旧的构建:为了不对服务器存储造成太大的压力,我们需要清理之前构建的一些项目,这里大家可以按照自己的情况进行设定。

3、源码管理

这里我使用的是自己的 gitlab 代码仓库,没有设定权限,在 github 上面的地址为:https://github.com/wangzan18/game-of-life 。

Repository URL:填写我们git项目的地址,gitlab上面的每个项目都有相应的地址。

Credentials:我这里演示的是公开的项目,如果是私有项目,需要添加认证,我会在其他的博文进行讲解演示。

Branches Specifer:因为我的项目直接在master分支上面进行更新的,所以这里填写了*/master,如果使用的其他分支可以相应的修改即可。

4、构建触发器

构建触发器我这里没有用到,意思就是说,当触发了什么条件等时候,我们后面会和 gitlab 进行自动发布。

5、构建环境

哈哈,这里我没有用到哦。

6、Pre Steps

也就这几个选项,也就是构建前需要做什么。反正我是没有用到。

7、Build

配置相对比较简单,按照如上填写即可。

8、Post Steps

暂时不写什么,还没有用得到。

9、构建设置

可以填写邮件通知,告诉我构建的结果。

构建后操作

这里是比较重要的一块,这里我们选择的是:Send build artifacts over SSH。

SSH Server Name:这里选择之前我们配置好的服务器,并且已经测试了SSH连接,服务器也已经安装了Tomcat。

Source files:需要上传的文件(注意:相对于工作区的路径。看后面的配置可以填写多个,默认用,分隔),为了简要也可以写**/*.war。

Remove prefix:移除前缀(只能指定Source files中的目录)。

Remote directory:远程目录(这里也是相对目录,目录根据我们配ssh的时候填写的Remote Directory 路径,我写的是/root,后面会上图,在这个路径的基础上的相对路径。

Exec command:把你要执行的命令写在里面,也就是我们平时手动操作的一些过程。

一切配置的差不多之后,我们保存,进行构建测试。

构建测试

1、开始构建

我们回到任务栏,点击任务最后面的构建按钮。

2、查看构建

我们点击我们构建的 #2 号任务。

我们点击控制台输出,查看一下构建的过程,一些失败的过程我们也可以从中去进行查看。

3、构建成功

我们可以看到BUILD SUCCESS,说明我们编译打包没有了什么问题。

再可以看到下面的一些命令的执行,也都是成功的,经过我们测试,项目也确实发布了,没有什么问题,至此大功告成,已经我们的更改,都只需点击一下按钮就可以发布测试了,为我们节省了很多时间。

本版本只是简要介绍了一下项目的发布,中间可能也会有很多设定不合理的地方,后期会不断的进行更新。

背景

之前的项目是 public 的,如果我们在 gitlab上面把它修改成 private,然后再次打开配置,就可能会出现下面的错误,两种协议都会进行报错。

名称IP备注

Jenkins172.18.2.101内网IP

Gitlab172.18.2.100内网IP

ssh 协议

http 协议


ssh 协议配置

1、在 Jenkins 服务器上面生产 SSH Keys

如果要通过 git 协议拉取代码,需要建立 Jenkins 服务器和 Gitlab 服务器的无密码通信,我们首先要生存一组密钥对。

ssh-keygen -t rsa -C"wzlinux"

[root@jenkins ~]# ll .ssh/total 12-rw------- 1 root root 1675 Sep  3 22:05 id_rsa-rw-r--r-- 1 root root  408 Sep  3 22:05 id_rsa.pub-rw-r--r-- 1 root root  348 Sep  4 21:21 known_hosts

2、在 Gitlab 上面配置生产的 SSH Keys

我们登录 gitlab,打开自己的用户设置,选择 SSH Keys 选项进行配置,填写我们刚刚生成的公钥(id_rsa.pub)。

SSH Keys 帮助我们建立 jenkins 到 gitlab 的安全无密码连接。

3、在 jenkins 服务器上面测试代码拉取

[root@jenkins test]# git clone git@172.18.2.100:java/game.gitCloning into 'game'...remote: Enumerating objects: 1768, done.remote: Counting objects: 100% (1768/1768), done.remote: Compressing objects: 100% (584/584), done.remote: Total 1768 (delta 1112), reused 1768 (delta 1112)Receiving objects: 100% (1768/1768), 15.02 MiB | 0 bytes/s, done.Resolving deltas: 100% (1112/1112), done.

我们可以看到,配置了SSH Keys之后,我们可以直接在服务器上面无密码拉取代码了。

4、再次打开jenkins任务配置

当我们再次打开配置,发现认证还是有问题,这时候我们需要添加认证,因为 jenkins 并不知道我们的秘钥。

添加私钥,然后确定。

认证选择我们刚刚添加的私钥,已经不再报错。

ssh 协议配置

1、在 Jenkins 服务器上面生产 SSH Keys

如果要通过 git 协议拉取代码,需要建立 Jenkins 服务器和 Gitlab 服务器的无密码通信,我们首先要生存一组密钥对。

ssh-keygen -t rsa -C"wzlinux"

[root@jenkins ~]# ll .ssh/total 12-rw------- 1 root root 1675 Sep  3 22:05 id_rsa-rw-r--r-- 1 root root  408 Sep  3 22:05 id_rsa.pub-rw-r--r-- 1 root root  348 Sep  4 21:21 known_hosts

2、在 Gitlab 上面配置生产的 SSH Keys

我们登录 gitlab,打开自己的用户设置,选择 SSH Keys 选项进行配置,填写我们刚刚生成的公钥(id_rsa.pub)。

SSH Keys 帮助我们建立 jenkins 到 gitlab 的安全无密码连接。

3、在 jenkins 服务器上面测试代码拉取

[root@jenkins test]# git clone git@172.18.2.100:java/game.gitCloning into 'game'...remote: Enumerating objects: 1768, done.remote: Counting objects: 100% (1768/1768), done.remote: Compressing objects: 100% (584/584), done.remote: Total 1768 (delta 1112), reused 1768 (delta 1112)Receiving objects: 100% (1768/1768), 15.02 MiB | 0 bytes/s, done.Resolving deltas: 100% (1112/1112), done.

我们可以看到,配置了SSH Keys之后,我们可以直接在服务器上面无密码拉取代码了。

4、再次打开jenkins任务配置

当我们再次打开配置,发现认证还是有问题,这时候我们需要添加认证,因为 jenkins 并不知道我们的秘钥。

添加私钥,然后确定。

认证选择我们刚刚添加的私钥,已经不再报错。

构建测试

修改了权限之后,我们重新构建项目,查看整个过程。

没有任何问题,SSH Keys权限认证通过。

http 协议配置

1、配置认证

和 ssh 基本一样,我们只需要再添加认证的时候写我们在 gitlab 上面的用户名密码即可。

然后再选择对应的认证方法。


构建测试

拉取代码也没有什么问题。


Jenkins 安装插件

要实现 gitlab 自动触发的功能,我们需要安装一个插件Gitlab Hook。


重新配置任务

在之前的任务里面,在构建触发器多了一项,如下,我们记住上面的接口,地址是我们 jenkins 的访问地址。


配置Gitlab

把我们上面记录的接口地址,配置在gitlab上面相应的工程里面。

配置好点击添加即可。


代码提交验证

我们可以看到,当我们每次提交好代码之后,会自动触发 jenkins 构建代码。

问题

jenkins自身的权限管理,无法实现用户指定显示视图或者视图中指定的jobs,之前分配的jenkins帐号是拥有所有权限的,因为担心故意或者误删job的情况。 我们使用jenkins自身的权限控制,而实现的效果用户只能构建,不能查看修改配置和删除job。虽然避免了故意或者失误删除job的情况,但是有时候开发需要修改配置,比如说更改构建的分支,或者想查看构建的参数,脚本就需要叫运维协助。对开发来说比较麻烦,对运维也很苦恼。

解决方法

为jenkins安装角色策略插件,通过配置角色策略可以解决上面问题。每个项目分配两个账号,一个账号可以查看job配置,修改配置,构建新的权限,不能删除,不能创建job,另一一个账号只有构建权限。不同项目组账号登录之后,jenkins视图只显示自己项目组的job。

配置

1、安装插件

系统管理--->管理插件。


全局安全配置

系统管理--->全局安全配置。


创建用户

系统管理--->管理用户


创建角色

系统管理--->Manage and Assign Roles

1、创建一个Global roles

我们先创建一个全局角色user,赋予全局标签下面的Read权限。

2、创建规划项目的角色

分别创建两个项目的角色,按照规划的分配权限,Pattern用于给项目匹配 job,使用正则表达式。


给用户分配角色

系统管理--->Manage and Assign Roles--->Assign Roles

给用户分配角色

系统管理--->Manage and Assign Roles--->Assign Roles


创建项目视图

我这里只展示创建其中一个视图,另一个方式一样。


登录验证

1、创建 job

因为创建的账号都没有赋予创建job的权限,所以我们先使用最高的管理账户创建几个job,具体如下:

2、项目隔离验证

我们分别登录 btest 和 qtest 这两个账号,分别查看一下各自账号下面可以查看到的 job。

btest

qtest

我们可以看到,隔离成功,不同的项目值可以看到各自的 job。

3、权限验证

我们分别登录 badmin 和 btest ,查看其权限。

badmin

btest

Sonar 介绍

Sonar 是一个用于代码质量管理的开放平台。通过插件机制,Sonar可以集成不同的测试工具,代码分析工具,以及持续集成工具。与持续集成工具(例如 Hudson/Jenkins 等)不同,Sonar 并不是简单地把不同的代码检查工具结果(例如 FindBugs,PMD 等)直接显示在 Web 页面上,而是通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而可以方便地对不同规模和种类的工程进行代码质量管理。

在对其他工具的支持方面,Sonar 不仅提供了对 IDE 的支持,可以在 Eclipse和 IntelliJ IDEA 这些工具里联机查看结果;同时 Sonar 还对大量的持续集成工具提供了接口支持,可以很方便地在持续集成中使用 Sonar。

此外,Sonar 的插件还可以对 Java 以外的其他编程语言提供支持,对国际化以及报告文档化也有良好的支持。


Sonar 安装

Sonar 的相关下载和文档可以在下面的链接中找到:http://www.sonarqube.org/downloads 。 需要注意最新版的 Sonar 需要至少JDK 1.8及以上版本。

之前我们已经可以成功的使用 git 进行拉取,Sonar 的功能就是来检查代码是否有BUG。除了检查代码是否有 bug 还有其他的功能,比如说:你的代码注释率是多少,代码有一些建议,编写语法的建议。所以我们叫质量管理

Sonar 还可以给代码打分,并且引用了技术宅的功能(告诉你有很多地方没改)

1、安装 jdk

JDK 可以去 Oracle 官网去下载,也可以使用 yum 仓库的 JDK。

rpm-ivhjdk-8u45-linux-x64.rpm

2、下载安装包

我这里下载的是长期维护的一个版本6.7.5。

wget https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-6.7.5.zip

3、解压

unzip sonarqube-6.7.5.zipmv sonarqube-6.7.5 /usr/local/sonarqube

准备数据库

因为 sonar 会用到数据库,所以我们要事先准备好数据库,它支持多种数据库,我们这里选择的是 MySQL/Mariadb,注意 MySQL 的版本最低要5.6,因为我这里有现有的数据库,所以安装过程省略。

mysql> CREATE DATABASE sonar CHARACTER SET utf8 COLLATE utf8_general_ci;mysql>GRANT ALL ON sonar.* TO'sonar'@'localhost'IDENTIFIED BY'sonar@pw';mysql>GRANT ALL ON sonar.* TO'sonar'@'%'IDENTIFIED BY'sonar@pw';mysql> FLUSH PRIVILEGES;

配置 Sonar

1、配置数据库连接

编辑/usr/local/sonarqube/conf/sonar.properties,修改内容如下:

# 数据库连接sonar.jdbc.username=sonar          sonar.jdbc.password=sonar@pw    sonar.jdbc.url=jdbc:mysql://10.0.1.13:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false# Web 端口sonar.web.host=0.0.0.0sonar.web.port=9000

2、创建用户 sonar

因为程序不允许我们使用 root 执行,所以我们需要注册一个普通用户

useradd sonarchownsonar.sonar /usr/local/sonarqube -R

3、启动 sonar

[root@monitor ~]# su - sonar[sonar@monitor ~]$ /usr/local/sonarqube/bin/linux-x86-64/sonar.sh startStarting SonarQube...Started SonarQube.

4、检查是否启动成功

[root@monitor ~]# netstat -tlnpActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address              Foreign Address            State      PID/Program name  tcp00127.0.0.1:320000.0.0.0:*LISTEN8126/java          tcp000.0.0.0:100500.0.0.0:*LISTEN1804/zabbix_agentd  tcp000.0.0.0:100510.0.0.0:*LISTEN2202/zabbix_server  tcp000.0.0.0:8730.0.0.0:*LISTEN16611/rsync        tcp000.0.0.0:33060.0.0.0:*LISTEN2040/mysqld        tcp000.0.0.0:220.0.0.0:*LISTEN1586/sshd          tcp00127.0.0.1:250.0.0.0:*LISTEN1677/master        tcp00::ffff:127.0.0.1:8005:::*LISTEN16486/java          tcp00:::9000:::*LISTEN8258/java          tcp00::ffff:127.0.0.1:9001:::*LISTEN8151/java          tcp00:::873:::*LISTEN16611/rsync        tcp00:::8080:::*LISTEN16486/java          tcp00:::80:::*LISTEN4841/httpd


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274