這是兩篇連載文章的第一篇,講解了如何使用 Docker 替代 Vagrant 開發基於 Express 框架的NodeJs App的部分細節。不過,這次要增加點難度:我們要使用 connect-redis 在 Redis 中實現 session 功能。第二篇文章將基於此繼續。
The Node App
這個 App 由一個 package.json , server.js 和 .gitignore 文件組成,就這么簡簡單單:
.gitignore :
1 node_modules/*
package.json :
1 { 2 "name": "docker-dev", 3 "version": "0.1.0", 4 "description": "Docker Dev", 5 "dependencies": { 6 "connect-redis": "~1.4.5", 7 "express": "~3.3.3", 8 "hiredis": "~0.1.15", 9 "redis": "~0.8.4" 10 } 11 }
server.js :
1 var express = require('express'), 2 app = express(), 3 redis = require('redis'), 4 RedisStore = require('connect-redis')(express), 5 server = require('http').createServer(app); 6 7 app.configure(function() { 8 app.use(express.cookieParser('keyboard-cat')); 9 app.use(express.session({ 10 store: new RedisStore({ 11 host: process.env.REDIS_HOST || 'localhost', 12 port: process.env.REDIS_PORT || 6379, 13 db: process.env.REDIS_DB || 0 14 }), 15 cookie: { 16 expires: false, 17 maxAge: 30 * 24 * 60 * 60 * 1000 18 } 19 })); 20 }); 21 22 app.get('/', function(req, res) { 23 res.json({ 24 status: "ok" 25 }); 26 }); 27 28 var port = process.env.HTTP_PORT || 3000; 29 server.listen(port); 30 console.log('Listening on port ' + port);
Server.js 處理了依賴關系並啟動了一個Express App,這個Express App 配置了 Redis 里存儲的 Session 信息並曝露了一個端點(endpoint)用來返回標准 json 格式的服務器狀態信息。
這里需要稍微注意的是,Redis 的連接信息會被使用的環境變量所覆蓋--后面發布時會有用到這一信息。
The Dockerfile
為了便於開發,我們會把 redis 和 node 運行在同一個容器里,要實現這點,就必須用 Dockerfile 來配置容器 :
Dockerfile :
1 FROM dockerfile/ubuntu 2 3 MAINTAINER Abhinav Ajgaonkar <abhinav316@gmail.com> 4 5 # Install Redis 6 RUN \ 7 apt-get -y -qq install python redis-server 8 9 # Install Node 10 RUN \ 11 cd /opt && \ 12 wget http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-x64.tar.gz && \ 13 tar -xzf node-v0.10.28-linux-x64.tar.gz && \ 14 mv node-v0.10.28-linux-x64 node && \ 15 cd /usr/local/bin && \ 16 ln -s /opt/node/bin/* . && \ 17 rm -f /opt/node-v0.10.28-linux-x64.tar.gz 18 19 # Set the working directory 20 WORKDIR /src 21 22 CMD ["/bin/bash"]
讓我們逐行逐行來看看 Docherfile 里的代碼:
1 FROM dockerfile/ubuntu
告訴 Docker 用 Docker 公司提供的 dockerfile/ubuntu 的鏡像文件作為 build 時的基礎鏡像。
1 RUN \ 2 apt-get -y -qq install python redis-server
基礎鏡像本身並沒有任何東西,所以,我們需要用 apt-get 命令將所需要的組件打包進去。這條表示安裝 python和 redis-server,Redis 服務是必需的,因為我們要把 session 信息存儲進去。對於 npm 而言,python 可以為 Redis Node 生成 C 擴展。
1 RUN \ 2 cd /opt && \ 3 wget http://nodejs.org/dist/v0.10.28/node-v0.10.28-linux-x64.tar.gz && \ 4 tar -xzf node-v0.10.28-linux-x64.tar.gz && \ 5 mv node-v0.10.28-linux-x64 node && \ 6 cd /usr/local/bin && \ 7 ln -s /opt/node/bin/* . && \ 8 rm -f /opt/node-v0.10.28-linux-x64.tar.gz
下載並解壓 64 位二進制版 NodeJs。
1 WORKDIR /src
這句話是告訴 Docker -- 容器開始運行時進入 cd /src 目錄。
1 CMD ["/bin/bash"]
此為最后一步:啟動 /bin/bash.
Build and run the container
Docker file 配置好后,我們來生成一個 Docker 鏡像:
1 docker build -t sqldump/docker-dev:0.1 .
生成成功后,就可以用下面的命令啟動容器了:
1 docker run -i -t --rm \ 2 -p 3000:3000 \ 3 -v `pwd`:/src \ 4 sqldump/docker-dev:0.1
我們來分析一下這個命令。
-i: 以交互模式運行容器 (不同於 -d : 以分離模式運行容器),這意味着交互回話 session 結束時,容器就會停止運行。
-t : 分配了一個虛擬終端(pseudo-tty)。
--rm : 停止運行時移除容器和文件系統。
-p 3000:3000 :主機端口 3000 : 容器端口 3000
1 -v `pwd`:/src
這條命令把主機的當前工作目錄(如,項目文件)載入容器的/src里面。我們載入文件而不是使用 add 命令增加文件,這使得我們在文本編輯器里更愛文件后能立刻在容器里看到效果。
sqldump/docker-dev:0.1 : 當前運行的 docker 鏡像的名字和版本號與我們生成的鏡像是一樣的。
由於 Dockerfiles 指定了 CMD ["/bin/bash"],容器運行后我們就進入了一個 bash shell。如果 docker 成功運行了命令,情景應該如下圖:

Start Developing
容器現在已經運行起來了,不過,在開始編寫代碼之前,需要將一些標配的,非 docker 文件分別安裝。首先,用如下命令在容器里啟動 redis 服務:
1 service redis-server start
然后,安裝相關依賴和 nodemon 。Nodemon 可以實時監聽項目文件的變化並重啟服務。
1 npm install 2 npm install -g nodemon
最后,用如下命令啟動服務:
1 nodemon server.js
現在,打開你的瀏覽器並訪問:http://localhost:3000,順利的話會出現下圖情景:

給 server.js 增加一個端點以模擬開發工作流:
1 app.get('/hello/:name', function(req, res) { 2 res.json({ 3 hello: req.params.name 4 }); 5 });
你將可以看到 nodemon 檢測到文件的更改並重啟了服務:

現在訪問:http://localhost:3000/hello/world,你將會看到的頁面:

Production
容器當前的狀態,還遠沒有成型。redis 里面的數據在容器重啟后會丟失。同樣的,如果刪掉這個容器重新生成一個,之前的數據也不會被保存。這樣的結果是不樂觀的,那么,在接下來的第二章,我會講解產品的設置並解決這一問題。
