ETSI NFV-SOL CNF 部署使用 Helm¶
本节介绍如何在 Tacker v2 API 中使用 Helm 部署 ETSI NFV-SOL 容器化 VNF。
注意
v1 VNF LCM API 也支持使用 Helm 部署 CNF,其要求与 v2 VNF LCM API 不同。有关在 v1 API 中使用 Helm 的详细信息,请参阅 如何将 Helm 安装到 Kubernetes 集群并使用 Helm Chart 部署 CNF。
有关支持的 Helm 版本的信息,请参阅 Tacker 架构。
概述¶
下图显示了 CNF 部署的概述。
请求创建 VNF
用户通过上传 VNF 包并请求
create VNF,向 tacker-server 请求创建 VNF。VNF 包应包含Helm chart,除了VNFD之外。请求实例化 VNF
用户通过请求
instantiate VNF并提供实例化参数,向 tacker-server 请求实例化创建的 VNF。执行 Helm 命令
收到 tacker-client 的请求后,tacker-server 会将其重定向到 tacker-conductor。在 tacker-conductor 中,请求会再次根据实例化参数的内容重定向到适当的 infra-driver(在本例中为 Helm infra-driver)。然后,Helm infra-driver 执行 Helm 命令。
通过 Helm 调用 Kubernetes API
Helm 调用 Kubernetes API 以创建 Pod 作为 VNF。
创建 Pod
Kubernetes Master 根据 API 调用创建 Pod。
准备 Helm VIM¶
1. 创建配置文件¶
在将 Helm VIM 注册到 tacker 之前,我们应该创建配置文件。以下示例提供了注册 Helm VIM 所需的信息。此示例指定了可以从 Kubernetes Master 节点获取的 bearer_token 和 ssl_ca_cert 参数的值。有关获取 bearer_token 和 ssl_ca_cert 的具体方法,请参阅 Kubernetes VIM 安装。它还包含 extra 字段。对于使用 Helm,extra 中的 use_helm 必须设置为 true。
auth_url: "https://192.168.56.10:6443"
bearer_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IkdVazBPakx4Q2NsUjJjNHhsZFdaaXJMSHVQMUo4NkdMS0toamlSaENiVFUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tazhzdmltIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJhNTIzYzFhMi1jYmU5LTQ1Y2YtYTc5YS00ZDA4MDYwZDE3NmEiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpkZWZhdWx0In0.BpKAAQLjXMIpJIjqQDsGtyh1a-Ij8e-YOVRv0md_iOGXd1KLR-qreM6xA-Ni8WFILzq3phaZU6npET8PlfhQ6csF5u20OT2SoZ7iAotHXpCcYkRdrUd2oO5KxSFTkOhasaN1pQ3pZyaFYUZbwwmLK3I31rG4Br2VbZQ7Qu8wFOXUK-syBGF48vIPZ5JQ3K00KNxpuEcGybMK5LtdSKZ25Ozp_I2oqm3KBZMPMfWwaUnvuRnyly13tsiXudPt_9H78AxLubMo3rcvECJU2y_zZLiavcZKXAz-UmHulxtz_XZ80hMu-XOpYWEYrOB0Lt0hB59ZoY1y3OvJElTfPyrwWw"
ssl_ca_cert: "-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIIa76wZDxLNAowDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0yMzExMDYwMDA3MzBaFw0zMzExMDMwMDEyMzBaMBUx
EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDd0LBXGxVexr09mVFNSXWQq3TN66IIcXCBAMbIWI4EiQ8Y0zI4hSwADdK2
ltYSdWw7wq3/YTFHK8/YTY7Jvd9/k3UJrqkZ6kBtL20pJUPXNJVLE/hRzsqEnHHv
cfqYZTHvTY4g7qNcMOcfl/oDUGUMfpQT2gs6xoNl0WX/1+QeQbadx1kWaD2Ii45F
d8TR+c4wccxNaLArk3ok4h1PNeAwra4mRmBHQQ2wFjkTYGl4+ss3v1yoUJkrQjXL
RgzLufeXaz8eRTi36HkjudGKfS3OnUeke3uBN7usW58FFJ8TdKOhuoguRm53kj6+
TwXtZCOPzn4gNxq6xJE1Xj2hwFfpAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRdmQ4r63pXBHIO8ODqxROE7x+aizAV
BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQBeQ/9+bzRe
qbA02MfYnN3vycGhDObcAoiDIMIutojFTpx4hGZjqVgTRpLH5ReddwR4kkxn3NRg
weCVkNkhzyGze64nb11qZG71olaOQRMYzyN2hYfmbq7MXSvmJQQYIr1OewaRk+xl
TyG1XRXoD2IEaHEvG0+pQJlDerd5Z6S1fkPaKZtcRbM/E6y5VXMV6hegN4MwHZSI
Ll1uEBTxUzzTm3dnl1KL8GDg05ajoYcyL3X/0aWsb/MFhtIlXe2CMxu5qUkLBhzy
fCfX4cZpI5KFxMgdmAEoaGbNy7iqsGrLFtEmub2gdEBIVNr7vgOk4OeQ9Uodj6K7
jK97z+cupc5G
-----END CERTIFICATE-----"
project_name: "default"
type: "kubernetes"
extra:
use_helm: true
1. 注册 Helm VIM¶
我们可以通过运行以下命令将 Helm VIM 注册到 tacker
$ openstack vim register --config-file CONFIG_FILE Helm_VIM_NAME --fit-width
第 1 章中的配置文件需要通过参数 –config-file 输入。执行成功后,将显示 VIM 信息。
$ openstack vim register --config-file vim-k8s.yaml test-vim-helm --fit-width
+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| auth_cred | { |
| | "bearer_token": "***", |
| | "ssl_ca_cert": "b'gAAAAABlcTEl6uPnFB4aCDCpRI8fuMI7r9K6lBC2nW0IiYpd3HifxUsXI20wxuTRpOVRSVasev_NuxfZxLneAfgwCF_FYed9cj0vng-Q32v2RM8EMziTfzNjV8qnESgMOtcsb10h80917tYSIoJOWhPdcLUPDKKL-Or3vJWzudRFnltI13GnK40ytVOaN4gF-wAE8zEL7gOq7iyC2L4 |
| | oVmZeb2VVAnp71KvJPMAe3hM0IPF2dgpqSEfImG8ipmzLnc_JGG40ybiaNG7lDjwSMDnjrEjiX32iTmsifOSr02mBhrrn7hoG7mbbcTfhCymjSjoutQKuXMBflAM8ytgBk6C70HtIBQJgNLIYGHrTMyEH6kPynM79EMxfENJVSxfmLzwwnw6YWc01oVIqW5GuK6cZDho4pbb8r-CxZk2XU0DOKRqSSju- |
| | B8wBoeI4EaKSAKhOVtQrsM6sQoz0yOF5NNH8tcebZsUFYO8jp6-xk8w91GhK6CFVL39_vuoyiAS-zQH0S_GA3tl8pTpufAIr_TShq6Jc7hac1cBWaeqsuofC-Ny6jhdN5-AMx0EsVzx_3tkOD_pA2wP8PkFO8gxC4egjdNVdOdU_ggXElcnsJJeBP5ljTmisu3sn_fqFMxpJeqdus-bquX8ErLPfGBjCy- |
| | cWDIRmc1XaLHZ88Ju2zqjDMfjotNXJeK_iYeBiEMNwburTy-VAmucPS6bdI8Y1dPWcT-V6Nk- |
| | jMl1ydUHptX42NXrxO83NjTIb5KDZQ8-AD7eQ1Bq2H6AkhZIw24qzLme6KTupzo-9CuzF3HElmwuy0NtyjvXhn4X36xlYJk-y9LYPV6KyvYPmND9uzskemDma0VCRbbIgdfar0AER0wDZlwv7Ra0G12CUKbxZVSXowS9nl- |
| | FC5ApT7NAGhKAP8BfyUnjxMqA641Yk8DVLXmyztYYYYSoQ-6OcKKfTEvbYvfDSeHH50IGzHfMkx5YkNExqGREjjYkVwjO0ZW1odRoXISXtfop0Xfpqkun_ckJXNYCXKEn6bc9q3EnSaIxI2NOyhsjZ3eDF-VHFo8K_H_5iBrqoqWOUwx-uWm4xKZ7GtOUhPL4_w9XSiBFcMSf0uCtvbcWsu_B00itWYMipPUbYWD- |
| | Un9p5ESmaFbPW4B9912sszjbJzQyawqV_LYoW6MPgLYEd46Oqn9RkxGqdj8DKJpUfBb_DKC3G29OiStaW6IWLBNmsNqb9xCy5UsF_sM_fLEbbAR76LEAau2Fhb5DTzHr27h_Ri8GsXfTWX8r61pseh41hZAmwpyAW4-WAxhx158gR14hPBrjecPuPSs_vB- |
| | 6lJkuu7NFQIlj2uQBSP_gkaJ4mg3tIzcVfQeyTcE5KhfJWy2TdyI7pIe1vthjzI8pgWxO_dUAkkIPA6emA01QxzzKNCHa0KCYGi_noTwnasb_vwDL0sqjd6eUwFjCzeEuhPex3aQkYxrg2wxWFxg58bLb_it8U1wqHEWfiCH4a5XE4TCnBbPF2DiRZ9KHkRgdAcz2Wo- |
| | iNx0ghJ0u25Phi6nHuxbtEOghHZH7cgx6KaZ300ilA1g=='", |
| | "auth_url": "https://192.168.56.10:6443", |
| | "username": "None", |
| | "key_type": "barbican_key", |
| | "secret_uuid": "***" |
| | } |
| auth_url | https://192.168.56.10:6443 |
| created_at | 2023-12-07 02:42:46.328344 |
| description | |
| extra | use_helm=True |
| id | 9c37f36f-f569-4259-b388-d550e55dd65c |
| is_default | False |
| name | test-vim-helm |
| placement_attr | { |
| | "regions": [ |
| | "default", |
| | "kube-node-lease", |
| | "kube-public", |
| | "kube-system" |
| | ] |
| | } |
| project_id | ebbc6cf1a03d49918c8e408535d87268 |
| status | ACTIVE |
| type | kubernetes |
| updated_at | None |
| vim_project | { |
| | "name": "default" |
| | } |
+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
我们还可以通过 openstack vim list 命令检查 VIM 的状态是否为 ACTIVE。
$ openstack vim list
+--------------------------------------+---------------+----------------------------------+------------+------------+--------+
| ID | Name | Tenant_id | Type | Is Default | Status |
+--------------------------------------+---------------+----------------------------------+------------+------------+--------+
| 9c37f36f-f569-4259-b388-d550e55dd65c | test-vim-helm | ebbc6cf1a03d49918c8e408535d87268 | kubernetes | False | ACTIVE |
+--------------------------------------+---------------+----------------------------------+------------+------------+--------+
注意
在 vim list 的返回结果中,Type 对于 Helm VIM 和 Kubernetes VIM 均显示为 kubernetes。
准备 VNF 包¶
1. 创建 VNF 包的目录¶
TOSCA YAML CSAR 文件是使用 ZIP 文件格式的归档文件,其结构符合 TOSCA Simple Profile YAML v1.2 规范。以下是构建 VNF 包 CSAR 目录的示例
$ mkdir -p deployment/{TOSCA-Metadata,Definitions,Files/kubernetes}
2. 创建 Helm chart¶
CSAR VNF 包应具有定义要部署的 Kubernetes 资源的 Helm chart。文件名应具有“.yaml”扩展名,并且所有 chart 文件应压缩为“.tgz”。
为了将 Helm chart 中定义的 Kubernetes 资源映射到 VNFD 中定义的 VDU,Helm chart 中的 metadata.name 必须符合以下规则进行描述。
metadata.name 必须设置为“VNFD 中定义的 properties.name”+”-” +“release 中的唯一字符串(例如,release 名称)”。“release 中的唯一字符串”不得包含“-”。
以下显示了示例描述。
apiVersion: apps/v1
kind: Deployment
metadata:
name: vdu1-{{ .Release.Name }}
labels:
{{- include "localhelm.labels" . | nindent 4 }
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCountVdu1 }}
{{- end }}
注意
在此示例中,replicas 的值通过 helm 命令指定为 replicaCountVdu1。此类参数名称需要作为实例化请求参数中的 helm_value_names 提供。实例化请求参数的示例在 1. 设置请求参数文件中的值 中描述。
注意
由于版本 1 VNF LCM API 支持使用外部存储库,因此 chart 文件可能包含在 VNF 包中,也可能包含在外部存储库中。另一方面,版本 2 VNF LCM API 要求 Helm chart 文件包含在 VNF 包中。
3. 创建 TOSCA.meta 文件¶
TOSCA.Meta 文件包含 TOSCA.Meta 文件、CSAR、Definitions 文件和 artifact 文件的版本信息。Artifact 文件的名称、content-Type、加密方法和哈希值需要在 TOSCA.Meta 文件中提供。以下是 TOSCA.meta 文件的示例
TOSCA-Meta-File-Version: 1.0
Created-by: dummy_user
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_cnf_top.vnfd.yaml
Name: Files/kubernetes/test-chart-0.1.0.tgz
Content-Type: test-data
Algorithm: SHA-256
Hash: fa05dd35f45adb43ff1c6c77675ac82c477c5a55a3ad14a87a6b542c21cf4f7c
4. 下载 ETSI 定义文件¶
下载官方文档。ETSI GS NFV-SOL 001 [i.4] 规定了基于 TOSCA 规范的 VNFD 的结构和格式。
$ cd deployment/Definitions
$ wget https://forge.etsi.org/rep/nfv/SOL001/raw/v2.6.1/etsi_nfv_sol001_common_types.yaml
$ wget https://forge.etsi.org/rep/nfv/SOL001/raw/v2.6.1/etsi_nfv_sol001_vnfd_types.yaml
5. 创建 VNFD¶
如何创建由多个部署 flavor 组成的 VNFD 在 基于 ETSI NFV-SOL001 的 VNF 描述符 (VNFD) 中描述。
VNFD 不会包含任何 Kubernetes 资源信息,例如连接点、虚拟链路,因为 CNF 的所有必需组件都将在 Kubernetes 资源文件中指定。
以下是包含 VNF 定义的 VNFD 文件的示例。
tosca_definitions_version: tosca_simple_yaml_1_2
description: Sample VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- sample_cnf_types.yaml
- sample_cnf_df_simple.yaml
topology_template:
inputs:
selected_flavour:
type: string
description: VNF deployment flavour selected by the consumer. It is provided in the API
node_templates:
VNF:
type: company.provider.VNF
properties:
flavour_id: { get_input: selected_flavour }
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
provider: Company
product_name: Sample VNF
software_version: '1.0'
descriptor_version: '1.0'
vnfm_info:
- Tacker
requirements:
#- virtual_link_external # mapped in lower-level templates
#- virtual_link_internal # mapped in lower-level templates
sample_cnf_types.yaml 文件定义了 VNF 的参数类型和默认值。
tosca_definitions_version: tosca_simple_yaml_1_2
description: VNF type definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
company.provider.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
descriptor_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
provider:
type: string
constraints: [ valid_values: [ 'Company' ] ]
default: 'Company'
product_name:
type: string
constraints: [ valid_values: [ 'Sample VNF' ] ]
default: 'Sample VNF'
software_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
vnfm_info:
type: list
entry_schema:
type: string
constraints: [ valid_values: [ Tacker ] ]
default: [ Tacker ]
flavour_id:
type: string
constraints: [ valid_values: [ simple,complex ] ]
default: simple
flavour_description:
type: string
default: ""
requirements:
- virtual_link_external:
capability: tosca.capabilities.nfv.VirtualLinkable
- virtual_link_internal:
capability: tosca.capabilities.nfv.VirtualLinkable
interfaces:
Vnflcm:
type: tosca.interfaces.nfv.Vnflcm
sample_cnf_df_simple.yaml 定义了 VNF 输入的参数类型。
tosca_definitions_version: tosca_simple_yaml_1_2
description: Simple deployment flavour for Sample VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- sample_cnf_types.yaml
topology_template:
inputs:
descriptor_id:
type: string
descriptor_version:
type: string
provider:
type: string
product_name:
type: string
software_version:
type: string
vnfm_info:
type: list
entry_schema:
type: string
flavour_id:
type: string
flavour_description:
type: string
substitution_mappings:
node_type: company.provider.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external: []
node_templates:
VNF:
type: company.provider.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
instantiate_start:
implementation: sample-script
instantiate_end:
implementation: sample-script
terminate_start:
implementation: sample-script
terminate_end:
implementation: sample-script
scale_start:
implementation: sample-script
scale_end:
implementation: sample-script
heal_start:
implementation: sample-script
heal_end:
implementation: sample-script
modify_information_start:
implementation: sample-script
modify_information_end:
implementation: sample-script
artifacts:
sample-script:
description: Sample script
type: tosca.artifacts.Implementation.Python
file: ../Scripts/sample_script.py
VDU1:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: vdu1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: vdu2
description: VDU2 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
policies:
- scaling_aspects:
type: tosca.policies.nfv.ScalingAspects
properties:
aspects:
vdu1_aspect:
name: vdu1_aspect
description: vdu1 scaling aspect
max_scale_level: 2
step_deltas:
- delta_1
vdu2_aspect:
name: vdu2_aspect
description: vdu2 scaling aspect
max_scale_level: 2
step_deltas:
- delta_1
- VDU1_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 1
targets: [ VDU1 ]
- VDU1_scaling_aspect_deltas:
type: tosca.policies.nfv.VduScalingAspectDeltas
properties:
aspect: vdu1_aspect
deltas:
delta_1:
number_of_instances: 1
targets: [ VDU1 ]
- VDU2_initial_delta:
type: tosca.policies.nfv.VduInitialDelta
properties:
initial_delta:
number_of_instances: 1
targets: [ VDU2 ]
- VDU2_scaling_aspect_deltas:
type: tosca.policies.nfv.VduScalingAspectDeltas
properties:
aspect: vdu2_aspect
deltas:
delta_1:
number_of_instances: 1
targets: [ VDU2 ]
- instantiation_levels:
type: tosca.policies.nfv.InstantiationLevels
properties:
levels:
instantiation_level_1:
description: Smallest size
scale_info:
vdu1_aspect:
scale_level: 0
vdu2_aspect:
scale_level: 0
instantiation_level_2:
description: Largest size
scale_info:
vdu1_aspect:
scale_level: 2
vdu2_aspect:
scale_level: 2
default_level: instantiation_level_1
- VDU1_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 3
targets: [ VDU1 ]
- VDU2_instantiation_levels:
type: tosca.policies.nfv.VduInstantiationLevels
properties:
levels:
instantiation_level_1:
number_of_instances: 1
instantiation_level_2:
number_of_instances: 3
targets: [ VDU2 ]
1. 压缩 VNF 包¶
CSAR 包应压缩为 ZIP 文件进行上传。以下命令是压缩 VNF 包的示例
$ cd -
$ cd ./deployment
$ zip deployment.zip -r Definitions/ Files/ TOSCA-Metadata/
$ ls deployment
deployment.zip Definitions Files TOSCA-Metadata
创建并上传 VNF 包¶
我们需要在 tacker 中创建一个空的 VNF 包对象,并上传之前章节中创建的压缩 VNF 包。
1. 创建 VNF 包¶
可以通过命令 openstack vnf package create 创建一个空的 VNF 包。创建 VNF 包成功后,将返回一些信息,包括 ID、链接、Onboarding 状态、Operational 状态和 Usage 状态。当 Onboarding 状态为 CREATED、Operational 状态为 DISABLED、Usage 状态为 NOT_IN_USE 时,表示创建成功。
$ openstack vnf package create
+-------------------+-------------------------------------------------------------------------------------------------+
| Field | Value |
+-------------------+-------------------------------------------------------------------------------------------------+
| ID | 88d490b1-7145-4bb8-accc-74ea13dccfa0 |
| Links | { |
| | "self": { |
| | "href": "/vnfpkgm/v1/vnf_packages/88d490b1-7145-4bb8-accc-74ea13dccfa0" |
| | }, |
| | "packageContent": { |
| | "href": "/vnfpkgm/v1/vnf_packages/88d490b1-7145-4bb8-accc-74ea13dccfa0/package_content" |
| | } |
| | } |
| Onboarding State | CREATED |
| Operational State | DISABLED |
| Usage State | NOT_IN_USE |
| User Defined Data | {} |
+-------------------+-------------------------------------------------------------------------------------------------+
2. 上传 VNF 包¶
通过运行以下命令将上述创建的 VNF 包上传到 VNF 包 openstack vnf package upload --path <vnf 包的路径> <vnf 包 ID>。以下是上传 VNF 包的示例
$ openstack vnf package upload --path test_helm_instantiate.zip 88d490b1-7145-4bb8-accc-74ea13dccfa0
Upload request for VNF package 88d490b1-7145-4bb8-accc-74ea13dccfa0 has been accepted.
3. 检查 VNF 包状态¶
通过 openstack vnf package list 命令检查 VNF 包状态。找到 id 与创建的 vnf 包 id 相同的项目,当 Onboarding 状态为 ONBOARDED、Operational 状态为 ENABLED、Usage 状态为 NOT_IN_USE 时,表示 VNF 包已成功上传。
$ openstack vnf package list
+--------------------------------------+------------------+------------------+-------------+-------------------+-------------------------------------------------------------------------------------------------+
| Id | Vnf Product Name | Onboarding State | Usage State | Operational State | Links |
+--------------------------------------+------------------+------------------+-------------+-------------------+-------------------------------------------------------------------------------------------------+
| 88d490b1-7145-4bb8-accc-74ea13dccfa0 | Sample VNF | ONBOARDED | NOT_IN_USE | ENABLED | { |
| | | | | | "self": { |
| | | | | | "href": "/vnfpkgm/v1/vnf_packages/88d490b1-7145-4bb8-accc-74ea13dccfa0" |
| | | | | | }, |
| | | | | | "packageContent": { |
| | | | | | "href": "/vnfpkgm/v1/vnf_packages/88d490b1-7145-4bb8-accc-74ea13dccfa0/package_content" |
| | | | | | } |
| | | | | | } |
+--------------------------------------+------------------+------------------+-------------+-------------------+-------------------------------------------------------------------------------------------------+
创建 VNF¶
1. 获取 VNFD ID¶
可以通过 openstack vnf package show <VNF 包 ID> 命令找到上传的 vnf 包的 VNFD ID。以下是检查 VNFD-ID 值的示例
$ openstack vnf package show 954df00a-8b14-485d-bfd8-8fc5df0197cb
+----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
| Additional Artifacts | [ |
| | { |
| | "artifactPath": "Files/kubernetes/test-chart-0.1.0.tgz", |
| | "checksum": { |
| | "algorithm": "SHA-256", |
| | "hash": "fa05dd35f45adb43ff1c6c77675ac82c477c5a55a3ad14a87a6b542c21cf4f7c" |
| | }, |
| | "metadata": {} |
| | } |
| | ] |
| Checksum | { |
| | "hash": "ac970df4d0c0583c5e152babcf74f72d15d31c92707e700dfd91a5ec9d742afcdf63baaa1e08d5a71f34f06043c1f0be1a49e42ab5693860528f7a382bcc0a76", |
| | "algorithm": "sha512" |
| | } |
| ID | 88d490b1-7145-4bb8-accc-74ea13dccfa0 |
| Links | { |
| | "self": { |
| | "href": "/vnfpkgm/v1/vnf_packages/88d490b1-7145-4bb8-accc-74ea13dccfa0" |
| | }, |
| | "packageContent": { |
| | "href": "/vnfpkgm/v1/vnf_packages/88d490b1-7145-4bb8-accc-74ea13dccfa0/package_content" |
| | } |
| | } |
| Onboarding State | ONBOARDED |
| Operational State | ENABLED |
| Software Images | |
| Usage State | NOT_IN_USE |
| User Defined Data | {} |
| VNF Product Name | Sample VNF |
| VNF Provider | Company |
| VNF Software Version | 1.0 |
| VNFD ID | 330f36dd-8398-4d2c-98c1-bc6c626e88b2 |
| VNFD Version | 1.0 |
+----------------------+-------------------------------------------------------------------------------------------------------------------------------------------------+
2. 执行创建 VNF 命令¶
我们可以通过运行 openstack vnflcm create <VNFD ID> --os-tacker-api-version 2 创建 VNF。执行该命令后,生成的 ID 为 VNF instance ID。
$ openstack vnflcm create 330f36dd-8398-4d2c-98c1-bc6c626e88b2 --os-tacker-api-version 2
+-----------------------------+------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+-----------------------------+------------------------------------------------------------------------------------------------------------------+
| ID | f082149a-c20f-43df-bc71-1fde035a1197 |
| Instantiation State | NOT_INSTANTIATED |
| Links | { |
| | "self": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197" |
| | }, |
| | "instantiate": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197/instantiate" |
| | } |
| | } |
| VNF Configurable Properties | |
| VNF Instance Description | |
| VNF Instance Name | |
| VNF Product Name | Sample VNF |
| VNF Provider | Company |
| VNF Software Version | 1.0 |
| VNFD ID | 330f36dd-8398-4d2c-98c1-bc6c626e88b2 |
| VNFD Version | 1.0 |
+-----------------------------+------------------------------------------------------------------------------------------------------------------+
实例化 VNF¶
1. 设置请求参数文件中的值¶
获取目标 VIM 的 ID。
$ openstack vim list
+--------------------------------------+---------------+----------------------------------+------------+------------+--------+
| ID | Name | Tenant_id | Type | Is Default | Status |
+--------------------------------------+---------------+----------------------------------+------------+------------+--------+
| 9c37f36f-f569-4259-b388-d550e55dd65c | test-vim-helm | ebbc6cf1a03d49918c8e408535d87268 | kubernetes | False | ACTIVE |
+--------------------------------------+---------------+----------------------------------+------------+------------+--------+
一个 json 文件,其中包含 Helm VIM 信息和 additionalParams,应提供用于实例化容器化 VNF。
以下显示了一个示例 json 文件。
{
"flavourId": "simple",
"vimConnectionInfo": {
"vim1": {
"vimId": " 9c37f36f-f569-4259-b388-d550e55dd65c",
"vimType": "ETSINFV.HELM.V_3"
}
},
"additionalParams": {
"helm_chart_path": "Files/kubernetes/test-chart-0.1.0.tgz",
"helm_parameters": {
"service.port": 8081,
"service.type": "NodePort"
},
"helm_value_names": {
"VDU1": {
"replica": "replicaCountVdu1"
},
"VDU2": {
"replica": "replicaCountVdu2"
}
},
"namespace": "default"
}
}
在 vimConnectionInfo 中指定 vimId 的情况下,vim 信息将通过注册的 vim 信息进行补充。
注意
当使用 Helm 时,vimType 必须设置为 ETSINFV.KUBERNETES.V_1。基于 extra.use_helm 的值,tacker 内部将其视为 Helm VIM。
或者,您可以指定完整的 vimConnectionInfo,而不是注册 VIM。以下显示了示例 json。
"vimConnectionInfo": {
"vim1": {
"vimId": "vim_id_1",
"vimType": "ETSINFV.HELM.V_3",
"interfaceInfo": {
"endpoint": "auth_url",
"ssl_ca_cert": "ssl_ca_cert"
},
"accessInfo": {
"bearer_token": "bearer_token"
}
}
}
注意
即使此操作指定与一个 VNF 实例关联的多个 vimConnectionInfo,也只会使用其中一个用于生命周期管理操作。
此外,json 文件必须包含一些 Helm 作为附加参数的参数。以下显示了通过 Helm chart 部署 CNF 的附加参数。
属性名称 |
数据类型 |
参数描述 |
|---|---|---|
helm_chart_path |
字符串 |
helm_chart 的文件路径。必须设置此参数。 |
namespace |
字符串 |
用于部署 Kubernetes 资源的命名空间。如果缺失,则默认使用 Helm chart 中的值。 |
helm_parameters |
Dict |
KeyValuePairs 参数,在 Helm 安装期间指定。 |
helm_value_names |
Dict |
此参数指定要设置为 Helm 安装参数的参数名称。 |
> replica |
KeyValuePairs |
映射到 Pod 数量的参数。 |
注意
VNF 实例化的 namespace 由以下优先级确定:
如果在实例化请求的 additionalParams 中指定了
namespace,则使用指定的namespace。如果未指定
namespace,则使用默认命名空间default。
警告
如果通过第 2 节中描述的方法在 manifest 中指定了多个命名空间,则 VNF 实例化将失败。
2. 执行实例化命令¶
运行 openstack vnflcm instantiate <VNF 实例 ID> <json 文件> --os-tacker-api-version 2 来实例化 VNF。
VNF instance ID 是在执行 openstack vnflcm create 命令后生成的 ID。我们可以在 [2. 执行创建 VNF 命令] 章节中找到它。
$ openstack vnflcm instantiate f082149a-c20f-43df-bc71-1fde035a1197 helm_instantiate_req --os-tacker-api-version 2
Instantiate request for VNF Instance f082149a-c20f-43df-bc71-1fde035a1197 has been accepted.
3. 检查实例化状态¶
我们可以通过运行以下命令检查实例化状态。当实例化状态为 INSTANTIATED 时,表示实例化成功。
$ openstack vnflcm show f082149a-c20f-43df-bc71-1fde035a1197 --os-tacker-api-version 2 --fit-width
+-----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+-----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ID | f082149a-c20f-43df-bc71-1fde035a1197 |
| Instantiated Vnf Info | { |
| | "flavourId": "simple", |
| | "vnfState": "STARTED", |
| | "scaleStatus": [ |
| | { |
| | "aspectId": "vdu1_aspect", |
| | "scaleLevel": 0 |
| | }, |
| | { |
| | "aspectId": "vdu2_aspect", |
| | "scaleLevel": 0 |
| | } |
| | ], |
| | "maxScaleLevels": [ |
| | { |
| | "aspectId": "vdu1_aspect", |
| | "scaleLevel": 2 |
| | }, |
| | { |
| | "aspectId": "vdu2_aspect", |
| | "scaleLevel": 2 |
| | } |
| | ], |
| | "vnfcResourceInfo": [ |
| | { |
| | "id": "vdu1-vnff082149ac20f43dfbc711fde035a1197-659966c5fb-nngts", |
| | "vduId": "VDU1", |
| | "computeResource": { |
| | "resourceId": "vdu1-vnff082149ac20f43dfbc711fde035a1197-659966c5fb-nngts", |
| | "vimLevelResourceType": "Deployment" |
| | }, |
| | "metadata": {} |
| | }, |
| | { |
| | "id": "vdu2-vnff082149ac20f43dfbc711fde035a1197-66bbcfdc84-p2tr8", |
| | "vduId": "VDU2", |
| | "computeResource": { |
| | "resourceId": "vdu2-vnff082149ac20f43dfbc711fde035a1197-66bbcfdc84-p2tr8", |
| | "vimLevelResourceType": "Deployment" |
| | }, |
| | "metadata": {} |
| | } |
| | ], |
| | "vnfcInfo": [ |
| | { |
| | "id": "VDU1-vdu1-vnff082149ac20f43dfbc711fde035a1197-659966c5fb-nngts", |
| | "vduId": "VDU1", |
| | "vnfcResourceInfoId": "vdu1-vnff082149ac20f43dfbc711fde035a1197-659966c5fb-nngts", |
| | "vnfcState": "STARTED" |
| | }, |
| | { |
| | "id": "VDU2-vdu2-vnff082149ac20f43dfbc711fde035a1197-66bbcfdc84-p2tr8", |
| | "vduId": "VDU2", |
| | "vnfcResourceInfoId": "vdu2-vnff082149ac20f43dfbc711fde035a1197-66bbcfdc84-p2tr8", |
| | "vnfcState": "STARTED" |
| | } |
| | ], |
| | "metadata": { |
| | "namespace": "default", |
| | "tenant": "default", |
| | "vdu_reses": { |
| | "VDU1": { |
| | "apiVersion": "apps/v1", |
| | "kind": "Deployment", |
| | "metadata": { |
| | "name": "vdu1-vnff082149ac20f43dfbc711fde035a1197", |
| | "labels": { |
| | "helm.sh/chart": "test-chart-0.1.0", |
| | "app.kubernetes.io/name": "test-chart", |
| | "app.kubernetes.io/instance": "vnff082149ac20f43dfbc711fde035a1197", |
| | "app.kubernetes.io/version": "1.16.0", |
| | "app.kubernetes.io/managed-by": "Helm" |
| | }, |
| | "namespace": "default" |
| | }, |
| | "spec": { |
| | "replicas": 1, |
| | "selector": { |
| | "matchLabels": { |
| | "app.kubernetes.io/name": "test-chart", |
| | "app.kubernetes.io/instance": "vnff082149ac20f43dfbc711fde035a1197" |
| | } |
| | }, |
| | "template": { |
| | "metadata": { |
| | "labels": { |
| | "app.kubernetes.io/name": "test-chart", |
| | "app.kubernetes.io/instance": "vnff082149ac20f43dfbc711fde035a1197" |
| | } |
| | }, |
| | "spec": { |
| | "serviceAccountName": "vnff082149ac20f43dfbc711fde035a1197-test-chart", |
| | "securityContext": {}, |
| | "containers": [ |
| | { |
| | "name": "test-chart", |
| | "securityContext": {}, |
| | "image": "nginx:1.16.0", |
| | "imagePullPolicy": "IfNotPresent", |
| | "ports": [ |
| | { |
| | "name": "http", |
| | "containerPort": 80, |
| | "protocol": "TCP" |
| | } |
| | ], |
| | "resources": {} |
| | } |
| | ] |
| | } |
| | } |
| | } |
| | }, |
| | "VDU2": { |
| | "apiVersion": "apps/v1", |
| | "kind": "Deployment", |
| | "metadata": { |
| | "name": "vdu2-vnff082149ac20f43dfbc711fde035a1197", |
| | "labels": { |
| | "helm.sh/chart": "test-chart-0.1.0", |
| | "app.kubernetes.io/name": "test-chart", |
| | "app.kubernetes.io/instance": "vnff082149ac20f43dfbc711fde035a1197", |
| | "app.kubernetes.io/version": "1.16.0", |
| | "app.kubernetes.io/managed-by": "Helm" |
| | }, |
| | "namespace": "default" |
| | }, |
| | "spec": { |
| | "replicas": 1, |
| | "selector": { |
| | "matchLabels": { |
| | "app.kubernetes.io/name": "test-chart", |
| | "app.kubernetes.io/instance": "vnff082149ac20f43dfbc711fde035a1197" |
| | } |
| | }, |
| | "template": { |
| | "metadata": { |
| | "labels": { |
| | "app.kubernetes.io/name": "test-chart", |
| | "app.kubernetes.io/instance": "vnff082149ac20f43dfbc711fde035a1197" |
| | } |
| | }, |
| | "spec": { |
| | "serviceAccountName": "vnff082149ac20f43dfbc711fde035a1197-test-chart", |
| | "securityContext": {}, |
| | "containers": [ |
| | { |
| | "name": "test-chart", |
| | "securityContext": {}, |
| | "image": "nginx", |
| | "imagePullPolicy": "IfNotPresent", |
| | "ports": [ |
| | { |
| | "name": "http", |
| | "containerPort": 80, |
| | "protocol": "TCP" |
| | } |
| | ], |
| | "resources": {} |
| | } |
| | ] |
| | } |
| | } |
| | } |
| | } |
| | }, |
| | "helm_chart_path": "Files/kubernetes/test-chart-0.1.0.tgz", |
| | "helm_value_names": { |
| | "VDU1": { |
| | "replica": "replicaCountVdu1" |
| | }, |
| | "VDU2": { |
| | "replica": "replicaCountVdu2" |
| | } |
| | }, |
| | "release_name": "vnff082149ac20f43dfbc711fde035a1197", |
| | "revision": "1" |
| | } |
| | } |
| Instantiation State | INSTANTIATED |
| Links | { |
| | "self": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197" |
| | }, |
| | "terminate": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197/terminate" |
| | }, |
| | "scale": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197/scale" |
| | }, |
| | "heal": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197/heal" |
| | }, |
| | "changeExtConn": { |
| | "href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/f082149a-c20f-43df-bc71-1fde035a1197/change_ext_conn" |
| | } |
| | } |
| VIM Connection Info | { |
| | "vim1": { |
| | "vimId": "9c37f36f-f569-4259-b388-d550e55dd65c", |
| | "vimType": "ETSINFV.HELM.V_3", |
| | "interfaceInfo": { |
| | "endpoint": "https://192.168.56.10:6443", |
| | "ssl_ca_cert": "-----BEGIN CERTIFICATE----- MIIDBTCCAe2gAwIBAgIIa76wZDxLNAowDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE AxMKa3ViZXJuZXRlczAeFw0yMzExMDYwMDA3MzBaFw0zMzExMDMwMDEyMzBaMBUx |
| | EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQDd0LBXGxVexr09mVFNSXWQq3TN66IIcXCBAMbIWI4EiQ8Y0zI4hSwADdK2 ltYSdWw7wq3/YTFHK8/YTY7Jvd9/k3UJrqkZ6kBtL20pJUPXNJVLE/hRzsqEnHHv |
| | cfqYZTHvTY4g7qNcMOcfl/oDUGUMfpQT2gs6xoNl0WX/1+QeQbadx1kWaD2Ii45F d8TR+c4wccxNaLArk3ok4h1PNeAwra4mRmBHQQ2wFjkTYGl4+ss3v1yoUJkrQjXL RgzLufeXaz8eRTi36HkjudGKfS3OnUeke3uBN7usW58FFJ8TdKOhuoguRm53kj6+ |
| | TwXtZCOPzn4gNxq6xJE1Xj2hwFfpAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRdmQ4r63pXBHIO8ODqxROE7x+aizAV BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQBeQ/9+bzRe |
| | qbA02MfYnN3vycGhDObcAoiDIMIutojFTpx4hGZjqVgTRpLH5ReddwR4kkxn3NRg weCVkNkhzyGze64nb11qZG71olaOQRMYzyN2hYfmbq7MXSvmJQQYIr1OewaRk+xl TyG1XRXoD2IEaHEvG0+pQJlDerd5Z6S1fkPaKZtcRbM/E6y5VXMV6hegN4MwHZSI |
| | Ll1uEBTxUzzTm3dnl1KL8GDg05ajoYcyL3X/0aWsb/MFhtIlXe2CMxu5qUkLBhzy fCfX4cZpI5KFxMgdmAEoaGbNy7iqsGrLFtEmub2gdEBIVNr7vgOk4OeQ9Uodj6K7 jK97z+cupc5G -----END CERTIFICATE-----" |
| | }, |
| | "accessInfo": {}, |
| | "extra": { |
| | "use_helm": true |
| | } |
| | } |
| | } |
| VNF Configurable Properties | |
| VNF Instance Description | |
| VNF Instance Name | |
| VNF Product Name | Sample VNF |
| VNF Provider | Company |
| VNF Software Version | 1.0 |
| VNFD ID | 330f36dd-8398-4d2c-98c1-bc6c626e88b2 |
| VNFD Version | 1.0 |
+-----------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
4. 检查 Kubernetes 中的部署¶
我们可以通过运行以下命令检查部署的 release。Release 是运行在 Kubernetes 集群上的 chart 的一个实例。
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
vnff082149ac20f43dfbc711fde035a1197 default 1 2023-12-07 05:12:00.368610985 +0000 UTC deployed test-chart-0.1.0 1.16.0
此外,我们可以通过运行以下命令检查部署的容器化 VNF。当 READY 为 1/1 时,表示部署已成功创建。
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
vdu1-vnff082149ac20f43dfbc711fde035a1197 1/1 1 1 6m13s
vdu2-vnff082149ac20f43dfbc711fde035a1197 1/1 1 1 6m13s
如果要在默认命名空间中检查资源是否部署,可以在命令行中追加 -A。
$ kubectl get deploy -A
NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE
default vdu1-vnff082149ac20f43dfbc711fde035a1197 1/1 1 1 6m42s
default vdu2-vnff082149ac20f43dfbc711fde035a1197 1/1 1 1 6m42s
kube-system coredns 2/2 2 2 28h
注意
如果在实例化期间为命名空间指定了除 default 之外的其他值,则部署的资源将在相应的命名空间中实例化。