MR SHIH

必幸施

Docker系列1 - 從Image到Service

| Comments

本身工作偏開發,為何要了解Docker?

最近Micro service火紅,大型服務被切成一個一個小模組,減少彼此耦合,個別跑在各自container上,方便重複部署,且彼此互相用Rest API或socket溝通。這樣帶來的好處在於可以在開發上減少後續迭代的困難,減少維運的難度,增加系統穩定性。

但要獲得這些好處,軟體在最初開始設計架構的階段就要拆分服務,一開始設計不好之後就算有docker工具那也只是把所有服務丟到同一個Container上,沒有辦法發揮docker切割服務的優勢。

所以想成為架構師,當然不能不了解這個工具。而從程式開發上來看,能夠把CI Keep in mind,有新Commit Push到develop branch,CI Server就自動執行下面步驟

  1. 打包新的Image
  2. 產生Container
  3. 執行Service
  4. Run Test Code…

能夠協助開發者確保最終程式碼在Production環境是可以正常運行,有問題可以立即發現與處理。

Docker對比傳統VM優點在於所消耗資源很小,啟動速度很快,並且兼具傳統VM打包成Image擋可以Anywhere都快速建立起一樣的Service等優點。

這篇文章主要就以上面幾點依序從包含如何製作Image,產生Container,利用Container啟動我們要的Service,最後如何匯出部署到另一台機器等一系列記錄下來。

Dockerfile與Base Image

要導入的Service是由三個部分組成

  1. Node.js專案,用Express作為Route Framework,為業務邏輯層負責API JSON的輸出
  2. Node.js專案,用Express作為Route Framework,使用Bootstrap來包含View的後台操作介面
  3. MySQL Instance

之前做法是把Node.js專案都放在同台Server,但因爲HA需要,所以也有一台Hot standby的實體機並排在HAProxy下。這裡帶來第一個問題是當要重新build環境時靠Install Guide手動安裝很不方便,解決方法便是用Dockerfile,把Install Guide所需的環境與步驟打包成一個Image。

1&2專案相同部分是Node.js專案,用Express作為Route Framework,所以可以Build一個Base Image來當基底環境,之後只要分別把/var/www置換成對應的專案檔即可。

1
2
3
4
5
6
7
8
FROM centos:7 // 指定CentOS
MAINTAINER daan.shih@gmail.com
RUN yum install -y epel-release // -y避免互動輸入
RUN yum install -y nodejs
RUN yum install -y npm
RUN npm install pm2 -g
RUN mkdir /var/www/
WORKDIR /var/www/

有了Dockerfile後就可以Build Image了

1
docker build -f /path/to/dockerfile ./

之後會得到一個Image ID,因為是只安裝npm基底就取名為npm-ready

1
docker tag [Image ID] npm-ready

現在打docker images應該可以看到類似npm-ready latest 43c671f848fd 22 hours ago 405.6 MB的資訊

以Base Image為底,加入express專案檔

這時候我們已經有一個Image檔,包含npm與必須的tools,只缺express專案檔。所以接下來就以FROM指令,指定以npm-ready:latest為底,用COPY指令把我們需要的專案檔複製到Image中。

這裡要注意第三行的PSControlCenter/這個目錄必須與Dockerfile同一個目錄(docker build後面會加上的path我們是打./),不然會出現找不到此目錄。

1
2
3
FROM npm-ready:latest
MAINTAINER daan.shih@gmail.com
COPY ControlCenter/ /var/www/

得到新Image ID後幫它補上名字方便識別

1
docker tag [Image ID] control-center

Image to Container

至此我們已經有一個包含全部所需要檔案的Image檔control-center了。現在只需要利用Image去產生一個Container,並且執行這個Container就行了。

本來產生create與執行start是兩個指令,這裡可以用run來代表連續執行上面兩個指令。類似git裡面pull是連續執行fetchmerge

1
docker run -t -i -p 9453:3000 [Image ID] /bin/bash

這裡要注意的是-p,每個運行中的container的網路環境與host是完全分離的,所以我們要從外部去訪問container,就要用這個指令來指定port的相依關係。

這個例子裡我們在host輸入127.0.0.1:9453就會訪問到container的3000 port。

建立完container後,這時可以透過下面指令拿到Container ID

1
docker ps -a // -a會列出所有container(包含停止)

運行container後,想再得到運行狀態中的container的bash shell可執行

1
docker exec -it [container-id] bash

exec指令就是對該運行中的container下達指令。

Run Service

到這裡我們已經利用Image去產生Container,並起啟動了Container,指定了Port的映射關係,最後取得該Container的Shell。這時環境已經建立起來,我們就可利用PM2把service給run起來:

1
pm2 start ./bin/www --watch --name "www"

然後在Host瀏覽器訪問127.0.0.9453,就可以看到服務啟動了。

部署在另一台機器上

把control-center這個Image用save指令匯出成.tar檔

1
docker save control-center > ./control-center.tar

接著在另一台的Dokcer instance上用load指令,指定tar檔路徑匯入成image

1
docker load < /path/to/control-center.tar

再來用docker images來確認是否有成功匯入Imagecontrol-center

之後我們便可以把該Imagecontrol-center打包匯出到其他機器,短短的兩行幾秒內就把服務給建立起來,非常輕鬆寫意的部屬方式。

1
2
docker run -t -i -p 9453:3000 [Image ID] /bin/bash
pm2 start ./bin/www --watch --name "www"

Next

下次介紹另一個Docker的重要概念:Volume。Container與Image有個很重要原則是裡面不要存Data,也就是不要把MySQL的資料給存在Image裡,所以在資料庫相關的Container裡有關Data的部分會採用另外掛載的方式。

參考FYI

Comments