Harbor en OKD 25 Agosto 2023

Harbor en OKD

Vimos en anteriores entregas como utilizar Harbor como cache de imágenes y como configurar CRI-O para que utilice Harbor como cache. Hoy queremos mostrar el caso específico para lograr el mismo comportamiento en un cluster OKD, cuya configuración presenta algunas dificultades adicionales.

Configurando los nodos

Supongamos que tenemos nuestro Harbor corriendo en my-harbor.example.com y que ya tenemos configurado el [proyecto de harbor para que funcione como cache de docker.io]. Además, OKD utiliza CRI-O, así que lo único que deberíamos hacer es configurarlo para que utilice nuestro registry como mirror, ¿verdad?

Veamos, lo que queremos es reemplazar el archivo /etc/containers/registries.conf en el filesystem de cada nodo. Según la documentación oficial de OKD, la configuración de las maquinas se realiza a través de un objeto especial, los MachineConfigs. Estos se generan a partir de una definición yaml:

❯ cat 99-worker-harbor.bu
variant: openshift
version: 4.11.0
metadata:
  name: 99-worker-harbor-proxy
  labels:
    machineconfiguration.openshift.io/role: worker
storage:
  files:
  - path: /etc/containers/registries.conf
    mode: 0644
    overwrite: true
    contents:
      inline: |
        unqualified-search-registries = ['registry.access.redhat.com', 'docker.io']
        [[registry]]
        prefix = 'docker.io'
        insecure = false
        blocked = false
        location = 'docker.io'
        [[registry.mirror]]
        location = 'my-harbor.example.com/proxy.docker.io'
        insecure = true        

Luego la documentación indica que se utilice la herramienta butane para generar los manifiestos de los MachineConfigs:

❯ butane 99-worker-harbor.bu -o ./99-worker-harbor.yaml
❯ cat 99-worker-harbor.yaml
# Generated by Butane; do not edit
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
  labels:
    machineconfiguration.openshift.io/role: worker
  name: 99-worker-harbor-proxy
spec:
  config:
    ignition:
      version: 3.2.0
    storage:
      files:
        - contents:
            compression: gzip
            source: data:;base64,H4sIAAAAAAAC/2yOQW6EMAxF9zlFdtkU9wScBGVhEgNWQ0ztRILbV5VK2xnN8uk9W7/Xz46FF6Y8GKGmbVBa2ZoymR/9FH7wAkyJzEApb9ggyR7efMiSPkiBJUQ3TXcbozuUFj79+D9xXI1SV/KjX7AYubl8y/zLRRI2lvp09/cZdlYVjfEhve2woc6iYFTREFaZAfX9UDkveD2jaSf3FQAA//95sGm0BQEAAA==
          mode: 420
          overwrite: true
          path: /etc/containers/registries.conf

Pero al aplicarlo, si bien se empiezan a actualizar los machineconfigpools no progresan correctamente y se muestra un error:

  pool is degraded because nodes fail with "1 nodes are reporting degraded
  status on sync": "Node okd-cluster-j92p5-blackbox-monitoring-pmbmh is
  reporting: \"failed decoding TOML content from file
  /etc/containers/registries.conf: files cannot contain NULL bytes; probably
  using UTF-16; TOML files must be UTF-8\"" 

Probamos diferentes formas de encodear el archivo, pero siempre obteníamos el mismo error.

Solución

La solución que encontramos fue encodear el archivo en base64 y pegarlo en el campo source del manifiesto antes generado. Para esto primero se escribe el archivo registries.conf con el contenido deseado, por ejemplo:

 cat registries.conf
unqualified-search-registries = ['registry.access.redhat.com','docker.io']
[[registry]]
prefix = 'docker.io'
insecure = false
blocked = false
location = 'docker.io'
[[registry.mirror]]
location = 'my-harbor.example.com/proxy.docker.io'
insecure = true

Luego se encodea en base64:

❯ cat registries.conf | base64 -w0
dW5xdWFsaWZpZWQtc2VhcmNoLXJlZ2lzdHJpZXMgPSBbJ3JlZ2lzdHJ5LmFjY2Vzcy5yZWRoYXQuY29tJywnZG9ja2VyLmlvJ10KW1tyZWdpc3RyeV1dCnByZWZpeCA9ICdkb2NrZXIuaW8nCmluc2VjdXJlID0gZmFsc2UKYmxvY2tlZCA9IGZhbHNlCmxvY2F0aW9uID0gJ2RvY2tlci5pbycKW1tyZWdpc3RyeS5taXJyb3JdXQpsb2NhdGlvbiA9ICdteS1oYXJib3IuZXhhbXBsZS5jb20vcHJveHkuZG9ja2VyLmlvJwppbnNlY3VyZSA9IHRydWUK%

Esto se pega en el campo source del archivo antes generado (haciendo caso omiso del comentario de no editar). Además borramos la compresión gzip y se le puede agregar el charset con data:text/plain;charset=utf-8;base64,. Entonces queda:

❯ cat 99-worker-harbor.yaml
  # Generated by Butane; do not edit
  apiVersion: machineconfiguration.openshift.io/v1
  kind: MachineConfig
  metadata:
    labels:
      machineconfiguration.openshift.io/role: worker
    name: 99-worker-harbor-proxy
  spec:
    config:
      ignition:
        version: 3.2.0
      storage:
        files:
          - contents:
              source: data:text/plain;charset=utf-8;base64,dW5xdWFsaWZpZWQtc2VhcmNoLXJlZ2lzdHJpZXMgPSBbJ3JlZ2lzdHJ5LmFjY2Vzcy5yZWRoYXQuY29tJywnZG9ja2VyLmlvJ10KW1tyZWdpc3RyeV1dCnByZWZpeCA9ICdkb2NrZXIuaW8nCmluc2VjdXJlID0gZmFsc2UKYmxvY2tlZCA9IGZhbHNlCmxvY2F0aW9uID0gJ2RvY2tlci5pbycKW1tyZWdpc3RyeS5taXJyb3JdXQpsb2NhdGlvbiA9ICdteS1oYXJib3IuZXhhbXBsZS5jb20vcHJveHkuZG9ja2VyLmlvJwppbnNlY3VyZSA9IHRydWUK
            mode: 420
            overwrite: true
            path: /etc/containers/registries.conf

Aplicando este manifiesto se empezarán a actualizar los nodos. Al finalizar se se puede ingresar a alguno de ellos y realizar un pull de cualquier imagen de docker.io con usando crictl pull. Si dicha imagen aparece en el proyecto de Harbor significa que la configuración se ha realizado exitosamente.