Ansible Lisapraktika
Need täiendavad harjutused on mõeldud neile, kes soovivad süvendada oma Ansible oskusi. Iga harjutus keskendub ühele edasijõudnud tehnikale, mida vajate produktsioonikeskkondades. Harjutused ehitatakse järk-järgult keerulisemaks.
Eeldused: Ansible põhitõed, playbook'ide kirjutamine, muutujad ja template'd
1. Dünaamilised Nginx konfiguratsioonid Jinja2'ga
1.1 Probleem
Labori käigus õppisite looma lihtsaid Jinja2 template'eid, kus asendati mõned muutujad. Aga kui teil on keeruline nginx konfiguratsioon mitme rakendusega, erinevatele keskkondadele, ning vajate tingimuslikku loogikat ja tsükleid? Lihtne muutujate asendamine ei ole piisav.
Reaalses maailmas võib teil olla:
- 5-10 erinevat rakendust (backend services)
- Igal rakendusel 2-5 koopiat (replicas) erinevatele IP-dele
- Erinevad seadistused dev vs production keskkondades
- SSL lubatud ainult production'is
- Health check path'd, mis erinevad rakenduste vahel
Kui kirjutate kõik käsitsi, tekib vigu. Kui kopeerite-kleebitakse, on kood kordav ja raske hooldada. Template'id Jinja2 filtrite ja tsüklitega lahendavad selle elegantsel viisil.
1.2 Lahendus
Jinja2 pakub võimsaid funktsioone, mis muudavad template'd programmeeritavaks. Kasutame filtreid andmete transformeerimiseks, tsükleid korduvate plokide genereerimiseks ja tingimusi keskkonna-põhise konfiguratsiooni jaoks.
Näide dünaamilisest nginx konfiguratsioonist:
# playbooks/advanced_nginx.yml
---
- name: "Deploy dynamic nginx configuration"
hosts: webservers
become: yes
vars:
environment: "production"
enable_ssl: true
ssl_cert: "/etc/ssl/certs/mysite.crt"
ssl_key: "/etc/ssl/private/mysite.key"
backend_apps:
- name: "api"
port: 8080
health_path: "/health"
replicas:
- { ip: "10.0.1.10", weight: 3 }
- { ip: "10.0.1.11", weight: 2 }
- { ip: "10.0.1.12", weight: 1 }
- name: "web"
port: 3000
health_path: "/status"
replicas:
- { ip: "10.0.2.10", weight: 1 }
- { ip: "10.0.2.11", weight: 1 }
tasks:
- name: "Deploy nginx config from template"
template:
src: ../templates/nginx_advanced.conf.j2
dest: /etc/nginx/sites-available/apps.conf
validate: 'nginx -t -c %s'
notify: reload nginx
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
Template fail templates/nginx_advanced.conf.j2:
# Generated by Ansible - DO NOT EDIT MANUALLY
# Environment: {{ environment | upper }}
{% for app in backend_apps %}
# Upstream for {{ app.name }}
upstream {{ app.name }}_backend {
{% for replica in app.replicas %}
server {{ replica.ip }}:{{ app.port }} weight={{ replica.weight | default(1) }};
{% endfor %}
# Health check
check interval=3000 rise=2 fall=3 timeout=1000;
check_http_send "GET {{ app.health_path }} HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
{% endfor %}
# Main server block
server {
listen 80;
server_name {{ inventory_hostname }};
{% if enable_ssl and environment == 'production' %}
# SSL Configuration (production only)
listen 443 ssl http2;
ssl_certificate {{ ssl_cert }};
ssl_certificate_key {{ ssl_key }};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Redirect HTTP to HTTPS
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
{% endif %}
{% for app in backend_apps %}
# Location for {{ app.name }}
location /{{ app.name }}/ {
proxy_pass http://{{ app.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;
{% if environment == 'production' %}
# Production settings
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
{% else %}
# Development settings - longer timeouts for debugging
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
{% endif %}
}
{% endfor %}
# Logging
access_log /var/log/nginx/apps_access.log combined;
error_log /var/log/nginx/apps_error.log {{ 'warn' if environment == 'production' else 'debug' }};
}
Kuidas see töötab:
{% for app in backend_apps %}- tsükkel üle kõigi rakenduste
{{ replica.weight | default(1) }}- filter mis annab vaikeväärtuse kui weight puudub
{% if enable_ssl and environment == 'production' %}- tingimus ainult production'i jaoks
{{ 'warn' if environment == 'production' else 'debug' }}- inline conditional
1.3 Harjutus: Multi-tier rakenduse konfiguratsioon
Looge playbook ja template, mis genereerib nginx konfiguratsiooni kolme keskkonna jaoks (dev, staging, production).
Nõuded:
- Template kasutab vähemalt 3 erinevat filtrit (default, upper, join)
- Tsükkel backend serverite üle, iga serveril erinev weight
- SSL on lubatud ainult staging ja production keskkondades
- Log level varieerub keskkonna järgi (debug/info/warn)
- Health check path on konfigureeritav iga rakenduse jaoks
- Validate nginx config enne rakendamist (validate parameter)
Näpunäiteid:
- Alustage lihtsast template'ist ja lisage järk-järgult keerukust
- Kasutage
ansible-playbook --check --diffet näha genereeritud konfiguratsiooni - Testide nginx config süntaksit:
nginx -t - Filtrite dokumentatsioon: https://jinja.palletsprojects.com/en/3.0.x/templates/
Testimine:
# Käivita playbook
ansible-playbook playbooks/advanced_nginx.yml --check --diff
# Vaata genereeritud konfiguratsiooni
ansible webservers -m shell -a "cat /etc/nginx/sites-available/apps.conf"
# Testi nginx süntaksit
ansible webservers -m shell -a "nginx -t" --become
Boonus:
- Lisage rate limiting production keskkonda
- Genereerige eraldi upstream plokk iga keskkonna jaoks
- Kasutage custom Jinja2 filtrit IP aadresside transformeerimiseks
2. Konfiguratsioonifailide automaatne migratsioon
2.1 Probleem
Tarkvara uuenduste käigus muutuvad sageli konfiguratsioonifailide formaadid. Näiteks:
- Vana parameeter nimetatakse ümber või muutub deprecated
- Uus versioon nõuab uusi kohustuslikke parameetreid
- Vanad vaikeväärtused ei sobi enam
Käsitsi muutmine 50 serveris on aeganõudev ja vigadele kalduv. Kui unustate ühe serveri või teete vea, võib rakendus kokku kukkuda.
Ansible pakub kolm võimsat moodulit failide täpseks muutmiseks:
lineinfile- täpselt ühe rea muutmine/lisamine
blockinfile- mitme rea ploki lisamine
replace- regex-põhine asendamine kogu failis
Kuid kuidas kasutada neid turvaliselt? Kuidas tagada, et muudatused ei riku konfiguratsiooni? Kuidas teha backup enne muutmist?
2.2 Lahendus
Kasutame kombinatsiooni lineinfile, blockinfile ja replace moodulitest koos valideerimise ja backup'iga. Näitame, kuidas migreerida SSH konfiguratsioon turvalisemaks.
# playbooks/config_migration.yml
---
- name: "Migrate SSH configuration to secure settings"
hosts: all
become: yes
tasks:
- name: "Backup original sshd_config"
copy:
src: /etc/ssh/sshd_config
dest: /etc/ssh/sshd_config.backup.{{ ansible_date_time.epoch }}
remote_src: yes
- name: "Disable deprecated protocol 1"
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?Protocol '
line: 'Protocol 2'
validate: '/usr/sbin/sshd -t -f %s'
backup: yes
- name: "Change SSH port from 22 to 2222"
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?Port '
line: 'Port 2222'
validate: '/usr/sbin/sshd -t -f %s'
- name: "Disable password authentication"
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication '
line: 'PasswordAuthentication no'
validate: '/usr/sbin/sshd -t -f %s'
- name: "Add custom security block"
blockinfile:
path: /etc/ssh/sshd_config
marker:
- "# {mark} ANSIBLE MANAGED BLOCK
- Security"
block: |
# Security hardening
PermitRootLogin no
MaxAuthTries 3
MaxSessions 2
ClientAliveInterval 300
ClientAliveCountMax 2
validate: '/usr/sbin/sshd -t -f %s'
- name: "Replace old cipher list"
replace:
path: /etc/ssh/sshd_config
regexp: 'Ciphers aes.*'
replace: 'Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com'
validate: '/usr/sbin/sshd -t -f %s'
- name: "Remove all Match User blocks (deprecated)"
replace:
path: /etc/ssh/sshd_config
regexp: '^Match User.*\n(.*\n)*?(?=^[A-Z]|\Z)'
replace: ''
validate: '/usr/sbin/sshd -t -f %s'
when: ansible_distribution_version is version('20.04', '>=')
- name: "Verify configuration is valid"
command: /usr/sbin/sshd -t
changed_when: false
register: sshd_test
- name: "Show validation result"
debug:
msg: "SSH config is valid!"
when: sshd_test.rc == 0
- name: "Restart SSH service"
service:
name: sshd
state: restarted
when: sshd_test.rc == 0
Olulised punktid:
validateparameeter testib konfiguratsiooni enne salvestamistbackup: yesloob automaatse varukoopiaregexpvõimaldab leida õige rea isegi kui see on kommenteeritudblockinfilelisab ploki ainult üks kord, ei tee duplikaatereplacekasutab regex'i kõigi vastetega asendamiseks
2.3 Harjutus: Apache konfiguratsiooni migratsioon
Looge playbook, mis migrееrib Apache konfiguratsiooni vanast formaadist (Apache 2.2) uuele (Apache 2.4).
Nõuded:
- Asendage
Order allow,deny→Require all granted - Asendage
Allow from all→Require all granted - Lisage uus security plokk (ServerTokens, ServerSignature)
- Muutke deprecated DirectoryIndex direktiivi
- Backup iga konfiguratsiooni faili enne muutmist
- Valideeri Apache config pärast iga muudatust:
apachectl configtest
Näpunäiteid:
- Testige esmalt ühe faili peal, seejärel laiendage kõigile
- Kasutage
--checkrežiimi et näha muudatusi enne rakendamist validateparameeter võib salvestada elu- kasutage seda alati
- Backup failid võite kustutada pärast edukast migratsiooni (eraldi task)
Testimine:
# Kuiv käivitus
ansible-playbook playbooks/apache_migration.yml --check --diff
# Päris käivitus
ansible-playbook playbooks/apache_migration.yml
# Kontrolli Apache konfiguratsiooni
ansible webservers -m shell -a "apachectl configtest" --become
# Vaata backup faile
ansible webservers -m shell -a "ls -la /etc/apache2/*.backup*"
Boonus:
- Lisage rollback funktsioon kui validatsioon ebaõnnestub
- Looge CSV raport kõigist tehtud muudatustest
- Slackisse või emailiga teavitus pärast migratsiooni
3. Deployment error handling ja rollback
3.1 Probleem
Labori käigus käivitasite playbook'e, mis enamasti õnnestusid. Aga produktsioonis läheb asjad valesti:
- Võrk katkeb deployment'i ajal
- Uus versioon ei käivitu
- Database migration ebaõnnestub
- Kettal pole ruumi
Kui deployment ebaõnnestub poolel teel, on teie rakendus katki. Kasutajad ei saa teenust kasutada. Vajate viisi, kuidas:
- Tuvastada vigu kohe
- Automaatselt tagasi rullida eelmisele versioonile
- Säilitada teenuse kättesaadavus
- Logida kõik tegevused
Ansible block/rescue/always konstruktsioon võimaldab kirjutada vastupidavaid playbook'e, mis käsitlevad vigu gracefully.
3.2 Lahendus
Kasutame block/rescue/always struktuuri koos retry loogikaga ja health check'idega. Näitame, kuidas teha zero-downtime deployment'i rollback võimalusega.
# playbooks/safe_deployment.yml
---
- name: "Safe application deployment with rollback"
hosts: webservers
become: yes
vars:
app_name: "myapp"
app_version: "2.0.0"
app_path: "/opt/{{ app_name }}"
backup_path: "{{ app_path }}/backup"
health_url: "http://localhost:8080/health"
tasks:
- name: "Deployment block with error handling"
block:
- name: "Create backup directory"
file:
path: "{{ backup_path }}"
state: directory
mode: '0755'
- name: "Backup current version"
synchronize:
src: "{{ app_path }}/current/"
dest: "{{ backup_path }}/{{ ansible_date_time.epoch }}/"
delegate_to: "{{ inventory_hostname }}"
- name: "Stop application gracefully"
service:
name: "{{ app_name }}"
state: stopped
register: app_stop
failed_when: false
- name: "Download new version"
get_url:
url: "https://releases.example.com/{{ app_name }}-{{ app_version }}.tar.gz"
dest: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
timeout: 30
register: download
until: download is succeeded
retries: 3
delay: 10
- name: "Extract new version"
unarchive:
src: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
dest: "{{ app_path }}/current/"
remote_src: yes
- name: "Run database migrations"
command: "{{ app_path }}/current/bin/migrate.sh"
register: migration
changed_when: migration.stdout | regex_search('Applied [0-9]+ migrations')
- name: "Start application"
service:
name: "{{ app_name }}"
state: started
- name: "Wait for application to start"
wait_for:
port: 8080
delay: 5
timeout: 60
- name: "Health check with retry"
uri:
url: "{{ health_url }}"
status_code: 200
timeout: 5
register: health
until: health.status == 200
retries: 12
delay: 5
- name: "Smoke test critical endpoints"
uri:
url: "http://localhost:8080{{ item }}"
status_code: 200
loop:
- "/api/status"
- "/api/version"
- "/api/db-check"
register: smoke_tests
rescue:
- name: "Deployment failed
- starting rollback"
debug:
msg: "ERROR: Deployment failed. Rolling back to previous version..."
- name: "Stop broken version"
service:
name: "{{ app_name }}"
state: stopped
failed_when: false
- name: "Find latest backup"
find:
paths: "{{ backup_path }}"
file_type: directory
register: backups
- name: "Restore from backup"
synchronize:
src: "{{ (backups.files | sort(attribute='mtime', reverse=true) | first).path }}/"
dest: "{{ app_path }}/current/"
delegate_to: "{{ inventory_hostname }}"
when: backups.matched > 0
- name: "Start application (rollback version)"
service:
name: "{{ app_name }}"
state: started
- name: "Verify rollback health"
uri:
url: "{{ health_url }}"
status_code: 200
register: rollback_health
until: rollback_health.status == 200
retries: 10
delay: 5
- name: "Send failure notification"
debug:
msg: |
DEPLOYMENT FAILED!
Server: {{ inventory_hostname }}
Version: {{ app_version }}
Rolled back successfully.
- name: "Fail playbook after rollback"
fail:
msg: "Deployment failed. System rolled back to previous version."
always:
- name: "Cleanup temp files"
file:
path: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
state: absent
- name: "Log deployment attempt"
lineinfile:
path: "/var/log/{{ app_name }}-deployments.log"
line: "{{ ansible_date_time.iso8601 }} | {{ app_version }} | {{ 'SUCCESS' if health.status == 200 else 'FAILED' }} | {{ inventory_hostname }}"
create: yes
Kuidas see töötab:
block- kõik deployment sammud
rescue- käivitub kui mõni block'i task ebaõnnestub
always- käivitub alati (cleanup, logging)
until/retries/delay- retry loogika health check'ide jaoks
failed_when: false- ei faili kohe, proovi edasi
3.3 Harjutus: Database migration rollback
Looge playbook, mis teeb PostgreSQL schema migration'i koos automaatse rollback'iga.
Nõuded:
- Block: create database backup, apply migration, verify schema
- Rescue: restore from backup, rollback migration
- Always: cleanup temp files, log attempt
- Retry logic health check'ile (3 korda, 10 sek delay)
- Database backup nimega:
db_backup_TIMESTAMP.sql - Kui migration ebaõnnestub, restore backup automaatselt
Näpunäiteid:
- Kasutage
postgresql_dbmoodulit backup'i tegemiseks postgresql_querymoodul migration skriptide käivitamiseks- Kontrollige schema versiooni:
SELECT version FROM schema_migrations - Testige esmalt test andmebaasiga
Testimine:
# Testi kuivas režiimis
ansible-playbook playbooks/db_migration.yml --check
# Testi fail migration'iga (simulatsioon)
ansible-playbook playbooks/db_migration.yml -e "simulate_failure=true"
# Päris migration
ansible-playbook playbooks/db_migration.yml
# Kontrolli backup faile
ansible dbservers -m shell -a "ls -lh /var/backups/postgres/"
Boonus:
- Blue-green deployment: migration uude andmebaasi, seejärel switch
- Parallel execution: backup ja migration erinevates serveritest
- Slack notification: saada teade kui rollback toimus
- Retention policy: kustuta vanad backup'd (üle 7 päeva)
Kasulikud Ressursid
Dokumentatsioon:
Tööriistad:
- ansible-lint
- playbook'ide kvaliteedi kontroll:
pip install ansible-lint - yamllint
- YAML süntaksi kontroll:
pip install yamllint - Ansible Tower
- GUI Ansible'i haldamiseks (kommertsiline)
- AWX
- Ansible Tower tasuta versioon
Näited:
Need harjutused on mõeldud süvendama teie Ansible oskusi. Alustage esimesest ja liikuge järk-järgult keerulisemate poole.