Abr 24 2022

Registrar un Esclavo de Jenkins con Ansible

Published by under Ansible,Devops,Jenkins

Anteriormente vimos cómo agregar un esclavo Jenkins llamando a una API REST con Curl. ¡Automaticemos esto con Ansible para tener un nuevo nodo disponible en minutos!

Primero necesitará un usuario de Jenkins y su token asociado con los permisos correctos. «Agent connect» y «create» deberían ser suficientes. Simplemente llamaré a este usuario «node». Inicie sesión en Jenkins con este usuario y cree un nuevo token.

Esclavo de Jenkins con Ansible


Los detalles del nuevo nodo deben pasarse a la URL REST. Incrustemos estos ajustes en una template Jinja2 como esta:

{
   "name": "{{ jenkins_node }}",
   "nodeDescription": "slave {{ jenkins_node }}",
   "numExecutors": "{{ jenkins_numExecutors }}",
   "remoteFS": "{{ jenkins_remoteFS }}",
   "labelString": "{{ jenkins_label }}",
   "mode": "EXCLUSIVE",
   "": [
      "hudson.slaves.JNLPLauncher",
      "hudson.slaves.RetentionStrategy$Always"
   ],
   "launcher": {
      "stapler-class": "hudson.slaves.JNLPLauncher",
      "$class": "hudson.slaves.JNLPLauncher",
      "workDirSettings": {
         "disabled": true,
         "workDirPath": "",
         "internalDir": "remoting",
         "failIfWorkDirIsMissing": false
      },
      "tunnel": "",
      "vmargs": ""
   },
   "retentionStrategy": {
      "stapler-class": "hudson.slaves.RetentionStrategy$Always",
      "$class": "hudson.slaves.RetentionStrategy$Always"
   },
   "nodeProperties": {
      "stapler-class-bag": "true",
      "hudson-slaves-EnvironmentVariablesNodeProperty": {
         "env": [
            {
               "key": "JAVA_HOME",
               "value": "{{ java_home }}"
            }
         ]
      },
      "_comment:": {
         "hudson-tools-ToolLocationNodeProperty": {
           "locations": [
               {
                  "key": "hudson.model.JDK$DescriptorImpl@JAVA-8",
                  "home": "/usr/bin/java"
               }
            ]
         }
      }
   }
}


Adapta el modelo según sus necesidades, si quiere un agente SSH por ejemplo.

Las variables que se reemplazarán en la template se pueden definir en el siguiente archivo «predeterminado»:

jenkins_slave_user: jenkins-slave
jenkins_token: xxxxxxde2152xxxxxxaa339exxxxxx48d6
jenkins_user: node
jenkins_url: https://jenkins.domain.lan
jenkins_node: "{{ansible_hostname}}"
jenkins_numExecutors: 4
jenkins_remoteFS: /home/jenkins-slave
jenkins_label: "label_1 label_2 label_3"
java_home: /usr/lib/jvm/java-8-openjdk-amd64/


jenkins_user se conectará al maestro y creará el nuevo nodo, autenticándose con el jenkins_token creado anteriormente.
jenkins_slave_user es el usuario sistema que iniciará el servicio de Jenkins en el nodo.

Ahora podemos agregar una tarea de Ansible a nuestro rol. Primero llamamos a la API REST:

- name: crear el nodo en el maestro Jenkins
  uri:
    url: "{{jenkins_url}}/computer/doCreateItem?name={{ jenkins_node }}&type=hudson.slaves.DumbSlave"
    method: POST
    body_format: form-urlencoded
    force_basic_auth: yes
    user: "{{ jenkins_user }}"
    password: "{{jenkins_token }}"
    body: "json={{ lookup('template', 'node.json.j2', convert_data=False) }}"
    return_content: yes
    status_code: 200, 302, 400
  register: webpage


Agregué el código de retorno 400 en caso de que el nodo ya exista, pero puede eliminarlo si prefiere que se detenga en este caso. Lo paso fail si el error no es ‘ya existe’:

- name: continuar en caso de que el agente ya exista
  fail:
  when: >
          webpage.status == '400'
          and 'already exists' not in webpage.x_error


El servicio agente de Jenkins necesita una clave del maestro para comenzar. La clave está disponible en la página del agente en formato XML.

- name: recuperar el contenido de la página del agente
  uri:
    url: "{{jenkins_url}}/computer/{{jenkins_node}}/slave-agent.jnlp"
    method: POST
    body_format: form-urlencoded
    force_basic_auth: yes
    user: "{{ jenkins_user }}"
    password: "{{ jenkins_token }}"
    return_content: yes
    status_code: 200
  register: slavepage

- name: recuperar la clave contenida en el xml
  xml:
    xmlstring: "{{slavepage.content}}"
    xpath: /jnlp/application-desc/argument
    content: text
  register: secretxml


La clave se almacenará en el archivo /etc/default/jenkins-slave del usuario sistema que se carga cuando se inicia el servicio.
Aquí está la template:

JENKINS_USER="jenkins-slave"
JENKINS_WORKDIR=$(eval echo "~$JENKINS_USER")
JENKINS_URL={{ jenkins_url }}
JENKINS_NODENAME=$(hostname)
JENKINS_SECRET={{ jenkins_secret }}
JAVA_ARGS="-Xmx6g"


Agregue el script de inicio. Ahora podemos copiar estos 2 archivos:

- name: copiar el archivo de configuración de jenkins-slave
  template:
    src: jenkins-slave-default.j2
    dest: /etc/default/jenkins-slave
    owner: jenkins-slave
    group: jenkins-slave
    mode: 0600
  vars:
    jenkins_secret: "{{secretxml.matches[0].argument}}"
  register: jenkins_config

- name: Copiar el init script jenkins-slave
  copy:
    src: jenkins-slave-init
    dest: /etc/init.d/jenkins-slave
    owner: root
    group: root
    mode: 0755


Y nos aseguramos de que el servicio se inicie correctamente:

- name: reiniciar y activar el servicio jenkins-slave si es necesario
  service:
    name: jenkins-slave
    enabled: yes
    state: restarted
  when: jenkins_config.changed

- name: iniciar y activar el servicio jenkins-slave
  service:
    name: jenkins-slave
    enabled: yes
    state: started


Estos pasos son los básicos, pero puede hacer mucho más, como agregar la creación de usuario sistema Jenkins, agregar su propia CA si tiene una IP privada y mucho más.

Vea también cómo acelerar Ansible y optimizar sus tiempos de implementación.

 

No responses yet

Mar 21 2022

Administre las Entradas de DNS con Terraform

Published by under Devops,Terraform

Terraform ayuda a construir la infraestructura as code. Cada vez más servidores web ahora ofrecen plugins de Terraform para administrar zonas DNS. Gandi es uno de ellos, junto con OVH y muchos otros. Probemos la versión 2 que se acaba de lanzar y veamos cómo crear registros DNS en Gandi desde un repositorio de Git.


El Provider de DNS Terraform Gandi

El proveedor Terraform de Gandi se declara en un archivo, por lo general, provider.tf. Puede agregar la clave API de Gandi necesaria para autenticar y registrar recursos. Se puede generar desde su sitio web en «Configuración de usuario», «Administración de cuentas de usuario y configuración de seguridad» en la pestaña Seguridad.
Por obvias razones de seguridad, se recomienda crear la clave de la clave API en la variable de entorno GANDI_KEY. Lo dejo así en provider.tf como parte de la prueba.

terraform {
  required_providers {
    gandi = {
      version = "~> 2.0"
      source   = "go-gandi/gandi"
    }
  }
}

provider "gandi" {
  key = "XXXXXXXXXXXXXXXXXXX"
}


Archivo de registros DNS

Gandi realiza copias de seguridad automáticas de las entradas de DNS en un formato de archivo simple, en el que me inspiré para el archivo de registros de DNS. Llamémoslo dominio.txt donde «dominio» es su nombre de dominio. Aquí hay un ejemplo con 3 entradas DNS.

www1 300 IN A 127.0.0.1
www2 300 IN A 127.0.0.1
www  300 IN CNAME www1


Debemos declarar el dominio como una variable que usaremos más adelante, en variable.tf.

variable domain {
  default = "mydomain.com"
}


Código Terraform de Entradas DNS

Aquí hay un archivo main.tf de Terraform minimalista que lee el archivo que contiene las entradas de DNS y crea un nuevo registro de cada línea.

data "local_file" "dns_file" {
  filename = "./${var.domain}.txt"
}

locals {
  # Convertit le fichier en tableau
  dns_records = toset([ for e in split("\n", data.local_file.dns_file.content) :
                        replace(e, "/\\s+/", " ") if e != "" ])
}
  
resource "gandi_livedns_record" "record" {
  for_each =  local.dns_records
  zone     =  var.domain
  name     =  split(" ",each.value)[0]
  type     =  split(" ",each.value)[3]
  ttl      =  split(" ",each.value)[1]
  values   = [split(" ",each.value)[4]]
}


Puede apreciar el poder de Terraform, que puede hacer mucho con solo unas pocas líneas de código.

Tenga en cuenta que estoy eliminando varios espacios – replace(e, «/\\s+/», » «) – al dividir las líneas porque la función de división crea un elemento vacío si procesa dos espacios. También ignoro las líneas en blanco. Luego recorremos cada línea para crear registros DNS con un recurso de tipo gandi_livedns_record.
Funciona con entradas A y CNAME.

Podríamos aplicar diferentes tratamientos para cada tipo de registro creando tablas separadas o ignorando otros tipos de DNS:

dns_records   = [ for e in split("\n", data.local_file.dns_file.content) :
                  replace(e, "/\\s+/", " ") if e != "" ]
A_records     = toset([for e in local.dns_records : e if split(" ",e)[3] == "A"])
CNAME_records = toset([for e in local.dns_records : e if split(" ",e)[3] == "CNAME"])


Luego podríamos recorrer A_records para crear las entradas A y procesar los CNAME en un segundo bloque de recursos.
Otras posibilidades: manejo de CNAMEs multivalor, registros TXT, etc…


Plan y Aplicación de Creación de Entradas de DNS

Ejecute terraform plan para comprobar lo qué terraform se va a modificar. Debería mostrar que se crearán 3 recursos. ejecute terraform apply para crear las entradas de DNS.

$ terraform plan

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # gandi_livedns_record.record["www 300 IN CNAME www1"] will be created
  + resource "gandi_livedns_record" "record" {
      + href   = (known after apply)
      + id     = (known after apply)
      + name   = "www"
      + ttl    = 300
      + type   = "CNAME"
      + values = [
          + "www1",
        ]
      + zone   = "domain.com"
    }

  [...]

Plan: 3 to add, 0 to change, 0 to destroy.


En cuanto al rendimiento, probé la creación de 200 entradas y tardé 2’57» con una conexión bastante mala. Sí, es un poco lento ya que se basa en llamar a una API REST para cada entrada. Sin embargo, crear una entrada adicional será muy rápido.
Un terraform plan tardó 1’31» en actualizar estas 200 entradas.

Por supuesto, siempre puede omitir el paso de actualización terraform plan -refresh=false que demora menos de un segundo.
El plan se puede guardar en un archivo temporal, que se puede cargar al aplicar los cambios.

$ terraform plan -refresh=false -out=plan.file
$ terraform apply plan.file
 

No responses yet

Feb 16 2022

Registrar un Esclavo Jenkins con API REST

Published by under Devops,Jenkins

Aquí se explica cómo registrar un nodo Jenkins automáticamente con una llamada a una API REST a el maestro.
La mayor parte del trabajo consiste en crear el código json que describe el nuevo esclavo de Jenkins. La configuración puede ser ligeramente diferente según la configuración del nodo que desee aplicar.
Para obtener exactamente lo que desea, puede crear un esclavo de prueba manualmente y capturar el objeto JSON en la pestaña de red de las herramientas de desarrollo de su navegador mientras hace clic en «Guardar». Mientras tanto, aquí hay un ejemplo, suponiendo que el agente se inicia a través de un script de inicio:

{
   "name": "my_jenkins_slave",
   "nodeDescription": "my Jenkins slave",
   "numExecutors": "2",
   "remoteFS": "/home/jenkins",
   "labelString": "slave",
   "mode": "EXCLUSIVE",
   "": [
      "hudson.slaves.JNLPLauncher",
      "hudson.slaves.RetentionStrategy$Always"
   ],
   "launcher": {
      "stapler-class": "hudson.slaves.JNLPLauncher",
      "$class": "hudson.slaves.JNLPLauncher",
      "workDirSettings": {
         "disabled": true,
         "workDirPath": "",
         "internalDir": "remoting",
         "failIfWorkDirIsMissing": false
      },
      "tunnel": "",
      "vmargs": ""
   },
   "retentionStrategy": {
      "stapler-class": "hudson.slaves.RetentionStrategy$Always",
      "$class": "hudson.slaves.RetentionStrategy$Always"
   },
   "nodeProperties": {
      "stapler-class-bag": "true",
      "hudson-slaves-EnvironmentVariablesNodeProperty": {
         "env": [
            {
               "key": "JAVA_HOME",
               "value": "/usr/lib/jvm/java-8-openjdk-amd64"
            }
         ]
      },
      "_comment:": {
         "hudson-tools-ToolLocationNodeProperty": {
           "locations": [
               {
                  "key": "hudson.model.JDK$DescriptorImpl@JAVA-8",
                  "home": "/usr/bin/java"
               }
            ]
         }
      }
   }
}



Todo lo que queda es establecer las siguientes 3 variables de entorno:

jenkins_user=my_jenkins_slave
jenkins_token=my_jenkins_token
jenkins_url=https://jenkins.midominio.lan



Y envíe un post del archivo JSON usando una simple solicitud de curl:

$ curl -L -s -o /dev/null -w "%{http_code}" -u $jenkins_user:$jenkins_token \
-H "Content-Type:application/x-www-form-urlencoded" -X POST \
-d "json=$(cat /tmp/node.json)" \
"$jenkins_url/computer/doCreateItem?name=my_jenkins_slave&type=hudson.slaves.DumbSlave"; \
echo
200


Si no obtiene un código de respuesta 200, ejecute el mismo omitiendo «-o /dev/null» para depurar.
Algunos dirán que también puede usar otras herramientas como Jenkins CLI o plugins, pero la API REST de Jenkins funciona a través de firewalls y sigue siendo bastante simple de implementar.
En el próximo artículo, usaré este método para registrar automáticamente un agente de Jenkins con Ansible.

 

No responses yet

Feb 12 2022

Recurso Compartido de Terraform con el Ejemplo: Claves SSH

Published by under Devops,Terraform

Terraform automatiza la creación de infraestructura en el cloud, comúnmente conocida como infraestructura como código. Necesitamos crear una máquina virtual, que debe contener las claves SSH de 3 administradores. El objetivo será hacer que este recurso compartido de Terraform sea reutilizable por otros módulos. Este ejemplo en Cloud IBM se basa en el plugin de IBM para Terraform, pero el método sigue siendo válido para otros proveedores de cloud, por supuesto.
No puse la creación de la VPC, subredes y grupos de seguridad para ganar legibilidad.

Recurso Compartido de Terraform


Recursos en un solo módulo

Comencemos por crear los archivos ssh.tf que contienen el código que crea las claves del administrador y vm.tf el código de creación del servidor en el mismo directorio. Las claves se dan como parámetros a la máquina virtual.

resource "ibm_is_ssh_key" "user1_sshkey" {
  name       = "user1"
  public_key = "ssh-rsa AAAAB3[...]k+XR=="
}

resource "ibm_is_ssh_key" "user2_sshkey" {
  name       = "user2"
  public_key = "ssh-rsa AAAAB3[...]Zo9R=="
}

resource "ibm_is_ssh_key" "user3_sshkey" {
  name       = "user3"
  public_key = "ssh-rsa AAAAB3[...]67GqV="
}
resource "ibm_is_instance" "server1" {
  name    = "server1"
  image   = var.image
  profile = var.profile
  vpc  = ibm_is_vpc.vpc.id
  zone = var.zone1

  primary_network_interface {
    subnet          = ibm_is_subnet.subnet1.id
    security_groups = [ibm_is_vpc.vpc.default_security_group]
  }

  keys = [
    ibm_is_ssh_key.user1_sshkey.id,
    ibm_is_ssh_key.user2_sshkey.id,
    ibm_is_ssh_key.user3_sshkey.id
  ]
}


El código es simple pero tiene un problema importante:
Las claves SSH no son reutilizables en otro módulo de Terraform. Si copiamos/pegamos este código para crear una segunda VM, un error indicará que las claves ya existen. Además, agregar una clave requiere modificar los 2 archivos de Terraform.


Recursos comunes de Terraform

Por lo tanto, es necesario crear las claves SSH en un módulo Terraform independiente y hacerlas accesibles desde los otros módulos. Esto se puede lograr exportando los id de las claves utilizando los valores output. Los outputs permiten que las variables estén disponibles en la línea de comandos o en otros módulos de Terraform para su reutilización.
Muevamos la declaración de las claves a un nuevo directorio de Terraform al que agregamos una salida output ssh_keys que devuelve una matriz de sus ID, ya que esto es lo que las máquinas virtuales esperan como parámetro.

resource "ibm_is_ssh_key" "user1_sshkey" {
  name       = "user1"
  public_key = "ssh-rsa AAAAB3[...]k+XR=="
}

resource "ibm_is_ssh_key" "user2_sshkey" {
  name       = "user2"
  public_key = "ssh-rsa AAAAB3[...]Zo9R=="
}

resource "ibm_is_ssh_key" "user3_sshkey" {
  name       = "user3"
  public_key = "ssh-rsa AAAAB3[...]67GqV="
}

output "ssh_keys" {
  value = [
    ibm_is_ssh_key.user1_sshkey.id,
    ibm_is_ssh_key.user2_sshkey.id,
    ibm_is_ssh_key.user3_sshkey.id
  ]
}


Después de ejecutar terraform apply, podemos mostrar los valores de salida con terraform output:

$ terraform output
ssh_keys = [
  "r010-3e98b94b-9518-4e11-9ac4-a014120344dc",
  "r010-b271dce5-4744-48c3-9001-a620e99563d9",
  "r010-9358c6ab-0eed-4de7-a4a0-4ba20b2c04c9",
]


Es exactamente lo que queríamos. Todo lo que queda es recuperar el contenido de la salida en forma de data lookup para usarlo en el módulo VM.

data "terraform_remote_state" "ssh_keys" {
  backend = "local"
  config = {
    path = "../ssh_keys/terraform.tfstate"
  }
}

resource "ibm_is_instance" "server1" {
  name    = "server1"
  image   = var.image
  profile = var.profile

  primary_network_interface {
    subnet          = ibm_is_subnet.subnet1.id
    security_groups = [ibm_is_vpc.vpc.default_security_group]
  }

  vpc  = ibm_is_vpc.vpc.id
  zone = var.zone1
  keys = data.terraform_remote_state.ssh_keys.outputs.ssh_keys
}


Es mucho mejor, podemos administrar las claves SSH independientemente de otros módulos de Terraform y reutilizarlas a voluntad. El path del data lookup es la ruta relativa al directorio que contiene el archivo ssh.tf.


Variables de Lista

No está mal pero podríamos hacer más elegante la creación de recursos compartidos (aquí las claves SSH).
De hecho, agregar una nueva clave se realiza en 2 lugares: crear un recurso de Terraform y agregarlo a los valores devueltos en la salida. Lo cual es restrictivo y genera errores.De hecho, agregar una nueva clave se realiza en 2 lugares: crear un recurso de Terraform y agregarlo a los valores devueltos en la salida. Lo cual es restrictivo y puede generar errores.
Además, sigue siendo bastante difícil de leer y sería más claro separar valores y código.

Para eso almacenaremos las claves en una tabla de tipo map en un archivo terraform.tfvars, que se cargará automáticamente.

ssh_keys = {
  "user1" = "ssh-rsa AAAAB3[...]k+XR=="
  "user2" = "ssh-rsa AAAAB3[...]Zo9R=="
  "user3" = "ssh-rsa AAAAB3[...]67GqV="
}


En ssh.tf, luego recorreremos esta matriz de clave/valor para crear los recursos y exportarlos en el output.

# Définition du tableau
variable "ssh_keys" {
  type = map(string)
}

resource "ibm_is_ssh_key" "keys" {
  for_each = var.ssh_keys
  name = each.key
  public_key = each.value
}

output "ssh_keys" {
  value = values(ibm_is_ssh_key.keys)[*].id
}


Recuperar valores es un poco complejo. Empecé generando valores de output (ibm_is_ssh_key.keys) para analizar la estructura y así recuperar los id.

Al final, un nuevo recurso compartido (una clave SSH en nuestro caso) se realiza simplemente agregándolo a una matriz, en un archivo que contiene solo variables. En un lugar. Cualquiera puede hacer esto sin siquiera leer o entender el código.

 

No responses yet

Nov 13 2021

Renovar un certificado LetsEncrypt automáticamente en Kubernetes

Published by under Kubernetes

Instalar cert-manager

Cert-manager es un chart Helm que proporciona sus propios recursos que puede instalar en un clúster de Kubernetes. Ayuda en la automatización de certificado, su renovación y gestión. Es un valor agregado real cuando utiliza proveedores de certificados que ofrecen API para automatizar estos procesos. Además, es mejor renovar los certificados de LetsEncrypt automáticamente ya que solo son válidos por un período de 3 meses.
cert-manager está disponible en el repositorio Helm Jetstack, agréguelo a su lista de repositorios de esta manera:

helm repo add jetstack https://charts.jetstack.io
helm repo update


Cert-manager se ejecuta en su propio namespace, así que créelo primero e instale el chart Helm cert-manager

kubectl create namespace cert-manager
helm install cert-manager \
     --namespace cert-manager jetstack/cert-manager \
     --set installCRDs=true

–set installCRDs=true dice a cert-manager que instale recursos personalizados como certificaterequests, certificates o clusterissuers.


Cluster Issuer LetsEncrypt

Un cluster issuer contendrá la información de un proveedor de certificados. Si desea obtener sus certificados SSL firmados por LetsEncrypt, deberá aplicar este archivo yaml al clúster de Kubernetes:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: it@company.xxx
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - http01:
        ingress:
          class: public-iks-k8s-nginx

LetsEncrypt pertenece a la categoría de emisores ACME, lo que significa que los navegadores web confían en ellos. Proporciona un certificado después de verificar que es el propietario del dominio. La verificación se puede hacer de 2 formas: ya sea una entrada TXT de DNS o un challenge HTTP. Kubernetes sirve HTTP, por lo que la mayoría elegirá el challenge HTTP01. Esto se define en la sección de solvers.

La segunda información importante es la clase. cert-manager mirará los ingresos cuya clase coincida y les proporcionará un certificado SSL. Anotación de clase de ingress públicas de IBM Cloud se llama public-iks-k8s-nginx, por lo tanto, debe configurarlo en el clúster issuer. Verifique su ingress para que se adapte a sus propias necesidades.


Definición de Ingress

Ahora que tiene un clúster issuer y cert-manager está instalado, deberá indicarles a qué ingress deben emitir certificados. Esto se hace con las anotaciones del ingress.
Simplemente especifique el emisor del clúster en la anotación cert-manager.io/cluster-issuer.
Como se vio anteriormente, la anotación kubernetes.io/ingress.class se establece en public-iks-k8s-nginx en IKS. Ingrese lo que sea apropiado en su configuración.
Agregue acme.cert-manager.io/http01-edit-in-place dependiendo de si desea un ingress separada para el challenge HTTP o si es parte de la entrada existente.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  labels:
    name: app-ingress
  annotations:
    acme.cert-manager.io/http01-edit-in-place: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: public-iks-k8s-nginx
spec:
  tls:
  - hosts:
    - www.netexpertise.eu
    secretName: letsencrypt-netexpertise

  rules:
  - host: www.netexpertise.eu
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-backend-app
            port:
              number: 80


Renovar certificados LetsEncrypt

Cert-manager creará ingress, servicio y pod en su namespace que proporcionará una página web para el challenge HTTP. Desaparecerán tan pronto como el certificado LetsEncrypt se haya renovado y emitido en el secreto definido en secretName.

Si encuentra un problema, puede echar un vistazo a los registros de los diferentes pods en el namespace de cert-manager, así como en el estado del recurso de certificado. Un kubectl describe cert debe proporcionar toda la información necesaria.

 

No responses yet

Next »