使用 Nginx 和 docker-compose 设置反向代理

2025-06-08

使用 Nginx 和 docker-compose 设置反向代理

Nginx 是一款出色的软件,它允许您轻松地将应用程序包装在反向代理中,然后可以处理与服务器相关的方面,例如 SSL 和缓存,对其背后的应用程序完全透明。

这是从我的个人网站转发的帖子

介绍

Web 应用程序的某些方面,例如 SSL 加密、请求缓存和服务发现,可以在应用程序本身之外进行管理。像 Nginx 这样的反向代理可以处理其中的许多任务,因此我们作为开发人员无需在软件中考虑这些。

此外,有些软件由于缺乏适当的安全措施,不宜通过互联网访问。许多数据库都是如此。一般来说,不将不必要的内部服务公开是一种良好做法。

所有这些都可以通过 docker-compose 和 Nginx 实现。

docker-compose

docker-compose是一个简洁的小工具,可以让你定义一系列需要同时启动的 docker 容器,以及它们启动时的配置。这些配置包括导出的端口、容器所属的网络、映射到容器的卷、环境变量以及所有其他可以通过命令配置的内容docker run

在本节中,我将简要解释如何配置本文中使用的 docker-compose 功能。更多详细信息,请参阅文档

主入口点是一个docker-compose.yml文件。它配置了需要一起启动的容器的各个方面。

以下是一个例子docker-compose.yml

version: '3'
services:
  nginx: 
    image: nginx:latest
    container_name: production_nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 80:80
      - 443:443

  ismydependencysafe:
    image: ismydependencysafe:latest
    container_name: production_ismydependencysafe
    expose:
      - "80"

如您所见,指定了两个镜像。

第一个nginx镜像名为production_nginx。它指定了一个卷,用于替换默认的 Nginx 配置文件。此外,它还定义了主机端口 80 和 443 到容器端口 80 和 443 的映射。

第二个镜像是我自己创建的,它暴露了 80 端口。与ports配置的不同之处在于,它们不会发布到主机。这就是为什么它也可以指定 80 端口,尽管nginx之前已经指定了。

本文还使用了一些其他配置选项,特别是网络、卷和环境变量。

网络

通过网络,可以指定哪些容器可以相互通信。它们被指定为新的根配置条目,并包含在容器配置中。

version: '3'
services:
  nginx:
    ...
    networks:
      - my-network-name

  ismydependencysafe:
    ...
    networks:
      - my-network-name

networks:
  my-network-name:

在根对象中networks,定义了网络my-network-name。每个容器都通过将它添加到列表中来分配到该网络network

如果未指定网络,则所有容器都位于同一个网络中,该网络是默认创建的。因此,如果仅使用一个网络,则无需指定任何网络。

网络的一个便捷特性是,同一个网络中的容器可以通过名称相互引用。在上面的例子中,urlhttp://ismydependencysafe将解析为 container ismydependencysafe

卷定义了 Docker 容器的持久存储。如果应用程序在未定义卷的地方写入数据,容器停止时该数据将会丢失。

卷有两种类型。一种是将文件或目录映射到容器内部;另一种是仅将文件或目录持久化(称为卷),但不使其在文件系统上可访问(当然它们在某个地方,但这是 Docker 实现特定的,不应干预)。

第一种类型是将特定文件或目录映射到容器中的卷,我们已经在上面的示例中见过。这里再次介绍一下,其中还有一个附加卷,它以相同的方式指定目录:

version: '3'
services:
  nginx: 
    image: nginx:latest
    container_name: production_nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - /etc/letsencrypt/:/etc/letsencrypt/
...

命名卷的指定方式与网络类似,作为单独的根配置条目并直接在容器配置上。

version: '3'
services:
  nginx:
    ...
    volumes:
      - "certificates:/etc/letsencrypt/"

    ...

volumes:
  certificates:
...

环境变量

Docker 还可以为容器中的应用程序指定环境变量。在 Compose 配置中,有多种方法可以做到这一点:要么指定包含它们的文件,要么直接在 中声明它们docker-compose.yml

version: '3'
services:
  nginx:
    ...
    env_file:
      - ./common.env
    environment:
      - ENV=development
      - APPLICATION_URL=http://ismydependencysafe
    ...

如您所见,这两种方法可以同时使用。但请注意,设置的变量environment会覆盖从文件加载的变量。

环境文件必须具有以下格式VAR=VAL,每行一个变量。

ENV=production
APPLICATION_URL=http://ismydependencysafe

命令行界面

启动和停止容器的命令非常简单。

要启动,请使用docker-compose up -d

指定-d它应在后台启动。如果没有它,容器将在命令行关闭时停止。

停止使用docker-compose down

这两个命令都会在当前目录中查找docker-compose.yml文件。如果文件位于其他目录,请使用 指定-f path/to/docker-compose.yml

现在 docker-compose 的基础知识已经清楚了,让我们继续讨论 Nginx。

Nginx

Nginx 是一款功能丰富的 Web 服务器,其中包括本文所介绍的反向代理功能。

它配置了一个nginx.conf。默认情况下,它会在 中查找/etc/nginx/nginx.conf,但当然也可以指定其他文件。

作为反向代理,它可以透明地处理 Web 应用程序的两个非常重要的方面:加密和缓存。但在详细介绍之前,让我们先看看反向代理功能本身是如何配置的:

http {
  server {
    server_name your.server.url;

    location /yourService1 {
      proxy_pass http://localhost:80;
      rewrite ^/yourService1(.*)$ $1 break;
    }

    location /yourService2 {
      proxy_pass http://localhost:5000;
      rewrite ^/yourService1(.*)$ $1 break;
    }
  }

  server {
    server_name another.server.url;

    location /yourService1 {
      proxy_pass http://localhost:80;
      rewrite ^/yourService1(.*)$ $1 break;
    }

    location /yourService3 {
      proxy_pass http://localhost:5001;
      rewrite ^/yourService1(.*)$ $1 break;
    }
  }
}

Nginx 配置以contexts为单位,contexts 定义了它们处理的流量类型。context http(显然)处理 http 流量。其他 contexts 包括mailstream

server配置指定了一个虚拟服务器,每个虚拟服务器可以拥有自己的规则。该server_name指令定义了虚拟服务器响应哪些 URL 或 IP 地址。

location配置定义了传入流量的路由目标。根据 URL,请求可以传递到某个服务。在上面的配置中,路由的开头指定了服务。

proxy_pass设置新的 URL,并rewrite重写 URL 以使其适合该服务。在本例中,yourService{x}将从 URL 中删除 。

这是一个概述,后面的部分将解释如何配置缓存和 SSL。

有关更多详细信息,请查看文档

现在我们已经了解了各个部分,让我们开始将它们组合在一起。

在 Docker 中将 Nginx 设置为反向代理

对于基本设置,只需要 3 件事:

1)将主机端口映射到容器端口
2)将配置文件映射到默认 Nginx 配置文件/etc/nginx/nginx.conf
3)Nginx 配置

在 docker-compose 文件中,可以使用配置条目完成端口映射ports,正如我们上面看到的。

    ...
    ports:
      - 80:80
      - 443:443
    ...

Nginx 配置的映射是通过卷完成的,我们之前也见过:

    ...
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ...

docker-compse.ymlNginx 配置被认为与( )位于同一目录中./nginx.conf,但当然可以位于任何位置。

缓存配置

在设置中添加缓存非常简单,只需更改 Nginx 配置即可。

http上下​​文中添加一条proxy_cache_path指令,定义缓存内容的本地文件系统路径以及内存区域的名称和大小。

但请记住,该路径位于容器内部,而不是主机的文件系统上。

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m;
}

在应缓存响应的上下文中,添加server指定内存区域的指令。locationproxy_cache

  ...
  server {
    proxy_cache one;
  ...

使用默认缓存配置定义缓存就足够了。还有很多其他指令可以更详细地指定要缓存哪些响应。有关这些指令的更多详细信息,请参阅文档

使用 SSL 保护 HTTP 流量

至此,服务器设置已完成。docker-compose 启动所有容器,Nginx 容器充当服务的反向代理。只剩下一件事需要设置,正如这个网站解释得非常清楚的那样,那就是加密。

要安装 certbot(从 Let's Encrypt 获取证书的客户端),请按照安装说明进行操作。

使用 certbot 生成 SSL 证书

certbot 提供多种获取 SSL 证书的方法。其中包括适用于 Apache 和 Nginx 等主流 Web 服务器的插件,以及使用独立 Web 服务器验证域名的方法,当然还有手动获取证书的方法。

我们将使用该standalone插件。它会启动一个单独的 Web 服务器来处理证书验证,这意味着 80 或 443 端口必须可用。为了使其正常工作,必须关闭 Nginx Web 服务器,因为它绑定了这两个端口,并且 certbot 服务器需要能够接受至少一个端口上的入站连接。

要创建证书,请执行

certbot --standalone -d your.server.url

-d并按照说明操作。您还可以通过添加更多参数(例如-d your.server1.url -d your.server2.url来一次为多个 URL 创建证书。

自动更新证书

Let's Encrypt CA 颁发的证书是短期证书,有效期仅为 90 天。因此,自动续订过程至关重要。幸好,certbot 使用命令 轻松实现了这一点certbot renew。它会检查所有已安装的证书,并续订那些将在 30 天内过期的证书。

续订时将使用与最初获取证书时相同的插件。在我们的例子中,就是这个standalone插件。

验证流程相同,因此续订时 80 或 443 端口也必须处于空闲状态。certbot

提供了预处理和后处理钩子,用于在续订期间停止和启动 Web 服务器,以释放端口。

这些钩子仅在证书需要续订时执行,因此不会造成不必要的服务停机。

由于我们使用的是docker-compose,因此整个命令如下所示:

certbot renew --pre-hook "docker-compose -f path/to/docker-compose.yml down" --post-hook "docker-compose -f path/to/docker-compose.yml up -d"

要完成自动化,只需将上一个命令添加为 cronjob。

使用 打开 cron 文件crontab -e

在其中添加一行

@daily certbot renew --pre-hook "docker-compose -f path/to/docker-compose.yml down" --post-hook "docker-compose -f path/to/docker-compose.yml up -d"

就是这样。现在续订命令每天都会执行,您不必再担心证书的到期日期了。

在 Nginx Docker 容器中使用证书

到目前为止,证书已经请求并存储在服务器上,但我们尚未使用它们。为此,我们必须

1)使证书可用于 Nginx 容器
2)更改配置以使用它们

要使证书可用于 Nginx 容器,只需将整个letsencrypt目录指定为其上的卷。

  ...
  nginx: 
    image: nginx:latest
    container_name: production_nginx
    volumes:
      - /etc/letsencrypt/:/etc/letsencrypt/
  ...

调整配置并使其更安全需要更多工作。
默认情况下,虚拟服务器监听端口 80,但使用 SSL 时,它还应该监听端口 443。这需要通过两个listen指令来指定。

此外,还必须定义证书。这可以通过ssl_certificatessl_certificate_key指令来完成。

  ...
  server {
    ...
    listen 80;
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/your.server.url/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your.server.url/privkey.pem;
  }
  ...

这些小改动足以配置 nginx 的 SSL 连接。

它使用 Nginx 的默认 SSL 设置,虽然还算可以,但还可以改进。

提高 Nginx 配置的安全性

在本节开头我应该提到,如果您使用最新版本的 nginx,其默认 SSL 设置是安全的。无需定义协议、密码和其他参数。

话虽如此,我们仍然可以使用一些 SSL 指令来进一步提升安全性。

但请记住,设置这些指令后,您有责任自行更新它们。Nginx 对默认配置设置的更改不会对您造成影响,因为您只是覆盖了它们。

首先,设置

ssl_protocols TLSv1.1 TLSv1.2;

这将禁用所有 SSL 协议和 TLSv1.0,因为它们被认为是不安全的(TLSv1.0SSLv3SSLv2)。在撰写本文时(2018 年 7 月),TLSv1.1 和 TLSv1.2 被认为是安全的,但没有人能保证它们将来不会被破坏。

接下来,设置

ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DHE+AES128:!ADH:!AECDH:!MD5;

密码定义了加密的方式。这些值是从这篇文章中复制过来的,因为我不是这方面的专家。

这些是最重要的设置。为了进一步提高安全性,请遵循以下文章:

您可以使用 SSL Labs 提供的优秀网站检查您的 SSL 配置的安全性

包起来

在本文中,我们介绍了如何设置 docker-compose、如何使用其网络和卷功能、如何设置环境变量、如何使用 Nginx 作为反向代理,以及缓存和 SSL 安全。托管项目所需的一切。

请记住,这不是一个非常专业的设置,任何重要的服务都需要更复杂的设置,但对于小型项目或副项目来说,这是完全没问题的。

修正案

以下是生成的nginx.conf文件docker-compose.yml。它们包含应用程序的占位符名称、URL 和路径。

docker-compose.yml

version: '3'
services:
  nginx: 
    image: nginx:latest
    container_name: production_nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/error.log:/etc/nginx/error_log.log
      - ./nginx/cache/:/etc/nginx/cache
      - /etc/letsencrypt/:/etc/letsencrypt/
    ports:
      - 80:80
      - 443:443

  your_app_1:
    image: your_app_1_image:latest
    container_name: your_app_1
    expose:
      - "80"

  your_app_2:
    image: your_app_2_image:latest
    container_name: your_app_2
    expose:
      - "80"

  your_app_3:
    image: your_app_3_image:latest
    container_name: your_app_3
    expose:
      - "80"

nginx.conf

events {

}

http {
  error_log /etc/nginx/error_log.log warn;
  client_max_body_size 20m;

  proxy_cache_path /etc/nginx/cache keys_zone=one:500m max_size=1000m;

  server {
    server_name server1.your.domain;

    location /your_app_1 {
      proxy_pass http://your_app_1:80;
      rewrite ^/your_app_1(.*)$ $1 break;
    }

    location /your_app_2 {
      proxy_pass http://your_app_2:80;
      rewrite ^/your_app_2(.*)$ $1 break;
    }
  }

  server {
    server_name server2.your.domain;
    proxy_cache one;
    proxy_cache_key $request_method$request_uri;
    proxy_cache_min_uses 1;
    proxy_cache_methods GET;
    proxy_cache_valid 200 1y;

    location / {
      proxy_pass http://your_app_3:80;
      rewrite ^/your_app_3(.*)$ $1 break;
    }

    listen 80;
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/server2.your.domain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/server2.your.domain/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
  }
}

在Twitter上关注我,了解我的更多想法、文章、项目和工作。

鏂囩珷鏉ユ簮锛�https://dev.to/domysee/setting-up-a-reverse-proxy-with-nginx-and-docker-compose-29jg
PREV
5 Things No One Tells You About Going to a Coding BootCamp Deer in headlights: Outside Resources: Community How Many rounds? Career Prep
NEXT
我建立的副项目可以帮助你找到很酷的开源项目并加入它们