Node.js開發人員寶典:上手Docker從這里開始
譯文難度等級:初學者
要求:Mac OS X(本教程假設你在使用Mac,但你也能找到Windows或Ubuntu的安裝指示,直接跳過“安裝”章節)。
Docker剛過了第二個生日,但它仍是一項“新興”的強大技術。與本人交流過的許多開發人員對它已有所耳聞,但并沒有實際用過它。Docker讓你可以做一些確實很酷的事情,比如可以迅速測試開發中的應用程序,所在環境與質量保證/測試/生產環境一模一樣;或者與其他開發人員共享該應用程序,以便盡快輕松熟悉情況。說到Docker,一個常打的比方就是它好比是現實生活中的集裝箱或樂高積木:它提供了一種基本單元,同時提供了一種不管是什么硬件,讓應用程序都易于移植、易于遷移的方法。
我在本教程中將大致介紹一下Docker以及為何想要使用它、如何安裝它,然后我們要著手安裝一個Node容器,并且在該窗口里面建立一個Express入門級應用程序。這篇教程內容蠻長!官方的Docker入門指南可以讓你更快地上手,我在這里的目的是,解釋這個過程中每一步出現的情況。
我們將介紹:
- 引言(Docker的性質及為何使用它)
- 安裝•Docker Hub和Docker文件
- Docker Pull:獲取Ubuntu映像
- Docker Run:運行我們的Ubuntu映像,及訪問容器
- Docker Commit:安裝node、npm和express,并提交變更內容
- Docker Push:推回容器,那樣別人就能使用它
注意:我將以下列格式指代在你自己的終端中執行的命令:
- $ command
并以下列格式指代容器里面的命令:
- $ root: command
引言
現在你可能已經聽說過Docker。每天總會有專業報章提到它,或者你在推特/IRC上看到別人談論它。它的人氣在過去幾年急劇飆升;大多數云服務提供商已經支持它。如果你對它感到很好奇,但是還沒有用過它,那么這篇教程蠻適合你。
閑話少說,那么Docker到底是什么東東?這么說吧,Docker可能指幾個方面:
Docker客戶程序:這是在我們的機器上運行的組件。它是docker二進制代碼;只要我們打開終端,輸入$ docker pull或$ docker run,就要與之交互。它連接至處理所有繁重任務的docker守護程序,而守護程序可能在同一個主機上(以Linux為例),也可能在遠地(我們與VirtualBox虛擬機進行交互)。
Docker守護程序:這是處理諸多繁重任務的組件,比如構建、運行和分發Docker容器。
Docker映像:docker映像好比是我們應用程序的藍圖。繼續說說上面的集裝箱/樂高積木那個比方,docker映像是用于實際構建真正實例的藍圖。映像可能是Ubuntu之類的操作系統,也有可能是你的Web應用程序及其所有的必要程序包都已安裝的Ubuntu。
Docker容器:容器是用docker映像構建而成的,它們是集裝箱/樂高積木的真正實例。它們可以啟動、運行、停止、刪除和移動。
Docker Hub(注冊中心):Docker注冊中心是一臺托管的注冊中心服務器,可以存放Docker映像。Docker公司提供了一個公共Docker注冊中心,名為Docker Hub,我們會在本教程中用到它,但Docker公司提供了開放源代碼的整個系統,那樣人們可以在自己的服務器上運行,以私密方式存儲映像。
我們已經明確了Docker的不同部分,下面介紹你可能想要使用它的幾個理由:
- 簡化開發環境的配置;
- 在類似質量保證/測試/生產環境的環境中,迅速測試應用程序(相比虛擬機,開銷較小);
- 與其他開發人員共享你的應用程序和環境,這樣便于別人盡快熟悉情況。
- 能夠構建不同的容器(這在調試中極其有用)
安裝
運行容器、因而運行Docker需要Linux機器。由于我們使用的是Mac,這就意味著我們需要虛擬機。為了讓安裝過程來得更容易,我們可以使用Boot2Docker,它可以安裝Boot2Docker管理工具VirtualBox,并在Docker已安裝的情況下在里面安裝虛擬機。
boot2docker
直接瀏覽到該鏈接,下載最新版本的Boot2Docker,并安裝它(截至本文截稿時最新版本是Boot2Docker-1.5.0.pkg)。
https://github.com/boot2docker/osx-installer/releases/latest
安裝完畢后,進入到你的Applications文件夾,打開Boot2Docker。那會在VirtualBox里面打開一個新的終端,運行幾個命令,這些命令的作用基本上就是啟動已經安裝了Docker的虛擬機,然后設置幾個環境變量,那樣我們就能從終端訪問該虛擬機。如果你不想總是打開Boot2Docker以便與Docker進行交互,只要運行下面幾個命令:
- # 如果你還沒有新的虛擬機,創建一個
- $ boot2docker init
- # 啟動虛擬機
- $ boot2docker start
- # 設置必需的環境變量
- $ $(boot2docker shellinit)
現在輸入:
- $ docker run hello-world
這會讓Docker從Docker Hub下載hello-world映像,并啟動基于它的容器。你的終端應該會給出類似如下的輸出結果:
- Hello from Docker.
- This message shows that your installation appears to be working correctly.
好了!Docker已安裝完畢。
要是你遇到什么問題,歡迎留言交流,也可以在此(https://docs.docker.com/installation/mac/)參閱Docker的官方安裝指示。
#p#
Docker文件和Docker Hub
在探討下一步操作之前,我認為明白我們在執行$ docker run hello-world后發生的事情很重要,那樣你不僅僅是復制粘貼接下來的指示。docker run是我們用來啟動基于映像的容器的基本命令,同時將命令傳送給它。在這里,我們說“Docker,啟動基于映像hello-world的容器,不需要額外的命令。”然后,它從Docker Hub下載映像,并且啟動VirtualBox虛擬機里面基于該映像的容器。但是hello-world映像來自哪里呢?這時候,Docker Hub有了用武之地。就像我們之前在引言部分提到的那樣,Docker Hub是公共注冊中心,含有與Docker一起使用的容器映像,以及由Docker、其他公司和個人構建的容器映像。你在這里可以找到我們剛執行的hello-world映像:
Docker Hub Hello-World映像(https://registry.hub.docker.com/u/library/hello-world/)
每個映像都是使用Docker文件構建的。在hello-world映像的描述部分,你能找到Docker文件的鏈接(https://github.com/docker-library/hello-world/blob/master/Dockerfile),該文件只有短短3行:
- FROM scratch
- COPY hello /
- CMD [“/hello”]
Docker文件就是文本文件,含有如何構建容器映像方面的Docker指示。映像就好比是機器快照,而容器好比是機器的實際的運行實例。Docker文件總是會有這種格式:
- INSTRUCTION arguments
所以在我們的hello-world例子中,我們可以看一下含有Docker文件的GitHub軟件庫的根。該映像是用另一個名為“scratch”的映像構建的(所有的Docker文件都是以FROM指示開頭),然后將hello文件復制到系統的根,最后運行hello。你還能在這里找到hello文件的內容(https://github.com/docker-library/hello-world/blob/master/hello.asm),包括我們在終端中剛看到的輸出結果。
Docker Pull:下載Ubuntu映像
我們已知道了安裝的Docker已正確設置好,不妨開始使用它!下一步是獲得Ubuntu映像。想找到映像,我們可以進入到Docker Hub網站(https://hub.docker.com),或者只要在終端中運行:
- $ docker search ubuntu
這會給出名稱中含有Ubuntu的所有映像的列表。我的終端中顯示如下:
輸出結果按每個映像軟件庫中的星號數量來排序。你能看到上面有Official(官方)欄和Automated(自動)欄。
•官方映像是由docker-library項目維護的映像,并得到Docker團隊的接受。這意味著,它們遵守這里的幾個準則(https://docs.docker.com/docker-hub/official_repos/),其中一些位于git軟件庫;對該軟件庫至少要有只讀權,以便用戶能查看其內容。你可以依賴那些映像,順利使用Docker。另外,與你需要使用USERNAME/IMAGE_NAME來獲取的其他映像相反,這些映像完全只要由IMAGE_NAME(比如Ubuntu)用命令來指代。所有Docker文件都呈現在這種組織體系中(https://github.com/docker-library)。
•自動欄是指自動構建(Automated Build)映像。這完全意味著,映像是通過GitHub或BitBucker軟件庫里面的Docker文件構建的;對它進行更改后,它會自動更新。
不妨下載官方的Ubuntu映像:
- $ docker pull ubuntu
$ docker pull IMAGE_NAME命令是明確下載映像的方式,但如果你使用$ docker run IMAGE_NAME命令,也能下載,就是Docker找不到你所指的那個映像。
Docker Run:運行我們的Ubuntu映像,訪問容器
我們已經有了Ubuntu映像(我們的藍圖)。現在不妨啟動基于該映像的新容器,并將命令傳送給它:
- $ docker run ubuntu /bin/echo ‘Hello world’
這在你的終端中應該會給出消息“Hello World”。我們剛啟動了一個運行Ubuntu完全隔離的實例的容器,并執行了命令,這很好,但是其實沒多大用處。
于是,現在不妨運行新的容器以及Ubuntu,并連接到它:
- $ docker run -i -t ubuntu
請注意:run命令很龐大(查看$ docker help run),我們會在下一篇博文中更深入全面地介紹。
-t旗標在我們的新容器里面分配了偽終端(pseudo-tty)或終端,-i旗標讓我們可以獲取容器的標準流(STDIN),從而建立交互性連接。如果一切順利,你應該可以連接至容器里面的終端,并顯示這樣的結果:
- $ root@c9989236296d:/#
運行ls –ls,就會發現運行中的命令就在Ubuntu系統的根里面。
終端ubuntu
我認為,有必要暫停一分鐘,考慮一下我們剛才執行的操作。這正是容器的出色方面之一。我們剛下載并啟動了運行Ubuntu的容器。這一切在短短5分鐘內發生的嗎(取決于你的互聯網連接速度)?拿這與下載虛擬機Ubuntu映像、啟動新虛擬機作一下比較。后者可能是不是要花15分鐘至30分鐘?然后,構建、停止及重啟新虛擬機,這個過程要花多久?如果你把這些時間都算上去,就會發現使用容器可以節省大量時間!
#p#
Docker Commit:安裝node、npm和express,提交變更內容
好了,鑒于我們已在運行中的Ubuntu容器里面,不妨安裝運行節點應用程序所需要的工具(切記:你只需要執行$ root:后面的部分):
- $ root: apt-get update
- $ root: apt-get install nodejs
- $ root: apt-get install nodejs-legacy
- $ root: apt-get install npm
請注意:我們需要安裝nodejs-legacy,才能運行express-generator模塊。
運行node -v應該會得出輸出結果:
- $ root: node -v
- v0.10.25
節點安裝完畢后,我們可以直接從npm安裝express generator模塊:
- $ root: npm install -g express-generator
現在我們已有了容器,而且我們所需的一切都安裝在里面。不妨接下來退出容器:
- $ root: exit
我們退出容器后,Docker會停止運行它。我們可以使用$ docker ps命令列出容器,不妨這么做:
- $ docker ps -a
終端容器
$ docker ps命令默認情況下只顯示運行中的容器,于是我們傳遞-a旗標,那樣我們就能查看剛退出的Ubuntu容器。
現在,我們可以使用該容器來構建別人可以使用的新映像。為此,我們可以使用commit命令:
- $ docker commit -a "Your Name <youremail@email.com>" -m "node and express" CONTAINER_ID node-express:0.1
commit命令只有幾個參數。-a旗標設定作者,你可以使用-m旗標設定消息,最后我們可以引用容器ID和所創建容器的名稱,這里是node-express。我們還可以在映像名稱后面添加:0.1,為映像設定標簽。如果我們運行:
- $ docker images
我們應該會看到:
終端容器
太棒了,你剛構建了第一個Docker映像!
現在,不妨為剛創建的映像添加另一個標簽。運行:
- $ docker tag node-express:0.1 node-express:latest
給映像標記特定的版本是個好做法,那樣人家知道自己在到底運行哪個映像。添加latest標簽大有幫助,那樣別人在下載映像時就能用名稱來指代你的映像(我們的例子中是node-express),Docker會自動下載latest標簽版本。如果你再次運行$ docker images,就能看到我們的映像有兩行,但它們都有同一個ID,這意味著它們并不占用硬盤中的任何額外空間。
現在我們就可以啟動想與映像一同使用的多個容器!不妨刪除舊的容器:
- $ docker ps -a
- $ docker rm YOUR_CONTAINER_ID
不妨運行基于新映像的容器,使用-i -t旗標連接到它,將主機(VirtualBox)的端口8080暴露為容器(虛擬機)的端口3000:
- $ docker run -i -t -p 8080:3000 node-express
不妨使用我們已安裝的express-generator,構建一個新的Node.js應用程序:
- $ root: express mynodeapp
按終端中的指示操作后,進入到應用程序文件夾,安裝依賴項,啟動該應用程序:
- $ root: cd mynodeapp
- $ root: npm install
- $ root: npm start
現在,我們已有了一個在容器里面運行的Node.js應用程序,暴露端口3000。想查看這個應用程序,我們需要找到Boot2Docker虛擬機IP地址,為此打開另一個終端,運行:
- $ boot2docker ip
- 192.168.59.103
另外切記:我們實際上暴露了容器的端口8080,以便訪問端口3000。于是,進入到瀏覽器,打開:
- 192.168.59.103:8080
- website express
現在,你可能會想:光擁有一個運行中的應用程序就要費這老大的勁!我早已有了開發環境,可以在30秒內完成這一切!沒錯,確實如此,但是在本教程中,我們運行的一個超級簡單的應用程序根本沒有許多依賴項。你在運行依賴項要多得多的實際項目時,可能需要開發環境擁有不同的程序包、Python、Redis、MongoDB、Postgres、Node.jsK io.js等。牽涉太多的方面,結果會讓在你的計算機中正常運行的應用程序在另一臺機器((或在質量保證/測試/生產環境)中卻無法正常運行,這就是Docker大受歡迎的主要原因。回到本教程的引言部分,通過提供一種獨立于硬件而執行的基本單元,該基本單元還能輕松運行、移動和共享,Docker絕對改變了我們開發、測試及共享應用程序的方式。
#p#
Docker Push:推送容器映像,那樣別人就能使用它
現在不妨共享我們這個“出色的”Ubuntu映像(已安裝了node、npm和express-generator),那樣別人也能使用它。退出運行中的Node應用程序和容器:
- # Ctrl+C to stop our node app
- $ root: exit
直接進入到Docker Hub:http://hub.docker.com,設立一個免費帳戶;
之后,回到終端,運行:
- $ docker login
鑒于我們已通過命令行接口(CLI)登錄上去,可以將映像推送到Docker Hub。不妨先對它進行更名,并為它添加用戶名,所以就像添加標簽:
- $ docker tag node-express your_docker_hub_username/node-express
- $ docker rmi node-express
- $ docker push your_docker_hub_username/node-express
大功告成!現在,擁有Docker的人都可以執行:
- $ docker pull your_docker_hub_username/node-express
擁有了與我們之前建立的環境一模一樣的環境,而Ubuntu、Node.js、npm和express-generator程序包一應俱全。