[Nuxt 系列 07] 不停機更新:基於 Docker & Jenkins

設想一下這些場景:Nuxt 版本出現重大更新須要升級;新功能的開發須要添加新的生產環境依賴;線上版本出現 bug 急需快速回滾至上一版本;開發環境和生產環境依賴版本一致性的保持……java

咱們須要解決的不只僅是不停機更新,還要使開發環境和生產環境的版本保持強一致性,而且能夠輕鬆地追溯歷史版本,以及更新過程使用戶無感知等等。綜合以上特質,能夠選擇 docker 一試。node

1、Docker 簡介

Docker 是一個開源的應用容器引擎,基於 Go 語言並聽從 Apache2.0 協議開源。linux

它號稱本身是實如今任何地方安全構建、分享和運行現代應用的最快實現方式。它可讓咱們把應用及其依賴所有打包到一個容器中,從而輕鬆地實如今任何系統下極速遷移。就像其 logo 展現的那樣,一艘鯨魚樣子的大船載滿集裝箱飄蕩在海面上。各個 OS 就是海洋,docker 承載着一個個容器飄蕩在 OS 的海洋裏。nginx

docker1

以 Nuxt 應用爲例,若有須要,咱們能夠將其運行須要依賴的 Nginx 、 NodeJs 、應用自己及其依賴等通通打包到一塊兒。此時就算你拿到一個新的服務器,只要上面安裝了 docker,只須要幾個簡單的指令就可讓應用運行起來,而不須要再進行繁瑣地配置。git

具體的使用細節再也不絮叨了,茫茫多的 docker 官方文檔 正向你招手~菜鳥教程和 docker 中文社區也是系統學習的不錯選擇。web

慣例,拋出 docker cli 的經常使用指令:docker

# 基於當前文件夾下的 dockerfile 建立一個鏡像
docker build -t helloworld .      
# 上面指令的全寫
docker build --tag=helloworld .
# 運行這個鏡像,並將本機 4000 端口映射到容器對外暴露的 80 端口,外部經過 4000端口訪問 
docker run -p 4000:80 helloworld # 使 container 在後臺運行
docker run -d -p 4000:80 helloworld # 全部正在運行的容器列表
docker container ls                        
# 全部容器列表
docker container ls -a  
# 停用一個容器
docker container stop <hash>
# 強制中止一個容器
docker container kill <hash>
# 移除一個容器
docker container rm <hash>  
# 移除全部容器
docker container rm $(docker container ls -a -q)
# 全部鏡像列表
docker image ls -a 
# 移除一個鏡像
docker image rm <image id> 
# 移除全部鏡像
docker image rm $(docker image ls -a -q) 
# 登陸註冊過的 docker hub
docker login 
# 爲鏡像打標籤
docker tag <image> username/repository:tag
# 將鏡像推送至遠程倉庫
docker push username/repository:tag 
# 運行這個鏡像
docker run username/repository:tag 
複製代碼

2、基於 docker 的解決方案

此時,容器 (container) 即當前承載應用的進程,它擁有一個獨立的文件系統,裏面包含了應用所需的全部代碼、依賴和運行時等等。這被稱之爲鏡像 (image) 的存在,正是咱們須要在以後生產並保存起來的東西。shell

假如每一次版本發佈咱們都生成一個鏡像 (image),並基於項目和版本號爲其打上獨一無二的標籤,而後把它們保存到一塊兒,須要的時候隨時取用,依賴更新的問題天然而然獲得解決。而當須要發佈新版本的時候,咱們只須要提早拉新版本的鏡像到生產環境,而後移除舊版本鏡像正在運行的容器,同時執行新版本的容器,開啓新的 container,這樣就能夠實現無縫升級,也實現了某種意義上的不停機。npm

接下來,咱們要有一個存儲版本鏡像的倉庫。docker 本身推出了一個鏡像組織和託管平臺,docker hub,學習的時候能夠簡單地用它來熟悉整個操做流程。以後能夠搭建本身的私有倉庫來知足實際的業務需求。由於咱們實際業務中各類服務器都基於阿里雲產品,因此這裏也選擇阿里雲容器鏡像服務來管理鏡像。json

倉庫的問題解決了,咱們又但願在每次 push 一個 tag 的時候可以自動地生成一個鏡像並上傳至倉庫。咱們的項目代碼託管在私有 GitLab 上,它自己就集成了不錯的 CI/CD 功能,能夠做爲備選的目標之一。咱們這裏選擇使用 Jenkins (一個基於 java 的持續集成工具)並結合 GitLab 的 webhooks 來實現這一需求。

最終方案定爲 GitLab + Jenkins + Docker + 阿里雲鏡像服務,下面來看一下大致的實施步驟。

3、Centos 下的 Jenkins 安裝的大體步驟

  • 安裝 java -> yum install java

  • 添加 Jenkins 庫至 yum -> sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo

  • 導入公鑰 -> sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key

  • 安裝 -> sudo yum -y install jenkins

  • 查看 Jenkins 根目錄 -> cd /var/lib/jenkins

  • 配置 Jenkins 根權限:

    vim /etc/sysconfig/jenkins,修改或添加項 -> JENKINS_USER="root"JENKINS_GROUP="root",保存退出 執行 gpasswd -a root jenkins 這裏jenkins服務的默認端口爲 8080,可在配置文件中修改,保存後執行 service Jenkins restart 重啓服務

  • 啓動Jenkins -> service jenkins start。此時訪問http://<服務器公網ip>:8080開啓 Jenkins 界面

  • /var/lib/jenkins/secrets/initalAdminPassword 下獲取管理員密碼以開啓 Jenkins

  • 設置管理員帳號密碼後,便可開始使用 Jenkins

4、Centos下 Docker 安裝的大體步驟

Docker 社區版和企業版,這裏選擇安裝社區版本

  • 首先,Docker CE 官方要求 CentOs 版本爲 CentOs7,以前的系統版本雖然可能能夠經過升級系統內核來強行安裝,但我的感受存在比較明顯性能缺陷,未考證。
  • 刪除可能存在的老版本 Docker,如無,忽略:
$ sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest- logrotate docker-logrotate docker-engine

複製代碼
  • 若是首次安裝,配置 Docker 庫至 yum:
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
 $ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

複製代碼
  • 安裝 Docker CE -> $ sudo yum install docker-ce docker-ce-cli containerd.io
  • 開啓docker -> $ sudo systemctl start docker
  • 經過運行 hello-world 鏡像檢測是否成功安裝 Docker -> $ sudo docker run hello-world
  • nuxt-demo 爲例的 Dockerfile 配置文件,因爲 docker 自身擁有守護進程和健康檢查的功能,因此能夠考慮放棄 pm2:
FROM node:10.16.0 
  ENV HOST 0.0.0.0
  RUN mkdir -p /app   COPY . /app   WORKDIR /app   EXPOSE 3000
  RUN npm install   RUN npm run build   CMD ["npm", "start"] 
複製代碼

5、配置阿里雲鏡像倉庫

  1. 訪問阿里雲容器鏡像服務

  2. 登陸後,建立命名空間,建立鏡像倉庫,這裏以 psl_one 爲例

    因爲這裏經過 jenkins 來構建 docker 鏡像,因此再也不配置代碼源,選擇本地倉庫並建立 (阿里雲鏡像服務自己也提供了鏡像構建功能)

    aliregistry1
    aliregistry2

  3. 進入新建立的鏡像倉庫,能夠在鏡像版本中查看已上傳的鏡像列表,在基本信息中查看該倉庫的基本信息,其第三條說明將鏡像推送到Registry中有接下來要用到的指令:

 $ sudo docker login --username=[username] registry.cn-qingdao.aliyuncs.com
 $ sudo docker tag [ImageId] registry.cn-qingdao.aliyuncs.com/[命名空間]/hjxy_test:[鏡像版本號]
 $ sudo docker push registry.cn-qingdao.aliyuncs.com/[命名空間]/hjxy_test:[鏡像版本號]
複製代碼

6、爲 nuxt_demo 配置 Jenkins Job

  • 首先安裝一些必要的插件。進入系統管理->插件管理->可選插件,在過濾中搜索須要的插件

    • 在高級選項卡下能夠設置升級站點 http://mirror.xmission.com/jenkins/updates/update-center.json
    • Localization: Chinese (Simplified) 簡體中文語言包
    • SSH Plugin 經過ssh鏈接遠程服務器執行shell腳本
    • Git Parameter Plug-In參數化構建過程選項下得到git相關參數,如branch/tag等
    • Generic Webhook Trigger Plugin 構建觸發器插件,接收一個http請求,與webhook配合,觸發job執行構建
    • nvm-wrapper 提供一個nodejs的構建環境
    • Email Extension Plugin 設置郵件通知的插件
    • etc......
  • 進入系統管理 -> 系統設置,進行 ssh 配置

    ssh.png

  • 進入系統管理 -> 管理用戶,查看|生成用戶 id 和用戶 token

  • 進入系統管理 -> 系統設置,配置郵件通知,這裏以 qq 郵箱爲例

    • 首先在 qq 郵箱中開啓 SMTP,開啓後會生成一個登陸受權碼,記下備用。而後進行 jenkins location 和郵件通知設置
    • location
    • email
    • 測試可否成功發送郵件
  • 點擊新建任務,選擇建立一個自由風格的項目,爲 nuxt_demo 建立一個鏡像構建、上傳並部署的 jenkins job。約定這個任務只用於當開發者向倉庫提交 tag 時觸發構建,不能進行主動構建(就算主動構建,由於缺乏 tag 信息也會失敗)

    • newjob
    • 常規選項卡中,添加該任務的必要描述信息
    • 配置源碼管理信息:
    • codesource

    • 配置構建觸發器,選擇 Generic Webhook Trigger
    • postparam

    • filter

    • 因爲 GitLab 的 webhook 傳遞來的 ref 信息形如 refs/tags/v1.0.0,因此須要在執行腳本中進行字符串拆分,獲取須要的部分: $ref|cut -c11-

    • 構建環境中選擇 Execute shell script on remote host using ssh
    • ssh site 即剛纔在系統設置中配置的ssh ,pre build script 能夠置空或根據須要填寫,這裏主要配置 post build script,其中 ***-deploy.sh 爲遠程服務器中的部署腳本,將在構建任務結束後觸發執行

    • executeshell

    • 構建中選擇執行 shell 腳本,這裏用到了在阿里雲鏡像倉庫基本信息中的操做指令,構建好的鏡像將被推送至鏡像倉庫
    • buildshell

    • 點擊保存,構建任務配置完成

7、設置GitLab webhook

  1. 代碼倉庫右上角(視具體版本而定)

webhook

  1. 配置 webhook

gitwebhook2
這裏只選擇 tag push events,點擊 add webhook 添加鉤子,並點擊 test按鈕測試是否可用
gitwebhook3
若成功,將觸發相應的 jenkins job 執行

8、服務器端配置

一、shell腳本

/var/myprojects/shell_script/***-deploy.sh

 #!/bin/bash
  echo "deploy start"

  preImageId=$(docker inspect -f {{.Image}} <Container Name>) # 獲取當前運行容器的鏡像Id,稍後用以刪除鏡像

  docker login -u <username> -p <password> <Docker Registry>    # 登陸遠程鏡像倉庫
  docker <Image>:$1            # 拉取最新構建的鏡像

  docker rm -f <Container Name> || true                                                     # 強制刪除當前運行的容器
  docker run -d --name <Container Name> -p 3000:3001 --restart=always <Image>:$1   # 使用最新拉取的鏡像開啓新的容器
  docker rm -f <Container Name2> || true                                                     # 強制刪除當前運行的容器
  docker run -d --name <Container Name2> -p 3001:3001 --restart=always <Image>:$1   # 使用最新拉取的鏡像開啓新的容器
  
  docker image rm -f ${preImageId}        # 移除上一個版本的鏡像

  echo "deploy end"

複製代碼

二、nginx配置文件(單機負載)

upstream nuxt_demo {
       server localhost:3000 max_fails=1 fail_timeout=15s weight=1;
       server localhost:3001 max_fails=1 fail_timeout=15s weight=1;
    }

    server {
        listen      80;
        server_name test.xyz.docker.com;

        location / {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
            proxy_set_header X-Nginx-Proxy true;
            proxy_cache_bypass $http_upgrade;
            proxy_pass http://nuxt_demo;
        }
    }

複製代碼

9、爲發佈和回滾操做各自建立單獨的 job

參考第六節中的部分,但再也不配置構建步驟。 在general選項卡中勾選參數化構建過程選項,並配置以下參數:

buildwithgitparams
此時,側邊欄的 當即構建將變成 build with parameters
buildwithparams2
點擊 build with parameters 將看到以下界面。releasetag 默認值爲配置時填寫的 notag,此時可參照 taglist 填寫正確的值執行構建操做:
startbuild
最終的任務列表大體以下:
jobls

10、最後

以上,提供一種可行思路的大體操做步驟,如要在生產環境實行還需細細考慮具體的場景和更多的細節問題~

相關文章
相關標籤/搜索