基于Docker的Nginx实践

Nginx是一个轻量级的高性能的Web、反向代理服务器,其在内存占用、并发等方面表现突出。这里基于Docker说明、实践其典型用法——反向代理、负载均衡、动静分离

abstract.png

反向代理

为便于演示,这里我们先在本地的hosts文件添加两条不同域名的映射。具体地,我们将service1.com、service2.com均映射到本地。如下所示

figure 1.jpeg

这里通过Docker Compose建立包含一个Nginx、两个Tomcat的服务,具体如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Compose 版本
version: '3.8'

# 定义Docker服务
services:

# Nginx 服务
Nginx-Service-1:
image: nginx:1.21
container_name: Nginx-Service-1
ports:
- "80:80"
volumes:
- ./Config/Nginx/nginx.conf:/etc/nginx/nginx.conf
networks:
reverseProxy_service_net:
ipv4_address: 123.115.107.80

# Tomcat 服务 1
Tomcat-Service-1:
image: tomcat:jre8-temurin-focal
container_name: Tomcat-Service-1
ports:
- "9111:8080"
volumes:
- ./Config/Service1:/usr/local/tomcat/webapps/ROOT
networks:
reverseProxy_service_net:
ipv4_address: 123.115.107.81

# Tomcat 服务 2
Tomcat-Service-2:
image: tomcat:jre8-temurin-focal
container_name: Tomcat-Service-2
ports:
- "9222:8080"
volumes:
- ./Config/Service2:/usr/local/tomcat/webapps/ROOT
networks:
reverseProxy_service_net:
ipv4_address: 123.115.107.82

# 定义网络
networks:
reverseProxy_service_net:
ipam:
config:
- subnet: 123.115.107.0/24

这里我们将Nginx的配置文件挂载到宿主机当中,并在http块中添加两个server块,具体如下所示。不难看出Nginx会将来自service1.com、service2.com两个不同域名的请求转发至各自指定的Tomcat服务。这里目标服务的地址使用的均是Tomcat容器内部的IP、Port

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...

http {
server {
# 监听端口
listen 80;
# 监听域名
server_name service1.com;
location / {
# 转发请求 至 指定目标服务
proxy_pass http://123.115.107.81:8080;
}
}

server {
# 监听端口
listen 80;
# 监听域名
server_name service2.com;
location / {
# 转发请求 至 指定目标服务
proxy_pass http://123.115.107.82:8080;
}
}

...
}

Tomcat的两个容器服务Tomcat-Service-1、Tomcat-Service-2分别使用如下的HTML文件

1
2
3
4
5
# Tomcat-Service-1 容器服务使用的HTML文件内容:
Hello, This is Tomcat Service 1

# Tomcat-Service-2 容器服务使用的HTML文件内容:
Hello, This is Tomcat Service 2

至此,所有相关文件的目录结构如下所示

figure 2.png

在启动容器服务后,我们分别通过各自Tomcat服务的端口进行访问,确认各Tomcat服务是否均正常。效果如下所示

figure 3.jpeg

现在我们看下通过不同域名进行访问,Nginx是否会将相关请求转发至指定Tomcat服务。效果如下所示,符合预期

figure 4.jpeg

负载均衡

为便于演示,这里我们先在本地的hosts文件再添加一个域名的映射。具体地,我们将hello.com映射到本地。如下所示

figure 5.jpeg

类似地,我们再次通过Docker Compose建立包含一个Nginx、两个Tomcat的服务,具体如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Compose 版本
version: '3.8'

# 定义Docker服务
services:

# Nginx 服务
Nginx-Service:
image: nginx:1.21
container_name: Nginx-Service
ports:
- "80:80"
volumes:
- ./Config/Nginx/nginx.conf:/etc/nginx/nginx.conf
networks:
loadBalance_service_net:
ipv4_address: 69.69.69.80

# Tomcat 服务 A
Tomcat-Service-A:
image: tomcat:jre8-temurin-focal
container_name: Tomcat-Service-A
ports:
- "8111:8080"
volumes:
- ./Config/ServiceA:/usr/local/tomcat/webapps/ROOT
networks:
loadBalance_service_net:
ipv4_address: 69.69.69.81

# Tomcat 服务 B
Tomcat-Service-B:
image: tomcat:jre8-temurin-focal
container_name: Tomcat-Service-B
ports:
- "8222:8080"
volumes:
- ./Config/ServiceB:/usr/local/tomcat/webapps/ROOT
networks:
loadBalance_service_net:
ipv4_address: 69.69.69.82

# 定义网络
networks:
loadBalance_service_net:
ipam:
config:
- subnet: 69.69.69.0/24

这里我们将Nginx的配置文件挂载到宿主机当中,并在http块中添加一个server块,具体如下所示。
Nginx会将来自hello.com的请求转发至名为helloServerCluster的服务集群,然后通过upstream指定该服务集群下的各实例地址。这里实例服务的地址使用的均是Tomcat容器内部的IP、Port

这里补充说明下,Nginx支持如下的负载均衡策略:

  1. 轮询:默认策略
  2. weight:基于权重的策略,权重越高优先级越高
  3. fair:基于服务实例的响应时间策略,实例响应时间越短优先级越高
  4. ip_hash:根据访问IP的Hash值确定服务实例,可以保证同一个IP的访问请求总是被转发至同一个服务实例

显然这里我们采用Nginx默认的轮询策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...

http {
server {
# 监听端口
listen 80;
# 监听域名
server_name hello.com;
location / {
# 转发请求 至 服务集群
proxy_pass http://helloServerCluster;
}
}

# 定义一个名为 helloServerCluster 服务集群
upstream helloServerCluster {
# 该服务集群下各实例地址
server 69.69.69.81:8080;
server 69.69.69.82:8080;
}

...
}

Tomcat的两个容器服务Tomcat-Service-A、Tomcat-Service-B分别使用如下的HTML文件

1
2
3
4
5
# Tomcat-Service-A 容器服务使用的HTML文件内容:
[Tomcat Service A]: Hello~

# Tomcat-Service-B 容器服务使用的HTML文件内容:
[Tomcat Service B]: Hello~

至此,所有相关文件的目录结构如下所示

figure 6.jpeg

在启动容器服务后,我们分别通过各自Tomcat服务的端口进行访问,确认各Tomcat服务是否均正常。效果如下所示

figure 7.jpeg

现在我们看下通过80端口进行访问,Nginx是否会将相关请求依次转发至上述两个Tomcat服务。效果如下所示,符合预期

figure 8.jpeg

动静分离

既然Nginx本身就是一个Web服务器,那我们就可以通过动静分离将静态资源文件部署在Nginx上。为便于演示,这里我们在本地的hosts文件再添加一个域名的映射。具体地,我们将test.com映射到本地。如下所示

figure 9.jpeg

类似地,我们再次通过Docker Compose建立包含一个Nginx、一个Tomcat的服务,具体如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Compose 版本
version: '3.8'

# 定义Docker服务
services:

# Nginx 服务
Nginx-Service-1:
image: nginx:1.21
container_name: Nginx-Service-1
ports:
- "80:80"
volumes:
- ./Config/Nginx/nginx.conf:/etc/nginx/nginx.conf
networks:
static_service_net:
ipv4_address: 151.151.12.80

# Tomcat 服务 1
Tomcat-Service-1:
image: tomcat:jre8-temurin-focal
container_name: Tomcat-Service-1
ports:
- "9111:8080"
volumes:
- ./Config/Service1:/usr/local/tomcat/webapps/ROOT
networks:
static_service_net:
ipv4_address: 151.151.12.81

# 定义网络
networks:
static_service_net:
ipam:
config:
- subnet: 151.151.12.0/24

这里我们将Nginx的配置文件挂载到宿主机当中,并在http块中添加一个server块,具体如下所示。这里我们配置了3个location规则。前两个分别是根据请求路径前缀、请求资源类型进行匹配的,同时通过root指定相关静态资源文件的父目录;最后一个location则是一个通用匹配,用于匹配之前两个location无法匹配的请求,具体地我们将其转发至Tomcat服务进行处理。同理,这里Tomcat服务的地址使用的是Tomcat容器内部的IP、Port

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...

http {
server {
# 监听端口
listen 80;
# 监听域名
server_name test.com;

# ^~ 修饰符表示前缀匹配, 即只匹配以/static/开头的请求
location ^~ /static/ {
# 指定静态文件的父级目录
root /resource;
}

# ~ 修饰符表示 区分大小写的正则匹配, 即匹配以.png结尾的请求
location ~ .png$ {
# 指定静态文件的父级目录
root /resource;
}

# / 修饰符表示通用匹配, 用于匹配其它location无法匹配的请求
location / {
# 转发请求 至 指定服务
proxy_pass http://151.151.12.81:8080;
}
}

...
}

Tomcat容器服务使用如下的HTML文件

1
2
# Tomcat-Service-1 容器服务使用的HTML文件内容:
Hello, Welcome To Tomcat

然后我们进入Nginx容器,创建、存放相应的静态文件。首先我们在根目录下创建resource目录,然后在其中再分别创建两个子目录:fig、static。前者的目录中存放一张图片;后者的目录中存放了两个html文件。后面会解释为什么需要创建这两个子目录

figure 10.jpeg

至此宿主机环境中相关文件的目录结构如下所示

figure 11.jpeg

一切准备就绪后,我们进行访问测试。通过下述测试结果的红框不难看出,我们访问Nginx的静态资源文件成功,而蓝框结果则表明我们通过Nginx进行反向代理访问Tomcat服务成功。这里补充说明下,Nginx确定静态资源文件的路径,是通过相应location的root配置项 + 资源路径拼接后得到。例如对于 test.com/static/a.html 请求而言,相应location的root配置项为/resource,资源路径为/static/a.html。则该静态资源在Nginx中的存放路径就必须是/resource/static/a.html

figure 12.jpeg

同理,对于Nginx容器中/resource/fig目录下的图片dog.png,其完整的请求路径就应该是test.com/fig/dog.png

测试结果如下所示,符合预期

figure 13.jpeg

0%