K8s 系列(五) - 淺談 CSI

1. 概述

進(jìn)入 K8s 的世界概行,會(huì)發(fā)現(xiàn)有很多方便擴(kuò)展的 Interface凳忙,包括 CSI, CNI, CRI 等消略,將這些接口抽象出來(lái),是為了更好的提供開(kāi)放桐臊、擴(kuò)展晓殊、規(guī)范等能力巫俺。

K8s 持久化存儲(chǔ)經(jīng)歷了從 in-tree Volume 到 CSI Plugin(out-of-tree) 的遷移介汹,一方面是為了將 K8s 核心主干代碼與 Volume 相關(guān)代碼解耦嘹承,便于更好的維護(hù)叹卷;另一方面則是為了方便各大云廠商實(shí)現(xiàn)統(tǒng)一的接口骤竹,提供個(gè)性化的云存儲(chǔ)能力蒙揣,以期達(dá)到云存儲(chǔ)生態(tài)圈的開(kāi)放共贏墨技。

本文將從持久卷 PV 的 創(chuàng)建(Create)、附著(Attach)断楷、分離(Detach)冬筒、掛載(Mount)舞痰、卸載(Unmount)、刪除(Delete) 等核心生命周期呀打,對(duì) CSI 實(shí)現(xiàn)機(jī)制進(jìn)行了解析贬丛。

相關(guān)術(shù)語(yǔ)

Term Definition
CSI Container Storage Interface.
CNI Container Network Interface.
CSI Container Runtime Interface.
PV Persistent Volume.
PVC Persistent Volume Claim.
Volume A unit of storage that will be made available inside of a CO-managed container, via the CSI.
Block Volume A volume that will appear as a block device inside the container.
Mounted Volume A volume that will be mounted using the specified file system and appear as a directory inside the container.
CO Container Orchestration system, communicates with Plugins using CSI service RPCs.
SP Storage Provider, the vendor of a CSI plugin implementation.
RPC Remote Procedure Call.
Node A host where the user workload will be running, uniquely identifiable from the perspective of a Plugin by a node ID.
Plugin Aka “plugin implementation”, a gRPC endpoint that implements the CSI Services.
Plugin Supervisor Process that governs the lifecycle of a Plugin, MAY be the CO.
Workload The atomic unit of "work" scheduled by a CO. This MAY be a container or a collection of containers.

本文及后續(xù)相關(guān)文章都基于 K8s v1.22

K8s-CSI.png

2. 從 CSI 說(shuō)起

CSI(Container Storage Interface) 是由來(lái)自 Kubernetes、Mesos恭应、Docker 等社區(qū) member 聯(lián)合制定的一個(gè)行業(yè)標(biāo)準(zhǔn)接口規(guī)范(https://github.com/container-storage-interface/spec),旨在將任意存儲(chǔ)系統(tǒng)暴露給容器化應(yīng)用程序褒纲。

CSI 規(guī)范定義了存儲(chǔ)提供商實(shí)現(xiàn) CSI 兼容的 Volume Plugin 的最小操作集和部署建議莺掠。CSI 規(guī)范的主要焦點(diǎn)是聲明 Volume Plugin 必須實(shí)現(xiàn)的接口彻秆。

先看一下 Volume 的生命周期:

   CreateVolume +------------+ DeleteVolume
 +------------->|  CREATED   +--------------+
 |              +---+----^---+              |
 |       Controller |    | Controller       v
+++         Publish |    | Unpublish       +++
|X|          Volume |    | Volume          | |
+-+             +---v----+---+             +-+
                | NODE_READY |
                +---+----^---+
               Node |    | Node
              Stage |    | Unstage
             Volume |    | Volume
                +---v----+---+
                |  VOL_READY |
                +---+----^---+
               Node |    | Node
            Publish |    | Unpublish
             Volume |    | Volume
                +---v----+---+
                | PUBLISHED  |
                +------------+

The lifecycle of a dynamically provisioned volume, from
creation to destruction, when the Node Plugin advertises the
STAGE_UNSTAGE_VOLUME capability.

從 Volume 生命周期可以看到酒朵,一塊持久卷要達(dá)到 Pod 可使用狀態(tài)蔫耽,需要經(jīng)歷以下階段:

CreateVolume -> ControllerPublishVolume -> NodeStageVolume -> NodePublishVolume

而當(dāng)刪除 Volume 的時(shí)候匙铡,會(huì)經(jīng)過(guò)如下反向階段:

NodeUnpublishVolume -> NodeUnstageVolume -> ControllerUnpublishVolume -> DeleteVolume

上面流程的每個(gè)步驟碍粥,其實(shí)就對(duì)應(yīng)了 CSI 提供的標(biāo)準(zhǔn)接口钦讳,云存儲(chǔ)廠商只需要按標(biāo)準(zhǔn)接口實(shí)現(xiàn)自己的云存儲(chǔ)插件蜂厅,即可與 K8s 底層編排系統(tǒng)無(wú)縫銜接起來(lái),提供多樣化的云存儲(chǔ)唇跨、備份买猖、快照(snapshot)等能力玉控。

3. 多組件協(xié)同

為實(shí)現(xiàn)具有高擴(kuò)展性、out-of-tree 的持久卷管理能力狮惜,在 K8s CSI 實(shí)現(xiàn)中高诺,相關(guān)協(xié)同的組件有:

K8s-CSI-component.png

3.1 組件介紹

  • kube-controller-manager:K8s 資源控制器,主要通過(guò) PVController, AttachDetach 實(shí)現(xiàn)持久卷的綁定(Bound)/解綁(Unbound)碾篡、附著(Attach)/分離(Detach)虱而;
  • CSI-plugin:K8s 獨(dú)立拆分出來(lái),實(shí)現(xiàn) CSI 標(biāo)準(zhǔn)規(guī)范接口的邏輯控制與調(diào)用开泽,是整個(gè) CSI 控制邏輯的核心樞紐;
  • node-driver-registrar:是一個(gè)由官方 K8s sig 小組維護(hù)的輔助容器(sidecar),它使用 kubelet 插件注冊(cè)機(jī)制向 kubelet 注冊(cè)插件惠呼,需要請(qǐng)求 CSI 插件的 Identity 服務(wù)來(lái)獲取插件信息导俘;
  • external-provisioner:是一個(gè)由官方 K8s sig 小組維護(hù)的輔助容器(sidecar),主要功能是實(shí)現(xiàn)持久卷的創(chuàng)建(Create)剔蹋、刪除(Delete)旅薄;
  • external-attacher:是一個(gè)由官方 K8s sig 小組維護(hù)的輔助容器(sidecar),主要功能是實(shí)現(xiàn)持久卷的附著(Attach)滩租、分離(Detach)赋秀;
  • external-snapshotter:是一個(gè)由官方 K8s sig 小組維護(hù)的輔助容器(sidecar),主要功能是實(shí)現(xiàn)持久卷的快照(VolumeSnapshot)律想、備份恢復(fù)等能力猎莲;
  • external-resizer:是一個(gè)由官方 K8s sig 小組維護(hù)的輔助容器(sidecar),主要功能是實(shí)現(xiàn)持久卷的彈性擴(kuò)縮容技即,需要云廠商插件提供相應(yīng)的能力著洼;
  • kubelet:K8s 中運(yùn)行在每個(gè) Node 上的控制樞紐,主要功能是調(diào)諧節(jié)點(diǎn)上 Pod 與 Volume 的附著而叼、掛載身笤、監(jiān)控探測(cè)上報(bào)等;
  • cloud-storage-provider:由各大云存儲(chǔ)廠商基于 CSI 標(biāo)準(zhǔn)接口實(shí)現(xiàn)的插件葵陵,包括 Identity 身份服務(wù)液荸、Controller 控制器服務(wù)、Node 節(jié)點(diǎn)服務(wù)脱篙;

3.2 組件通信

由于 CSI plugin 的代碼在 K8s 中被認(rèn)為是不可信的娇钱,因此 CSI Controller Server 和 External CSI SideCar、CSI Node Server 和 Kubelet 通過(guò) Unix Socket 來(lái)通信绊困,與云存儲(chǔ)廠商提供的 Storage Service 通過(guò) gRPC(HTTP/2) 通信:

K8s-CSI-network.png

4. RPC 調(diào)用

從 CSI 標(biāo)準(zhǔn)規(guī)范可以看到文搂,云存儲(chǔ)廠商想要無(wú)縫接入 K8s 容器編排系統(tǒng),需要按規(guī)范實(shí)現(xiàn)相關(guān)接口秤朗,相關(guān)接口主要為:


K8s-CSI-PRC.png
  • Identity 身份服務(wù):Node Plugin 和 Controller Plugin 都必須實(shí)現(xiàn)這些 RPC 集煤蹭,協(xié)調(diào) K8s 與 CSI 的版本信息,負(fù)責(zé)對(duì)外暴露這個(gè)插件的信息取视。
  • Controller 控制器服務(wù):Controller Plugin 必須實(shí)現(xiàn)這些 RPC 集硝皂,創(chuàng)建以及管理 Volume,對(duì)應(yīng) K8s 中 attach/detach volume 操作作谭。
  • Node 節(jié)點(diǎn)服務(wù):Node Plugin 必須實(shí)現(xiàn)這些 RPC 集吧彪,將 Volume 存儲(chǔ)卷掛載到指定目錄中,對(duì)應(yīng) K8s 中的 mount/unmount volume 操作丢早。

相關(guān) RPC 接口功能如下:

K8s-CSI-RPC-info.png

5. 創(chuàng)建/刪除 PV

K8s 中持久卷 PV 的創(chuàng)建(Create)與刪除(Delete)姨裸,由 external-provisioner 組件實(shí)現(xiàn)秧倾,相關(guān)工程代碼在:
https://github.com/kubernetes-csi/external-provisioner

首先,通過(guò)標(biāo)準(zhǔn)的 cmd 方式獲取命令行參數(shù)傀缩,執(zhí)行 newController -> Run() 邏輯那先,相關(guān)代碼如下:

// external-provisioner/cmd/csi-provisioner/csi-provisioner.go
main() {
...
    // 初始化控制器,實(shí)現(xiàn) Volume 創(chuàng)建/刪除接口
    csiProvisioner := ctrl.NewCSIProvisioner(
        clientset,
        *operationTimeout,
        identity,
        *volumeNamePrefix,
        *volumeNameUUIDLength,
        grpcClient,
        snapClient,
        provisionerName,
        pluginCapabilities,
        controllerCapabilities,
        ...
    )
    ...
    // 真正的 ProvisionController赡艰,包裝了上面的 CSIProvisioner
    provisionController = controller.NewProvisionController(
        clientset,
        provisionerName,
        csiProvisioner,
        provisionerOptions...,
    )
    ...
    run := func(ctx context.Context) {
        ...
        // Run 運(yùn)行起來(lái)
        provisionController.Run(ctx)
    }
}

接著售淡,調(diào)用 PV 創(chuàng)建/刪除流程:

PV 創(chuàng)建:runClaimWorker -> syncClaimHandler -> syncClaim -> provisionClaimOperation -> Provision -> CreateVolume
PV 刪除:runVolumeWorker -> syncVolumeHandler -> syncVolume -> deleteVolumeOperation -> Delete -> DeleteVolume

K8s-CSI-provisioner.png

由 sigs.k8s.io/sig-storage-lib-external-provisioner 抽象了相關(guān)接口:

// 通過(guò) vendor 方式引入 sigs.k8s.io/sig-storage-lib-external-provisioner
// external-provisioner/vendor/sigs.k8s.io/sig-storage-lib-external-provisioner/v7/controller/volume.go
type Provisioner interface {
    // 調(diào)用 PRC CreateVolume 接口實(shí)現(xiàn) PV 創(chuàng)建
    Provision(context.Context, ProvisionOptions) (*v1.PersistentVolume, ProvisioningState, error)
    // 調(diào)用 PRC DeleteVolume 接口實(shí)現(xiàn) PV 刪除
    Delete(context.Context, *v1.PersistentVolume) error
}

6. Controller 調(diào)諧

K8s 中與 PV 相關(guān)的控制器有 PVController、AttachDetachController慷垮。

6.1 PVController

PVController 通過(guò)在 PVC 添加相關(guān) Annotation(如 pv.kubernetes.io/provisioned-by)揖闸,由 external-provisioner 組件負(fù)責(zé)完成對(duì)應(yīng) PV 的創(chuàng)建/刪除,然后 PVController 監(jiān)測(cè)到 PV 創(chuàng)建成功的狀態(tài)料身,完成與 PVC 的綁定(Bound)汤纸,調(diào)諧(reconcile)任務(wù)完成。然后交給 AttachDetachController 控制器進(jìn)行下一步邏輯處理芹血。

值得一提的是贮泞,PVController 內(nèi)部通過(guò)使用 local cache,高效實(shí)現(xiàn)了 PVC 與 PV 的狀態(tài)更新與綁定事件處理幔烛,相當(dāng)于在 K8s informer 機(jī)制之外啃擦,又自己維護(hù)了一個(gè) local store 進(jìn)行 Add/Update/Delete 事件處理。

首先饿悬,通過(guò)標(biāo)準(zhǔn)的 newController -> Run() 邏輯:

// kubernetes/pkg/controller/volume/persistentvolume/pv_controller_base.go
func NewController(p ControllerParameters) (*PersistentVolumeController, error) {
    ...
    // 初始化 PVController
    controller := &PersistentVolumeController{
        volumes:                       newPersistentVolumeOrderedIndex(),
        claims:                        cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc),
        kubeClient:                    p.KubeClient,
        eventRecorder:                 eventRecorder,
        runningOperations:             goroutinemap.NewGoRoutineMap(true /* exponentialBackOffOnError */),
        cloud:                         p.Cloud,
        enableDynamicProvisioning:     p.EnableDynamicProvisioning,
        clusterName:                   p.ClusterName,
        createProvisionedPVRetryCount: createProvisionedPVRetryCount,
        createProvisionedPVInterval:   createProvisionedPVInterval,
        claimQueue:                    workqueue.NewNamed("claims"),
        volumeQueue:                   workqueue.NewNamed("volumes"),
        resyncPeriod:                  p.SyncPeriod,
        operationTimestamps:           metrics.NewOperationStartTimeCache(),
    }
    ...
    // PV 增刪改事件監(jiān)聽(tīng)
    p.VolumeInformer.Informer().AddEventHandler(
        cache.ResourceEventHandlerFuncs{
            AddFunc:    func(obj interface{}) { controller.enqueueWork(controller.volumeQueue, obj) },
            UpdateFunc: func(oldObj, newObj interface{}) { controller.enqueueWork(controller.volumeQueue, newObj) },
            DeleteFunc: func(obj interface{}) { controller.enqueueWork(controller.volumeQueue, obj) },
        },
    )
    ...
    // PVC 增刪改事件監(jiān)聽(tīng)
    p.ClaimInformer.Informer().AddEventHandler(
        cache.ResourceEventHandlerFuncs{
            AddFunc:    func(obj interface{}) { controller.enqueueWork(controller.claimQueue, obj) },
            UpdateFunc: func(oldObj, newObj interface{}) { controller.enqueueWork(controller.claimQueue, newObj) },
            DeleteFunc: func(obj interface{}) { controller.enqueueWork(controller.claimQueue, obj) },
        },
    )
    ...
    return controller, nil
}

接著令蛉,調(diào)用 PVC/PV 綁定/解綁邏輯:

PVC/PV 綁定:claimWorker -> updateClaim -> syncClaim -> syncBoundClaim -> bind
PVC/PV 解綁:volumeWorker -> updateVolume -> syncVolume -> unbindVolume

6.2 AttachDetachController

AttachDetachController 將已經(jīng)綁定(Bound) 成功的 PVC/PV,內(nèi)部經(jīng)過(guò) InTreeToCSITranslator 轉(zhuǎn)換器狡恬,實(shí)現(xiàn)由 in-tree 方式管理的 Volume 向 out-of-tree 方式管理的 CSI 插件模式轉(zhuǎn)換言询。

接著,由 CSIPlugin 內(nèi)部邏輯實(shí)現(xiàn) VolumeAttachment 資源類型的創(chuàng)建/刪除傲宜,調(diào)諧(reconcile) 任務(wù)完成。然后交給 external-attacher 組件進(jìn)行下一步邏輯處理夫啊。

相關(guān)核心代碼在 reconciler.Run() 中實(shí)現(xiàn)如下:

// kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go
func (rc *reconciler) reconcile() {

    // 先進(jìn)行 DetachVolume函卒,確保因 Pod 重新調(diào)度到其他節(jié)點(diǎn)的 Volume 提前分離(Detach)
    for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() {
        // 如果不在期望狀態(tài)的 Volume,則調(diào)用 DetachVolume 刪除 VolumeAttachment 資源對(duì)象
        if !rc.desiredStateOfWorld.VolumeExists(
            attachedVolume.VolumeName, attachedVolume.NodeName) {
            ...
            err = rc.attacherDetacher.DetachVolume(attachedVolume.AttachedVolume, verifySafeToDetach, rc.actualStateOfWorld)
            ...
        }
    }
    // 調(diào)用 AttachVolume 創(chuàng)建 VolumeAttachment 資源對(duì)象
    rc.attachDesiredVolumes()
    ...
}

7. 附著/分離 Volume

K8s 中持久卷 PV 的附著(Attach)與分離(Detach)撇眯,由 external-attacher 組件實(shí)現(xiàn)报嵌,相關(guān)工程代碼在:
https://github.com/kubernetes-csi/external-attacher

external-attacher 組件觀察到由上一步 AttachDetachController 創(chuàng)建的 VolumeAttachment 對(duì)象,如果其 .spec.Attacher 中的 Driver name 指定的是自己同一 Pod 內(nèi)的 CSI Plugin熊榛,則調(diào)用 CSI Plugin 的ControllerPublish 接口進(jìn)行 Volume Attach锚国。

首先,通過(guò)標(biāo)準(zhǔn)的 cmd 方式獲取命令行參數(shù)玄坦,執(zhí)行 newController -> Run() 邏輯血筑,相關(guān)代碼如下:

// external-attacher/cmd/csi-attacher/main.go
func main() {
    ...
    ctrl := controller.NewCSIAttachController(
        clientset,
        csiAttacher,
        handler,
        factory.Storage().V1().VolumeAttachments(),
        factory.Core().V1().PersistentVolumes(),
        workqueue.NewItemExponentialFailureRateLimiter(*retryIntervalStart, *retryIntervalMax),
        workqueue.NewItemExponentialFailureRateLimiter(*retryIntervalStart, *retryIntervalMax),
        supportsListVolumesPublishedNodes,
        *reconcileSync,
    )

    run := func(ctx context.Context) {
        stopCh := ctx.Done()
        factory.Start(stopCh)
        ctrl.Run(int(*workerThreads), stopCh)
    }
    ...
}

接著绘沉,調(diào)用 Volume 附著/分離邏輯:

Volume 附著(Attach):syncVA -> SyncNewOrUpdatedVolumeAttachment -> syncAttach -> csiAttach -> Attach -> ControllerPublishVolume
Volume 分離(Detach):syncVA -> SyncNewOrUpdatedVolumeAttachment -> syncDetach -> csiDetach -> Detach -> ControllerUnpublishVolume

K8s-CSI-attacher.png

8. kubelet 掛載/卸載 Volume

K8s 中持久卷 PV 的掛載(Mount)與卸載(Unmount),由 kubelet 組件實(shí)現(xiàn)豺总。

kubelet 通過(guò) VolumeManager 啟動(dòng) reconcile loop车伞,當(dāng)觀察到有新的使用 PersistentVolumeSource 為CSI 的 PV 的 Pod 調(diào)度到本節(jié)點(diǎn)上,于是調(diào)用 reconcile 函數(shù)進(jìn)行 Attach/Detach/Mount/Unmount 相關(guān)邏輯處理喻喳。

// kubernetes/pkg/kubelet/volumemanager/reconciler/reconciler.go
func (rc *reconciler) reconcile() {
    // 先進(jìn)行 UnmountVolume另玖,確保因 Pod 刪除被重新 Attach 到其他 Pod 的 Volume 提前卸載(Unmount)
    rc.unmountVolumes()

    // 接著通過(guò)判斷 controllerAttachDetachEnabled || PluginIsAttachable 及當(dāng)前 Volume 狀態(tài)
    // 進(jìn)行 AttachVolume / MountVolume / ExpandInUseVolume
    rc.mountAttachVolumes()

    // 卸載(Unmount) 或分離(Detach) 不再需要(Pod 刪除)的 Volume
    rc.unmountDetachDevices()
}

相關(guān)調(diào)用邏輯如下:

Volume 掛載(Mount):reconcile -> mountAttachVolumes -> MountVolume -> SetUp -> SetUpAt -> NodePublishVolume
Volume 卸載(Unmount):reconcile -> unmountVolumes -> UnmountVolume -> TearDown -> TearDownAt -> NodeUnpublishVolume

K8s-CSI-kubelet.png

9. 小結(jié)

本文通過(guò)分析 K8s 中持久卷 PV 的 創(chuàng)建(Create)、附著(Attach)表伦、分離(Detach)谦去、掛載(Mount)、卸載(Unmount)蹦哼、刪除(Delete) 等核心生命周期流程鳄哭,對(duì) CSI 實(shí)現(xiàn)機(jī)制進(jìn)行了解析,通過(guò)源碼翔怎、圖文方式說(shuō)明了相關(guān)流程邏輯窃诉,以期更好的理解 K8s CSI 運(yùn)行流程。

可以看到赤套,K8s 以 CSI Plugin(out-of-tree) 插件方式開(kāi)放存儲(chǔ)能力飘痛,一方面是為了將 K8s 核心主干代碼與 Volume 相關(guān)代碼解耦,便于更好的維護(hù)容握;另一方面在遵從 CSI 規(guī)范接口下宣脉,便于各大云廠商根據(jù)業(yè)務(wù)需求實(shí)現(xiàn)相關(guān)的接口,提供個(gè)性化的云存儲(chǔ)能力剔氏,以期達(dá)到云存儲(chǔ)生態(tài)圈的開(kāi)放共贏塑猖。

PS: 更多內(nèi)容請(qǐng)關(guān)注 k8s-club

參考資料

  1. CSI 規(guī)范
  2. Kubernetes 源碼
  3. kubernetes-csi 源碼
  4. kubernetes-sig-storage 源碼
  5. K8s CSI 概念
  6. K8s CSI 介紹
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谈跛,隨后出現(xiàn)的幾起案子羊苟,更是在濱河造成了極大的恐慌,老刑警劉巖感憾,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜡励,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡阻桅,警方通過(guò)查閱死者的電腦和手機(jī)凉倚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嫂沉,“玉大人稽寒,你說(shuō)我怎么就攤上這事√苏拢” “怎么了杏糙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵慎王,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我搔啊,道長(zhǎng)柬祠,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任负芋,我火速辦了婚禮漫蛔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旧蛾。我一直安慰自己莽龟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布锨天。 她就那樣靜靜地躺著毯盈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪病袄。 梳的紋絲不亂的頭發(fā)上搂赋,一...
    開(kāi)封第一講書(shū)人閱讀 51,775評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音益缠,去河邊找鬼脑奠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛幅慌,可吹牛的內(nèi)容都是我干的宋欺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胰伍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼齿诞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起骂租,我...
    開(kāi)封第一講書(shū)人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祷杈,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后渗饮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體但汞,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年抽米,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糙置。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡云茸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谤饭,到底是詐尸還是另有隱情标捺,我是刑警寧澤懊纳,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站亡容,受9級(jí)特大地震影響嗤疯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闺兢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一茂缚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屋谭,春花似錦脚囊、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至我擂,卻和暖如春衬以,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背校摩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工看峻, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秧耗。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓备籽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親分井。 傳聞我的和親對(duì)象是個(gè)殘疾皇子车猬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容