Linux中國

從 Hello World 容器進階是件困難的事情

一個縮略圖微服務

我設計的微服務在理論上很簡單。以 JPG 或者 PNG 格式在 HTTP 終端發布一張數字照片,然後獲得一個100像素寬的縮略圖。

下面是它的流程:

container-diagram-0

我決定使用 NodeJS 作為我的開發語言,使用 ImageMagick 來轉換縮略圖。

我的服務的第一版的邏輯如下所示:

container-diagram-1

我下載了 Docker Toolbox,用它安裝了 Docker 的快速啟動終端(Docker Quickstart Terminal)。Docker 快速啟動終端使得創建容器更簡單了。終端會啟動一個裝好了 Docker 的 Linux 虛擬機,它允許你在一個終端里運行 Docker 命令。

雖然在我的例子里,我的操作系統是 Mac OS X。但是 Windows 下也有相同的工具。

我準備使用 Docker 快速啟動終端里為我的微服務創建一個容器鏡像,然後從這個鏡像運行容器。

Docker 快速啟動終端就運行在你使用的普通終端里,就像這樣:

container-diagram-2

第一個小問題和第一個大問題

我用 NodeJS 和 ImageMagick 瞎搞了一通,然後讓我的服務在本地運行起來了。

然後我創建了 Dockerfile,這是 Docker 用來構建容器的配置腳本。(我會在後面深入介紹構建過程和 Dockerfile)

這是我運行 Docker 快速啟動終端的命令:

$ docker build -t thumbnailer:0.1

獲得如下回應:

docker: "build" requires 1 argument.

呃。

我估摸著過了15分鐘我才反應過來:我忘記了在末尾參數輸入一個點.

正確的指令應該是這樣的:

$ docker build -t thumbnailer:0.1 .

但是這不是我遇到的最後一個問題。

我讓這個鏡像構建好了,然後我在 Docker 快速啟動終端輸入了 run 命令來啟動容器,名字叫 thumbnailer:0.1:

$ docker run -d -p 3001:3000 thumbnailer:0.1

參數 -p 3001:3000 讓 NodeJS 微服務在 Docker 內運行在埠3000,而綁定在宿主主機上的3001。

到目前看起來都很好,對吧?

錯了。事情要馬上變糟了。

我通過運行 docker-machine 命令為這個 Docker 快速啟動終端里創建的虛擬機指定了 ip 地址:

$ docker-machine ip default

這句話返回了默認虛擬機的 IP 地址,它運行在 Docker 快速啟動終端里。在我這裡,這個 ip 地址是 192.168.99.100。

我瀏覽網頁 http://192.168.99.100:3001/ ,然後找到了我創建的上傳圖片的網頁:

container-diagram-3

我選擇了一個文件,然後點擊上傳圖片的按鈕。

但是它並沒有工作。

終端告訴我他無法找到我的微服務需要的 /upload 目錄。

現在,你要知道,我已經在此耗費了將近一天的時間-從浪費時間到研究問題。我此時感到了一些挫折感。

然後靈光一閃。某人記起來微服務不應該自己做任何數據持久化的工作!保存數據應該是另一個服務的工作。

所以容器找不到目錄 /upload 的原因到底是什麼?這個問題的根本就是我的微服務在基礎設計上就有問題。

讓我們看看另一幅圖:

container-diagram-4

我為什麼要把文件保存到磁碟?微服務按理來說是很快的。為什麼不能讓我的全部工作都在內存里完成?使用內存緩衝可以解決「找不到目錄」這個問題,而且可以提高我的應用的性能。

這就是我現在所做的。下面是我的計劃:

container-diagram-5

這是我用 NodeJS 寫的在內存運行、生成縮略圖的代碼:

// Bind to the packages
var express = require('express');
var router = express.Router();
var path = require('path'); // used for file path
var im = require("imagemagick");

// Simple get that allows you test that you can access the thumbnail process
router.get('/', function (req, res, next) {
 res.status(200).send('Thumbnailer processor is up and running');
});

// This is the POST handler. It will take the uploaded file and make a thumbnail from the 
// submitted byte array. I know, it's not rocket science, but it serves a purpose
router.post('/', function (req, res, next) {
 req.pipe(req.busboy);
 req.busboy.on('file', function (fieldname, file, filename) {
   var ext = path.extname(filename)

   // Make sure that only png and jpg is allowed 
   if(ext.toLowerCase() != '.jpg' && ext.toLowerCase() != '.png'){
     res.status(406).send("Service accepts only jpg or png files");
   }

   var bytes = [];

   // put the bytes from the request into a byte array 
   file.on('data', function(data) {
     for (var i = 0; i < data.length; ++i) {
       bytes.push(data[i]);
     }
     console.log(&apos;File [&apos; + fieldname + &apos;] got bytes &apos; + bytes.length + &apos; bytes&apos;);
   });

   // Once the request is finished pushing the file bytes into the array, put the bytes in 
   // a buffer and process that buffer with the imagemagick resize function
   file.on(&apos;end&apos;, function() {
     var buffer = new Buffer(bytes,&apos;binary&apos;);
     console.log(&apos;Bytes  got &apos; + bytes.length + &apos; bytes&apos;);

     //resize
     im.resize({
         srcData: buffer,
         height: 100
     }, function(err, stdout, stderr){
       if (err){
         throw err;
       }
       // get the extension without the period
       var typ = path.extname(filename).replace(&apos;.&apos;,&apos;&apos;);
       res.setHeader("content-type", "image/" + typ);
       res.status(200);
       // send the image back as a response
       res.send(new Buffer(stdout,&apos;binary&apos;));
     });
   });
 });
});

module.exports = router;

好了,一切回到了正軌,已經可以在我的本地機器正常工作了。我該去休息了。

但是,在我測試把這個微服務當作一個普通的 Node 應用運行在本地時...

Containers Hard

它工作的很好。現在我要做的就是讓它在容器裡面工作。

第二天我起床後喝點咖啡,然後創建一個鏡像——這次沒有忘記那個"."!

$ docker build -t thumbnailer:01 .

我從縮略圖項目的根目錄開始構建。構建命令使用了根目錄下的 Dockerfile。它是這樣工作的:把 Dockerfile 放到你想構建鏡像的地方,然後系統就默認使用這個 Dockerfile。

下面是我使用的Dockerfile 的內容:

FROM ubuntu:latest
MAINTAINER bob@CogArtTech.com

RUN apt-get update
RUN apt-get install -y nodejs nodejs-legacy npm
RUN apt-get install imagemagick libmagickcore-dev libmagickwand-dev
RUN apt-get clean

COPY ./package.json src/

RUN cd src && npm install

COPY . /src

WORKDIR src/

CMD npm start

這怎麼可能出錯呢?

第二個大問題

我運行了 build 命令,然後出了這個錯:

Do you want to continue? [Y/n] Abort.

The command &apos;/bin/sh -c apt-get install imagemagick libmagickcore-dev libmagickwand-dev&apos; returned a non-zero code: 1

我猜測微服務出錯了。我回到本地機器,從本機啟動微服務,然後試著上傳文件。

然後我從 NodeJS 獲得了這個錯誤:

Error: spawn convert ENOENT

怎麼回事?之前還是好好的啊!

我搜索了我能想到的所有的錯誤原因。差不多4個小時後,我想:為什麼不重啟一下機器呢?

重啟了,你猜猜結果?錯誤消失了!(LCTT 譯註:萬能的「重啟試試」)

繼續。

將精靈關進瓶子里

跳回正題:我需要完成構建工作。

我使用 rm 命令刪除了虛擬機里所有的容器。

$ docker rm -f $(docker ps -a -q)

-f 在這裡的用處是強制刪除運行中的鏡像。

然後刪除了全部 Docker 鏡像,用的是命令 rmi:

$ docker rmi if $(docker images | tail -n +2 | awk &apos;{print $3}&apos;)

我重新執行了重新構建鏡像、安裝容器、運行微服務的整個過程。然後過了一個充滿自我懷疑和沮喪的一個小時,我告訴我自己:這個錯誤可能不是微服務的原因。

所以我重新看到了這個錯誤:

Do you want to continue? [Y/n] Abort.

The command &apos;/bin/sh -c apt-get install imagemagick libmagickcore-dev libmagickwand-dev&apos; returned a non-zero code: 1

這太打擊我了:構建腳本好像需要有人從鍵盤輸入 Y! 但是,這是一個非交互的 Dockerfile 腳本啊。這裡並沒有鍵盤。

回到 Dockerfile,腳本原來是這樣的:

RUN apt-get update
RUN apt-get install -y nodejs nodejs-legacy npm
RUN apt-get install imagemagick libmagickcore-dev libmagickwand-dev
RUN apt-get clean

第二個apt-get 忘記了-y 標誌,它用於自動應答提示所需要的「yes」。這才是錯誤的根本原因。

我在這條命令後面添加了-y

RUN apt-get update
RUN apt-get install -y nodejs nodejs-legacy npm
RUN apt-get install -y imagemagick libmagickcore-dev libmagickwand-dev
RUN apt-get clean

猜一猜結果:經過將近兩天的嘗試和痛苦,容器終於正常工作了!整整兩天啊!

我完成了構建工作:

$ docker build -t thumbnailer:0.1 .

啟動了容器:

$ docker run -d -p 3001:3000 thumbnailer:0.1

獲取了虛擬機的IP 地址:

$ docker-machine ip default

在我的瀏覽器裡面輸入 http://192.168.99.100:3001/

上傳頁面打開了。

我選擇了一個圖片,然後得到了這個:

container-diagram-7

工作了!

在容器裡面工作了,我的第一次啊!

這讓我學到了什麼?

很久以前,我接受了這樣一個道理:當你剛開始嘗試某項技術時,即使是最簡單的事情也會變得很困難。因此,我不會把自己當成最聰明的那個人,然而最近幾天嘗試容器的過程就是一個充滿自我懷疑的旅程。

但是你想知道一些其它的事情嗎?這篇文章是我在凌晨2點完成的,而每一個受折磨的時刻都值得了。為什麼?因為這段時間你將自己全身心投入了喜歡的工作里。這件事很難,對於所有人來說都不是很容易就獲得結果的。但是不要忘記:你在學習技術,運行世界的技術。

P.S. 了解一下Hello World 容器的兩段視頻,這裡會有 Raziel Tabib』s 的精彩工作內容。

千萬被忘記第二部分...

via: https://deis.com/blog/2015/beyond-hello-world-containers-hard-stuff

作者:Bob Reselman 譯者:Ezio 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國