> For the complete documentation index, see [llms.txt](https://docs.facephi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.facephi.com/sdks/backend-sdk/ocr/installation/kubernetes_deployment.md).

# Despliegue en Kubernetes

## 1. Introducción

En la siguiente sección explicaremos cómo instalar el servicio en un entorno Kubernetes.

## 2. Despliegue manual

### 2.1 Introducción

El servicio OCR se puede desplegar en kubernetes con **kubectl**:

```bash
kubectl apply -f manifest.yaml
```

Utilizando un fichero *manifest.yaml* similar a este:

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: facephi-ocr-service
---

apiVersion: v1
kind: Secret
metadata:
  name: ocr-license-secret
  namespace: facephi-ocr-service
stringData:
  license.lic: |-
    {
      CONFIG_DIR=<provided by facephi>
      LICENSE_TYPE=<provided by facephi>
      LICENSE_BEHAVIOUR=<provided by facephi>
      LICENSE_ID=<provided by facephi>
      LICENSE_DATA=<provided by facephi>
      LICENSE_KEY=<provided by facephi>
    } 
---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ocr
  namespace: facephi-ocr-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ocr
  template:
    metadata:
      labels:
        app: ocr
    spec:
      volumes:
        - name: license-volume
          secret:
            secretName: ocr-license-secret
            defaultMode: 420
      containers:
        - name: facephi-ocr-service
          image: facephicorp.jfrog.io/docker-pro-fphi/facephi-ocr-service:2.4.6
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 6982
          env:
            # Optional JWT protection
            # - name: FACEPHI_OCR_REST_AUTH_ENABLED
            #   value: "true"
            # - name: FACEPHI_OCR_REST_AUTH_JWT_SECRET
            #   valueFrom:
            #     secretKeyRef:
            #       name: ocr-jwt-secret
            #       key: shared-secret
            # - name: FACEPHI_OCR_REST_AUTH_ACCEPT_AUTHORIZATION_HEADER
            #   value: "true"
            # - name: FACEPHI_OCR_REST_AUTH_ACCEPT_API_KEY_HEADER
            #   value: "false"
            # - name: FACEPHI_OCR_REST_AUTH_API_KEY_HEADER_NAME
            #   value: "x-api-key"
          volumeMounts:
            - name: license-volume
              readOnly: true
              mountPath: /service/license/license.lic
              subPath: license.lic
          resources:
            limits:
              cpu: 1024m
              memory: 2Gi
            requests:
              cpu: 512m
              memory: 1Gi
---

apiVersion: v1
kind: Service
metadata:
  name: ocr-service
  namespace: facephi-ocr-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 6982
  selector:
    app: ocr
  type: ClusterIP
---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ocr-ingress
  namespace: facephi-ocr-service
  annotations:
    konghq.com/strip-path: "true"
spec:
  ingressClassName: kong
  rules:
    - host: <your own dns for this service>
      http:
        paths:
          - path: /ocr
            pathType: Prefix
            backend:
              service:
                name: ocr-service
                port:
                  number: 80
---

```

Es importante haber iniciado sesión previamente en artifactory u obtener la imagen **facephicorp.jfrog.io/docker-pro-fphi/facephi-ocr-service** y almacenarla en un repositorio de imágenes docker desde el que el clúster pueda descargarla.

### 2.2 Secret

Es necesario declarar un secret de kubernetes donde se pasa la licencia a kubernetes.

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: ocr-license-secret
  namespace: facephi-ocr-service
stringData:
  # Write here your license content. E.g:
  license.lic: |-
    {
       "key":"XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX",
       "type":"NODE_ONLINE"
    }
```

### 2.3 Deployment

Recuerde iniciar sesión en Artifactory o descargar previamente la imagen docker.

#### 2.3.1 Volúmenes

Es necesario añadir el volumen con la licencia del cliente. Por defecto, la ruta donde se almacena el fichero de licencia es `/service/license`.

Una vez creado ese secret, el deployment asociará el volumen en la ruta correspondiente con las siguientes líneas:

```yaml
...
spec:
  ...
  template:
    ...
    spec:
      volumes:
        - name: license-volume
          secret:
            secretName: ocr-license-secret
            defaultMode: 420
        ...
      containers:
        ...
        - volumeMounts:
            - name: license-volume
              readOnly: true
              mountPath: /service/license/license.lic
              subPath: license.lic
```

`spec.volumes[0].secret.secretName` busca en el namespace el secret generado previamente y lo almacena en un volumen con el nombre `license-volume`. Al montar el `license-volume` se busca el Secret asociado, y `mountPath` se establece en la ruta donde se almacena el fichero, pudiendo especificar un objeto concreto del secret con `subPath`, en este caso la clave `license`.

#### 2.3.2 Recursos

Tras numerosas pruebas de carga y estrés, se han obtenido los siguientes resultados para garantizar un tiempo de respuesta de entre 2700 y 3000 ms para documentos de identidad.

| Servicio         | CPU   | Memoria | Tiempo medio |
| ---------------- | ----- | ------- | ------------ |
| /api/v1/process/ | 512m  | 1Gi     | 5.4s         |
| /api/v1/process/ | 1024m | 2Gi     | 2.9s         |
| /api/v1/process/ | 2048m | 4Gi     | 2.8s         |

Con estas pruebas, se establece la siguiente configuración a nivel de requests y limits.

```yaml
spec:
  ...
  template:
    ...
    spec:
      ...
      containers:
        ...
          resources:
            limits:
              cpu: 2048m
              memory: 4Gi
            requests:
              cpu: 1024m
              memory: 2Gi
```

### 2.4 Service

Cuando la autenticación JWT está habilitada, `GET /api/v1/health` y `GET /api/v1/version` permanecen públicos y los endpoints protegidos requieren un JWT válido. Los ajustes de arranque de JWT no se exponen mediante `GET /api/v1/config` ni se pueden actualizar mediante `POST /api/v1/config`.

#### 2.4.1 LoadBalancer

Tenemos en cuenta que configuraremos un LoadBalancer con Kong por delante para acceder al servicio Facephi OCR Service. Tenga en cuenta que el servicio se expone en el puerto `80` y ataca al Pod en el `6982`.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: ocr-service
  namespace: facephi-ocr-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 6982
  selector:
    app: ocr
  type: ClusterIP
```

### 2.5 Ingress

Configuramos un Ingress por delante para redirigir las peticiones desde Kong al servicio dentro del Pod que hemos expuesto previamente en el puerto 80.

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ocr-ingress
  namespace: facephi-ocr-service
  annotations:
    konghq.com/strip-path: "true"
spec:
  ingressClassName: kong
  rules:
    - host: <your own dns for this service>
      http:
        paths:
          - path: /ocr
            pathType: Prefix
            backend:
              service:
                name: ocr-service
                port:
                  number: 80
```

## 3. Tipos de instancias

Si el clúster va a hacer uso de recursos HPA de kubernetes para escalar el número de pods, se recomienda tener en cuenta el número máximo de pods soportado por cada instancia. El número máximo de pods que se considera adecuado según las pruebas se refleja en la siguiente tabla.

| Tipo de instancia | CPU | Memoria | Capacidad de pods OCR |
| ----------------- | --- | ------- | --------------------- |
| c5.xlarge         | 4   | 8       | 3                     |
| c5.2xlarge        | 8   | 16      | 6                     |
| c5.4xlarge        | 16  | 32      | 12                    |

Es posible añadir más pods de OCR, pero sacrificando el tiempo de respuesta.
