AWS CloudFormation

定义

AWS CloudFormation 提供了一种简便地创建和管理一批相关的 AWS 资源的方法,并通过有序且可预测的方式对其进行资源配置和更新。可以使用 AWS CloudFormation 的示例模板或自己创建模板来描述 AWS 资源以及应用程序运行时所需的任何相关依赖项或运行时参数。可以不需要了解亚马逊 AWS 服务需要配置的顺序,也不必弄清楚让这些依赖项正常运行的细枝末节。 当设置完成后,可通过按受控制、可预测的方式修改和更新 AWS 资源,可以像软件版本控制一样对 AWS 基础结构进行版本控制。无需为 CloudFormation 支付额外的费用,只需支付支持应用程序 的 AWS 资源费用。
堆栈
使用 AWS CloudFormation 时,可将相关资源作为一个称为“堆栈”的单元进行管理。换句话说,我们可以通过创建、更新和删除堆栈来创建、更新和删除一系列资源。堆栈中的所有资源均由堆栈的 AWS CloudFormation 模板定义。

使用

AWS根据一些用户典型场景,定义了一些典型场景下的Stack模版,供用户使用。下面主要一个 WordPress 服务器的模板为例。
Stack支持两种格式JSON和YAML:

// 模板格式的版本信息,下面的是 2010-09-09 版本的。如果不指定,AWS CloudFormation 将使用最新版本
AWSTemplateFormatVersion: '2010-09-09'
//模板描述信息,Description 必须紧随 AWSTemplateFormatVersion 部分之后。且长度是介于 0 和 1024 个字节之间的文字字符串,目前貌似不支持中文
Description: My First Cloudformation Stack

//参数,要在运行时 (创建或更新堆栈时) 传递到模板的值。您可引用模板的 Resources 和 Outputs 部分中的参数。这些参数会在执行Stack时候,生成界面,让用户输入。参数可以被 Stack 中其他元素通过名称引用。
Parameters:
  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t2.small //默认值
    //包含参数允许值列表的阵列
    AllowedValues:
      - t2.micro
      - t2.small
      - t2.medium
    //用于在违反约束时说明该约束的字符串。
    ConstraintDescription: must be a valid EC2 instance type.
  SSHLocation:
    Description: The IP address range that can be used to SSH to the EC2 instances
    Type: String
    //一个整数值,确定要允许 String 类型使用的字符的最小数目。
    MinLength: '9'
    //一个整数值,确定要允许 String 类型使用的字符的最大数目
    MaxLength: '18'
    Default: 0.0.0.0/0
    //一个正则表达式,表示要允许 String 类型使用的模式。
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  DevopsGirlsUser:
    Default: zhang.san
    Description: Your firstname and lastname
    Type: String
    MinLength: '1'
    MaxLength: '64'
    ConstraintDescription: must begin with a letter and contain only alphanumeric
      characters.
  // 这里使用 S3 bucket
  WordpressS3Bucket:
    Default: devopsgirls-training-xian
    Description: Bucket to get Wordpress from
    Type: String

//EC2 的配置与CPU内存架构的映射关系.
Mappings:
  AWSInstanceType2Arch:
    t2.micro:
      Arch: HVM64
    t2.small:
      Arch: HVM64
    t2.medium:
      Arch: HVM64
  //AMI与CPU架构和AWS Zone的映射关系。
  AWSRegionArch2AMI:
    ap-southeast-1:
      HVM64: ami-01da99628f381e50a

//AWS定义了一个资源清单,每个资源有一个Type,Type的值只能从已有的资源类型选择,不能随意指定。
Resources: //唯一必须的,一般有逻辑ID,资源类型, 资源属性组成。
  //申请一个 ELB。这个ELB的逻辑名称为 ElasticLoadBalancer。
  //每个资源有一个逻辑名称,在Stack中唯一;当资源创建出来之后,有一个全局唯一的物理ID(PhysicalID),由AWS分配。逻辑名称与PhysicalID对应。
  ElasticLoadBalancer:
    Type: AWS::ElasticLoadBalancing::LoadBalancer
    Properties:
      AvailabilityZones: !GetAZs ''
      SecurityGroups:
        - !GetAtt 'ElasticLoadBalancerSecurityGroup.GroupId'
      CrossZone: 'true'
      LBCookieStickinessPolicy:
        - PolicyName: CookieBasedPolicy
          CookieExpirationPeriod: '30'
      Listeners:
        - LoadBalancerPort: '80'
          InstancePort: '80'
          Protocol: HTTP
          PolicyNames:
            - CookieBasedPolicy
      HealthCheck:
        Target: TCP:80
        HealthyThreshold: '2'
        UnhealthyThreshold: '5'
        Interval: '10'
        Timeout: '5'

  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP access via port 80 locked down to the load balancer
        + SSH access
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          SourceSecurityGroupOwnerId: !GetAtt 'ElasticLoadBalancer.SourceSecurityGroup.OwnerAlias'
          SourceSecurityGroupId: !GetAtt 'ElasticLoadBalancerSecurityGroup.GroupId'
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Ref 'SSHLocation' //使用 Ref 内部函数引用 SSHLocation 参数

  ElasticLoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP access via port 80 locked down to the load balancer
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: 0.0.0.0/0

  WebServerGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AvailabilityZones: !GetAZs ''
      LaunchConfigurationName: !Ref 'LaunchConfig'
      MinSize: '1'
      MaxSize: '5'
      DesiredCapacity: '2'
      LoadBalancerNames: 
        - !Ref 'ElasticLoadBalancer'    //Ref 函数将返回它指代的对象的值,也可以将某个资源的属性设置为另一个资源的属性值
    CreationPolicy: //堆栈创建继续之前等待资源配置操作时,使用 CreationPolicy 属性
      ResourceSignal: //AWS CloudFormation 创建关联的资源时,配置所需的成功信号数以及 AWS CloudFormation 等待这些信号的时间长度。
        //AWS CloudFormation 在将资源状态设置为 CREATE_COMPLETE 之前必须收到的成功信号的数目。如果资源在超时期限过期之前收到失败信号或未收到指定数目的信号,则资源创建将失败并且 AWS CloudFormation 将回滚堆栈。
        Count: 2 //默认为1
        //AWS CloudFormation 等待 Count 属性中指定数目的信号的时长.该值必须采用以下形式的 ISO8601 持续时间格式:"PT#H#M#S",其中各个 # 分别表示小时数、分钟数和秒数。为获得最佳结果,请指定足够长的时间段,以便实例可以正常启动和运行。更短的超时可导致回滚。
        Timeout: PT30M
    UpdatePolicy:
      //可以指定 AWS CloudFormation 是批量更新还是一次性全部更新 Auto Scaling 组中的实例。
      AutoScalingRollingUpdate:
       // 指定当 AWS CloudFormation 更新旧实例时,Auto Scaling 组中必须处于服务状态的实例的最小数目
        MinInstancesInService: '1'
        //指定 AWS CloudFormation 更新的实例的最大数目。
        MaxBatchSize: '1'
        // 在对一批实例进行更改以便为这些实例提供启动软件应用程序的时间后,AWS CloudFormation 暂停的时间量。例如,在扩展 Auto Scaling 组中的实例数时,您可能需要指定 PauseTime。
        PauseTime: PT30M
        //指定 Auto Scaling 组在更新期间是否等待来自新实例的信号。使用此属性可确保实例在 Auto Scaling 组继续更新之前已完成安装和配置应用程序。AWS CloudFormation 在新的 EC2 实例在 Auto Scaling 组中启动后暂停对该组的更新。在继续更新之前,AWS CloudFormation 必须在指定的 PauseTime 内收到来自每个新实例的信号
        WaitOnResourceSignals: 'true'

  LaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !FindInMap [AWSRegionArch2AMI, !Ref 'AWS::Region', !FindInMap [AWSInstanceType2Arch,
          !Ref 'InstanceType', Arch]]
      InstanceType: !Ref 'InstanceType'
      SecurityGroups:
        - !Ref 'WebServerSecurityGroup'
      //用户自定义脚本
      UserData: !Base64
        Fn::Join:
          - ''
          - - "#!/bin/bash -xe \n"
            - "yum update -y aws-cfn-bootstrap \n"
            - "aws configure set default.s3.multipart_threshold 64MB \n"
            - aws s3 cp s3://devopsgirls-training-xian/
            - !Ref 'DevopsGirlsUser'
            - "-wordpress.tgz /var/www/wordpress.tgz --no-sign-request \n"
            - "yum install httpd php php-mysql -y \n"
            - "mkdir -p /var/www/html \n"
            - "tar xvfz /var/www/wordpress.tgz -C /var/www/html/ \n"
            - "chown -R apache /var/www/html/ \n"
            - "service httpd start \n"
            - '/opt/aws/bin/cfn-signal -e $? '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource WebServerGroup '
            - '         --region '
            - !Ref 'AWS::Region'
            - '

//用户自定义的输出。这个是 Stack 创建完成了之后,暴露给外部访问的入口。
//如这里的一个Output为Wordpress安装好了之后的访问地址。              '
Outputs:
  WebsiteURL 是一个由一个 Join 函数调用返回的字符串,该字符串并置了 http://,资源 ElasticLoadBalancer 的 DNS 名
  WebsiteURL:
    Value: !Join ['', ['http://', !GetAtt 'ElasticLoadBalancer.DNSName', /]]
    Description: WordPress Website

可以在 aws 界面将模板上传然后创建 stack,之后在底部的对话框中,单击事件。不停刷新它, 然后就能看到 Cloudformation 正在创建我们的环境。 等到堆栈状态变为 CREATE_COMPLETE,就代表创建成功,点击该堆栈的输出,找到并访问 WebsiteURL 的值,我们将会访问到刚才使用 Cloudformation 创建的 WordPress了。
或者通过 AWS CLI 创建堆栈

$ aws cloudformation create-stack --stack-name wordpress --template-body file:///path-to-your-yaml

帮助程序列表

cfn-init

cfn-init 帮助程序脚本读取来自 AWS::CloudFormation::Init key 的模板元数据并进行相应操作:

  • 提取和解析来自 AWS CloudFormation 的元数据

  • 安装软件包

  • 将文件写入磁盘

  • 启用/禁用以及启动/停止服务

注意:
如果使用 cfn-init 来更新现有文件,则它将在同一目录下为原始文件创建一个扩展名为 .bak 的备份副本。例如,如果更新 /*path*/*to*/*file_name*,则操作生成两个文件:/*path*/*to*/*file_name*.bak 包含原始文件的内容,而 /*path*/*to*/*file_name* 包含更新的内容。

cfn-init 无需提供凭证,因此不需要使用 --access-key--secret-key--role--credential-file 选项。但是,如果不指定凭证,则 AWS CloudFormation 会检查堆栈成员并将调用范围限制为实例所属的堆栈。

语法

cfn-init --stack|-s stack.name.or.id \
         --resource|-r logical.resource.id \
         --region region
         --access-key access.key \
         --secret-key secret.key \
         --role rolename\
         --credential-file|-f credential.file \
         --configsets|-c config.sets \
         --url|-u service.url \
         --http-proxy HTTP.proxy \
         --https-proxy HTTPS.proxy \
         --verbose|-v

选项

选项1.png

选项2.png

选项3.png

示例
下面的代码段演示 EC2 实例的 UserData 属性,该实例运行与 WebServerInstance 资源关联的 InstallAndRun 配置集。

UserData: !Base64 
  'Fn::Join':
    - ''
    - - |
        #!/bin/bash -xe
      - |
        # Install the files and packages from the metadata
      - '/opt/aws/bin/cfn-init -v '
      - '         --stack '
      - !Ref 'AWS::StackName'
      - '         --resource WebServerInstance '
      - '         --configsets InstallAndRun '
      - '         --region '
      - !Ref 'AWS::Region'
      - |+
cfn-signal

cfn-signal 帮助程序脚本向 AWS CloudFormation 发送信号来指示是否已成功创建或更新 Amazon EC2 实例。如果在实例上安装和配置软件应用程序,则可在这些软件应用程序准备就绪时向 AWS CloudFormation 发送信号。
将 cfn-signal 脚本与 CreationPolicy 结合使用或将 Auto Scaling 组与 WaitOnResourceSignals 更新策略结合使用。当 AWS CloudFormation 使用这些策略创建或更新资源时,它将暂停堆栈上的工作直至资源收到所需数量的信号或超出超时期限。对于 AWS CloudFormation 接收的每个有效信号,AWS CloudFormation 会将信号发送到堆栈事件以便您跟踪每个信号。

注意:
cfn-signal 不需要凭证,因此不需要使用 --access-key--secret-key--role--credential-file 选项。但是,如果不指定凭证,则 AWS CloudFormation 会检查堆栈成员并将调用范围限制为实例所属的堆栈。

如果想向 AWS CloudFormation 资源发送信号,请使用以下语法。

cfn-signal --success|-s signal.to.send \ ##如为true,则信号“成功”,否则“失败”。
        --access-key access.key \ ##(仅适用于向资源发送信号)
        --credential-file|-f credential.file \ ##同时包含秘密访问密钥和访问密钥的文件。凭证文件参数取代 --role、--access-key 和 --secret-key 参数。
        --exit-code|-e exit.code \ ##进程中出现错误代码,用于确定成功或失败。如果已经指定,则忽略 --success 选项。
        --http-proxy HTTP.proxy \ 
        --https-proxy HTTPS.proxy \
        --id|-i unique.id \ ##  要发送的唯一 ID。
        --region AWS.region \ ##(仅适用于向资源发送信号),要使用的 AWS CloudFormation 区域终端节点。
        --resource resource.logical.ID \ ##仅适用于向资源发送信号,包含要向其发送信号的创建策略的资源的[逻辑 ID]
        --role IAM.role.name \
        --secret-key secret.key \ (仅适用于向资源发送信号)对应于指定的 AWS 访问密钥的 AWS 秘密访问密钥。
        --stack stack.name.or.stack.ID \ ##(仅适用于向资源发送信号)包含要向其发送信号的资源的堆栈名称或堆栈 ID。
        --url AWS CloudFormation.endpoint # 仅适用于向资源发送信号,要使用的 AWS CloudFormation 终端节点。

如果想向等待条件句柄发送信号,请使用以下语法。

cfn-signal --success|-s signal.to.send \
        --reason|-r resource.status.reason \ ##仅适用于等待条件句柄,目前仅适用于 failure 的状态原因,如果  success = false ,默认为 'Configuration failed'
        --data|-d data \ ##(仅适用于等待条件句柄)要通过 waitConditionHandle 发送回的数据。默认值待留空。
        --id|-i unique.id \
        --exit-code|-e exit.code \
        waitconditionhandle.url ##(仅适用于等待条件句柄)可用于向关联的 WaitCondition 发送成功或失败信号的预签名 URL

可使用的选项取决于是向创建策略还是等待条件句柄发送信号。适用于创建策略的一些选项可能不适用于等待条件句柄。
常规使用模式将同时使用 cfn-init 和 cfn-signal。cfn-signal 调用利用了 cfn-init 调用返回状态(使用 $? shell 程序构造)。如果应用程序安装失败,则该实例将无法创建,并且堆栈将回滚。
示例:

AWSTemplateFormatVersion: 2010-09-09
Description: Simple EC2 instance
Resources:
  MyInstance:
    Type: 'AWS::EC2::Instance'
    Metadata:
      'AWS::CloudFormation::Init':
        config:
          files:
            /tmp/test.txt:
              content: Hello world!
              mode: '000755'
              owner: root
              group: root
    Properties:
      ImageId: ami-a4c7edb2
      InstanceType: t2.micro
      UserData: !Base64
        'Fn::Join':
          - ''
          - - |
              #!/bin/bash -x
            - |
              # Install the files and packages from the metadata
            - '/opt/aws/bin/cfn-init -v '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource MyInstance '
            - '         --region '
            - !Ref 'AWS::Region'
            - |+

            - |
              # Signal the status from cfn-init
            - '/opt/aws/bin/cfn-signal -e $? '
            - '         --stack '
            - !Ref 'AWS::StackName'
            - '         --resource MyInstance '
            - '         --region '
            - !Ref 'AWS::Region'
            - |+

    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M
cfn-hup

cfn-hup 帮助程序作为一项后台程序,当检测出资源元数据中出现变更时,运行用户指定操作。通过此项操作,可以通过 UpdateStack API 操作对正在运行的 Amazon EC2 实例进行配置更新。
语法:

cfn-hup --config|-c config.dir  --no-daemon   --verbose|-v

配置参数说明:

image.png

cfn-hup.conf
cfn-hup.conf 文件存储了堆栈的名称和 cfn-hup 后台程序目标 AWS 凭证。
cfn-hup.conf 文件采用以下格式:

[main]
stack=<stack-name-or-id> ##堆栈名称或 ID。
credential-file=<credential-file> ##仅用户可用凭证文件,与用于命令行工具的格式相同。role 参数替代此参数。
role=<role> ##与实例关联的 IAM 角色的名称。
region=<region> ##包含堆栈的 AWS 区域名称。
umask=< umask> ##cfn-hup 守护程序使用的 umask。指定此值时可选择带或不带前导 0。在两种情况下,它都会被解释为八进制数 (与 Linux umask 命令非常相似)。此参数在 Windows 中无效。类型:介于 0 和 0777 之间的八进制整数。默认值:022,版本 1.4-22 及更高版本。022 的默认值覆盖组和全球写权限,因此 cfn-hup 守护程序创建的文件在默认情况下不支持组和全球写入。版本 1.4-21 及更低版本的默认值为 0,不覆盖任何值。
interval=<interval> ##用于检查资源元数据更改的时间间隔 (以分钟为单位)
verbose=< verbose> ##指定是否要使用详细日志记录。

hooks.conf 配置文件
cfn-hup 后台程序定期调用的用户操作已在 hooks.conf 配置文件中予以定义。hooks.conf 文件采用以下格式:

[hookname] ##此 hook 的唯一名称
triggers=post.add or post.update or post.remove ##待检测条件的逗号分隔列表。
path=Resources.<logicalResourceId> (.Metadata or .PhysicalResourceId)(.<optionalMetadatapath>)##元数据对象路径。支持元数据块中的随机深度路径。<LogicalResourceId>:监测资源的最新更新时间,触发资源的任何变更。<LogicalResourceId>.PhysicalResourceId:监测资源的物理 ID,仅当已关联资源标识变更时(例如,新的 EC2 实例)发生触发行为。<LogicalResourceId>.元数据(.可选路径):监测资源元数据的变更情况(可将元数据子路径指定为任一深度级别,以便监测特定数值)
action=<arbitrary shell command> ##按照既定运行的任意 shell 程序命令。
runas=<runas user> ##运行命令的用户。Cfn-hup 使用 su 命令切换至用户。

操作运行时,它将在当前环境(cfn-hup 处于其中)的副本中运行,此时 CFN_OLD_METADATA 设定为路径的先前数值,且 CFN_NEW_METADATA 设定为当前值。

hooks.conf仅可通过 cfn-hup 后台程序启动加载,因此需要后台程序才能重新启动新的hook。以前的元数据值的缓存存储在 /var/lib/cfn-hup/data/metadata_db 中,可以删除该缓存以强制 cfn-hup 再次运行所有 post.add 操作。
hooks.d 目录
要通过部署变更通知 hook 支持若干应用程序组合,cfn-hup 要支持位于 hook 配置目录中名为 hooks.d 的目录。可以将一个或多个附加 hook 配置文件置于 hooks.d 目录之中。附加 hook 文件必须使用与 hooks.conf 文件相同的版式。
cfn-hup 后台程序将解析并加载此目录中的所有文件。如果 hooks.d 中的任何 hook 与 hooks.conf 中的 hook 同名,则应将上述 hook 进行合并(表示 hooks.d 将重写 hooks.conf 针对上述两个文件指定的任何数值)。
示例:
在以下模板代码段中,当更改了与 LaunchConfig 资源关联的 AWS::CloudFormation::Init 资源时,AWS CloudFormation 会触发 cfn-auto-reloader.conf hook文件。

...
  LaunchConfig:
    Type: "AWS::AutoScaling::LaunchConfiguration"
    Metadata:
      QBVersion: !Ref paramQBVersion
      AWS::CloudFormation::Init:
...
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: "root"
              group: "root"
...
cfn-get-metadata

cfn-get-metadata 帮助程序脚本从 AWS CloudFormation 中提取元数据块,并将其打印进行标准输出。如果指定了密钥,则还可以打印元数据块的子树。但是,仅支持具有最高级别的密钥。

注意
cfn-get-metadata 无需提供凭证,因此不需要使用 --access-key、--secret-key、--role 或 --credential-file 选项。但是,如果不指定凭证,则 AWS CloudFormation 会检查堆栈成员并将调用范围限制为实例所属的堆栈。

cfn-get-metadata --access-key access.key \
                 --secret-key secret.key \
                 --credential-file|f credential.file \
                 --key|k key \
                 --stack|-s stack.name.or.id \
                 --resource|-r logical.resource.id \
                 --role IAM.role.name \
                 --url|-u service.url \
                 --region region 

https://docs.aws.amazon.com/zh_cn/AWSCloudFormation/latest/UserGuide/deploying.applications.html

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

推荐阅读更多精彩内容

  • CloudFormation 是AWS的一款管理服务,用来集中式快速地创建AWS配套资源。 概念 模板 Cloud...
    azzgo阅读 3,834评论 0 2
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,565评论 25 707
  • 01 去年腊月二十一日的早晨,厂里距离较远的省外员工,第一批人准假放回家乡过年。这天天气晴朗,气候比较寒冷。陽光虽...
    仙圣腾飞阅读 525评论 1 2
  • 跳蚤妮妮阅读 140评论 0 0