Node server with Docker

我發現Docker基本原則是這樣的,就是

一個container只運行一個服務
一個container只運行一個服務
一個container只運行一個服務

(很重要所以說三遍)

所以以前我們可能會把 nginx + node 全部都一起裝在一台VM上,
但現在如果要把它docker化,就變成要開兩個container,一個nginx一個node server的服務。
而不是直接開一個ububtu(之類的)的container然後把全部東西灌在上面build成一個image。

所以如果要把上述的東西docker化,檔案結構會長成這樣

1
2
3
4
5
6
7
8
/ (專案根目錄)
| -- docker-compose.yml
` -- node
| | -- index.js
| | -- Dockerfile
`-- nginx
| | -- nginx.conf
| | -- Dockerfile

完整範例可以參考我的 github

Docker安裝走這邊:(ubuntu 16.04)
https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04

一些名詞簡介

OK,所以到底啥是 Dockerfile ? 啥又是 docker-compose ?

Dockerfile

其實它就是用來 build image 的步驟描述檔。

如果你要建立image檔,有兩種方式:

  1. 先下載一個base image (像是ubuntu or centos那種),就像是開一台VM一樣,
    然後在你開起來的那個container裡面做平常你會在VM裡面做的事情,
    再把這個container的狀態commit上去變成一個image檔。

  2. 把你所有要做的事情寫成一個 Dockerfile,然後直接下指令build就好!!

我覺得寫 Dockerfile 的好處是,你不用真的把你的image推到像是docker hub的地方,
而是直接給別人你的 Dockerfile + 需要的源碼讓人家去build就好
感覺這樣進版本庫也比較輕量(?)

docker-compose

一開始提到了,Docker的基本原則是,一個container只負責一項服務
但基本上不太可能一個APP只用到一個服務 (像是本文就用到了 node + nginx)
所以需要 docker-compose 來一次幫你把你的 APP (服務群?) 開好
(不然也是可以自己先把所有的image build好再一個個開起來啦只是會開到起笑)

那怎麼知道要開哪些服務哩?

這時候就需要用到 docker-compose.yml 這個設定檔了
而這個設定檔怎麼寫,下面會有範例所以這邊不多說了

所以兩者關係是這樣的:

一個 Dockerfile 可以build成一個 image
一個 container 則是一個 image 的實體,
docker-compose.yml 就是描述很多個 container (or service) 之間的交互作用。(吧)

好像沒有很總結到的感覺XDDDD,直接往下看程式比較快(ㄜ)

node

index.js

(毫無反應,就只是個node server)

陽春到極致的簡單範例:

1
2
3
4
5
6
7
8
9
10
const http = require('http')
const port = process.env.PORT || 8080
const requestHandler = function handler(req, res) {
res.end("Hello! This is a simple node server!")
}
const server = http.createServer(requestHandler)
server.listen(port, () => console.log(`Node simple server is now listening on *:${port}`))

Dockerfile

  • building from ubuntu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM ubuntu:trusty
# install curl & node
RUN apt-get update && \
apt-get -y install curl && \
curl -sL https://deb.nodesource.com/setup_7.x | bash - && \
apt-get install -y nodejs
WORKDIR /src
ADD . /src
EXPOSE 8080
CMD ["node", "/src/index.js"]
  • building from node (with pm2)

直接用官方的node image環境當底,
這個方法build起來速度快很多XD

1
2
3
4
5
6
7
8
9
10
FROM node:7.10.1
RUN npm install pm2 -g
WORKDIR /src
COPY . /src
EXPOSE 8080
CMD ["pm2-docker", "process.yml"]
  • process.yml

這個是 pm2 在用的啦,順便列一下

1
2
3
4
5
apps:
- script : './index.js'
name : 'node-server'
exec_mode: 'cluster'
instances: 4

EXPOSE 好像只是讓大家知道 container 打算要使用哪個port,
真正要運行的時候,還是需要加上類似-p xxxx:8080這樣的東西。

但因為我們不會直接build images出來,而是透過 docker-compose 來建立整套服務,
所以到時候寫在 docker-compose.yml裡面就可以了。

手動 build image 並啟動

假如沒有要透過 docker-compose 而只是想要啟用單一服務的話,
也可以先手動建立image然後啟動。

Dockerfile 就是拿來build image用的,
所以我們現在其實已經擁有所有需要的素材了。

先切到 node 本身的目錄下面:

1
$ cd your-project/node

建立image:

1
$ docker build -t your-namespace/node .

build完以後執行啟動

1
$ docker run -d -p 3000:8080 your-namespace/node

連到 *:3000 就可以看到我們的node伺服器正常運作中拉,Hooray!

nginx

nginx.conf

這 nginx 的 config 也是直接複製網路上範例,(所以我沒有深究它在幹嘛XD)
注意連到 node server 的服務的時候用的是 http://nodejs:8080
其中 nodejs 是寫在 docker-compose.yml裡面 node server的 name
port 則是當初在 node Dockerfile 裡面EXPOSE指定的 port

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
worker_processes 4;
events { worker_connections 1024; }
http {
server {
listen 80;
location / {
proxy_pass http://nodejs:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}

nginx config 參數配置參考

Dockerfile

1
2
3
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf

docker-compose

docker-compose.yml

首先在根目綠下面加上 docker-compose.yml 這個檔案
它主要就是來告訴 docker-compose 說要開哪些服務
然後這些服務彼此之間的關係等等

1
2
3
4
5
6
7
8
9
10
nginx:
build: ./nginx
links:
- nodejs:nodejs
ports:
- "80:80"
nodejs:
build: ./node
ports:
- "8080" # 對外沒有port!!!!! 所以外面的人是無法直接連到 node 服務的

啟動

在專案根目錄下面執行:

1
$ docker-compose up -d

注意 docker-compose 是要另外裝的,所以沒有要記得裝。

詳細安裝教學:(ubuntu 16.04)
https://www.digitalocean.com/community/tutorials/how-to-install-docker-compose-on-ubuntu-16-04

然後就好啦!!!

更新

假如更新程式的話怎麼辦?????
像是我的 node 裡面的程式碼如果更新了要怎麼辦???

因為我這邊用的是 COPY,所以基本上程式碼算是和docker分開管理(的吧)

我現在的想像是整個project都進版本庫管理
然後在專案底下執行 git pull or svn up 之類的完畢後,使用

1
$ docker-compose up --build -d

來建立新版本並更新。

(而且我發現這可以在已經執行中的狀態下使用,可以無痛更新ㄚ!!!!!)

注意 --build 這個參數,如果沒有用的話他就會使用已經建立過的版本下去跑,
這樣你更新的程式碼就完全沒用了~

其他很多像是 ADDCOPY 差在哪啦,什麼樣的更新方式比較OK啦
這類的細節和討論我不是很清楚,
總之先可以跑起來就好了XD (欸)

參考資料