Quản lý tập tin cấu hình là một phần cốt lõi của tự động hóa cơ sở hạ tầng. Ansible cung cấp các công cụ mạnh mẽ cho cả việc triển khai tệp tĩnh và tạo cấu hình động. Bạn sẽ học cách sử dụng các mẫu thích ứng với các môi trường khác nhau, quản lý quyền và quyền sở hữu tệp cũng như sắp xếp các tệp cấu hình để có thể bảo trì.

Hiểu quản lý tệp trong Ansible

Ansible cung cấp một số mô-đun cho hoạt động của tệp, mỗi mô-đun phù hợp với các tình huống khác nhau:

  • copy: Triển khai các tệp tĩnh không thay đổi
  • template: Tạo tệp từ các mẫu Jinja2 với sự thay thế có thể thay đổi
  • file: Quản lý thuộc tính tệp, thư mục và liên kết
  • lineinfile: Sửa đổi từng dòng trong tệp hiện có
  • blockinfile: Quản lý khối văn bản trong tệp

Làm việc với các tệp tĩnh

Triển khai tệp cơ bản

Mô-đun copy xử lý việc triển khai tệp đơn giản:

---
- name: Deploy static configuration files
  hosts: webservers
  become: yes

  tasks:
    - name: Copy SSL certificate
      copy:
        src: files/ssl/server.crt
        dest: /etc/ssl/certs/server.crt
        owner: root
        group: root
        mode: '0644'
        backup: yes

    - name: Copy SSL private key
      copy:
        src: files/ssl/server.key
        dest: /etc/ssl/private/server.key
        owner: root
        group: ssl-cert
        mode: '0640'
        backup: yes
      notify: restart nginx

    - name: Deploy application scripts
      copy:
        src: files/scripts/
        dest: /opt/myapp/scripts/
        owner: myapp
        group: myapp
        mode: '0755'
        directory_mode: '0755'

Các tính năng chính của mô-đun sao chép:

  • backup: yes tạo bản sao lưu trước khi ghi đè
  • directory_mode đặt quyền cho thư mục khi sao chép toàn bộ thư mục
  • Đường dẫn src kết thúc bằng / sao chép nội dung thư mục chứ không phải chính thư mục đó

Quản lý nội dung tập tin

Tạo tệp có nội dung cụ thể trực tiếp trong playbook của bạn:

tasks:
  - name: Create maintenance page
    copy:
      content: |
        <!DOCTYPE html>
        <html>
        <head>
            <title>Maintenance Mode</title>
        </head>
        <body>
            <h1>Site Under Maintenance</h1>
            <p>We'll be back shortly. Estimated downtime: {{ maintenance_duration | default('30 minutes') }}</p>
        </body>
        </html>
      dest: /var/www/html/maintenance.html
      owner: www-data
      group: www-data
      mode: '0644'

Tạo mẫu động

Các mẫu sử dụng công cụ tạo khuôn mẫu Jinja2 để tạo các tệp cấu hình với các thay thế, điều kiện và vòng lặp có thể thay đổi.

Cấu trúc mẫu cơ bản

Tạo tệp mẫu templates/nginx.conf.j2:

# nginx configuration generated by Ansible
# Last updated: {{ ansible_date_time.iso8601 }}

user {{ nginx_user | default('www-data') }};
worker_processes {{ nginx_worker_processes | default(ansible_processor_vcpus) }};
pid /run/nginx.pid;

events {
    worker_connections {{ nginx_worker_connections | default(1024) }};
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log {{ nginx_access_log | default('/var/log/nginx/access.log') }} main;
    error_log {{ nginx_error_log | default('/var/log/nginx/error.log') }} warn;

    # Performance settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout {{ nginx_keepalive_timeout | default(65) }};

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length {{ nginx_gzip_min_length | default(1024) }};
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Triển khai mẫu:

tasks:
  - name: Generate nginx main configuration
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
      owner: root
      group: root
      mode: '0644'
      backup: yes
    notify: restart nginx

Mẫu có điều kiện

Sử dụng các điều kiện Jinja2 để điều chỉnh cấu hình dựa trên các biến:

# SSL configuration - templates/site.conf.j2
server {
{% if ssl_enabled | default(false) %}
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate {{ ssl_cert_path | default('/etc/ssl/certs/server.crt') }};
    ssl_certificate_key {{ ssl_key_path | default('/etc/ssl/private/server.key') }};
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    ssl_prefer_server_ciphers off;
{% else %}
    listen 80;
    listen [::]:80;
{% endif %}

    server_name {{ server_name | default(ansible_fqdn) }};
    root {{ document_root | default('/var/www/html') }};
    index index.html index.php;

{% if environment == 'development' %}
    # Development-specific settings
    error_log /var/log/nginx/{{ server_name }}-error.log debug;
    access_log /var/log/nginx/{{ server_name }}-access.log combined;
{% else %}
    # Production settings
    error_log /var/log/nginx/{{ server_name }}-error.log warn;
    access_log /var/log/nginx/{{ server_name }}-access.log main;
{% endif %}

    location / {
        try_files $uri $uri/ =404;
    }

{% if php_enabled | default(false) %}
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php{{ php_version | default('7.4') }}-fpm.sock;
    }
{% endif %}
}

{% if ssl_enabled | default(false) %}
# HTTP to HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name {{ server_name | default(ansible_fqdn) }};
    return 301 https://$server_name$request_uri;
}
{% endif %}

Mẫu có vòng lặp

Tạo cấu hình lặp đi lặp lại bằng cách sử dụng các vòng lặp:

# Load balancer configuration - templates/upstream.conf.j2
{% for service in load_balanced_services %}
upstream {{ service.name }}_backend {
    least_conn;
{% for server in service.servers %}
    server {{ server.host }}:{{ server.port }} weight={{ server.weight | default(1) }}{% if server.backup | default(false) %} backup{% endif %};
{% endfor %}

    # Health check
    keepalive {{ service.keepalive | default(32) }};
}

server {
    listen {{ service.port | default(80) }};
    server_name {{ service.domain }};

    location / {
        proxy_pass http://{{ service.name }}_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Timeouts
        proxy_connect_timeout {{ service.connect_timeout | default('5s') }};
        proxy_send_timeout {{ service.send_timeout | default('60s') }};
        proxy_read_timeout {{ service.read_timeout | default('60s') }};
    }
}

{% endfor %}

Sử dụng nó với các biến tương ứng:

vars:
  load_balanced_services:
    - name: api
      domain: api.example.com
      port: 80
      servers:
        - host: 192.168.1.10
          port: 8080
          weight: 2
        - host: 192.168.1.11
          port: 8080
          weight: 1
        - host: 192.168.1.12
          port: 8080
          backup: true
      keepalive: 64

    - name: admin
      domain: admin.example.com
      port: 80
      servers:
        - host: 192.168.1.15
          port: 9000

Kỹ thuật mẫu nâng cao

Sử dụng Bộ lọc trong Mẫu

Bộ lọc Jinja2 chuyển đổi dữ liệu trong các mẫu:

# Database configuration - templates/database.conf.j2
[database]
host = {{ db_host | default('localhost') }}
port = {{ db_port | default(5432) }}
name = {{ db_name | upper }}
user = {{ db_user | lower }}

# Convert memory setting to MB
shared_buffers = {{ (total_memory_mb * 0.25) | int }}MB
effective_cache_size = {{ (total_memory_mb * 0.75) | int }}MB

# Generate secure random password if not provided
{% if db_password is not defined %}
password = {{ ansible_date_time.epoch | hash('sha256') | truncate(16, true, '') }}
{% else %}
password = {{ db_password }}
{% endif %}

# Conditional features based on environment
{% set features = environment_features | default([]) %}
enable_logging = {{ 'logging' in features | lower }}
enable_replication = {{ 'replication' in features | lower }}
enable_ssl = {{ 'ssl' in features | lower }}

# Generate comma-separated list of allowed hosts
allowed_hosts = {{ allowed_host_list | join(', ') }}

# Format timestamp
last_updated = {{ ansible_date_time.iso8601 | strftime('%Y-%m-%d %H:%M:%S') }}

Mẫu bao gồm

Chia các mẫu lớn thành các phần có thể quản lý được:

# Main template - templates/nginx-site.conf.j2
server {
{% include 'nginx/listen.j2' %}
{% include 'nginx/server-name.j2' %}
{% include 'nginx/ssl.j2' %}
{% include 'nginx/locations.j2' %}
}

Tạo một phần mẫu:

# templates/nginx/listen.j2
{% if ssl_enabled %}
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
{% else %}
    listen 80;
    listen [::]:80;
{% endif %}
# templates/nginx/ssl.j2
{% if ssl_enabled %}
    ssl_certificate {{ ssl_certificate }};
    ssl_certificate_key {{ ssl_certificate_key }};
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
{% endif %}

Gỡ lỗi mẫu

Thêm thông tin gỡ lỗi vào mẫu trong quá trình phát triển:

{% if debug_mode | default(false) %}
<!-- Debug Information -->
<!-- Generated on: {{ ansible_date_time.iso8601 }} -->
<!-- Target host: {{ inventory_hostname }} -->
<!-- Environment: {{ environment | default('unknown') }} -->
<!-- Variables:
     - ssl_enabled: {{ ssl_enabled | default('not set') }}
     - server_name: {{ server_name | default('not set') }}
     - document_root: {{ document_root | default('not set') }}
-->
{% endif %}

Kỹ thuật sửa đổi tập tin

Sửa đổi từng dòng

Sử dụng lineinfile để thay đổi cấu hình chính xác:

tasks:
  - name: Configure SSH settings
    lineinfile:
      path: /etc/ssh/sshd_config
      regexp: '{{ item.regexp }}'
      line: '{{ item.line }}'
      backup: yes
    loop:
      - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
      - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
      - { regexp: '^#?Port', line: 'Port {{ ssh_port | default(22) }}' }
    notify: restart sshd

  - name: Add custom SSH configuration
    lineinfile:
      path: /etc/ssh/sshd_config
      line: '{{ item }}'
      insertafter: EOF
    loop:
      - '# Custom configuration'
      - 'ClientAliveInterval 300'
      - 'ClientAliveCountMax 2'
    notify: restart sshd

Khối sửa đổi

Sử dụng blockinfile để quản lý các phần cấu hình:

tasks:
  - name: Configure application settings block
    blockinfile:
      path: /etc/myapp/config.ini
      block: |
        # Application settings managed by Ansible
        [performance]
        max_workers = {{ max_workers | default(ansible_processor_vcpus) }}
        memory_limit = {{ memory_limit | default('512M') }}
        cache_enabled = {{ cache_enabled | default(true) | lower }}

        [database]
        host = {{ db_host }}
        port = {{ db_port | default(5432) }}
        pool_size = {{ db_pool_size | default(10) }}

        [logging]
        level = {{ log_level | default('INFO') }}
        file = {{ log_file | default('/var/log/myapp/app.log') }}
      marker: '# {mark} ANSIBLE MANAGED BLOCK - APPLICATION CONFIG'
      backup: yes
    notify: restart application

Sắp xếp mẫu và tập tin

Cấu trúc thư mục

Sắp xếp các mẫu và tệp của bạn một cách hợp lý:

ansible-project/
├── templates/
│   ├── nginx/
│   │   ├── nginx.conf.j2
│   │   ├── site.conf.j2
│   │   └── upstream.conf.j2
│   ├── database/
│   │   ├── postgresql.conf.j2
│   │   └── pg_hba.conf.j2
│   └── application/
│       ├── app.conf.j2
│       └── systemd.service.j2
├── files/
│   ├── ssl/
│   │   ├── server.crt
│   │   └── server.key
│   ├── scripts/
│   │   ├── backup.sh
│   │   └── maintenance.sh
│   └── static/
│       └── maintenance.html

Tổ chức biến mẫu

Tạo các tệp biến tương ứng với mẫu của bạn:

# group_vars/webservers.yml
nginx_config:
  user: www-data
  worker_processes: '{{ ansible_processor_vcpus }}'
  worker_connections: 1024
  keepalive_timeout: 65
  gzip_enabled: true
  ssl_enabled: true

site_config:
  server_name: www.example.com
  document_root: /var/www/html
  php_enabled: true
  php_version: '8.1'

Ví dụ hoàn chỉnh: Cấu hình đa dịch vụ

Dưới đây là ví dụ toàn diện minh họa các mẫu, tệp và handler hoạt động cùng nhau:

---
- name: Configure web application stack
  hosts: webservers
  become: yes

  vars:
    app_name: ecommerce
    app_version: '2.1.0'
    ssl_enabled: true
    php_enabled: true
    cache_enabled: true

  tasks:
    - name: Create application directories
      file:
        path: '{{ item }}'
        state: directory
        owner: www-data
        group: www-data
        mode: '0755'
      loop:
        - /var/www/{{ app_name }}
        - /var/www/{{ app_name }}/logs
        - /var/www/{{ app_name }}/cache

    - name: Deploy SSL certificates
      copy:
        src: 'files/ssl/{{ item }}'
        dest: '/etc/ssl/{{ item }}'
        owner: root
        group: ssl-cert
        mode: "{{ '0640' if 'key' in item else '0644' }}"
      loop:
        - certs/{{ app_name }}.crt
        - private/{{ app_name }}.key
      notify: restart nginx

    - name: Generate nginx main configuration
      template:
        src: nginx/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        backup: yes
      notify: restart nginx

    - name: Generate site configuration
      template:
        src: nginx/site.conf.j2
        dest: '/etc/nginx/sites-available/{{ app_name }}'
        backup: yes
      notify: restart nginx

    - name: Enable site
      file:
        src: '/etc/nginx/sites-available/{{ app_name }}'
        dest: '/etc/nginx/sites-enabled/{{ app_name }}'
        state: link
      notify: restart nginx

    - name: Configure PHP-FPM pool
      template:
        src: php/pool.conf.j2
        dest: '/etc/php/{{ php_version }}/fpm/pool.d/{{ app_name }}.conf'
        backup: yes
      notify: restart php-fpm
      when: php_enabled

    - name: Generate application configuration
      template:
        src: application/config.php.j2
        dest: '/var/www/{{ app_name }}/config.php'
        owner: www-data
        group: www-data
        mode: '0640'

    - name: Deploy maintenance scripts
      copy:
        src: files/scripts/
        dest: /opt/{{ app_name }}/scripts/
        owner: root
        group: root
        mode: '0755'
        directory_mode: '0755'

  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted

    - name: restart php-fpm
      service:
        name: 'php{{ php_version }}-fpm'
        state: restarted

Các bước tiếp theo

Quản lý mẫu và tệp là xương sống của tự động hóa cấu hình. Bạn đã học cách tạo các tệp cấu hình động, triển khai các tài nguyên tĩnh và sắp xếp quá trình tự động hóa của mình để có thể bảo trì.

Trong phần tiếp theo, chúng ta sẽ khám phá các vai trò - Cách tổ chức tự động hóa của Ansible thành các thành phần có thể chia sẻ, tái sử dụng, có thể kết hợp để tạo ra tự động hóa cơ sở hạ tầng phức tạp.

Các kỹ thuật quản lý tệp và mẫu mà bạn đã học ở đây cung cấp nền tảng để tạo tính năng quản lý cấu hình linh hoạt, phù hợp với môi trường, phù hợp với nhu cầu cơ sở hạ tầng của bạn.