pipeline&jenkins

1:部署完成之后检测pod状态,获取同名pod的个数,一个就执行检测,多个就直接报失败,设置超时时间内检测

stage("get pod and test to rollouting"){
        steps{
            script {
                def namespace="${params.namespace}";
                def depName="dep-${params.moduleCode}";
                timeout(240) {
                   while(true) {
                     def result = sh(script: "kubectl get pod -n " +namespace+ " --kubeconfig=/root/.kube/dev-config  |grep " +depName+"-*|wc -l", returnStdout:true).trim()
                     sh "echo ${result}"
                        if("${result}" == "1"){
                           sleep (10)
                           sh "kubectl get pod -n " +namespace+ " --kubeconfig=/root/.kube/dev-config |grep " +depName+ " > appname.txt"
                           def data = readFile(file: "appname.txt")
                           println data
                           def list =data.split(' ')
                           sh "kubedog  rollout track -n "  +namespace+ " pod " +list[0]+ " --kube-config=/root/.kube/dev-config && kubectl logs --since=2m " +list[0]+ " -n " +namespace+ " --kubeconfig=/root/.kube/dev-config  || kubectl  rollout undo deployment/"+depName+ " -n "  +namespace+ " --kubeconfig=/root/.kube/dev-config"
                           sh "break"
                        }else{
                           sleep (10)
                           def result1 = sh(script: "kubectl get pod -n " +namespace+ " --kubeconfig=/root/.kube/dev-config |grep " +depName+"-*|wc -l", returnStdout:true).trim()
                           sh "echo ${result1}"
                           if("${result1}" == "1"){
                               sh "kubectl get pod -n " +namespace+ " --kubeconfig=/root/.kube/dev-config |grep " +depName+ " > appname.txt"
                               def data = readFile(file: "appname.txt")
                               println data
                               def list =data.split(' ')
                               sh "kubedog  rollout track -n "  +namespace+ " pod " +list[0]+ " --kube-config=/root/.kube/dev-config && kubectl logs --since=2m " +list[0]+ " -n " +namespace+ " --kubeconfig=/root/.kube/dev-config ||  kubectl  rollout undo deployment/"+depName+ " -n "  +namespace+ " --kubeconfig=/root/.kube/dev-config"
                               sh "break"
                           }else{
                               sh "echo '继续检测中.....'"
                           }
                            
                        }
                    }
                }  
            } 
        }
    }

2:选分支拉取代码

stage("git pull code") {
  steps {
    //clear workspace
    cleanWs()
    script {
      //pull code
      print "repoBranch : ${params.repoBranch}"
      checkout([$class: 'GitSCM', branches: [[name: "${params.repoBranch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${params.repoCred}", url: "${params.repoUrl}"]]])
    }
  }
  }

3:kubernetes之服务回滚

stage("rollout pod"){
        steps{
            script{
                def namespace="${params.namespace}";
                def controller="${params.controller}";
                def depName="${params.depName}";
                sh "kubectl get " +controller+  " -n " +namespace+ " |grep " +depName+ " > controllerName.txt"
                def data = readFile(file: "controllerName.txt")
                println data
                def list =data.split(' ')
                sh "kubectl  rollout undo " +controller+"/"+list[0]+ " -n "  +namespace
                //sh "kubedog  rollout track -n "  +namespace+ " " +controller+ " " +list[0]+ " && echo 'undo start success' || echo 'undo start faild'"
                
            }
        }
    }

4:gradle结合sonar编译加扫描

stage("gradle and sonar"){
        steps{
              script {
                  def timeStamp=new Date().format("yyyy-MM-dd");
                  def targetDir="${WORKSPACE}/${params.targetDir}";
                  dir(targetDir){
                      withSonarQubeEnv('sonarqube-jenkins') {
                          print "repoBranch : ${params.repoBranch}"
                          sh "gradle clean build --parallel -x test sonarqube -Dsonar.projectKey=${params.projectKey} -Dsonar.projectName=${params.projectKey} -Dsonar.sourceEncoding=UTF-8 -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.java.binaries=build/classes -Dsonar.analysis.isFortify=true -Dsonar.projectDate="+timeStamp
                      }
                  }
            }
     }
}

5:docker镜像编译

pipeline {
    agent any
    environment {
        def tag = sh(script: "echo `date +%s`", returnStdout: true).trim()
    }
    stage("docker build"){
        steps{
                //def IMAGE_URL="10.10.0.100:8081/test-docker-dev/${params.IMAGE_NAME}:${tag};
                sh "cd ${WORKSPACE}/ && docker build -t 10.10.0.100:8081/test-docker-dev/${params.IMAGE_NAME}:${tag} -f Dockerfile ."
                sh "docker push  10.10.0.100:8081/test-docker-dev/${params.IMAGE_NAME}:${tag}"
        }
    }
}

6:灵活部署yaml资源,如存在就打上原始标注,不存在就部署并打上标注,方便后期回滚

stage("k8s build"){
        steps{
            script{
                def filePath="${params.ymlPath}";
                def namespace="${params.namespace}";
                def depName="${params.depName}";
                def imageUrl="${params.imageUrl}";
               def imageTag="${params.imageTag}";
                //def artifactVersion="${params.artifactVersion}";
                def imageName="${params.imageName}"
                def namesp="devops-test"
                def nFile="${WORKSPACE}/management-deployment-svc.yaml"
                def data = readFile(file: filePath)
                data=data.replaceAll("imageUrl",imageUrl+"/"+imageName)
                data=data.replaceAll("imageTag",imageTag)
                data=data.replaceAll("depName",depName)
                //data=data.replaceAll("artifactVersion",artifactVersion)
                println(data)
                writeFile(file: nFile, text: data)
                def resulting = sh(script: "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:initial -n " +namespace , returnStatus: true)
                if ("${resulting}" == 0){
                    sh "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:initial -n " +namespace
                    sh "kubectl apply -f " +nFile
                    sh "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:"+imageTag+ " -n " +namespace
                }else{
                    sh "kubectl apply -f " +nFile
                    sh "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:"+imageTag+ " -n " +namespace
                }
            }
        }
    }

7:maven结合sonar扫描

stage("mavne and sonar"){
        steps{
            withSonarQubeEnv('sonarqube-jenkins') {
                sh "mvn -U -s ${params.mavenXml} -f ${params.pomXml} clean compile -P dev -Dmaven.test.skip=true sonar:sonar -Dsonar.projectKey=${params.projectKey} -Dsonar.projectName=${params.projectKey} -Dsonar.sourceEncoding=UTF-8 -Dsonar.java.binaries=target/ -Dsonar.exclusions=src/test/** -Dsonar.sources=src/ -Dsonar.language=java    -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.isFortify=true"
            }
            script {
                timeout(1) {
                  sleep(5)
                  def qg = waitForQualityGate()
                  if (qg.status != 'OK') {
                      error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                  }
                }
            } 
        }
}

8:npm,maven,gradle,python四种编译结合,传参选择

stage("gradle or maven or npm or python"){
             steps{
                 script {
                     if("${params.compileType}" == "maven"){
                         def targetDir="${WORKSPACE}/${params.targetDir}";
                         dir(targetDir){
                             if("${params.needSonarScan}" == "no"){
                                 if ("${params.settingXml}" == "" && "${params.pomXml}" == "") {
                                        withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                     sh "mvn -U clean package -Dmaven.test.skip=true"
                                     }
                                 }else if("${params.settingXml}" != "" && "${params.pomXml}" == ""){
                                        withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                     sh "mvn -U -s ${params.settingXml} clean package -Dmaven.test.skip=true"
                                     }
                                 }else if("${params.settingXml}" == "" && "${params.pomXml}" != ""){
                                        withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                     sh "mvn -U -f ${params.pomXml} clean package -Dmaven.test.skip=true"
                                     }
                                 }else {
                                        withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                     sh "mvn -U -s ${params.settingXml} -f ${params.pomXml} clean package -Dmaven.test.skip=true"
                                     }
                                 }
                             }else{
                                 withSonarQubeEnv('sonarqube-jenkins') {
                                     if ("${params.settingXml}" == "" && "${params.pomXml}" == "") {
                                            withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                         sh "mvn -U clean package -Dmaven.test.skip=true sonar:sonar -Dsonar.projectKey=${params.moduleCode} -Dsonar.projectName=${params.moduleCode} -Dsonar.sourceEncoding=UTF-8 -Dsonar.java.binaries=target/ -Dsonar.exclusions=src/test/** -Dsonar.sources=src/ -Dsonar.language=java    -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.isFortify=true"
                                         }
                                     }else if("${params.settingXml}" != "" && "${params.pomXml}" == ""){
                                            withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                         sh "mvn -U -s ${params.settingXml} clean package -Dmaven.test.skip=true sonar:sonar -Dsonar.projectKey=${params.moduleCode} -Dsonar.projectName=${params.moduleCode} -Dsonar.sourceEncoding=UTF-8 -Dsonar.java.binaries=target/ -Dsonar.exclusions=src/test/** -Dsonar.sources=src/ -Dsonar.language=java    -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.isFortify=true"
                                         }
                                     }else if("${params.settingXml}" == "" && "${params.pomXml}" != ""){
                                            withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                         sh "mvn -U -f ${params.pomXml} clean package -Dmaven.test.skip=true sonar:sonar -Dsonar.projectKey=${params.moduleCode} -Dsonar.projectName=${params.moduleCode} -Dsonar.sourceEncoding=UTF-8 -Dsonar.java.binaries=target/ -Dsonar.exclusions=src/test/** -Dsonar.sources=src/ -Dsonar.language=java    -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.isFortify=true"
                                         }
                                     }else {
                                            withMaven(globalMavenSettingsConfig: "${env.mavenSettingsId}") {
                                         sh "mvn -U -s ${params.settingXml} -f ${params.pomXml} clean package -Dmaven.test.skip=true sonar:sonar -Dsonar.projectKey=${params.moduleCode} -Dsonar.projectName=${params.moduleCode} -Dsonar.sourceEncoding=UTF-8 -Dsonar.java.binaries=target/ -Dsonar.exclusions=src/test/** -Dsonar.sources=src/ -Dsonar.language=java    -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.isFortify=true"
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
                 script {
                     if("${params.compileType}" == "npm"){
                         def targetDir="${WORKSPACE}/${params.targetDir}";
                         dir(targetDir){
                             sh "npm install --unsafe-perm=true --allow-root --registry=https://registry.npm.taobao.org && npm run build:prod"
                         }
                     }
                 }
                 script {
                     if("${params.compileType}" == "gradle"){
                         def targetDir="${WORKSPACE}/${params.targetDir}";
                         dir(targetDir){
                             if("${params.needSonarScan}" == "no"){
                                 print "repoBranch : ${params.repoBranch}"
                                 sh "gradle clean build --parallel -x test"
                             }else{
                                 withSonarQubeEnv('sonarqube-jenkins') {
                                     print "repoBranch : ${params.repoBranch}"
                                     sh "gradle clean build --parallel -x test sonarqube -Dsonar.projectKey=${params.moduleCode} -Dsonar.projectName=${params.moduleCode} -Dsonar.sourceEncoding=UTF-8  -Dsonar.branch.name=${params.repoBranch} -Dsonar.analysis.buildNumber=${env.BUILD_NUMBER} -Dsonar.analysis.jobName=${env.JOB_NAME} -Dsonar.java.binaries=build/classes -Dsonar.analysis.isFortify=true"
                                 }
                             }
                         }

                     }
                 }
                 script {
                     if("${params.compileType}" == "python"){
                         def targetDir="${WORKSPACE}/${params.targetDir}";
                         dir(targetDir){
                             sh "pwd && ls -l"
                             sh "source /etc/profile && pyinstaller -F httpsrv.py"
                         }
                     }
                 }
                 script{
                     if("${params.needSonarScan}" == "yes"){
                         timeout(1) {
                             sleep(5)
                             def qg = waitForQualityGate()
                             if (qg.status != 'OK') {
                                 error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                             }
                         }
                     }
                 }
             }
         }

9:kubernetes部署最终版

stage("kubernetes yaml build"){
        steps{
            script{
                def ymlPath="${params.ymlPath}";
                def namespace="${params.namespace}";
                def appName="${params.appName}";
                def depName="${params.depName}";
                def appPort="${params.appPort}";
                def cmName="${params.cmName}";
                def svcName="${params.svcName}";
                def svcPort="${params.svcPort}";
                def imageName="${params.imageName}";
                def imageTag="${params.imageTag}";
                def envKubeconfig="${params.envKubeconfig}";
                def nFile="${WORKSPACE}/deployment-svc.yaml"
                def data = readFile(file: ymlPath)
              data=data.replaceAll("#namespace#",namespace)
              data=data.replaceAll("#appName#",appName)
              data=data.replaceAll("#depName#",depName)
              data=data.replaceAll("#appPort#",appPort)
              data=data.replaceAll("#cmName#",cmName)
              data=data.replaceAll("#svcName#",svcName)
              data=data.replaceAll("#svcPort#",svcPort)
              data=data.replaceAll("#imageName#",imageName)
              data=data.replaceAll("#imageTag#",imageTag)
                println(data)
                writeFile(file: nFile, text: data)
                def resulting = sh(script: "kubectl get deployment " +depName+ " -n " +namespace+ " --kubeconfig="+envKubeconfig , returnStatus: true)
                sh "echo ${resulting}"
                if ("${resulting}" == "0"){
                    sh "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:initial -n " +namespace+ " --kubeconfig="+envKubeconfig
                    sh "kubectl apply -f " +nFile+ " --kubeconfig="+envKubeconfig
                    sh "curl -X PUT -u user:passwd -T ${WORKSPACE}/deployment-svc.yaml  https://10.10.10.10:8081/artifactory/test-docker-dev/"+imageName+"/"+imageTag+"/"
                    sh "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:"+imageTag+ " -n " +namespace+ " --kubeconfig="+envKubeconfig
                }else{
                    sh "kubectl apply -f " +nFile+ " --kubeconfig="+envKubeconfig   #envKubeconfig是不同环境的config文件,可以对不同环境操作
                    sh "curl -X PUT -u user:passwd -T ${WORKSPACE}/deployment-svc.yaml  https://10.10.10.10:8081/artifactory/test-docker-dev/"+imageName+"/"+imageTag+"/"     #每次部署完,将yaml文件传入jfrog保存,方便下次根据yaml指定镜像名镜像标签回滚
                    sh "kubectl annotate deployment/"+depName+ " kubernetes.io/change-cause=image-tag:"+imageTag+ " -n " +namespace+ " --kubeconfig="+envKubeconfig     #每次部署完成,给deployment打上标签,方便回滚以及知道当前版本
                }
            }
        }
    }


10:K8S服务回滚和检测pod状态

stage("New kubernetes server building"){
    steps{
        script{
            def environment = "${params.environment}"
            def url = "${env.DEVOPS_URL}/rTaskRunRecord/getYamlAndRecordVariable"
            def jsonParamsHttp = [:];
            jsonParamsHttp.put("env", environment);
            jsonParamsHttp.put("taskId", "${env.JOB_NAME}");
            jsonParamsHttp.put("buildNumber", "${env.BUILD_NUMBER}");
            jsonParamsHttp.put("pipelineId", "${env.pipelineId}");
            def postResult = httpRequestStage(url,jsonParamsHttp);
            serYamlData = jsonParseText(postResult.result).data

            sh "touch ${WORKSPACE}/server.yaml;echo '@depData@' > ${WORKSPACE}/server.yaml"
            def depYmlPath="${WORKSPACE}/server.yaml"
            def nFile="${WORKSPACE}/deployment.yaml"
            def data = readFile(file: depYmlPath)
            data=data.replaceAll("@depData@",serYamlData)
            writeFile(file: nFile, text: data)
            sh "cat " +nFile


            def depName = null
            def namespace = null
            def yamlContent = serYamlData
            def yamlList = yamlConvertToList(yamlContent)
            if(null != yamlList){
                for(def map : yamlList){
                    def kind = map.get("kind")
                    if("Deployment" == kind){
                        def metadata = (Map)map.get("metadata")
                        depName = metadata.get("name")
                        namespace = metadata.get("namespace")
                        break
                    }
                }
            }
            println("==================================================")
            println("部署检测所需参数: depName=" + depName + ", namespace=" + namespace)
            println("==================================================")
            if(null == depName || null == namespace){
                error "解析部署包名与命名空间失败, 请检查yaml解析脚本"
            }


            sh "kubectl apply -f " +nFile+ " --validate=false --kubeconfig=/root/.kube/" +environment+ "-config"
            println("等待60s后, 开始检测服务")
            sleep 60



            def i=0;
            while(true) {
                if(i>=10){      
                    sh """
                        echo "服务启动失败,开始回滚........"
                        kubectl rollout undo deployment ${depName} -n ${namespace} \
                        --kubeconfig=/root/.kube/${environment}-config 
                        echo "服务回滚完成"
                        """
                    error "服务启动失败,完成回滚"
                }
                i++;
                def result = sh(script: "kubectl get pod -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config  |grep " +depName+"-[0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z]* |wc -l", returnStdout:true).trim()
                sh "echo ${result}"
                if("${result}" == "1"){
                    sleep 10
                    sh "kubectl get pod -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config |grep " +depName+ " > appname.txt"
                    def fileData = readFile(file: "appname.txt")
                    println fileData
                    def list =fileData.split(' ')
                    sh "kubedog  rollout track -n  "+namespace+"  pod " +list[0]+ " --kube-config=/root/.kube/"+environment+"-config && kubectl logs --since=4m " +list[0]+ " -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config  || kubectl  rollout undo deployment/"+depName+ " -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config"
                    break;
                }else{
                    sleep 10
                    sh "kubectl get pod -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config |grep " +depName
                    def result1 = sh(script: "kubectl get pod -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config |grep " +depName+"-[0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z][0-9/a-z]* |wc -l", returnStdout:true).trim()
                    sh "echo ${result1}"
                    if("${result1}" == "1"){
                        sh "kubectl get pod -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config |grep " +depName+ " > appname.txt"
                        def fileData = readFile(file: "appname.txt")
                        println fileData
                        def list =fileData.split(' ')
                        sh "kubedog  rollout track -n  "+namespace+"  pod " +list[0]+ " --kube-config=/root/.kube/"+environment+"-config && kubectl logs --since=4m " +list[0]+ " -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config ||  kubectl  rollout undo deployment/"+depName+ " -n  "+namespace+"  --kubeconfig=/root/.kube/"+environment+"-config"
                        break;
                    }else{
                        sh "echo '继续检测中.....'"
                    }
                }
            }
        }
    }
}

11:一键导入变量并引用yaml文件

stage("envsubst kubernetes yaml build"){
        steps{
            script{
              def replicas="${params.replicas}";
                sh "curl -O -u user:passwd https://10.10.10.10:8081/artifactory/test-docker-dev/yaml/dep-svc-sso-app.yaml"
                sh " envsubst < dep-svc-sso-app.yaml | kubectl apply -f -  --kubeconfig=/root/.kube/dev-config"
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容