Docker 從入門到實踐
一般說來 SPA 的項目我們只要啟一個靜態(tài)文件 Server 就可以了,但是針對傳統(tǒng)項目就不一樣了,一個項目會依賴很多服務端程序。之前我們的開發(fā)模式是在一臺開發(fā)機上部署開發(fā)環(huán)境,所有人都在這臺開發(fā)機上使用 Samba 連接開發(fā)。老式開發(fā)是沒什么問題的,但是前端因為引入了編譯流程,增加了 Webpack 打包構(gòu)建的行為,當多人共同開發(fā)的時候經(jīng)常會因為內(nèi)存爆滿進程被殺導致打包失敗。痛定思痛后為了解決這個問題,我決定將 Docker 引入我們的開發(fā)環(huán)境,通過將開發(fā)環(huán)境本地化來解決這個問題,所以有了本文。
本文內(nèi)容主要是我之前分享的文字版,若想看重點的話可以看之前的Slide①
也可以查看分享視頻②
Why Docker?
普通的 Web 服務一般都會依賴很多程序,例如 PHP, MySQL, Redis, Node 等等。正常情況下我們會去手動安裝這些程序來配置服務需要的環(huán)境,這樣會帶來幾個問題:
- 同一環(huán)境不同的服務依賴同一個軟件的不同版本,經(jīng)典的例如 Python2 和 Python3, 本地 Mac 上是 PHP7,但是服務只能支持 PHP5.6。
- 同一環(huán)境不同的服務可能會修改同一份文件,例如系統(tǒng)的配置,Nginx 的配置等,都會造成影響。
- 同一服務在多臺機器上部署需要手工操作,導致大量的人力成本浪費。
這樣逐個的安裝軟件實在是太麻煩了,所以大家就想干脆就直接把整個系統(tǒng)打包好放到機器上得了,于是就出現(xiàn)了虛擬機技術。這樣做能保證系統(tǒng)環(huán)境的穩(wěn)定以及重復的手工操作可以避免,但是也同樣會帶來一些問題:
- 打包后的虛擬機文件包含系統(tǒng)鏡像所以特別大。
- 打包后的虛擬機文件包含系統(tǒng)鏡像所以服務需要等待系統(tǒng)啟動成功之后才能啟動。
- 打包過程無法實現(xiàn)自動化。
針對第三點,后來出現(xiàn)了 Vagrant 使用 vagrantfile 的形式將鏡像構(gòu)建腳本化從而實現(xiàn)自動化的功能,不過其它兩點沒有解決。所以后來就出來了系統(tǒng)之上的進程級別虛擬化技術 —— Docker。它為我們帶來了以下幾個優(yōu)點:
- 不需要打包系統(tǒng)進鏡像所以體積非常小
- 不需要等待虛擬系統(tǒng)啟動所以啟動快速資源占用低
- 沙箱機制保證不同服務之間環(huán)境隔離
- Dockerfile 鏡像構(gòu)建機制讓鏡像打包部署自動化
- Docker hub 提供鏡像平臺方便共享鏡像
以下是 VM 和 Docker 技術的具體區(qū)別,可以看到 VM 是打包了 Guest OS 進入鏡像中的,而 Docker 是直接基于宿主系統(tǒng)虛擬化的實例。


Docker 基礎
Docker 支持 Windows/Linux/Mac/AWS/Azure 多種平臺的安裝,其中 Windows 需要 Win10+,Mac 需要 EI Captain+。Docker 是一個 C/S 架構(gòu)的服務,安裝好 docker 之后需要啟動 docker 軟件后才能使用 docker 命令。
Docker 主要有 Dockerfile, Image, Container, Repository 四個基本概念。通過 Dockerfile 我們可以生成 Docker Image(鏡像)。自己制作的鏡像可以上傳到 Docker hub 平臺,也可以從平臺上拉去我們需要的鏡像。當鏡像拉到本地之后,我們就可以實例化這個鏡像形成一個 Container(實例) 了。一個簡單的鏡像啟動的命令是:
$ docker run?[組織名稱]/<鏡像名稱>:[鏡像標簽]`

其中除了鏡像名稱,其它的都是可選參數(shù)。組織名稱不填默認為library,鏡像標簽不填則默認為latest。例如經(jīng)典的啟動一個 Hello World 鏡像的過程如下:
可以看到當我實例化hello-world這個鏡像的時候,docker 發(fā)現(xiàn)本地沒有這個鏡像會先去 Docker hub 遠端拉取鏡像,如剛才說的,默認是latest標簽。拉取后就會實例化執(zhí)行入口命令了。我們除了可以使用 Docker hub 查找我們需要的鏡像之外,也可以使用docker search命令來查找。16年的一篇文章③顯示,Docker hub 上的鏡像包總量已經(jīng)超過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 端口映射到鏡像實例內(nèi)的 80 端口,而-v表示將本地的$PWD/workspace文件夾映射到鏡像實例里的/var/www/hello.world文件夾,后面的同理。最后再指定一下鏡像名稱,就能完成一次 Nginx 實例的啟動了。此時訪問http://hello.world:8080即可看到效果。
注:千萬不要在容器實例中存儲內(nèi)容,實例銷毀時實例內(nèi)的所有內(nèi)容都會被銷毀,下次啟動的時候又是全新的實例,內(nèi)容不會保存下來。如果需要存儲服務需要使用掛載卷或者外部存儲服務。
Dockerfile
Dockerfile 是 Docker 比較重要的概念。它是 Docker 創(chuàng)建鏡像的核心,它的出現(xiàn)給 Docker 提供了兩大好處:
- 文本化的鏡像生成操作讓其方便版本管理和自動化部署
- 每條命令對應鏡像的一層,細化操作后保證其可增量更新,復用鏡像塊,減小鏡像體積

Dockerfile 的一些編寫規(guī)則主要如下:
- 使用#來注釋
- FROM 指令告訴 Docker 使用哪個鏡像作為基礎
- RUN 開頭的指令會在創(chuàng)建中運行,比如安裝一個軟件包
- COPY 指令將文件復制進鏡像中
- WORKDIR 指定工作目錄
- CMD/ENTRYPOINT 容器啟動執(zhí)行命令
RUN 和 CMD/ENTRYPOINT 都是執(zhí)行命令,區(qū)別在于 RUN 是在鏡像構(gòu)建過程中執(zhí)行的,而 CMD/ENTRYPOINT 是在鏡像生成實例的時候執(zhí)行的,類似于 C/C++ 語言的頭文件的正常代碼的區(qū)別。而且后者在一個 Dockerfile 文件中只能有一個存在。CMD/ENTRYPOINT 的區(qū)別除了在寫法上有區(qū)別之外,還有在docker run命令后增加 CMD 參數(shù)的情況下有區(qū)別(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?/wechatCOPY?package.json?/wechat/package.jsonRUN npm install?--production?--registry=https://registry.npm.taobao.org
COPY index.js?/wechat/index.jsENTRYPOINT?[?"node",?"/wechat/index.js"?]
這里我認為依賴是比較固定的,沒有代碼修改那么頻繁,所以將其提前了。最終保證了所以越穩(wěn)定的變化的命令至于上層,保證了每層打包出來的 Layer 能夠盡可能的復用,而不會徒增鏡像的大小。最后我們使用如下命令就可以完成一個 Docker 鏡像的構(gòu)建了:
$ docker build lizheming/drone-wechat:latest
參數(shù)和docker run是一樣的。構(gòu)建完成之后就可以開心的 push 到 Docker hub 上啦~
Docker Compose
以上我們說了下如何啟動一個服務,但是我們都明白一個完整的項目肯定是不止依賴一個服務的,而 Docker 鏡像的 ENTRYPOINT 只能設置一個,所以難道我們要使用docker run命令手動創(chuàng)建 N 個容器實例嗎?為了解決這個問題,Docker Compose 就瞬時出現(xiàn)了。Docker Compose 是一款容器編排程序,使用 YAML 配置的形式將你需要啟動的容器管理起來,免去我們需要多次執(zhí)行docker run命令的煩惱。
Docker Compose 是使用 Python 開發(fā)的,它的安裝非常的簡單,直接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.#-nginx"????php:????????image:?"lizheming/php-fpm-yaf"????????volumes:????????????-?"$PWD:/home/q/system/m_look_360_cn"????????container_name:?"m.look.#-php"
Docker Compose 的另外一個好處就是能夠幫我們處理容器的依賴關系,在每個容器中會將容器的 IP 和服務的名稱使用 hosts 的方式綁定,這樣我們就能在容器中直接使用服務名稱來接入對應的容器了。例如下面這個 Nginx 配置中的php:9000就是利用了這個原理。
server?{??listen?80;??server_name?dev.m.look.#;??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 容器虛擬化技術除了以上說的解決部署環(huán)境之外,還有一些其它的優(yōu)點,例如:
- 基于 Docker 的 CI 持續(xù)集成和 CD 持續(xù)支付
- 基于 Kubernetes, Docker Swarm 的集群彈性擴容和縮容
CI/CD 對于現(xiàn)在的敏捷開發(fā)是非常重要的,自動化任務幫助我們節(jié)省很多不必要的開發(fā)時間浪費,具體可查看我之間的文章《基于Docker的CI工具》④。而 k8s 和 Docker Swarm 帶來的彈性擴容和縮容讓業(yè)務不在為流量問題而頭疼。通過監(jiān)控報警設置當出現(xiàn)峰值的時候自動擴容抗壓,當出現(xiàn)低谷的時候自動去除多余的容器來節(jié)省成本,同時也將多余的資源給其它服務使用。
學習資料
- 什么是 Docker ?https://cloud.tencent.com/developer/article/1005172
- Docker 從入門到實踐https://yeasy.gitbooks.io/docker_practice/
- Docker compose 詳解https://www.jianshu.com/p/2217cfed29d7
- 深入淺出Dockerhttps://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
來自:怡紅院落 ,作者:怡紅公子
鏈接:https://imnerd.org/docker-in-action.html
聲明:文章來源于網(wǎng)絡,侵刪!