ansible

老男孩教育61期--week12--系统批量管理服务(ansible)

01. 课程介绍部分:

1)ansible软件作用概述
2)ansible软件功能组成
3)ansible软件安装配置 (hosts 配置主机清单信息)
4)ansible软件应用方法 (模块 剧本)
5)ansible自动部署服务 (rsync nfs sersync)
6)ansible扩展配置说明
7)ansible剧本角色配置

02. 知识重点回顾:

1)网络知识部分补充
   a 网络重要协议原理  ARP
   b 网络知识概念分类  IP  分类  子网划分
   c 网络上网原理介绍  企业上网原理(vlan)  虚拟主机上网原理(nat 桥接 仅主机)
   d 系统网络路由配置  
2)远程管理服务概念    SSH telnet
3)远程连接方式说明    基于口令  基于密钥(私钥 公钥)
4)基于密钥连接配置    利用批量分发公钥脚本
5)远程服务配置文件    什么是监听信息
6)远程入侵防范案例    

03. ansible程序服务概念介绍

ansible软件概念:实现批量管理
串行批量管理:yum install -y nginx  --> web01(01) web02 web03
并行批量管理:yum install -y nginx  --> web01 
                                        web02 
                                        web03
ansible软件作用:
1)批量管理系统服务
2)批量管理服务部署
3)批量分发数据信息
4)批量采集数据信息

ansible软件对运维帮助:
01. 提高工作的效率
02. 提高工作准确度
03. 减少维护的成本
04. 减少重复性工作

ansible软件特点说明:
01. 管理端不需要启动服务程序
02. 管理端不需要编写配置文件
03. 服务功能强大

分为:普通开源版(500台-(200~300)) saltstack
      企业架构版 管理端(总) --- 分支管理端  --- 服务器主机     

04. ansible程序功能组成

ansible主机清单  inventory
ansible模块功能  module     yum  cron  service  copy  script  shell     命令
ansible剧本编写  playbook   让多件事进行汇总                            脚本

05. ansible软件安装部署

准备工作:
01. 主机公钥分发完毕
02. 确认epel源更新完毕
yum install -y ansible
/etc/ansible/ansible.cfg   默认配置文件
/etc/ansible/hosts         主机清单       能管理哪些主机
/etc/ansible/roles         角色目录       规范化配置
/usr/bin/ansible
/usr/bin/ansible-playbook

06. ansible程序主机清单配置:/etc/ansible/hosts

第一种方式:简单配置
   172.16.1.7
   172.16.1.31
   172.16.1.41
   
第二种方式:分组配置
[web]
172.16.1.7
[data]
172.16.1.31
172.16.1.41
   
第三种方式:符号匹配主机信息
172.16.1.1 172.16.1.2  .. 172.16.1.200
[data]
172.16.1.[30:45]
web[01:30]
   
第四种方式:配置特殊变量信息
特殊变量官方参考:https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
[web]
172.16.1.7  ansible_user=root  ansible_password=123456  ansible_port=22
[web]
web01  ansible_host=172.16.1.7   ansible_user=root  ansible_password=123456  ansible_port=22
   
第五中方式:嵌入式配置:
第一种嵌入式:嵌入子组
[rsync:children]
rsync_server
rsync_client
   
[rsync_server]
172.16.1.41
   
[rsync_client]
172.16.1.7
172.16.1.31
   
第二种嵌入式:嵌入变量 (剧本)
[rsync_client]
172.16.1.7  
172.16.1.31
[rsync_client:vars]
ansible_user=root
ansible_password=123456
   

ansible管理多台主机命令:
语法格式:
ansible  主机信息  -m(指定使用的模块)  模块名称  -a(指定完成的动作)  "动作操作信息"
[root@m01 scripts]# ansible 172.16.1.7 -m command -a "hostname"
172.16.1.7 | CHANGED | rc=0 >>
web01

07. ansible程序模块功能 总的模块 2000+

常用的模块:10+
command  shell(万能) copy     fetch  file 
script   yum           service  cron   user 
mount    ping          setup    debug  mysql(相关)
archive??(压缩)  unarchive(解压)


1) command --- 可以批量管理主机执行命令(默认)
官方:https://docs.ansible.com/ansible/latest/modules/command_module.html#command-module
参数:
chdir    --- 在执行命令操作前进行切换目录
[root@m01 scripts]# ansible 172.16.1.7 -m command -a "chdir=/tmp pwd"
172.16.1.7 | CHANGED | rc=0 >>
/tmp

creates  --- 判断一个文件是否存在,如果存在,后续命令不会被执行
[root@m01 scripts]# ansible 172.16.1.7 -m command -a "creates=/etc/hosts touch /etc/hosts01"
172.16.1.7 | SUCCESS | rc=0 >>
skipped, since /etc/hosts exists

removes  --- 判断一个文件是否存在,如果不存在,后续命令不会被执行

2)shell   --- 可以批量管理主机执行命令(万能模块)
官方:https://docs.ansible.com/ansible/latest/modules/shell_module.html#shell-module
参数:
chdir    --- 在执行命令操作前进行切换目录
[root@m01 scripts]# ansible 172.16.1.7 -m shell -a "chdir=/tmp pwd"
172.16.1.7 | CHANGED | rc=0 >>
/tmp

creates  --- 判断一个文件是否存在,如果存在,后续命令不会被执行
[root@m01 scripts]# ansible 172.16.1.7 -m shell -a "creates=/etc/hosts touch /etc/hosts01"
172.16.1.7 | SUCCESS | rc=0 >>
skipped, since /etc/hosts exists

removes  --- 判断一个文件是否存在,如果不存在,后续命令不会被执行 

3)script --- 批量执行脚本信息
shell  -- 批量多个主机执行脚本
第一步:编写脚本
[root@m01 scripts]# cat yum.sh 
#!/bin/bash
yum install -y iftop

第二步:将脚本进行分发
第三步:脚本直接运行(授予执行权限)
第四步:批量运行脚本
ansible rsync -m shell -a "/server/scripts/yum.sh"

script -- 批量多个主机执行脚本
第一步:编写脚本
[root@m01 scripts]# cat yum.sh 
#!/bin/bash
yum install -y iftop
第二步:批量运行脚本
ansible rsync -m script -a "/server/scripts/yum.sh"

4)yum  --- 批量部署软件程序
官方:https://docs.ansible.com/ansible/latest/modules/yum_module.html#yum-module
参数: 
name:  指定安装软件的名称
state:  指定软件安装或卸载
ansible 172.16.1.7 -m yum -a "name=htop state=installed"

5)service  --- 批量管理服务运行状态
官方: 
name:   指定服务名称
state:  started  restarted reloaded stopped
enabled:设置服务是否开机自动运行  yes开机自动运行  no
ansible 172.16.1.7 -m service -a "name=sshd state=started enabled=yes"

6)copy     --- 将管理端主机数据文件,分发给被管理端
            --- 将被管理端目录中的数据移动到其他目录   172.16.1.31  /etc/hosts  /tmp
官方:https://docs.ansible.com/ansible/latest/modules/copy_module.html#copy-module
参数: 
src:   将管理端什么数据进行分发
dest:  将数据分发到远程主机什么路径中
owner: 传输文件之后修改文件属主权限
group: 传输文件之后修改文件属组权限
mode:  直接修改文件权限位(12位)
ansible 172.16.1.7 -m copy -a "src=/etc/hosts dest=/tmp owner=oldboy group=oldboy mode=666"

backup:在分发传输文件之前,将源文件进行备份,按照时间信息备份
remote_src: no 默认从管理端找寻数据进行分发  yes 默认从被管理端找寻数据进行分发
ansible 172.16.1.7 -m copy -a "dest=/etc/hosts src=/tmp/hosts.bak owner=oldboy group=oldboy mode=666 remote_src=yes"

content:在分发文件时,在文件中创建简单信息
ansible 172.16.1.7 -m copy -a "content='oldboy123' dest=/tmp/oldboy.txt  mode=600"

7)fetch   --- 将远程主机数据进行拉取 
参数: 
src:   远程主机的数据信息
dest:   本地保存数据的路径   dest-->path
  
8) file    --- 修改文件属性信息 用于创建数据信息(文件  目录  链接文件  删除数据)
官方:https://docs.ansible.com/ansible/latest/modules/file_module.html#file-module
参数: 
path:    指定处理数据路径信息
owner:   传输文件之后修改文件属主权限
group:   传输文件之后修改文件属组权限
mode:    直接修改文件权限位(12位)
state:   touch(创建文件)  directory(创建目录) hard(创建硬链接) link(创建软连接)
         absent(删除数据)

创建文件/目录:
ansible 172.16.1.7 -m file -a "path=/tmp/oldboy.txt state=touch"
ansible 172.16.1.7 -m file -a "path=/tmp/oldboy_dir state=directory"

创建链接文件
ansible 172.16.1.7 -m file -a "src=/tmp/oldboy.txt  path=/tmp/oldboy_soft_link.txt state=link"
ansible 172.16.1.7 -m file -a "src=/tmp/oldboy.txt  path=/tmp/oldboy_hard_link.txt state=hard"

删除数据信息
ansible 172.16.1.7 -m file -a "path=/tmp/oldboy_dir state=absent"

9)mount  --- 挂载模块
官方:https://docs.ansible.com/ansible/latest/modules/mount_module.html#mount-module
参数: 
src:    指定需要被挂载存储数据信息 分区/共享目录
path:   挂载点目录
fstype: 指定挂载后,文件系统类型   ext3 ext4 xfs nfs
state:  mounted  present  unmounted  absent     
mounted 特点:推荐
01. 会立即进行挂载操作
02. 可以实现永久挂载
present 特点:
01. 可以实现永久挂载

unmounted 特点: 
01. 进行临时卸载
absent 特点:
01. 进行临时卸载 
02. 进行永久卸载
03. 挂载点目录会被删除

ansible 172.16.1.7 -m mount -a "src=172.16.1.31:/data path=/mnt state=mounted fstype=nfs"
ansible 172.16.1.7 -m mount -a "src=172.16.1.31:/data path=/mnt state=unmounted fstype=nfs"

10)cron  --- 批量设置定时任务信息
官方:https://docs.ansible.com/ansible/latest/modules/cron_module.html#cron-module
参数:
name:   定义定时任务注释信息
minute: 表示分钟信息
hour:   表示小时信息
day:    表示日期信息
month: 表示月份信息
weekday: 表示星期信息
job:    表示定义任务信息
state:  present 创建定时任务   absent 删除定时任务
disabled: 让定时任务临时失效

crontab -e 
# xxxx
* * * * *   任务信息 &>/dev/null

利用ansible编写时间同步定时任务:每隔5分钟,进行时间同步:
ansible 172.16.1.7 -m cron -a "name='date sync' minute=*/5  job='ntpdate ntp1.aliyun.com &>/dev/null'"

删除定时任务:
ansible 172.16.1.7 -m cron -a "name='date sync' state=absent"

注释定时任务:
ansible 172.16.1.7 -m cron -a "name='date sync' minute=*/5  job='ntpdate ntp1.aliyun.com &>/dev/null'  disabled=yes"

11)user --- 批量创建用户模块  group
官方:
参数: 
name:    指定用户名称
uid:    指定用户uid信息
group:   指定属于哪个组  主要组
groups:  指定属于哪个组  附属组
password:  指定用户密码信息(必须密文的信息)
shell:  指定用户shell信息  /sbin/nologin
create_home:  no 不创建家目录

创建用户:
ansible 172.16.1.7 -m user -a "name=oldgirl uid=777 group=oldboy groups=oldboy666 shell=/sbin/nologin create_home=no"

删除用户:
ansible 172.16.1.7 -m user -a "name=oldgirl state=absent"

设置用户密码信息:
ansible 172.16.1.7 -m user -a 'name=oldgirl password="$6$oldboy123$W3jkmkkVTr.9UStm4S50RT2uIEjB/4GEtaAeVCSZ..uWVN1YGxHvluss9JVfAPV0gSJoGn1qAfxGyttIsTjcz0"'

08. ansible剧本编写说明

剧本作用:
1)将多个模块操作功能进行整合
2)实现重复工作简单化(提高工作效率)
3)实现批量管理特殊需求

如何编写剧本:角色信息  任务信息
hosts: 角色信息 
tasks: 任务信息 

剧本编写规范: pythonyaml
1) 缩进  两个空格表示一个缩进关系
   hosts: xxx 
     tasks
       oldboy
2) 冒号  创建键值对 key--value  age: 18  冒号后面必须有空格  age:18(错误)
         PS: 冒号结尾  冒号出现在描述和注释信息中
3)列表  相同的信息/类别出现多次 多个列表利用短横线空格
         - 60期人名信息  张三 李四
         - 61期人名信息  张三 李四

09. 实践编写剧本:

练习01:批量创建oldgirl01用户(rsync服务端)  批量分发/etc/fstab --> /tmp(rsync服务端和客户端)   
第一个历程:编写角色信息(设置主机清单)
[rsync:children]
rsync_server
rsync_client

[rsync_server]
172.16.1.41

[rsync_client]
172.16.1.7
172.16.1.31

第二个历程:编写剧本信息:
mkdir /etc/ansible/ansible_playbook -p
vim test01.yaml
- hosts: rsync_server
  tasks:
    - user: name=oldgirl01
    - group: name=oldgirl01

- hosts: rsync
  tasks:
    - copy: src=/etc/fstab dest=/tmp/

第三个历程:测试剧本
ansible-playbook  --syntax-check  test01.yaml    --- 检查语法结构
ansible-playbook  -C test01.yaml                 --- 模拟执行剧本

第四个历程:执行剧本
ansible-playbook   test01.yaml

练习题2:编写rsync服务一键化安装的剧本
服务端:
01. 安装软件        rsync 
02. 编写配置文件    rsyncd.conf 
03. 创建虚拟用户    rsync 
04. 创建备份目录    /backup  rsync.rsync 
05. 创建密码文件    /etc/rsync.password  600   rsync_backup:oldboy123
06. 启动程序服务    systemctl start/enable rsyncd
客户端:
01. 创建密码文件    /etc/rsync.password  600   oldboy123

修改主机清单:
[rsync_server]
172.16.1.41

[rsync_client]
172.16.1.7
172.16.1.31
    
编写剧本
- hosts: rsync_server
  tasks:
    - name: 01:安装部署软件
      yum: name=rsync state=installed 
    - name: 02:分发配置文件
      copy: src=./rsyncd.conf dest=/etc/rsyncd.conf
    - name: 03:创建虚拟用户
      user: name=rsync shell=/sbin/nologin create_home=no
    - name: 04:创建备份目录
      file: path=/backup state=directory owner=rsync group=rsync
    - name: 05:创建密码文件
      copy: content='rsync_backup:oldboy123' dest=/etc/rsync.password  mode=600
    - name: 06:启动服务程序
      service: name=rsyncd state=started enabled=yes

- hosts: rsync_client
  tasks:
    - name: 01:创建密码文件
      copy: content='oldboy123' dest=/etc/rsync.password mode=600

练习题3:编写nfs服务一键化安装的剧本
服务端:
01. 安装软件        nfs-utils rpcbind 
02. 编写配置文件    exports  
03. 创建存储目录    /data  nfsnobody.nfsnobody 
04. 启动程序服务    systemctl start/enable rpcbind-->nfs
客户端:
01. 安装软件        nfs-utils
02. 挂载操作        /data   /mnt    

修改主机清单:
[nfs_server]
172.16.1.31

[nfs_client]
172.16.1.7
172.16.1.41

编写剧本:
- hosts: nfs_server
   tasks:
     - name: 01:安装部署软件
       yum: 
         name:
           - nfs-utils 
           - rpcbind   
         state: installed 
     - name: 02:分发配置文件
       copy: src=./exports dest=/etc/exports
     - name: 03:创建存储目录
       file: path=/data state=directory owner=nfsnobody group=nfsnobody
     - name: 04:启动服务程序
       service:
         name: 
           - rpcbind
           - nfs
         state: started
         enabled: yes
 
 - hosts: nfs_client
   tasks:
     - name: 01:安装部署软件
       yum: name=nfs-utils state=installed
     - name: 02:目录进行挂载
       mount: src=172.16.1.31:/data path=/mnt fstype=nfs state=mounted

练习题4:编写sersync服务一键化安装的剧本

10. 剧本编写扩展说明:

a 剧本编写设置变量   OK
b 剧本编写注册信息   OK
c 剧本编写循环功能
d 剧本编写判断功能  
e 剧本编写忽略错误
f 剧本编写标签功能
g 剧本编写忽略采集
h 剧本编写触发功能
i 剧本编写汇总功能

1)剧本设置变量功能:
第一种:在剧本中设置变量:
- hosts: 172.16.1.7
  vars:
    dir_info: /etc/
    dest_file: hosts_bak
  tasks:
    - name: 传输文件
      copy: src={{ dir_info }}hosts  dest={{ dir_info  }}{{ dest_file }}

第二种:在命令行指定变量
- hosts: 172.16.1.7
  tasks:
    - name: 传输文件
      copy: src={{ dir_info }}hosts  dest={{ dir_info  }}{{ dest_file }}
ansible-playbook -e dir_info=/etc/ -e dest_file=hosts_bak test_变量功能.yaml

第三种:在主机清单文件中指定
[rsync_client]
172.16.1.7
172.16.1.31
[rsync_client:vars]
dir_info=/etc/
dest_file=hosts_bak

- hosts: 172.16.1.7
  tasks:
    - name: 传输文件
      copy: src={{ dir_info }}hosts  dest={{ dir_info  }}{{ dest_file }}

结论:三种配置方法:命令行配置变量最优先,剧本文件中配置其次,主机清单中配置最不优先

2) 剧本编写注册信息
编写剧本: 
- hosts: 172.16.1.7
  tasks:
    - name: 启动SSH服务程序
      service: name=sshd state=started
    - name: 检查SSH端口信息
      shell: netstat -lntup|grep 22
      register: port_info
    - name: 显示SSH端口信息
      debug: msg={{ port_info.stdout_lines }}

TASK [显示SSH端口信息] *************************************************************************************
ok: [172.16.1.7] => {
    "msg": [
        "tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      6747/sshd           ", 
        "tcp6       0      0 :::22                   :::*                    LISTEN      6747/sshd           "
    ]
}


补充:ansible结果颜色含义
绿色: 命令执行成功  没有对远程主机做任何修改
黄色: 命令执行成功  大部分情况表示对远程主机做了改动
红色: 命令执行失败
粉色: 建议进行操作的方法
蓝色: 显示命令或剧本执行的过程

SSH服务进程信息:
sshd: root@pts/0      --- ssh连接会话进程
sshd: root@notty      --- ansible建立ssh连接会话进程
/usr/sbin/sshd -D     --- 显示服务程序进程

ansible帮助文档如何查看:
ansible-doc -l        --- 查看所有ansible模块信息
ansible-doc -s 模块   --- 查看指定模块详细说明
ansible-doc 模块    --- 查看指定模块更加详细说明

如何创建用户密文密码
方法一:利用ansible的模块功能
ansible all -i localhost, -m debug -a "msg={{ 'mypassword' | password_hash('sha512', 'mysecretsalt') }}"
mypassword:  指定明文密码信息
mysecretsalt:加密计算方式(辅助加密)

ansible all -i localhost, -m debug -a "msg={{ '123456' | password_hash('sha512', 'oldboy123') }}"
localhost | SUCCESS => {
  "msg": "$6$oldboy123$W3jkmkkVTr.9UStm4S50RT2uIEjB/4GEtaAeVCSZ..uWVN1YGxHvluss9JVfAPV0gSJoGn1qAfxGyttIsTjcz0"
}

方法二:利用python模块功能(环境准备)
yum install -y python-pip
pip install passlib   -- 安装不上(pip源需要更新)
==================================================
更新pip源:
pypi
配置方法
在文件

~/.pip/pip.conf
中添加或修改:

[global]
index-url = https://mirrors.aliyun.com/pypi/simple/

[install]
trusted-host=mirrors.aliyun.com
==================================================  
python -c "from passlib.hash import sha512_crypt; import getpass; print(sha512_crypt.using(rounds=5000).hash(getpass.getpass()))"

11. 课程知识总结梳理:

1)ansible软件概念介绍
   作用  功能  特点
2)ansible软件部署安装
   a 公钥分发完毕
   b epel源
3)ansible软件配置方法
   主机清单信息  5种方式
4)ansible模块应用方法
   command shell script file copy yum service cron mount user debug 
5)ansible剧本编写应用
   剧本编写格式  hosts: 角色 tasks: 任务 
   剧本编写规范  缩进 冒号 列表
6)实现了服务一键化部署 nfs rsync
7)ansible剧本扩展编写
   a 剧本变量设置功能
   b 剧本变量注册功能

作业:

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

推荐阅读更多精彩内容