本文是“Microservices June 微服务之月 2023”系列教程的延伸阅读之一,旨在帮助您将概念付诸实践。该系列教程包括:
该系列教程包括:
自动化部署对于大多数项目的成功而言至关重要。但是,仅仅部署代码还不够,您还需确保减少(或消除)停机时间,并能够在发生故障时迅速回滚。结合使用灰度部署和蓝绿部署是确保新代码成功部署的一种常见方法。该策略包括两个步骤:
I如果您尚不熟悉在不同版本的应用或网站之间分配流量(流量分割)的各种用例,请参阅我们的博文《如何通过高级流量管理提高 Kubernetes 的弹性》,以获得对蓝绿部署、灰度发布、A/B 测试、速率限制、断路等的概念性理解。虽然本文主要针对 Kubernetes,但这些概念广泛适用于微服务应用。
在本教程中,我们将展示如何使用 GitHub Actions 自动执行灰度蓝绿部署的第一步。在本教程的四个挑战中,您将使用 Microsoft Azure Container Apps 来部署新版本的应用,然后使用 Azure 流量管理器将流量从旧环境转移到新环境:
注:虽然本教程使用 Azure Container Apps,但相关概念和技术可应用于任何基于云的主机。
如果您想在自己的环境中学习本教程,则需要:
创建并配置必要的基础资源。创建分支(Fork)并克隆本教程所用的代码库,登录 Azure CLI,并为 Azure Container App 安装扩展插件。
在您的主目录下,创建 microservices-march 目录。(您也可以使用其他目录名称,相应修改指令即可)。
注:本教程中省略了 Linux 命令行提示符,以便您将命令复制和粘贴到终端。
mkdir ~/microservices-marchcd ~/microservices-march
使用 GitHub CLI 或 GUI Fork 为 Microservices March 平台代码库创建分支(Fork),并克隆到您的个人 GitHub 帐户。
如果使用 GitHub GUI:
在窗口的右上角点击 Fork,并在 Owner(所有者)菜单中选择您的个人 GitHub 帐户。
克隆代码库到本地,用您的帐户名称替换 <your_GitHub_account>
:
git clone https://github.com/<your_GitHub_account>/platform.gitcd platform
如果使用 GitHub CLI,则运行:
gh repo fork microservices-march/platform -–clone
登录到 Azure CLI。按照提示,使用浏览器登录:
az login
[
{
"cloudName": "AzureCloud",
"homeTenantId": "cfd11e0f-1435-450s-bdr1-ffab73b4er8e",
"id": "60efapl2-38ad-41w8-g49a-0ecc6723l97c",
"isDefault": true,
"managedByTenants": [],
"name": "Azure subscription 1",
"state": "Enabled",
"tenantId": "cfda3e0f-14g5-4e05-bfb1-ffab73b4fsde",
"user": {
"name": "user@example.com",
"type": "user"
}
}
]
安装 containerapp
扩展插件:
az extension add --name containerapp -upgradeThe installed extension 'containerapp' is in preview.
在首个挑战中,您需要创建一个 NGINX Azure 容器应用作为应用初始版本,用作灰度蓝绿部署基线。Azure Container Apps 是一种 Microsoft Azure 服务,支持您在生产就绪的容器环境中轻松执行封装在容器中的应用代码。
为容器应用创建一个 Azure 资源组:
az group create --name my-container-app-rg --location westus{
"id": "/subscriptions/0efafl2-38ad-41w8-g49a-0ecc6723l97c/resourceGroups/my-container-app-rg",
"location: "westus",
"managedBy": null,
"name": "my-container-app-rg",
"properties": {
"provisioningState": "Succeeded"
},
"tags": null,
"type": "Microsoft.Resources/resourceGroups"
}
将容器部署到 Azure Container Apps(这一步可能需要一些时间):
az containerapp up \ --resource-group my-container-app-rg \
--name my-container-app \
--source ./ingress \
--ingress external \
--target-port 80 \
--location westus
...
- image:
registry: cac085021b77acr.azurecr.io
repository: my-container-app
tag: "20230315212413756768"
digest: sha256:90a9fc67c409e244195ec0ea190ce3b84195ae725392e8451...
runtime-dependency:
registry: registry.hub.docker.com
repository: library/nginx
tag: "1.23"
digest: sha256:aa0afebbb3cfa473099a62c4b32e9b3fb73ed23f2a75a65ce...
git: {}
Run ID: cf1 was successful after 27s
Creating Containerapp my-container-app in resource group my-container-app-rg
Adding registry password as a secret with name "ca2ffbce7810acrazurecrio-cac085021b77acr"
Container app created. Access your app at https://my-container-app.delightfulmoss-eb6d59d5.westus.azurecontainerapps.io/
...
在第二步的输出结果中,找到您在 Azure 容器注册表 (ACR) 中创建的容器应用的名称和 URL。在示例输出结果中,它们以橙色高亮显示。在整个教程中,您需要用输出结果中的值(不同于第二步中的示例输出结果)来替换命令中的指定变量:
容器应用的名称——在 image.registry
键中,是指 .azurecr.io
前面的字符串。在第二步的示例输出结果中,它是 cac085021b77acr
。
在随后的命令中,用这个字符串替换 <ACR_name>
。
容器应用的 URL——以 Container
app
created
开头的一行中的 URL。在第二步的示例输出结果中,它是 https://my-container-app.delightfulmoss-eb6d59d5.westus.azurecontainerapps.io/
。
在随后的命令中,用这个 URL 替换 <ACR_URL>
。
按照蓝绿部署的要求,为该容器应用启用版本修订:
az containerapp revision set-mode \ --name my-container-app \
--resource-group my-container-app-rg \
--mode multiple
"Multiple"
(可选)通过查询容器中的 /health 端点来测试部署是否正在运行:
curl <ACR_URL>/healthOK
在这个挑战中,您需要获取 JSON 令牌,以自动执行 Azure 容器应用部署。
首先,您需要获取用于 Azure 容器注册表 (ACR) 的 ID 以及您的 Azure 托管身份的主 ID。然后,将用于 ACR 的内置 Azure 角色分配给托管身份,并将容器应用配置为使用该托管身份。最后,获取托管身份的 JSON 凭证,GitHub Action 将使用该凭证向 Azure 进行身份验证。
虽然这一系列步骤可能看起来很繁琐,但您只需在新建应用时执行一次,便可将该流程完全脚本化。在本教程中,您可以通过手动执行这些步骤加深印象。
注:这个为部署创建凭证的流程仅限于 Azure。
查询您托管身份的主 ID。它显示在输出结果的 PrincipalID
栏(为方便阅读,输出结果分成了两行)。您将用这个值替换第三步中的 <managed_identity_principal_ID>
:
az containerapp identity assign \ --name my-container-app \
--resource-group my-container-app-rg \
--system-assigned \
--output table
PrincipalId ...
------------------------------------ ...
39f8434b-12d6-4735-81d8-ba0apo14579f ...
... TenantId
... ------------------------------------
... cfda3e0f-14g5-4e05-bfb1-ffab73b4fsde
在 ACR 中查找容器应用的资源 ID,将 <ACR_name>
替换为您在 挑战 1 第三步中记录的名称。您将用这个值替换下一步中的 <ACR_resource_ID>
:
az acr show --name <ACR_name> --query id --output tsv/subscriptions/60efafl2-38ad-41w8-g49a-0ecc6723l97c/resourceGroups/my-container-app-rg/providers/Microsoft.ContainerRegistry/registries/cac085021b77acr
将用于 ACR 的内置 Azure 角色分配给容器应用的托管身份,将 <managed_identity_principal_ID>
替换为您在第一步中获得的托管身份,并将 <ACR_resource_ID>
替换为您在第二步中获得的资源 ID:
az role assignment create \ --assignee <managed_identity_principal_ID> \
--role AcrPull \
--scope <ACR_resource_ID>
{
"condition": null,
"conditionVersion": null,
"createdBy": null,
"createdOn": "2023-03-15T20:28:40.831224+00:00",
"delegatedManagedIdentityResourceId": null,
"description": null,
"id": "/subscriptions/0efafl2-38ad-41w8-g49a-0ecc6723l97c/resourceGroups/my-container-app-rg/providers/Microsoft.ContainerRegistry/registries/cac085021b77acr/providers/Microsoft.Authorization/roleAssignments/f0773943-8769-44c6-a820-ed16007ff249",
"name": "f0773943-8769-44c6-a820-ed16007ff249",
"principalId": "39f8ee4b-6fd6-47b5-89d8-ba0a4314579f",
"principalType": "ServicePrincipal",
"resourceGroup": "my-container-app-rg",
"roleDefinitionId": "/subscriptions/60e32142-384b-43r8-9329-0ecc67dca94c/providers/Microsoft.Authorization/roleDefinitions/7fd21dda-4fd3-4610-a1ca-43po272d538d",
"scope": "/subscriptions/ 0efafl2-38ad-41w8-g49a-0ecc6723l97c/resourceGroups/my-container-app-rg/providers/Microsoft.ContainerRegistry/registries/cac085021b77acr",
"type": "Microsoft.Authorization/roleAssignments",
"updatedBy": "d4e122d6-5e64-4bg1-9cld-2aceeb0oi24d",
"updatedOn": "2023-03-15T20:28:41.127243+00:00"
}
将容器应用配置为从 ACR 中提取镜像时使用的托管身份,并将 <ACR_name>
替换为您在挑战 1 的第三步中记录的容器应用名称(在上面第二步中也用过):
az containerapp registry set \ --name my-container-app \
--resource-group my-container-app-rg \
--server <ACR_name>.azurecr.io \
--identity system
[
{
"identity": "system",
"passwordSecretRef": "",
"server": "cac085021b77acr.azurecr.io",
"username": ""
}
]
查询您的 Azure 订阅 ID。
az account show --query id --output tsv0efafl2-38ad-41w8-g49a-0ecc6723l97c
创建一个 JSON 令牌,其中包含 GitHub Action 要使用的凭证,然后将 <subscription_ID>
替换为您的 Azure 订阅 ID。保存输出结果,在“将密钥添加到您的 GitHub 代码库”中用作 AZURE_CREDENTIALS
密钥的值。您可以安然地忽略有关 --sdk-auth
被弃用的警告;这是一个已知问题:
az ad sp create-for-rbac \ --name my-container-app-rbac \
--role contributor \
--scopes /subscriptions/<subscription_ID>/resourceGroups/my-container-app-rg \
--sdk-auth \
--output json
Option '--sdk-auth' has been deprecated and will be removed in a future release.
...
{
"clientId": "0732444d-23e6-47fb-8c2c-74bddfc7d2er",
"clientSecret": "qg88Q~KJaND4JTWRPOLWgCY1ZmZwN5MK3N.wwcOe",
"subscriptionId": "0efafl2-38ad-41w8-g49a-0ecc6723l97c",
"tenantId": "cfda3e0f-14g5-4e05-bfb1-ffab73b4fsde",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
在这个挑战中,您需要将密钥添加到您的 GitHub 代码库(用于管理您的 GitHub Action 工作流中的敏感数据),创建 Action 工作流文件并执行 Action 工作流。
有关密钥管理的详细介绍,请参阅我们的博文——Microservices June 23 的第二篇教程《如何安全地管理容器中的密钥》。
为了部署新版本的应用,您需要在设置中 fork 过的 GitHub 代码库中创建一系列密钥。这些密钥是在挑战 2 中创建的托管身份的 JSON 凭证,以及将新版本的 NGINX 镜像部署到 Azure 所需的一些敏感的部署参数。在下一部分中,您将在 GitHub Action 中使用这些密钥实现灰度蓝绿部署自动化。
如果使用 GitHub GUI:
在指定字段中输入以下值:
AZURE_CREDENTIALS
重复执行第三步到第五步三次,创建表格中所列的密钥。分别将 Secret Name(密钥名称)和 Secret Value(密钥值)列中的值输入到 GUI 的 Name(名称)和 Secret(密钥)字段。对于第三个密钥,将 <ACR_name>
替换为您在挑战 1 第三步中记录的分配给容器应用的名称。
密钥名称 | 密钥值 |
---|---|
CONTAINER_APP_NAME | my-container-app |
RESOURCE_GROUP | my-container-app-rg |
ACR_NAME | <ACR_name> |
如果使用 GitHub CLI:
在您代码库的根目录下,创建一个临时文件。
touch ~/creds.json
创建密钥:
gh secret set AZURE_CREDENTIALS --repo <your_GitHub_account>/platform < ~/creds.json
删除 creds.json:
rm ~/creds.json
重复运行这个命令,再创建三个密钥:
gh secret set <secret_name> --repo <your_GitHub_account>/platform
每次重复操作时,将 <secret_name>
替换为表格内 Secret Name(密钥名称)列中的一个值。根据提示,粘贴 Secret Value(密钥值)列中的相关值。对于第三个密钥,将 <ACR_name>
替换为您在挑战 1 第三步中记录的分配给容器应用的名称。
密钥名称 | 密钥值 |
---|---|
CONTAINER_APP_NAME | my-container-app |
RESOURCE_GROUP | my-container-app-rg |
ACR_NAME | <ACR_name> |
有了托管身份和密钥,您可为 GitHub Action 创建一个工作流文件,以自动执行灰度蓝绿部署。
注:工作流文件以 YAML 格式定义,其中空格是有意义的。请务必保留以下步骤中显示的缩进。
为 Action 工作流创建一个文件。
如果使用 GitHub GUI:
如果使用 GitHub CLI,则创建 .github/workflows 目录并新建一个名为 main.yml 的文件:
mkdir .github/workflowstouch .github/workflows/main.yml
使用您常用的文本编辑器,将工作流的文本添加到 main.yml 中。最简单的方法是直接复制完整工作流文件中显示的文本。或者,您也可以通过添加此步骤中注释的代码段来手动创建文件。
注:工作流文件以 YAML 格式定义,其中空格是有意义的。复制代码段时,请务必保留缩进(保险起见,请对您的文件与完整工作流文件进行比较)。
定义工作流的名称:
name: Deploy to Azure
将工作流配置为在向主分支发出 push 或 pull 请求时运行:
on:
push:
branches:
- main
pull_request:
branches:
- main
在 jobs
部分,定义 build-deploy
作业,该作业会检测代码、登录 Azure 并将应用部署到 Azure 容器应用中:
jobs: build-deploy:
runs-on: ubuntu-22.04
steps:
- name: Check out the codebase
uses: actions/checkout@v3
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Build and deploy Container App
run: |
# 手动添加 containerapp 扩展
az extension add --name containerapp --upgrade
# 使用 Azure CLI 来部署更新
az containerapp up -n ${{ secrets.CONTAINER_APP_NAME }}\
-g ${{ secrets.RESOURCE_GROUP }} \
--source ${{ github.workspace }}/ingress \
--registry-server ${{ secrets.ACR_NAME }}.azurecr.io
定义 test-deployment
作业,该作业可获取新部署修订版本的预发布 URL,并使用 GitHub Action 对 API 端点 /health 进行 ping 操作,以确保新修订版本可正常响应。 如果健康检查成功,容器应用上的 Azure 流量管理器会进行更新,将所有流量都定向到新部署的容器。
注:请确保 test-deployment
键值的缩进量与您在上一节中定义的 build-deploy
键值相同:
test-deployment: needs: build-deploy
runs-on: ubuntu-22.04
steps:
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Get new container name
run: |
# 手动添加 containerapp 扩展
az extension add --name containerapp --upgrade
# 获取最新部署修订版本的名称
REVISION_NAME=`az containerapp revision list -n ${{ secrets.CONTAINER_APP_NAME }} -g ${{ secrets.RESOURCE_GROUP }} --query "[].name" -o tsv | tail -1`
# 获取最新部署修订版本的 fqdn
REVISION_FQDN=`az containerapp revision show -n ${{ secrets.CONTAINER_APP_NAME }} -g ${{ secrets.RESOURCE_GROUP }} --revision "$REVISION_NAME" --query properties.fqdn -o tsv`
# 将值存储在 env vars 中
echo "REVISION_NAME=$REVISION_NAME" >> $GITHUB_ENV
echo "REVISION_FQDN=$REVISION_FQDN" >> $GITHUB_ENV
- name: Test deployment
id: test-deployment
uses: jtalk/url-health-check-action@v3 # Marketplace action 触达端点
with:
url: "https://${{ env.REVISION_FQDN }}/health" # 预发布端点
- name: Deploy succeeded
run: |
echo "Deployment succeeded! Enabling new revision"
az containerapp ingress traffic set -n ${{ secrets.CONTAINER_APP_NAME }} -g ${{ secrets.RESOURCE_GROUP }} --revision-weight "${{ env.REVISION_NAME }}=100"
以下为 Action 工作流文件的全文。
name: Deploy to Azureon:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-deploy:
runs-on: ubuntu-22.04
steps:
- name: Check out the codebase
uses: actions/checkout@v3
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Build and deploy Container
run: |
# 手动添加 containerapp 扩展
az extension add --name containerapp -upgrade
# 使用 Azure CLI 来部署更新
az containerapp up -n ${{ secrets.CONTAINER_APP_NAME }} \
-g ${{ secrets.RESOURCE_GROUP }} \
--source ${{ github.workspace }}/ingress \
--registry-server ${{ secrets.ACR_NAME }}.azurecr.io
test-deployment:
needs: build-deploy
runs-on: ubuntu-22.04
steps:
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Get new container name
run: |
# 为 Azure CLI 安装 containerapp 扩展
az extension add --name containerapp --upgrade
# 获取最新部署修订版本的名称
REVISION_NAME=`az containerapp revision list -n ${{ secrets.CONTAINER_APP_NAME }} -g ${{ secrets.RESOURCE_GROUP }} --query "[].name" -o tsv | tail -1`
# 获取最新部署修订版本的 fqdn
REVISION_FQDN=`az containerapp revision show -n ${{ secrets.CONTAINER_APP_NAME }} -g ${{ secrets.RESOURCE_GROUP }} --revision "$REVISION_NAME" --query properties.fqdn -o tsv`
# 将值存储在 env vars 中
echo "REVISION_NAME=$REVISION_NAME" >> $GITHUB_ENV
echo "REVISION_FQDN=$REVISION_FQDN" >> $GITHUB_ENV
- name: Test deployment
id: test-deployment
uses: jtalk/url-health-check-action@v3 # Marketplace action 触达端点
with:
url: "https://${{ env.REVISION_FQDN }}/health" # 预发布端点
- name: Deploy succeeded
run: |
echo "Deployment succeeded! Enabling new revision"
az containerapp ingress traffic set -n ${{ secrets.CONTAINER_APP_NAME }} -g ${{ secrets.RESOURCE_GROUP }} --revision-weight "${{ env.REVISION_NAME }}=100"
如果使用 GitHub GUI:
如果使用 GitHub CLI:
将 main.yml 添加到 Git 暂存区:
git add .github/workflows/main.yml
提交文件:
git commit -m "feat: create GitHub Actions workflow"
把您的更改推送到 GitHub:
git push
监控工作流的进度:
gh workflow view main.yml --repo <your_GitHub_account>/platform
在这个挑战中,您将测试工作流。首先,您需要模拟一次对 Ingress 负载均衡器的成功更新,并确认应用已更新。然后,模拟一次不成功的更新(导致内部服务器错误),并确认已发布的应用没有变化。
进行一次成功的更新,并观察工作流是否成功。
如果使用 GitHub GUI:
在靠近文件末尾的 location
/health
块中,更改 return
指令,如下所示:
location /health { access_log off;
return 200 "Successful Update!\n";
}
Successful
Update!
。您可以确认该消息,具体方式是启动终端会话并向应用发送健康检查请求,再次将 <ACR_URL>
替换为您在 挑战 1 第三步中记录的值:
curl <ACR_URL>/healthSuccessful Update!
如果使用 GitHub CLI:
创建一个名为 patch-1 的新分支:
git checkout -b patch-1
在您常用的文本编辑器中,打开 ingress/default.conf.template 并在靠近文件末尾的 location
/health
块中,更改 return
指令,如下所示:
location /health {
access_log off;
return 200 "Successful Update!\n";
}
将 default.conf.template 添加到 Git 暂存区:
git add ingress/default.conf.template
提交文件:
git commit -m "feat: update NGINX ingress"
把您的更改推送到 GitHub:
git push --set-upstream origin patch-1
创建拉取请求 (PR):
gh pr create --head patch-1 --fill --repo <your_GitHub_account>/platform
监控工作流的进度:
gh workflow view main.yml --repo <your_GitHub_account>/platform
当工作流完成时,向应用发送健康检查请求,将 <ACR_URL>
替换为您在 换为您在挑战 1 第三步中记录的值:
curl <ACR_URL>/health
Successful Update!
现在进行一次不成功的更新,并观察工作流是否失败。其步骤与进行一次成功的更新的步骤基本相同,只是 return
指令的值不同。
如果使用 GitHub GUI:
更改 return
指令,如下所示:
location /health { access_log off;
return 500 "Unsuccessful Update!\n";
}
当工作流完成时,导航到位于 <ACR_URL>/health 端点的容器应用,其中 <ACR_URL> 是您在 挑战 1 第三步中记录的 URL。
注意返回的消息是 Successful
Update!
(与上一次更新后显示的消息相同——成功更新)。虽然这似乎自相矛盾,但事实上它证实了此次更新失败——更新尝试生成了状态码 500
(表示内部服务器错误),未能成功应用。
您可以确认该消息,具体方式是启动终端会话并向应用发送健康检查请求,再次将 <ACR_URL>
替换为您在挑战 1 第三步中记录的值:
curl <ACR_URL>/healthSuccessful Update!
如果使用 GitHub CLI:
查看您在上一部分中创建的 patch-1 分支:
git checkout patch-1
在您常用的文本编辑器中,打开 ingress/default.conf.template 再次更改 return
指令,如下所示:
location /health {
access_log off;
return 500 "Unsuccessful Update!\n";
}
将 default.conf.template 添加到 Git 暂存区:
git add ingress/default.conf.template
提交文件:
git commit -m "feat: update NGINX ingress again"
把您的更改推送到 GitHub:
git push
监控工作流的进度:
gh workflow view main.yml --repo <your_GitHub_account>/platform
当工作流完成时,向应用发送健康检查请求,将 <ACR_URL>
替换为您在 挑战 1 第三步中记录的值:
curl <ACR_URL>/healthSuccessful Update!
结果看似自相矛盾,返回的消息是 Successful
Update!
(与上一次更新后显示的消息相同——成功更新)。虽然这似乎自相矛盾,但事实上它证实了此次更新失败——更新尝试导致了状态码 500
(代表内部服务器错误),未能成功应用。
如果您希望删除在操作本教程期间部署的 Azure 资源,以避免任何潜在的费用,输入下面一行代码:
az group delete -n my-container-app-rg -y
您也可以按需删除所创建的 fork。
如果使用 GitHub GUI:
如果使用 GitHub CLI:
gh repo delete <your_GitHub_account>/platform -yes
恭喜!您已了解如何使用 GitHub Action 来执行微服务应用的灰度蓝绿部署。请查阅 GitHub 文档中的以下文章,继续探索并增进有关 DevOps 的知识:
如果您准备尝试灰度部署的第二步(在生产环境中测试),请参阅我们的博文——Microservices June 2022 教程《NGINX 实操教程:通过灰度部署改善正常运行时间和弹性》。该教程介绍了如何使用 NGINX Service Mesh 逐步过渡到新的应用版本。即使您的部署尚未复杂到需要使用服务网格的程度,或者您并未使用 Kubernetes,这些原则同样适用于只使用 Ingress Controller(Ingress 控制器)或负载均衡器的较简单部署。
如欲进一步了解有关微服务的更多内容,请观看 Microservices June 2023 第三单元:利用 Docker、Kubernetes 和 Gitlab实现微服务自动化部署和 CI/CD 的课程录像,详细了解部署自动化的相关概念。
"This blog post may reference products that are no longer available and/or no longer supported. For the most current information about available F5 NGINX products and solutions, explore our NGINX product family. NGINX is now part of F5. All previous NGINX.com links will redirect to similar NGINX content on F5.com."