ansible简介

一、简介

在Linux自动化运维中,常见的自动化运维工具可分为需要安装终端的puppet、func和不需要安装终端的ansible,fabric。而ansible就是其中一个模块化的自动运维管理工具,其基于Python语言实现,由Paramiko,PYYAML和Jinjia2三个关键模块组成。Ansible无需再控制终端安装客户端,因此部署起来十分简单。Ansible的主要特点包括以下几点:

  • agentless,去中心化。
  • 模块化部署,可自定义模块。
  • 支持playbook。
  • 幂等性,多次执行结果相同。

二、Ansible的架构

ansible的架构图

Ansible的架构由以下几个核心组件构成:

ansible:ansible的核心程序,提供一个命令行接口给用户对ansible进行管理操作;
Host Inventory:为Ansible定义了管理主机的策略。一般小型环境下我们只需要在host文件中写入主机的IP地址即可,但是到了中大型环境我们有可能需要使用静态inventory或者动态主机清单来生成我们所需要执行的目标主机。
Core Modules:用于执行管理任务的核心模块,在Ansible管理主机之前,先调用core Modules中的模块,然后指明管理Host Lnventory中的主机,就可以完成管理主机。
Custom Modules:自定义模块,如何ansible自带的模块无法满足我么你的需求,用户可自定义相应的模块来满足自己的需求。
Connection Plugins:Ansible默认是基于SSH连接到目标机器上执行操作的,但是其也支持其他不同的连接方法,此时就需要连接插件帮助我们进行连接。
Playbook:定义需要给远程主机执行的一系列任务。

三、ansible的安装和配置

  • ansible的安装
    ansible的安装来源于epel仓库,因此在安装前需确保安装了正确的epel源。
[root@localhost ~]# yum install -y epel-release
[root@localhost ~]# yum install -y ansible
  • ansible的配置文件
    ansible的配置文件为/etc/ansible/ansible.cfg,包含着下面几个配置段,每个配置段下都有自己默认的配置,多数配置保持默认即可:
(1)[defaults]
    #inventory      = /etc/ansible/hosts  # 主机列表配置文件
    #library        = /usr/share/my_modules/ # 库文件存放目录 
    #remote_tmp     = $HOME/.ansible/tmp  # 生成的临时py命令文件存放在远程主机的目录
    #local_tmp      = $HOME/.ansible/tmp # 本机的临时命令执行目录
    #forks          = 5   # 默认并发数
    #poll_interval  = 15   # 默认的线程池
    #sudo_user      = root  # 默认sudo 用户
    #ask_sudo_pass = True
    #ask_pass      = True
    #transport      = smart
    #remote_port    = 22
    #module_lang    = C
    #module_set_locale = False

    host_key_checking = False  ### 检查对应服务器的host_key


(2)[privilege_escalation]
(3)[paramiko_connection]
(4)[ssh_connection]
(5)[accelerate]
(6)[selinux]
(7)[colors]
  • ansible的主机清单
    /etc/ansible/hosts文件为ansible用于定义管理主机的配置,其定义方式包括:

直接指明主机地址或主机名

node1
10.10.10.11

或者定义一个主机组,把主机地址或者主机名写进去,然后通过组名来调用主机组:

[webservers]
10.10.10.11
10.10.10.12
[dbservers]
db1
10.10.10.13

/etc/ansible/hosts文件支持使用以下变量设置相关的远程主机信息:

ansible_ssh_host     #用于指定被管理的主机的真实IP
ansible_ssh_port     #用于指定连接到被管理主机的ssh端口号,默认是22
ansible_ssh_user     #ssh连接时默认使用的用户名
ansible_ssh_pass     #ssh连接时的密码
ansible_sudo_pass     #使用sudo连接用户时的密码
ansible_sudo_exec     #如果sudo命令不在默认路径,需要指定sudo命令路径
ansible_ssh_private_key_file     #秘钥文件路径,秘钥文件如果不想使用ssh-agent管理时可以使用此选项
ansible_shell_type     #目标系统的shell的类型,默认sh
ansible_connection     #SSH 连接的类型: local , ssh , paramiko,在 ansible 1.2 之前默认是 paramiko ,后来智能选择,优先使用基于 ControlPersist 的 ssh (支持的前提)
ansible_python_interpreter     #用来指定python解释器的路径,默认为/usr/bin/python 同样可以指定ruby 、perl 的路径
ansible_*_interpreter     #其他解释器路径,用法与ansible_python_interpreter类似,这里"*"可以是ruby或才perl等其他语言
  • ansible的管理远程主机
    默认情况下ansible使用ssh连接到远程主机上去进行管理,此时需要提供相应的ssh认证才能完成远程管理。

ansible可以通过以下两种方式完成ssh认证:
1、通过在主机清单中配置对应主机的ssh账号及密码来完成认证:

[root@Monitor ansible]# vim /etc/ansible/hosts             
[webserver]
#192.168.180.4
192.168.180.5 ansible_ssh_port=10022 ansible_ssh_user=root ansible_ssh_pass='123456'
192.168.180.6 ansible_ssh_user=root ansible_ssh_pass='123456'
192.168.180.23 ansible_ssh_user=appuser ansible_ssh_pass='123456'

但是这种方式在管理大量的远程主机的配置会显得特别麻烦,而且容易泄露账号密码信息,因此通常只作为测试使用。
2、使用ssh的免密钥登录方式来远程管理主机。
首先需要在ansible主机上生成ssh的公钥和私钥文件:

[root@ansible ~]# ssh-keygen -t rsa -N ''
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
/root/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
eb:74:58:83:93:11:3a:fa:9b:8f:3f:88:4a:94:16:a0 root@ansible
The key's randomart image is:
+--[ RSA 2048]----+
|.       .        |
|..     . .       |
|E .   o .        |
|   o . . +       |
|  + .   S o      |
| o   .   = .     |
|  .  ...+ .      |
| .  . .*..       |
|  ..  +o+.       |
+-----------------+
[root@ansible ~]# ll /root/.ssh/
总用量 12
-rw-------. 1 root root 1675 6月   4 17:44 id_rsa  #生成的私钥文件
-rw-r--r--. 1 root root  394 6月   4 17:44 id_rsa.pub  #生成的公钥文件
-rw-r--r--. 1 root root  348 6月   4 17:12 known_hosts

接着将生成的公钥文件送往远程主机:

[root@ansible ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.0.83
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.0.83's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@192.168.0.83'"
and check to make sure that only the key(s) you wanted were added.
[root@ansible ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.0.84
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.0.84's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'root@192.168.0.84'"
and check to make sure that only the key(s) you wanted were added.

分发之后默认会在远程主机的/root/.ssh下生成authorized_keys这个文件。随后即可使用ansible对远程主机进行管理操作:

[root@ansible ~]# ansible webservers -m ping
192.168.0.83 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
192.168.0.84 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

四、ansible的命令工具

ansible的命令工具有以下几种:

/usr/bin/ansible:Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行
/usr/bin/ansible-doc:Ansible 模块功能查看工具
/usr/bin/ansible-galaxy:下载/上传优秀代码或Roles模块的官网平台,基于网络的
/usr/bin/ansible-playbook:Ansible 定制自动化的任务集编排工具
/usr/bin/ansible-pull:Ansible远程执行命令的工具(使用较少,海量机器时使用,对运维的架构能力要求较高)
/usr/bin/ansible-vault:Ansible 文件加密工具
/usr/bin/ansible-console:Ansible基于Linux Consoble界面可与用户交互的命令执行工具

其中最为常用的当属/usr/bin/ansible,/usr/bin/ansible-doc和/usr/bin/ansible-playbook了。ansible和playbook分别对应着ansible的两种任务的执行方式:AD-Hoc和playbook,而ansible-doc则可以方便于管理员查询指定的模块功能的使用方式。

  • ansible命令
    ansible命令的使用语法为:

ansible <host-pattern> [options]

其中options常见的参数有:

-C,--check:测试模式,只显示测试的结果而不做出实际的改变。
-m module_name:用于指定要执行的模块的名字,如:ansible all -m ping;
-a MODULE_ARGS:用于指定要执行的模块的参数,参数格式类似于key=value,如:ansible all -m group -a "gid=3000 name=mygrp state=absent system=no";

使用示例:

[root@ansible ~]# ansible all -m shell  -a "echo magedu"
192.168.0.83 | SUCCESS | rc=0 >>
magedu

192.168.0.84 | SUCCESS | rc=0 >>
magedu
  • ansible-doc命令
    ansible-doc命令的使用语法为:

ansible-doc [-l|-F|-s] [options] [-t <plugin type> ] [plugin]

其中常用的参数的解释为:

-l:列出所有可用的模块;
-s:显示指定的模块的片段参数;
-F:列出模块名字及其对应的python脚本文件;

使用示例:

[root@ansible ~]# ansible-doc -s copy
- name: Copies files to remote locations
  copy:
      attributes:            # Attributes the file or directory should have. To get supported flags look at the man page for `chattr' on the target system.
                               This string should contain the attributes in the same order as the one displayed by `lsattr'.
      backup:                # Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it
                               incorrectly.
      checksum:              # SHA1 checksum of the file being transferred. Used to valdiate that the copy of the file was successful. If this is not
                               provided, ansible will use the local calculated checksum of the src file.
      content:               # When used instead of `src', sets the contents of a file directly to the specified value. For anything advanced or with
                               formatting also look at the template module.
......
  • ansible-playbook命令
    ansible-playbook的使用语法为:

ansible-playbook [options] [playbooks]

其中playbooks为以.yaml结尾的文本文件,里面包含着以yaml语言所编写的执行内容。
而常用的options包括:

--check:测试模式,检查playbook的执行结果,不是真正的执行操作;ansible-syntax-check也可以用于检查指定playbook的语法是否有错;
--list-hosts:列出运行指定playbook的主机;
-t tags_name:执行yaml文件中的指定的tags配置;
-e VARS=value:自定义在playbook中使用到的变量;

使用示例:

ansible-playbook -e pkgname=memcached -C fourth.yaml
  • ansible的常用模块
    上述三个命令的运行都需要调用到ansible的模块来执行,这是因为ansible是模块化的工具,不同的功能被划分为不同的模块来方便管理员进行调用,而查看对应可用的模块及使用方式则使用上述所说到的ansible-doc -l或者ansible-doc -s module_name
    下面我们来看看ansible常用的模块有哪些:
    1)command模块
    此模块用于在远程主机上执行ansible所设置命令。需要注意的是command模块要执行命令无须为key=value格式,而是直接给出要执行的命令即可。
    其常用的模块参数有:

chdir=path :切换路径到指定的目录下,然后执行指定的命令

使用示例:

ansible all -m command -a "ifconfig"
ansible all -m command -a "chdir=/var/tmp mkdir hi.dir"

2)shell模块
因为command模块不支持管道符号等shell的特性,因此当需要指定的命令需要利用到管道等shell特性时,需要调用shell模块,这样即可在远程主机的shell进程下运行命令。
使用示例:

ansible all -m shell  -a "echo magedu | passwd --stdin testuser"

3)file模块
file模块用于在远程主机上创建或设置文件属性。
其常用的模块参数有:

group=GROUPNAME:用于指定创建文件的属组;
mode=value:用于指定文件的权限;
owner=USERNAME:用于指定文件的属主;
path=PATH:用于指定目标文件的路径;
src=PATH:指定要被链接的源文件路径,只应用于state=link的情况;
dest=PATH:指定创建链接文件的路径,只应用于state=link的情况;
state=ATTIBUTES:用于指定文件属性状态,常见的属性状态有:
    directory:指定文件类型为目录,如果目录不存在则创建目录;
    file:指定文件类型为file,即使文件不存在,也不会创建文件;
    hard:创建硬链接
    link:创建软链接
    touch:如果文件不存在,则会常见一个新的文件,如果文件或目录已存在,则更新其最后修改的时间
    absent:删除文件、目录或取消链接文件;
    present:新增文件、目录或链接文件

使用示例;

#在远程主机的指定目录下创建目录
ansible all -m file -a  "path=/var/tmp/hello.dir state=directory"

4)copy模块
copy模块用于将本地文件复制到远程主机的指定路径下。
其模块参数有:

src:指定复制文件的来源,注意路径最后是否带有/会影响复制的是目录,还是目录下的文件。
content:将指定的内容复制保存为指定路径的文件内容;
dest:指定复制文件的存放目录;
mode:指定复制目标文件的文件权限;
owner:指定复制目标文件的属主;
group:指定复制目标文件的属组;

使用示例:

#将源文件复制到远程主机的指定路径,并设置文件权限为600
ansible all -m copy -a "src=/etc/fstab dest=/tmp/fstab.ansible mode=600"
#将指定的content内容,复制保存为远程主机指定路径下的文件
ansible all -m copy -a "content='hi there\n' dest=/tmp/hi.txt"
  • cron模块
    cron模块用于在远程主机上设置cron周期性任务。
    常见模块参数:
minute:定义cron任务每几分钟运行一次;
hour:定义cron任务每几个小时运行一次;
day:定义cron任务每几天运行一次;
month:定义cron任务每几个月运行一次;
weekday:定义cron任务每星期几运行;
name:定义cron任务的名字;
job:定义cron任务的工作内容;
state:通常为present或absent,新增或删除cron任务;

使用示例:

#在远程主机创建时间同步的cron任务
ansible all -m cron -a "minute=*/3 job='/usr/sbin/update ntp1.aliyun.com &> /dev/null' name=dateupdate
#在远程主机上删除时间同步的cron任务
ansible all -m cron -a "minute=*/3 job='/usr/sbin/update ntp1.aliyun.com &> /dev/null' name=dateupdate state=absent "
  • user模块
    user模块故名思义就是用来管理用户账号的,其常用模块参数有:
name:指定用户的名称;
system:指定用户是否为系统用户,yes or no;
uid:指定用户的uid;
shell:指定用户的默认shell;
group:指定用户的属组;
groups:指定用户的附加组;
home:指定用户的家目录;
state:指定是否在远程主机上创建或删除指定的用户,present为创建,absent为删除;

使用示例:

ansible all -m user -a "uid=5000 name=testuser state=present group=mygrp shell=/bin/tcsh"
  • group模块
    此模块用于管理用户组,其常见模块参数有:
gid:用于指定用户组的id;
name:用于指定用户组的名称;
state:是否在远程主机上创建或删除指定的组,present为创建,absent为删除;
system:用于指定用户组是否为系统组;

使用示例:

ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
  • yum模块
    yum模块用于在远程主机上安装指定的程序包,其模块参数有:
name:指明要安装的程序包的名字;
state:指明安装或卸载的方式,包括present、installed、latest、absent、removed;

使用示例:

ansible all -m yum -a "name=epel-release state=installed"
  • service模块
    用于管理远程主机服务的模块,其常用模块参数有:
name:指定要管理的服务的名称;
enabled:指定管理的服务是否开机自启动;
state:设置服务的状态为started、stopped或者restarted、reloaded;

使用示例:

ansible all -m service -a "name=nginx state=started"
  • script模块
    可以将本地脚本复制到远程主机上去执行,其常用模块参数有:
chdir:指定脚本执行的目录路径;

使用示例:

ansible all -m script -a "chdir=/etc/ /root/ehco.sh"
  • template模块
    可以基于模板文件,分别为不同的远程主机复制生成对应的目标文件。其常用参数有:
src:本地Jinjia2模版的template文件位置;
dest:远程节点上的绝对路径,用于放置template文件;
group:设置远程节点上的的template文件的所属用户组;
owner:设置远程节点上的的template文件的所属用户组;
mode:设置远程节点上的template文件权限;

使用示例:

#模板文件
[root@ansible playbooks]# vim mylisten.conf 
Listen {{ http_port }}
#ansible主机变量:
[root@ansible playbooks]# vim /etc/ansible/hosts
[webservers]
192.168.0.83 http_port=8080
192.168.0.84 http_port=10080
#yaml配置:
[root@ansible ~]# vim first.yaml
- hosts: webservers
  remote_user: root
  tasks:
  - name: install httpd
    yum: name=httpd state=latest
  - name: install config file
    template: src=/root/playbooks/mylisten.conf dest=/etc/httpd/conf.d/mylisten.conf
  - name: start httpd
    service: name=httpd state=started
#ansible-playbook执行调用
[root@ansible ~]# ansible-playbook all first.yaml

五、Ansible-playbook

playbook应该说是ansible的核心模块了,一个playbook相当于是一堆task任务的合集。执行一个playbook,相当于执行了一堆task任务,而且这个playbook还是可复用的,这样就能大大减少了重复性的工作量。Playbook是使用一种叫YAML语言来编写的,其本身就是一种可读性高,用于表达数据序列的格式的语言。一个playbook文件应以.yaml结尾。
playbook由以下几个核心元素组成:

1、Hosts:指远程控制的主机;
2、Tasks:指在远程控制主机上执行的任务;
3、Variables:执行Tasks任务时调用的变量;
4、Templates:包含了模板语法的文本文件;
5、Handlers:根据特殊的触发条件而判断执行某个具体的Task任务;
6、Roles:Playbook的角色,将经常调用的Task任务定义成一个角色,方便日后进行复用;

下面我们来看每个元素在playbook里面的实现。

  • Hosts
    ansible playbook的hosts主机通常存放在/etc/ansible/hosts文件中,其定义的方式之前已经提及到,这里就不再重复了。
    在playbook里面的实现方式为:
#调用全部主机
- hosts: all  
#调用指定的主机组
- hosts: webservers
  • Tasks
    Tasks就是指远程主机要执行的任务,其格式通常为:

1)action:module arguments;
2)module:arguments

注意,shell和command模块后面只需直接添加命令,而不是使用key=value类似的参数列表。另外某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers;任务可以通过“tags"打标签,而后可在ansible-playbook命令上使用-t指定进行调用。

Tasks在playbook中的实现:

- hosts: all    #定义远程主机
  remote_user: root  #定于远程主机上执行任务的用户
  tasks:
  - name: install redis  #定义第一个task任务
    yum: name=redis state=latest  #定义task任务的内容
  - name: install nginx  #定义第二个task任务
    yum: name=nginx state=latest
    notify: restart nginx    #调用名为restart nginx的handlers
    tags: installngx  #添加installngx的tags标签
  • handlers
    handlers用于在playbook中匹配特定的条件后触发执行的任务,通常在接到其他任务的notify通知后被触发。
    其在playbook文件中的实现方式为:
- hosts: all
  remote_user: root
  tasks:
  - name: install redis
    yum: name=redis state=latest
  - name: copy config file
    copy: src=/root/playbooks/redis.conf dest=/etc/redis.conf owner=redis
    notify: restart redis    #调用指定的handlers
    tags: configfile
  - name: start redis
    service: name=redis state=started enabled=true
  handlers:    
  - name: restart redis    #定义一个handlers
    service: name=redis state=restarted    #定义handlers所触发的任务
  • variables
    playbook中可调用的变量分为好几种,分别为:
    1)facts:即系统变量facts,可利用命令ansible all -m setup来调用获取目标主机的facters。
    常见的facters有:
ansible_distribution:目标主机系统的社区版本
ansible_os_family:目标主机系统派系
ansible_default_ipv4.address:目标主机的Ip地址

2)用户自定义变量
包括通过ansible-playbook -e VARS=value playbook.yaml命令所指定的变量,以及在playbook中通过下面方式定义的变量:

  - hosts: all
    gather_facts: False
    vars:
        pkgname: nginx
    tasks:
    - name: install nginx
      yum: name={{ pkgname }} state=latest

3)通过roles传递变量;

4)通过在/etc/ansible/hosts文件中定义变量,如:

#向不同的主机传递不同的变量
[root@ansible playbooks]# vim /etc/ansible/hosts
[webservers]
192.168.0.83 http_port=8080
192.168.0.84 http_port=10080

#向组中的主机传递相同的变量
[webservers]
192.168.0.83
192.168.0.84

[webservers:vars]
http_port=8080
  • roles
    playbook的roles存放在/etc/ansible/roles目录下,每一个roles以特定的层级目录结构进行组织,将原本编写在一个playbook里面的配置分解保存位层级目录结构中的各个目录节点。
    一个roles通常包含着以下的目录节点:

files/:存放由copy或script模块等调用的文件;
templates/ : 存放template模块查找所需要模板文件的目录;
tasks/ :至少应该包含一个名为mail.yml的文件,此文件保存着与tasks相关的信息,其它的文件需要在此文件中通过include进行包含;
handlers/ :至少应该包含一个名为mail.yml的文件,此文件保存着与handlers相关的信息,其他的文件需要在此文件中通过include进行包含;
vars/:至少应该包含一个名为mail.yml的文件,此文件保存着与vars相关的信息,其他的文件需要在此文件中通过include进行包含;
meta/:至少应该包含一个名为mail.yml的文件,定义当前角色的特殊设定及其依赖关系;其他的文件需要在此文件中通过include进行包含;
default/:设定默认变量时使用此目录的mail.yml文件;

一个roles的目录架构:

[root@ansible ~]# tree /etc/ansible/roles/nginx/
/etc/ansible/roles/nginx/
├── default
├── files
│   └── index.html
├── handlers
│   └── main.yml
├── meta
├── tasks
│   └── main.yml
├── templates
│   └── vhost1.conf.j2
└── vars
    └── main.yml

在playbook中调用角色的方法:

- hosts: webservers
  remote_user: root
  roles:
  - mysql
  - memcached
  - { role: nginx,username: nginx}  #将username变量传递给nginx角色
  • 条件测试
    playbook还支持条件测试语句when,如根据目标系统为RedHat或Debian时执行不同的安装指令:
tasks:
- hosts: webservers
  remote_user: root
  tasks:
  - name: install nginx
    yum: name=nginx state=installed
    when: ansible_os_family == "RedHat"
  - name: install apahce2
    apt: name=apache2 state=installed
    when: ansible_os_family == "Debian"

也可以根据when条件测试来实现角色的调用:

- hosts: webservers
  remote_user: root
  roles:
  - { role: nginx, when: "ansible_distribution_major_version == '7' " }

另外在playbook中也可以实现循环迭代的操作,其实现需要在tasks引用用固定变量"item",然后在task中使用with_items给定要循环迭代的元素列表,列表支持以字符串或者字典的方式提供,如:

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

推荐阅读更多精彩内容