在軟件開發過程當中,若是咱們每一次提交的代碼都可以進行一次完整的編譯、測試、打包、發佈,就能及早發現問題、及早修復,在保證代碼質量的同時讓產品快速迭代。這就是持續集成(CI)、持續部署(CD)的好處。java
目前 CI/CD 的方案有不少,本文將展現一個用 Docker + Jenkins 實現的完整過程。node
開發人員提交代碼到本身的分支並 push 到遠程倉庫 ==>
觸發遠程倉庫(GitHub/GitLab)的 Webhooks ==>
Jenkins 接到通知自動執行以前準備好的一個流程(克隆代碼,對代碼進行編譯、測試、打包,沒有問題後會執行 docker 命令進行鏡像構建)==>
最終發佈到測試服務器中。react
無需本地安裝 docker
,固然在本地操做也是能夠的。GitHub
公有倉庫,若是是私有倉庫或 GitLab,步驟會略有不一樣。Jenkins
也是用的 docker 版,並非直接安裝在宿主機上的。要演示整個過程,就得有一個應用,這裏咱們用一個 create-react-app
爲例,無需 IDE,一個 terminal 便可搞定。linux
四、5 行是增長了一個設置,是關掉 webpack 的 host 檢查,若是不加此項,訪問綁定域名的服務器就會被 webpack-dev-server 攔截。
$ npm install -g create-react-app $ create-react-app my-app $ cd my-app $ touch .env $ echo DANGEROUSLY_DISABLE_HOST_CHECK=true > .env $ touch Dockerfile
爲了簡單,咱們這裏直接採用
npm start
的方式啓動它,就不 build 了,安裝 cnpm 是爲了提升依賴的下載速度
FROM node:8.11.1-slim WORKDIR /home/app COPY . ${WORKDIR} RUN npm install -g cnpm --registry=https://registry.npm.taobao.org \ && cnpm install EXPOSE 3000 ENTRYPOINT [ "npm", "start" ]
若是沒有 Jenkins,就上面那個例子,咱們想要將本身的代碼集成而且部署到服務器,可能
要經歷如下步驟:webpack
$ cd repository $ docker build -t test . $ docker run -d -p 80:3000 --name my-react test
能夠看到上面那個過程須要人工操做,很是繁瑣,這還沒算上對代碼進行測試,若是每次提交了代碼都要來一個這樣的過程,那是真的無法專心搞開發了。git
若是用了 Jenkins,上面的整個過程均可以自動化完成。web
Jenkins 的官網是 jenkins.io
,它有不少種安裝方式,例以下載 war 包到宿主機上,而後用 java -jar jenkins.war
命令啓動。可是這種安裝方式很是不利於管理和服務器的遷移,徹底是在給 docker 託後腿。因此我選擇用 docker 版的 jenkins。docker
使用 docker 版的 jenkins 是須要注意不少細節的shell
$ vi Dockerfile
FROM jenkins/jenkins:lts USER root RUN echo deb http://mirrors.aliyun.com/debian wheezy main contrib non-free \ deb-src http://mirrors.aliyun.com/debian wheezy main contrib non-free \ deb http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free \ deb-src http://mirrors.aliyun.com/debian wheezy-updates main contrib non-free \ deb http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free \ deb-src http://mirrors.aliyun.com/debian-security wheezy/updates main contrib non-free \ > /etc/apt/sources.list \ && apt-get update \ && apt-get install -y libltdl-dev
這裏用了 jenkins 最新穩定版最爲基礎鏡像,主要乾了兩件事:1、將帳戶改成 root,避免後面沒必要要的權限問題;2、安裝 libltdl-dev ,它是爲了解決用 jenkins 調用容器外部 docker 命令時發生 如下錯誤的問題。(第 4~10 行是爲了換阿里源提升速度)
docker: error while loading shared libraries: libltdl.so.7: cannot open shared object file: No such file or directory
能讓容器內的 jenkins 調用並操做容器外的 docker
。$ vi docker-compose.yml
version: "3" services: jenkins: build: . image: my_jenkins ports: - "8090:8080" - "50000:50000" container_name: my_jenkins volumes: - "/home/jenkins_home:/var/jenkins_home" - "/var/run/docker.sock:/var/run/docker.sock" - "/usr/bin/docker:/usr/bin/docker"
爲了看起來清晰,我寫了一個 docker-compose.yml 文件,將這個文件和以前的 Dockerfile 放在同一個目錄中,能夠用如下命令快速啓動 jenkins,啓動以後新構建的鏡像和容器都名爲 my_jenkins。
$ docker-compose up -d
ip:8090
可看到初始化頁面這裏要輸入密碼,它給出了密碼在容器內的位置,咱們要將路徑改爲宿主機上的,而後 cat 一下就能看到密碼。
$ cat /home/jenkins_home/secrets/initialAdminPassword
要讓 jenkins 操做本機上的 docker 的前提是它收到了咱們 push 代碼的通知,而這個通知就是由 GitHub 上的 Webhooks 來完成的,因此要將這二者關聯起來。npm
打開全局安全配置,軟後進行以下操做,不然 Webhooks 鏈接不成功,設置好了別忘了點保存。
點擊首頁的「開始建立一個新任務」,起個名字,選擇流水線。
點擊「構建觸發器」,選擇「觸發遠程構建」,而後隨便填寫一段字符,而後把 URL 複製下來,記得把「JENKINS_URL」和「TOKEN_NAME」替換爲相應的值,例以下圖最終獲得的 URL 就是
111.11.1.1:8090/job/test/build?token=123456
,記下 URL 後點保存。
打開咱們要 push 代碼的倉庫,點擊「Add webhook」
而後將剛纔記下的回調 URL 填寫到這裏便可
此時,能夠嘗試一下 push 代碼到倉庫,正常狀況下,jenkins 就會自動進行構建,雖然沒有配置要構建什麼,可是它也會進行這個任務,若是構建歷史中自動出現了一個顏色是藍色
的任務則表明整個自動觸發的過程是配置成功的。
本文不介紹流水線語法
,就用 shell 命令來編寫整個過程。但咱們首先仍是要點擊「流水線語法」以後把生成好的流水線腳本複製下來
。node{}
裏面才行。如下是我寫的生成好的流水線腳本,記得把定義的四個變量替換一下。
node { sh '''#!/bin/sh REPOSITORY_NAME="你的倉庫名" REPOSITORY_URL="你的倉庫地址" IMAGE_NAME="給你要構建的鏡像起個名字" CONTAINER_NAME="給你要構建的容器起個名字" echo "清除倉庫目錄" rm ${REPOSITORY_NAME} -r echo "克隆遠程倉庫" git clone ${REPOSITORY_URL} echo "刪除以前的鏡像和容器" docker stop ${CONTAINER_NAME} docker rm ${CONTAINER_NAME} docker rmi ${IMAGE_NAME} echo "構建鏡像" cd ${REPOSITORY_NAME} docker build -t ${IMAGE_NAME} . echo "發佈應用" docker run -d -p 80:3000 --name ${CONTAINER_NAME} ${IMAGE_NAME}''' }
此時,瀏覽器訪問 ip 就能看到更新過的應用了,這就是一個 CI/CD 過程,整個過程省略的測試環節,可自行加上。
用一個測試服務器來作 CI/CD,可以更及時的發現問題、解決問題,提升代碼質量。
可是本文所展現的過程缺陷也很明顯,在更新應用時,是會先停掉容器,再啓動新容器的,不能作到無宕機更新。並且整個過程也沒有服務監控什麼的,不能很好地瞭解無服務的運行狀態。
總之,到目前爲止,咱們已經可以很好地將 docker 用在平常的開發中了。