创建包含VPC私网反向访问功能的全托管服务,可以实现服务商与用户VPC私网的反向连接,通过服务实例创建的ECS资源反向访问用户侧的资源信息。本文介绍如何创建包含VPC私网反向访问功能的全托管服务并访问用户VPC内的资源。

功能介绍

VPC反向访问指服务商到用户的VPC私网连接,即服务商预先创建FULLNAT类型的VPC NAT网关并创建终端节点服务,当用户通过服务商创建的全托管服务创建服务实例后,通过终端节点服务与用户的VPC建立连接,并实现反向访问用户自己VPC下的资源。

场景说明

准备工作

服务商在创建包含VPC私网反向访问功能的全托管服务前,需要提前创建VPC实例、vSwitch、安全组、FULLNAT类型的VPC NAT网关和VPC NAT类型的终端节点服务,并模拟用户在自己的VPC下准备两个ECS实例。

  1. 在目标地域(本文以cn-beijing为例)创建VPC实例、vSwitch及安全组。更多信息,请参见创建和管理专有网络创建安全组
  2. 在目标地域创建FULLNAT类型的VPC NAT网关并获取VPC NAT网关ID、NAT IP默认地址段和FULLNAT表ID信息。
    1. 创建FULLNAT类型的VPC NAT网关。
      当前无法从专有网络控制台创建FullNAT类型的VPC NAT网关,只能通过API或ROS创建。创建FullNAT类型的VPC NAT网关代码如下:
      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.google.gson.Gson;
      import java.util.*;
      import com.aliyuncs.vpc.model.v20160428.*;
      
      public class CreateNatGateway {
      
          public static void main(String[] args) {
              DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", "<accessKeyId>", "<accessSecret>");
              IAcsClient client = new DefaultAcsClient(profile);
      
              CreateNatGatewayRequest request = new CreateNatGatewayRequest();
              request.setRegionId("cn-beijing");
              request.setVpcId("vpc-xxxxxx");
              request.setName("test");
              request.setVSwitchId("vsw-xxxxxx");
              request.setNatType("Enhanced");
              request.setInstanceChargeType("PostPaid");
              request.setNetworkType("intranet");
              request.putQueryParameter("PrivateLinkEnabled", "true");
              request.putQueryParameter("PrivateLinkMode", "Fullnat");
      
              try {
                  CreateNatGatewayResponse response = client.getAcsResponse(request);
                  System.out.println(new Gson().toJson(response));
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
      
          }
      }
    2. 专有网络管理控制台查看创建的私有网络VPC的NAT网关IDNAT IP默认地址段FULLNAT表ID信息。
      说明 由于创建服务时需要用到这些信息,请在完成创建后记录。
      • 查看NAT网关ID。NAT网关ID
      • 查看NAT IP默认地址段。默认地址段
      • 查看FULLNAT表ID。FULLNAT表ID
  3. 创建VPC NAT类型的终端节点服务。

    在创建终端节点服务时,服务资源类型需选择VPC NAT网关;选择服务资源选择上一步创建的NAT网关ID。

    服务终端节点
  4. 模拟用户操作,在用户侧的VPC下创建ECS。
    本文以访问MySQL服务为例,创建了两个ECS,安装MySQL服务并允许远程连接。ecs实例

步骤一:创建全托管服务

完成准备工作后,创建全托管服务。

  1. 登录计算巢控制台
  2. 在左侧导航栏中,选择服务管理 > 未发布服务,然后单击创建新服务
  3. 创建新服务界面,完成如下配置。
    1. 服务类型区域,选择全托管服务
    2. 服务信息区域,上传服务图标并填写服务名称服务简介版本描述
    3. 部署配置区域,完成相关配置。
      1. 租户类型用户类型部署地域保持默认配置。
      2. 录入方式区域,选择手动录入模板
      3. 部署方式区域,选择ROS
      4. 模板内容处,填写VPC私网反向访问的模板内容。

        该模板会创建一个ECS实例,为其分配公网IP并在ECS实例中安装MySQL数据库。关于模板内容的详细信息,请参见模板示例

      5. 套餐设置处,创建一个套餐。
      6. 参数映射关系处,将RegionIdVpcIdNatGatewayIdFullNatTableIdNatIpCidr进行映射;将ZoneIdvSwitchIdSecurityGroupId进行映射。并设置依赖参数和子依赖参数。VPC、NatGateway、安全组和vSwitch为准备工作阶段创建的资源。映射参数
      7. 隐藏的参数中,设置隐藏参数。

        VpcIdNatGatewayIdFullNatTableIdvSwitchIdSecurityGroupIdNatlpCidr设置为隐藏参数。

      8. 角色名称处,填写角色名称。角色名称为ComputeNestDeploy。
  4. 部署应用权限设置OAuth认证配置区域保存默认配置。
  5. 虚拟互联网配置区域,配置虚拟互联相关参数。
    1. 开启VPC私网反向访问
    2. 反向终端节点服务配置处,选择已创建的终端节点服务ID。
      虚拟互联网配置
  6. 单击保存服务

步骤二:创建服务实例

以用户视角创建服务实例,配置反向访问参数。

  1. 在已创建服务的服务详情页面,单击用户部署链接后的链接,进入创建服务实例页面。
  2. 创建服务实例页面,填写服务实例所需参数。
    1. 服务实例名称处,设置服务实例的名称。
    2. 地域实例规格处,根据界面提示设置相关参数。
    3. 套餐选择处,选择套餐。
    4. 反向访问参数处,配置需要反向访问的用户服务信息。
      此处填写准备工作中创建的MySQL测试实例的私网IP和端口。反向访问参数
    5. 反向访问处,设置专有网络信息并选中我同意授权服务商通过私网反向访问安全组下的资源
      选择用户侧的专有网络、安全组和可用区与交换机的信息。反向访问
  3. 选择我已阅读并同意《计算巢服务协议》
  4. 单击创建

步骤三:验证反向访问网络的连通性

服务实例创建成功后,您可以验证反向访问网络的连通性进行验证。

  1. 进入服务实例详情页面,单击资源页签。
  2. 资源页签中,找到创建的ECS实例资源。
    资源页签
  3. 操作列,单击远程连接,进入实例内部。
  4. 使用创建服务实例时配置的域名,远程连接用户侧的MySQL服务。
    若能成功连接,则表示反向私网访问配置成功。结果验证

模板示例

重要 在编写模板时,模板中的AccessDomainAccessIpAccessPort分别对应服务商请求用户服务使用的域名、用户侧的服务内网IP和用户的侧服务端口,其中AccessDomain非必填,AccessIpAccessPort为必填参数,且参数名称必须为AccessDomainAccessIpAccessPort,否则服务将不能识别配置。
ROSTemplateFormatVersion: '2015-09-01'
Description:
  en: the best practice of reverse VPC connection
  zh-cn: VPC私网访问最佳实践
Parameters:
  VpcReverseParameters:
    Type: Json
    AssociationProperty: List[Parameters]
    Default: []
    AssociationPropertyMetadata:
      Parameters:
        AccessDomain:
          Type: String
          Label:
            zh-cn: 用户服务域名
        AccessIp:
          Type: String
          Label:
            zh-cn: 用户服务IP
        AccessPort:
          Type: String
          Label:
            zh-cn: 用户服务端口
  ZoneId:
    Type: String
    AssociationPropertyMetadata:
      ComputeNestNetworkConfigZone: true
    Description:
      en: Availability zone ID,<br><b>note: <font color='blue'>Before selecting, please confirm that the Availability Zone supports the specification of creating ECS resources</font></b>
      zh-cn: 可用区ID
    Label:
      en: VSwitch Available Zone
      zh-cn: 可用区
  DataDiskCategory:
    Type: String
    AllowedValues:
      - cloud_efficiency
      - cloud_ssd
      - cloud_essd
    Default: cloud_efficiency
    Label:
      en: Disk Type
      zh-cn: 数据盘类型
  DataDiskSize:
    Type: Number
    Label:
      en: Data Disk Space
      zh-cn: 数据盘空间
    MinValue: 20
    MaxValue: 500
    Default: 40
  VpcId:
    AssociationProperty: ALIYUN::ECS::VPC::VPCId
    Type: String
    Label:
      en: VPC ID
      zh-cn: 专有网络VPC实例ID
  SecurityGroupId:
    AssociationPropertyMetadata:
      VpcId: VpcId
    Default: sg-2zeip0loevltubwfzrtm
    Label:
      zh-cn: 安全组ID
      en: Security Group ID
    AssociationProperty: ALIYUN::ECS::SecurityGroup::SecurityGroupId
    Type: String
  VSwitchId:
    AssociationProperty: ALIYUN::ECS::VSwitch::VSwitchId
    AssociationPropertyMetadata:
      VpcId: ${VpcId}
      ZoneId: ${ZoneId}
    Type: String
    Label:
      en: VSwitch ID
      zh-cn: 交换机实例ID
  EcsInstanceType:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例类型
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
  FullNatTableId:
    Type: String
    AssociationProperty: ALIYUN::VPC::NatGateway::ForwardTableId
  NatGatewayId:
    Type: String
    AssociationProperty: ALIYUN::VPC::NatGateway::NatGatewayId
  NatIpCidr:
    Type: String
Metadata:
  ALIYUN::ROS::Interface:
    ParameterGroups:
      - Parameters:
          - ZoneId
          - InstanceType
          - DataDiskCategory
          - DataDiskSize
          - VpcId
          - SecurityGroupId
          - VSwitchId
        Label:
          default:
            en: instance
            zh-cn: 实例规格
      - Parameters:
          - VpcReverseParameters
        Label:
          default:
            en: Vpc Reverse Connection
            zh-cn: 反向访问参数
Mappings: {}
Conditions: {}
Outputs:
  NatIp:
    Value:
      Fn::GetAtt:
        - VPCNatIp
        - NatIp
Resources:
  WaitConditionHandle:
    Type: ALIYUN::ROS::WaitConditionHandle
  WaitCondition:
    Type: ALIYUN::ROS::WaitCondition
    Properties:
      Count: 1
      Handle:
        Ref: WaitConditionHandle
      Timeout: 1800
  EcsInstanceGroup:
    Type: ALIYUN::ECS::InstanceGroup
    DependsOn:
      - VPCNatIp
    Properties:
      IoOptimized: optimized
      ZoneId:
        Ref: ZoneId
      DiskMappings:
        - Category:
            Ref: DataDiskCategory
          Device: /dev/xvdb
          Size:
            Ref: DataDiskSize
      SystemDiskSize: 40
      UserData:
        Fn::Sub:
          - |
            #!/bin/sh
            Hosts="${AccessDomains}"
            PrivateIps="${NatIp}"
            Hosts=(`echo $Hosts | sed 's/\[//g' | sed 's/\]//g' | sed 's/,//g'`)
            PrivateIps=(`echo $PrivateIps | sed 's/\[//g' | sed 's/\]//g' | sed 's/,//g'`)
            arraylength=${!#Hosts[@]}
            for (( i=0; i<$arraylength; i++ ));
              do
                echo ${!PrivateIps[$i]} ${!Hosts[$i]} >> /etc/hosts
              done
            ${CurlCli} -d "{\"Data\" : \"SUCCESS\", \"Status\" : \"SUCCESS\"}"
          - CurlCli:
              Fn::GetAtt:
                - WaitConditionHandle
                - CurlCli
            NatIp:
              Fn::GetAtt:
                - VPCNatIp
                - NatIp
            AccessDomains:
              Fn::SelectMapList:
                - AccessDomain
                - Ref: VpcReverseParameters
      InstanceChargeType: PrePaid
      PeriodUnit: Month
      Period: 1
      SecurityGroupId:
        Ref: SecurityGroupId
      VSwitchId:
        Ref: VSwitchId
      MaxAmount: 1
      SystemDiskCategory: cloud_efficiency
      InstanceName:
        Fn::Join:
          - '-'
          - - reverse-vpc
            - Ref: ALIYUN::StackName
      VpcId:
        Ref: VpcId
      ImageId: m-2zefr7ixarlg8coevwzl
      InstanceType:
        Ref: EcsInstanceType
      HostName: reverse-vpc
      Password: passw0RD
      AllocatePublicIP: false
  VPCNatIp:
    Type: ALIYUN::VPC::NatIp
    Count:
      Fn::Length:
        Ref: VpcReverseParameters
    Properties:
      NatIpCidr:
        Ref: NatIpCidr
      NatIpDescription: test
      NatIpName: test
      NatGatewayId:
        Ref: NatGatewayId