Jenkins for Kubernetes實現(xiàn)Slave動態(tài)伸縮

本文章案例可用于參考Jenkins for Kubernetes部署。因每個公司的架構(gòu)和環(huán)境不一樣,需要改變一些部署的方式。

Jenkins for Kubernetes的好處:

  • Jenkins-Master的高可用。Kubernetes的RC或Deployment可以監(jiān)控副本的存活狀態(tài)(通過探針)和副本數(shù)量,如果Master出現(xiàn)無法提供服務(wù)的情況,就會重啟或者遷移到其他節(jié)點。
  • Jenkins-Slave的動態(tài)伸縮。每次構(gòu)建都會啟動一個Pod用于部署Slave,構(gòu)建完成后就會釋放掉。那么Pod在創(chuàng)建的時候,Kubernetes就會選擇集群內(nèi)資源剩余較多的節(jié)點創(chuàng)建Slave的Pod,構(gòu)建完成后Pod會自動刪除。
  • 擴展性好。 因為可以同時擁有很多個Slave,可以配置Jenkins同時執(zhí)行很多構(gòu)建操作,減少排隊等待構(gòu)建的時間。

部署思路

首先在Kubernetes中部署Jenkins-Master然后使用Kubernetes Plugin插件進行Slave的動態(tài)伸縮。并且使用NFS作為后端存儲的PersistentVolume來掛載Jenkins-Master的jenkins_home目錄、構(gòu)建時Slave的Maven緩存m2目錄(可以利用緩存加快每次構(gòu)建的速度)、保留Slave每次構(gòu)建產(chǎn)生的數(shù)據(jù)(workspace目錄中的每個Job)。

使用PersistentVolume的原因是Kubernetes任何節(jié)點都可以訪問到掛載的目錄,不會因為Master遷移節(jié)點導(dǎo)致數(shù)據(jù)丟失。NFS方便部署而且性能也滿足Jenkins的使用需求所以選擇了NFS,也可以使用其他的后端存儲。

部署

部署方式可以自定義也可以使用Kubernetes Pugin官網(wǎng)提供的部署yml。自定義使用Deployment也是可以的,但是官網(wǎng)的部署方式使用了StatefulSet。Jenkins是一個有狀態(tài)的應(yīng)用,我感覺使用StatefulSet部署更加嚴(yán)謹(jǐn)一點。我這里使用了官網(wǎng)提供的文檔進行部署的,但是也根據(jù)實際情況修改了一些東西。

首先需要在Kubernetes所有節(jié)點部署NFS客戶端:

yum -y install nfs-utils

systemctl start nfs-utils

systemctl enable nfs-utils

rpcinfo -p

NFS服務(wù)端配置文件增加配置:

/data/dev_jenkins       10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check)

dev環(huán)境Jenkins Slave節(jié)點掛載workspace

/data/dev_jenkins/workspace  0.0.0.0/0(rw,sync,no_root_squash,no_subtree_check)

dev環(huán)境Jenkins Slave節(jié)點掛載m2 Maven緩存目錄

/data/dev_jenkins/m2 0.0.0.0/0(rw,sync,no_root_squash,no_subtree_check)

共享目錄一定要給777權(quán)限。不然容器內(nèi)部會報錯沒有寫入權(quán)限。

service-account.yml此文件用于創(chuàng)建Kubernetes的RBAC,授權(quán)給后面的Jenkins應(yīng)用可以創(chuàng)建和刪除Slave的Pod。

# In GKE need to get RBAC permissions first with

# kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin [--user=<user-name>|--group=<group-name>]



---

apiVersion: v1

kind: ServiceAccount

metadata:

name: jenkins



---

kind: Role

apiVersion: rbac.authorization.k8s.io/v1beta1

metadata:

name: jenkins

rules:

- apiGroups: [""]

resources: ["pods"]

verbs: ["create","delete","get","list","patch","update","watch"]

- apiGroups: [""]

resources: ["pods/exec"]

verbs: ["create","delete","get","list","patch","update","watch"]

- apiGroups: [""]

resources: ["pods/log"]

verbs: ["get","list","watch"]

- apiGroups: [""]

resources: ["events"]

verbs: ["watch"]

- apiGroups: [""]

resources: ["secrets"]

verbs: ["get"]



---

apiVersion: rbac.authorization.k8s.io/v1beta1

kind: RoleBinding

metadata:

name: jenkins             #與jenkins.yml中的serviceAccountName: jenkins相對應(yīng)

roleRef:

apiGroup: rbac.authorization.k8s.io

kind: Role

name: jenkins

subjects:

- kind: ServiceAccount

name: jenkins

jenkins-pv.yml和jenkins-pvc.yml用于創(chuàng)建掛載jenkins_home目錄:

[root@dev-master1 kubernetes]# cat jenkins-pv.yml 

apiVersion: v1

kind: PersistentVolume

metadata:

name: jenkins-home

spec:

capacity:  #指定容量

storage: 20Gi

accessModes:

- ReadWriteOnce  #訪問模式,還有ReadOnlyMany ##ReadOnlymany

#  persistenVolumeReclaimPolicy: Recycle

#  storageClassName: nfs  ##指定存儲的類型

nfs:

path: /data/dev_jenkins  #指明NFS的路徑

server: 10.0.0.250  #指明NFS的IP



[root@dev-master1 kubernetes]# cat jenkins-pvc.yml 

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

namespace: kubernetes-plugin

name: jenkins-home

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

    storage: 20Gi

創(chuàng)建Jenkins的Master,可以根據(jù)實際情況限制Jenkins的資源使用。

[root@dev-master1 kubernetes]# cat jenkins.yml 

# jenkins

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

name: jenkins

labels:

name: jenkins

spec:

selector:

matchLabels:

  name: jenkins

serviceName: jenkins

replicas: 1

updateStrategy:

type: RollingUpdate

template:

metadata:

  name: jenkins

  labels:

    name: jenkins

spec:

  terminationGracePeriodSeconds: 10

  serviceAccountName: jenkins

  containers:

    - name: jenkins

      image: 10.0.0.59/jenkins/jenkins:lts-alpine #官方鏡像為jenkins/jenkins:lts-alpine,為了節(jié)省下載時間已經(jīng)push到自己到Harbor倉庫

      imagePullPolicy: Always

      ports:

        - containerPort: 8080

        - containerPort: 50000

      resources:

        limits:

          cpu: 1

          memory: 1Gi

        requests:

          cpu: 0.5

          memory: 500Mi

      env:

        - name: LIMITS_MEMORY

          valueFrom:

            resourceFieldRef:

              resource: limits.memory

              divisor: 1Mi

        - name: JAVA_OPTS

          # value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

          value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

      volumeMounts:         #掛載PVC存儲到Jenkins容器的/var/jenkins_home

        - name: jenkinshome

          mountPath: /var/jenkins_home

      livenessProbe:

        httpGet:

          path: /login

          port: 8080

        initialDelaySeconds: 600        #存活探針時間改為600s,如果服務(wù)器配置低,Jenkins還沒有啟動成功就被重啟了。

        timeoutSeconds: 5

        failureThreshold: 12 # ~2 minutes

      readinessProbe:

        httpGet:

          path: /login

          port: 8080

        initialDelaySeconds: 60

        timeoutSeconds: 5

        failureThreshold: 12 # ~2 minutes

  securityContext:

    fsGroup: 1000

  volumes:     #此處聲明Jenkins的PVC存儲

    - name: jenkinshome

      persistentVolumeClaim:

        claimName: jenkins-home

#      imagePullSecrets:                        如果使用私有倉庫,并且倉庫對鏡像設(shè)置了訪問權(quán)限,需要在Kubernetes Master創(chuàng)建一個secret

#        - name: registry-secret

jenkins-sv.yml用于創(chuàng)建Jenkins的Service。

[root@dev-master1 kubernetes]# cat jenkins-sv.yml 

apiVersion: v1

kind: Service

metadata:

name: jenkins

spec:

sessionAffinity: "ClientIP"

type: NodePort

selector:

name: jenkins

ports:

-

  name: http

  port: 80

  nodePort: 31006

  protocol: TCP

-

  name: agent

  port: 50000

  nodePort: 31007

  protocol: TCP

掛載Maven緩存目錄。

[root@dev-master1 kubernetes]# cat m2-pv.yml 

m2是Maven的緩存,掛載以提高build速度

apiVersion: v1

kind: PersistentVolume

metadata:

name: maven-m2

spec:

capacity:  #指定容量

storage: 200Gi

accessModes:

- ReadWriteOnce  #訪問模式,還有ReadOnlyMany ##ReadOnlymany

#  persistenVolumeReclaimPolicy: Recycle

#  storageClassName: nfs  ##指定存儲的類型

nfs:

path: /data/dev_jenkins/m2  #指明NFS的路徑

server: 10.0.0.250  #指明NFS的IP

[root@dev-master1 kubernetes]# cat m2-pvc.yml 

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

namespace: kubernetes-plugin

name: maven-m2

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 200Gi

掛載Slave節(jié)點保存構(gòu)建結(jié)果的目錄。

[root@dev-master1 kubernetes]# cat workspace-pv.yml 

m2是maven的緩存,掛載以提高build速度

apiVersion: v1

kind: PersistentVolume

metadata:

name: workspace

spec:

capacity:  #指定容量

storage: 200Gi

accessModes:

- ReadWriteOnce  #訪問模式,還有ReadOnlyMany ##ReadOnlymany

#  persistenVolumeReclaimPolicy: Recycle

#  storageClassName: nfs  ##指定存儲的類型

nfs:

path: /data/dev_jenkins/workspace  #指明NFS的路徑

server: 10.0.0.250  #指明NFS的IP

[root@dev-master1 kubernetes]# cat workspace-pvc.yml 

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

namespace: kubernetes-plugin

name: workspace

spec:

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 200Gi

創(chuàng)建Jenkins的Ingress。因為我的Kubernetes集群里面使用的是Traefik,所以我把Traefik的配置文件和kubernetes-plugin官網(wǎng)給出的Ingress一起貼出來。

[root@dev-master1 kubernetes]# cat jenkins-traefik.yml 

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: jenkins

namespace: kubernetes-plugin

annotations:

kubernetes.io/ingress.class: traefik

spec:

rules:

- host: jenkins-dev.doudou.com

http:

  paths:

  - path: /  

    backend:

      serviceName: jenkins

      servicePort: 80





[root@dev-master1 kubernetes]# cat jenkins-Ingress.yml 

因為集群使用Traefik所以此Ingress配置文件不創(chuàng)建,此文件為官方原版

apiVersion: extensions/v1beta1

kind: Ingress

metadata:

name: jenkins

annotations:

nginx.ingress.kubernetes.io/ssl-redirect: "true"

kubernetes.io/tls-acme: "true"

# "413 Request Entity Too Large" uploading plugins, increase client_max_body_size

nginx.ingress.kubernetes.io/proxy-body-size: 50m

nginx.ingress.kubernetes.io/proxy-request-buffering: "off"

# For nginx-ingress controller < 0.9.0.beta-18

ingress.kubernetes.io/ssl-redirect: "true"

# "413 Request Entity Too Large" uploading plugins, increase client_max_body_size

ingress.kubernetes.io/proxy-body-size: 50m

ingress.kubernetes.io/proxy-request-buffering: "off"

spec:

rules:

- http:

paths:

- path: /

backend:

serviceName: jenkins

servicePort: 80

host: jenkins.example.com

tls:

- hosts:

- jenkins.example.com

secretName: tls-jenkins

創(chuàng)建以上的配置文件:

kubectl create namespace kubernetes-plugin   #創(chuàng)建kubernetes-plugin namespace,下面創(chuàng)建的所有東西都?xì)w屬到這個namespace

kubectl config set-context $(kubectl config current-context) --namespace=kubernetes-plugin  #修改Kubernetes默認(rèn)的namespace為kubernetes-plugin,這樣下面創(chuàng)建的都默認(rèn)為kubernetes-plugin命名空間

kubectl create -f service-account.yml

kubectl create -f jenkins-Ingress.yml

kubectl create -f jenkins-pv.yml

kubectl create -f jenkins-pvc.yml

kubectl create -f jenkins-sv.yml

kubectl create -f jenkins.yml

kubectl create -f m2-pvc.yml

kubectl create -f m2-pv.yml

kubectl create -f workspace-pvc.yml

kubectl create -f workspace-pv.yml

查看創(chuàng)建狀態(tài):

[root@dev-master1 ~]# kubectl get service,pod,StatefulSet -o wide

NAME              TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                        AGE   SELECTOR

service/jenkins   NodePort   10.105.123.193   <none>        80:31006/TCP,50000:31007/TCP   9d    name=jenkins



NAME            READY   STATUS    RESTARTS   AGE    IP             NODE        NOMINATED NODE   READINESS GATES

pod/jenkins-0   1/1     Running   0          6d5h   100.78.0.141   dev-node4   <none>           <none>



NAME                       READY   AGE   CONTAINERS   IMAGES

statefulset.apps/jenkins   1/1     7d    jenkins      10.0.0.59/jenkins/jenkins:lts-alpine

[root@dev-master1 ~]# kubectl get pv,pvc

NAME                            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                            STORAGECLASS   REASON   AGE

persistentvolume/jenkins-home   20Gi       RWO            Retain           Bound    kubernetes-plugin/jenkins-home                           13d

persistentvolume/maven-m2       200Gi      RWO            Retain           Bound    kubernetes-plugin/maven-m2                               7d5h

persistentvolume/workspace      200Gi      RWO            Retain           Bound    kubernetes-plugin/workspace                              7d5h



NAME                                           STATUS    VOLUME         CAPACITY   ACCESS MODES   STORAGECLASS   AGE

persistentvolumeclaim/jenkins-home             Bound     jenkins-home   20Gi       RWO                           13d

persistentvolumeclaim/maven-m2                 Bound     maven-m2       200Gi      RWO                           7d5h

persistentvolumeclaim/workspace                Bound     workspace      200Gi      RWO                           7d5h

PV的狀態(tài)為Bound狀態(tài)表示已經(jīng)綁定到對應(yīng)的PVC上。Jenkins的Pod狀態(tài)為1/1就說明啟動成功了,可以通過綁定Ingress的域名訪問了。或者使用Service配置中的nodePort端口訪問Kubernetes任意節(jié)點IP:nodePort。

查看Jenkins密碼:

kubectl exec -it jenkins-0 -n kubernetes-plugin -- cat /var/jenkins_home/secrets/initialAdminPassword

Jenkins配置

Jenkins安裝完成后進入UI界面,首先需要安裝需要的插件。

Jenkins可以根據(jù)實際情況選擇適合的源:

系統(tǒng)管理->插件管理->高級

https://updates.jenkins.io/update-center.json #官方源

https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json #清華源

然后安裝需要的插件:

  • Git pPugin

  • Maven Integration Plugin

  • Docker Plugin

  • Kubernetes Continuous Deploy Plugin

  • Kubernetes Plugin

  • Publish Over SSH Plugin

  • SSH Agent Plugin

  • SSH Build Agents Plugin

  • promoted builds plugin

  • Promoted Builds (Simple)

配置

Kubernetes Plugin插件安裝完成后在Jenkins設(shè)置里面點擊【系統(tǒng)配置】拉到最下面就可以看到一個Cloud。

image

單擊之,添加一個云:

image
  • 名稱:名字隨便取,后面連接云的時候需要這個名字。
  • Kubernetes地址:訪問Kubernetes Master上kube-apiserver服務(wù)的地址。
  • Kubernetes命名空間:Jenkins部署在哪個命名空間里面了。
  • Jenkins地址:Jenkins訪問地址。
  • Jenkins通道(這特么是一個大坑) :訪問Jenkins容器內(nèi)50000端口地址。因為Jenkins的Service配置文件中我把50000端口映射為nodePort,再加上我配置了DNS所以我這里寫了域名:端口號的格式,也可以使用IP地址+端口號。

因為Jenkins-Master和Jenkins-Slave都在Kubernetes集群內(nèi)部,所以寫ClusterIP:端口號應(yīng)該也是可以的,但是我沒試過,略略略:),地址只要能訪問到容器內(nèi)部的50000端口就可以,但是有一點需要注意,這里的格式不能加http不能加/感覺應(yīng)該是協(xié)議的問題,但是還沒搞懂。

點擊連接測試,是否能夠成功。

測試

連接成功后,創(chuàng)建一個流水線Job進行測試使用。

podTemplate(label: 'jnlp-slave', cloud: 'kubernetes', containers: [

containerTemplate(name: 'maven', image: '10.0.0.59/jenkins/maven:3.3.9-jdk-8-alpine', ttyEnabled: true, command: 'cat'),

],

volumes: [

persistentVolumeClaim(mountPath: '/root/.m2', claimName: 'maven-m2'),

persistentVolumeClaim(mountPath: '/home/jenkins/agent/workspace', claimName: 'workspace'),

]

)

{

node("jnlp-slave"){

  stage('Build'){

      git branch: 'master', url: 'http://root:qrGw1S_azFE3F77Rs7tA@gitlab.gemantic.com/java/$JOB_NAME.git'

      container('maven') {

          stage('Build a Maven project') {

              sh 'mvn clean package -U deploy'

          }

      }

  }

  stage('deploy'){

      sshPublisher(publishers: [sshPublisherDesc(configName: '76', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '/data/script/jenkins.sh $JOB_NAME', execTimeout: 120000000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/data/kubernetes/service/$JOB_NAME', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/$JOB_NAME*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

  }

}

} 

Pipeline解讀:

  • podTemplate創(chuàng)建了一個Pod模版。Cloud字段指定了連接哪個Kubernetes云,Kubernetes就是剛才創(chuàng)建一個一個Kubernetes,云的名字就是kubernetes。

  • Maven鏡像為了加快下載速度,我傳到了私有倉庫,官方鏡像就是把IP地址去掉對應(yīng)的鏡像。

  • persistentVolumeClaim定義了目錄掛載,把Maven構(gòu)建的緩存目錄.m2和構(gòu)建產(chǎn)生的數(shù)據(jù)目錄workspace都掛載了一下

  • 下面的Pipeline指定后面的操作在jnlp-slave中(也就是Pod模版同時也是Slave節(jié)點)

  • 在build操作中,需要先拉取代碼,GitLab拉取代碼這里使用了GitLab的root token進行拉取的。GitLab用戶獲取Token方法:

    image
  • 下面就是開始編譯啦~,因為是一個Java服務(wù),編譯完成后會生成一個jar包。

  • deploy步驟就是開始發(fā)布了,下面的Pipeline是用流水線語法自動生成的。

    image
  • 然后點擊構(gòu)建進行測試。

    image
  • 構(gòu)建過程中,可以看到Pod調(diào)度到master3上進行構(gòu)建了。

  • 構(gòu)建過程中用到了兩個鏡像,一個Maven(已被上傳到了私有倉庫),一個inbound-agent鏡像。inbound-agent鏡像是官方的鏡像,和Maven的關(guān)系是都在同一個Pod中共享數(shù)據(jù),并和Jenkins-master進行交互。(inbound-agent鏡像怎么修改為私有倉庫鏡像還沒搞明白,總是去公網(wǎng)下載速度慢)

  • 構(gòu)建過程中不斷的下載Java程序依賴的各種包,因為是第一次時間久了一點,但是我們已經(jīng)把.m2緩存目錄掛載出來了,下次再次構(gòu)建的時候就可以大大縮減構(gòu)建的時間。

  • workspace也被掛載了出來,每次構(gòu)建的數(shù)據(jù)也會保留,以備不時之需。

構(gòu)建成功后查看NFS共享目錄中的數(shù)據(jù):

image
root@sa-storage:/data/dev_jenkins# du -sh m2/

218M    m2/

root@sa-storage:/data/dev_jenkins# du -sh workspace/

65M workspace/

至此所有的需求都實現(xiàn)了,Slave實現(xiàn)了動態(tài)伸縮,相關(guān)的目錄都被掛載出來了。

排錯

kubectl get pod -n kubernetes-plugin -o wide命令可以查看Slave的Pod狀態(tài),如果出現(xiàn)問題Slave一直無限重啟,需要查看Pod日志。

kubectl logs `kubectl get pod -n kubernetes-plugin -o wide|grep jnlp-slave|awk '{print $1}'` -n  kubernetes-plugin

每次重啟Pod的名字都會重新生成,而且正在創(chuàng)建中的Pod是無法查看日志的,就算有問題Pod也是瞬間就重啟了,所以只能上面的這個命令無限的刷。手速快的可以手動哦~手速跟不上的也可以寫個循環(huán)噠。主要就是文中說的那個大坑,那個坑過去,小問題都可以通過看日志解決的。如果忘記大坑在哪里,可以ctrl+f搜索關(guān)鍵字 “大坑” 哦~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,703評論 2 380

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