微服务架构基于Nginx、Node.js和Redis的Docker工作流

本文是一篇实践性很强的文章。作者通过一个完整的示例讲述了构建一个基于Nginx、Node.js、Redis的应用服务的Docker流程。推荐所有Docker使用者阅读,并根据文章实践。

在我的前一篇文章中,我已经介绍了关于容器和Docker是如何影响PaaS、微服务和云计算的。如果你刚刚接触Docker和容器,我强烈建议你先读一读我之前的文章。作为之前文章的一个延续,在本文中我仍会讲述一些Docker工作流实例的内容。你可以在GitHub上找到所有的代码示例。

在这个例子中,我有一个非常简单的Node.js应用,它实现了一个递增的计数器并且将数据存储在Redis上。为了保证应用的高可扩展的能力,我会独立运行Redis和Node应用。

让我们先谈谈容器,特别是Docker容器。我们要为每个服务/进程分配一个容器!

1个Redis容器

3个Node容器

1个Nginx容器

因此,整体架构看上去是这样的:

我可以用Docker命令来构建容器,但为了更加简单,我推荐使用Dockerfile。我也用Docker Compose去编排应用连接容器。

首先,我先介绍下如何定义容器。

-----------------华丽的分割线--------------------

有多种方法来配置一个Dockerfile和镜像层次。一个方法,将启动一个基于操作系统的镜像,如Ubuntu,并建立自己的应用和在这之上的依赖项。另一个可能是最理想的方法是为你的具体使用而使用一个预建的镜像。Docker Hub Registry有许多用于构建流行应用和其依赖的预建镜像,这些可以直接用。

我会修改例子来演示不同的使用情况。我将演示为Redis容器使用一个预建镜像,为Nginx容器使用一个预建的自定义配置的镜像和一个构建在Ubuntu镜像上的Node容器。

———————————————————————————————————————————

Redis 容器

让我们使用官方的Redis镜像从Docker Hub上的Redis容器。它预打包了Redis服务的安装,运行在默认端口6379。所以你只要默认配置ok就不需要修改任何配置,直接创建并运行Redis容器镜像:

docker run -d--name redis -p 6379:6379 redis

如果你想从基于Ubuntu的镜像构建Redis镜像,Dockerfile会是这样:

# Set the base image to UbuntuFROM ubuntu# File Author / MaintainerMAINTAINER Anand Mani Sankar# Update the repository and install Redis ServerRUN apt-get update && apt-get install -y redis-server# Expose Redis port 6379EXPOSE 6379# Run Redis ServerENTRYPOINT [“/usr/bin/redis-server"]

Node 容器

让我们来看看Node应用。我不想做太多的解释。我做的是在每个请求使用Redis的INCR的递增的一个视图计数器。我使用node-redis模块连同hiredis从而获得更好的性能。(Yeah,超高性能的视图计数器不会受损!)

var express =require('express'),http =require('http'),redis =require('redis');var app = express();console.log(process.env.REDIS_PORT_6379_TCP_ADDR +':'+ process.env.REDIS_PORT_6379_TCP_PORT);// APPROACH1: Using environment variables created by Docker// var client = redis.createClient(// process.env.REDIS_PORT_6379_TCP_PORT,// process.env.REDIS_PORT_6379_TCP_ADDR// );// APPROACH2: Using host entries created by Dockerin/etc/hosts (RECOMMENDED)var client = redis.createClient('6379','redis');app.get('/',function(req, res, next){client.incr('counter',function(err, counter){if(err)returnnext(err);res.send('This page has been viewed '+ counter +' times!');});});http.createServer(app).listen(process.env.PORT ||8080,function(){console.log('Listening on port '+ (process.env.PORT ||8080));});

你可能已经注意到用于地址和端口的Redis服务的环境变量,这些环境变量是在容器连接时由Docker定义,以方便容器间通讯。

-----------------华丽的分割线--------------------

Docker,除了创建环境变量,还会更新

/etc/hosts文件中的主机记录。事实上,Docker官方推荐使用/etc/hosts文件来替代环境变量,因为如果源容器重启的时候,环境变量并不会自动更新。

———————————————————————————————————————————

接下来将我们使用另一种方法来构建Node容器。我们从一个基本的Ubuntu镜像开始,并逐步在上面添加Node和它的依赖项。

Node Dockerfile:

# Set the base image to UbuntuFROM ubuntu#FileAuthor/ MaintainerMAINTAINER Anand Mani Sankar# Update the repositoryRUN apt-get update# Install Node.js and other dependenciesRUN apt-get -y install curlRUN curl -sL https://deb.nodesource.com/setup | sudo bash -RUN apt-get -y install python build-essential nodejs# Install nodemonRUN npm install -g nodemon# Bundle app sourceCOPY ./src# Install app dependenciesRUN cd /src; npm install# Expose portEXPOSE8080# Run app using nodemonCMD ["nodemon", “/src/index.js"]

上面的Dockerfile解释如下:

从Docker Hub拉取Ubuntu基础镜像

使用apt-get安装Node.js以及依赖

使用npm安装nodemon

从host目录复制应用源码到容器内src

运行npm install安装Node应用依赖

端口8080从容器抛出,使用nodemon运行应用

使用Dockerfile构建一个Docker镜像:

dockerbuild -t msanand/node .

从自定义镜像中创建一个Node容器并连接Redis容器:

docker run -d--name node -p 8080 --link redis:redis msanand/node

由于我计划在3个node服务器之间做负载均衡,我会创建3个容器 - node1、node2和node3。

请注意,Redis容器将会连接到Node容器,所以Node容器可以通过Docker创建的主机记录或者环境变量定义的IP地址和端口来与Redis容器交互。

有了这一点,我有一个Node应用显示一个视图计数器并将数据保存在Redis。让我们来看看如何使用Nginx来做负载均衡。

NGINX容器

Nginx的核心是它的配置:一个conf文件。我使用一个简单的Nginx配置文件定义3个upstream server:

worker_processes 4;events { worker_connections 1024; }http {upstream node-app { least_conn; server node1:8080 weight=10 max_fails=3 fail_timeout=30s; server node2:8080 weight=10 max_fails=3 fail_timeout=30s; server node3:8080 weight=10 max_fails=3 fail_timeout=30s;}server { listen 80; location / { proxy_pass http://node-app; proxy_http_version 1.1; proxy_set_header Upgrade$http_upgrade; proxy_set_header Connection'upgrade'; proxy_set_header Host$host; proxy_cache_bypass$http_upgrade; }}}

我已经注册一个node-appupstream server,它会负载均衡3个服务:node1、node2、node3。我还通过一个全等加权least_conn负载平衡策略指定了每个服务器上的活动连接数的负载均衡。或者,你可以使用一个基于负载均衡方法的罗宾循环( round robin)或IP哈希或键哈希。Nginx监听80端口,它基于负载均衡策略代理请求到上游服务器node-app。如果要了解更多的Nginx的配置我会另外讨论。

为了构建Nginx容器,我计划从Docker Hub上使用正式的Nignx镜像。我将使用一个Dockerfile以及我自定义的Nginx conf文件配置Nignx。

该Dockerfile是最小的-使用Nginx镜像和副本自定义Nginx的配置:

# Set nginx base imageFROM nginx#FileAuthor/ MaintainerMAINTAINER Anand Mani Sankar# Copy custom configuration file from the current directoryCOPY nginx.conf /etc/nginx/nginx.conf

构建Docker镜像:

docker build -t msanand/nginx .

从镜像中创建一个Nginx容器,并连接到Node容器:

docker run -d --name nginx -p 80:80 --link node:node msanand/nginx

最后,我们有个Nginx服务器负载均衡3个Node服务器。回过来谈Node服务器,他们每一个运行在自己的容器中!

如果我们要创建一个基本的Ubuntu镜像定制Nginx的镜像,Dockerfile会是这个样子:

# Set the base image to UbuntuFROM ubuntu#FileAuthor/ MaintainerMAINTAINER Anand Mani Sankar# Install Nginx# Add application repository URL to the default sources# RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list# Update the repositoryRUN apt-get update# Install necessary toolsRUN apt-get install -y nano wget dialog net-tools# Download and Install NginxRUN apt-get install -y nginx # Remove thedefaultNginx configuration fileRUN rm -v/etc/nginx/nginx.conf# Copy a configuration file from the current directoryADD nginx.conf /etc/nginx/#Append"daemon off;"to the configuration fileRUN echo"daemon off;">>/etc/nginx/nginx.conf# Expose portsEXPOSE 80# Set the default command to execute when creating a new containerCMD service nginx start

Dockerfile(daemon off)配置了Nginx不以守护进程运行,这也是必须的,因为Docker容器本身就是无状态的,只有当他们所承载的进程运行的时候,容器才有存在的意义。所以把Nginx当成后台进程运行根本不可能。相反,把Nginx作为一个服务运行可以确保容器的正常运行。官方Nginx镜像默认配置也是这样的。

Docker Compose编排应用

Compose是一个使用Docker定义和运行复杂应用的工具。

使用单独的命令来构建镜像并运行和连接容器非常繁琐和复杂,特别是你要运行多个容器的时候。

Docker Compose让你在一个文件中定义多容器应用并用一个命令使应用程序运行起来。

我已经定义一个Docker Compose YAML文件,如下:

nginx:build: ./nginxlinks: -node1:node1 -node2:node2 -node3:node3ports: -"80:80"node1:build: ./nodelinks: -redisports: -"8080"volumes: -node:/srcnode2:build: ./nodelinks: -redisports: -"8080"volumes: -node:/srcnode3:build: ./nodelinks: -redisports: -"8080"volumes: -node:/srcredis:image:redisports: - “6379"

YAML文件定义每个容器的名字,指向一个用于构建的Dockerfile。此外,它包含所述容器连接并通过它们暴露端口。由于Redis容器使用Redis官方镜像,所以不必构建。

只需要一个命令,Docker Compose就可以构建所需镜像,并导出所需端口,然后通过YAML中的定义运行和连接容器。所有你需要做的就是运行docker-compose up,然后你的5个容器应用就会启动并运行。输入你的主机URL和80端口,你就可以看到你的视图计数器!

Docker Compose的一个显著特点就是具有动态扩展容器的能力。使用命令docker-compose scale node=5可以扩展容器的数量来运行一个服务。如果你已有一个基于微服务架构的Docker,你可以轻松地扩展,并动态地根据负载分配具体服务。理想情况下,我宁愿定义一个node服务并使用Docker Compose来扩展它。但我还没有想出一个方法来动态地调整Nginx的配置。如果你有这方面的想法,请在本文后回复。

但这里有个需要注意的是,Docker Compose还不能用于生产环境。文档建议使用在开发环境中,而不是生产环境。但也有其它的容器编排引擎如我之前的文章讨论的Kubernetes。

持续集成和部署

我在我的GitHub仓库中配置了2个hook服务(译者注:作者指的是GitHub Webhook)。

CircleCI-用于持续集成(以及部署)

Docker Hub -用于Docker构建(continuous Docker builds)

CircleCI YAML配置文件看这儿:

machine:services:-dockerdependencies:override:- sudo pip install -U docker-composetest:override:- docker-compose run -d --no-deps node1 - cd node; mocha

YAML配置文件使用CircleCI提供的Docker服务,并安装Docker Compose依赖,创建了Node容器(未连接到Redis容器)。它使用Mocha(译者注:Mocha是一个基于Node.js和浏览器的集合各种特性的JavaScript测试框架,并且可以让异步测试也变的简单和有趣。Mocha的测试是连续的,在正确的测试条件中遇到未捕获的异常时,会给出灵活且准确的报告。Mocha托管在Github上)在Node应用上触发测试,这确保了GitHub上每个提交都会对应一个测试。

每次提交都会触发我的Docker Hub Repository进行一次构建。这确保在Docker Hub中通过持续部署到生产环境的最终镜像总是可用的。生产环境能在任何时间从Docker Hub和从容器中编排的应用中能拉到最终镜像。

以上是我的一个基于Nginx、Node.js和Redis的Docker流程实例。如果你有任何建议和更好的方法,请发表评论。

推荐:大家可以关注我,私信发送‘架构’即可获取以下资料,里面有源码分析、性能优化、微服务架构、工程化、分布式等知识点 走的就是高端路线 下图是资料的一部分知识点 有用没用一看就知道的

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小樱的经验随笔

【经验分享】Hydra(爆破神器)使用方法

这个也是backtrack下面很受欢迎的一个工具 参数详解: -R 根据上一次进度继续破解 -S 使用SSL协议连接 -s 指定端口 -l 指定用户名 -L 指...

3836
来自专栏程序猿DD

Spring Cloud构建微服务架构:分布式配置中心(加密解密)

最近正好想发一篇关于配置中心加密的细节内容,结果发现基础的加密解密居然漏了,所以在这个入门系列中补充一下。后面再更新一下,使用配置中心的一些经验和教训。 ? 在...

3137
来自专栏我有一个梦想

SourceTree 基本介绍

Git的服务器端: 最出名的是GitHub,但是不能创建私有仓库,创建私有得需要Money Bitbucket:可以创建私有数据库,但是速度太慢,太消磨激情了 ...

1785
来自专栏Java成神之路

java支付宝开发-01-沙箱环境接入

(1)蚂蚁沙箱环境(Beta)是协助开发者进行接口功能开发及主要功能联调的辅助环境。沙箱环境模拟了开放平台部分产品的主要功能和主要逻辑(当前沙箱支持产品请参考“...

752
来自专栏用户2442861的专栏

sublime text 配置php调试环境(快捷键)

2、在sublime text 2中 工具->编译系统->新建编译系统 添加如下代码:

411
来自专栏自然语言处理

GitHub简明操作指南

GitHub的是版本控制和协作代码托管平台。它可以让你和其他人的项目从任何地方合作。Git也是目前世界上最先进的分布式版本控制系统(没有之一)。最初编写用作Li...

622
来自专栏搜云库

Ubuntu 17.04 x64 安装 Docker CE

Docker 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的运行效率,降低了云计算资源供应的成本!使用 Docker,可以让应用的部署、测...

30910
来自专栏python3

Docker修改hosts

Docker修改hosts?这还不简单,打开vim直接敲就完事儿了!然而事与愿违,事情没有我们想的那么简单。在很多场景中,比如我们需要搭建一个集群,这时候容器要...

1622
来自专栏姚姝娜的专栏

关于 Git 的那些事

本篇文章主要讲了下 Git 的一些基本的概念以及如果使用 git log 的命令来进行一段时间段的代码量统计,其中还可以统计不同作者修改的代码量,其中关于 gi...

3931
来自专栏IMWeb前端团队

Nodejs进阶:核心模块https 之 如何优雅的访问12306

本文作者:IMWeb 陈映平 原文出处:IMWeb社区 未经同意,禁止转载 模块概览 这个模块的重要性,基本不用强调了。在网络安全问题日益严峻的今天,...

24210

扫码关注云+社区