Al agregar componentes de Kubernetes a una plantilla de nube de Cloud Assembly, puede optar por agregar clústeres o permitir que los usuarios creen espacios de nombres en diversas configuraciones. Por lo general, esta opción depende de los requisitos de control de acceso, de cómo se configuran los componentes de Kubernetes y de los requisitos de implementación.

Para agregar un componente de Kubernetes a una plantilla de nube en Cloud Assembly, seleccione Diseño > Plantillas de nube, haga clic en Nueva y, a continuación, busque y expanda la opción Kubernetes en el menú de la izquierda. A continuación, elija la opción que desee, ya sea Clúster o Espacio de nombres KBS, arrastrándola al lienzo.

Agregar un clúster de Kubernetes asociado con un proyecto a una plantilla de nube es el método más sencillo de hacer que los recursos de Kubernetes estén disponibles para los usuarios válidos. Puede utilizar etiquetas en los clústeres para controlar dónde se implementan, como lo haría con otros recursos de Cloud Assembly. Puede usar etiquetas para seleccionar una zona y un plan de VMware Tanzu Kubernetes Grid Integrated Edition (TKGI) durante la fase de asignación de la implementación de un clúster.

Una vez que se agrega un clúster de esta manera, queda automáticamente disponible para todos los usuarios válidos.

Ejemplos de plantillas de nube

El primer ejemplo de plantilla de nube muestra una plantilla para una implementación de Kubernetes simple controlada mediante el etiquetado. Se creó una zona de Kubernetes con dos planes de implementación, configurados en la página Nueva zona de Kubernetes. En este caso, se agregó una etiqueta denominada placement:tag como una capacidad en la zona, que se usó para que coincida con la restricción análoga de la plantilla de nube. Si hubiera más de una zona configurada con la etiqueta, se seleccionará la que tenga el número de prioridad más bajo.

formatVersion: 1
inputs: {}
resources:
  Cluster_provisioned_from_tag:
    type: Cloud.K8S.Cluster
    properties:
      hostname: 109.129.209.125
      constraints:
	-tag: 'placement tag'
      port: 7003
      workers: 1
      connectBy: hostname 

El segundo ejemplo de plantilla de nube muestra cómo configurar una plantilla con una variable denominada $(input.hostname) para que los usuarios puedan introducir el nombre de host del clúster deseado al solicitar una implementación. Las etiquetas también se pueden utilizar para seleccionar una zona y un plan de TKGI durante la fase de asignación de recursos de la implementación del clúster.

formatVersion: 1
inputs:
  hostname:
    type: string
    title: Cluster hostname
resources:
  Cloud_K8S_Cluster_1:
    type: Cloud.K8S.Cluster
    properties:
      hostname: ${input.hostname}
      port: 8443
      connectBy: hostname
      workers: 1

Si desea utilizar espacios de nombres a fin de administrar el uso de clústeres, puede configurar una variable en la plantilla de nube denominada name: ${input.name} para sustituir el nombre del espacio de nombres que el usuario introduce al solicitar una implementación. Para este tipo de implementaciones, se crea una plantilla similar al siguiente ejemplo:

1 formatVersion: 1
2 inputs:
3 name:
4    type: string
5    title: "Namespace name"
6 resources:
7    Cloud_KBS_Namespace_1:
8        type: Cloud.K8S.Namespace
9        properties:
10            name: ${input.name}

Los usuarios pueden administrar los clústeres implementados a través de archivos kubeconfig a los que se puede acceder desde la página Infraestructura > Recursos > Clústeres de Kubernetes. Busque la tarjeta en la página del clúster deseado y haga clic en Kubeconfig.

Espacios de nombres de supervisor en VMware Cloud Templates

A continuación, se muestra el esquema de un espacio de nombres de supervisor básico en una plantilla de nube de Cloud Assembly.

{
  "title": "Supervisor namespace schema",
  "description": "Request schema for provisioning of Supervisor namespace resource",
  "type": "object",
  "properties": {
    "name": {
      "title": "Name",
      "description": "Alphabetic (a-z and 0-9) string with maximum length of 63 characters. The character ‘-’ is allowed anywhere except the first or last position of the identifier.",
      "type": "string",
      "pattern": "^.*\\$\\{.*\\}.*$|^((?!-)[a-z0-9-]{1,63}(?<!-))$",
      "ignoreOnUpdate": true
    },
    "description": {
      "title": "Description",
      "description": "An optional description of this Supervisor namespace.",
      "type": "string",
      "ignoreOnUpdate": true
    },
    "content": {
      "title": "Content",
      "description": "Kubernetes Yaml Content",
      "type": "string",
      "maxLength": 65000
    },
    "constraints": {
      "title": "Constraints",
      "description": "To target the correct resources, blueprint constraints are matched against infrastructure capability tags. Constraints must include the key name. Options include value, negative [!], and hard or soft requirement.",
      "type": "array",
      "recreateOnUpdate": true,
      "items": {
        "type": "object",
        "properties": {
          "tag": {
            "title": "Tag",
            "description": "Constraint definition in syntax `[!]tag_key[:tag_value][:hard|:soft]` \nExamples:\n```\n!location:eu:hard\n location:us:soft\n!pci\n```",
            "type": "string",
            "recreateOnUpdate": true
          }
        }
      }
    },
    "limits": {
      "title": "Limits",
      "description": "Defines namespace resource limits such as pods, services, etc.",
      "type": "object",
      "properties": {
        "stateful_set_count": {
          "title": "stateful_set_count",
          "description": "This represents the new value for 'statefulSetCount' option which is the maximum number of StatefulSets in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "deployment_count": {
          "title": "deployment_count",
          "description": "This represents the new value for 'deploymentCount' option which is the maximum number of deployments in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "cpu_limit_default": {
          "title": "cpu_limit_default",
          "description": "This represents the new value for the default CPU limit (in Mhz) for containers in the pod. If specified, this limit should be at least 10 MHz.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "config_map_count": {
          "title": "config_map_count",
          "description": "This represents the new value for 'configMapCount' option which is the maximum number of ConfigMaps in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "pod_count": {
          "title": "pod_count",
          "description": "This represents the new value for 'podCount' option which is the maximum number of pods in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "job_count": {
          "title": "job_count",
          "description": "This represents the new value for 'jobCount' option which is the maximum number of jobs in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "secret_count": {
          "title": "secret_count",
          "description": "This represents the new value for 'secretCount' option which is the maximum number of secrets in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "cpu_limit": {
          "title": "cpu_limit",
          "description": "This represents the new value for 'limits.cpu' option which is equivalent to the maximum CPU limit (in MHz) across all pods in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "cpu_request_default": {
          "title": "cpu_request_default",
          "description": "This represents the new value for the default CPU request (in Mhz) for containers in the pod. If specified, this field should be at least 10 MHz.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "memory_limit_default": {
          "title": "memory_limit_default",
          "description": "This represents the new value for the default memory limit (in mebibytes) for containers in the pod.",
         "type": "integer",
          "recreateOnUpdate": false
        },
        "memory_limit": {
          "title": "memory_limit",
          "description": "This represents the new value for 'limits.memory' option which is equivalent to the maximum memory limit (in mebibytes) across all pods in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "memory_request_default": {
          "title": "memory_request_default",
          "description": "This represents the new value for the default memory request (in mebibytes) for containers in the pod.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "service_count": {
          "title": "service_count",
          "description": "This represents the new value for 'serviceCount' option which is the maximum number of services in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "replica_set_count": {
          "title": "replica_set_count",
          "description": "This represents the new value for 'replicaSetCount' option which is the maximum number of ReplicaSets in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "replication_controller_count": {
          "title": "replication_controller_count",
          "description": "This represents the new value for 'replicationControllerCount' option which is the maximum number of ReplicationControllers in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "storage_request_limit": {
          "title": "storage_request_limit",
          "description": "This represents the new value for 'requests.storage' which is the limit on storage requests (in mebibytes) across all persistent volume claims from pods in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "persistent_volume_claim_count": {
          "title": "persistent_volume_claim_count",
          "description": "This represents the new value for 'persistentVolumeClaimCount' option which is the maximum number of PersistentVolumeClaims in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        },
        "daemon_set_count": {
          "title": "daemon_set_count",
          "description": "This represents the new value for 'daemonSetCount' option which is the maximum number of DaemonSets in the namespace.",
          "type": "integer",
          "recreateOnUpdate": false
        }
      },
      "additionalProperties": false
    },
    "vm_classes": {
      "title": "VM classes",
      "description": "Defines set of Virtual Machine classes to be assigned to the namespace",
      "type": "array",
      "recreateOnUpdate": false,
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "title": "Name",
            "description": "Name of the Virtual Machine class.",
            "type": "string",
            "recreateOnUpdate": false
          }
        }
      }
    },
    "storage": {
      "title": "Storage policies",
      "description": "Defines set of storage profiles to be used to assign storage policies to the namespace.",
      "type": "array",
      "recreateOnUpdate": false,
      "items": {
        "type": "object",
        "properties": {
          "profile": {
            "type": "object",
            "title": "Storage profile",
            "description": "Defines storage policies to be assigned to the namespace",
            "recreateOnUpdate": false,
            "properties": {
              "constraints": {
                "title": "Constraints",
                "description": "To target the correct storage profiles, blueprint constraints are matched against storage profile capability tags.",
                "type": "array",
                "recreateOnUpdate": false,
                "items": {
                  "type": "object",
                  "properties": {
                    "tag": {
                      "title": "Tag",
                      "description": "Constraint definition in syntax `[!]tag_key[:tag_value][:hard|:soft]` \nExamples:\n```\nlocation:eu:hard\n location:us:soft\n```",
                      "type": "string",
                      "recreateOnUpdate": false
                    }
                  }
                },
                "minItems":1
              },
              "limitMb": {
                "title": "Limit",
                "description": "The maximum amount of storage (in mebibytes) which can be utilized by the namespace for this storage policy. Optional. If unset, no limits are placed.",
                "type": "integer"
              }
            },
            "required": [
              "constraints"
            ]
          }
        }
      }
    }
  },
  "required": [
    "name"
  ]
}

Las VMware Cloud Templates admiten el uso de límites en los espacios de nombres de supervisor. Los límites permiten controlar el uso de recursos de CPU y memoria, así como el número máximo de pods que permiten las máquinas implementadas en el espacio de nombres.

formatVersion: 1
inputs: {}
resources:
  Cloud_SV_Namespace_1:
    type: Cloud.SV.Namespace
    properties:
      name: '${env.deploymentName}'
      limits:
        - cpu_limit: 1000
          cpu_request_default: 800
          memory_limit: 2000
          memory_limit_default: 1500
          pod_count: 200

El siguiente ejemplo muestra cómo se puede especificar una directiva de almacenamiento mediante etiquetas.

formatVersion: 1
inputs: {}
resources:
  Cloud_SV_Namespace_1:
    type: Cloud.SV.Namespace
    properties:
      name: 'ns-with-storage-policy'
      description: 'sample'
      storage: 
        - profile: 
            limitMb: 1000
            constraints: 
              - tag: 'storage:fast'
        - profile: 
            constraints: 
              - tag: 'storage:cheap'

Usar YAML arbitrarios con espacios de nombres de autoservicio o VCT de clúster

Como parte de la creación de un clúster o espacio de nombres, los usuarios a menudo desean ejecutar personalizaciones adicionales. Por ejemplo: es posible que desee agregar usuarios (función/enlace de funciones) o crear una directiva de seguridad del pod o instalar agentes. Al utilizar la propiedad de YAML content, los usuarios pueden definir paquetes personalizados que desean aprovisionar en ese clúster/espacio de nombres/espacio de nombres de supervisor.

Cada paquete de contenido de YAML asociado con la propiedad content debe separarse con un guion doble (---). Además, la información de contenido debe ser una cadena de varias líneas. Consulte el siguiente ejemplo de YAML para ver cómo se pueden configurar los paquetes de contenido.

formatVersion: 1
inputs: {}
resources:
  Cloud_Tanzu_Cluster_1:
    type: Cloud.Tanzu.Cluster
    properties:
      name: ddonchev-tkc
      plan: small
      content: |-
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: psp:authenticated-from-yaml
        subjects:
        - apiGroup: rbac.authorization.k8s.io
          kind: Group
          name: system:authenticated
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: psp:vmware-system-privileged
        ---
        apiVersion: apiextensions.k8s.io/v1
        kind: CustomResourceDefinition
        metadata:
          # name must match the spec fields below, and be in the form: <plural>.<group>
          name: crontabs.stable.example.com
        spec:
          # group name to use for REST API: /apis/<group>/<version>
          group: stable.example.com
          # list of versions supported by this CustomResourceDefinition
          versions:
            - name: v1
              # Each version can be enabled/disabled by Served flag.
              served: true
              # One and only one version must be marked as the storage version.
              storage: true
              schema:
                openAPIV3Schema:
                  type: object
                  properties:
                    spec:
                      type: object
                      properties:
                        cronSpec:
                          type: string
                        image:
                          type: string
                        replicas:
                          type: integer
          # either Namespaced or Cluster
          scope: Namespaced
          names:
            # plural name to be used in the URL: /apis/<group>/<version>/<plural>
            plural: crontabs
            # singular name to be used as an alias on the CLI and for display
            singular: crontab
            # kind is normally the CamelCased singular type. Your resource manifests use this.
            kind: CronTab
            # shortNames allow shorter string to match your resource on the CLI
            shortNames:
            - ct

El YAML definido en la propiedad de contenido también aparece en la pestaña Propiedades de la implementación.

Cloud Assembly solo puede crear recursos de contenido en el ámbito del recurso que se está implementando. Por ejemplo: si aprovisiona un espacio de nombres de Kubernetes, Cloud Assembly no puede crear una implementación dentro de un espacio de nombres diferente. Los usuarios tienen los mismos derechos que si utilizaran kubeconfig con kubectl.

Después de aprovisionar la máquina virtual, se iniciará una instalación de los objetos de Kubernetes dentro de la propiedad content. Si uno de los recursos a los que se hace referencia en la propiedad de contenido de YAML no se puede aprovisionar, Cloud Assembly revertirá y eliminará todos los objetos de Kubernetes anteriores del recurso y la implementación tendrá el estado Error. El recurso seguirá aprovisionándose y estando visible. Además, puede seguir utilizando las acciones del día 2, incluido el intento de volver a aplicar el contenido.

Puede mejorar la propiedad content con entradas de la plantilla de nube como se muestra en el siguiente ejemplo.

formatVersion: 1
inputs: {}
resources:
  Cloud_SV_Namespace_1:
    type: Cloud.SV.Namespace
    properties:
      name: sv-namespace-with-vm-classes
      vm_classes:
        - name: best-effort-2xlarge
        - name: best-effort-4xlarge
        - name: best-effort-8xlarge

También puede aprovisionar recursos personalizados como TanzuKubernetesCluster. Se producirá un error en esta operación del día 1, ya que el espacio de nombres de supervisor no contendrá las clases de máquinas virtuales y las clases de almacenamiento requeridas. Cuando las clases de máquinas virtuales y las clases de almacenamiento están enlazadas al espacio de nombres de supervisor, puede crear el TanzuKubernetesCluster (u otro recurso) mediante la acción del día 2.

Nota: Puede aprovisionar un recurso sin contenido y aún podrá agregar objetos de Kubernetes como YAML con la acción del día 2.

El contenido que aparece en la propiedad YAML define lo que se aprovisiona en el recurso. Al editar este contenido, la siguiente tabla muestra los posibles resultados:

Acción Resultado
Si agrega un objeto de Kubernetes y envía. El objeto especificado se crea en el recurso.
Si elimina un objeto de Kubernetes y envía. El objeto especificado se elimina del recurso.
Si modifica un objeto de Kubernetes y envía. El objeto especificado se revisa en el recurso.

Es importante aclarar qué acciones se consideran una modificación del objeto actual. Por ejemplo: si modifica el campo de espacio de nombres de un objeto, se crea un nuevo objeto en lugar del anterior que se revisa.

La exclusividad de un recurso se define mediante los siguientes campos: apiVersion, kind, metadata.name, metadata.namespace