通常說來 SPA 的項目咱們只要啓一個靜態文件 Server 就能夠了,可是針對傳統項目就不同了,一個項目會依賴不少服務端程序。以前咱們的開發模式是在一臺開發機上部署開發環境,全部人都在這臺開發機上使用 Samba 鏈接開發。老式開發是沒什麼問題的,可是前端由於引入了編譯流程,增長了 Webpack 打包構建的行爲,當多人共同開發的時候常常會由於內存爆滿進程被殺致使打包失敗。痛定思痛後爲了解決這個問題,我決定將 Docker 引入咱們的開發環境,經過將開發環境本地化來解決這個問題,因此有了本文。php
本文內容主要是歸納性,詳情參照文尾連接1html
也可參考視頻連接2前端
普通的 Web 服務通常都會依賴不少程序,例如 PHP, MySQL, Redis, Node 等等。正常狀況下咱們會去手動安裝這些程序來配置服務須要的環境,這樣會帶來幾個問題:node
同一環境不一樣的服務依賴同一個軟件的不一樣版本,經典的例如 python2 和 python3, 本地 Mac 上是 PHP7,可是服務只能支持 PHP5.6。python
同一環境不一樣的服務可能會修改同一份文件,例如系統的配置,Nginx 的配置等,都會形成影響。nginx
同一服務在多臺機器上部署須要手工操做,致使大量的人力成本浪費。git
這樣逐個的安裝軟件實在是太麻煩了,因此你們就想幹脆就直接把整個系統打包好放到機器上得了,因而就出現了虛擬機技術。這樣作能保證系統環境的穩定以及重複的手工操做能夠避免,可是也一樣會帶來一些問題:docker
打包後的虛擬機文件包含系統鏡像因此特別大。npm
打包後的虛擬機文件包含系統鏡像因此服務須要等待系統啓動成功以後才能啓動。json
打包過程沒法實現自動化。
針對第三點,後來出現了 Vagrant 使用 vagrantfile 的形式將鏡像構建腳本化從而實現自動化的功能,不過其它兩點沒有解決。因此後來就出來了系統之上的進程級別虛擬化技術 —— Docker。它爲咱們帶來了如下幾個優勢:
不須要打包系統進鏡像因此體積很是小
不須要等待虛擬系統啓動因此啓動快速資源佔用低
沙箱機制保證不一樣服務之間環境隔離
Dockerfile 鏡像構建機制讓鏡像打包部署自動化
Docker hub 提供鏡像平臺方便共享鏡像
如下是 VM 和 Docker 技術的具體區別,能夠看到 VM 是打包了 Guest OS 進入鏡像中的,而 Docker 是直接基於宿主系統虛擬化的實例。
Docker 支持 Windows/Linux/Mac/AWS/Azure 多種平臺的安裝,其中 Windows 須要 Win10+,Mac 須要 EI Captain+。Docker 是一個 C/S 架構的服務,安裝好 docker 以後須要啓動 docker 軟件後才能使用 docker 命令。
Docker 主要有 Dockerfile, Image, Container, Repository 四個基本概念。經過 Dockerfile 咱們能夠生成 Docker Image(鏡像)。本身製做的鏡像能夠上傳到 Docker hub 平臺,也能夠從平臺上拉去咱們須要的鏡像。當鏡像拉到本地以後,咱們就能夠實例化這個鏡像造成一個 Container(實例) 了。一個簡單的鏡像啓動的命令是:
$ docker run [組織名稱]/<鏡像名稱>:[鏡像標籤]`
其中除了鏡像名稱,其它的都是可選參數。組織名稱不填默認爲library,鏡像標籤不填則默認爲latest。例如經典的啓動一個 Hello World 鏡像的過程以下:
能夠看到當我實例化hello-world這個鏡像的時候,docker 發現本地沒有這個鏡像會先去 Docker hub 遠端拉取鏡像,如剛纔說的,默認是latest標籤。拉取後就會實例化執行入口命令了。咱們除了可使用 Docker hub 查找咱們須要的鏡像以外,也可使用docker search命令來查找。16年的一篇文章③顯示,Docker hub 上的鏡像包總量已經超過40萬了,而且以每週4-5k的速度增加着。
下面咱們就來看看如何運行一個 Nginx 容器實例:
$ docker run -d --rm -p 8080:80 -v "$PWD/workspace":/var/www/hello.world -v "$PWD/hello.world.conf":/etc/nginx/conf.d/hello.world.conf nginx
使用docker run命令就能啓動一個實例了,其中-p表示將本機的 8080 端口映射到鏡像實例內的 80 端口,而-v表示將本地的$PWD/workspace文件夾映射到鏡像實例裏的/var/www/hello.world文件夾,後面的同理。最後再指定一下鏡像名稱,就能完成一次 Nginx 實例的啓動了。此時訪問http://hello.world:8080便可看到效果。
注:千萬不要在容器實例中存儲內容,實例銷燬時實例內的全部內容都會被銷燬,下次啓動的時候又是全新的實例,內容不會保存下來。若是須要存儲服務須要使用掛載卷或者外部存儲服務。
Dockerfile 是 Docker 比較重要的概念。它是 Docker 建立鏡像的核心,它的出現給 Docker 提供了兩大好處:
文本化的鏡像生成操做讓其方便版本管理和自動化部署
每條命令對應鏡像的一層,細化操做後保證其可增量更新,複用鏡像塊,減少鏡像體積
Dockerfile 的一些編寫規則主要以下:
使用#來註釋
FROM 指令告訴 Docker 使用哪一個鏡像做爲基礎
RUN 開頭的指令會在建立中運行,好比安裝一個軟件包
COPY 指令將文件複製進鏡像中
WORKDIR 指定工做目錄
CMD/ENTRYPOINT 容器啓動執行命令
RUN 和 CMD/ENTRYPOINT 都是執行命令,區別在於 RUN 是在鏡像構建過程當中執行的,而 CMD/ENTRYPOINT 是在鏡像生成實例的時候執行的,相似於 C/C++ 語言的頭文件的正常代碼的區別。並且後者在一個 Dockerfile 文件中只能有一個存在。CMD/ENTRYPOINT 的區別除了在寫法上有區別以外,還有在docker run命令後增長 CMD 參數的狀況下有區別(CMD會被複寫)。通常建議使用 ENTRYPOINT 會更方便點。一個簡單的 Node 命令行腳本的 Dockerfile 文件以下:
FROM mhart/alpine-node:8.9.3 LABEL maintainer="lizheming <i@imnerd.org>" org.label-schema.name="Drone Wechat Notification" org.label-schema.vendor="lizheming" org.label-schema.schema-version="1.1.0" WORKDIR /wechat COPY package.json /wechat/package.json RUN npm install --production --registry=https://registry.npm.taobao.org COPY index.js /wechat/index.js ENTRYPOINT [ "node", "/wechat/index.js" ]
這裏我認爲依賴是比較固定的,沒有代碼修改那麼頻繁,因此將其提早了。最終保證了因此越穩定的變化的命令至於上層,保證了每層打包出來的 Layer 可以儘量的複用,而不會徒增鏡像的大小。最後咱們使用以下命令就能夠完成一個 Docker 鏡像的構建了:
$ docker build lizheming/drone-wechat:latest
參數和docker run是同樣的。構建完成以後就能夠開心的 push 到 Docker hub 上啦~
以上咱們說了下如何啓動一個服務,可是咱們都明白一個完整的項目確定是不止依賴一個服務的,而 Docker 鏡像的 ENTRYPOINT 只能設置一個,因此難道咱們要使用docker run命令手動建立 N 個容器實例嗎?爲了解決這個問題,Docker Compose 就瞬時出現了。Docker Compose 是一款容器編排程序,使用 YAML 配置的形式將你須要啓動的容器管理起來,免去咱們須要屢次執行docker run命令的煩惱。
Docker Compose 是使用 Python 開發的,它的安裝很是的簡單,直接pip install docker-compose就行了。安裝完成以後分別使用up和stop命令能夠啓動和中止服務。一個簡單的 docker-compose.yaml 配置文件大概以下:
version: "2" services: nginx: depends_on: - "php" image: "nginx:latest" volumes: - "$PWD/src/docker/conf:/etc/nginx/conf.d" - "$PWD:/home/q/system/m_look_360_cn" ports: - "8082:80" container_name: "m.look.360.cn-nginx" php: image: "lizheming/php-fpm-yaf" volumes: - "$PWD:/home/q/system/m_look_360_cn" container_name: "m.look.360.cn-php"
Docker Compose 的另一個好處就是可以幫咱們處理容器的依賴關係,在每一個容器中會將容器的 IP 和服務的名稱使用 hosts 的方式綁定,這樣咱們就能在容器中直接使用服務名稱來接入對應的容器了。例以下面這個 Nginx 配置中的php:9000就是利用了這個原理。
server { listen 80; server_name dev.m.look.360.cn; charset utf-8; root /home/q/system/m_look_360_cn/public; index index.html index.htm index.php; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ .php$ { fastcgi_pass php:9000; #fastcgi_pass unix:/tmp/fcgi.sock; fastcgi_index index.php; } }
基於 Docker 容器虛擬化技術除了以上說的解決部署環境以外,還有一些其它的優勢,例如:
基於 Docker 的 CI 持續集成和 CD 持續支付
基於 Kubernetes, Docker Swarm 的集羣彈性擴容和縮容
CI/CD 對於如今的敏捷開發是很是重要的,自動化任務幫助咱們節省不少沒必要要的開發時間浪費,具體可查看連接④。而 k8s 和 Docker Swarm 帶來的彈性擴容和縮容讓業務不在爲流量問題而頭疼。經過監控報警設置當出現峯值的時候自動擴容抗壓,當出現低谷的時候自動去除多餘的容器來節省成本,同時也將多餘的資源給其它服務使用。
什麼是 Docker ?https://cloud.tencent.com/developer/article/1005172
Docker 從入門到實踐 https://yeasy.gitbooks.io/docker_practice/
Docker compose 詳解 https://www.jianshu.com/p/2217cfed29d7
深刻淺出Docker https://www.kancloud.cn/infoq/docker/79768
①https://ppt.baomitu.com/d/025d62c6
②http://cloud.live.360vcloud.net/theater/play?roomid=2321
③https://medium.com/microscaling-systems/how-many-public-images-are-there-on-docker-hub-bcdd2f7d6100
④https://imnerd.org/drone.html