Post

Containerização de Servidores DNS BIND

Containerização de Servidores DNS BIND

Este é um tutorial complementar à nossa série sobre implementação e configuração do DNS BIND no Oracle Linux 9. Nos artigos anteriores, abordamos desde os conceitos fundamentais do DNS até a automação com Ansible e GitLab CI/CD. Agora, vamos explorar Containerização de Servidores DNS BIND.

Benefícios da Containerização para Servidores DNS

Antes de mergulharmos nos detalhes técnicos, vamos entender os benefícios específicos da containerização para servidores DNS:

  1. Isolamento: Cada instância do BIND opera em seu próprio ambiente isolado
  2. Consistência: Garante que o ambiente de execução seja idêntico em todos os ambientes
  3. Portabilidade: Facilita a migração entre diferentes infraestruturas
  4. Escalabilidade: Permite escalar horizontalmente com facilidade
  5. Implantação rápida: Reduz o tempo de provisionamento de novos servidores
  6. Rollback simplificado: Facilita o retorno a versões anteriores em caso de problemas
  7. Eficiência de recursos: Utiliza recursos de forma mais eficiente que máquinas virtuais tradicionais
+-----------------------------------+      +-----------------------------------+
| Servidor Tradicional (VM ou Bare Metal) |      | Servidor Containerizado (Docker)  |
+-----------------------------------+      +-----------------------------------+
| Sistema Operacional Host          |      | Sistema Operacional Host          |
|-----------------------------------|      |-----------------------------------|
| Bibliotecas e Dependências        |      | Engine Docker                     |
|-----------------------------------|      |-----------------------------------|
| Processo BIND (named)             |      | Contêiner BIND                    |
|                                   |      |  |-- Bibliotecas Isoladas         |
|                                   |      |  |-- Processo BIND (named)        |
|                                   |      |  `-- Configuração Isolada        |
+-----------------------------------+      +-----------------------------------+
| Vantagens:                        |      | Vantagens:                        |
| - Familiaridade                   |      | - Isolamento, Portabilidade       |
| - Acesso direto ao hardware       |      | - Consistência, Escalabilidade    |
| Desvantagens:                     |      | - Implantação Rápida, Eficiência  |
| - Conflitos de dependência        |      | Desvantagens:                     |
| - Provisionamento lento           |      | - Curva de aprendizado            |
| - Menos portável                  |      | - Overhead da engine Docker       |
+-----------------------------------+      +-----------------------------------+

Figura 1: Comparação entre Servidor DNS Tradicional e Containerizado

Abordagens para Containerização de DNS

Abordagens para Containerização de DNS

Existem várias abordagens para containerizar servidores DNS:

  1. Contêiner único: Uma instância do BIND por contêiner
  2. Sidecar: Contêiner BIND acompanhado de contêineres auxiliares (ex: exportador de métricas)
  3. Orquestração: Gerenciamento de múltiplos contêineres DNS com Kubernetes ou Docker Swarm

Neste tutorial, focaremos principalmente na abordagem de contêiner único, mas também abordaremos aspectos de orquestração para ambientes mais complexos.

Criação de Imagem Docker para BIND

Vamos começar criando uma imagem Docker personalizada para o BIND.

Estrutura de Diretórios

1
2
3
4
5
6
7
8
9
10
11
12
docker/
├── bind/
│   ├── Dockerfile
│   ├── config/
│   │   ├── named.conf
│   │   └── rndc.key
│   ├── zones/
│   │   ├── example.com.zone
│   │   └── 200.168.192.in-addr.arpa.zone
│   └── scripts/
│       ├── entrypoint.sh
│       └── healthcheck.sh

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Dockerfile para BIND DNS Server
FROM oraclelinux:9

# Instalar BIND e utilitários
RUN dnf -y install bind bind-utils bind-dnssec-utils && \
    dnf clean all && \
    mkdir -p /var/named/zones /var/named/data /var/named/dynamic /var/log/named && \
    chown -R named:named /var/named /var/log/named

# Copiar arquivos de configuração
COPY config/named.conf /etc/named.conf
COPY config/rndc.key /etc/rndc.key
COPY zones/ /var/named/zones/

# Configurar permissões
RUN chown root:named /etc/named.conf /etc/rndc.key && \
    chmod 640 /etc/named.conf /etc/rndc.key && \
    chown named:named /var/named/zones/* && \
    chmod 640 /var/named/zones/*

# Copiar scripts
COPY scripts/entrypoint.sh /entrypoint.sh
COPY scripts/healthcheck.sh /healthcheck.sh
RUN chmod +x /entrypoint.sh /healthcheck.sh

# Expor portas
EXPOSE 53/tcp 53/udp 953/tcp

# Configurar healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD ["/healthcheck.sh"]

# Definir usuário
USER named

# Definir entrypoint
ENTRYPOINT ["/entrypoint.sh"]
CMD ["named", "-g", "-c", "/etc/named.conf"]

Script de Entrypoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
# entrypoint.sh - Script de inicialização para contêiner BIND

# Verificar arquivos de configuração
echo "Verificando configuração do BIND..."
named-checkconf /etc/named.conf
if [ $? -ne 0 ]; then
    echo "Erro na configuração do BIND. Verifique o arquivo named.conf."
    exit 1
fi

# Verificar arquivos de zona
for zone_file in /var/named/zones/*.zone; do
    if [ -f "$zone_file" ]; then
        zone_name=$(basename "$zone_file" .zone)
        echo "Verificando zona $zone_name..."
        named-checkzone "$zone_name" "$zone_file"
        if [ $? -ne 0 ]; then
            echo "Erro na zona $zone_name. Verifique o arquivo $zone_file."
            exit 1
        fi
    fi
done

# Executar comando fornecido
echo "Iniciando BIND..."
exec "$@"

Script de Healthcheck

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
# healthcheck.sh - Script para verificar a saúde do servidor BIND

# Verificar se o processo named está em execução
pgrep named > /dev/null
if [ $? -ne 0 ]; then
    echo "Processo named não está em execução."
    exit 1
fi

# Verificar se o servidor está respondendo a consultas
dig @127.0.0.1 localhost > /dev/null
if [ $? -ne 0 ]; then
    echo "Servidor DNS não está respondendo a consultas."
    exit 1
fi

# Verificar se o servidor está respondendo a consultas de zonas configuradas
if [ -f /var/named/zones/example.com.zone ]; then
    dig @127.0.0.1 example.com SOA +short > /dev/null
    if [ $? -ne 0 ]; then
        echo "Servidor DNS não está respondendo a consultas para example.com."
        exit 1
    fi
fi

# Tudo OK
exit 0

Arquivo de Configuração Named.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// named.conf para contêiner Docker
options {
    directory "/var/named";
    
    listen-on port 53 { any; };
    listen-on-v6 port 53 { ::1; };
    
    allow-query { any; };
    recursion no;
    
    dnssec-validation auto;
    
    pid-file "/var/run/named/named.pid";
    
    // Logging configuration
    logging {
        channel general_log {
            file "/var/log/named/general.log" versions 3 size 5m;
            severity info;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        
        channel security_log {
            file "/var/log/named/security.log" versions 3 size 5m;
            severity info;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        
        category default { general_log; };
        category general { general_log; };
        category security { security_log; };
    };
};

// Zone definitions
zone "example.com" IN {
    type master;
    file "zones/example.com.zone";
    allow-transfer { none; };
};

zone "200.168.192.in-addr.arpa" IN {
    type master;
    file "zones/200.168.192.in-addr.arpa.zone";
    allow-transfer { none; };
};

// Include standard zones
include "/etc/named.rfc1912.zones";

// Include DNSSEC root key
include "/etc/named.root.key";

Construção e Execução da Imagem Docker

1
2
3
4
5
6
7
8
9
10
11
# Construir a imagem
docker build -t bind-dns:latest docker/bind/

# Executar o contêiner
docker run -d --name bind-dns \
  -p 53:53/tcp \
  -p 53:53/udp \
  -v $(pwd)/zones:/var/named/zones \
  -v $(pwd)/logs:/var/log/named \
  --restart=unless-stopped \
  bind-dns:latest

Configuração de Contêiner Secundário

Para configurar um servidor DNS secundário em contêiner, precisamos ajustar a configuração:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// named.conf para contêiner secundário
options {
    directory "/var/named";
    
    listen-on port 53 { any; };
    listen-on-v6 port 53 { ::1; };
    
    allow-query { any; };
    recursion no;
    
    dnssec-validation auto;
    
    pid-file "/var/run/named/named.pid";
    
    // Logging configuration
    logging {
        channel general_log {
            file "/var/log/named/general.log" versions 3 size 5m;
            severity info;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        
        category default { general_log; };
    };
};

// TSIG key
key "xfer-key" {
    algorithm hmac-sha256;
    secret "a2xhc2Rqa2xhanNka2xqYWtsc2RqYWtsc2RqbGFrc2o=";
};

server 192.168.200.49 {
    keys { xfer-key; };
};

// Zone definitions
zone "example.com" IN {
    type slave;
    masters { 192.168.200.49; };
    file "slaves/example.com.zone";
};

zone "200.168.192.in-addr.arpa" IN {
    type slave;
    masters { 192.168.200.49; };
    file "slaves/200.168.192.in-addr.arpa.zone";
};

// Include standard zones
include "/etc/named.rfc1912.zones";

// Include DNSSEC root key
include "/etc/named.root.key";

Docker Compose para Múltiplos Servidores DNS

Para ambientes que requerem múltiplos servidores DNS, podemos usar Docker Compose:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# docker-compose.yml
version: '3.8'

services:
  bind-primary:
    build:
      context: ./docker/bind
      dockerfile: Dockerfile
    image: bind-dns:latest
    container_name: bind-primary
    volumes:
      - ./config/primary/named.conf:/etc/named.conf:ro
      - ./config/primary/rndc.key:/etc/rndc.key:ro
      - ./zones:/var/named/zones:ro
      - ./logs/primary:/var/log/named
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    networks:
      dns_network:
        ipv4_address: 172.20.0.2
    restart: unless-stopped

  bind-secondary:
    build:
      context: ./docker/bind
      dockerfile: Dockerfile
    image: bind-dns:latest
    container_name: bind-secondary
    volumes:
      - ./config/secondary/named.conf:/etc/named.conf:ro
      - ./config/secondary/rndc.key:/etc/rndc.key:ro
      - ./slaves:/var/named/slaves
      - ./logs/secondary:/var/log/named
    ports:
      - "54:53/tcp"
      - "54:53/udp"
    networks:
      dns_network:
        ipv4_address: 172.20.0.3
    depends_on:
      - bind-primary
    restart: unless-stopped

networks:
  dns_network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

Integração com Ansible

Agora, vamos integrar nossa abordagem de containerização com Ansible para automatizar o gerenciamento dos contêineres.

Role Ansible para Gerenciamento de Contêineres DNS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# Criar estrutura da role
ansible-galaxy role init ansible/roles/bind_container

# Tarefas principais
cat > ansible/roles/bind_container/tasks/main.yml << EOF
---
- name: Instalar dependências
  dnf:
    name:
      - docker
      - python3-docker
    state: present

- name: Iniciar e habilitar serviço Docker
  systemd:
    name: docker
    state: started
    enabled: yes

- name: Criar diretórios para BIND
  file:
    path: "{{ item }}"
    state: directory
    owner: "{{ bind_container_user }}"
    group: "{{ bind_container_group }}"
    mode: '0750'
  loop:
    - "{{ bind_container_config_dir }}"
    - "{{ bind_container_zones_dir }}"
    - "{{ bind_container_logs_dir }}"
    - "{{ bind_container_slaves_dir }}"

- name: Copiar Dockerfile
  template:
    src: Dockerfile.j2
    dest: "{{ bind_container_config_dir }}/Dockerfile"
    owner: "{{ bind_container_user }}"
    group: "{{ bind_container_group }}"
    mode: '0640'

- name: Copiar scripts
  template:
    src: "{{ item.src }}"
    dest: "{{ bind_container_config_dir }}/{{ item.dest }}"
    owner: "{{ bind_container_user }}"
    group: "{{ bind_container_group }}"
    mode: '0750'
  loop:
    - { src: 'entrypoint.sh.j2', dest: 'entrypoint.sh' }
    - { src: 'healthcheck.sh.j2', dest: 'healthcheck.sh' }

- name: Copiar configuração do BIND
  template:
    src: "named.conf.{{ bind_container_role }}.j2"
    dest: "{{ bind_container_config_dir }}/named.conf"
    owner: "{{ bind_container_user }}"
    group: "{{ bind_container_group }}"
    mode: '0640'

- name: Copiar arquivos de zona
  template:
    src: "zones/{{ item.name }}.zone.j2"
    dest: "{{ bind_container_zones_dir }}/{{ item.name }}.zone"
    owner: "{{ bind_container_user }}"
    group: "{{ bind_container_group }}"
    mode: '0640'
  loop: "{{ bind_container_zones }}"
  when: bind_container_role == 'primary' and item.type == 'master'

- name: Construir imagem Docker
  docker_image:
    name: "{{ bind_container_image_name }}"
    tag: "{{ bind_container_image_tag }}"
    build:
      path: "{{ bind_container_config_dir }}"
      pull: yes
    source: build
    state: present

- name: Executar contêiner BIND
  docker_container:
    name: "{{ bind_container_name }}"
    image: "{{ bind_container_image_name }}:{{ bind_container_image_tag }}"
    state: started
    restart_policy: unless-stopped
    ports:
      - "{{ bind_container_port_tcp }}:53/tcp"
      - "{{ bind_container_port_udp }}:53/udp"
    volumes:
      - "{{ bind_container_config_dir }}/named.conf:/etc/named.conf:ro"
      - "{{ bind_container_zones_dir }}:/var/named/zones:{% if bind_container_role == 'primary' %}ro{% else %}rw{% endif %}"
      - "{{ bind_container_logs_dir }}:/var/log/named:rw"
      {% if bind_container_role == 'secondary' %}
      - "{{ bind_container_slaves_dir }}:/var/named/slaves:rw"
      {% endif %}
    networks:
      - name: "{{ bind_container_network }}"
        ipv4_address: "{{ bind_container_ip }}"
EOF

# Variáveis padrão
cat > ansible/roles/bind_container/defaults/main.yml << EOF
---
# Configurações do contêiner
bind_container_user: "root"
bind_container_group: "root"
bind_container_role: "primary"  # primary ou secondary
bind_container_name: "bind-{{ bind_container_role }}"
bind_container_image_name: "bind-dns"
bind_container_image_tag: "latest"
bind_container_port_tcp: 53
bind_container_port_udp: 53
bind_container_network: "dns_network"
bind_container_ip: "172.20.0.2"  # Para primary, usar 172.20.0.3 para secondary

# Diretórios
bind_container_config_dir: "/opt/bind/config"
bind_container_zones_dir: "/opt/bind/zones"
bind_container_logs_dir: "/opt/bind/logs"
bind_container_slaves_dir: "/opt/bind/slaves"

# Zonas
bind_container_zones:
  - name: "example.com"
    type: "master"
  - name: "200.168.192.in-addr.arpa"
    type: "master"

# Configuração de rede
bind_container_allow_query:
  - "localhost"
  - "172.20.0.0/24"
  - "192.168.200.0/24"

# Configuração de transferência
bind_container_use_tsig: true
bind_container_tsig_key_name: "xfer-key"
bind_container_tsig_key_algorithm: "hmac-sha256"
bind_container_tsig_key_secret: "a2xhc2Rqa2xhanNka2xqYWtsc2RqYWtsc2RqbGFrc2o="

# Configuração de secundário
bind_container_primary_server: "172.20.0.2"  # IP do servidor primário
EOF

# Templates
mkdir -p ansible/roles/bind_container/templates/zones

# Template para Dockerfile
cat > ansible/roles/bind_container/templates/Dockerfile.j2 << EOF
# Dockerfile para BIND DNS Server
FROM oraclelinux:9

# Instalar BIND e utilitários
RUN dnf -y install bind bind-utils bind-dnssec-utils && \\
    dnf clean all && \\
    mkdir -p /var/named/zones /var/named/data /var/named/dynamic /var/named/slaves /var/log/named && \\
    chown -R named:named /var/named /var/log/named

# Copiar scripts
COPY entrypoint.sh /entrypoint.sh
COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /entrypoint.sh /healthcheck.sh

# Expor portas
EXPOSE 53/tcp 53/udp 953/tcp

# Configurar healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD ["/healthcheck.sh"]

# Definir usuário
USER named

# Definir entrypoint
ENTRYPOINT ["/entrypoint.sh"]
CMD ["named", "-g", "-c", "/etc/named.conf"]
EOF

# Template para entrypoint.sh
cat > ansible/roles/bind_container/templates/entrypoint.sh.j2 << EOF
#!/bin/bash
# entrypoint.sh - Script de inicialização para contêiner BIND

# Verificar arquivos de configuração
echo "Verificando configuração do BIND..."
named-checkconf /etc/named.conf
if [ \$? -ne 0 ]; then
    echo "Erro na configuração do BIND. Verifique o arquivo named.conf."
    exit 1
fi

# Verificar arquivos de zona
for zone_file in /var/named/zones/*.zone; do
    if [ -f "\$zone_file" ]; then
        zone_name=\$(basename "\$zone_file" .zone)
        echo "Verificando zona \$zone_name..."
        named-checkzone "\$zone_name" "\$zone_file"
        if [ \$? -ne 0 ]; then
            echo "Erro na zona \$zone_name. Verifique o arquivo \$zone_file."
            exit 1
        fi
    fi
done

# Executar comando fornecido
echo "Iniciando BIND..."
exec "\$@"
EOF

# Template para healthcheck.sh
cat > ansible/roles/bind_container/templates/healthcheck.sh.j2 << EOF
#!/bin/bash
# healthcheck.sh - Script para verificar a saúde do servidor BIND

# Verificar se o processo named está em execução
pgrep named > /dev/null
if [ \$? -ne 0 ]; then
    echo "Processo named não está em execução."
    exit 1
fi

# Verificar se o servidor está respondendo a consultas
dig @127.0.0.1 localhost > /dev/null
if [ \$? -ne 0 ]; then
    echo "Servidor DNS não está respondendo a consultas."
    exit 1
fi

# Tudo OK
exit 0
EOF

# Template para named.conf primário
cat > ansible/roles/bind_container/templates/named.conf.primary.j2 << EOF
// named.conf para contêiner Docker - Servidor Primário
options {
    directory "/var/named";
    
    listen-on port 53 { any; };
    listen-on-v6 port 53 { ::1; };
    
    allow-query { 
        {% for net in bind_container_allow_query %}
        {{ net }};
        {% endfor %}
    };
    
    recursion no;
    
    dnssec-validation auto;
    
    pid-file "/var/run/named/named.pid";
    
    // Logging configuration
    logging {
        channel general_log {
            file "/var/log/named/general.log" versions 3 size 5m;
            severity info;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        
        channel security_log {
            file "/var/log/named/security.log" versions 3 size 5m;
            severity info;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        
        category default { general_log; };
        category general { general_log; };
        category security { security_log; };
    };
};

{% if bind_container_use_tsig %}
// TSIG key
key "{{ bind_container_tsig_key_name }}" {
    algorithm {{ bind_container_tsig_key_algorithm }};
    secret "{{ bind_container_tsig_key_secret }}";
};
{% endif %}

// Zone definitions
{% for zone in bind_container_zones %}
zone "{{ zone.name }}" IN {
    type master;
    file "zones/{{ zone.name }}.zone";
    {% if bind_container_use_tsig %}
    allow-transfer { key {{ bind_container_tsig_key_name }}; };
    {% else %}
    allow-transfer { none; };
    {% endif %}
    notify yes;
};
{% endfor %}

// Include standard zones
include "/etc/named.rfc1912.zones";

// Include DNSSEC root key
include "/etc/named.root.key";
EOF

# Template para named.conf secundário
cat > ansible/roles/bind_container/templates/named.conf.secondary.j2 << EOF
// named.conf para contêiner Docker - Servidor Secundário
options {
    directory "/var/named";
    
    listen-on port 53 { any; };
    listen-on-v6 port 53 { ::1; };
    
    allow-query { 
        {% for net in bind_container_allow_query %}
        {{ net }};
        {% endfor %}
    };
    
    recursion no;
    
    dnssec-validation auto;
    
    pid-file "/var/run/named/named.pid";
    
    // Logging configuration
    logging {
        channel general_log {
            file "/var/log/named/general.log" versions 3 size 5m;
            severity info;
            print-time yes;
            print-severity yes;
            print-category yes;
        };
        
        category default { general_log; };
    };
};

{% if bind_container_use_tsig %}
// TSIG key
key "{{ bind_container_tsig_key_name }}" {
    algorithm {{ bind_container_tsig_key_algorithm }};
    secret "{{ bind_container_tsig_key_secret }}";
};

server {{ bind_container_primary_server }} {
    keys { {{ bind_container_tsig_key_name }}; };
};
{% endif %}

// Zone definitions
{% for zone in bind_container_zones %}
zone "{{ zone.name }}" IN {
    type slave;
    masters { {{ bind_container_primary_server }}; };
    file "slaves/{{ zone.name }}.zone";
};
{% endfor %}

// Include standard zones
include "/etc/named.rfc1912.zones";

// Include DNSSEC root key
include "/etc/named.root.key";
EOF

# Template para zona example.com
cat > ansible/roles/bind_container/templates/zones/example.com.zone.j2 << EOF
\$TTL {{ dns_ttl | default('86400') }}
@       IN SOA  ns1.{{ dns_domain | default('example.com') }}. {{ dns_admin_email | default('admin.example.com') }}. (
                    {{ '%Y%m%d01' | strftime }}  ; Serial
                    {{ dns_refresh | default('10800') }}       ; Refresh
                    {{ dns_retry | default('3600') }}        ; Retry
                    {{ dns_expire | default('604800') }}      ; Expire
                    {{ dns_minimum | default('86400') }} )     ; Minimum TTL

        IN NS   ns1.{{ dns_domain | default('example.com') }}.
        IN NS   ns2.{{ dns_domain | default('example.com') }}.

ns1     IN A    {{ bind_primary_ip | default('192.168.200.49') }}
ns2     IN A    {{ bind_secondary_ip | default('192.168.200.50') }}
www     IN A    {{ bind_primary_ip | default('192.168.200.49') }}
mail    IN A    {{ bind_primary_ip | default('192.168.200.49') }}
@       IN MX   10 mail.{{ dns_domain | default('example.com') }}.
EOF

# Template para zona reversa
cat > ansible/roles/bind_container/templates/zones/200.168.192.in-addr.arpa.zone.j2 << EOF
\$TTL {{ dns_ttl | default('86400') }}
@       IN SOA  ns1.{{ dns_domain | default('example.com') }}. {{ dns_admin_email | default('admin.example.com') }}. (
                    {{ '%Y%m%d01' | strftime }}  ; Serial
                    {{ dns_refresh | default('10800') }}       ; Refresh
                    {{ dns_retry | default('3600') }}        ; Retry
                    {{ dns_expire | default('604800') }}      ; Expire
                    {{ dns_minimum | default('86400') }} )     ; Minimum TTL

        IN NS   ns1.{{ dns_domain | default('example.com') }}.
        IN NS   ns2.{{ dns_domain | default('example.com') }}.

; Registros PTR
49      IN PTR  ns1.{{ dns_domain | default('example.com') }}.
50      IN PTR  ns2.{{ dns_domain | default('example.com') }}.
EOF

Playbook para Contêineres DNS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# ansible/playbooks/container.yml
cat > ansible/playbooks/container.yml << EOF
---
- name: Configurar servidores DNS primários em contêineres
  hosts: primary
  become: yes
  vars:
    bind_container_role: "primary"
    bind_container_port_tcp: 53
    bind_container_port_udp: 53
    bind_container_ip: "172.20.0.2"
  roles:
    - bind_container

- name: Configurar servidores DNS secundários em contêineres
  hosts: secondary
  become: yes
  vars:
    bind_container_role: "secondary"
    bind_container_port_tcp: 53
    bind_container_port_udp: 53
    bind_container_ip: "172.20.0.3"
    bind_container_primary_server: "172.20.0.2"
  roles:
    - bind_container
EOF

Integração com GitLab CI/CD

Agora, vamos integrar nossa abordagem de containerização com o pipeline GitLab CI/CD.

Atualização do .gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
---
stages:
  - validate
  - build
  - test
  - deploy_dev
  - deploy_prod

variables:
  ANSIBLE_CONFIG: ansible/ansible.cfg
  DOCKER_IMAGE_NAME: bind-dns
  DOCKER_REGISTRY: registry.example.com

# Validação de sintaxe e lint
validate:
  stage: validate
  image: python:3.9-slim
  before_script:
    - apt-get update && apt-get install -y bind9utils
    - pip install ansible ansible-lint
  script:
    - ansible-playbook ansible/playbooks/site.yml --syntax-check
    - ansible-playbook ansible/playbooks/container.yml --syntax-check
    - ansible-lint ansible/playbooks/site.yml ansible/playbooks/container.yml
  tags:
    - docker

# Validação de zonas
validate_zones:
  stage: validate
  image: ubuntu:22.04
  before_script:
    - apt-get update && apt-get install -y bind9utils python3-pip
    - pip3 install ansible jinja2
  script:
    - mkdir -p /tmp/zones
    - for template in ansible/roles/bind_primary/templates/zones/*.zone.j2 ansible/roles/bind_container/templates/zones/*.zone.j2; do
        zone_file="/tmp/zones/$(basename $template .j2)";
        ansible-playbook -i localhost, -c local ansible/playbooks/generate_zone.yml -e "template=$template output=$zone_file" --skip-tags=all;
        named-checkzone $(basename $zone_file .zone) $zone_file;
      done
  tags:
    - docker

# Construção da imagem Docker
build_docker:
  stage: build
  image: docker:20.10
  services:
    - docker:20.10-dind
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $DOCKER_REGISTRY/$DOCKER_IMAGE_NAME:$CI_COMMIT_SHORT_SHA -t $DOCKER_REGISTRY/$DOCKER_IMAGE_NAME:latest docker/bind/
    - docker push $DOCKER_REGISTRY/$DOCKER_IMAGE_NAME:$CI_COMMIT_SHORT_SHA
    - docker push $DOCKER_REGISTRY/$DOCKER_IMAGE_NAME:latest
  only:
    - master
    - development
  tags:
    - docker

# Testes em ambiente de desenvolvimento
test_dev:
  stage: test
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  script:
    - ansible-playbook -i ansible/inventory/development/hosts.yml ansible/playbooks/container.yml --check -e "docker_image_tag=$CI_COMMIT_SHORT_SHA"
  environment:
    name: development
  tags:
    - shell
  only:
    - master
    - development

# Implantação em ambiente de desenvolvimento
deploy_dev:
  stage: deploy_dev
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  script:
    - ansible-playbook -i ansible/inventory/development/hosts.yml ansible/playbooks/container.yml -e "docker_image_tag=$CI_COMMIT_SHORT_SHA"
  environment:
    name: development
  tags:
    - shell
  only:
    - master
    - development

# Implantação em ambiente de produção
deploy_prod:
  stage: deploy_prod
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  script:
    - ansible-playbook -i ansible/inventory/production/hosts.yml ansible/playbooks/container.yml -e "docker_image_tag=$CI_COMMIT_SHORT_SHA"
  environment:
    name: production
  when: manual
  tags:
    - shell
  only:
    - master

Orquestração com Kubernetes

Para ambientes mais complexos, podemos usar Kubernetes para orquestrar nossos contêineres DNS.

Manifesto Kubernetes para BIND

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# kubernetes/bind-deployment.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: bind-config
data:
  named.conf: |
    // named.conf para Kubernetes
    options {
        directory "/var/named";
        
        listen-on port 53 { any; };
        listen-on-v6 port 53 { ::1; };
        
        allow-query { any; };
        recursion no;
        
        dnssec-validation auto;
        
        pid-file "/var/run/named/named.pid";
        
        // Logging configuration
        logging {
            channel general_log {
                file "/var/log/named/general.log" versions 3 size 5m;
                severity info;
                print-time yes;
                print-severity yes;
                print-category yes;
            };
            
            category default { general_log; };
        };
    };

    // Zone definitions
    zone "example.com" IN {
        type master;
        file "zones/example.com.zone";
        allow-transfer { none; };
    };

    zone "200.168.192.in-addr.arpa" IN {
        type master;
        file "zones/200.168.192.in-addr.arpa.zone";
        allow-transfer { none; };
    };

    // Include standard zones
    include "/etc/named.rfc1912.zones";

    // Include DNSSEC root key
    include "/etc/named.root.key";

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: bind-zones
data:
  example.com.zone: |
    $TTL 86400
    @       IN SOA  ns1.example.com. admin.example.com. (
                        2025052501  ; Serial
                        10800       ; Refresh
                        3600        ; Retry
                        604800      ; Expire
                        86400 )     ; Minimum TTL

            IN NS   ns1.example.com.
            IN NS   ns2.example.com.

    ns1     IN A    192.168.200.49
    ns2     IN A    192.168.200.50
    www     IN A    192.168.200.49
    mail    IN A    192.168.200.49
    @       IN MX   10 mail.example.com.

  200.168.192.in-addr.arpa.zone: |
    $TTL 86400
    @       IN SOA  ns1.example.com. admin.example.com. (
                        2025052501  ; Serial
                        10800       ; Refresh
                        3600        ; Retry
                        604800      ; Expire
                        86400 )     ; Minimum TTL

            IN NS   ns1.example.com.
            IN NS   ns2.example.com.

    ; Registros PTR
    49      IN PTR  ns1.example.com.
    50      IN PTR  ns2.example.com.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bind-dns
  labels:
    app: bind-dns
spec:
  replicas: 2
  selector:
    matchLabels:
      app: bind-dns
  template:
    metadata:
      labels:
        app: bind-dns
    spec:
      containers:
      - name: bind
        image: registry.example.com/bind-dns:latest
        ports:
        - containerPort: 53
          protocol: TCP
          name: dns-tcp
        - containerPort: 53
          protocol: UDP
          name: dns-udp
        volumeMounts:
        - name: config
          mountPath: /etc/named.conf
          subPath: named.conf
        - name: zones
          mountPath: /var/named/zones
        - name: logs
          mountPath: /var/log/named
        livenessProbe:
          exec:
            command:
            - /healthcheck.sh
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          exec:
            command:
            - dig
            - @127.0.0.1
            - example.com
            - SOA
            - +short
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          limits:
            cpu: "500m"
            memory: "512Mi"
          requests:
            cpu: "100m"
            memory: "128Mi"
      volumes:
      - name: config
        configMap:
          name: bind-config
      - name: zones
        configMap:
          name: bind-zones
      - name: logs
        emptyDir: {}

---
apiVersion: v1
kind: Service
metadata:
  name: bind-dns
spec:
  selector:
    app: bind-dns
  ports:
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: dns-tcp
  - name: dns-udp
    port: 53
    protocol: UDP
    targetPort: dns-udp
  type: LoadBalancer

Integração do Kubernetes com Ansible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# ansible/roles/bind_kubernetes/tasks/main.yml
cat > ansible/roles/bind_kubernetes/tasks/main.yml << EOF
---
- name: Instalar dependências
  dnf:
    name:
      - kubectl
      - python3-kubernetes
    state: present

- name: Criar diretório para manifestos Kubernetes
  file:
    path: "{{ kubernetes_manifest_dir }}"
    state: directory
    owner: "{{ kubernetes_user }}"
    group: "{{ kubernetes_group }}"
    mode: '0750'

- name: Copiar manifesto Kubernetes
  template:
    src: bind-deployment.yaml.j2
    dest: "{{ kubernetes_manifest_dir }}/bind-deployment.yaml"
    owner: "{{ kubernetes_user }}"
    group: "{{ kubernetes_group }}"
    mode: '0640'

- name: Aplicar manifesto Kubernetes
  k8s:
    state: present
    src: "{{ kubernetes_manifest_dir }}/bind-deployment.yaml"
    kubeconfig: "{{ kubernetes_kubeconfig }}"
EOF

# ansible/roles/bind_kubernetes/defaults/main.yml
cat > ansible/roles/bind_kubernetes/defaults/main.yml << EOF
---
kubernetes_user: "root"
kubernetes_group: "root"
kubernetes_manifest_dir: "/opt/kubernetes/manifests"
kubernetes_kubeconfig: "/root/.kube/config"
kubernetes_namespace: "default"
kubernetes_replicas: 2
kubernetes_image: "registry.example.com/bind-dns:latest"
EOF

# ansible/roles/bind_kubernetes/templates/bind-deployment.yaml.j2
cat > ansible/roles/bind_kubernetes/templates/bind-deployment.yaml.j2 << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: bind-config
  namespace: {{ kubernetes_namespace }}
data:
  named.conf: |
    // named.conf para Kubernetes
    options {
        directory "/var/named";
        
        listen-on port 53 { any; };
        listen-on-v6 port 53 { ::1; };
        
        allow-query { any; };
        recursion no;
        
        dnssec-validation auto;
        
        pid-file "/var/run/named/named.pid";
        
        // Logging configuration
        logging {
            channel general_log {
                file "/var/log/named/general.log" versions 3 size 5m;
                severity info;
                print-time yes;
                print-severity yes;
                print-category yes;
            };
            
            category default { general_log; };
        };
    };

    // Zone definitions
    {% for zone in bind_container_zones %}
    zone "{{ zone.name }}" IN {
        type master;
        file "zones/{{ zone.name }}.zone";
        allow-transfer { none; };
    };
    {% endfor %}

    // Include standard zones
    include "/etc/named.rfc1912.zones";

    // Include DNSSEC root key
    include "/etc/named.root.key";

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: bind-zones
  namespace: {{ kubernetes_namespace }}
data:
  {% for zone in bind_container_zones %}
  {{ zone.name }}.zone: |
{{ lookup('template', 'zones/' + zone.name + '.zone.j2') | indent(4, True) }}
  {% endfor %}

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bind-dns
  namespace: {{ kubernetes_namespace }}
  labels:
    app: bind-dns
spec:
  replicas: {{ kubernetes_replicas }}
  selector:
    matchLabels:
      app: bind-dns
  template:
    metadata:
      labels:
        app: bind-dns
    spec:
      containers:
      - name: bind
        image: {{ kubernetes_image }}
        ports:
        - containerPort: 53
          protocol: TCP
          name: dns-tcp
        - containerPort: 53
          protocol: UDP
          name: dns-udp
        volumeMounts:
        - name: config
          mountPath: /etc/named.conf
          subPath: named.conf
        - name: zones
          mountPath: /var/named/zones
        - name: logs
          mountPath: /var/log/named
        livenessProbe:
          exec:
            command:
            - /healthcheck.sh
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          exec:
            command:
            - dig
            - @127.0.0.1
            - example.com
            - SOA
            - +short
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          limits:
            cpu: "500m"
            memory: "512Mi"
          requests:
            cpu: "100m"
            memory: "128Mi"
      volumes:
      - name: config
        configMap:
          name: bind-config
      - name: zones
        configMap:
          name: bind-zones
      - name: logs
        emptyDir: {}

---
apiVersion: v1
kind: Service
metadata:
  name: bind-dns
  namespace: {{ kubernetes_namespace }}
spec:
  selector:
    app: bind-dns
  ports:
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: dns-tcp
  - name: dns-udp
    port: 53
    protocol: UDP
    targetPort: dns-udp
  type: LoadBalancer
EOF

# ansible/playbooks/kubernetes.yml
cat > ansible/playbooks/kubernetes.yml << EOF
---
- name: Implantar BIND no Kubernetes
  hosts: kubernetes_master
  become: yes
  roles:
    - bind_kubernetes
EOF

Integração do Kubernetes com GitLab CI/CD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Adição ao .gitlab-ci.yml
deploy_kubernetes:
  stage: deploy_prod
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
  script:
    - ansible-playbook -i ansible/inventory/production/hosts.yml ansible/playbooks/kubernetes.yml -e "kubernetes_image=$DOCKER_REGISTRY/$DOCKER_IMAGE_NAME:$CI_COMMIT_SHORT_SHA"
  environment:
    name: kubernetes
  when: manual
  tags:
    - shell
  only:
    - master

Monitoramento de Contêineres DNS

Para monitorar contêineres DNS, podemos usar ferramentas como Prometheus e Grafana.

Exportador de Métricas BIND para Prometheus

1
2
3
4
5
6
7
8
9
10
11
12
# Dockerfile para BIND Exporter
FROM golang:1.18 as builder

RUN go install github.com/prometheus-community/bind_exporter@latest

FROM oraclelinux:9-slim

COPY --from=builder /go/bin/bind_exporter /usr/local/bin/bind_exporter

EXPOSE 9119

ENTRYPOINT ["/usr/local/bin/bind_exporter"]

Docker Compose com Monitoramento

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# docker-compose-monitoring.yml
version: '3.8'

services:
  bind-primary:
    build:
      context: ./docker/bind
      dockerfile: Dockerfile
    image: bind-dns:latest
    container_name: bind-primary
    volumes:
      - ./config/primary/named.conf:/etc/named.conf:ro
      - ./config/primary/rndc.key:/etc/rndc.key:ro
      - ./zones:/var/named/zones:ro
      - ./logs/primary:/var/log/named
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    networks:
      dns_network:
        ipv4_address: 172.20.0.2
    restart: unless-stopped

  bind-secondary:
    build:
      context: ./docker/bind
      dockerfile: Dockerfile
    image: bind-dns:latest
    container_name: bind-secondary
    volumes:
      - ./config/secondary/named.conf:/etc/named.conf:ro
      - ./config/secondary/rndc.key:/etc/rndc.key:ro
      - ./slaves:/var/named/slaves
      - ./logs/secondary:/var/log/named
    ports:
      - "54:53/tcp"
      - "54:53/udp"
    networks:
      dns_network:
        ipv4_address: 172.20.0.3
    depends_on:
      - bind-primary
    restart: unless-stopped

  bind-exporter-primary:
    build:
      context: ./docker/bind-exporter
      dockerfile: Dockerfile
    container_name: bind-exporter-primary
    command: --bind.stats-url=http://bind-primary:8053/ --web.listen-address=:9119
    ports:
      - "9119:9119"
    networks:
      dns_network:
    depends_on:
      - bind-primary
    restart: unless-stopped

  bind-exporter-secondary:
    build:
      context: ./docker/bind-exporter
      dockerfile: Dockerfile
    container_name: bind-exporter-secondary
    command: --bind.stats-url=http://bind-secondary:8053/ --web.listen-address=:9120
    ports:
      - "9120:9120"
    networks:
      dns_network:
    depends_on:
      - bind-secondary
    restart: unless-stopped

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    networks:
      dns_network:
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards
    ports:
      - "3000:3000"
    networks:
      dns_network:
    depends_on:
      - prometheus
    restart: unless-stopped

networks:
  dns_network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

volumes:
  prometheus_data:
  grafana_data:

Configuração do Prometheus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'bind_primary'
    static_configs:
      - targets: ['bind-exporter-primary:9119']
        labels:
          instance: 'bind-primary'

  - job_name: 'bind_secondary'
    static_configs:
      - targets: ['bind-exporter-secondary:9120']
        labels:
          instance: 'bind-secondary'

Melhores Práticas para DNS Containerizado

Segurança

  1. Imagens mínimas: Use imagens base mínimas para reduzir a superfície de ataque
  2. Usuário não-root: Execute o BIND como usuário não-root dentro do contêiner
  3. Read-only filesystem: Monte volumes como somente leitura quando possível
  4. Secrets: Use ferramentas de gerenciamento de secrets (Docker Secrets, Kubernetes Secrets)
  5. Escaneamento de vulnerabilidades: Escaneie imagens regularmente com ferramentas como Trivy ou Clair

Desempenho

  1. Recursos adequados: Aloque CPU e memória adequados para os contêineres
  2. Cache otimizado: Configure o cache do BIND para o tamanho apropriado
  3. Volumes persistentes: Use volumes persistentes para dados que precisam sobreviver a reinicializações
  4. Rede otimizada: Configure a rede do contêiner para desempenho ideal

Alta Disponibilidade

  1. Múltiplas réplicas: Execute múltiplas instâncias do BIND
  2. Distribuição geográfica: Distribua contêineres em diferentes zonas/regiões
  3. Health checks: Implemente verificações de saúde robustas
  4. Atualizações sem downtime: Configure atualizações rolling para evitar interrupções

Monitoramento

  1. Métricas: Exporte métricas do BIND para Prometheus
  2. Logs centralizados: Envie logs para um sistema centralizado (ELK, Graylog)
  3. Alertas: Configure alertas para problemas críticos
  4. Dashboards: Crie dashboards no Grafana para visualização

Conclusão

A containerização de servidores DNS BIND, combinada com automação Ansible e pipelines GitLab CI/CD, oferece uma solução moderna, escalável e gerenciável para infraestrutura DNS. Esta abordagem permite:

  1. Implantação consistente: Garante que todos os ambientes sejam idênticos
  2. Ciclo de vida gerenciado: Facilita atualizações, rollbacks e manutenção
  3. Escalabilidade: Permite escalar horizontalmente conforme necessário
  4. Portabilidade: Funciona em qualquer ambiente que suporte contêineres
  5. Integração com DevOps: Alinha-se com práticas modernas de DevOps e IaC

Ao adotar esta abordagem, você estará preparado para gerenciar infraestruturas DNS complexas e de grande escala, com maior eficiência, segurança e confiabilidade, aproveitando ao máximo as tecnologias modernas de containerização e orquestração.

Referências e Recursos Adicionais

This post is licensed under CC BY 4.0 by the author.