在Docker中運行Node.js的Web應用

jopen 9年前發布 | 47K 次閱讀 Docker

本文是十七蟬同學撰寫的基礎實戰類博客,作者通過代碼的形式Step by step介紹了如何在Docker中運行Node.js應用。初學的同學可以一讀。

在Docker環境下搭建了Node.js的Web應用運行環境:

  • Node.js
  • MongoDB
  • Redis
  • winston和morgan,日志

    以下介紹一下搭建環境的步驟和注意事項。

    準備工作

    需要安裝Docker,我的環境是Ubuntu Serer 14.04虛擬機。如果直接用apt-get install docker.io無法獲得比較新的Docker版本。我參照這里:Docker 1.2 on Ubuntu 14.04.1,安裝了Docker 1.2版本。即使用Docker官方的第三方Ubuntu源。

    加入Docker的GPG Key
    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 

    加入Docker的源:
    sudo sh -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" 

    更新包列表:
    sudo apt-get update

    安裝Docker
    sudo apt-get install lxc-docker 

    重啟系統:
    sudo reboot

    如果執行下面命令并看到類似的結果就說明安裝成功了:
    $ docker version
    Client version: 1.2.0

    最簡單的通過Docker執行Node.js

    執行一個簡單的Node.js命令:
    node --version

    不是使用本地的Node.js,而是使用Docker,只需執行:
    $ sudo docker run -it --rm node node --version
    v0.10.33

    對于第一次運行上面命令,會出現類似:
    Unable to find image 'node' locally
    Pulling repository node
    63d7e1e1d897: Pulling dependent layers 
    511136ea3c5a: Download complete 
    36fd425d7d8a: Download complete 
    aaabd2b41e22: Download complete 
    f99c114b8ec1: Downloading [==>  
    ...

    Docker本地并沒有node的鏡像(image),需要到官網(https://hub.docker.com)上查詢這個名字的鏡像,并下載到本地。這個過程可能比較漫長,在我這里需要30分鐘左右。總之,下載完鏡像(700多MB)后,鏡像會啟動一個容器(container)。可以把鏡像看做Java的類(class),容器看做對象(object)。

    這個容器包含一個最小的可運行的輕量級的虛擬機,當然還有Node.js。

    說下命令的參數:

    docker run -it --rm node node --version

    其中--it:
    • i,容器的標準輸入保持打開
    • t,Docker分配一個偽終端(pseudo-tty)并綁定到容器的標準輸入

    --rm,運行結束后刪除容器。再后面就是我們要執行的命令。

    將Web Application跑起來

    首先,要準備一個簡單的Web Application。我這里寫好了一個簡單的應用ProtoWebApp。拿到項目文件后,先用宿主的node安裝:
    $ sudo npm install
    然后跑起來測試一下,看是否能在瀏覽器上訪問。
    下面,是用Docker里的Node.js跑這個Web Application了(在項目的根目錄下):
    sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp -v "$(pwd)":/webapp -w /webapp  node npm start

    在這里:
    -v后:分割的路徑,前者表示宿主的路徑(在這里也就是expressjs項目的主目錄),后者表示映射到Docker容器的路徑。
    -w,表示將-v映射的/webapp目錄設置為work directory,也就是運行node命令的目錄。這個設置將覆蓋Dockfiie中的設置:/Data。

    如果需要讓Docker容器跑在后臺,可以加上-d:
    sudo docker run --rm -itd -p 3000:3000 --name ProtoWebApp -v "$(pwd)":/webapp -w /webapp  node npm start

    另外,如想了解這個鏡像都包含哪些內容,可以看這里:Dockerfile/Node.js

    日志的處理

    運維中需要記錄幾種日志:
    • HTTP請求日志,為了以后分析訪問量等數據時使用
    • 應用日志,可能有錯誤或者其他調試信息,便于發現錯誤,排錯

    HTTP請求日志

    很多情況下未必用到這個,因為在Node.js的Web Appp前,可能還有Nginx,用后者做端口代理。目前的Expressjs,是4.x版本,使用的HTTP日志,是morgan。可以在app.js中看到:
    var logger = require('morgan');
    默認的日志是對接到標準輸出上的。我們希望在生產環境(production)下和開發環境(development)情況下不一樣:
  • 生產環境(production):HTTP日志記錄到文件
  • 開發環境(development):打印到標準輸出

    這需要做兩件事:
  • 通過docker命令設置為production
    1.app.js在production情況下記錄日志到文件中

    docker run命令中加入production變量設置:
    sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp -v "$(pwd)":/webapp -w /webapp -e NODE_ENV=production  node npm start

    即,-e NODE_ENV=production。
    設置保存日志到文件。找到app.js的這行:
    app.use(logger('dev'));
    改為:
    if (app.get('env') === 'development') {
    app.use(logger('dev'));
    }

if (app.get('env') === 'production') { var fs = require('fs') var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'}) app.use(logger('combined', {stream: accessLogStream})) }</pre>
這樣,當development模式打印到標準輸出,production模式下輸出到項目根目錄下的access.log文件中。源代碼見這里:https://github.com/MarshalW/ProtoWebApp/tree/m2

應用日志

這個日志是必須要有的,可幫助開發者發現和診斷問題。使用的是winston。需要將winston加入到package.json中:

"winston":""

然后引入庫:
var winston = require('winston');
再設置文件路徑(我這里是app.log):

if (app.get('env') === 'production') {
var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'});
app.use(logger('combined', {stream: accessLogStream}));

winston.add(winston.transports.File, { filename: 'app.log' }); }</pre>
Docker不需要設置什么,就可以在項目的根目錄下看到app.log文件了,如果運行沒有問題的話。

連接Redis

和Node.js鏡像類似,可以通過如下命令將Redis跑起來:
$ sudo docker run -d --name redis -p 6379:6379 redis
當Docker本地沒有redis鏡像的時候,會自動先下載該鏡像的最新版本。redis鏡像內容見:Dockerfile/redis。然后,我們可以啟動Web App:

$ sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp --link redis:redis -v "$(pwd)":/webapp -w /webapp -e NODE_ENV=production  node npm start

> ProtoWebApp@0.0.0 start /webapp > node ./bin/www

info: Hello again distributed logs Reply: OK Reply: 0 Reply: 0 2 replies: 0: hashtest 1 1: hashtest 2</pre>
比上面啟動Node.js的方式,多了:--link redis:redis,冒號前的redis表示鏡像名稱,后面的redis表示這里使用的別名。

另外,創建Client的代碼有點不同:

var redis = require("redis"),
    client = redis.createClient(6379, "redis");

其中redis是redis容器的別名。或者講究點也可以這樣:

var redisHost  = process.env.REDIS_PORT_6379_TCP_ADDR;
var redis = require("redis"),
    client = redis.createClient(6379, redisHost);

源代碼見這里:https://github.com/MarshalW/ProtoWebApp/tree/m4

連接MongoDB

執行命令,啟動mongoDB:

sudo docker run -d -p 27017:27017 -v "$(pwd)"/db:/data/db --name mongodb dockerfile/mongodb

數據庫文件保存在當前目錄下的db目錄下,如果不存在目錄的話會自動創建。

在package.json中加入:

"mongoose":""

在app.js代碼中加入:

//測試mongoDB
var mongoose = require('mongoose');
mongoose.connect('mongodb://mongodb/test');

var Cat = mongoose.model('Cat', { name: String });

var kitty = new Cat({ name: 'Zildjian' }); kitty.save(function (err) { if (err) console.log(err); console.log('meow'); });</pre>
執行Docker命令:

$ sudo docker run --rm -it -p 3000:3000 --name ProtoWebApp --link redis:redis --link mongodb:mongodb  -v "$(pwd)":/webapp -w /webapp -e NODE_ENV=production  node npm start

> ProtoWebApp@0.0.0 start /webapp > node ./bin/www

info: Hello again distributed logs js-bson: Failed to load c++ bson extension, using pure JS version Reply: OK Reply: 0 Reply: 0 2 replies: 0: hashtest 1 1: hashtest 2 meow</pre>

源代碼見這里:https://github.com/MarshalW/ProtoWebApp/tree/m5

本文收發于我的個人博客http://blog.shiqichan.com/Depl ... cker/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!