1. 程式人生 > >docker實戰——在測試中使用Docker

docker實戰——在測試中使用Docker

enable max 指定 roc 網站 寫入 seq essential broadcast

在之前幾章中介紹的都是Docker的基礎知識,了解什麽是鏡像,docker基本的啟動流程,以及如何去運作一個容器等等。

接下來的幾個章節將介紹如何在實際開發和測試過程中使用docker。

將Docker作為本地Web開發環境是使用Docker的一個最簡單的場景。這個環境可以完全重現生產環境,保證開發環境和部署環境一致。下面從將Nginx安裝到容器來架構一個簡單的網站開始。

使用Docker測試靜態網站

## 創建一個sample的鏡像目錄並創建一個Dockerfile
# mkdir sample
# cd sample/
# touch Dockerfile

## 在sample 目錄中創建一個叫nginx的目錄,並用來存放nginx的配置文件
# mkdir nginx && cd nginx
# vim global.conf
server {
        listen          0.0.0.0:80;
        server_name     _;

        root            /var/www/html/website;
        index           index.html index.htm;

        access_log      /var/log/nginx/default_access.log;
        error_log       /var/log/nginx/default_error.log;
}

# vim nginx.conf
user www-data;
worker_processes 4;
pid /run/nginx.pid;
daemon off;

events {  }

http {
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;
  gzip on;
  gzip_disable "msie6";
  include /etc/nginx/conf.d/*.conf;
}

接下來我們編輯Dockerfile

# cd ..
# cat Dockerfile 
FROM ubuntu:14.04
MAINTAINER BOurbon Tian "[email protected]"
ENV REFRESHED_AT 2017-05-25
RUN apt-get update
RUN apt-get -y -q install nginx
RUN mkdir -p /var/www/html
ADD nginx/global.conf /etc/nginx/conf.d/
ADD nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

這個簡單的Dockerfile內容包括以下幾項:

  • 選擇基礎鏡像;
  • 安裝nginx;
  • 在容器中創建一個/var/www/html的目錄;
  • 將我們本地創建的nginx配置文件添加到鏡像中;
  • 公開鏡像的80端口。

接下來通過docker build命令構建新的鏡像:

# docker build -t="test/nginx" .
# docker images
REPOSITORY                                TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
test/nginx                                latest              f495ccf93291        43 minutes ago      231.3 MB

創建一個靜態網站

在sample目錄中創建一個名為website的目錄,並創建一個靜態的測試頁面放到website目錄中。

# cd sample
# mkdir website && cd website
# vim index.html
<head>
<title>Test website</title>
</head>
<body>
<h1>This is a test website</h1>
</body>

通過docker run創建一個新的容器:

# docker run -d -p 80 --name website -v /opt/sample/website:/var/www/html/website test/nginx nginx
  • -v選項,將宿主機的目錄作為卷,掛載到容器裏。

卷在Docker裏非常重要,也很有用。卷是在一個或者多個容器內被選定的目錄,可以繞過分層的聯合文件系統(Union File System),為Docker提供持久數據或者共享數據。這意味著對卷的修改會直接生效,並繞過鏡像。當提交或者創建鏡像時,卷不被包含在鏡像裏。卷可以在容器間共享。即便容器停止,卷裏的內容依舊存在。在後面的章節會看到如何使用卷來管理數據。

當我們因為某些原因不想把應用或者代碼構建到鏡像中時,就體現出了卷的價值。例如:

  • 希望同時對代碼做開發和測試;
  • 代碼改動很頻繁,不想再開發過程中重構鏡像;
  • 希望在多個容器間共享代碼。

參數-v指定了卷的目錄(本地宿主機的目錄)和容器裏的目錄,這兩個目錄通過:來分隔。如果目的目錄不存在,Docker會自動創建一個。也可以通過在目的目錄的後面加上rw或者ro來指定目的目錄的讀寫狀態如:

# docker run -d -p 80 --name website -v /opt/sample/website:/var/www/html/website:ro test/nginx nginx

這將使目的目錄/var/www/html/website變成只讀狀態。

# docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                   NAMES
9e13aebecae3        test/nginx          "nginx"             2 hours ago         Up 2 hours          0.0.0.0:32775->80/tcp   website 

通過docker ps命令查看正在運行的容器,可以看到名為website容器正處於活躍狀態,其80端口被映射到本地的32775端口。

如果在docker宿主機上瀏覽32775端口,就會到這個測試靜態頁:

技術分享

接下來,修改宿主機上的index.html文件再次查看網站

# vi index.html
<head>
<title>Test website</title>
</head>
<body>
<h1>This is a test website for Docker</h1>
</body>

刷新瀏覽器,可以看到,Sample網站已經更新了。

技術分享

顯然這個修改太簡單了,不過可以看出,更復雜的修改也並不困難。更重要的是,你正在測試網站的運行環境,完全是生產環境裏的真實狀態。現在可以給每個用於生產的網站服務環境(如Apache、Nginx)配置一個容器,給不同開發框架的運行環境(如PHP或者Ruby on Rails)配置一個容器,或者給後端數據庫配置一個容器,等等。

使用Docker構建並測試Web應用程序

看一個更復雜的例子,接下來我們將要測試一個基於Sinatra的Web應用程序,而不是靜態網站。下面的例子會演示如何在Docker裏開發並測試應用程序。這個應用程序會接收輸入參數,並使用JSON散列輸出這些參數。

# mkdir -p /opt/sinatra
# cd /opt/sinatra

# vim Dockerfile
FROM ubuntu:latest
MAINTAINER Bourbon Tian "[email protected]"
ENV REFRESHED_AT 2017-05-25
RUN apt-get update
RUN apt-get -y install ruby ruby-dev build-essential redis-tools
RUN gem install --no-rdoc --no-ri sinatra json redis
RUN mkdir -p /opt/webapp
EXPOSE 4567
CMD ["/opt/webapp/bin/webapp"]

這裏基於ubuntu創建了一個新的鏡像,安裝了Ruby和RubyGem,並且使用gem命令安裝了sinatra、json和redis包。還創建了一個目錄來存放新的Web應用程序,並公開了WEBrick的默認端口4567。最後,使用CMD指定/opt/webapp/bin/webapp作為Web應用程序的啟動文件。

## 通過docker build構建新的鏡像
# docker build -t="test/sinatra" .

## 構建應用程序代碼
# mkdir -p webapp/bin
# mkdir -p webapp/lib
# vim webapp/bin/webapp
#!/usr/bin/ruby
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
require ‘app‘
App.run!

# chmod +x webapp/bin/webapp


# vim webapp/lib/app.rb
require "rubygems"
require "sinatra"
require "json"
class App < Sinatra::Application
  set :bind, ‘0.0.0.0‘
  get ‘/‘ do
    "<h1>DockerBook Test Sinatra app</h1>"
  end
  post ‘/json/?‘ do
    params.to_json
  end
end

通過docker run命令從鏡像創建一個新的容器:

# docker run -d -p 4567 --name webapp -v /opt/sinatra/webapp:/opt/webapp test/sinatra
5f1b5d2069eb443427c8f13318fb115ec6fb4a23f71877ada982ba1c2bbfee61

這裏通過之前構建的test/sinatra鏡像,創建了一個名為webapp的容器。指定了一個新卷/opt/sinatra/webapp來存放新的Sinatra Web應用程序,並將到這個卷掛載到webapp容器的/opt/webapp目錄。

我們可以通過以下方式查看容器的狀態及一些基本信息:

# docker logs -f webapp
[2017-06-08 05:28:13] INFO  WEBrick 1.3.1
[2017-06-08 05:28:13] INFO  ruby 2.3.1 (2016-04-26) [x86_64-linux-gnu]
== Sinatra (v2.0.0) has taken the stage on 4567 for development with backup from WEBrick
[2017-06-08 05:28:13] INFO  WEBrick::HTTPServer#start: pid=1 port=4567

# docker top webapp
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                76521               1360                0                   13:28               ?                   00:00:00            /usr/bin/ruby /opt/webapp/bin/webapp

# docker port webapp 4567
0.0.0.0:32769

現在可以使用curl命令來測試這個應用程序:

# curl -i -H ‘Accept: application/json‘ -d ‘name=Foo&status=Bar‘ http://localhost:32769/json
HTTP/1.1 200 OK 
Content-Type: text/html;charset=utf-8
Content-Length: 29
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Thu, 08 Jun 2017 05:39:18 GMT
Connection: Keep-Alive

{"name":"Foo","status":"Bar"}

可以看到,我們個Sinatra應用程序傳入一些參數,並看到這些參數轉化成JSON散列後的輸出:{"name":"Foo","status":"Bar"}

然後試試看,添加一個服務(這個服務運行在另一個容器裏),能不能把當前的示例應用程序容器擴展為真正的應用程序棧。

構建Redis鏡像和容器

現在我們將要擴展Sinatra應用程序,加入Redis後端數據庫,並在Redis數據庫中存儲輸入的參數。為了達到這個目的,要構建全新的鏡像和容器來運行Redis數據庫。之後,要利用Docker的特性來關聯兩個容器。

為了構建Redis數據庫,要創建一個新的鏡像。從一個新的Dockerfile開始,逐步讓Redis運行在Docker裏:

## 創建Dockerfile文件
# cat Dockerfile 
FROM ubuntu:latest
MAINTAINER Bourbon Tian "[email protected]"
ENV REFRESHED_AT 2017-05-26
RUN apt-get update
RUN apt-get -y install redis-server redis-tools
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
CMD []

## 構建鏡像並創建容器
# docker build -t="test/redis" .
# docker run -d -p 6379 --name redis test/redis

## 測試redis容器是否正常運行
# docker port redis 6379
0.0.0.0:32770
# redis-cli -h 127.0.0.1 -p 32770
redis 127.0.0.1:32770> 

這裏使用redis客戶端連接到127.0.0.1的32770端口,驗證Redis服務器正在正常工作。

連接到Redis容器

現在來更新Sinatra應用程序,讓其連接到Redis並存儲傳入的參數。為此,需要能夠與Redis服務器對話。要做到這一點,可以有幾種方法。來看看每種方法的優劣。

第一種方法涉及Docker自己的網絡棧。到目前為止,我們看到的Docker容器都是公開端口並綁定到本地網絡接口的,這樣可以把容器裏的服務在本地Docker宿主機所在的外部網絡上(比如,把容器的80端口綁到本地宿主機的更高端口上)公開。除了這種用法,Docker這個特性還有種用法我們沒見過,那就是內部網絡。

在安裝Docker時,會創建一個新的網絡接口,名字是docker0。每個Docker容器都會在這個接口上分配一個IP地址。

# ifconfig docker0
docker0   Link encap:Ethernet  HWaddr 56:84:7A:FE:97:99  
          inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6479 errors:0 dropped:0 overruns:0 frame:0
          TX packets:7662 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:361421 (352.9 KiB)  TX bytes:26025103 (24.8 MiB)

docker0接口有符合RFC1918的私有IP地址,範圍是172.16~172.30(如果子網被占用,Docker會在172.16~172.30這個範圍內嘗試創建子網。)。接口本身的地址是172.17.42.1是這個Docker網絡的網關地址,也是所有Docker容器的網關地址。接口docker0是一個虛擬的以太網橋,用於連接容器和本地宿主網絡。如果進一步的查看Docker宿主機的其他網絡接口會發現一些列名字以veth開頭的接口,

vethfcf286d Link encap:Ethernet  HWaddr C2:E7:4B:50:5D:99  
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
...

vethff609e8 Link encap:Ethernet  HWaddr F2:D0:D2:D1:3A:8D  
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
...

Docker每創建一個容器就會創建一組互聯的網絡接口。這組接口就像管道的兩端。這組接口其中一端作為容器的eth0接口,而另一端統一命名為類似vethfcf286d這種名字,作為宿主機的一個端口。這裏可以吧veth接口認為是虛擬網線的一端。這個虛擬網線一端插在名為docker0的網橋上,另一端插在容器裏。通過把每個veth*接口綁定到docker0網橋,Docker創建了一個虛擬子網,這個子網由宿主機和所有的Docker容器共享。

[email protected]:/# ifconfig 
eth0      Link encap:Ethernet  HWaddr 02:42:ac:11:00:03  
          inet addr:172.17.0.3  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8062 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6398 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:24717646 (24.7 MB)  TX bytes:438531 (438.5 KB)

可以看到,Docker給容器分配了IP地址172.17.0.3作為宿主虛擬接口的另一端。這樣就能夠讓宿主網絡和容器互相通信了。讓我們從容器內跟蹤對外通信的路由,看看是如何建立連接的:

[email protected]:/# apt-get install -yqq traceroute
...

[email protected]:/# traceroute www.baidu.com
traceroute to www.baidu.com (180.97.33.107), 30 hops max, 60 byte packets
 1  172.17.42.1 (172.17.42.1)  0.026 ms  0.006 ms  0.005 ms
 2  172.30.10.254 (172.30.10.254)  3.016 ms  3.120 ms  3.313 ms
...

可以看到,容器地址後的下一跳是宿主網絡上docker0接口的網關IP172.17.42.1。

不過Docker網絡還有另一個部分配置才能允許建立連接:防火墻規則和NAT配置。這些配置允許Docker在宿主網絡和容器間路由。現在來查看一下宿主機上的IPTables NAT配置:

# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0           ADDRTYPE match dst-type LOCAL 

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8         ADDRTYPE match dst-type LOCAL 

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0           
MASQUERADE  tcp  --  172.17.0.1           172.17.0.1          tcp dpt:5000 

Chain DOCKER (2 references)
target     prot opt source               destination         
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:5000 to:172.17.0.1:5000

這裏有幾個值得註意的IPTables規則。首先,我們註意到,容器默認是無法訪問的。從宿主網絡與容器通信時,必須明確指定打開的端口。下面我們以DNAT(即目標NAT)這個規則為例,這個規則把容器裏的訪問路由到Docker宿主機的5000端口。

連接Redis

# docker inspect redis
...
 "NetworkSettings": {
        "Bridge": "",
        "EndpointID": "d194571e408dfe4e94f3e4e2fa4ec048b03aeef1eb57d82b313d979d0d5f9f74",
        "Gateway": "172.17.42.1",
        "GlobalIPv6Address": "",
        "GlobalIPv6PrefixLen": 0,
        "HairpinMode": false,
        "IPAddress": "172.17.0.6",
        "IPPrefixLen": 16,
        "IPv6Gateway": "",
        "LinkLocalIPv6Address": "",
        "LinkLocalIPv6PrefixLen": 0,
        "MacAddress": "02:42:ac:11:00:06",
        "NetworkID": "612b14a43b7dee666f72c8ff28c7ec0d1f8cb03c00ed6a67d8680bf18ac67844",
        "PortMapping": null,
        "Ports": {
            "6379/tcp": [
                {
                    "HostIp": "0.0.0.0",
                    "HostPort": "32770"
                }
            ]
        },
...

docker inspect 命令展示了Docker容器的細節,這些細節包括配置信息和網絡狀況。為了清晰,這個例子去掉了大部分信息,只展示了網絡配置。也可以在命令裏使用-f標誌,只獲取IP地址:

# docker inspect -f ‘{{ .NetworkSettings.IPAddress}}‘ redis
172.17.0.6

可以看到,容器的IP地址為172.17.0.16,並使用了docker0接口作為網關地址。還可以看到6379端口被映射到本地宿主機的32770端口。只是,因為運行在本地的Docker宿主機上,所以不是一定要用映射後的端口,也可以直接使用172.17.0.6地址與Redis服務器的6379端口通信:

# redis-cli -h 172.17.0.6
redis 172.17.0.6:6379> 

Docker默認會把公開的端口綁定到所有的網絡接口上。因此,也可以通過localhost或者127.0.0.1來訪問Redis服務器。

雖然第一眼看上去這是讓容器互聯的一個好方案,但可惜的是,這種方法有兩個大問題:

  • 要在應用程序裏對Redis容器的IP地址做硬編碼
  • 如果重啟容器,Docker會改變容器的IP地址
# docker restart redis
redis
# docker inspect -f ‘{{ .NetworkSettings.IPAddress}}‘ redis
172.17.0.7

讓Docker容器互連

Docker有個叫做連接(link)的功能非常有用,這個功能可以把一個或多個容器連接起來,讓其互相通信。

讓一個容器和另一個連接起來只需要一個簡單的流程。

# docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                     NAMES
4359ee0cc5fd        test/redis          "/usr/bin/redis-serv   3 days ago          Up 3 days           0.0.0.0:32771->6379/tcp   redis               
5f1b5d2069eb        test/sinatra        "/opt/webapp/bin/web   3 days ago          Up 3 days           0.0.0.0:32769->4567/tcp   webapp

## 重新建一個名叫redis的容器,因為名字必須唯一,所以在這裏刪除原來的redis容器
# docker stop 4359ee0cc5fd
4359ee0cc5fd
# docker rm 4359ee0cc5fd
4359ee0cc5fd

## 創建一個名為redis的容器
# docker run -d --name redis test/redis
6dc87a9b56e0959f4426eedfe0f4a03ab56b2a40c1f2a89eedb9527c95517048

## 同樣重新創建一個webapp容器,刪除原有的容器
# docker stop 5f1b5d2069eb 
5f1b5d2069eb
# docker rm 5f1b5d2069eb 
5f1b5d2069eb
    
## 創建一個名為webapp的容器,並將它連接到redis容器上
# docker run -p 4567 --name webapp -d --link redis:db -v /opt/sinatra/webapp:/opt/webapp test/sinatra

這裏,使用了一個新的標誌--link。 --link標誌創建了兩個容器間的父子連接。這個標誌需要兩個參數:一個是要連接的容器名字,另一個是連接後容器的別名。這個例子中,我們把新容器連接到redis容器,並使用db作為別名。別名讓我們可以訪問公開的信息,而無需關註底層容器的名字。連接讓父容器有能力訪問子容器,並且把子容器的一些連接細節分享給父容器,這些細節有助於配置應用程序並使用這個連接。

連接也能得到一些安全上的好處。註意到啟動Redis容器時,並沒有使用-p標誌公開Redis的端口。因為不需要這麽做。通過吧容器連接在一起,可以讓父容器直接訪問任意子容器的公開端口(比如,父容器webapp可以連接到子容器redis的6379端口)。更妙的是,只有使用--link標誌連接到這個容器的容器才能連接到這個端口。容器的端口不需要對本地宿主機公開,現在我們已經擁有一個非常安全的模型。在這個模型裏,容器化的應用程序限制了可被攻擊的界面,減少了公開暴露的網絡。

出於安全原因(或者其他的原因),還可以強制Docker只允許有連接的容器之間互相通信。需要在啟動Docker守護進程時加上一個--icc=false標誌,關閉所有沒有連接的容器間的通信。

也可以把多個容器連接在一起。比如,如果想讓這個Redis實力服務多個Web應用程序,可以把每個Web應用程序的容器和同一個redis容器連接在一起:

# docker run -p 4567 --name webapp2 --link redis:db
...

# docker run -p 4567 --name webapp3 --link redis:db
...

Docker在父容器裏的以下兩個地方寫入了連接信息:

## 這裏引入一個知識點,如何進入一個正在運行的後臺容器
## 通過attach,但是它有一個缺點,只要這個連接終止,或者使用了exit命令,容器就會退出後臺運行
# docker attach webapp


## 通過exec,這個命令使用exit命令後,不會退出後臺
# docker exec docker
  • /etc/hosts文件中;
  • 包含連接信息的環境變量中。
[email protected]:/# cat /etc/hosts 
172.17.0.9      b3360fcb0cb0
...
172.17.0.8      db 6dc87a9b56e0 redis

[email protected]:/# ping db
PING db (172.17.0.8): 56 data bytes
64 bytes from 172.17.0.8: icmp_seq=0 ttl=64 time=0.234 ms
64 bytes from 172.17.0.8: icmp_seq=1 ttl=64 time=0.052 ms

再來看一下環境變量,其中一些以DB開頭。Docker在連接webapp和redis容器時,自動創建了這些以DB開頭的環境變量。以DB開頭是因為DB是創建連接時使用的別名。

[email protected]:/# env 
HOSTNAME=b3360fcb0cb0
DB_NAME=/webapp/db
DB_PORT_6379_TCP_PORT=6379
DB_PORT=tcp://172.17.0.8:6379
DB_PORT_6379_TCP=tcp://172.17.0.8:6379
...
DB_ENV_REFRESHED_AT=2017-05-26
DB_PORT_6379_TCP_ADDR=172.17.0.8
DB_PORT_6379_TCP_PROTO=tcp
...

這些自動創建的環境變量包含以下信息。

  • 子容器的名字
  • 容器裏運行的服務所使用的協議、IP和端口號
  • 容器裏運行的不同服務所指定的協議、IP和端口號
  • 容器裏有Docker設置的環境變量的值

這些環境變量會隨容器不同而變化,取決於容器是如何配置的(如容器的Dockerfile中裏有ENV和EXPOSE指令定義的內容)。更重要的是,這些連接信息可以讓容器內的應用程序使用相同的方法與別的容器進行連接,而不用關心被連接的容器的具體細節。

使用容器連接來通信

那麽如何使用這個連接呢?給Sinatra應用程序加入一些連接信息,以便與Redis通信。有以下兩種方法可以讓應用程序連接到Redis。

  • 使用環境變量的一些連接信息。
  • 使用DNS和/etc/hosts信息。

先試試第一種方法,看看Web應用程序lib/app.rb文件是如何利用這些新的環境變量的:

# vim /opt/sinatra/webapp/lib/app.rb 
require "rubygems"
require "sinatra"
require "json"
require "redis"
require "uri"
class App < Sinatra::Application uri=URI.parse(ENV[‘DB_PORT‘]) redis = Redis.new(:host => uri.host, :port => uri.port) set :bind, ‘0.0.0.0‘ get ‘/‘ do "<h1>DockerBook Test Redis-enabled Sinatra app</h1>" end get ‘/json‘ do params = redis.get "params" params.to_json end post ‘/json/?‘ do redis.set "params", [params].to_json params.to_json end end

這裏使用Ruby的URI模塊來解析DB_PORT環境變量,並使用解析後的結果來配置Redis連接。應用程序現在可以使用這個鏈接信息找到相連接的Redis容器。通過環境變量,這裏不再需要硬編碼IP地址和端口來進行連接。這是一種發現服務的方法。

另外一種方法,可以使用本地DNS:

require "rubygems"
require "sinatra"
require "json"
require "redis"

class App < Sinatra::Application

      redis = Redis.new(:host => ‘db‘, :port => ‘6379‘)

      set :bind, ‘0.0.0.0‘

      get ‘/‘ do
        "<h1>DockerBook Test Redis-enabled Sinatra app</h1>"
      end

      get ‘/json‘ do
        params = redis.get "params"
        params.to_json
      end

      post ‘/json/?‘ do
        redis.set "params", [params].to_json
        params.to_json
      end
end

應用程序會在本地查找名叫db的主機,找到/etc/hosts文件裏的項並解析到正確的IP地址。這也解決了硬編碼IP地址的問題。

現在在宿主機上再次使用curl命令測試應用程序:

# curl -i -H ‘Accept: application/json‘ -d ‘name=Foo&status=Bar‘ http://localhost:32779/json
HTTP/1.1 200 OK 
Content-Type: text/html;charset=utf-8
Content-Length: 29
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Mon, 12 Jun 2017 08:49:36 GMT
Connection: Keep-Alive

{"name":"Foo","status":"Bar"}

現在來確認下Redis實力接收到了這個更新:

# curl -i http://localhost:32779/json
HTTP/1.1 200 OK 
Content-Type: text/html;charset=utf-8
Content-Length: 41
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Mon, 12 Jun 2017 08:51:10 GMT
Connection: Keep-Alive

"[{\"name\":\"Foo\",\"status\":\"Bar\"}]"

總結

這個Web應用程序由以下幾個部分組成:

  • 一個運行Sinatra的Web服務器容器。
  • 一個Redis數據庫容器
  • 這兩個容器間的一個安全連接

可以很容易把這個概念擴展到別的應用棧,並用其在本地開發中做復雜的管理,比如:

  • Wordpress、HTML、CSS和Javascript
  • Ruby on Rails
  • Django和Flask。
  • Node.js
  • Play!。
  • 你喜歡的其他框架

這樣就可以在本地環境構建、復制、叠代開發用於生產的應用程序,甚至很復雜的多層應用程序。

docker實戰——在測試中使用Docker