您的位置:首页 > 运维架构 > Docker

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

2018-02-20 20:22 579 查看
转载过程中,图片丢失,代码显示错乱。为了更好的学习内容,请访问原创版本:https://www.missshi.cn/api/view/blog/5a6327d10a745f6335000005Ps:初次访问由于js文件较大,请耐心等候(5s左右) 
 

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

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

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

使用Docker测试静态网站

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

准备Dockerfile

mkdir sample

cd sample

touch Dockerfile

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

wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/global.conf[/code] 
wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/nginx/nginx.conf[/code] 
cd ..

接下来,我们修改
Dockerfile
如下:
FROM ubuntu:14.04

MAINTAINER nianshi "nianshi0912@gmail.com"

ENV REFREASHED_AT 2018-02-06

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

其中,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;

}

另外,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;

}

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

构建镜像

利用我们准备好的Dockerfile,可以使用
docker build
命令来构建镜像。
docker build -t nianshi/nginx .

启动容器

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

wget https://raw.githubusercontent.com/jamtur01/dockerbook-code/master/code/5/sample/website/index.html[/code] 
cd ..

接下来,我们可以执行如下命令来启动容器:
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
命令看查看当前运行的容器。 



其Docker容器的80端口被映射到了本地的49161端口。 
接下来,我们在浏览器中访问49161端口可以得到如下页面: 


修改网站

如果我们希望修改网站内容,那么我们可以直接编辑
index.html
文件即可。 
例如,我们将内容可以修改为
This is a test website for docker!

接下来,刷新浏览器,可以看到页面变化如下: 


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

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

构建Sinatra应用程序

编写Dockerfile文件如下:
FROM ubuntu:16.04

MAINTAINER nianshi "nianshi0912@gmail.com"

ENV REFREASHED_AT 2018-02-07

RUN apt-get update

RUN apt-get -y install ruby2.3 ruby2.3-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" ]

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

创建Sinatra容器

首先,我们需要下载应用程序依赖的源代码:
wget --cut-dirs=3 -nH -r --no-parent http://dockerbook.com/code/5/sinatra/webapp/[/code] 同时,我们需要将webapp/bin/webapp的文件修改为可执行文件。
chmod +x $PWD/webapp/bin/webapp

接下来,我们就可以启动Sinatra容器了:
sudo docker run -d -p 4567 --name webapp -v $PWD/webapp:/opt/webapp nianshi/sinatra

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

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

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

#0.0.0.0:32768

上述结果表明该容器的4567端口映射到了本地宿主机的32768端口。 
下面,我们来试用一下这个服务吧,该服务目前的功能非常简单,仅仅是将输入的数据转化为JSON的格式进行输出,示例如下: 


 
接下来,我们将会拓展Sinatra应用程序,为其添加Redis数据库。

构建Redis镜像和容器

首先创建Dockerfile如下:
FROM ubuntu:16.04

MAINTAINER nianshi "nianshi0912@gmail.com"

ENV REFREASHED_AT 2018-02-07


RUN apt-get -qq update && apt-get -qq install redis-server redis-tools


EXPOSE 6379


ENTRYPOINT ["/usr/bin/redis-server" ]

CMD []

我们在Dockerfile中指定了安装Redis服务器并公开了6379端口。 
下面,我们来构造该镜像:
docker build -t nianshi/redis .

在镜像构造完成后,我们可以根据我们构建的镜像来启动容器:
docker run -d -p 6379 --name redis nianshi/redis

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

#0.0.0.0:32769

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

安装安装后,执行如下命令来连接Redis服务器:
redis-cli -h 127.0.0.1 -p 32769

#127.0.0.1:32769>

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

容器网络机制

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

查看容器IP地址

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

#...

#"NetworkSettings": {

#       "Bridge": "",

#       "EndpointID": "1fb2fe39f81332aeaade03549ee13046426d47f8528a46e074c6d8c9acee585c",

#       "Gateway": "172.17.42.1",

#       "GlobalIPv6Address": "",

#       "GlobalIPv6PrefixLen": 0,

#       "HairpinMode": false,

#       "IPAddress": "172.17.0.18",

#       "IPPrefixLen": 16,

#       "IPv6Gateway": "",

#

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

#172.17.0.18

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

#172.17.0.18:6379> 

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

docker inspect -f '{{ .NetworkSettings.IPAddress }}' redis

#172.17.0.19

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

容器互联

让容器之间可以相互连接主要借用了一个link的功能。 
接下来,我们将关闭之前启动的全部容器,重新开始演示如何实现容器互联的功能。Step1:启动Redis服务
docker run -d --name redis nianshi/redis

Ps:此处我们没有显示公开任何端口。后续会说明原因。Step2:接下来,我们启动Web应用容器,并将其连接到新的Redis容器上。
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
文件:
cat /etc/hosts

#172.17.0.21   8ec25c2f5c19

#...

#172.17.0.20   db 840a446245c4 redis

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

#HOSTNAME=8ec25c2f5c19

#DB_NAME=/webapp/db

#DB_PORT_6379_TCP_PORT=6379

#TERM=xterm

#DB_PORT=tcp://172.17.0.20:6379

#DB_PORT_6379_TCP=tcp://172.17.0.20:6379

#DB_ENV_REFRESHED_AT=2014-06-01

#DB_PORT_6379_TCP_ADDR=172.17.0.20

#DB_PORT_6379_TCP_PROTO=tcp

#SHLVL=1

#HOME=/root

#REFREASHED_AT=2018-02-07

#_=/usr/bin/env

这些环境变量中包含如下内容:子容器的名称
容器中运行的服务所写的协议、IP和端口号等
容器中Docker设置的环境变量的值
那么如何在我们的服务中利用这些环境变量提供的连接信息呢?方式1: 使用环境变量 
方式2:使用DNS和/etc/hosts信息先看来第一种方法:利用环境变量。 
在Web应用程序中
lib/app.rb
文件中,可以如下获取host和port。
require 'uri'

...

uri = URI.parse(ENV['DB_PORT'])

redis = Redis.new(:host => uri.host, :port => uri.port)

...

此处,我们使用URI模块来解析DB_PORT环境变量,然后用于连接数据库,防止硬连接。下面,我们来看另外一种方法:使用本地DNS。 
使用方式如下:
redis = Redis.new(:host => 'db', :port => '6379')

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

服务启动后,我们来测试Redis服务是否正常。Step1:用POST请求传递参数访问本地服务写入数据。 
Step2:用GET请求查看目前存储的数据。

 
可以看到GET请求得到得到上次POST请求传送的数据,说明我们Redis服务已经正常启动了。在这个相对完整的Web应用程序中,我们涉及到了如下内容:一个运行Sinatra的Web服务器
一个Redis数据库容器
两个容器之间的连接
以此为例,我们可以将其扩展到其他应用程序栈,例如Django,Flask等等。
 
更多更详细的内容,请访问原创网站:https://www.missshi.cn/api/view/blog/5a6327d10a745f6335000005Ps:初次访问由于js文件较大,请耐心等候(5s左右)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Docker