【实战】基于Nginx、Node.js和Redis的Docker工作流

本文讲的是【实战】基于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去编排应用连接容器。

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

-----------------华丽的分割线--------------------
修改日期:2015年4月3日

有多种方法来配置一个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 Ubuntu
FROM        ubuntu

# File Author / Maintainer
MAINTAINER Anand Mani Sankar

# Update the repository and install Redis  Server
RUN         apt-get update && apt-get install -y redis-server

# Expose Redis port 6379
EXPOSE      6379

# Run Redis Server
ENTRYPOINT  [“/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);

// APPROACH 1: Using environment variables created by Docker
// var client = redis.createClient(
//  process.env.REDIS_PORT_6379_TCP_PORT,
//      process.env.REDIS_PORT_6379_TCP_ADDR
// );

// APPROACH 2: Using host entries created by Docker in /etc/hosts (RECOMMENDED)
var client = redis.createClient('6379', 'redis');

app.get('/', function(req, res, next) {
client.incr('counter', function(err, counter) {
if(err) return next(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定义,以方便容器间通讯。

-----------------华丽的分割线--------------------
修改日期:2015年3月31日

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

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

Node Dockerfile:

# Set the base image to Ubuntu
FROM    ubuntu

# File Author / Maintainer
MAINTAINER Anand Mani Sankar

# Update the repository
RUN apt-get update

# Install Node.js and other dependencies
RUN apt-get -y install curl
RUN curl -sL https://deb.nodesource.com/setup | sudo bash -
RUN apt-get -y install python build-essential nodejs

# Install nodemon
RUN npm install -g nodemon

# Bundle app source
COPY . /src

# Install app dependencies
RUN cd /src; npm install

# Expose port
EXPOSE  8080

# Run app using nodemon
CMD ["nodemon", “/src/index.js"]

上面的Dockerfile解释如下:

  • 从Docker Hub拉取Ubuntu基础镜像
  • 使用apt-get安装Node.js以及依赖
  • 使用npm安装nodemon
  • 从host目录复制应用源码到容器内 src
  • 运行 npm install 安装Node应用依赖
  • 端口8080从容器抛出,使用nodemon运行应用

使用Dockerfile构建一个Docker镜像:

docker build -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-app upstream 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 image
FROM nginx

# File Author / Maintainer
MAINTAINER Anand Mani Sankar

# Copy custom configuration file from the current directory
COPY 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 Ubuntu
FROM ubuntu

# File Author / Maintainer
MAINTAINER 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 repository
RUN apt-get update

# Install necessary tools
RUN apt-get install -y nano wget dialog net-tools

# Download and Install Nginx
RUN apt-get install -y nginx  

# Remove the default Nginx configuration file
RUN rm -v /etc/nginx/nginx.conf

# Copy a configuration file from the current directory
ADD nginx.conf /etc/nginx/

# Append "daemon off;" to the configuration file
RUN echo "daemon off;" >> /etc/nginx/nginx.conf

# Expose ports
EXPOSE 80

# Set the default command to execute when creating a new container
CMD service nginx start

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

Docker Compose编排应用

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

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

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

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

nginx:
build: ./nginx
links:
    - node1:node1
    - node2:node2
    - node3:node3
ports:
    - "80:80"
node1:
build: ./node
links:
    - redis
ports:
    - "8080"
volumes:
    - node:/src
node2:
build: ./node
links:
    - redis
ports:
    - "8080"
volumes:
    - node:/src
node3:
build: ./node
links:
    - redis
ports:
    - "8080"
volumes:
    - node:/src
redis:
image: redis
ports:
    - “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:
    - docker

dependencies:
override:
    - sudo pip install -U docker-compose

test:
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流程实例。如果你有任何建议和更好的方法,请发表评论。
原文链接:A sample Docker workflow with Nginx, Node.js and Redis (翻译:吴锦晟 校对:李颖杰)

=======================
译者介绍
吴锦晟,硕士研究生,就职于上海金桥信息股份有限公司技术中心。目前负责云计算、虚拟化、大数据及其信息可视化等方向的研究和应用。希望通过翻译技术文章于DockOne来回馈社区。

原文发布时间为:2015-04-05

本文作者:吴锦晟 

本文来自合作伙伴DockerOne,了解相关信息可以关注DockerOne。

原文标题:【实战】基于Nginx、Node.js和Redis的Docker工作流

时间: 2024-09-11 07:44:07

【实战】基于Nginx、Node.js和Redis的Docker工作流的相关文章

基于NodeJS的前后端分离的思考与实践(六)Nginx + Node.js + Java 的软件栈部署实践_node.js

淘宝网线上应用的传统软件栈结构为 Nginx + Velocity + Java,即: 在这个体系中,Nginx 将请求转发给 Java 应用,后者处理完事务,再将数据用 Velocity 模板渲染成最终的页面. 引入 Node.js 之后,我们势必要面临以下几个问题: 技术栈的拓扑结构该如何设计,部署方式该如何选择,才算是科学合理?项目完成后,该如何切分流量,对运维来说才算是方便快捷?遇到线上的问题,如何最快地解除险情,避免更大的损失?如何确保应用的健康情况,在负载均衡调度的层面加以管理?承系

《Redis入门指南》一5.4 Node.js与Redis

5.4 Node.js与Redis Redis入门指南 Redis官方推荐的Node.js客户端是node_redis1. 5.4.1 安装 使用npm install redis命令安装最新版本的node_redis,目前版本是0.8.2. 5.4.2 使用方法 首先加载node_redis模块: var redis = require('redis'); 下面的代码将创建一个默认连接到地址127.0.0.1,端口6379的Redis连接: var client = redis.createC

Node.js自动导航模式在Docker的应用

本文讲的是Node.js自动导航模式在Docker的应用[编者的话]Joyent公司作为云计算和服务提供商,不久前刚被三星公司并购.该公司开源了两个Node.js模块,Consulite和Piloted,旨在使用Node.js来整合Docker容器.本文通过实例介绍了如何使用这两个模块在Node.js中应用AutoPilot Patten(自动导航模式)在Docker容器中快捷地构建应用. 自动导航模式使得当程序被部署的时候能够自动根据配置构建应用程序变得非常简单(容易在开发和测试的时候获得一个

node.js应用Redis数据库

node.js下使用Redis,首先: 1.有一台安装了Redis的服务器,当然,安装在本机也行 2.本机,也就是客户端,要装node.js 3.项目要安装nodejs_redis模块 注意第 3 点,不是在本机安装就行了,而是说,要在项目中安装(引用). 方法是,DOS窗口,在项目目录下,输入 npm install redis 这样就将nodejs_redis下载一份,放到当前目录下了.看看,多了一个文件夹:node_modules\redis   编写以下代码,保存到当前目录下\hello

为高负载网络优化 Nginx 和 Node.js

来源: http://www.oschina.net/translate/optimising-nginx-node-js-and-networking-for-heavy-workloads 英文原文:Optimising NginX, Node.JS and networking for heavy workloads 在搭建高吞吐量web应用这个议题上,NginX和Node.js可谓是天生一对.他们都是基于事件驱动模型而设计,可以轻易突破Apache等传统web服务器的C10K瓶颈.预设的

在Node.js应用中读写Redis数据库的简单方法

  这篇文章主要介绍了在Node.js应用中读写Redis数据库的简单方法,Redis是一个内存式高速数据库,需要的朋友可以参考下 在开始本文之前请确保安装好 Redis 和 Node.js 以及 Node.js 的 Redis 扩展 -- node_redis 首先创建一个新文件夹并新建文本文件 app.js 文件内容如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var redis = require("redis") ,

在Node.js应用中使用Redis的方法简介

  在开始本文之前请确保安装好 Redis 和 Node.js 以及 Node.js 的 Redis 扩展 -- node_redis 首先创建一个新文件夹并新建文本文件 app.js 文件内容如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var redis = require("redis") , client = redis.createClient();   client.on("error", f

在Node.js应用中读写Redis数据库的简单方法_node.js

 在开始本文之前请确保安装好 Redis 和 Node.js 以及 Node.js 的 Redis 扩展 -- node_redis 首先创建一个新文件夹并新建文本文件 app.js 文件内容如下:   var redis = require("redis") , client = redis.createClient(); client.on("error", function (err) { console.log("Error " + er

「开往春天的 Node.js」 - Node 地下铁第二期线下沙龙总结

前言 寒冬已逝,春之伊始. 3 月 26 日下午,Node 地下铁第 2 次线下沙龙在上海世博展览馆万信酒店举行.本次沙龙邀请了四位在不同领域应用 Node.js 进行探索的大牛,带着我们在温暖的午后,感受 Node.js 的魅力. 虽然当天是近来久违的好天气,而且会场地点有些偏,但这些都没有令同学们对 Node.js 的热情衰减,13 点半签到,很快会议室的位置所剩无几了,在这里,组委会感谢各位同学的支持. 回顾 Node.js 源站的发展与挑战 淘宝首页是淘宝很重要的一个页面,它原本一直运行