# 7.6 Definindo o chart utilizado para execução local e na nuvem

Vamos definir um *chart* para executarmos a aplicação, inicialmente localmente, e posteriormente na nuvem da Oracle. Para isso, vamos criar o diretório *chart* na raíz do nosso projeto, e acrescentar nele `Chart.yaml`, contendo o seguinte conteúdo:

```yaml
apiVersion: v2
name: loja-virtual
description: Chart para implantação da loja virtual no Kubernetes

type: application
version: 1.0.0
appVersion: 1.0

```

Nele, definimos o nome do nosso *chart* (`name: loja-virtual`) e sua versão (`version: 1.0.0`), elaboramos uma descrição (`description: Chart para implantação da loja virtual no Kubernetes`), definimos seu tipo como aplicação (`type: application`) e informamos a versão da aplicação que ele instala (`appVersion: 1.0`).

Uma boa prática consiste em definir um arquivo chamado `.helmignore` dentro do diretório chart. Esse arquivo informa ao helm padrões de arquivos que devem ser ignorados. Cada linha desse arquivo deve conter um único padrão, tal como no arquivo `.gitignore` dos repositórios git. Vamos usar o seguinte arquivo:

```gitignore
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

```

Nele podemos ver que diretórios e arquivos comuns às diversas IDEs existentes são ignorados, como por exemplo os diretório `.vscode`, referente à IDE Visual Studio Code, e o diretório `.idea`, referente à IDE IntelliJ, bem como arquivos comuns de backup (aqueles com extensão .bak e .swp) e arquivos de sistemas de controle de versão como o próprio `.gitignore`.

Na sequencia, é necessário criar o subdiretório `templates` dentro do diretório `chart`, que como visto anteriormente, deve conter os *templates* utilizados para gerar os arquivos de recursos do Kubernetes.

Vamos inicialmente criar nosso primeiro deployment, no arquivo `loja-deployment.yaml`. Ele terá o seguinte formato:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: loja
  labels:
    app.kubernetes.io/name: loja-virtual
    app.kubernetes.io/instance: loja-virtual
    app.kubernetes.io/version: '1.0'
    app.kubernetes.io/component: webserver
    app.kubernetes.io/part-of: loja-virtual
    app.kubernetes.io/managed-by: Helm 
    app.kubernetes.io/created-by: curso-devops
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: loja-virtual
      app.kubernetes.io/instance: loja-virtual
      app.kubernetes.io/version: '1.0'
      app.kubernetes.io/component: webserver
      app.kubernetes.io/part-of: loja-virtual
  template:
    metadata:
      labels:
        app.kubernetes.io/name: loja-virtual
        app.kubernetes.io/instance: loja-virtual
        app.kubernetes.io/version: '1.0'
        app.kubernetes.io/component: webserver
        app.kubernetes.io/part-of: loja-virtual
        app.kubernetes.io/managed-by: Helm 
        app.kubernetes.io/created-by: curso-devops
    spec:
      imagePullSecrets:
      containers:
      - name: loja
        image: registry-local:5005/loja-virtual
        imagePullPolicy: IfNotPresent
        ports:
          - name: http
            containerPort: 8080
            protocol: TCP   


```

É interessante observar que na linha 4 definimos o nome do deployment (loja-virtual), e entre as linhas 6 e 12 definimos os labels dele, usando os labels recomendados pela documentação do Kubernetes (<https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/>). Os labels, como dito anteriormente, ajudam a classificar e agrupar os recursos.

Os mesmos labels são utilizados nas linhas 25-31, e são aplicados nesse caso aos containers criados pelo *deployment*. No exemplo temos apenas um container, com o nome loja (linha 35), que usa a imagem registry-local:5005/loja-virtual (linha 36) e somente tenta baixar a imagem do registry se ela não estiver presente ainda no cluster Kubernetes (IfNotPresent, na linha 37).

Nosso container permite a conexão externa ao POD por meio da porta 8080 (linha 40), por meio do protocolo TCP (linha 41), e a essa porta foi atribuído o nome arbitrário de http (linha 39). Para o deployment saber quais são os *containers* que são gerenciados por ele, definimos nas linhas 17-21 os labels que serão utilizados para selecionar os respectivos *containers*. No nosso caso, estamos usando apenas 4 labels, em vez das 6 definidas para os containers. Poderíamos usar todas as 6, ou qualquer número entre 1 e 5. O importante nesse caso é selecionar as labels que identifiquem univocamente os *containers* que devem ser gerenciados pelo *deployment*.

Com a definição desse deployment, podemos testar o *deploy* no *cluster* Kubernetes usando o Helm. Mas primeiro, é necessário realizar o *build* da imagem da aplicação, e seu envio para o *registry* (`registry-local:5005`). Esses dois passos podem ser realizados pelo seguinte comando, o qual deve ser executado na raíz do projeto:

```
docker build -t registry-local:5005/loja-virtual-base . && \
docker push registry-local:5005/loja-virtual-base
```

Na linha 1 usamos o comando `build` do docker para criar a *tag* `registry-local:5005/loja-virtual`, por meio do parâmetro `-t`, e informamos que o contexto utilizado será o diretório atual (`.`).

Já na linha 2, o comando `push` é utilizado para enviar a imagem gerada para o *registry*. Com isso, nossa imagem poderá ser baixada pelo Kubernetes para gerar o *container* que definimos no *deployment*.

O arquivo Dockerfile presente na raiz é utilizado para formar a imagem base da nossa aplicação. Ela basicamente contém um servidor Tomcat, na versão 7.0, e o jdk8. A imagem que será efetivamente utilizada no cluster é gerada com base nela, e contém também a aplicação da loja virtual. Essa imagem é gerada pelo Jib.

Agora, é possível usar o Helm para realizar o deploy no kubernetes com o seguinte comando:

```
helm install chart --create-namespace -n loja-virtual --name-template loja-virtual
```

O comando `install` é responsável por instalar um *chart* no cluster Kubernetes. Deve ser informado o diretório no qual está localizado o *chart*. No nosso exemplo, o diretório tem o nome `chart`.

Vamos instalar o *chart* em um *namespace* chamado loja-virtual, definido pelo parâmetro `-n`. Esse *namespace* ainda não existe, por isso, utilizamos o comando `--create-namespace` que cria o *namespace* caso ele ainda não exista. O Helm criará uma nova *release* no Kubernetes, cujo nome `loja-virtual`, é definido por meio do parâmetro `--name-template`.

A saida desse comando será semelhante a essa:

```
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/bruno/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/bruno/.kube/config
NAME: loja-virtual
LAST DEPLOYED: Mon Dec 13 10:49:43 2021
NAMESPACE: loja-virtual
STATUS: deployed
REVISION: 1
TEST SUITE: None
```

Podemos observar que o status é *deployed*, indicando que o foi realizado o *deploy* no *cluster* Kubernetes. Isso nos indica que nosso *deployment* está correto. Podemos verificar o *log* do container com o comando a seguir:

```
kubectl logs deployment/loja -c loja -n loja-virtual 
```

O kubectl, como mencionado anteriormente é uma aplicação de linha de comando que permite interagirmos com o cluster Kubernetes. Utilizamos o comando *logs* para informar que queremos obter os *logs* do *deployment* loja (`deployment/loja`), de maneira mais específica do container loja (`-c loja`), que está localizado no namespace loja-virtual (`-n loja-virtual`).

Para usar nossa aplicação no navegador, é necessário mapear a porta do contêiner em uma porta local da nossa máquina. Esse procedimento é realizado utilizando o comando a seguir:

```
kubectl port-forward deployment/loja 8080:8090
```

Com esse comando, estamos mapeando a porta 8080 do contêiner na porta 8090 da nosso máquina. Desse modo, no navegador, conseguimos acessar nossa aplicação com o endereço <http://localhost:8090>.

//TODO: Explicar que falta o banco de dados

Como podemos observar, nosso arquivo de template ainda está estático, assim como os arquivos do Kubernetes que criamos na seção 7.3. Vamos ver agora 3 modos de deixar o arquivo mais dinâmico, com a utilização de valores definidos conforme necessidade.

### Valores definidos no arquivo values.yaml

Podemos melhorar isso com a criação do arquivo `values.yaml` dentro do diretório `chart`. Esse arquivo, como mencionamos, será utilizado para definir chaves com valores-padrão que serão utilizadas e substituídas nos *templates*.

Inicialmente ele será assim:

```yaml
replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: ""
imagePullPolicy: IfNotPresent
imagePullSecrets: 
  - name: gitlab-registry
nameOverride: ""
fullnameOverride: ""

serviceAccount:
  # Specifies whether a service account should be created
  create: false
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: ClusterIP
  port: 8080

ingress:
  enabled: true
  className: ""
  annotations: 
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
  hosts:
    - host: loja-disciplina-devops.tk
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

nodeSelector: {}

tolerations: []

affinity: {}
```

Uma vez definido esses valores, podemos utilizá-los nos arquivos de *templates*. A utilização será realizada usando a seguinte sintaxe:

```
{{ .Values.CHAVE_DESEJADA }}
```

Por exemplo, podemos definir a porta utilizada no contêiner do seguinte modo:

```
containerPort: {{ .Values.service.port }}
```

Essa notação indica que deve ser utilizado o valor definido no arquivo `values.yaml` (.Values) por meio da sub-chave `port`, definida aninhada na chave `service` (`service.port`*)*. Olhando novamente o arquivo `values.yaml` vemos que esse valor é 8080:

```
service:
  type: ClusterIP
  port: 8080
```

Portanto, o resultado do *template* será `containerPort: 8080`, que é exatamente o valor que queremos.

### Valores definidos no arquivo charts.yaml

Outra substituição que podemos fazer, é utilizar informações do arquivo *Chart.yaml*. De modo análogo, podemos definir, por exemplo, o nome do continer do seguinte modo:

```yaml
containers:
    - name: {{ .Chart.Name }}
```

Nesse caso, dada a definição a seguir presente no arquivo Chart.yaml:

```yaml
name: loja-virtual
```

Teremos o seguinte arquivo resultante da substituição de valores no arquivo de template:

```yaml
containers:
    name: loja-virtual
```

### Valores definidos com *named templates* no arquivo \_helpers.tpl

Um recurso interessante do Helm é a utilização de [*`named templates`*](https://helm.sh/docs/chart_template_guide/named_templates/). Named templates são templates definidos em um arquivo, aos quais é atribuído um nome, e que podem ser utilizados em outros templates, de modo semelhante a uma função ou método em programação. É um modo de reutilizar código.

Podemos definir *named templates* no arquivo `_helpers.tpl`, dentro do diretório `templates`. A seguir, temos a definição do *named template* `chart.name`:

```soy
{{/*
Expand the name of the chart.
*/}}
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
```

No arquivo `_helpers.tpl`, usamos sempre blocos que iniciam com `{{` e são finalizados com `}}`. Podemos definir comentários entre os símbolos `/*` e `*/`.

A definição de um *named template* pode ser realizada com a palavra define, tal como na linha 4 do código anterior, no qual definimos o *named template* `chart.name`. Podem ser usadas funções tal como na linha 5, onde são usadas as funções `default`, `trunc` e `trimSuffix`. As funções disponíveis no Helm podem ser conferidas na [documentação](https://helm.sh/docs/chart_template_guide/function_list/).

Na linha 5, estamos definindo que vamos utilizar o valor definido pela chave nameOverride, definida no arquivo `values.yaml`. caso ela exista, caso contrário, o valor padrão (default) será o valor da chave `Name`, presente no arquivo `chart.yaml`. A seguir, o valor será utilizado como parâmetro para a função `trunc`, por meio da utilização do *pipe* (`|`).

A função `trunc` retorna somente os N primeiros caracteres de um texto. No exemplo anterior, o valor de `N` foi definido como `63`. A seguir, o valor obtido é passado como parâmetro para a função `trimSuffix`, que remove o sufixo informado como parâmetro, no caso `-`. O valor resultante, pode ser utilizado nos arquivos de *templates* com a palavra **include**, do seguinte modo:

```yaml
name: {{ include "chart.name" . }}
```

O nosso arquivo \_helpers.tpl completo ficará assim:

```soy
{{/*
Expand the name of the chart.
*/}}
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "chart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "chart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "chart.labels" -}}
helm.sh/chart: {{ include "chart.chart" . }}
{{ include "chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "chart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "chart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}


{{- define "db.labels" -}}
helm.sh/chart: {{ include "chart.chart" . }}
{{ include "db.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}


{{/*
Selector labels
*/}}
{{- define "db.selectorLabels" -}}
app.kubernetes.io/name: {{ .Values.db.name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

```

Podemos testar o template do deployment, com a substituição de variáveis com o seguinte comando:

```
helm upgrade loja-virtual ./chart -n loja-virtual
```

Esse comando vai atualizar a versão do chart de acordo com as alterações que realizamos. Para acesarmos pelo navegador teremos que repetir o comando para mapear a porta do contêiner em uma porta da nossa máquina:

```
kubectl port-forward deployment/loja 8080:8090
```

O próximo passo agora é definir o template para criar um serviço para nossa aplicação. Para isso, será criado o arquivo `service.yaml` dentro do diretório `templates`. Ele ficará do seguinte modo:

```
apiVersion: v1
kind: Service
metadata:
  name: {{ include "chart.fullname" . }}
  labels:
    {{- include "chart.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "chart.selectorLabels" . | nindent 4 }}
```

Note que assim como fizemos no caso do deployment, estamos também usando valores provenientes do arquivo values.yaml, tal como em:

```
type: {{ .Values.service.type }}
```

Também são utilizados *named templates* como no caso a seguir:

```
selector:
    {{- include "chart.selectorLabels" . | nindent 4 }}
```

Observe que podemos utilizar o named template como entrada para uma função. No exemplo apresentado, o named template chart.selectorLabels é usado como parâmetro para a função `nident`, que realiza a identação colocando `N` número de espaços antes do valor do parâmetro. No nosso caso, são 4 espaços.

Podemos atualizar novamente nossa release, com o comando já apresentado, e na sequencia realizar o mapeamento agora de uma porta do serviço para uma porta da nossa máquina:

```
kubectl port-forward service/loja-virtual 8080:8090
```

Observe que o resultado prático foi o mesmo de fazer o mapeamento para o deployment, porém, se tivéssemos mais de uma réplica do *deployment*, ao acessarmos o endereço pelo navegador, poderíamos a cada momento ser atendidos por uma instância diferente da aplicação.

Essa é uma das vantagens de utilizar *services*, conforme explicado anteriormente.

Ainda falta uma peça nesse quebra-cabeça para que nossa aplicação funcione adequadamente, que é o banco de dados.

Como o banco de dados é uma dependência de nossa aplicação, podemos criar um subchart para implementar o banco. A vantagem de fazer isso é ter uma estrutura de arquivos mais organizada. Para criar o subchart do banco de dados, vamos criar o diretório charts/mysql dentro do diretório chart, e dentro dele vamos ter uma estrutura semelhante à do chart:

```
chart
├── charts
│   └── mysql
│       ├── Chart.yaml
│       ├── templates
│       │   ├── db-deployment.yaml
│       │   ├── db-service.yaml
│       │   └── _helpers.tpl
│       └── values.yam
```

O arquivo Chart.yaml ficará assim:

```yaml
apiVersion: v2
name: mysql
description: Banco de dados da aplicação
type: application
version: 0.1.0
appVersion: "1.16.0"
```

Já o arquivo values.yaml será desse modo:

```yaml
db:
  name: db
  labels:
    helm.sh/chart: chart-0.1.0
    app.kubernetes.io/name: chart
    app.kubernetes.io/instance: RELEASE-NAME
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm 
    test: true
  selectorLabels:
    test: true
  
  replicaCount: 1

  image: aurimrv/mysql-server-img
    # repository: nginx
    # pullPolicy: IfNotPresent
    # # Overrides the image tag whose default is the chart appVersion.
    # tag: ""
  imagePullPolicy: IfNotPresent
  imagePullSecrets: []
  nameOverride: ""
  fullnameOverride: ""

  serviceAccount:
    # Specifies whether a service account should be created
    create: "false"
    # Annotations to add to the service account
    annotations: {}
    # The name of the service account to use.
    # If not set and create is true, a name is generated using the fullname template
    name: ""

  podAnnotations: {}

  podSecurityContext: {}
    # fsGroup: 2000

  securityContext: {}
    # capabilities:
    #   drop:
    #   - ALL
    # readOnlyRootFilesystem: true
    # runAsNonRoot: true
    # runAsUser: 1000

  service:
    type: ClusterIP
    port: 3306

  
  resources:
    # We usually recommend not to specify default resources and to leave this as a conscious
    # choice for the user. This also increases chances charts run on environments with little
    # resources, such as Minikube. If you do want to specify resources, uncomment the following
    # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
    limits:
      cpu: 400m
      memory: 256Mi
    requests:
      cpu: 100m
      memory: 128Mi

  autoscaling:
    enabled: false
    minReplicas: 1
    maxReplicas: 100
    targetCPUUtilizationPercentage: 80
    # targetMemoryUtilizationPercentage: 80

  nodeSelector: {}


  tolerations: []

  affinity: {}
```

O arquivo \_helpers.tpl terá o seguinte conteúdo:

```tt2
{{- define "db.labels" -}}
helm.sh/chart: {{ include "chart.chart" . }}
{{ include "db.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}


{{/*
Selector labels
*/}}
{{- define "db.selectorLabels" -}}
app.kubernetes.io/name: {{ .Values.db.name }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
```

E os arquivos db-deployment.yaml e db-service.yaml ficarão respectivamente assim:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.db.name }}
  labels:
{{- include "db.labels" . | nindent 4 }}
spec:
{{- if not .Values.db.autoscaling.enabled }}
  replicas: {{ .Values.db.replicaCount }}
{{- end }}
  selector:
    matchLabels:
{{- include "db.selectorLabels" . | nindent 6}}
  template:
    metadata:
      {{- with .Values.db.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
{{- include "db.selectorLabels" . | nindent 8}}
    spec:
      {{- with .Values.db.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      securityContext:
        {{- toYaml .Values.db.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Values.db.name }}
          securityContext:
            {{- toYaml .Values.db.securityContext | nindent 12 }}
          image: "{{ .Values.db.image }}"
          imagePullPolicy: {{ .Values.db.imagePullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.db.service.port}}
              protocol: TCP
          # livenessProbe:
          #   httpGet:
          #     path: /
          #     port: http
          # readinessProbe:
          #   httpGet:
          #     path: /
          #     port: http
          resources:
            {{- toYaml .Values.db.resources | nindent 12 }}
      {{- with .Values.db.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.db.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.db.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
```

```yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.db.name }}
  labels:
{{- include "db.labels" . | nindent 4 }}
spec:
  type: {{ .Values.db.service.type }}
  ports:
    - port: {{ .Values.db.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "db.selectorLabels" . | nindent 4 }}
```

Observe que nossa aplicação acessará o banco de dados por meio do nome do serviço, segundo nosso exemplo, esse nome será `db`. E a porta será a `3306`.
