全文超過一萬字,閱讀此文章,你可能須要一杯咖啡☕️~筆者花生PeA,百度前端汪、ACGer。我的博客 pea3nut.blog;我的資料 pea3nut.infohtml
在保留此段前提下,隨意轉載。請確保此段爲文章最頭部前端
本篇文章詳細而又簡短的介紹了:一名徹底不瞭解 Docker 前端程序員,將全站 Docker 化的過程。內容主要包含:node
真實站點遷移過程:mysql
文章會講解使用 Docker 過程當中用到的所有技術棧(Github CI、Nginx 反向代理、docker-compose),毫不會出現「詳見:http://xxx」甩連接的狀況linux
無需再查閱其餘文檔,看着一篇就夠了!nginx
筆者維護了諸多網站,其中包含:git
而每次我想修改某個網站內容是十分麻煩的。拿你們熟悉的純靜態站點來講,修改過程以下:程序員
npm install
npm run dev
本地修改代碼,測試npm run build
使用 Webpack 進行編譯,產出靜態資源哪怕我只是修改個錯別字,都要十幾分鍾github
網站太多,改動太頻繁,而每次無論多小的改動都很麻煩。簡直讓我感受本身在維護一個萬級QPS的大型項目sql
最近我發現個人 MySQL 進程老是掛掉,致使全部依賴於 MySQL 的站點都掛了
我也不知道爲何,以前仍是好好的
我嘗試了重啓進程、重啓服務器、撈報錯日誌百度,均未奏效
好吧,其實我不太懂 Linux,也不太懂 MySQL,我只是想用下他們搭建 WordPress 站點。而最近總出問題,讓我意識到:
我不只要維護站點,我還要維護環境
這個對於一名前端來講太難了,裝個 nvm 就已是個人極限了。MySQL平白無故掛掉,我根本沒有能力查出個一二三四,而後解決它
我不只要保證站點本地能跑通,還必需要部署在遠程VPS穩定運行。。。
重啓不行。。。那就只能重裝系統了
但是,因爲搭建了許多站點,VPS服務器環境至關複雜(或許這就是 MySQL 掛掉的緣由),光 Apache 配置文件都幾百行了。重作系統的遷移成本,光是想想就耗光了我全部的勇氣
總結一下有以下問題:
而 Docker,正是我解決全部問題SCP-500萬能藥!
那麼 Docker 是如何作的呢?
Docker 中有兩個重要概念。
一個是容器(Container):容器特別像一個虛擬機,容器中運行着一個完整的操做系統。能夠在容器中裝 Nodejs,能夠執行npm install
,能夠作一切你當前操做系統能作的事情
另外一個是鏡像(Image):鏡像是一個文件,它是用來建立容器的。若是你有裝過 Windows 操做系統,那麼 Docker 鏡像特別像「Win7純淨版.rar」文件
上邊就是你所須要瞭解的 Docker 所有基礎知識。就這麼簡單
順便一提,在 Docker 中,咱們一般稱你當前使用的真實操做系統爲「宿主機」(Host)
安裝 Docker 在你的電腦上就像安裝 VS Code 同樣簡單
若是你使用的是Windows電腦,須要購買支持虛擬化的版本。如Win10專業版,Win10家庭版是不行的
安裝完Docker後,你可能會發現本身能夠打開一個漂亮的 Docker 窗口。其實這個窗口沒什麼用處,一般咱們都是經過CLI命令行的方式操做 Docker的,就像 Git 同樣
接下來咱們搭建一個可以託管靜態文件的 Nginx 服務器
容器運行程序,而容器哪來的呢?容器是鏡像建立出來的。那鏡像又是哪來的呢?
鏡像是經過一個 Dockerfile 打包來的,它很是像咱們前端的package.json
文件
因此建立關係爲:
Dockerfile: 相似於「package.json」 | V Image: 相似於「Win7純淨版.rar」 | V Container: 一個完整操做系統
咱們建立一個目錄hello-docker
,在目錄中建立一個index.html
文件,內容爲:
<h1>Hello docker</h1>
而後再在目錄中建立一個Dockerfile
文件,內容爲:
FROM nginx COPY ./index.html /usr/share/nginx/html/index.html EXPOSE 80
此時,你的文件結構應該是:
hello-docker |____index.html |____Dockerfile
文件建立好了,如今咱們就能夠根據Dockerfile
建立鏡像了!
在命令行中(Windows優先使用PowerShell)鍵入:
cd hello-docker/ # 進入剛剛的目錄 docker image build ./ -t hello-docker:1.0.0 # 打包鏡像
注意!Docker 中的選項(Options)放的位置很是有講究,docker —help image
和docker image —help
是徹底不一樣的命令
docker image build ./ -t hello-docker:1.0.0
的意思是:基於路徑./
(當前路徑)打包一個鏡像,鏡像的名字是hello-docker
,版本號是1.0.0
。該命令會自動尋找Dockerfile
來打包出一個鏡像
Tips: 你可使用
docker images
來查看本機已有的鏡像
不出意外,你應該能獲得以下輸出:
Sending build context to Docker daemon 3.072kB Step 1/3 : FROM nginx ---> 5a3221f0137b Step 2/3 : COPY ./index.html /usr/share/nginx/html/index.html ---> 1c433edd5891 Step 3/3 : EXPOSE 80 ---> Running in c2ff9ec2e945 Removing intermediate container c2ff9ec2e945 ---> f6a472c1b0a0 Successfully built f6a472c1b0a0 Successfully tagged hello-docker:1.0.0
能夠看到其運行了 Dockerfile 中的內容,如今咱們簡單拆解下:
FROM nginx
:基於哪一個鏡像COPY ./index.html /usr/share/nginx/html/index.html
:將宿主機中的./index.html
文件複製進容器裏的/usr/share/nginx/html/index.html
EXPOSE 80
:容器對外暴露80端口咱們剛剛使用 Dockerfile 建立了一個鏡像。如今有鏡像了,接下來要根據鏡像建立容器:
docker container create -p 2333:80 hello-docker:1.0.0 docker container start xxx # xxx 爲上一條命令運行獲得的結果
而後在瀏覽器打開127.0.0.1:2333
,你應該能看到剛剛本身寫的index.html
內容
在上邊第一個命令中,咱們使用docker container create
來建立基於hello-docker:1.0.0
鏡像的一個容器,使用-p
來指定端口綁定——將容器中的80
端口綁定在宿主機的2333
端口。執行完該命令,會返回一個容器ID
而第二個命令,則是啓動這個容器
啓動後,就能經過訪問本機的2333
端口來達到訪問容器內80
端口的效果了
Tips: 你可使用
docker containers ls
來查看當前運行的容器
當容器運行後,能夠經過以下命令進入容器內部:
docker container exec -it xxx /bin/bash # xxx 爲容器ID
原理其實是啓動了容器內的/bin/bash
,此時你就能夠經過bash shell
與容器內交互了。就像遠程鏈接了SSH同樣
咱們總結下都發生了什麼:
docker image build
來將Dockerfile
打包成鏡像docker container create
來根據鏡像建立一個容器docker container start
來啓動一個建立好的容器
雖然很簡單,可是也沒有感受到「廣闊天地,大有可爲,隨心所欲」呢?
接下來咱們實戰遷移一個由 Vuejs 寫的純靜態 SPA 單頁站點:
在沒遷移 Docker 以前,若我想更新線上網站中內容時,須要:
npm run build
打包產出靜態文件git push
更新 Github 源碼稍微有點麻煩,所以我打算這樣改:
git push
而這樣作的好處是:
git push
,而沒必要本地npm run build
首先是讓 Github 在我每次更新代碼時打包出一個鏡像
在 Github,能夠有免費的 CI 資源用,它就是 Travis CI
在項目中根目錄中添加.travis.yml
文件,內容以下:
language: node_js node_js: - "12" services: - docker before_install: - npm install script: - npm run build - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - docker build -t pea3nut/pea3nut-info:latest . - docker push pea3nut/pea3nut-info:latest
文件內容很是簡單,就是使用npm run build
編譯靜態產出後,打包一個鏡像而且 push 到遠程。有幾點須要詳細說一下:
hub.docker.com
中註冊一個帳號,而後替換代碼中的pea3nut/pea3nut-info:latest
爲用戶名/包名:latest
便可DOCKER_USERNAME
和DOCKER_PASSWORD
環境變量。這樣保證咱們能夠祕密的登陸 Docker Hub 而不被其餘人看到本身的密碼。以下圖
而後須要添加 Dockerfile 文件來描述如何打包 Docker 鏡像。
按照.travis.yml
的命令次序,在打包鏡像時,npm run build
已經執行過了,項目產出已經有了。沒必要在 Docker 容器中運行npm install
和npm run build
之類的,直接複製文件便可:
FROM nginx COPY ./dist/ /usr/share/nginx/html/ EXPOSE 80
Note: 過程雖然簡單可是線條很長,建議本地多測試測試再進行
git push
若你編譯出的靜態站點也是一個 SPA 單頁應用,須要增長額外的 Nginx 配置來保證請求都能打到index.html
。下邊是我寫的vhost.nginx.conf
Nginx 配置文件,將不訪問文件的請求所有重定向到/index.html
:
server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; proxy_set_header Host $host; if (!-f $request_filename) { rewrite ^.*$ /index.html break; } } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
而後在 Dockerfile 中新加一行,將本機的vhost.nginx.conf
文件複製到容器的/etc/nginx/conf.d/pea3nut-info.conf
,讓 Nginx 可以讀取該配置文件:
FROM nginx COPY ./dist/ /usr/share/nginx/html/ + COPY ./vhost.nginx.conf /etc/nginx/conf.d/pea3nut-info.conf EXPOSE 80
而後執行git push
後,你能夠在 Travis CI 看到 CI 的編譯結果。若是編譯沒問題,遠程實際上就有了pea3nut/pea3nut-info:latest
這個鏡像。本地能夠試試看該鏡像工做是否正常:
docker image pull pea3nut/pea3nut-info:latest docker container create -p 8082:80 pea3nut/pea3nut-info:latest docker container start xxx # xxx 爲上一條命令執行的返回值
運行完成後,瀏覽器訪問127.0.0.1:8082
應該就能看到效果了!
而後你能夠登陸遠程 VPS 服務器,安裝 Docker,執行一樣的命令。而後訪問遠程 VPS 服務器的公網 IP + 8082 端口號,應該能看到和本地相同的效果
Tips: 忘了如何在 VPS 上安裝 Docker?在上文「安裝 Docker」一節,你可能須要的是 Linux 的安裝方式
curl https://get.docker.com/ > install-docker.sh # 下載安裝腳本 sh install-docker.sh # 執行安裝腳本
Note: 接下來的操做都是在你的遠程 VPS 服務器上操做,並不是本地電腦,或者容器中
目前咱們將容器掛到了 8082 端口,可是線上不可能讓用戶手動輸入 8082 端口進行訪問。而若是將容器直接掛到 80 端口,雖然這樣用戶能夠直接不加端口直接訪問,可是若是有第二個容器,或者更多容器呢?
這時候就須要在宿主機跑一個 Nginx,由它來獨佔 80 端口,而後根據域名來說請求分發給響應的容器。以下圖:
這種方案叫作「反向代理」
登陸VPS服務器,安裝 Nginx。由於我是 Ubuntu,因此能夠用apt
安裝。其餘 Linux 發行版能夠百度下安裝方法,一般2行內能夠搞定:
apt update # 更新軟件包 apt-get install nginx # 安裝 Nginx systemctl status nginx # 查看 Nginx 狀態
此時本地經過瀏覽器訪問 VPS 的公網 IP 可用看到 Nginx 的歡迎頁面
而後在 VPS 服務器的/etc/nginx/conf.d/
中創建一個vhost.conf
文件,配置以下內容:
server { listen 80; server_name pea3nut.info; location / { proxy_pass http://127.0.0.1:8082; } }
配置的意思是,監聽來自 80 端口的流量,若訪問域名是pea3nut.info
(替換爲你本身的域名),則所有轉發到http://127.0.0.1:8082
中
配置完成後,重啓 Nginx 服務器。如果 Ubuntu 可使用systemctl restart nginx
命令,不一樣 Linux 發行版稍有不一樣
配置成功後,訪問pea3nut.info
會看到和VPS公網IP:8082
相同的效果
而遷移完成 Docker 後,我想改一個錯別字的流程變爲:
git push
docker image pull pea3nut/pea3nut-info:latest docker container create -p 8082:80 pea3nut/pea3nut-info:latest # 獲得 yyy docker container stop xxx # xxx 爲當前運行的容器ID,可用 docker container ls 查看 docker container start yyy # yyy 第二條命令返回值
命令仍是有些長?咱們在下面會進一步優化它
接下來咱們實戰遷移一個由 Nodejs 寫的 Express SSR 站點
網站使用 Ejs 模板渲染頁面。在沒遷移 Docker 以前,若我想更新線上網站中內容時,須要:
npm install
git push
更新 Github 源碼稍微有點麻煩,所以我打算這樣改:
git push
而這樣作的好處是:
具體的過程和處理靜態站點沒有什麼特別的區別,無非是:
此次就不重複講了,具體的配置能夠參考項目中的相關文件
Tips: 你可能發現了 Dockerfile 中的ENTRYPOINT
命令必須指定一個前臺進程。若你的 Nodejs 應用是使用 PM2 進行保活的,你須要替換pm2 start app.js
爲pm2-docker app.js
當將 Nodejs 站點遷移完成,咱們的 VPS 服務器上已經運行了2個容器。每次鏡像更新都要手動的docker container create
帶一堆參數是比較麻煩的,尤爲是當往後容器日益增多的時候。而這時,就輪到docker-compose
登場了~
docker-compose 是 Docker 官方提供的一個 Docker 管理工具。若你是經過桌面端的 Docker 安裝包安裝的 Docker,它是會默認爲你安裝 docker-compose 的。能夠試試以下命令:
docker-compose --help
若是是在 Linux,能夠經過以下命令安裝 docker-compose:
curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmode +x /usr/local/bin/docker-compose
docker-compose 和 Docker 差很少,也是隻要一份文件就能跑起來。docker-compose 主要的做用就是可以讓你沒必要手敲那麼多 Docker 命令
創建一個目錄,而後在目錄中創建docker-compose.yml
,內容以下:
version: "3.7" # 這個是配置文件的版本,不一樣的版本號聲明方式會有細微的不一樣 services: info: container_name: pea3nut-info image: pea3nut/pea3nut-info:latest ports: - "8082:80" restart: on-failure
而後在目錄中鍵入以下命令就能將服務跑起來:
docker-compose up info
docker-compose 會幫咱們自動去拉鏡像,建立容器,將容器中的80
端口映射爲宿主機的8082
端口。restart
字段還要求 docker-compose 當發現容器意外掛掉時從新啓動容器,相似於 pm2,因此你沒必要再在容器內使用 pm2
若是想要更新一個鏡像建立新容器,只須要:
docker-compose pull info docker-compose stop info docker-compose rm info docker-compose up -d info # -d 表明後臺運行
筆者已將本身網站部署方式開源,可參考 github/pea3nut-hub
接下來咱們實戰遷移一個 WordPress 站點
可能你也發現了這個站點和其餘站點的一個很是大的不一樣——他的源碼和數據是不能公開的
以前咱們打包鏡像時,都是直接將代碼打進鏡像內的。這條方案用在這裏顯然是不行的,有兩個問題:
docker image pull
下載到鏡像,而後取得鏡像內的文件Docker 提供了一個叫作 Volume 的東西,能夠將容器內和宿主機的某個文件夾進行」綁定「,任何文件改動都會獲得同步。因此,我能夠將整個站點目錄和 MySQL 目錄都掛載爲 Volume。這樣,當容器刪除時,全部數據文件和源碼都會保留。
在本地創建./blog/mysql-data
目錄存儲 MySQL 數據,創建./blog/wordpress
目錄存儲 WordPress 源碼。而後修改docker-compose.yml
以下:
version: "3.7" services: info: container_name: pea3nut-info image: pea3nut/pea3nut-info:latest ports: - "8082:80" restart: on-failure + blog: + container_name: pea3nut-blog + image: tutum/lamp:latest + ports: + - "8081:80" + volumes: + - ./blog/mysql-data:/var/lib/mysql + - ./blog/wordpress:/app + restart: on-failure
能夠看到此次根本沒有打包鏡像,而是直接使用tutum/lamp
鏡像提供的 LAMP 環境(Linux + Apache + MySQL + PHP),而後將 MySQL 數據目錄/var/lib/mysql
和源碼目錄/app
都掛載出來就能夠了
Tips: 經過 Volume 咱們只是解決了部署問題,而如何本地開發而後將源碼同步到服務器呢?用 FTP 固然是能夠的,可是稍微有點麻煩。其實你能夠自建一個 Git 服務器!詳見: pea3nut.blog/e127
靜態站點遷移(筆者簡歷):
PHP 站點遷移(筆者博客):
Nodejs 遷移(Pxer 官網):
其餘:
你好,這裏是花生PeA。感謝你能看完這篇文章,很是感謝!
在文章撰寫兩個月前,我決定將站點所有遷移到 Docker。兩個星期前,我決定將過程整理成一篇博客。沒想到寫了這麼久,寫了上萬字
說實話筆者在撰寫過程當中實際上是有些擔憂的。一方面本身真的只是一名前端,對於 Docker 的瞭解僅僅停留在使用方面,擔憂本身是否真的能「跨界」寫出一篇Docker教程;另外一方面隨着文章字數止不住的上升,也十分擔憂在當今的網絡環境下,是否真的有人願意花時間讀一篇上萬字的技術文章
可是 Docker 真的很好用。全站 Docker 化後,當我再次遷移服務器時,我發現我竟能夠在十行命令內完成整個環境的遷移,耗時十分鐘!這種「爽快」的感受也是我撰寫文章的動力——我想將這份爽快分享給屏幕前的你。但願你也能喜歡上 Docker ~ ❤️