14. 代碼部署流程與案例

1 代碼部署流程

1.1 代碼克隆

公司的代碼都是放到gitlab倉庫, 代碼的克隆會由Jenkins的項目構(gòu)建配置去觸發(fā). 運維人員需要在Jenkins中創(chuàng)建項目, 并且配置構(gòu)建的步驟. 之后點擊構(gòu)建, 觸發(fā)代碼的克隆以及后續(xù)階段

代碼克隆階段需要配置的內(nèi)容

1. Jenkins服務器上的Jenkins用戶和Gitlab服務器的免秘鑰認證: Jenkins是以jenkins用戶運行, 因此, 需要在Jenkins服務器生成jenkins用的秘鑰對, 把公鑰添加到Gitlab的root賬號下,防止其他用戶誤刪除
2. 在Jenkins上創(chuàng)建項目, 填寫項目的gitlab代碼地址, 添加jenkins用戶的私鑰信息

配置好這些信息后, Jenkins就可以從gitlab拉取代碼到本地的/var/lib/jenkins/workspace目錄下

圖片.png

1.2 源代碼測試

代碼測試可以使用商業(yè)的軟件, 也可以使用開源的Sonarqube. 需要部署單獨Sonarqube服務器并且在Jenkins服務器上安裝Sonar-Scanner客戶端, 讓Jenkins拉取代碼后, 在本地進行代碼掃描, 之后上傳到Sonarqube服務器進行分析

代碼掃描一定是在編譯前進行

代碼測試階段需要配置的內(nèi)容

1. 部署Sonarqube Server和PgSQL
2. 在PgSQL創(chuàng)建數(shù)據(jù)庫, 配置Sonarqube和PgSQL的連接
3. 配置SonarScanner和服務器的連接
4. 配置項目構(gòu)建使用Sonarqube進行掃描. 如果是使用腳本, 那么只需在構(gòu)建中使用Shell腳本, 同時這樣也可以把代碼克隆到自定義的Jenkins服務器目錄下, 之后在項目目錄下, 創(chuàng)建properties文件. 并且在項目目錄下執(zhí)行scanner客戶端命令
5. 如果是使用Jenkins的Scanner插件, 那么需要安裝插件, 配置Sonarqube服務器地址, 以及本地的Scanner客戶端命令的路徑, 包括編輯properties文件
6. 執(zhí)行構(gòu)建即可完成代碼克隆和掃描
圖片.png

1.3 代碼編譯

對于Java這類編譯性語言, 一旦源代碼掃描沒有問題, 就會執(zhí)行編譯, 如果代碼有問題, 那么就不會繼續(xù)構(gòu)建. 如果是html, php這些語言, 那么編譯步驟是不用的

Java代碼編譯可以在單獨的Maven服務器進行執(zhí)行, 并且可以搭建本地的倉庫, 去緩存編譯的依賴包, 如Nexus

圖片.png

1.4 代碼拷貝

代碼編譯完, 需要把生成的jar|war包拷貝到后端的web服務器, 可以基于scp, rsync或者ansible

這一步需要配置Jenkins和后端服務器的免秘鑰認證, 將jenkins用戶的公鑰拷貝到web服務器的運行用戶的賬號. 如果web服務是tomcat用戶啟動, 那么就是ssh-copy-id tomcat@xxxx

圖片.png

1.5 將web服務器從負載均衡器下線

做代碼替換需要先把服務器從負載均衡器下線, 如果在沒有下線前, 直接停止服務, 那么客戶端訪問會立刻報錯. 因此, 要先把web服務器從負載均衡器下線, 然后停止服務, 再做代碼替換. web服務都是會做Session共享的, 因此, 即使用戶正在訪問的服務器下線了, 下次再訪問被調(diào)度到其他的服務器, Session信息也不會丟失

HAProxy可以通過socket文件, 進行動態(tài)的上線下
LVS需要使用lvsadmin命令或者在Keepalived中注釋掉被下線的服務器
Nginx需要修改配置文件, 然后reload服務

圖片.png

1.6 停止web服務

服務器從負載均衡下線后, 要停止服務, 否則某些文件正在被占用時, 執(zhí)行代碼替換會出現(xiàn)錯誤

圖片.png

1.7 代碼替換

代碼替換有多種方案, 可以把壓縮包存放到統(tǒng)一的路徑, 把解壓后的目錄存放到統(tǒng)一路經(jīng), 然后把用戶訪問的路徑軟連接到代碼目錄. 之后代碼升級或者降級只需要刪除和重新創(chuàng)建軟連接即可

對于舊版本的代碼, 也可以暫時保留上一個版本, 一旦升級失敗, 可以直接把上一個版本的代碼拷貝到目標目錄下, 修改軟連接, 即可完成回滾

圖片.png

1.8 啟動服務

代碼替換后, 進行服務啟動

圖片.png

1.9 服務測試

在把服務器重新添加到負載均衡前, 要先進行服務測試, 因為, 代碼掃描只能掃描語法和bug等信息. 但是具體的功能, 還需要部署后才能進行測試. 因此, 在上線前, 要進行功能測試, 或者至少訪問以下監(jiān)控的url. 確保服務啟動, 且運行正常. 如果發(fā)現(xiàn)服務啟動失敗, 或者功能缺失, 可以在這一步進行回滾

圖片.png

1.10 將服務器添加到負載均衡器

添加到負載均衡器后, 用戶就可以正常訪問了

圖片.png
圖片.png

2 代碼部署策略

2.1 灰度/金絲雀部署

對于灰度部署, 可以先只升級一部分服務器, 讓剩余的服務器正常運行在舊版本下. 新版本服務器升級測試沒問題后, 上線到負載均衡器, 讓用戶訪問一段時間, 確定沒問題, 再繼續(xù)升級后續(xù)的服務器

2.2 藍綠部署

藍綠部署要求有兩套相同的環(huán)境, 每次升級只升級一套, 當運行穩(wěn)定后, 再把另一套升級到相同的版本

2.3 滾動部署

每次都升級一個或者幾個服務器, 并且連續(xù)的把所有的服務器都升級完.

具體的部署流程和策略, 還需要根據(jù)具體情況和業(yè)務, 進行修改

3 Jenkins參數(shù)化構(gòu)建

3.1 創(chuàng)建一個項目

圖片.png
圖片.png

3.2 配置構(gòu)建

  • 丟棄舊的構(gòu)建

只保留7天內(nèi)的5次構(gòu)建

  • 參數(shù)化構(gòu)建過程

字符參數(shù): 用于指定部署的是哪個分支, 也就是代碼部署到哪個環(huán)境
選項參數(shù): 用于選擇此次部署的內(nèi)容, 比如可以定義某個選項為執(zhí)行灰度部署, 部署后, 測試沒問題, 再選擇部署剩余的服務器

選項參數(shù)案例:

圖片.png
圖片.png

Jenkins服務器創(chuàng)建腳本, 測試$1返回值

jenkins@jenkins:/data/jenkins$ vim tomcat-1.sh

#!/bin/bash
echo $1

jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh GROUP
GROUP
jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh GROUP1
GROUP1
jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh GROUP2
GROUP2

圖片.png

在執(zhí)行Shell構(gòu)建時, 腳本后要接$GROUP. 之后執(zhí)行構(gòu)建時, 可以在Jenkins頁面選擇GROUP1或者GROUP2, 如果執(zhí)行GROUP1, 那么GROUP1就會作為參數(shù), 傳給GROUP變量, 最終傳給腳本中的$1. 只需要在腳本中判斷$1的值, 就可以判斷此次是灰度發(fā)布, 還是發(fā)布剩余的服務器

  • 此時, 執(zhí)行構(gòu)建時, 是沒有立即構(gòu)建選項的, 而是要使用Build with Parameters
圖片.png
  • 構(gòu)建時, 可以根據(jù)不同的選項進行構(gòu)建
圖片.png

Group1

圖片.png
圖片.png

Group2

圖片.png
圖片.png

字符參數(shù)案例:

一般用于指定部署到不同的環(huán)境, 比如生產(chǎn), 開發(fā)或者測試環(huán)境

  • 字符參數(shù)創(chuàng)建
圖片.png
jenkins@jenkins:/data/jenkins$ vim tomcat-1.sh 

#!/bin/bash
BRANCH=$1
GROUP=$2

echo "部署的分支是:${BRANCH}"
echo "部署的服務器是:${GROUP}"
~                             
jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh master GROUP1
部署的分支是:master # master就表示部署的是master分支的代碼, 而master分支的代碼是部署到生產(chǎn)環(huán)境的
部署的服務器是:GROUP1
圖片.png
圖片.png
圖片.png
  • 在構(gòu)建腳本中, 定義克隆的分支
#!/bin/bash
BRANCH=$1
GROUP=$2


cd /data/jenkins/qq && rm -rf web-02

git clone -b ${BRANCH} git@10.0.0.239:qq/web-02.git

echo "部署的分支是:${BRANCH}"
#echo "部署的服務器是:${GROUP}"

  • 測試部署克隆
圖片.png
jenkins@jenkins:/data/jenkins/qq/web-02$ git status
On branch develop
Your branch is up to date with 'origin/develop'.

nothing to commit, working tree clean
jenkins@jenkins:/data/jenkins/qq/web-02$ git branch
* develop

3.3 通過選項參數(shù), 控制部署的服務器組

jenkins@jenkins:/data/jenkins$ vim tomcat-1.sh 

#!/bin/bash
BRANCH=$1
GROUP=$2


if [ ${GROUP} == GROUP1 ]; then
    IP_LIST="10.0.0.199"
elif [ ${GROUP} == GROUP2 ]; then
    IP_LIST="10.0.0.209 10.0.0.219"
fi

echo "${GROUP} ----> ${IP_LIST}"

#cd /data/jenkins/qq && rm -rf web-02

#git clone -b ${BRANCH} git@10.0.0.239:qq/web-02.git

#echo "部署的分支是:${BRANCH}"
#echo "部署的服務器是:${GROUP}"

jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh master GROUP1
GROUP1 ----> 10.0.0.199
圖片.png
#測試關(guān)閉指定服務器組的tomcat服務

jenkins@jenkins:/data/jenkins$ vim tomcat-1.sh 

#!/bin/bash
BRANCH=$1
GROUP=$2


if [ ${GROUP} == GROUP1 ]; then
    IP_LIST="10.0.0.199"
elif [ ${GROUP} == GROUP2 ]; then
    IP_LIST="10.0.0.209 10.0.0.219"
fi

for node in ${IP_LIST}; do
    ssh tomcat@${node} "/etc/init.d/tomcat.sh stop"
done

#echo "${GROUP} ----> ${IP_LIST}"

#cd /data/jenkins/qq && rm -rf web-02

#git clone -b ${BRANCH} git@10.0.0.239:qq/web-02.git

#echo "部署的分支是:${BRANCH}"
#echo "部署的服務器是:${GROUP}"

jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh master GROUP1
正在判斷Tomcat運行狀態(tài), 倒計時3秒
3
2
1
Tomcat正在運行, 進程ID為5167, 共1個進程
三秒后, 準備關(guān)閉Tomcat
3
2
1
已嘗試關(guān)閉Tomcat, 30秒后檢查是否關(guān)閉成功!
3
2
1
Tomcat關(guān)閉成功!

驗證10.0.0.199的tomcat服務關(guān)閉成功

jenkins@jenkins:/data/jenkins$ curl 10.0.0.199:8080/myapp/
curl: (7) Failed to connect to 10.0.0.199 port 8080: Connection refused
驗證10.0.0.199的tomcat服務啟動成功

jenkins@jenkins:/data/jenkins$ vim tomcat-1.sh 

#!/bin/bash
BRANCH=$1
GROUP=$2


if [ ${GROUP} == GROUP1 ]; then
    IP_LIST="10.0.0.199"
elif [ ${GROUP} == GROUP2 ]; then
    IP_LIST="10.0.0.209 10.0.0.219"
fi

for node in ${IP_LIST}; do
    ssh tomcat@${node} "/etc/init.d/tomcat.sh start"
done

#echo "${GROUP} ----> ${IP_LIST}"

#cd /data/jenkins/qq && rm -rf web-02

#git clone -b ${BRANCH} git@10.0.0.239:qq/web-02.git

#echo "部署的分支是:${BRANCH}"
#echo "部署的服務器是:${GROUP}"

jenkins@jenkins:/data/jenkins$ bash tomcat-1.sh master GROUP1
正在判斷服務狀態(tài), 請稍等!
倒計時三秒鐘!
3
2
1
Tomcat沒有運行, 三秒后準備啟動!
3
2
1
Tomcat started.
Tomcat啟動已經(jīng)執(zhí)行, 五秒后判斷是否啟動成功!
5
4
3
2
1
Tomcat啟動成功! 共1個java進程, 其PID為5660

  • 到此, 確保了可以通過構(gòu)建腳本, 對于指定的服務器, 通過選項和字符參數(shù)進行服務的停止和啟動, 以及根據(jù)分支, 進行代碼克隆

4 灰度部署案例-靜態(tài)頁面

  • 實現(xiàn)qq/web-02項目的灰度發(fā)布, 先升級tomcat-1
三臺tomcat服務器
10.0.0.199
10.0.0.209
10.0.0.219
灰度服務器: 10.0.0.199
其余服務器: 10.0.0.209, 10.0.0.219

4.1 準備工作

    1. 把jenkins上的項目以及workspace目錄清空
    1. 準備sonar-project.properties文件, 放到web-02項目, 和代碼一起提交到Gitlab
sonar.projectKey=sonarqube-qq-web-02-projectKey
sonar.projectName=sonarqube-qq-web-02-projectName
sonar.projectVersion=1.0

# Jenkins克隆代碼到/var/lib/jenkins/workspace/目錄, 該文件會在web-02目錄下, sonar-scanner會掃描web-02中的所有文件

sonar.sources=./  
sonar.language=html
sonar.sourceEncoding=UTF-8

    1. Jenkins服務器以及tomcat服務器安裝zip和unzip命令
root@jenkins:~# apt -y install zip unzip
root@tomcat-1:~# apt -y install zip unzip
root@tomcat-2:~# apt -y install zip unzip
root@tomcat-3:~# apt -y install zip unzip
    1. haproxy安裝socat
root@ha-1:~# apt -y install socat
root@ha-2:~# apt -y install socat
    1. 將Jenkins服務器jenkins賬號的公鑰, 傳給haproxy服務器的root賬號, 做免秘鑰認證.
      jenkins是以jenkins用戶連接到haproxy, 而haproxy用root執(zhí)行服務器上下線
jenkins@jenkins:~$ ssh-copy-id root@10.0.0.179
jenkins@jenkins:~$ ssh-copy-id root@10.0.0.189

4.2 修改構(gòu)建配置

  • 選項參數(shù)GROUP, 用來控制部署/回滾的服務器
  • 選項參數(shù)METHOD, 表示此次部署是升級還是回滾
圖片.png
  • 選項參數(shù)BRANCH, 表示此次部署的是哪個分支
圖片.png
  • 構(gòu)建腳本
圖片.png
#!/bin/bash
DATE=`date +%Y-%m-%d_%H-%M-%S`
# METHOD變量用來指定本次執(zhí)行是升級代碼還是回滾代碼
METHOD=$1
# BRANCH變量用來指定本次執(zhí)行部署或者回滾的分支
BRANCH=$2
# 服務器分組
# GROUP1灰度環(huán)境服務器,10.0.0.199
# GROUP2灰度以外的服務器, 10.0.0.209,10.0.0.219
# GROUP3一次新部署所有服務器, 用于緊急的服務升級, 比如需要執(zhí)行緊急的bug修復
GROUP_LIST=$3

ip_list(){
  if [[ ${GROUP_LIST} == "GROUP1" ]];then
     Server_IP="10.0.0.199"
     
     echo "灰度服務器地址為: ${Server_IP}"
     

  elif [[ ${GROUP_LIST} == "GROUP2" ]];then
     Server_IP="10.0.0.209 10.0.0.219"
     
     echo "剩余服務器地址為: ${Server_IP}"
     

  elif [[ ${GROUP_LIST} == "GROUP3" ]];then
     Server_IP="10.0.0.199 10.0.0.209 10.0.0.219"
     
     echo "所有服務器地址: ${Server_IP}"
     
  fi
}

clone_code(){
  
  echo "即將開始克隆 ${BRANCH} 分支的代碼"
  
  cd /var/lib/jenkins/workspace && rm -rf web-02 && git clone -b  ${BRANCH} git@10.0.0.239:qq/web-02.git
  
  echo "${BRANCH}分支代碼clone完成核无,準備開始代碼掃描"
  
}

scanner_code(){
  cd /var/lib/jenkins/workspace/web-02 && /apps/sonar-scanner/bin/sonar-scanner 
  
  echo "代碼掃描完成,請打開sonarqube查看掃描結(jié)果"
  
}

code_maven(){
  echo  "cd /var/lib/jenkins/workspace/web-02 && mvn clean package -Dmaven.test.skip=true"
  
  echo "代碼編譯完成"
  
}


make_zip(){
  cd /var/lib/jenkins/workspace/web-02 && zip -r web-02.zip ./
  
  echo "代碼打包完成"
  
}



down_node(){
  for node in ${Server_IP};do
    ssh root@10.0.0.179 "echo "disable server  tomcat/${node}" | socat stdio /var/lib/haproxy/admin.sock"
    
    echo "${node} 從負載均衡10.0.0.179下線成功"
    
    ssh root@10.0.0.189 "echo "disable server  tomcat/${node}" | socat stdio /var/lib/haproxy/admin.sock"
    
    echo "${node} 從負載均衡10.0.0.189下線成功"
    
  done
}

scp_zipfile(){
  for node in ${Server_IP};do
    scp /var/lib/jenkins/workspace/web-02/web-02.zip  tomcat@${node}:/data/tomcat/tomcat_zip/web-02-${DATE}.zip
    ssh tomcat@${node} "unzip /data/tomcat/tomcat_zip/web-02-${DATE}.zip  -d /data/tomcat/tomcat_file/web-02-${DATE} && rm -rf  /data/tomcat/tomcat_webapps/myapp && ln -sv  /data/tomcat/tomcat_file/web-02-${DATE} /data/tomcat/tomcat_webapps/myapp"
  done
}

stop_tomcat(){
  for node in ${Server_IP};do
    ssh tomcat@${node}   "/etc/init.d/tomcat.sh stop"
  done
}

start_tomcat(){
  for node in ${Server_IP};do
    ssh tomcat@${node}   "/etc/init.d/tomcat.sh start"

  done
}

web_test(){

  for node in ${Server_IP};do
    NUM=`curl -s  -I -m 10 -o /dev/null  -w %{http_code}  http://${node}:8080/myapp/index.html`
    if [[ ${NUM} -eq 200 ]];then
       
       echo "${node} 測試通過,即將添加到負載"
       
       add_node ${node}
    else
       
       echo "${node} 測試失敗,請檢查該服務器是否成功啟動tomcat"
       
    fi
  done
}

add_node(){
  node=$1
  
  echo "準備添加${node}到負載均衡"
  

  if [ ${node} == "10.0.0.199" ];then
    ssh root@10.0.0.179 ""echo enable  server tomcat/${node}" | socat stdio /var/lib/haproxy/admin.sock"
    ssh root@10.0.0.189 ""echo enable  server tomcat/${node}" | socat stdio /var/lib/haproxy/admin.sock"
    
    echo "灰度部署環(huán)境服務器-->10.0.0.199 部署完畢,請進行代碼測試!"
    
  else

      ssh root@10.0.0.179 ""echo enable  server tomcat/${node}" | socat stdio /var/lib/haproxy/admin.sock"
      ssh root@10.0.0.189 ""echo enable  server tomcat/${node}" | socat stdio /var/lib/haproxy/admin.sock"


  fi
}

rollback_last_version(){
  for node in ${Server_IP};do
    
    echo $node
    
    CURRENT_VERSION=`ssh tomcat@${node} ""/bin/ls -l  -rt /data/tomcat/tomcat_webapps/ | awk -F"->" '{print $2}'  | tail -n1""`
    CURRENT_VERSION=`basename ${CURRENT_VERSION}`
    
    echo "當前版本為: $CURRENT_VERSION"
    
    LAST_VERSION=`ssh  tomcat@${node}  ""ls  -l  -rt  /data/tomcat/tomcat_file/ | grep -B 1 ${CURRENT_VERSION} | head -n1 | awk '{print $9}'""`
    
    echo "上一個版本為: $LAST_VERSION"
    
    ssh tomcat@${node} "rm -rf /data/tomcat/tomcat_webapps/myapp && ln -sv  /data/tomcat/tomcat_file/${LAST_VERSION} /data/tomcat/tomcat_webapps/myapp"
  done 
}

delete_last_version(){
  for node in ${Server_IP};do
    ssh tomcat@${node} "rm -rf /data/tomcat/tomcat_zip/*"
    NUM=`ssh tomcat@${node}  ""/bin/ls -l -d -rt /data/tomcat/tomcat_file/web-02-* | wc -l""`
    
    echo "當前歷史構(gòu)建數(shù)量為: $NUM"
    
    if [ ${NUM} -gt 5 ];then
      # 只保留最近5個版本, 超過5個, 那么下次部署完, 就刪除當前最舊的一個版本
      NAME=`ssh tomcat@${node} ""/bin/ls -l -d   -rt /data/tomcat/tomcat_file/web-02-* | head -n1 | awk '{print $9}'""`
      ssh tomcat@${node} "rm -rf ${NAME}"
      
      echo "${node} 刪除歷史版本 /data/tomcat/tomcat_file/${NAME}成功!"
      
    fi
  done 
}

main(){
   case $1  in
      deploy)
        ip_list;        
        clone_code;
        scanner_code;
        #code_maven;
        make_zip;
        down_node;
        stop_tomcat;
        scp_zipfile;
        start_tomcat;
        web_test;
        delete_last_version;
         ;;
      rollback)
        ip_list;
        #echo ${Server_IP}
        down_node;
        stop_tomcat;
        rollback_last_version;
        start_tomcat;
        web_test;
         ;;
    esac
}

main $1 $2 $3

4.3 部署測試

圖片.png
  • 先部署灰度服務器, GROUP1, 測試沒問題后, 部署GROUP2
  • 如果發(fā)現(xiàn)全部升級后, 出現(xiàn)bug, 可以直接rollback GROUP3
  • 如果是部署開發(fā)/測試環(huán)境, 那么還需要做額外的判斷, 添加開發(fā)/測試環(huán)境的服務器, 在Jenkins上創(chuàng)建單獨的目錄存放測試環(huán)境的代碼, 然后傳給測試環(huán)境服務器.
  • 整個灰度升級過程是分多個階段的, 首先代碼克隆后, 要進行代碼質(zhì)量檢測, 檢測通過后, 會進行灰度服務器的代碼拷貝, 并且把灰度服務器在負載均衡器上線, 此時, 可以給灰度服務器專門配置一個vip, 在haproxy添加一個灰度服務器的監(jiān)聽組, 讓開發(fā)測試人員, 修改本地hosts文件, 把網(wǎng)站的域名, 解析到灰度服務器的vip, 之后進行測試, 測試沒問題, 再把灰度服務器上線到生成環(huán)境的vip組上. 之后再升級剩余的服務器
  • 如果想基于指定版本進行升級或者回滾, 可以基于tag號標簽, 或者再給構(gòu)建腳本傳一個參數(shù), 先確定要回滾到的指定的目錄名, 如,web-02-2021_xx_xx-xx-xx-xx, 之后把這個目錄名, 作為參數(shù), 傳給構(gòu)建腳本, 修改軟鏈接, 直接指向這個指定的版本
listen tomcat 
    bind 10.0.0.188:80
    mode http
    log global
    server 10.0.0.199 10.0.0.199:8080 check inter 3000 fall 2 rise 5
    server 10.0.0.209 10.0.0.209:8080 check inter 3000 fall 2 rise 5
    server 10.0.0.219 10.0.0.219:8080 check inter 3000 fall 2 rise 5


listen canary                                                                                                                                                                
    bind 10.0.0.189:80
    mode http
    log global
    server 10.0.0.199 10.0.0.199:8080 check inter 3000 fall 2 rise 5
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    garp_master_delay 10
    smtp_alert
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        10.0.0.188 label eth0:0 dev eth0
        10.0.0.189 label eth0:1 dev eth0          # 189這個vip專門給內(nèi)網(wǎng)用戶使用, 并不會在防火墻做解析, 因此, 公網(wǎng)用戶是訪問不到的                                                                                                                           
    }
}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姨蝴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痰驱,老刑警劉巖坑傅,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件觅玻,死亡現(xiàn)場離奇詭異垢箕,居然都是意外死亡,警方通過查閱死者的電腦和手機呢撞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門损姜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人殊霞,你說我怎么就攤上這事摧阅。” “怎么了绷蹲?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵棒卷,是天一觀的道長。 經(jīng)常有香客問我祝钢,道長比规,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任拦英,我火速辦了婚禮蜒什,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疤估。我一直安慰自己吃谣,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布做裙。 她就那樣靜靜地躺著,像睡著了一般肃晚。 火紅的嫁衣襯著肌膚如雪锚贱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天关串,我揣著相機與錄音拧廊,去河邊找鬼。 笑死晋修,一個胖子當著我的面吹牛吧碾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播墓卦,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼倦春,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起睁本,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤尿庐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后呢堰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抄瑟,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年枉疼,在試婚紗的時候發(fā)現(xiàn)自己被綠了皮假。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡骂维,死狀恐怖惹资,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情席舍,我是刑警寧澤布轿,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站来颤,受9級特大地震影響汰扭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜福铅,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一萝毛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧滑黔,春花似錦笆包、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汛兜,卻和暖如春巴粪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粥谬。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工肛根, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人漏策。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓派哲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掺喻。 傳聞我的和親對象是個殘疾皇子芭届,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

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

  • 4 常見代碼部署方式 基礎(chǔ)工作: 4.1 藍綠部署 藍綠部署指的是不停老版本代碼(不影響上一個版本訪問)储矩,而是在另...
    隨便寫寫咯閱讀 1,339評論 0 6
  • 【 ①Java代碼自動部署-總結(jié)簡介】 代碼部署是每一個軟件開發(fā)項目組都會有的一個流程,也是從開發(fā)環(huán)節(jié)到發(fā)布功能必...
    程序員日常填坑閱讀 606評論 0 1
  • jenkins要想在遠程服務器執(zhí)行命令喉脖,發(fā)送文件等操作椰苟,必然要處理jenkins與遠程機的免交互問題。下面如何讓j...
    阿當運維閱讀 1,248評論 0 9
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月树叽,有人笑有人哭舆蝴,有人歡樂有人憂愁,有人驚喜有人失落题诵,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,531評論 28 53
  • 信任包括信任自己和信任他人 很多時候洁仗,很多事情,失敗性锭、遺憾赠潦、錯過,源于不自信草冈,不信任他人 覺得自己做不成她奥,別人做不...
    吳氵晃閱讀 6,187評論 4 8