Desenvolvendo apps
Desenvolque uma configuração para implementar sua carga de trabalho do app para IBM Cloud® Kubernetes Service. Como o Kubernetes é uma plataforma de orquestração de contêiner extensível que não exige um idioma ou um app específico, é possível executar várias cargas de trabalho, como apps stateless, stateful e de processamento de dados que são gravados no idioma de sua escolha.
Especificando os requisitos de app em seu arquivo YAML
Em Kubernetes, você descreve seu app em um arquivo YAML que declara a configuração do objeto do Kubernetes. O servidor de API do Kubernetes, então, processa o arquivo YAML e armazena a configuração e o estado necessário do objeto no armazenamento de dados etcd. O planejador do Kubernetes planeja suas cargas de trabalho nos nós do trabalhador dentro de seu cluster, levando em consideração a especificação em seu arquivo YAML, quaisquer políticas de cluster que o administrador configura e a capacidade disponível do cluster.
Revise uma cópia do arquivo YAML completo. Em seguida, revise as seções a seguir para entender como é possível aprimorar a implementação do app.
Quer mais informações sobre como os objetos de Kubernetes trabalham juntos para a sua implantação? Confira Understanding Kubernetes objects para apps.
Metadados de implementação básica
Use a versão apropriada da API para o tipo de objeto do Kubernetes que você implementa. A versão da API determina os recursos suportados para o objeto do Kubernetes que estão
disponíveis para você. O nome fornecido nos metadados é o nome do objeto, não seu rótulo. Você usa o nome ao interagir com o objeto, como kubectl get deployment <name>.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
Conjunto de réplicas
Para aumentar a disponibilidade de seu app, é possível especificar um conjunto de réplicas em sua implementação. Em um conjunto de réplicas, são definidas quantas instâncias de seu app você deseja implementar. Os conjuntos de réplicas são gerenciados e monitorados por sua implementação do Kubernetes. Se uma instância do app fica inativa, o Kubernetes acelera automaticamente uma nova instância de seu app para manter o número especificado de instâncias do app.
spec:
replicas: 3
Rótulos
Com rótulos, é possível marcar diferentes tipos de recursos em seu cluster com o mesmo par key: value. Em seguida, é possível especificar o seletor para
corresponder ao rótulo para que seja possível construir sobre esses outros recursos. Se você planeja expor seu app publicamente, deve-se usar um rótulo que corresponda ao seletor especificado no serviço. No exemplo, a especificação de implementação
usa o template que corresponde ao rótulo app: wasliberty.
É possível recuperar objetos que são rotulados em seu cluster, como para ver os componentes staging ou production. Por exemplo, liste todos os recursos com um rótulo env: production em todos os namespaces
no cluster. Nota: você precisa de acesso a todos os namespaces para executar esse comando.
kubectl get all -l env=production --all-namespaces
- Para obter mais informações sobre rótulos, consulte a Documentação do Kubernetes.
- Aplicar rótulos aos nós do trabalhador.
- Para obter um exemplo mais detalhado, veja Implementando apps em nós do trabalhador específicos usando rótulos.
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
Afinidade
Especifique a afinidade (colocação conjunta) quando desejar ter mais controle sobre em quais nós de trabalho os pods serão agendados. A afinidade afeta os pods somente no tempo de planejamento. Por exemplo, para difundir a implementação em
nós do trabalhador em vez de permitir que os pods sejam planejados no mesmo nó, use a opção podAntiAffinity com seus clusters padrão. É possível definir dois tipos de antiafinidade do pod: preferencial ou necessário.
Para obter mais informações, consulte a documentação do Kubernetes em Designando pods a nós.
- Antiafinidade necessária
- É possível implementar apenas o número de réplicas para as quais você tem nós do trabalhador. Por exemplo, se você tiver três nós do trabalhador em seu cluster, mas definir cinco réplicas em seu arquivo YAML, somente três réplicas serão implementadas. Cada réplica mora em um nó de trabalhador diferente. As duas réplicas restantes continuarão pendentes. Se você incluir outro nó do trabalhador no cluster, uma das réplicas restantes será implementada no novo nó do trabalhador automaticamente. Se um nó do trabalhador falhar, o pod não reagendará porque a política de afinidade é necessária. Para obter um YAML de exemplo com antiafinidade necessária, consulte App Liberty com antiafinidade de pod necessária.
- Antiafinidade preferencial
- É possível implementat seus pods em nós com capacidade disponível, o que proporciona mais flexibilidade para sua carga de trabalho. Quando possível, os pods são planejados em diferentes nós do trabalhador. Por exemplo, se você tiver três nós do trabalhador com capacidade suficiente em seu cluster, ele poderá planejar os cinco pods de réplica nos nós. No entanto, se você adicionar mais dois nós de trabalho ao seu cluster, a regra de afinidade não força os dois pods adicionais que estão em execução nos nós existentes a serem reprogramados para o nó disponível.
- Afinidade do nó do trabalhador
- É possível configurar sua implementação para executar apenas em determinados nós do trabalhador, como o bare metal. Para obter mais informações, veja Implementando apps em nós do trabalhador específicos usando rótulos.
Exemplo de antiafinidade preferencial
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
Imagem do contêiner
Especifique a imagem que você deseja usar para seus contêineres, o local da imagem e a política de extração de imagem. Se você não especificar uma tag de imagem, por padrão, a imagem identificada como latest será extraída.
Evite usar a tag mais recente para cargas de trabalho de produção. Você pode não ter testado sua carga de trabalho com a imagem mais recente se estiver usando um repositório público ou compartilhado, como o Docker Hub ou o IBM Cloud Container Registry.
Por exemplo, para listar as tags de imagens públicas da IBM:
- Alterne para a região de registro global.
ibmcloud cr region-set global - Liste as imagens da IBM.
ibmcloud cr images --include-ibm
O imagePullPolicy padrão é configurado como IfNotPresent, que fará pull da imagem somente se ela não existir localmente. Se desejar que a imagem seja puxada toda vez que o contêiner for iniciado, especifique o imagePullPolicy: Always.
containers:
- name: wasliberty
image: icr.io/ibm/liberty:webProfile8
imagePullPolicy: Always
Porta para o serviço do app
Selecione uma porta de contêiner na qual abrir os serviços do app. Para ver qual porta precisa ser aberta, consulte suas especificações de app ou o Dockerfile. A porta é acessível por meio da rede privada, mas não de uma conexão de rede pública.
Para expor o app publicamente, deve-se criar um serviço NodePort, balanceador de carga ou Ingress. Você usa esse mesmo número da porta ao criar um objeto Service.
A porta 25 está bloqueada para todos os serviços no IBM Cloud.
ports:
- containerPort: 9080
Solicitações de recurso e limites
Os administradores de cluster certificam-se de que as equipes que compartilham um cluster não obtenham mais do que seu compartilhamento justo de recursos de cálculo (memória e CPU) criando um objeto ResourceQuota para cada namespace do Kubernetes no cluster. Se o administrador de cluster configurar uma cota de recurso de cálculo, cada contêiner dentro do modelo de implementação
deverá especificar solicitações de recurso e limites para memória e CPU, caso contrário, a criação do pod falhará.
- Verifique se uma cota de recurso está configurada para um namespace.
kubectl get quota --namespace=<namespace> - Veja quais são os limites de cota.
kubectl describe quota <quota_name> --namespace=<namespace>
Mesmo que nenhuma cota de recurso seja configurada, é possível incluir solicitações de recurso e limites em sua implementação para melhorar o gerenciamento de recursos do nó do trabalhador.
Se um contêiner exceder seu limite, o contêiner poderá ser reiniciado ou falhar. Se um contêiner exceder uma solicitação, seu pod poderá ser despejado se o nó do trabalhador ficar sem esse recurso que foi excedido. Para obter mais informações sobre resolução de problemas, consulte Os pods falham repetidamente ao reiniciar ou são removidos inesperadamente.
- Solicitação
- A quantidade mínima do recurso reservado pelo planejador para utilização do contêiner. Se a quantia for igual ao limite, a solicitação será garantida. Se a quantia for menor do que o limite, a solicitação ainda será garantida, mas o planejador poderá usar a diferença entre a solicitação e o limite para preencher os recursos de outros contêineres.
- Limite
- A quantidade máxima do recurso que o contêiner pode consumir. Se a quantia total de recursos que for usada nos contêineres exceder a quantia disponível no nó do trabalhador, os contêineres poderão ser despejados para liberar espaço. Para evitar o despejo, configure a solicitação de recurso igual ao limite do contêiner. Se nenhum limite for especificado, o padrão será a capacidade do nó do trabalhador.
Para obter mais informações, consulte a Documentação do Kubernetes.
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
Análises de vivacidade e prontidão
Por padrão, o Kubernetes envia o tráfego para seus pods de app após todos os contêineres no pod serem iniciados e reinicia os contêineres quando eles travam. No entanto, é possível configurar verificações de funcionamento para melhorar a robustez do roteamento de tráfego de serviço.
Por exemplo, seu app pode ter um atraso de inicialização. Os processos de app podem ser iniciados antes que o app inteiro esteja completamente pronto, o que pode afetar as respostas especialmente ao escalar para cima em muitas instâncias. Com as verificações de funcionamento, é possível permitir que seu sistema possa saber se o seu app está em execução e pronto para receber solicitações. Configurando essas análises, é possível também ajudar a evitar o tempo de inatividade ao executar uma atualização contínua de seu app. É possível configurar dois tipos de verificações de funcionamento: análises de vivacidade e de prontidão.
- Análise de vivacidade
- Configure uma análise de vivacidade para verificar se o contêiner está em execução. Se a análise falha, o contêiner é reiniciado. Se o contêiner não especifica uma análise de vivacidade, a análise é bem-sucedida porque presume que o contêiner está ativo quando o contêiner está em um status Em execução.
- Análise de prontidão
- Configure uma análise de prontidão para verificar se o contêiner está pronto para receber solicitações e tráfego externo. Se a análise falhar, o endereço IP do pod será removido como um endereço IP utilizável para serviços que correspondem ao pod, mas o contêiner não será reiniciado. Configurar uma sonda de prontidão com um atraso inicial é especialmente importante se o seu aplicativo demorar um pouco para iniciar. Antes do atraso inicial, a análise não é iniciada, fornecendo ao seu contêiner um tempo para ficar ativo. Se o contêiner não fornece uma análise de prontidão, a análise é bem-sucedida porque presume que o contêiner está ativo quando o contêiner está em um status Em execução.
É possível configurar as análises como comandos, solicitações de HTTP ou soquetes TCP. O exemplo usa solicitações de HTTP. Dê ao sensor de vivacidade mais tempo do que à análise de prontidão. Para obter mais informações, consulte a Documentação do Kubernetes.
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
Orçamento de interrupção de pod
Para aumentar a disponibilidade do app, é possível controlar como o app reage a interrupções com base no tipo de disponibilidade
desejado com um objeto PodDisruptionBudget.
Um orçamento de interrupção de pod pode ajudá-lo a planejar como o seu app se comporta durante interrupções voluntárias, assim como quando você inicia uma reinicialização direta atualizando a implementação do app, ou interrupções involuntárias,
como um pânico kernel.
minAvailable : É possível especificar o número ou porcentagem de pods que ainda precisam estar disponíveis após ocorrer uma interrupção.
maxUnavailable- É possível especificar o número ou porcentagem de pods indisponíveis após uma interrupção. O exemplo usa
maxUnavailable: 1. selector- Digite o rótulo para selecionar o conjunto de pods ao qual o
PodDisruptionBudgetse aplica. Note que se você usou este mesmo rótulo em outras implementações de pod, o pod se aplica a esses também.
Para obter mais informações, consulte a Documentação do Kubernetes.
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
Expondo o serviço de app
É possível criar um serviço que exponha seu app. Na seção spec, certifique-se de corresponder os valores de port e de rótulo aos que você usou na implementação. O serviço expõe objetos que correspondem ao rótulo,
tal como app: wasliberty no exemplo a seguir.
- Por padrão, um serviço usa
ClusterIP, o que torna o serviço acessível somente dentro do cluster, mas não fora do cluster. - É possível criar um serviço NodePort, balanceador de carga ou Ingress para expor o app publicamente. Esses serviços têm dois IPs, um externo e um interno. Quando o tráfego é recebido no IP externo, ele é encaminhado para o IP do cluster interno. Em seguida, por meio do IP do cluster interno, o tráfego é roteado para o IP do contêiner do app.
- O exemplo usa
NodePortpara expor o serviço fora do cluster. Para obter mais informações sobre como configurar o acesso externo, consulte Escolhendo um serviço NodePort, Ingress ou de balanceador de carga.
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
Se você tiver um requisito para implementar os pods hostNetwork para atender em portas específicas ou para usar um hostPort para expor os pods de app em uma porta específica no nó do trabalhador, use uma porta no
intervalo 11000-11200. O IBM Cloud Kubernetes Service designa o intervalo de portas 11000-11200 em nós do trabalhador para esse propósito para evitar conflitos com portas locais e outras usadas pelo IBM Cloud Kubernetes
Service. Como os pods hostNetwork e hostPorts referem-se a um endereço IP do nó do trabalhador específico, os pods são limitados para serem executados somente nesse nó do trabalhador. Se houver algum imprevisto,
como o nó do trabalhador ser removido ou ficar sem recursos, o pod não poderá ser reprogramado. Para expor uma porta do pod no nó do trabalhador, considere usar um serviçoNodePort no lugar. Para obter mais informações, consulte a Documentação de melhores práticas do Kubernetes.
Configmaps para variáveis de ambiente de contêiner
Os configmaps fornecem informações de configuração não sensíveis para suas cargas de trabalho de implementação.
O exemplo a seguir mostra como é possível referenciar valores de seu configmap como variáveis de ambiente na seção de especificação de contêiner de seu YAML de implementação. Referenciando valores de seu configmap, é possível desacoplar essas informações de configuração de sua implementação para manter seu app conteinerizado móvel.
- Ajude-me a decidir se deve ser usado um objeto do Kubernetes
ConfigMapouSecretpara variáveis. - Para saber outras maneiras de usar configmaps, consulte a Documentação do Kubernetes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
...
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
Segredos para variáveis de ambiente de contêiner
Os segredos fornecem informações confidenciais de configuração, como senhas para suas cargas de trabalho de implementação.
O exemplo a seguir mostra como é possível referenciar valores de seu segredo como variáveis de ambiente na seção de especificação de contêiner de seu YAML de implementação. Também é possível montar o segredo como um volume. Referenciando valores de seu segredo, é possível desacoplar essas informações de configuração de sua implementação para manter seu app conteinerizado móvel.
- Ajude-me a decidir se deve ser usado um objeto do Kubernetes
ConfigMapouSecretpara variáveis. - Para criar um segredo, consulte a Documentação do Kubernetes.
Para gerenciamento centralizado de todos os seus segredos entre os clusters e injeção no tempo de execução do aplicativo, tente IBM Cloud Secrets Manager.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
env:
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
...
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
Volumes Persistentes para Armazenamento de Contêiner
Os volumes persistentes (PVs) fazem interface com o armazenamento físico para fornecer armazenamento de dados persistentes para as cargas de trabalho do contêiner.
O exemplo a seguir mostra como é possível incluir armazenamento persistente em seu app. Para provisionar o armazenamento persistente, crie uma solicitação de volume persistente (PVC) para descrever o tipo e o tamanho do armazenamento de arquivo
que você deseja ter. Após criar o PVC, o volume persistente e o armazenamento físico são criados automaticamente por meio do provisionamento dinâmico. Referenciando o PVC em seu YAML de implementação, o armazenamento é montado automaticamente
em seu pod de app. Quando o contêiner em seu pod grava dados no diretório de caminho de montagem /test, os dados são armazenados na instância de armazenamento de arquivo NFS. Para obter opções sobre outros tipos de armazenamento
que é possível provisionar, veja Planejando o armazenamento persistente altamente disponível.
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
template:
...
spec:
...
containers:
- name: wasliberty
...
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi
YAML de implementação de exemplo completo
O exemplo a seguir é uma cópia do YAML de implementação que foi discutido seção a seção anteriormente. Também é possível fazer download do YAML por meio do GitHub.
Para aplicar o YAML,
kubectl apply -f file.yaml [-n <namespace>]
Exemplo de YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: wasliberty
spec:
replicas: 3
selector:
matchLabels:
app: wasliberty
template:
metadata:
labels:
app: wasliberty
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wasliberty
topologyKey: kubernetes.io/hostname
containers:
- name: wasliberty
image: icr.io/ibm/liberty:latest
env:
- name: VERSION
valueFrom:
configMapKeyRef:
name: wasliberty
key: VERSION
- name: LANGUAGE
valueFrom:
configMapKeyRef:
name: wasliberty
key: LANGUAGE
- name: username
valueFrom:
secretKeyRef:
name: wasliberty
key: username
- name: password
valueFrom:
secretKeyRef:
name: wasliberty
key: password
ports:
- containerPort: 9080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1024Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 300
periodSeconds: 15
readinessProbe:
httpGet:
path: /
port: 9080
initialDelaySeconds: 45
periodSeconds: 5
volumeMounts:
- name: pvmount
mountPath: /test
volumes:
- name: pvmount
persistentVolumeClaim:
claimName: wasliberty
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: wasliberty
spec:
maxUnavailable: 1
selector:
matchLabels:
app: wasliberty
---
apiVersion: v1
kind: Service
metadata:
name: wasliberty
labels:
app: wasliberty
spec:
ports:
- port: 9080
selector:
app: wasliberty
type: NodePort
---
apiVersion: v1
kind: ConfigMap
metadata:
name: wasliberty
labels:
app: wasliberty
data:
VERSION: "1.0"
LANGUAGE: en
---
apiVersion: v1
kind: Secret
metadata:
name: wasliberty
labels:
app: wasliberty
type: Opaque
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wasliberty
annotations:
volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
labels:
billingType: "hourly"
app: wasliberty
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 24Gi