Jenkins使用
Jenkins
Jenkins使用
目录
本节实践
实战名称 |
---|
💘 实践:创建自由风格流水线-2023.3.21(测试成功) |
💘 实践:自定义agent通信端口-2023.3.21(测试成功) |
💘 实践:Jenkins配置node方法-2023.3.21(测试成功) |
💘 实践:如何在流水线构建的时候使用页面参数-2023.3.22(测试成功) |
💘 实践:pipeline里调shell的方式-2023.3.22(测试成功) |
💘 实践:流水线回放功能-2023.3.22(测试成功) |
💘 实践:使用jenkins的内置变量来显示构建名及构建分支-2023.3.23(测试成功) |
💘 实践:pipeline里配置丢弃历史构建-2023.3.22(测试成功) |
💘 实践:pipeline中字符参数和选项参数-2023.3.22(测试成功) |
💘 实践:pipeline里配置远程构建-2023.3.22(测试成功) |
💘 实践:Jenkins项目分类-2023.3.23(测试成功) |
💘 实践:Jenkins 凭据管理-2023.3.25(测试成功) |
💘 实践:Jenkins BlueOcean安装-2023.3.25(测试成功) |
1、Jenkins数据目录管理
-
Jenkins采用本地文件的数据存储方式;
-
Jenkins的所有数据都存储在JENKINS_HOME变量定义的数据目录下;
-
默认JENKINS_HOME的值是**/var/lib/jenkins**;
-
注意:
jenkins_home
jenkins把所有的数据都存放在了文件系统里面。
rpm方式安装的jenkins: 其家目录在/var/lib/jenkins 目录下;#注意
docker方式安装的jenkins: 其家目录在/var/jenkins_home 目录下;
jenkins所有的配置文件都是以.xml格式存储的!
docker方式安装的jenkins:
[root@Devops6 ~]#docker exec -it jenkins bash
jenkins@d6139736ef4c:/$ cd /var/jenkins_home/
jenkins@d6139736ef4c:~$ ls
config.xml hudson.model.UpdateCenter.xml jenkins.install.InstallUtil.lastExecVersion jenkins.model.JenkinsLocationConfiguration.xml jobs nodeMonitors.xml plugins secret.key secrets userContent war
copy_reference_file.log identity.key.enc jenkins.install.UpgradeWizard.state jenkins.telemetry.Correlator.xml logs nodes queue.xml.bak secret.key.not-so-secret updates users
jenkins@d6139736ef4c:~$
jenkins容器对应宿主机数据目录位置:
[root@Devops6 ~]#cd /data/devops6/jenkins_home/
[root@Devops6 jenkins_home]#ls
config.xml hudson.model.UpdateCenter.xml jenkins.install.InstallUtil.lastExecVersion jenkins.model.JenkinsLocationConfiguration.xml jobs nodeMonitors.xml plugins secret.key secrets userContent war
copy_reference_file.log identity.key.enc jenkins.install.UpgradeWizard.state jenkins.telemetry.Correlator.xml logs nodes queue.xml.bak secret.key.not-so-secret updates users
Jenkins启动后会读取JENKINS_HOME变量,根据次变量的值来决定数据存储的位置。Jenkins是以XML格式的文件存储到数据目录。
注意:我们以后做集成的时候,会经常和这个
config.xml
文件打交道。但是需要注意的是,这里有个坑,在做集成前,一定要对这个文件做个备份。万一在做用户权限时,不小心把自己权限给设置错了,此时就需要用到这个文件来救场了。
Jenkins目录的用途:
- caches: 系统缓存数据
- jobs: Jenkins项目作业
- nodes: Jenkins slave节点信息
- secrets: 秘钥信息
- userContent: 类似于web站点目录,可以上传一些文件 (这里可以放一个index.html文件,然后它会给你展示出来!类似于sftp功能,用的不是很多!)
- workspace: 默认的工作目录
- fingerprints: 指纹验证信息
- logs : 日志信息
- plugins: 插件相关配置
- updates: 插件更新目录
- users: jenkins系统用户目录
⚠️ 注意:
workspace :默认的工作目录
一般要把这个工作目录和jenkins的数据目录要分开存放,要不万一你这个目录io比较高的话,也会对jenkins有影响!
这个目录非常重要,就是我们以后运行的每一个流水线作业,它是在哪里工作,是在这个workspace目录下工作的!
默认情况下,这个workspace目录也是在jenkins_home
下,但是我们一般是去自定义这个目录的!
2、Jenkins项目类型-Pipeline
- 参数化构建: 为流水线传参
- String 字符参数
- Choice 选项参数
- 历史构建管理
- 丢弃历史构建: 保留 5天内 10个构建
- 并行构建控制
创建自由风格流水线
💘 实践:创建自由风格流水线-2023.3.21(测试成功)
接下来我们新创建1条流水线:
默认是自由风格
类型流水线;(如果要创建Pipeline
类型流水线,则需要安装pipeline插件才行。)
- 创建自由风格流水线
点击+New Item
:
填写item name
,点击ok
:
此时,一个自由风格的流水线项目就被创建成功了,接下来,我们只需要填写一些配置参数就好:
- 配置参数
全局配置:
丢弃旧的构建
:
参数化构建
:
并行构建控制
:
Build
:
完成后,点击Save
:
点击Build with Parameters
:
填写构建版本 和构建环境,点击Build
:
可以看到构建成功被完成了:
测试结束。😘
创建pipeline流水线
💘 实践:创建pipeline流水线-2023.3.21(测试成功)
- 在使用Pipeline类型的项目时,需要提前安装Jenkins的Pipeline插件。
点击管理Jenkins-管理插件
:
在Availabe
里查找pipeline
插件,完成安装:
- 安装好插件后新建一个Pipeline类型的作业:
自由风格和pipeline风格的区别
- 自由风格项目和Pipeline类型的项目区别是:
自由风格
项目构建部分的操作都是在页面上面完成的,而Pipeline
的构建任务描述都是通过代码的方式。
自由风格:
pipeline风格:
但自由风格入门可能简单一点,这些配置我们都可以在这里选择,基本上,这一条流水线就调好了。
pipeline中字符参数和选项参数
💘 实践:pipeline中字符参数和选项参数-2023.3.22(测试成功)
选项参数和字符参数是我们用的最多的!
- 同样,我们按照上面自由风格类型填写字符串参数,选项参数等配置
参数化构建是可以满足不同情况下我们可以在流水线运行前填写参数传递。这些参数会在构建流水线的页面展示。
如果我们需要填写一个具体的值,我们可以用字符参数。如果已经确定值的可选范围可以使用选项参数供用户选择。
- 进行构建
pipeline里配置丢弃历史构建
💘 实践:pipeline里配置丢弃历史构建-2023.3.22(测试成功)
- Jenkins每一次构建都会生成对应的构建日志数据。进行大规模编译会有许多日志,占用磁盘空间。我们可以选择性的保留周期内的构建信息,例如: 1个月内的10次构建。具体的保留日期可以参考项目的发布周期。
- 进行多次构建并查看效果:发现这边只有最近的10次构建记录
pipeline里配置并行构建
- 并发构建选项,可以控制项目是否支持多次并行的构建。按照实际的场景进行选择。(默认就是开启并行构建的(只是一个选项配置而已))
如何在流水线构建的时候使用页面参数(3种方)
💘 实践:如何在流水线构建的时候使用页面参数-2023.3.22(测试成功)
定义的参数可以通过${params.NAME}
的方式使用。
其实在Jenkins运行时这些参数也会注入到env这个对象中,也可以通过${env.NAME}
或者${NAME}
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
echo "${VERSION}"
echo "${env.VERSION}"
echo "${params.VERSION}"
}
}
}
}
pipeline里调shell的方式
💘 实践:pipeline里调shell的方式-2023.3.22(测试成功)
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
echo "${VERSION}"
echo "${env.VERSION}"
echo "${params.VERSION}"
sh "ls -l"
}
}
}
}
注意:这里的引号一定要是双引号。
流水线回放功能
💘 实践:流水线回放功能-2023.3.22(测试成功)
- 使用
回放
功能测试代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcpGQmTS-1679729768321)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230321074428691.png)]
另外,在回放里测试的代码是不会保存到自己的流水线代码里的,要记得及时保存代码哦。
⚠️ 注意:
如果某次的构建失败了的话,那么本次回放是不可用的,即上次的代码会丢失!(注意:推荐在本地编辑器编辑代码,然后复制到回放中就好)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYHtVq1a-1679729768321)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20220420080126050.png)]
使用jenkins的内置变量来显示构建名及构建分支
💘 实践:使用jenkins的内置变量来显示构建名及构建分支-2023.3.23(测试成功)
- 代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRKTlS3G-1679729768322)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230321222621841.png)]
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"
pipeline {
agent any
stages {
stage('Hello') {
steps {
echo 'Hello World'
echo "${VERSION}"
echo "${env.VERSION}"
echo "${params.VERSION}"
sh "ls -l"
}
}
}
}
- 构建
3、Jenkins项目构建方式
- 定时构建:到具体时间进行触发。
- API 触发构建
- 与其他系统联动
- Gitlab提交代码后通过webhook触发构建(主动)
- Jenkins 轮训Gitlab检测到代码变更触发构建(被动)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UvZQK83o-1679729768322)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230321063742284.png)]
💘 实践:pipeline里配置远程构建-2023.3.22(测试成功)
远程构建:配置一个认证token,触发时必须带有token才行。
触发命令:
#触发
curl -uadmin:admin http://192.168.1.200:8080/job/demo-pipeline/build?token=zeyang
#触发并传递参数
curl -uadmin:admin "http://192.168.1.200:8080/job/demo-pipeline/buildWithParameters?token=zeyang&VERSION=4.4.4&ENV_NAME=uat"
注意: 当流水线选择了参数化构建就需要用参数化构建的URL进行触发。参数构建也要注意,传递选项参数时值必须在值列表内。
测试过程:
- 配置远程构建
在pipeline-demo项目里,Build Triggers
里勾选Trigger builds remotely
,配置一个认证toktenpipeline-demo
,点击Save
:
- (1)先使用curl命令测试
curl -uadmin:admin "http://172.29.9.101:8080/job/pipeline-demo/buildWithParameters?token=pipeline-demo&VERSION=4.4.4&envName=pro"
运行,观察效果:
- (2)这里我们使用postman再次测试下
http://172.29.9.101:8080/job/pipeline-demo/buildWithParameters?token=pipeline-demo&VERSION=4.4.4&envName=pre
运行:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yP8ozMfi-1679729768325)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230322130019768.png)]
- 注意:
注意: 当流水线选择了参数化构建就需要用参数化构建的URL进行触发。参数构建也要注意,传递选项参数时值必须在值列表内。
4、Jenkins项目分类
项目分类:
使用视图或者文件夹来进行分类。
视图中可以使用正则表达式匹配一组作业而不用手动添加到视图。
💘 实践:Jenkins项目分类-2023.3.23(测试成功)
- 使用视图进行分类: 创建一个demo的视图,用于归类demo team的作业。
- 选择纳入devops视图的作业, 可以手动选择作业。当作业很多的时候,推荐使用正则表达式进行匹配。(参考Java正则表达式的语法)
注意:这里的正则表达式写法
.*
表示匹配任意长度的任意字符
?
匹配其前面的字符0或1次,即:可有可无因此这里可以写成:
^devops01-.*
也是可以的。(推荐不写?
)。
- 验证
- 同样,我们创建2个pipeline流水线,名称叫做
devops-app-service
,devops-merge-service
,不配置什么,只简单创建,用于测试视图功能。
创建一个叫做devops
的视图:
验证:
符合预期。
5、Jenkins忘记密码与找回
- 如果Jenkins登录的用户名忘记了,可以进入JENKINS_HOME/users目录中查看。
取消认证则需要修改config.xml,然后重启Jenkins。
<useSecurity>false</useSecurity>
默认值为true,代表开始安全设置,false则关闭。
<denyAnonymousReadAccess>false</denyAnonymousReadAccess>
默认值为true,代码禁止匿名用户访问,false则允许。
(此处仅关闭安全设置,然后重启Jenkins服务器即可)
- 重启后:修改用户密码,然后进入
全局安全设置
中开启用户认证。
⚠️ 注意:当前jenkins/jenkins:2.346.3-2-lts-jdk11
版本jenkins如下用户的config.xml文件
里无法找到这2处配置。以上方法仅仅作为记录。
vim /data/devops6/jenkins_home/users/admin_14059091204073200622/config.xml
6、Jenkins用户管理
-
用户来源
- 本地存储数据库
- 其他认证系统[ gitlab/ ldap/ github ]
-
用户操作
- 增删改查禁用
-
Jenkins默认使用自带的数据库管理用户, 也可以通过安装插件实现LDAP、GitLab认证集成。
企业里Jenkins一般与ldap系统做集成;
也可以与gitalb做单点登录;
Jenkins使用文件存储,在JENKINS_HOME/users目录中可以看到每个用户的配置。
- 系统设置, 用户管理,可以查看到当前的用户列表。 可以对用户新建、更新、删除。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P0ITyTJd-1679729768330)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230324070152987.png)]
7、Jenkins授权管理
- 插件: Role-Based Strategy
- Role类型
- Global roles 全局角色
- Iterm roles 定义一类项目
- Role授权
在企业中可能多个开发组织共用同一个Jenkins服务器, 不会让用户具有管理员权限的, 需要给用户分配对应的Group组织权限。例如: 张三, 属于devops1这个组织, 仅允许张三对devops1组织相关的jenkins作业进行构建操作。
就是后面我们把jenkins落地到了企业后,大家是如何登录这个系统呢?我们需要给它创建用户和账号,企业里面用户那么多,我们怎么给他们一一分配账号呢?
1、这里面有一种方法,叫做认证集成,你可以和LDAP系统做对接,LDAP是我们做统一认证的地方,用户的组织结构信息都在这里!然后我们可以拿LDAP的插件和jenkins做集成,最终实现的一个效果就是,可以用LDAP里面的账号和密码去去登录Jenkins。2、如果你的集团里面可能没有LDAP,但是你有gitlab,那么Jenkins也可以支持gitlab的单点登录!
所以,登录这一块,一个是它自己的数据库,刚才我们看到的那个jenkins_home目录就是它的数据库。另外一个就是跟认证系统做集成,或者和其他第三方平台做一个sso(类似于单点登录一样!)
💘 实践:Jenkins授权管理-2023.3.24(测试成功)
背景:
我们再刚开始安装的时候创建了一个admin
账户,现在我们再创建一个账户xyy
:
点击系统管理-管理用户
:
点击新建用户
,创建新用户:账号:xyy
密码:123456
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wydGJLbO-1679729768331)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20220414220934658.png)]
查看创建好的xyy
账户:
然后退出当前用户,使用刚创建的xyy
账户登录jenkins系统:
我们会发现,本来自己是想创建一个普通用户来着的,结果创建的用户具有管理员权限,这可不得了。为什么会这样呢?
我们退出当前账户继续以admin账户登录jenkins:
点击系统管理-安全-全局安全设置
:会发现当前授权策略是登录用户可以做任何事
,原来产生上面那个现象的原因在这里!
要想解决以上问题,是需要安装一个叫Role-Base strategy
的授权插件来着的:
现在,我们来开始安装一下这个插件:
⚠️ 好用的插件也就这个了。这个插件不光提供了ui界面,还提供了api。
很早之前,老师分享了一篇这个Role-Base strategy
api操作的文章,因为老师当时在后台给他们勾选权限的时候,都是框框……,看的眼睛真的受不了了!这个时候就可以调用api去加权限,特别恐怖,勾选一个权限,特别考验你的眼力!(当时老师应该有个公开课来着的)并且其api也没怎么变化!
还有其他同学使用矩阵matrix strategy
插件:(这个Role-Base strategy
要比矩阵插件好用一些!)
1、安装插件
Jenkins系统中授权插件应用最广的是Role-based Authorization Strategy
, 在插件管理中安装。 安装插件后重启Jenkins服务器以使其生效。
重启Jenkins服务器之后,进入系统设置,全局安全配置。 配置授权策略为Role-Based Strategy
然后保存修改, 返回Jenkins系统设置页面,可以看到Manage and Assign Roles
设置。
2、创建Role
进入Manage and Assign Roles
设置, 选择Manage Roles
:
接下来,我们演示一下有问题版本:
创建Gobal Role devops
:
- 全局Read权限
- 查看凭据权限
- 代理Build权限
- 任务构建、取消、配置、读、工作目录权限
- 视图无权限
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bHfkfx9z-1679729768333)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230324073954562.png)]
创建item roles devops
: 使用正则表达式匹配关联的一组项目
- 匹配以devops01-开头且任意结尾的作业生效。
- 查看凭据权限
- 任务构建、取消、配置、读取、工作目录权限
- 视图读权限
授权Role
我们来创建一个devopsuser1
用户,密码为admin
这里使用刚才创建的devopsuser1
用户登入系统:
可以看到,此时效果不符合预期。
那么是什么问题呢?
这里要给Gobal Role devops
把作业的读
权限给去掉,因为全局配置会覆盖局部配置的:
因此,这里最终统一的配置为:
创建Gobal Role devops
:
- 全局Read权限
- 查看凭据权限
- 代理Build权限
- 任务构建、取消、配置、工作目录权限
- 视图无权限
创建item roles devops
: 使用正则表达式匹配关联的一组项目
- 匹配以devops01-开头且任意结尾的作业生效。
- 查看凭据权限
- 任务构建、取消、配置、读取、工作目录权限
- 视图读权限
这里再次测试:
使用devopsuser1用户登入系统,只能看到devops-组织的项目
符合预期。
如果未赋予用户Global Role "read"会报错。
测试结束。😘
8、Jenkins 凭据管理
-
用途: 存储构建需要与其他系统认证所使用的账户或者密码信息。
-
类型:
- ssh-key
- secret-text
- secret-file
- username/password
- ……
-
Username with password
类型:存储Harbor或者其他系统的用户名和密码。 -
GitLab API token
类型:存储Gitlab的用户API token。 -
Secret text
类型:可以用来存储OpenShift等系统中的token。 -
Certificate
类型:可以用户存储证书,例如k8s的用户证书。
凭据管理,这个在装pipelin插件就会装好了的。
其实,jenkins的凭据管理不是很安全,但总比明文密码要强很多!
💘 实践:Jenkins 凭据管理-2023.3.25(测试成功)
1、创建凭据:
保存凭据, 后面流水线中使用凭据的原理是根据凭据ID进行定位的。
注意:可以指定
ID
,如果不指定,默认是一串uid值。
2、使用凭据:
图形化界面,其实我们不推荐大家使用了!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zsBL6VNa-1679729768336)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20220415202246816.png)]
以后都是使用流水线pipeline:
后期流水线里,我们就是用这种方式去写,这样的话,它就会把这个凭据对应的账号和密码都赋予给对应的变量,我们就可以像使用环境变量一样去调用username和password。
测试结束。
9、Jenkins数据备份
- 备份方式
- 编写备份脚本,通过Crontab脚本备份;
- 通过Jenkins插件进行定时备份;
- 备份排除范围
- workspace目录
- 备份触发时间
- 每天凌晨 1-2点,保留最近3、5天的备份
备份的目录是JENKINS_HOME目录
, 可以通过编写脚本结合Crontab定时任务自动备份。或者使用Jenkins的插件进行备份。
也可以使用rsync同步;
备份时,插件目录很大。如果你的插件时用docker镜像来管理的话,那么插件是不用备份的,你只需要备份下job和credentials就行。
介绍下使用ThinBackup
插件进行备份, 安装插件然后重启jenkins。
系统设置, 最下方:
填写备份目录,此目录需要存在哦:
点击立即备份:
进入到备份的目录查看备份文件: 出现备份压缩包成功。
可以基于时间点进行数据还原:
10、Jenkins版本升级
- 不要跨太高的版本号
- 数据目录要使用同一个
- 升级前进行数据目录备份
- RPM部署: rpm –uvh
- Docker部署: 替换新版本镜像
对于升级可能是最容易出现问题的地方。 升级后重启Jenkins无法正常运行的情况出现过很多。 大部分是由于插件的版本导致的, 这个怎么排查呢?可以通过Jenkins logs日志定位具体的问题。
对于升级, 我们要升级一个版本后,就到插件管理更新插件。 然后再升级,再更新插件,再升级…循环。 这样可以减少因为插件的原因导致的jenkins升级后无法运行。–先升级jenkins版本,再升级插件
- 跨小版本升级;
- 升级前备份数据目录;
docker方式不是的Jenkins可以直接替换docker镜像, rpm方式部署的应用可以在yum源中下载jenkins的安装包,然后rpm -Uvh更新升级。
11、Jenkins BlueOcean
Blue Ocean减少了混乱而且进一步明确了团队中每个成员
Blue Ocean 的主要特性包括:
-
Jenkins 可视化页面
-
美观 简洁
-
持续交付(CD)Pipeline的 复杂可视化 ,可以让您快速直观地理解管道状态。
-
Pipeline 编辑器 - 引导用户通过直观的、可视化的过程来创建Pipeline,从而使Pipeline的创建变得平易近人。
-
个性化 以适应团队中每个成员不同角色的需求。
-
在需要干预和/或出现问题时 精确定位 。Blue Ocean 展示 Pipeline中需要关注的地方,简化异常处理,提高生产力。
-
本地集成分支和合并请求, 在与GitHub 和 Bitbucket中的其他人协作编码时实现最大程度的开发人员生产力。
官方文档:https://jenkins.io/zh/doc/book/blueocean/getting-started/
BlueOcean也是需要很多很多的依赖,后来我们就自己开发了devops平台。
💘 实践:Jenkins BlueOcean安装-2023.3.25(测试成功)
BlueOcean是一套Jenkins的web ui页面,比原始的ui更美观和简洁一些。 需要安装插件,重启jenkins
进入BlueOcean
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xSAogxWF-1679729768339)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/tstmp_20230325122303.png)]
运行Pipeline
查看构建日志
关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码
x2675263825 (舍得), qq:2675263825。
🍀 微信公众号
《云原生架构师实战》
🍀 语雀
https://www.yuque.com/xyy-onlyone
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G0bPs675-1679729768340)(https://bucket-hg.oss-cn-shanghai.aliyuncs.com/img/image-20230306221144511.png)]
🍀 csdn
https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421
🍀 知乎
https://www.zhihu.com/people/foryouone
最后
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!
更多推荐
所有评论(0)