Docker新手入门之五:Docker在真实应用中的使用实践

转载过程中,图片丢失,代码显示错乱。

为了更好的学习内容,请访问原创版本:

https://www.missshi.cn/api/view/blog/5a6327d10a745f6335000005

Ps:初次访问由于js文件较大,请耐心等候(5s左右)

 

 

在之前的文章中,我们已经了解了Docker一些基础知识,包括什么是Docker,镜像,容器等相关信息。

在本文中,我们将会在一些实际场景中演示如何在开发和测试中来使用Docker。

本文主要包含3个部分:使用Docker测试一个静态网站、使用Docker创建并测试一个Web应用、使用Docker进行持续集成。

使用Docker测试静态网站

将Docker作为本地Web开发环境是Dockers的一个常见的应用场景。 
这样可以保证开发运行与生产环境的配置、部署、依赖等一致,避免后续运维相关的问题。 
下面,我们演示一下如何将Nginx安装到Docker中进行运行。

准备Dockerfile

 
  
  1. mkdir sample
  2. cd sample
  3. touch Dockerfile

下面,我们需要准备一些Nginx的配置的文件。

 
  
  1. mkdir nginx && cd nginx
  2. wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/global.conf
  3. wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/nginx.conf
  4. cd ..

接下来,我们修改Dockerfile如下:

 
  
  1. FROM ubuntu:14.04
  2. MAINTAINER nianshi "[email protected]"
  3. ENV REFREASHED_AT 2018-02-06
  4. RUN apt-get update
  5. RUN apt-get -y -q install nginx
  6. RUN mkdir -p /var/www/html
  7. ADD nginx/global.conf /etc/nginx/conf.d/
  8. ADD nginx/nginx.conf /etc/nginx/nginx.conf
  9. EXPOSE 80

其中,global.conf的内容如下:

 
  
  1. server {
  2. listen 0.0.0.0:80;
  3. server_name _;
  4. root /var/www/html/website;
  5. index index.html index.htm;
  6. access_log /var/log/nginx/default_access.log;
  7. error_log /var/log/nginx/default_error.log;
  8. }

另外,nginx.conf的内容如下:

 
  
  1. user www-data;
  2. worker_processes 4;
  3. pid /run/nginx.pid;
  4. daemon off;
  5. events { }
  6. http {
  7. sendfile on;
  8. tcp_nopush on;
  9. tcp_nodelay on;
  10. keepalive_timeout 65;
  11. types_hash_max_size 2048;
  12. include /etc/nginx/mime.types;
  13. default_type application/octet-stream;
  14. access_log /var/log/nginx/access.log;
  15. error_log /var/log/nginx/error.log;
  16. gzip on;
  17. gzip_disable "msie6";
  18. include /etc/nginx/conf.d/*.conf;
  19. }

其中,nginx配置为非守护进行的模型,从而可以在Docker中工作。

构建镜像

利用我们准备好的Dockerfile,可以使用docker build命令来构建镜像。

 
  
  1. docker build -t nianshi/nginx .

启动容器

首先,执行如下命令来准备待测试的网站。

 
  
  1. mkdir website && cd website
  2. wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/website/index.html
  3. cd ..

接下来,我们可以执行如下命令来启动容器:

 
  
  1. sudo docker run -d -p 80 --name website -v $PWD/website:/var/www/html/website nianshi/nginx nginx

此时,-v参数进行卷映射。 
卷的特点:

  • 为Docker提供持久化或共享数据。
  • 对卷的修改实时生效,无需重启容器。

我们常常在如下场景中会使用卷:

  • 代码改动频繁,不希望频繁构建镜像
  • 希望在多个容器之间共享文件

Ps:在卷映射后,可以使用:ro或者:rw来指定读写权限。默认是可读写的。

执行上述命令后,我们可以使用docker ps命令看查看当前运行的容器。 
title
其Docker容器的80端口被映射到了本地的49161端口。 
接下来,我们在浏览器中访问49161端口可以得到如下页面: 
Docker新手入门之五:Docker在真实应用中的使用实践_第1张图片

修改网站

如果我们希望修改网站内容,那么我们可以直接编辑index.html文件即可。 
例如,我们将内容可以修改为

 
  
  1. This is a test website for docker!

接下来,刷新浏览器,可以看到页面变化如下: 
Docker新手入门之五:Docker在真实应用中的使用实践_第2张图片

使用Docker创建并测试一个Web应用

在上面的内容中,我们使用Docker运行了一个Nginx服务,可以用于测试一个静态网址。 
接下来,我们将继续使用Docker来构建一个更大的Web应用程序。

构建Sinatra应用程序

编写Dockerfile文件如下:

 
  
  1. FROM ubuntu:16.04
  2. MAINTAINER nianshi "[email protected]"
  3. ENV REFREASHED_AT 2018-02-07
  4. RUN apt-get update
  5. RUN apt-get -y install ruby2.3 ruby2.3-dev build-essential redis-tools
  6. RUN gem install --no-rdoc --no-ri sinatra json redis
  7. RUN mkdir -p opt/webapp
  8. EXPOSE 4567
  9. CMD [ "/opt/webapp/bin/webapp" ]

观察一下我们的Dockerfile文件,首先安装了Ruby和RubyGem工具。 
接下来使用gem安装了sinatra json redis的Ruby库。 
此外还创建了一个目录用于存放Web应用程序并公开了4567的端口。 
最后指定了程序的启动命令。 
下面,我们可以根据Dockerfile来构建镜像:

 
  
  1. docker build -t nianshi/sinatra .

创建Sinatra容器

首先,我们需要下载应用程序依赖的源代码:

 
  
  1. wget --cut-dirs=3 -nH -r --no-parent http://dockerbook.com/code/5/sinatra/webapp/

同时,我们需要将webapp/bin/webapp的文件修改为可执行文件。

 
  
  1. chmod +x $PWD/webapp/bin/webapp

接下来,我们就可以启动Sinatra容器了:

 
  
  1. sudo docker run -d -p 4567 --name webapp -v $PWD/webapp:/opt/webapp nianshi/sinatra

容器启动后,我们可以执行docker logs命令来查看容器的日志信息:

 
  
  1. docker logs -f webapp

此外,可以执行docker top命令来查看容器的进程:

 
  
  1. docker top webapp

下面,我们需要查看我们的容器的端口映射在本地宿主机的哪个端口,查看方式如下:

 
  
  1. docker port webapp 4567
  2. # 0.0.0.0:32768

上述结果表明该容器的4567端口映射到了本地宿主机的32768端口。 
下面,我们来试用一下这个服务吧,该服务目前的功能非常简单,仅仅是将输入的数据转化为JSON的格式进行输出,示例如下: 
Docker新手入门之五:Docker在真实应用中的使用实践_第3张图片 
接下来,我们将会拓展Sinatra应用程序,为其添加Redis数据库。

构建Redis镜像和容器

首先创建Dockerfile如下:

 
  
  1. FROM ubuntu:16.04
  2. MAINTAINER nianshi "[email protected]"
  3. ENV REFREASHED_AT 2018-02-07
  4. RUN apt-get -qq update && apt-get -qq install redis-server redis-tools
  5. EXPOSE 6379
  6. ENTRYPOINT ["/usr/bin/redis-server" ]
  7. CMD []

我们在Dockerfile中指定了安装Redis服务器并公开了6379端口。 
下面,我们来构造该镜像:

 
  
  1. docker build -t nianshi/redis .

在镜像构造完成后,我们可以根据我们构建的镜像来启动容器:

 
  
  1. docker run -d -p 6379 --name redis nianshi/redis

下面,我们需要检查一下redis映射到宿主机的哪个端口:

 
  
  1. docker port redis 6379
  2. # 0.0.0.0:32769

接下来,我们需要在本地尝试连接一下Redis服务器,从而检查Redis服务器是否能够正常工作。 
首先检查一下本地是否安装有redis-cli工具,如果没有的话需要执行如下命令进行安装:

 
  
  1. apt-get install redis-tools

安装安装后,执行如下命令来连接Redis服务器:

 
  
  1. redis-cli -h 127.0.0.1 -p 32769
  2. # 127.0.0.1:32769>

如果进入了Redis交互式命令行,表示我们的Redis服务器目前是正常工作的。

容器网络机制

为了将Sinatra服务能够连接到Redis数据库,我们需要能够在容器之间进行对话。 
到目前为止,我们访问服务时都是通过将公开端口绑定到本地的宿主机的某个端口上,然后再来访问本地宿主机的端口。 
除了这种方式之外,实际上每个Docker容器都有自己的IP地址。 
通过容器自身的IP地址,我们也可以直接访问该容器的服务。

查看容器IP地址

我们可以使用如下命令来直接查看容器的IP地址等信息:

 
  
  1. docker inspect redis
  2. # ...
  3. # "NetworkSettings": {
  4. # "Bridge": "",
  5. # "EndpointID": "1fb2fe39f81332aeaade03549ee13046426d47f8528a46e074c6d8c9acee585c",
  6. # "Gateway": "172.17.42.1",
  7. # "GlobalIPv6Address": "",
  8. # "GlobalIPv6PrefixLen": 0,
  9. # "HairpinMode": false,
  10. # "IPAddress": "172.17.0.18",
  11. # "IPPrefixLen": 16,
  12. # "IPv6Gateway": "",
  13. #

其中,我们在NetworkSettings中可以找到一个IPAddress。 
此处显示为172.17.0.18,即表明Redis容器的IP地址实际上就是172.17.0.18。 
当然,由于inspect命令是查看容器的全部信息,所以可能会存在大量我们并不太关心的内容,为了更加清晰的显示我们希望得到的信息,我们可以使用如下方式进行更细粒度的信息查询:

 
  
  1. docker inspect -f '{{ .NetworkSettings.IPAddress }}' redis
  2. # 172.17.0.18

我们可以直接使用该地址进行服务连接。此时,Redis的端口号不再是被映射的端口号32769了,而是默认的端口号6379。 
连接一下试试吧:

 
  
  1. redis-cli -h 172.17.0.18
  2. # 172.17.0.18:6379>

那么我们是不是可以直接使用这种方式进行容器互联呢?实际上这种方式有一个致命的问题:那就是容器一旦重启,则该容器对应的IP地址会发生变化。

 
  
  1. docker restart redis
  2. docker inspect -f '{{ .NetworkSettings.IPAddress }}' redis
  3. # 172.17.0.19

因此,如果我们在代码中对数据库的地址进行硬编码,则服务重启后就无法再正常运行了。 
下面我们来了解一下如何在真正的应用中进行容器互联。

容器互联

让容器之间可以相互连接主要借用了一个link的功能。 
接下来,我们将关闭之前启动的全部容器,重新开始演示如何实现容器互联的功能。

Step1:启动Redis服务

 
  
  1. docker run -d --name redis nianshi/redis

Ps:此处我们没有显示公开任何端口。后续会说明原因。

Step2:接下来,我们启动Web应用容器,并将其连接到新的Redis容器上。

 
  
  1. docker run -p 4567 --name webapp --link redis:db -it -v $PWD/webapp:/opt/webapp nianshi/sinatra /bin/bash

在这个命令中,我们使用到了--link redis:db。 
link标识用于创建两个容器之间的连接,前者的参数表示的是容器名称,后者表示的是别名。 
通过连接,可以让父容器有方法直接连接到子容器中。 
Ps:在使用纯Docker时,被连接的容器必须在同一个Docker宿主机中。不同宿主机之间的容器如果想要连接,则需要借助Swarm或Kubernetes等编排工具。

在启动容器时,我们利用/bin/bash进入和交互式命令环境,而不是直接启动服务。 
主要原因是希望在容器中进一步了解容器直接是如何连接的。 
Docker在父容器中的以下两个地方写入了连接信息:

  • /etc/hosts文件中
  • 包含连接信息的环境变量中

我们先看一下/etc/hosts文件:

 
  
  1. cat /etc/hosts
  2. # 172.17.0.21 8ec25c2f5c19
  3. # ...
  4. # 172.17.0.20 db 840a446245c4 redis

其中,第一部分是容器自己的IP地址和主机名称。 
第二部分是有连接指令创建的,包含redis容器的IP地址和别名,主机名称以及容器名称。 
此外,我们可以执行一下env命令来查看一下相关的环境变量信息:

 
  
  1. env
  2. # HOSTNAME=8ec25c2f5c19
  3. # DB_NAME=/webapp/db
  4. # DB_PORT_6379_TCP_PORT=6379
  5. # TERM=xterm
  6. # DB_PORT=tcp://172.17.0.20:6379
  7. # DB_PORT_6379_TCP=tcp://172.17.0.20:6379
  8. # DB_ENV_REFRESHED_AT=2014-06-01
  9. # DB_PORT_6379_TCP_ADDR=172.17.0.20
  10. # DB_PORT_6379_TCP_PROTO=tcp
  11. # SHLVL=1
  12. # HOME=/root
  13. # REFREASHED_AT=2018-02-07
  14. # _=/usr/bin/env

这些环境变量中包含如下内容:

  1. 子容器的名称
  2. 容器中运行的服务所写的协议、IP和端口号等
  3. 容器中Docker设置的环境变量的值

那么如何在我们的服务中利用这些环境变量提供的连接信息呢?

方式1: 使用环境变量 
方式2:使用DNS和/etc/hosts信息

先看来第一种方法:利用环境变量。 
在Web应用程序中lib/app.rb文件中,可以如下获取host和port。

 
  
  1. require 'uri'
  2. ...
  3. uri = URI.parse(ENV['DB_PORT'])
  4. redis = Redis.new(:host => uri.host, :port => uri.port)
  5. ...

此处,我们使用URI模块来解析DB_PORT环境变量,然后用于连接数据库,防止硬连接。

下面,我们来看另外一种方法:使用本地DNS。 
使用方式如下:

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

此时,应用程序会在本地查找名为db的主机,通过/etc/hosts文件解析得到正确的IP地址。 
我们来启动该服务:

 
  
  1. nohup /opt/webapp/bin/webapp &

服务启动后,我们来测试Redis服务是否正常。

Step1:用POST请求传递参数访问本地服务写入数据。 
Step2:用GET请求查看目前存储的数据。

Docker新手入门之五:Docker在真实应用中的使用实践_第4张图片 
可以看到GET请求得到得到上次POST请求传送的数据,说明我们Redis服务已经正常启动了。

在这个相对完整的Web应用程序中,我们涉及到了如下内容:

  1. 一个运行Sinatra的Web服务器
  2. 一个Redis数据库容器
  3. 两个容器之间的连接

以此为例,我们可以将其扩展到其他应用程序栈,例如Django,Flask等等。


 

更多更详细的内容,请访问原创网站:

https://www.missshi.cn/api/view/blog/5a6327d10a745f6335000005

Ps:初次访问由于js文件较大,请耐心等候(5s左右)

你可能感兴趣的:(Docker,Docker,Swarm,Kubernetes技术栈学习)