NGINX教程:如何部署和配置微服务

NGINX | 2023年3月07日

本文是四篇教程之一,旨在帮助您将以下内容中的概念付诸实践:2023年3月微服务专题:开始交付微服务:

所有应用都需要配置,但配置微服务时的考虑因素可能与配置单体应用时有所不同。我们可以参考原则 3(在环境中存储配置),这是十二要素应用中同时适用于这两类应用的指导原则,但可以针对微服务应用进行调整。具体而言,我们可以调整服务配置的定义方式、向服务提供配置的方式,以及将服务作为配置值提供给可能依赖该服务的其他服务的方式。

要从概念上了解如何针对微服务调整因素3,尤其是配置文件、数据库和服务发现方面的良好实践,请阅读我们博客中的文章《微服务应用配置良好实践》。本文有助于将这些知识付诸实践。

注意:本教程旨在说明一些核心概念,而非展示在生产环境中部署微服务的正确方式。虽然教程采用了真实的“微服务”架构,但仍有一些重要注意事项:

  • 本教程不使用Kubernetes或Nomad等容器编排框架,以便您专注于学习微服务概念,而不必深陷某个特定框架的具体细节。本教程介绍的模式可移植到运行此类框架的系统中。
  • 这些服务经过优化,侧重于易于理解,而非严格遵循软件工程规范。重点是了解服务在系统中的作用及其通信模式,而不是代码的具体细节。有关更多信息,请参阅各服务的README文件。

教程概述

本教程介绍如何将Factor 3概念应用于微服务应用。通过4项挑战,您将探索一些常见的微服务配置模式,并使用这些模式部署和配置服务:

  • 在挑战 1和挑战 2中,您将探索第一种模式,了解微服务应用的配置应存放在何处。通常有以下三个位置:
    • 应用程序代码
    • 应用程序的部署脚本
    • 部署脚本访问的外部源
  • 在挑战 3中,您又设置了两种模式:通过将NGINX用作反向代理向外部公开应用,以及使用Consul启用服务发现。
  • 在挑战 4中,您将实现最后一种模式:将微服务的一个实例用作“作业运行程序”,执行一项不同于其常规功能的一次性操作(本例中为模拟数据库迁移)。

本教程使用以下四种技术:

  • messenger:专为本教程创建的简单聊天API,支持消息存储
  • NGINX Open Source,作为messenger服务及整个系统的入口
  • Consul,动态服务注册表和键值存储
  • RabbitMQ,一种广受欢迎的开源消息代理,可实现服务之间的异步通信

拓扑图显示NGINX Open Source和Consul模板在同一容器中运行。Consult模板与Consult客户端通信。NGINX Open Source充当messenger服务的反向代理;该服务将数据存储在messenger_db中,并与Rabbit MQ通信。

观看此视频,了解本教程的概况。视频中的步骤与本文并不完全一致,但有助于理解相关概念。

 

先决条件和设置

先决条件

要在自己的环境中完成本教程,需要:

  • 与Linux/Unix兼容的环境
  • 基本熟悉Linux命令行、JavaScript和bash(但我们会提供并讲解所有代码和命令,因此即使相关知识有限,也能顺利完成)
  • Docker和Docker Compose
  • Node.js 19.x或更高版本
    • 我们测试了19.x版本,但预计较新版本的Node.js也可以正常运行。
    • 有关安装Node.js的详细信息,请参阅README,该文件位于messenger服务代码库中。还可以安装asdf,以获取与容器中所用版本完全相同的Node.js版本。
  • curl(大多数系统已安装)
  • 以下是教程概述中列出的4项技术:messenger(将在下一节下载)、NGINX Open Source、Consul和RabbitMQ。

设置

  1. 启动一个终端会话(后续说明中将其称为“应用终端”)。
  2. 在主目录中创建microservices-march目录,并将本教程所需的GitHub存储库克隆到该目录中。(也可以使用其他目录名称,并相应调整说明。)注意:为便于将命令复制并粘贴到终端,本教程中的Linux命令行均省略提示符。波浪号(~)表示主目录。
mkdir ~/microservices-marchcd ~/microservices-march git clone https://github.com/microservices-march/platform.git --branch mm23-twelve-factor-start git clone https://github.com/microservices-march/messenger.git --branch mm23-twelve-factor-start
  1. 切换到platform代码仓库并启动Docker Compose:
cd platformdocker compose up -d --build
  1. 这将同时启动RabbitMQ和Consul,后续挑战中会用到它们。
    • The -d 标志指示Docker Compose在容器启动后与其分离(否则容器将继续附加到终端)。
    • The --build 标志指示Docker Compose在启动时重新构建所有镜像。这样,即使文件可能发生更改,正在运行的镜像也能保持更新。
  2. 切换到messenger存储库并启动Docker Compose:
cd ../messengerdocker compose up -d --build
  1. 这将启动messenger服务的PostgreSQL数据库。在本教程的后续内容中,我们将其称为messenger-database。

挑战 1:定义应用级微服务配置

在本挑战中,您将在本教程介绍的三个配置位置中的第一个位置(应用级别)设置配置。(挑战 2将介绍第二个和第三个位置:部署脚本和外部源。)

十二要素应用明确排除了应用级配置,因为此类配置无需随不同的部署环境(十二要素应用将其称为“部署”)而变化。不过,为确保内容完整,我们仍会介绍全部三种类型,因为在开发、构建和部署服务时,每类配置的处理方式各不相同。

The messenger服务使用Node.js编写,入口点位于app/index.mjs,该文件在messenger代码库中。文件中的以下代码行:

app.use(express.json());

是应用级配置的一个示例。它将Express framework配置为把类型为application/json的请求正文反序列化为JavaScript对象。

此逻辑与应用程序代码紧密耦合,并不属于十二要素应用所定义的“配置”。不过,在软件开发中,一切都取决于具体情况,不是吗?

在接下来的两节中,您将修改此行,以实现两个应用级配置示例。

示例 1

在此示例中,您将设置messenger服务可接受的请求正文大小上限。此大小限制由express.json函数的limit参数设置,具体请参阅Express API文档。在此,将limit参数添加到上文所述的Express框架JSON中间件配置中。

  1. 使用常用的文本编辑器打开app/index.mjs,然后替换以下内容:
app.use(express.json())
  1. 替换为:
app.use(express.json({ limit: "20b" }));
  1. 在应用终端(即在“设置”中使用的终端)中,切换到app目录并启动messenger服务:
cd app npm install node index.mjs
messenger_service listening on port 4000
  1. 启动另一个独立的终端会话(后续说明中称为“客户端终端”),并向POST服务发送messenger请求。该错误消息表明,请求已成功处理,因为请求正文未超过步骤1中设置的20字节限制,但JSON有效负载的内容不正确:
curl -d '{ "text": "hello" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
{ "error": "Conversation must have 2 unique users" }
  1. 发送一个稍长的消息正文(仍在客户端终端中操作)。此时的输出比步骤3多得多,其中包括一条错误消息,表明本次请求正文超过20字节:
curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
\”PayloadTooLargeError: request entity too large"

示例 2

此示例使用convict库,可在单个文件中定义完整的配置“模式”。它还说明了十二要素应用方法中要素3的两项准则:

  • 将配置存储在环境变量中:修改应用,使用环境变量(JSON_BODY_LIMIT)设置最大请求正文大小,而不是将其硬编码在应用代码中。
  • 明确定义服务配置:这是针对微服务对要素3所做的调整。如果您不熟悉这一概念,建议花些时间阅读我们博客中的微服务应用配置最佳实践。

该示例还设置了一些基础机制,供您在“挑战2”中使用:您将在该挑战中创建的messenger部署脚本会设置JSON_BODY_LIMIT环境变量,而您会在此处将该变量插入应用代码,以说明如何在部署脚本中指定配置。

  1. 打开convict配置文件app/config/config.mjs,并在amqpport键后添加以下新键:
jsonBodyLimit: {  doc: `The max size (with unit included) that will be parsed by the
        JSON middleware. Unit parsing is done by the
        https://www.npmjs.com/package/bytes library.
        ex: "100kb"`,
  format: String,
  default: null,
  env: "JSON_BODY_LIMIT",
},
  1. convict库会负责解析JSON_BODY_LIMIT环境变量。您将在下面的步骤 3 中通过命令行使用该变量设置请求正文大小上限:
    • 从正确的环境变量中获取值
    • 检查变量的类型(String)
    • 允许在应用程序中通过jsonBodyLimit键访问该变量
  2. 在“app/index.mjs”中替换:
app.use(express.json({ limit: "20b" }));
  1. ,并使用
app.use(express.json({ limit: config.get("jsonBodyLimit") }));
  1. 在应用终端中(即在示例 1的第 2 步中启动messenger服务的终端),按Ctrl+c停止该服务。然后重新启动该服务,并使用JSON_BODY_LIMIT环境变量将请求正文大小上限设置为 27 字节:
^cJSON_BODY_LIMIT=27b node index.mjs
  1. 这是一个根据用例需要修改配置方式的示例。您已不再将值(此处为大小限制)硬编码到应用代码中,而是按照十二要素应用的建议,改用环境变量进行设置。如上所述,在挑战 2中,当您使用JSON_BODY_LIMIT环境变量时,它将成为第二个配置位置的示例:您将通过messenger服务的部署脚本设置该环境变量,而不是在命令行中进行设置。
  2. 在客户端终端中,重复执行curl命令,即示例 1的步骤 4(使用较大的请求正文)。由于现在已将大小限制提高到 27 字节,请求正文不再超出限制,因此会收到一条错误消息,表明请求已处理,但 JSON 有效负载的内容不正确:
curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations{ "error": "Conversation must have 2 unique users" }
  1. 如果愿意,可以关闭客户端终端。本教程后续的所有命令都将在应用终端中执行。
  2. 在应用终端中,按Ctrl+c停止messenger服务(您已在上述步骤 3 中通过此终端停止并重启了该服务)。
^c
  1. 停止messenger-database。可以放心忽略显示的错误消息,因为platform存储库中定义的基础设施元素仍在使用该网络。请在messenger存储库的根目录下运行此命令。
docker compose down
...failed to remove network mm_2023....

挑战 2:为服务创建部署脚本

“配置应与代码严格分离(否则,如何在不同部署之间调整配置?)”——摘自十二要素应用的第3项原则

乍看之下,您可能会将其理解为“不要将配置签入源代码管理系统”。在本挑战中,您将为微服务环境实现一种常见模式。该模式看似违反了这条规则,实际上却遵循了规则,同时还能带来对微服务环境至关重要的流程改进。

在本挑战中,您将创建部署脚本,模拟基础设施即代码和部署清单的功能,为微服务提供配置;然后修改脚本,使其使用外部配置源;设置密钥;最后运行脚本,部署服务及其基础设施。

在infrastructure目录中新建部署脚本。该目录位于messenger代码仓库中。在现代微服务架构中,通常会使用名为infrastructure(或类似名称)的目录来存储以下内容:

此模式的优势包括:

  • 它将服务部署以及服务专用基础设施(如数据库)部署的所有权分配给负责该服务的团队。
  • 团队可以确保对其中任何元素的更改都经过其开发流程(代码审查、CI等)。
  • 团队可以轻松更改服务及其支持基础设施的部署方式,无需依赖外部团队代为完成相关工作。

如前所述,本教程并非旨在演示如何搭建真实系统,您在本挑战中部署的脚本也并不代表真实的生产系统。相反,这些脚本旨在说明部署微服务相关基础设施时的一些核心概念,以及特定工具配置所解决的问题,同时尽可能减少脚本对特定工具的依赖。

创建初始部署脚本

  1. 在应用终端中,在messenger存储库的根目录下创建infrastructure目录,并创建用于存放messenger服务和messenger-database的部署脚本文件。根据具体环境,可能需要在chmod命令前添加sudo:
mkdir infrastructurecd infrastructure touch messenger-deploy.sh chmod +x messenger-deploy.sh touch messenger-db-deploy.sh chmod +x messenger-db-deploy.sh
  1. 在常用的文本编辑器中打开messenger-deploy.sh,然后添加以下内容,为messenger服务创建初始部署脚本:
#!/bin/bashset -e

JSON_BODY_LIMIT=20b

docker run \
  --rm \
  -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
  messenger

此脚本目前尚不完整,但它说明了几个概念:

  • 通过将相关配置直接包含在部署脚本中,为环境变量赋值。
  • 它使用-e标志,通过docker run命令在运行时将环境变量注入容器。

以这种方式设置环境变量的值可能显得多余,但这样一来,无论此部署脚本变得多么复杂,只需快速查看脚本最上方,就能了解配置数据是如何提供给部署的。

此外,实际的部署脚本虽然不一定会显式调用docker run命令,但此示例脚本旨在说明Kubernetes清单等文件所解决的核心问题。使用Kubernetes等容器编排系统时,部署过程会启动容器,并向该容器提供从Kubernetes配置文件中获取的应用配置。因此,可以将此示例部署文件视为部署脚本的精简版本,其作用与Kubernetes清单等特定于框架的部署文件相同。

在实际开发环境中,可以将此文件签入源代码管理系统并进行代码审查。这样,团队其他成员就有机会对相关设置提出意见,从而有助于避免因值配置错误而导致意外行为。例如,在此屏幕截图中,一位团队成员正确地指出,传入JSON请求正文的大小上限设为20字节(通过JSON_BODY_LIMIT设置)过低。

代码审查截图,其中指出消息正文的20字节限制过小

修改部署脚本,以从外部源查询配置值

在本部分挑战中,您将为微服务配置设置第三个存储位置:在部署时查询的外部源。与硬编码值相比,在部署时动态注册值并从外部源获取这些值是更合理的做法。硬编码值需要不断更新,并且可能导致故障。有关详细讨论,请参阅我们博客中的《微服务应用配置最佳实践》。

此时,两个基础设施组件正在后台运行,为messenger服务提供所需的辅助服务:

  1. RabbitMQ,在实际部署中由平台团队负责(已在“设置”的第 3 步中启动)
  2. 在实际部署中由贵团队负责的messenger-database(已在“设置”的第4步中启动)

The convict架构在messenger服务的app/config/config.mjs中定义了与这些外部配置项对应的必需环境变量。本节将设置这两个组件,使其能够提供配置。具体方法是将变量值设置在一个可共同访问的位置,以便messenger服务在部署时查询这些值。

RabbitMQ和messenger-database所需的连接信息已注册到Consul键值(KV)存储中。该存储是所有服务部署时均可访问的公共位置。Consul KV存储并非存储此类数据的标准位置,但本教程为简化操作而采用这种方式。

  1. 将infrastructure/messenger-deploy.sh(在上一节的第2步中创建)的内容替换为以下内容:
#!/bin/bashset -e

# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb

# Postgres database configuration by pulling information from 
# the system
POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)

# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)

docker run \
  --rm \
  -e NODE_ENV="${NODE_ENV}" \
  -e PORT="${PORT}" \
  -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
  -e PGUSER="${POSTGRES_USER}" \
  -e PGPORT="${PGPORT}" \
  -e PGHOST="${PGHOST}" \
  -e AMQPPORT="${AMQPPORT}" \
  -e AMQPHOST="${AMQPHOST}" \
  messenger
  1. 此脚本展示了两种配置类型:
    • 直接在部署脚本中指定的配置:设置部署环境(NODE_ENV)和端口(PORT),并将JSON_BODY_LIMIT更改为100 KB,这比20字节更符合实际需求。
    • 从外部源查询的配置:从Consul KV存储中获取环境变量POSTGRES_USER、PGPORT、PGHOST、AMQPHOST和AMQPPORT的值。可通过以下两个步骤在Consul KV存储中设置这些环境变量的值。
  2. 打开messenger-db-deploy.sh,添加以下内容,为messenger-database创建初始部署脚本:
#!/bin/bashset -e

PORT=5432
POSTGRES_USER=postgres

docker run \
  -d \
  --rm \
  --name messenger-db \
  -v db-data:/var/lib/postgresql/data/pgdata \
  -e POSTGRES_USER="${POSTGRES_USER}" \
  -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
  -e PGPORT="${PORT}" \
  -e PGDATA=/var/lib/postgresql/data/pgdata \
  --network mm_2023 \
  postgres:15.1

# Register details about the database with Consul
curl -X PUT http://localhost:8500/v1/kv/messenger-db-port \
  -H "Content-Type: application/json" \
  -d "${PORT}"

curl -X PUT http://localhost:8500/v1/kv/messenger-db-host \
  -H "Content-Type: application/json" \
  -d 'messenger-db' # This matches the "--name" flag above
                    # (the hostname)

curl -X PUT http://localhost:8500/v1/kv/messenger-db-application-user \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_USER}"
  1. 除了定义可供messenger服务在部署时查询的配置外,该脚本还说明了与创建初始部署脚本中messenger服务的初始脚本相同的两个概念:
    • 它直接在部署脚本中指定某些配置。在本例中,这些配置用于告知PostgreSQL数据库要在哪个端口上运行,以及默认用户的用户名。
    • 它使用-e标志运行Docker,以便在运行时将环境变量注入容器。它还将运行中容器的名称设置为messenger-db。该名称会成为数据库在Docker网络中的主机名;此网络是在设置的第2步中启动platform服务时创建的。
  2. 在实际部署中,通常由平台团队(或类似团队)负责在platform仓库中部署和维护RabbitMQ之类的服务,就像您在messenger仓库中管理messenger-database一样。随后,平台团队会确保依赖该基础设施的服务能够发现其位置。在本教程中,请自行设置RabbitMQ的值:
curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Content-Type: application/json" \ -d "rabbitmq" \ http://localhost:8500/v1/kv/amqp-host curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Content-Type: application/json" \ -d "5672" \ http://localhost:8500/v1/kv/amqp-port
  1. (您可能想知道为什么使用amqp来定义RabbitMQ变量,这是因为AMQP是RabbitMQ使用的协议。)

在部署脚本中设置密钥

服务的部署脚本中只缺少一项(关键)数据:messenger 服务的 messenger-database 密码!

注意:本教程不重点介绍密钥管理,因此为简化操作,密钥直接定义在部署文件中。切勿在实际环境(无论是开发、测试还是生产环境)中这样做,否则会造成巨大的安全风险。

要了解如何正确管理密钥,请参阅Microservices March 2023的“第2单元:微服务密钥管理入门”。(提前透露:只有使用密钥管理工具,才能真正安全地存储密钥。)

  1. 将infrastructure/messenger-db-deploy.sh的内容替换为以下内容,以便将messenger-database的密码密钥存储在Consul KV存储中:
#!/bin/bashset -e

PORT=5432
POSTGRES_USER=postgres
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
POSTGRES_PASSWORD=postgres

docker run \
  --rm \
  --name messenger-db-primary \
  -d \
  -v db-data:/var/lib/postgresql/data/pgdata \
  -e POSTGRES_USER="${POSTGRES_USER}" \
  -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
  -e PGPORT="${PORT}" \
  -e PGDATA=/var/lib/postgresql/data/pgdata \
  --network mm_2023 \
  postgres:15.1

echo "Register key messenger-db-port\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
  -H "Content-Type: application/json" \
  -d "${PORT}"

echo "Register key messenger-db-host\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
  -H "Content-Type: application/json" \
  -d 'messenger-db-primary' # This matches the "--name" flag above
                            # which for our setup means the hostname

echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_USER}"

echo "Register key messenger-db-password-never-do-this\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_PASSWORD}"

printf "\nDone registering postgres details with Consul\n"
  1. 将infrastructure/messenger-deploy.sh的内容替换为以下内容,以从Consul KV存储中获取messenger-database密码密钥:
#!/bin/bashset -e

# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb

# Postgres database configuration by pulling information from 
# the system
POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true)

# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)

docker run \
  --rm \
  -d \
  -e NODE_ENV="${NODE_ENV}" \
  -e PORT="${PORT}" \
  -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
  -e PGUSER="${POSTGRES_USER}" \
  -e PGPORT="${PGPORT}" \
  -e PGHOST="${PGHOST}" \
  -e PGPASSWORD="${PGPASSWORD}" \
  -e AMQPPORT="${AMQPPORT}" \
  -e AMQPHOST="${AMQPHOST}" \
  --network mm_2023 \
  messenger

运行部署脚本

  1. 切换到app目录(位于messenger仓库中),并为messenger服务构建Docker镜像:
cd ../appdocker build -t messenger .
  1. 确认只有属于platform服务的容器正在运行:
docker ps --format '{{.Names}}'consul-server
consul-client
rabbitmq
  1. 切换到messenger存储库的根目录,然后部署messenger-database和messenger服务:
cd .../infrastructure/messenger-db-deploy.sh ./infrastructure/messenger-deploy.sh
  1. The messenger-db-deploy.sh脚本会启动messenger-database,并向系统(在本例中为Consul KV存储)注册相应信息。随后,messenger-deploy.sh脚本会启动应用程序,并从系统(同样为Consul KV存储)中提取由messenger-db-deploy.sh注册的配置。提示:如果容器无法启动,请在部署脚本中删除docker run命令的第二个参数(即-d \所在行),然后再次运行该脚本。之后,容器将在前台启动,其日志会显示在终端中,可能有助于确定问题。问题解决后,请恢复-d \所在行,以便实际容器在后台运行。
  2. 向应用发送一个简单的运行状况检查请求,以验证部署是否成功:
curl localhost:4000/healthcurl: (7) Failed to connect to localhost port 4000 after 11 ms: Connection refused
  1. 糟糕,失败了!原来还缺少一项关键配置,messenger服务尚未向整个系统开放。该服务在mm_2023网络中正常运行,但该网络只能从Docker内部访问。
  2. 停止正在运行的容器,为下一项挑战中创建新镜像做好准备:
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))

挑战 3:向外部开放服务

在生产环境中,通常不会直接对外公开服务,而是遵循常见的微服务模式,在主服务前部署反向代理服务。

在本挑战中,您将通过设置服务发现向外部公开messenger服务。服务发现会注册新的服务信息,并在其他服务访问这些信息时动态更新。为此,您将使用以下技术:

  • Consul(动态服务注册表)和Consul template(根据Consul数据动态更新文件的工具)
  • NGINX Open Source,作为反向代理和负载均衡器,为messenger服务提供单一入口点。该服务由多个在容器中运行的独立应用实例组成

要详细了解服务发现,请参阅博客文章将服务作为配置提供,该文章收录于《微服务应用配置最佳实践》。

设置Consul

The app/consul/index.mjs文件位于messenger代码库中,其中包含在启动时向Consul注册messenger服务并在正常关闭时将其注销所需的全部代码。该文件公开了一个函数register,用于向Consul的服务注册表注册任何新部署的服务。

  1. 在常用的文本编辑器中打开app/index.mjs,然后在其他import语句后添加以下代码片段,以从register函数从app/consul/index.mjs导入:
import { register as registerConsul } from "./consul/index.mjs";
  1. 然后按如下所示修改脚本末尾的SERVER START部分,以便在应用程序启动后调用registerConsul():
/* =================  SERVER START
================== */
app.listen(port, async () => {
  console.log(`messenger_service listening on port ${port}`);
  registerConsul();
});

export default app;
  1. 打开convict架构文件app/config/config.mjs,在jsonBodyLimit键后添加以下配置值。该键是您在示例 2的步骤 1 中添加的。
  consulServiceName: {    doc: "The name by which the service is registered in Consul. If not specified, the service is not registered",
    format: "*",
    default: null,
    env: "CONSUL_SERVICE_NAME",
  },
  consulHost: {
    doc: "The host where the Consul client runs",
    format: String,
    default: "consul-client",
    env: "CONSUL_HOST",
  },
  consulPort: {
    doc: "The port for the Consul client",
    format: "port",
    default: 8500,
    env: "CONSUL_PORT",
  },
  1. 此配置用于设置新服务的注册名称,并定义Consul客户端的主机名和端口。下一步,修改messenger服务的部署脚本,以加入新的Consul连接和服务注册信息。
  2. 打开infrastructure/messenger-deploy.sh,并将其内容替换为以下内容,以便在messenger服务配置中加入上一步设置的Consul连接和服务注册信息:
#!/bin/bashset -e

# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb

CONSUL_SERVICE_NAME="messenger"

# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"

# Postgres database configuration by pulling information from 
# the system
POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-application-user?raw=true")
PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
PGHOST=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-host?raw=true")
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true")

# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")

docker run \
  --rm \
  -d \
  -e NODE_ENV="${NODE_ENV}" \
  -e PORT="${PORT}" \
  -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
  -e PGUSER="${POSTGRES_USER}" \
  -e PGPORT="${PGPORT}" \
  -e PGHOST="${PGHOST}" \
  -e PGPASSWORD="${PGPASSWORD}" \
  -e AMQPPORT="${AMQPPORT}" \
  -e AMQPHOST="${AMQPHOST}" \
  -e CONSUL_HOST="${CONSUL_HOST}" \
  -e CONSUL_PORT="${CONSUL_PORT}" \
  -e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
  --network mm_2023 \
  messenger
  1. 主要注意事项如下:注意: 在实际部署中,此配置示例必须由相关团队协商确定。负责Consul的团队必须在所有环境中提供CONSUL_HOST和CONSUL_PORT环境变量,因为服务缺少这些连接信息便无法查询Consul。
    • The CONSUL_SERVICE_NAME环境变量用于指定messenger服务实例向Consul注册时使用的名称。
    • 环境变量CONSUL_HOST和CONSUL_PORT用于在部署脚本运行位置运行的Consul客户端。
  2. 在应用终端中,切换到app目录,停止messenger服务的所有运行实例,然后重新构建Docker镜像,将新的服务注册代码写入镜像:
cd appdocker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}")) docker build -t messenger .
  1. 在浏览器中访问http://localhost:8500,查看Consul UI的运行情况(不过目前还没有什么值得关注的内容)。
  2. 在messenger代码仓库的根目录中,运行部署脚本以启动一个messenger服务实例:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
  1. 在浏览器的Consul UI中,单击标题栏中的“Services”,确认有且仅有一个“messenger”服务正在运行。
    Consul UI 的 Services 选项卡,其中 consul 和 messenger 服务各有一个实例
  2. 再运行几次部署脚本,以启动更多messenger服务实例。在Consul UI中确认这些实例正在运行。
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
  1. Consul UI中的“messenger”选项卡,显示该服务的5个实例

设置NGINX

下一步是添加NGINX Open Source作为反向代理和负载均衡器,将传入流量路由到所有正在运行的messenger实例。

  1. 在应用终端中,切换到messenger代码库的根目录,然后创建一个名为load-balancer的目录和以下3个文件:
mkdir load-balancercd load-balancer touch nginx.ctmpl touch consul-template-config.hcl touch Dockerfile
  1. The Dockerfile定义了运行NGINX和Consul template的容器。当服务注册表中的messenger服务发生变化(服务实例上线或下线)时,Consul template会使用另外两个文件动态更新NGINX上游配置。
  2. 打开在步骤1中创建的nginx.ctmpl文件,并添加以下NGINX配置片段。Consul template使用该片段动态更新NGINX上游组:
upstream messenger_service {    {{- range service "messenger" }}
    server {{ .Address }}:{{ .Port }};
    {{- end }}
}

server {
    listen 8085;
    server_name localhost;

    location / {
        proxy_pass http://messenger_service;
        add_header Upstream-Host $upstream_addr;
    }
}
  1. 此代码片段将Consul中注册的每个messenger服务实例的IP地址和端口号添加到NGINX的messenger_service上游组。NGINX将传入请求代理到动态定义的上游服务实例集。
  2. 打开在步骤1中创建的“consul-template-config.hcl”文件,并添加以下配置:
consul {  address = "consul-client:8500"

  retry {
    enabled  = true
    attempts = 12
    backoff  = "250ms"
  }
}
template {
  source      = "/usr/templates/nginx.ctmpl"
  destination = "/etc/nginx/conf.d/default.conf"
  perms       = 0600
  command     = "if [ -e /var/run/nginx.pid ]; then nginx -s reload; else nginx; fi"
}
  1. 此Consul template配置会指示其重新渲染source模板(即上一步创建的NGINX配置片段),将其放置在指定的destination位置,最后运行指定的command(该命令会指示NGINX重新加载配置)。实际上,这意味着每当在Consul中注册、更新或注销服务实例时,都会创建一个新的default.conf文件。随后,NGINX会在不中断服务的情况下重新加载配置,确保其拥有一组最新且运行正常的服务器(messenger服务实例),以便向其发送流量。
  2. 打开在步骤1中创建的Dockerfile文件,并添加以下内容以构建NGINX服务。(完成本教程无需理解Dockerfile,但为方便参考,代码中已添加注释。)
FROM nginx:1.23.1
ARG CONSUL_TEMPLATE_VERSION=0.30.0

# Set an environment variable for the location of the Consul
# cluster. By default, it tries to resolve to consul-client:8500
# which is the behavior if Consul is running as a container in the 
# same host and linked to this NGINX container (with the alias 
# consul, of course). But this environment variable can also be
# overridden as the container starts if we want to resolve to
# another address.

ENV CONSUL_URL consul-client:8500

# Download the specified version of Consul template
ADD https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip /tmp

RUN apt-get update \
  && apt-get install -y --no-install-recommends dumb-init unzip \
  && unzip /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip -d /usr/local/bin \
  && rm -rf /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip

COPY consul-template-config.hcl ./consul-template-config.hcl
COPY nginx.ctmpl /usr/templates/nginx.ctmpl

EXPOSE 8085

STOPSIGNAL SIGQUIT

CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]
  1. 构建Docker镜像:
docker build -t messenger-lb .
  1. 切换到messenger目录的根目录,创建名为messenger-load-balancer-deploy.sh的文件,作为NGINX服务的部署文件(与本教程中部署其他服务时的操作相同)。根据具体环境,可能需要在chmod命令前添加sudo:
cd .. touch infrastructure/messenger-load-balancer-deploy.sh chmod +x infrastructure/messenger-load-balancer-deploy.sh
  1. 打开messenger-load-balancer-deploy.sh,并添加以下内容:
#!/bin/bashset -e

# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"

docker run \
  --rm \
  -d \
  --name messenger-lb \
  -e CONSUL_URL="${CONSUL_HOST}:${CONSUL_PORT}"  \
  -p 8085:8085 \
  --network mm_2023 \
  messenger-lb
  1. 一切准备就绪后,部署NGINX服务:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-load-balancer-deploy.sh
  1. 检查是否可以从外部访问messenger服务:
curl -X GET http://localhost:8085/health
OK
  1. 成功了!NGINX现在会在已创建的所有messenger服务实例之间进行负载均衡。可以确认这一点,因为X-Forwarded-For标头中显示的messenger服务IP地址与上一节第 8 步中Consul UI里显示的相同。

挑战 4:使用服务作为作业运行程序迁移数据库

大型应用通常会使用由小型工作进程组成的“作业运行器”,来执行修改数据等一次性任务(例如Sidekiq和Celery)。这些工具通常还需要Redis或RabbitMQ等额外的配套基础设施。在本例中,直接将messenger服务用作“作业运行器”来执行一次性任务。这样做很合理,因为该服务本身非常小巧,完全能够与数据库及其依赖的其他基础设施组件交互,并且与处理流量的应用完全独立运行。

这样做有三个好处:

  1. 作业运行程序(包括其运行的脚本)需经过与生产服务完全相同的检查和审核流程。
  2. 可以轻松更改数据库用户等配置值,以提高生产部署的安全性。例如,可以使用“低权限”用户运行生产服务,该用户只能写入和查询现有表。还可以配置另一个服务实例,使用能够创建和删除表的高权限用户来更改数据库结构。
  3. 有些团队会在同时处理服务生产流量的实例上运行作业。这种做法存在风险,因为作业出现问题时,可能会影响容器内应用正在执行的其他功能。我们采用微服务,不正是为了避免这类情况吗?

在本挑战中,您将探索如何通过更改一些数据库配置值来修改构件,使其承担新的角色,并迁移messenger数据库以使用新值,然后测试其性能。

迁移 messenger 数据库

在实际生产部署中,可以创建两个权限不同的独立用户:“应用程序用户”和“迁移用户”。为简化操作,本示例将默认用户用作应用程序用户,并创建一个拥有超级用户权限的迁移用户。在实际场景中,建议根据每个用户的角色,仔细确定其所需的具体最小权限。

  1. 在应用终端中,创建一个具有超级用户权限的新PostgreSQL用户:
echo "CREATE USER messenger_migrator WITH SUPERUSER PASSWORD 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres
  1. 打开数据库部署脚本(infrastructure/messenger-db-deploy.sh),替换其中的内容以添加新用户的凭据。注意:这里再次强调,对于实际部署,切勿将数据库凭据等机密信息放入部署脚本或机密信息管理工具以外的任何位置。有关详细信息,请参阅Microservices March 2023中的第2单元:微服务机密信息管理基础知识。
#!/bin/bash
set -e

PORT=5432
POSTGRES_USER=postgres
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
# Because we’re focusing on other concepts in this tutorial, we
# set the password this way here for convenience.
POSTGRES_PASSWORD=postgres

# Migration user
POSTGRES_MIGRATOR_USER=messenger_migrator
# NOTE: As above, never do this in a real deployment.
POSTGRES_MIGRATOR_PASSWORD=migrator_password

docker run \
  --rm \
  --name messenger-db-primary \
  -d \
  -v db-data:/var/lib/postgresql/data/pgdata \
  -e POSTGRES_USER="${POSTGRES_USER}" \
  -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
  -e PGPORT="${PORT}" \
  -e PGDATA=/var/lib/postgresql/data/pgdata \
  --network mm_2023 \
  postgres:15.1

echo "Register key messenger-db-port\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
  -H "Content-Type: application/json" \
  -d "${PORT}"

echo "Register key messenger-db-host\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
  -H "Content-Type: application/json" \
  -d 'messenger-db-primary' # This matches the "--name" flag above
                            # which for our setup means the hostname

echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_USER}"

curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_PASSWORD}"

echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-user \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_MIGRATOR_USER}"

curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this \
  -H "Content-Type: application/json" \
  -d "${POSTGRES_MIGRATOR_PASSWORD}"

printf "\nDone registering postgres details with Consul\n"
  1. 此更改只是将迁移用户添加到数据库部署后在Consul中设置的用户集合中。
  2. 在infrastructure目录中新建一个名为messenger-db-migrator-deploy.sh的文件(同样,可能需要在chmod命令前加上sudo):
touch infrastructure/messenger-db-migrator-deploy.shchmod +x infrastructure/messenger-db-migrator-deploy.sh
  1. 打开“messenger-db-migrator-deploy.sh”,并添加以下内容:
#!/bin/bashset -e

# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb

CONSUL_SERVICE_NAME="messenger-migrator"

# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"

# Get the migrator user name and password
POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-user?raw=true")
PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true")

# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")

docker run \--rm \
  -d \
  --name messenger-migrator \
  -e NODE_ENV="${NODE_ENV}" \
  -e PORT="${PORT}" \
  -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
  -e PGUSER="${POSTGRES_USER}" \
  -e PGPORT="${PGPORT}" \
  -e PGHOST="${PGHOST}" \
  -e PGPASSWORD="${PGPASSWORD}" \
  -e AMQPPORT="${AMQPPORT}" \
  -e AMQPHOST="${AMQPHOST}" \
  -e CONSUL_HOST="${CONSUL_HOST}" \
  -e CONSUL_PORT="${CONSUL_PORT}" \
  -e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
  --network mm_2023 \
  messenger
  1. 此脚本与infrastructure/messenger-deploy.sh脚本的最终版本非常相似,后者是您在第 3 步“设置 Consul”中创建的。主要区别在于,CONSUL_SERVICE_NAME设置为messenger-migrator而不是messenger,并且PGUSER对应于您在上述第 1 步中创建的“migrator”超级用户。请务必将CONSUL_SERVICE_NAME设置为messenger-migrator。如果将其设置为messenger,NGINX会自动将此服务加入轮转以接收API调用,但该服务并非用于处理任何流量。此脚本会部署一个充当迁移器的短期实例,从而避免迁移过程中出现的问题影响主要messenger服务实例处理流量。
  2. 重新部署PostgreSQL数据库。由于本教程使用bash脚本,因此需要停止并重启数据库服务。在生产应用中,通常只需改为运行基础设施即代码脚本,仅添加发生变更的元素。
docker stop messenger-db-primaryCONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-deploy.sh
  1. 部署PostgreSQL数据库迁移器服务:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-migrator-deploy.sh
  1. 验证实例是否按预期运行:
docker ps --format "{{.Names}}"
...
messenger-migrator
  1. 还可以在Consul UI中验证数据库迁移服务是否已正确向Consul注册为messenger-migrator(同样,由于该服务不处理流量,因此不会以messenger名称注册):
    Consul UI 的“Services”选项卡,其中 consul 和 messenger-migrator 服务各有1个实例,messenger 服务有4个实例
  2. 现在进行最后一步:运行数据库迁移脚本!这些脚本与实际的数据库迁移脚本并不相似,但确实会使用messenger-migrator服务来运行特定于数据库的脚本。数据库迁移完成后,停止messenger-migrator服务:
docker exec -i -e PGDATABASE=postgres -e CREATE_DB_NAME=messenger messenger-migrator node scripts/create-db.mjsdocker exec -i messenger-migrator node scripts/create-schema.mjs docker exec -i messenger-migrator node scripts/create-seed-data.mjs docker stop messenger-migrator

实际测试messenger服务

现在,您已将messenger数据库迁移到最终格式,可以实际查看messenger服务的运行情况了!为此,请对NGINX服务运行一些基本的curl查询(您已在“设置NGINX”中将NGINX配置为系统入口点)。

以下部分命令使用jq库来格式化JSON输出。可根据需要安装该库,也可以从命令行中省略相关内容。

  1. 创建对话:
curl -d '{"participant_ids": [1, 2]}' -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations'{
  "conversation": { "id": "1", "inserted_at": "YYYY-MM-DDT06:41:59.000Z" }
}
  1. 以ID为1的用户身份向该对话发送消息:
curl -d '{"content": "This is the first message"}' -H "User-Id: 1" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
  "message": {
    "id": "1",
    "content": "This is the first message",
    "index": 1,
    "user_id": 1,
    "username": "James Blanderphone",
    "conversation_id": 1,
    "inserted_at": "YYYY-MM-DDT06:42:15.000Z"
  }
}
  1. 使用另一位用户(ID 为 2)的消息进行回复:
curl -d '{"content": "This is the second message"}' -H "User-Id: 2" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
  "message": {
    "id": "2",
    "content": "This is the second message",
    "index": 2,
    "user_id": 2,
    "username": "Normalavian Ropetoter",
    "conversation_id": 1,
    "inserted_at": "YYYY-MM-DDT06:42:25.000Z"
  }
}
  1. 获取消息:
curl -X GET 'http://localhost:8085/conversations/1/messages' | jq{
  "messages": [
    {
      "id": "1",
      "content": "This is the first message",
      "user_id": "1",
      "channel_id": "1",
      "index": "1",
      "inserted_at": "YYYY-MM-DDT06:42:15.000Z",
      "username": "James Blanderphone"
    },
    {
      "id": "2",
      "content": "This is the second message",
      "user_id": "2",
      "channel_id": "1",
      "index": "2",
      "inserted_at": "YYYY-MM-DDT06:42:25.000Z",
      "username": "Normalavian Ropetoter"
    }
  ]
}

清理

在本教程中,您已创建了大量容器和镜像。使用以下命令移除不需要保留的Docker容器和镜像。

  • 删除所有正在运行的Docker容器:
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))docker rm $(docker stop messenger-db-primary) docker rm $(docker stop messenger-lb)
  • 要移除平台服务,请执行以下操作:
# From the platform repositorydocker compose down
  • 要删除本教程中使用的所有Docker镜像:
docker rmi messengerdocker rmi messenger-lb docker rmi postgres:15.1 docker rmi hashicorp/consul:1.14.4 docker rmi rabbitmq:3.11.4-management-alpine

后续步骤

您可能会想:“只是设置一个简单的功能,似乎就要做很多工作。”确实如此!转向以微服务为重点的架构,需要细致规划服务的结构和配置。尽管过程很复杂,您仍取得了切实的进展:

  • 您设置了以微服务为重点的配置,便于其他团队理解。
  • 您对微服务系统进行配置,使其在扩缩容和各项相关服务的使用方面具备一定的灵活性。

如需继续学习微服务相关知识,请参阅Microservices March 2023。第 2 单元:微服务密钥管理入门深入浅出地介绍了微服务环境中的密钥管理。


分享

相关博客文章

NGINX Plus R26 正式发布
NGINX | 2022/02/15

NGINX Plus R26 正式发布

NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
NGINX | 2022/02/10

使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击

gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
NGINX | 2021/09/13

Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划

了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。

NGINX Plus R19 正式发布
NGINX | 2019/09/03

NGINX Plus R19 正式发布

NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。

NGINX现已正式加入F5
NGINX | 2019/05/09

NGINX现已正式加入F5

NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。

使用NGINX和NGINX Plus实现高性能缓存
NGINX | 2016/08/24

使用NGINX和NGINX Plus实现高性能缓存

我将介绍NGINX在内容缓存和性能方面的工作原理,并分享一些控制NGINX内容缓存方式的技巧。