Ansible 学习笔记

一、安装

Ansible 是默认来通过 ssh 协议来管理机器的。安装好 ansible 之后不需要启动或者允许一个后台进程,只要安装好(哪怕是在我们的笔记本上,只要安装了 python 2.6 或2.7),就可以通过这台机器管理一组远程的机器。在被管理的远程机器上,也不需要安装运行任何软件(安装了 python 2.5 以上),因此升级 ansible 版本很容易。

mac 上使用 pip 来安装:

sudo pip install ansible

二、Ansible 如何通过 ssh 与远程服务器连接

Ansible 1.3 及之后的版本默认会在本地的 OpenSSH 可用时会尝试与其进行远程通讯。会弃用 ControlPersist(新的性能特性),Kerberos,和在~/.ssh/config 中配置选项如 Jump Host setup。在Ansible 1.2 及之前的版本,默认将会使用 paramiko. 本地OpenSSH必须通过-c ssh 或者 在配置文件中设定。

对于远程设备,Ansible会默认假定你使用 SSH Key(推荐)但是密码也一样可以.通过在需要的地方添加 –ask-pass选项 来启用密码验证.如果使用了sudo 特性,当sudo需要密码时,也同样适当的提供了–ask-sudo-pass选项.

注意:任何管理系统受益于被管理的机器在主控机附近运行.如果在云中运行,可以考虑在使用云中的一台机器来运行Ansible.

三、使用

1. 基本用法

编辑(或创建) /etc/ansible/hosts 并在其中加入一个或多个远程系统。public SSH key必须在这些系统的authorized_keys中,或者 -i 指定 inventory 文件

   $ ansible -i /etc/ansible/host all [options]

其中 -i 用来指定 inventory 文件,默认就是使用 /etc/ansible/hosts,其中 all 是针对 hosts 定义的所有主机执行,这里也可以指定 hosts 中定义的组名或模式。

[options]

-m:指定模块(默认是command模块)

-a:指定模块的参数

-u:指定执行远程主机的用户(默认是root)ansible.cfg中可配置

-k:指定远程主机的密码

-s:以sudo方式运行

-U:sudo到那个用户(默认是root)

-f:指定多少个进程并发处理(默认是5)

--private-key=/path:指定私钥路径

-T:ssh连接超时时间(默认(10s)

-t:日志输出到该目录

-v:显示详细信息

2. Inventory

Inventory 文件用来定义你要管理的主机,其默认位置在 /etc/ansible/hosts,如果不保存在默认位置,也可通过 -i 选项指定。被管理的机器可以通过其 IP 或域名指定。每个中括号里代表一个分组,其下的机器列表都归属于这个分组,直到出现下一个中括号为止。通常我们按组来执行任务,同一组受控服务器应用相同的配置。一台服务器也可以归属到多个组,以完成多个功能角色的配置。低耦合、模块化,非常灵活!如下定义一个分组 [gocd-agent] 和 [elk] 分组,并给定一组主机。

[gocd-agent]
vm-gocd-agent-batman ansible_ssh_host=shell-gocd-agent-1
vm-gocd-agent-flashman ansible_ssh_host=shell-gocd-agent-2
vm-gocd-agent-spiderman ansible_ssh_host=shell-gocd-agent-3

[elk]
vm-elk ansible_ssh_host=shell-elk

使用 ansible 来测试被管理的节点的网络连通情况, -u 是用户,-k 是使用密码登录

ansible -i /etc/ansible/hosts test -u root -m ping -k

或者

$ ansible test -u root -m ping -k

或者使用--private-key 测试连通 inventory

ansible -m ping -i inventory_infra.ini elk --private-key ./keys/decoded_keys/inf_key -u dcsp

输出的正确的信息如下:


image.png

注意:全局用户的设置见配置文件 /etc/ansible/ansible.cfg,修改[defaults]段落里的 remote_user 的值即可。修改 remote_port 可以来定义 ansible 使用非默认的22/SSH 端口来连接和管理。如果没有给被管理节点或者被管理组进行特定设置时,ansible将默认使用全局设置。

1)格式:

# 指定IP地址并且支持通配;
[web01] ###[]中是组名,用于对系统进行分类,便于对不同系统进行个别管理。一个系统可以属于不同的组,例如一台服务器可以同时属于 webserver 和 dbserver组.这时属于两个组的变量都可以为这台主机所用
192.168.10.1
192.168.10.[1-9]
 
# 指定IP加端口;
[web02]
192.168.20.1:5252
 
# 指定域名,必须可以解析;
[web03]
www.example.com
 
# 组嵌套,当执行组[weball]时就会执行它的所有子组但是子组可以独立执行;
[weball:children]
[web01]
[web02]
[web03]

常用参数如下:

ansible_connection=ssh                #指定连接类型,可以使local、ssh、paramiko;
ansible_ssh_user=root                 #用于指定远程主机的账号;
ansible_ssh_pass=password             #指定连接到主机的密码连-k都省了;
ansible_sudo_pass       #sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass);
ansible_sudo_exe (new in version 1.8)  #sudo 命令路径(适用于1.8及以上版本)
ansible_ssh_host         #将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置;
ansible_ssh_port=23                   #用于指定远程主机SSH端口;
ansible_ssh_privare_key_file=/PATH    #用于指定key文件;
aost_key_checking=false               #当第一次连接远程主机,跳过yes/no环节;
ansible_shell_type                    #指定目标系统的shell(默认为sh);
ansible_python_interpreter=/          #指定Python解释器路径(默认/USR/BIN/PYTHON);
ansible_shell_type    #目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'.

使用方式:

[web01]###对于每一个 host,可以选择连接类型和连接用户名:
localhost              ansible_connection=local
other1.example.com     ansible_connection=ssh        ansible_ssh_user=mpdehaan
192.168.10.1 ansible_ssh_user=root 
192.168.10.[1] ansible_ssh_pass=password

2)主机变量和组变量
Inventory除了上述的基本功能外,还可以在主机后面定义变量以便于在playbook中使用,例如:

[web01]
192.168.10.1 http_port=80 maxRequest=100
192.168.10.2 http_port=8080 maxRequest=200

不光对主机可以定义变量,对组也可以定义变量。组变量是指赋予给组内所有主机,都可以在playbook中使用,格式如下:

[web01:vars]##这就是针对web01组设置的组变量,:vars是固定格式,组内的所有变量都可以给组内的主机使用。
http_port01=80
http_port02=8080

3)分文件定义 group_vars 和 host_vars
在 inventory 主文件中保存所有的变量并不是最佳的方式.还可以保存在独立的文件中,这些独立文件与 inventory 文件保持关联. 不同于 inventory 文件(INI 格式),这些独立文件的格式为 YAML. group_vars/ 和 host_vars/目录可放在 inventory 目录下,或是 playbook 目录下. 如果两个目录下都存在,那么 playbook 目录下的配置会覆盖 inventory 目录的配置
4)关于动态 Inventory,之后遇到要使用时 查资料

3. 常用模块

根据Ansible官方的分类,将模块分为核心模块和额外模块
1)ping 模块
测试主机是否是通的,上面介绍过
2)setup 模块
主要是用于获取主机信息。在playbooks里经常会用到的一个参数。gather_facts就与该模块相关,setup模块下经常使用的一个参数是filter参数,具体使用示例如下:

# 查看主机内存信息
$ ansible test -m setup -a 'filter=ansible_*_mb'
 
# 查看地接口为eth0-2的网卡信息
$ ansible test -m setup -a 'filter=ansible_eth[0-2]'
 
# 将所有主机的信息输入到/tmp/facts目录下,每台主机的信息输入到主机名文件中(/etc/ansible/hosts里的主机名)
$ ansible test -m setup --tree /tmp/facts

操作示例:


image.png

3)file 模块
file模块主要用于远程主机上的文件操作,具体使用示例如下:

# 创建一个软连接
$ ansible test -m file -a "src=/etc/fstab dest=/tmp/fstab state=link"
 
# 删除一个文件
$ ansible test -m file -a "path=/tmp/fstab state=absent"
 
# 创建一个文件
$ ansible test -m file -a "path=/tmp/test state=touch"

常用参数:

force        #需要在两种情况下强制创建软链接,一种是源文件不存在但之后会建立的情况下;另一种是目标软链接已存在,需要先取消之前的软链,然后创建新的软链,有两个选项:yes|no;
group        #定义文件/目录的属组;
mode         #定义文件/目录的权限;
owner        #定义文件/目录的属主;
path         #必选项,定义文件/目录的路径, required;
recurse      #递归的设置文件的属性,只对目录有效;
src          #要被链接的源文件的路径,只应用于state=link的情况;
dest         #被链接到的路径,只应用于state=link的情况;
state:
  directory  #如果目录不存在,创建目录;
  file       #即使文件不存在,也不会被创建;
  link       #创建软链接;
  hard       #创建硬链接;
  touch      #如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间;
  absent     #删除目录、文件或者取消链接文件;
image.png

4)copy 模板
复制文件到远程主机,如下示例:

$ ansible test -m copy -a "src=/srv/myfiles/foo.conf dest=/etc/foo.conf owner=foo group=foo mode=0644"
$ ansible test -m copy -a "src=/mine/ntp.conf dest=/etc/ntp.conf owner=root group=root mode=644 backup=yes"
$ ansible test -m copy -a "src=/mine/sudoers dest=/etc/sudoers validate='visudo -cf %s'"

常用参数:

backup          #在覆盖之前将原文件备份,备份文件包含时间信息。有两个选项:yes|no;
content         #用于替代"src",可以直接设定指定文件的值;
directory_mode  #递归的设定目录的权限,默认为系统默认权限;
force           #如果目标主机包含该文件,但内容不同,如果设置为yes,则强制覆盖,如果为no,则只有当目标主机的目标位置不存在该文件时,才复制。默认为yes;
others          #所有的file模块里的选项都可以在这里使用;
dest            #必选项,要将源文件复制到的远程主机的绝对路径,如果源文件是一个目录,那么该路径也必须是个目录, required;
src             #要复制到远程主机的文件在本地的地址,可以是绝对路径,也可以是相对路径。如果路径是一个目录,它将递归复制。在这种情况下,
                #如果路径使用"/"来结尾,则只复制目录里的内容,如果没有使用"/"来结尾,则包含目录在内的整个内容全部复制,类似于rsync;
validate        #复制文件前进行验证,文件的路径的验证是通过"%s";

5)service 模块
用于管理主机服务,能够同时管理CentOS6和CentOS7,不区分CentOS6的service和CentOS7的systemctl,如下实例:

$ ansible test -m service -a "name=httpd state=started enabled=yes"
$ ansible test -m service -a "name=foo pattern=/usr/bin/foo state=started"
$ ansible test -m service -a "name=network state=restarted args=eth0"

常用参数:

name        #必选项,服务名称;
state       #对当前服务执行启动,停止、重启、重新加载等操作(started, stopped, restarted, reloaded);
enabled     #是否开机启动yes|no;
pattern     #定义一个模式,如果通过status指令来查看服务的状态时,没有响应,就会通过ps指令在进程中根据该模式进行查找,如果匹配到,则认为该服务依然在运行;
runlevel    #运行级别;
arguments   #给命令行提供一些选项;
sleep       #如果执行了restarted,在则stop和start之间沉睡几秒钟;

6)cron 模块
用于管理计划任务,如下:

$ ansible test -m cron -a 'name="a job for reboot" special_time=reboot job="/some/job.sh"'
$ ansible test -m cron -a 'name="yum autoupdate" minute=*/1 hour=* day=* month=* weekday=* user="root" job="date>>/tmp/1.txt"'
$ ansible test -m cron -a 'name="yum autoupdate" minute=1 hour=*/1 day=* month=* weekday=* user="root" job="date>>/tmp/1.txt"'
$ ansible test -m cron -a 'backup="True" name="test" minute="0" hour="5,2" job="ls -alh > /dev/null"'
$ ansilbe test -m cron -a 'cron_file=ansible_yum-autoupdate state=absent'

常用参数:

name          #该任务的描述;
backup        #对远程主机上的原任务计划内容修改之前做备份;
cron_file     #如果指定该选项,则用该文件替换远程主机上的cron.d目录下的用户的任务计划;
day           #日(1-31,,/2,……);
hour          #小时(0-23,,/2,……);
minute        #分钟(0-59,,/2,……);
month         #月(1-12,,/2,……);
weekday       #周(0-7,*,……);
job           #要执行的任务,依赖于state=present;
special_time  #指定什么时候执行,参数:reboot(重启时),yearly(每年),annually,monthly,weekly,daily,hourly;
state         #确认该任务计划是创建还是删除;
user          #以哪个用户的身份执行;

7)yum 模块
使用yum包管理器来管理软件包,如下:

# 安装最新版本的apache;
$ ansible test -m yum -a 'name=httpd state=latest'
 
# 移除apache;
$ ansible test -m yum -a 'name=httpd state=absent'
 
# 升级所有的软件包;
$ ansible test -m yum -a 'name=* state=latest '
 
# 安装整个Development tools相关的软件包;
$ ansible test -m yum -a 'name="@Development tools" state=present'
 
# 从本地仓库安装nginx;
$ ansible test -m yum -a 'name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present'
 
# 从一个远程yum仓库安装nginx;
$ ansible test -m yum -a 'name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present'

常用参数:

name               #要进行操作的软件包的名字,也可以传递一个url或者一个本地的rpm包的路径;
config_file        #yum的配置文件;
disable_gpg_check  #关闭gpg_check;
disablerepo        #不启用某个源;
enablerepo         #启用某个源;
state              #用于描述安装包最终状态,<em>present/latest</em>用于安装包,<em>absent</em>用于卸载安装包;

8)user 模块
用户管理模块,使用示例:

$ ansible test -m user -a 'name=johnd comment="John Doe" uid=1040 group=admin'

常用参数

home        #指定用户的家目录,需要与createhome配合使用;
groups      #指定用户的属组;
uid         #指定用的uid;
password    #指定用户的密码;
name        #指定用户名;
createhome  #是否创建家目录yes|no;
system      #是否为系统用户;
remove      #当state=absent时,remove=yes则表示连同家目录一起删除,等价于userdel -r;
state       #是创建还是删除;
shell       #指定用户的shell环境;

注:指定password参数时,不能使用明文密码,因为后面这一串密码会被直接传送到被管理主机的/etc/shadow文件中,所以需要先将密码字符串进行加密处理。然后将得到的字符串放到password中即可。
生成一个密码:

$ echo "123456" | openssl passwd -1 -salt $(< /dev/urandom tr -dc '[:alnum:]' | head -c 32) -stdin
$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0

用上面生成的密码创建用户

$ ansible all -m user -a 'name=foo password="$1$4P4PlFuE$ur9ObJiT5iHNrb9QnjaIB0"'

不同的发行版默认使用的加密方式可能会有区别,具体可以查看/etc/login.defs文件确认,centos 6.5版本使用的是SHA512加密算法。
9)group 模块
组管理模块,使用示例:

$ ansible all -m group -a 'name=somegroup state=present'

常用参数:

gid     #指定gid;
name    #指定组名称;
state   #操作状态,present,absent;
system  #是否为系统组;

10)filesystem 模块
在块设备上创建文件系统,示例如下:

$ ansible test -m filesystem -a 'fstype=ext2 dev=/dev/sdb1 force=yes'
$ ansible test -m filesystem -a 'fstype=ext4 dev=/dev/sdb1 opts="-cc"'

常用参数

dev     #目标块设备;
force   #在一个已有文件系统的设备上强制创建;
fstype  #文件系统的类型;
opts    #传递给mkfs命令的选项;

11)mount 模块
配置挂载点

$ ansible test -m mount 'name=/mnt/dvd src=/dev/sr0 fstype=iso9660 opts=ro state=present'
$ ansible test -m mount 'name=/srv/disk src='LABEL=SOME_LABEL' state=present'
$ ansible test -m mount 'name=/home src='UUID=b3e48f45-f933-4c8e-a700-22a159ec9077' opts=noatime state=present'
$ ansible test -a 'dd if=/dev/zero of=/disk.img bs=4k count=1024'
$ ansible test -a 'losetup /dev/loop0 /disk.img'
$ ansible test -m filesystem -a 'fstype=ext4 force=yes opts=-F dev=/dev/loop0'
$ ansible test -m mount 'name=/mnt src=/dev/loop0 fstype=ext4 state=mounted opts=rw'

常用参数:

fstype    #必选项,挂载文件的类型;
name      #必选项,挂载点;
opts      #传递给mount命令的参数;
src       #必选项,要挂载的文件;
state     #必选项;
present   #只处理fstab中的配置;
absent    #删除挂载点;
mounted   #自动创建挂载点并挂载之;
umounted  #卸载;

12) get_url 模块
主要用于从 http、ftp、https 服务器上下载文件,类似wget

$ ansible test -m filesystem -a 'url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440'

常用参数:

sha256sum                   #下载完成后进行sha256 check;
timeout                     #下载超时时间,默认10s;
url                         #下载的URL;
url_password、url_username  #主要用于需要用户名密码进行验证的情况;
use_proxy                   #使用代理,代理需事先在环境变更中定义;

13)unarchive 模块
用于解压文件的模块

$ ansible test -m unarchive -a 'src=foo.tgz dest=/var/lib/foo'
$ ansible test -m unarchive -a 'src=/tmp/foo.zip dest=/usr/local/bin copy=no'
$ ansible test -m unarchive -a 'src=https://example.com/example.zip dest=/usr/local/bin copy=no'

常用参数:

copy        #在解压文件之前,是否先将文件复制到远程主机,默认为yes。若为no,则要求目标主机上压缩包必须存在;
creates     #指定一个文件名,当该文件存在时,则解压指令不执行;
dest        #远程主机上的一个路径,即文件解压的路径;
group       #解压后的目录或文件的属组;
list_files  #如果为yes,则会列出压缩包里的文件,默认为no,2.0版本新增的选项;
mode        #解决后文件的权限;
src         #如果copy为yes,则需要指定压缩文件的源路径;
owner       #解压后文件或目录的属主;

14)script 模块
在指定节点上执行 shell、python 脚本(该脚本必须是ansible控制节点上面的)

$ ansible test -m script -a '/root/src.sh'

15)shell 模块
在指定节点上执行shell/python脚本(注意,该脚本是在远程节点)

$ ansible test -m shell -a '/bin/bash /root/dest.sh'

16)command 模块
用于执行远程系统命令,此模块为 ansible 默认执行的模块,也是常用模块之一。不支持 shell 变量,也不支持管道等。

$ ansible test -m command -a 'uname -n'

17)raw 模块
类似 command 模块,区别在于 raw 模块支持管道传递

$ ansible test -m raw -a "tail -n2 /etc/passwd | head -n1"

4. Ad-Hoc Commands

Ansible提供两种方式去完成任务,一是 ad-hoc 命令,一是写 Ansible playbook.前者可以解决一些简单的任务, 后者解决较复杂的任务。(ad-hoc 其实是一个概念性的名字,是相对于写 Ansible playbook 来说的。类似于在命令行敲入shell命令和 写shell scripts两者之间的关系)
例如:现在执行如下命令,这个命令中,atlanta是一个组,这个组里面有很多服务器,”/sbin/reboot”命令会在atlanta组下 的所有机器上执行.这里ssh-agent会fork出10个子进程(bash),以并行的方式执行reboot命令.如前所说“每次重启10个” 即是以这种方式实现:

$ ansible atlanta -a "/sbin/reboot" -f 10

确认某个服务在所有的webservers上都已经启动:

$ ansible webservers -m service -a "name=httpd state=started"

或是在所有的webservers上重启某个服务:

$ ansible webservers -m service -a "name=httpd state=restarted"

确认某个服务已经停止:

$ ansible webservers -m service -a "name=httpd state=stopped"

需要长时间运行的命令可以放到后台去,在命令开始运行后我们也可以检查运行的状态.如果运行命令后,不想获取返回的信息, 可执行如下命令:

$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"

如果你确定要在命令运行后检查运行的状态,可以使用 async_status 模块.前面执行后台命令后会返回一个 job id, 将这个 id 传给 async_status 模块:

$ ansible web1.example.com -m async_status -a "jid=488359678239.2844"

获取状态的命令如下:

$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"

其中 -B 1800 表示最多运行30分钟, -P 60 表示每隔60秒获取一次状态信息.
Polling 获取状态信息的操作会在后台工作任务启动之后开始.若你希望所有的工作任务快速启动, --forks 这个选项的值 要设置得足够大,这是前面讲过的并发进程的个数.在运行指定的时间(由-B选项所指定)后,远程节点上的任务进程便会被终止.

5. 权限提升 become

Ansible可以使用现有的权限提升系统来允许用户执行另一个任务。
Ansible允许你成为另一个用户,与登录到本机的用户或远程用户不同。这是使用现有的特权升级工具(privilege escalation tools)完成的,可能已经使用或已经配置了这些工具,如sudo,su,pfexec,doas,pbrun,dzdo,ksu等。
说明:
(1)在1.9 Ansible之前,大多数情况下都允许使用sudo和有限的su来允许登录/远程用户成为不同的用户并执行任务,用第二个用户的权限创建资源。从1.9开始become代替旧的sudo / su,同时仍然向后兼容。这个新系统也使得添加诸如pbrun(Powerbroker),pfexec,dzdo(Centrify)等其他特权升级工具变得更加容易。
(2)变量和指令是独立的,即设置become_user并不是设置become。

Directives

(1)become
set to ‘true’/’yes’ to activate privilege escalation.
使用“true”或“yes”来表示启用这个特权,如:become=true
表示打开了become开关。
(2)become_user
set to user with desired privileges — the user you ‘become’, NOT the user you login as. Does NOT imply become: yes, to allow it to be set at host level.
become_user=root 设置为root账户,相当于我们以普通账户登入到远程主机时,再使用su - root切换为root账户。
(3)become_method
(at play or task level) overrides the default method set in ansible.cfg, set to sudo/su/pbrun/pfexec/doas/dzdo/ksu
become_method=su 表示用什么方式将普通账户切换到root或所需的其他账户,这里可以用su或sudo。
(4)become_flags
(at play or task level) permit to use specific flags for the tasks or role. One common use is to change user to nobody when the shell is set to no login. Added in Ansible 2.2.
表示允许为任务或角色使用特定的标志。一个常见的用法是在shell设置为不登录时将用户更改为nobody。ansible2.2版本中增加。

例如,在以非root用户身份连接时管理系统服务(需要root权限)(become_user 的默认值为root),此时以 root 用户运行

- name: Ensure the httpd service is running
  service:
    name: httpd
    state: started
  become: yes

下面的将以 apache 用户

- name: Run a command as the apache user
  command: somecommand
  become: yes
  become_user: apache

此任务不会对become_user执行任何操作,因为未设置为yes并且默认为false / no

- do: something
  become_user: someone

6. playbook

参考:
http://ansible-tran.readthedocs.io/en/latest/docs/intro_configuration.html#ssh-args
http://www.ywnds.com/?p=6045

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

推荐阅读更多精彩内容