> 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/docs.facephi-pt-br/sdks/backend-sdk/ocr/installation/kubernetes_deployment.md).

# Implantação em Kubernetes

## 1. Introdução

Na seção seguinte, explicaremos como instalar o serviço em um ambiente Kubernetes.

## 2. Implantação manual

### 2.1 Introdução

O serviço OCR pode ser implantado no Kubernetes com **kubectl**:

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

Usando um arquivo *manifest.yaml* semelhante 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:
            # Proteção JWT opcional
            # - 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
---

```

É importante ter feito login previamente no Artifactory ou obter a imagem **facephicorp.jfrog.io/docker-pro-fphi/facephi-ocr-service** e armazená-la em um repositório de imagens Docker a partir do qual o cluster possa baixá-la.

### 2.2 Secret

É necessário declarar um secret do Kubernetes onde a licença é passada para o Kubernetes.

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: ocr-license-secret
  namespace: facephi-ocr-service
stringData:
  # Escreva aqui o conteúdo da sua licença. Ex.:
  license.lic: |-
    {
       "key":"XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX",
       "type":"NODE_ONLINE"
    }
```

### 2.3 Deployment

Lembre-se de fazer login no Artifactory ou baixar previamente a imagem Docker.

#### 2.3.1 Volumes

É necessário adicionar o volume com a licença do cliente. Por padrão, o caminho onde o arquivo de licença é armazenado é `/service/license`.

Depois que esse secret for criado, o deployment associará o volume no caminho correspondente com as seguintes linhas:

```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 no namespace o secret gerado previamente e o armazena em um volume com o nome `license-volume`. Ao montar o volume `license-volume` o Secret associado é buscado, e `mountPath` é definido no caminho onde o arquivo é armazenado, podendo especificar um objeto específico do secret com `subPath`, neste caso a chave `license`.

#### 2.3.2 Recursos

Após numerosos testes de carga e estresse, foram obtidos os seguintes resultados para garantir um tempo de resposta entre 2700 e 3000 ms para documentos de identidade.

| Serviço          | CPU   | Memória | Tempo médio |
| ---------------- | ----- | ------- | ----------- |
| /api/v1/process/ | 512m  | 1Gi     | 5.4s        |
| /api/v1/process/ | 1024m | 2Gi     | 2.9s        |
| /api/v1/process/ | 2048m | 4Gi     | 2.8s        |

Com estes testes, estabelece-se a seguinte configuração em nível de requests e limits.

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

### 2.4 Service

Quando a autenticação JWT está habilitada, `GET /api/v1/health` e `GET /api/v1/version` permanecem públicos e os endpoints protegidos exigem um JWT válido. As configurações de inicialização do JWT não são expostas por meio de `GET /api/v1/config` nem podem ser atualizadas por meio de `POST /api/v1/config`.

#### 2.4.1 LoadBalancer

Levamos em conta que configuraremos um LoadBalancer com Kong à frente para acessar o serviço Facephi OCR Service. Observe que o serviço é exposto na porta `80` e ataca o Pod na `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 um Ingress à frente para redirecionar as requisições do Kong para o serviço dentro do Pod que expusemos anteriormente na porta 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 instâncias

Se o cluster for usar recursos HPA do Kubernetes para escalar o número de pods, recomenda-se levar em conta o número máximo de pods suportado por cada instância. O número máximo de pods considerado adequado segundo os testes é mostrado na tabela a seguir.

| Tipo de instância | CPU | Memória | Capacidade de pods OCR |
| ----------------- | --- | ------- | ---------------------- |
| c5.xlarge         | 4   | 8       | 3                      |
| c5.2xlarge        | 8   | 16      | 6                      |
| c5.4xlarge        | 16  | 32      | 12                     |

É possível adicionar mais pods de OCR, mas sacrificando o tempo de resposta.
