本文以ASP.NET应用为例,讲述了如何通过NGINX访问运行在本机上的ASP.NET 5服务,并采用Docker Compose对ASP服务进行编排,同时提供简单的负载均衡机制。
ASP.NET 5可以在很多操作系统下运行,也支持IIS等多种不同的web服务器。网上关于ASP.NET 5的教程有很多,这里我就不多说了,大家可以自己去搜索。今天我们要讲的是如何通过NGINX访问运行在本机上的ASP.NET 5服务,并采用Docker Compose对ASP服务进行编排,同时提供简单的负载均衡机制。
当然,在Docker Compose出现之前,.NET web开发领域也有其他实现负载均衡的办法,比方说,大家也可以在mono框架下运行ASP.NET Web API应用,并通过NGINX提供访问接口,供用户访问。只不过这样做有点麻烦,用ASP.NET 5 来实现这些功能要方便得多。
本文中我们用的就是ASP.NET 5,整个应用搭建好以后大概就是下图这个架构,详细的代码我会贴在文中,方便大家学习:
这里我建了一个简单的ASP.NET 5应用,这个应用会输出一条问候信息,同时列出本机的环境变量。项目架构如下所示:
tugberk@ubuntu:~/apps/aspnet-5-samples/nginx-lb-sample$ tree
.
├── docker-compose.yml
├── docker-nginx.dockerfile
├── docker-webapp.dockerfile
├── global.json
├── nginx.conf
├── NuGet.Config
├── README.md
└── WebApp
├── hosting.json
├── project.json
└── Startup.cs
应用程序的信息代码很简单,这里就不贴了。不过大家要注意一点,由于我们需要通过Kestrel公开ASP.NET 5应用的服务器URL,在公开的时候最好采用“0.0.0.0”,而不是localhost或者127.0.0.1,这样才能更好地支持Docker。我用的hosting.json file 是这样写的:
{
"server": "Microsoft.AspNet.Server.Kestrel",
"server.urls": "http://0.0.0.0:5090"
}
接下来就该在Docker容器里运行ASP.NET 5了。整个过程非常简单,大家一看就懂。首先我们要先从Docker Hub中下载ASP.NET的镜像,这里我用的是下面的Dockerfile脚本:
FROM microsoft/aspnet:1.0.0-rc1-update1
COPY ./WebApp/project.json /app/WebApp/
COPY ./NuGet.Config /app/
COPY ./global.json /app/
WORKDIR /app/WebApp
RUN ["dnu", "restore"]
ADD ./WebApp /app/WebApp/
EXPOSE 5090
ENTRYPOINT ["dnx", "run"]
就是这短短几行命令,ASP.NET 5应用就可以跑在Docker容器里面了。下面要做的就是构建并运行Docker镜像:
docker build -f docker-webapp.dockerfile -t hellowebapp .
docker run -d -p 5090:5090 hellowebapp
执行上面两行命令,以detached模式运行容器。容器跑起来之后我们就可以在自己的主机上提供HTTP终端访问该ASP应用了:
大家可以对容器进行任何操作,重建、停止、删除等等都可以。
终于来到最后一步,这里我们要引入NGINX 和 Docker Compose了。可能有些读者还不熟悉NGINX,我先科普一下,NGINX是一个免费的高性能开源HTTP服务器,同时也是一个反向代理服务器。在生产环境中,我们不能直接将Kestrel公开给外部网络用户,最好的办法就是用NGINX、IIS或Apache Web Server之类的技术成熟的服务器做代理,将Kestrel的服务转一道手再提供给用户访问。
至于这样做的原因,大家可以去网上搜索相关的教程,这里就不赘述了。总之大家切记一点,在生产环境中运行应用时千万不能直接把Kestrel暴露给外网。
介绍了NIGINX,那么什么是Docker Compose呢?Docker Compose跟NIGNX的性质完全不同,它是一个工具,可以用来对多容器应用进行定义和管理。有了Compose我们就可以通过Compose file(一种YAML文件)来配置应用服务,在本例中我们要同时运行三个以上的容器,用Compose来进行管理将大大提升运行效率。本例中各容器作用如下:
・ 容器1:运行ASP.NET 5 应用实例
・ 容器2:运行另一个ASP.NET 5 应用实例
・ NGINX容器:运行NGINX进程,为访问ASP.NET 5应用的请求提供代理服务。
为了在Docker容器中运行NIGNX,下面我们先对NGINX进行配置。Docker Hub上也提供了NGINX的镜像,我们可以通过该镜像来设置NGINX的配置文件。我把配置文件设成了下面这样:
worker_processes 4;
events { worker_connections 1024; }
http {
upstream web-app {
server webapp1:5090;
server webapp2:5090;
}
server {
listen 80;
location / {
proxy_pass http://web-app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}
这个配置文件没什么特别之处,唯一要指出的是,我们在该文件中对负载均衡和反向代理功能进行了设置。NGINX会根据这些设置来接收来自80端口的请求,并将请求分发到webapp1:5090和webapp2:5090上。当然了,NGINX的设置是门大学问,不过上面的简单设置足够满足我们在本例中的需要了。
这里我要多说一句,因为Kestrel的RC1版有bug(RC2版已经修正了这个bug),为了更好地支持Kestrel,我们要在NGINX配置中加一个“Connection: keep-alive”的header,大家可以在上面贴出的代码里看到,我在里面写了“proxy_set_header Connection keep-alive;”。
NGINX的Dockerfile脚本如下:
FROM nginx
COPY ./nginx.conf /etc/nginx/nginx.conf
大家可能要问,在本例中webapp1和webapp2(我们之前在NGINX的配置文件中已经进行了设置)的映射关系是怎样的呢?我们设置了运行ASP.NET 5应用的容器的DNS reference文件,我们将reference文件链接到在Docker Compose文件时,DNS会自动映射到相应的容器。Docker Compose 文件中的composition设置如下:
webapp1:
build: .
dockerfile: docker-webapp.dockerfile
container_name: hasample_webapp1
ports:
- "5091:5090"
webapp2:
build: .
dockerfile: docker-webapp.dockerfile
container_name: hasample_webapp2
ports:
- "5092:5090"
nginx:
build: .
dockerfile: docker-nginx.dockerfile
container_name: hasample_nginx
ports:
- "5000:80"
links:
- webapp1
- webapp2
大家可以看到,在nginx容器的定义中,我把之前定义好的两个容器连接到了NGINX容器。
到这里准备工作就完成了,接下来我们只需执行下面两条docker-compose命令即可启动应用:
docker-compose build
docker-compose up
现在三个容器都跑起来了。大家可以访问本机的 localhost:5000端口,看看NGINX是否在提供负载均衡功能,把访问请求分摊到两个ASP.NET 5应用的容器上:
大功告成!现在我们已经可以通过NGINX和Docker Compose来实现多容器的负载均衡和反向代理了。不过本文只是一个简单例子,如果大家要把所有容器都跑在一个box里面的话,本文的方法就不合适了。大家如果需要更多这方面的例子,可以关注我的公众号,我会继续为大家深入讲解Docker和负载均衡之间不得不说的故事。