1. 程式人生 > >通過做一個基於Node的微伺服器來學習Docker

通過做一個基於Node的微伺服器來學習Docker

如果你正準備著手學習 Docker,別再觀望,動起手來吧!

在這篇文章中,我將告訴你 Docker 是如何工作的?使用中會遇到什麼問題?如何通過 Docker 完成一個基本的開發任務——構建一個微伺服器。

我們將以一臺配有 Node.js 服務和 MySQL 後臺的伺服器為例,從在本地執行程式碼開始,完成一個執行著微服務和資料庫的容器。

什麼是 Docker ?

從本質上來說,Docker 是一種軟體,讓使用者建立映象檔案(就像虛擬機器中的模板),然後在容器中執行這個映象的例項。

Docker 維護著有著大量映象的儲存庫,名字叫 Docker Hub ,你可以將它作為嘗試映象的起始點,或者用來免費儲存你的映象。你可以安裝 Docker ,選擇你喜歡的映象,然後在容器中執行它的例項。

本文我們將介紹建立映象、從映象建立容器等一系列內容。

安裝 Docker

如果你想跟上本文的節奏,那麼你需要安裝 Docker 。

如果你是 Mac 或者 Windows 作業系統,那麼你需要使用虛擬機器。我在 Mac OS X 上使用 Parallels 安裝 Ubuntu 虛擬機器來應付大多數的開發任務。因為它支援快照功能,當你做實驗的時候,他可以方便的將破壞了的環境恢復回去。

試試看

輸入以下命令:

Shell
1 docker run-it ubuntu

一段時間後,你將會看到如下提示:

Shell
1 root@719059da250d:/#

試試如下的命令,然後退出容器:

Shell
1234567 root@719059da250d:/# lsb_release -a  No LSB modules are available.Distributor ID:Ubuntu  Description:Ubuntu14.04.4LTS  Release:14.04Codename:trusty  root@719059da250d:/# exit

這看起來沒什麼,但是其實在後臺發生了很多事情。

你看到的是在你的機器上執行著的 Ubuntu 的隔離容器環境裡的 bash shell。這個環境完全歸你所有——可以在上面安裝軟體,執行軟體,可以做任何你想做的事情。

下圖表明瞭剛剛發生了什麼(圖來自於《 理解 Docker 架構 》一文):

Docker Run Flow

1. 列出如下的 Docker 指令:

  • docker : 執行 docker 客戶端
  • run : 執行一個新的容器
  • -it :讓容器帶有“互動終端”的一個引數
  • ubuntu : 容器所依賴的基礎映象

2. 在主機(我們的機器)上執行的 docker 服務檢查本地是否有所請求的映象拷貝——這裡發現沒有。

3. docker 服務檢查公有儲存庫(the docker hub),看是否有可用的名為 ubuntu 的映象——這裡發現有。

4. docker 服務下載映象,將其儲存到本地快取裡(為了下一次直接使用)。

5. docker 服務基於 ubuntu 映象建立新的容器。

Try any of these:

試試下面這些命令:

Shell
123 docker run-it haskell  docker run-it java  docker run-it python

我們沒準備使用 Haskell ,但是你可以看到,搭建一個環境是多麼容易。

構建自己的映象也很輕鬆,可以在這上面安裝應用程式或者服務,可以是資料庫,或者是其他你需要的。隨後就可以在任意安裝了 Docker 的機器上執行它們——要保證映象是相同的、可預測的方式在每臺機器上執行。我們可以將軟體及其執行所需的環境整體構建成程式碼,並且輕鬆部署。

讓我們以一個簡單微伺服器為例。

概述

我們將要用 Node.js 和 MySQL 建立一個讓我們管理郵件地址到電話號碼目錄的微服務。

開始

要完成本地開發,需要安裝MySQL,並且建立一個測試資料庫…

…搖頭。

建立本地資料庫,並且上面執行指令碼,這很容易,但是可能會帶來一些問題。很多不受控制的事情開始了。它可能工作,我們甚至可以通過提交進程式碼庫的 shell 指令碼來控制這些步驟,但是如果其他開發人員已經安裝了 MySQL 了呢?如果他們的資料庫已經使用了我們想要建立的名稱  ‘users’  了呢?

第一步:在 Docker 中建立一個數據庫測試伺服器

這是很好的 Docker 應用場景。我們可能不想在 Docker 裡執行生產環境資料庫(比如可能會使用 Amazon RDS),但是可以使用 Docker 容器建立一個乾淨的 MySQL 資料庫做開發——讓我們的開發及其保持乾淨,並且保證所有東西都在控制中,並且可重複使用。

執行下面的命令:

Shell
1 docker run--name db-d-eMYSQL_ROOT_PASSWORD=123-p3306:3306mysql:latest

該命令啟動一個執行著的 MySQL 例項,通過 3306 埠訪問,root 密碼為 123 。

  1.  docker run 告訴引擎,使用者想要執行一個映象(在最後傳入的是映象,mysql:latest
  2.  –name db 將整個容器命名為 db 。
  3.  -d detach,在後臺執行容器。
  4.  -e MYSQL_ROOT_PASSWORD=123(或者是 –env)環境變數 – 引數告訴 docker 所提供的環境變數。這之後跟著的變數正是 MySQL 映象檢查且用來設定的預設 root 密碼。
  5.  -p 3306:3306(或者 --publish) 告訴引擎使用者想要將容器內的3306埠對映到外部的3306埠上。

最後一部分很重要——即使這是 MySQL 的預設埠,如果使用者不顯式告訴 docker 想要對映的埠,docker 就會阻塞該埠的訪問(因為容器預設是隔離的,直到使用者告訴 docker 想要訪問它們)。

該命令返回值是容器 id,這是容器的指標,使用者可以用它來停止容器,向容器傳送命令等等。讓我們看看正在執行的是哪些容器:

Shell
123 $docker psCONTAINER IDIMAGE...NAMES36e68b966fd0mysql:latest...db

關鍵的資訊是容器 ID,映象和名稱。連線到這個映象看看裡面有什麼:

Shell
1234567891011121314 $docker exec-it db/bin/bashroot@36e68b966fd0:/# mysql -uroot -p123  mysql>show databases;+--------------------+|Database|+--------------------+|information_schema|+--------------------+1rows inset(0.01sec)mysql>exitBye  root@36e68b966fd0:/# exit

下面這麼做也很有意思:

1. docker exec -it db :告訴 docker 使用者想要在名為 db 的容器裡執行一個命令(我們也可以使用 id,或者 id 的前幾個字母)。 -it 確保使用者有互動型終端。

2. mysql -uroot -p123 :我們實際在容器裡作為程序執行的命令,這裡是 mysql 客戶端。

我們可以建立資料庫,表,使用者,其他你需要的等等。

打包測試資料庫

在容器內執行 MySQL 需要一些 Docker 技巧,但是讓我們先打住,看看服務。現在,使用指令碼建立一個 test-database 目錄來啟動資料庫,停止資料庫以及搭建測試資料:

Shell
123 test-databasesetup.sqltest-databasestart.shtest-databasestop.sh

啟動指令碼很簡單:

Shell
123456789101112131415161718 #!/bin/sh# Run the MySQL container, with a database named 'users' and credentials# for a users-service user which can access it.echo"Starting DB..."docker run--name db-d-eMYSQL_ROOT_PASSWORD=123-eMYSQL_DATABASE=users-eMYSQL_USER=users_service-eMYSQL_PASSWORD=123-p3306:3306mysql:latest# Wait for the database service to start up.echo"Waiting for DB to start up..."docker exec db mysqladmin--silent--wait=30-uusers_service-p123 ping||exit1# Run the setup script.echo"Setting up initial data..."docker exec-idb mysql-uusers_service-p123 users<setup.sql

該指令碼在一個分離容器鍾執行資料庫映象(比如,在後臺執行),建立了一個使用者來訪問 users 資料庫,然後等待資料庫伺服器啟動,隨後執行 setup.sql 指令碼來設定初始資料。

setup.sql 的內容是:

MySQL
123456 createtabledirectory(user_idINTNOT NULLAUTO_INCREMENTPRIMARY KEY,emailTEXT,phone_numberTEXT);insertintodirectory(email,phone_number)values('[email protected]','+1 888 123 1111');insertintodirectory(email,phone_number)values('[email protected]','+1 888 123 1112');insertintodirectory(email,phone_number)values('[email protected]','+1 888 123 1113');insertintodirectory(email,phone_number)values('[email protected]','+1 888 123 1114');insertintodirectory(email,phone_number)values('[email protected]','+1 888 123 1115');

stop.sh 指令碼會停止容器並且刪除容器(docker 預設會保留容器,這樣能夠快速重啟,本示例中並不需要這樣):

Shell
1234 #!/bin/sh# Stop the db and remove the container.docker stop db&&docker rmdb

之後會進一步簡化這個過程,讓它更加順暢。在 repo 裡的 step1 分支裡檢視這一階段的程式碼。

第二步:用 Node.js 建立一個微服務

本文的主題是 Docker 的學習,因此並不會花太多篇幅講解 Node.js 的微服務。只是強調一些重點。

Shell
12345678 test-database/# contains the code seen in Step 1  users-service/# root of our node.js microservice  -package.json# dependencies, metadata-index.js# main entrypoint of the app-api/# our apis and api tests-config/# config for the app-repository/# abstraction over our db-server/# server setup code

讓我們仔細看看這一部分。首先看看這個程式碼庫。最好將你的資料庫訪問封裝和抽象成一些類,允許模擬它來實現測試目的:

JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 //  repository.js////  Exposes a single function - 'connect', which returns//  a connected repository. Call 'disconnect' on this object when you're done.'use strict';varmysql=require('mysql');//  Class which holds an open connection to a repository//  and exposes some simple functions for accessing data.classRepository{constructor(connection){this.connection=connection;}getUsers(){returnnewPromise((resolve,reject)=>{this.connection.query('SELECT email, phone_number FROM directory',(err,results)=>{if(er