SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-21

Ansible check mode en --diff gebruiken voor dry runs

Ontdek hoe Ansible check mode en --diff werken tijdens een dry run. Leer waarom sommige modules geen wijzigingen rapporteren en hoe u foutieve aannames in uw playbooks voorkomt.

Wat de check-modus van Ansible doet

De check-modus van Ansible is een dry run: ansible-playbook --check maakt verbinding met elke host in de play, vraagt aan elke module of de huidige status al overeenkomt met de gewenste status, en rapporteert wat er zou veranderen zonder iets weg te schrijven. Voeg --diff toe om ook de inhoud van de bestanden voor en na de wijziging te tonen. Samen beantwoorden ze de vraag die u voor elke echte uitvoering moet stellen: wat gaat er op deze servers veranderen?

De check-modus is geen simulatie van uw playbook. Er is nergens een model van de server aanwezig. Aan elke module wordt simpelweg gevraagd om te kijken in plaats van te schrijven. Een module die alleen-lezen kan antwoorden, rapporteert changed en gaat verder. Een module die niet kan antwoorden, doet niets en rapporteert niets. De documentatie van Ansible vat dit samen in één zin: "Modules die de check-modus niet ondersteunen, rapporteren niets en doen niets." Dat gat is waar een dry run u een onjuist antwoord kan geven; daarom gaat het grootste deel van deze handleiding over dit gat.

Voer de dry run uit: --check en --diff

ansible-playbook -i inventory.ini site.yml --check --diff --limit web1

-C en -D zijn de verkorte vormen van de twee vlaggen. De --limit is bewust gekozen. De diff van één host is leesbaar. De diff van twintig hosts is iets waar u voorbij scrolt.

Vier resultaatwoorden vormen de kern van het hele rapport.

  • ok: [web1] betekent dat de module de status heeft gecontroleerd en dat deze al overeenkomt. Er zou niets veranderen.
  • changed: [web1] betekent dat de module iets zou hebben geschreven. Met --diff tonen de regels erboven wat dat is.
  • skipping: [web1] betekent dat de taak niet is geëvalueerd. Ofwel was een when onwaar, of de module kan niet in check mode draaien.
  • fatal: [web1] betekent dat de taak is mislukt tijdens de controle. Lees het bericht voordat u aanneemt dat het playbook defect is.

--diff print een unified diff voor bestandsmodules, waarbij verwijderde regels worden gemarkeerd met - en toegevoegde regels met +, onder een koptekst waarvan de regels beginnen met --- before en +++ after en het doelpad benoemen. Modules die geen bestanden schrijven, printen hun eigen voor- en na-situatie, dus ansible.builtin.user toont de attributen die zouden wijzigen in plaats van de bestandsinhoud.

Schakel diff permanent in in ansible.cfg zodat u de vlag nooit vergeet:

[diff]
always = true
context = 5

Twee goedkopere controles horen vóór de check mode plaats te vinden. ansible-playbook site.yml --syntax-check parseert de YAML en de play-structuur zonder contact te maken met een host. ansible-playbook site.yml --list-tasks print de taken die zouden worden uitgevoerd; dit is hoe u ontdekt dat een rol waarvan u dacht dat deze getagd was, dat niet is. Geen van beide maakt verbinding, dus beide zijn onmiddellijk klaar.

Check mode zelf maakt wel verbinding. Het opent SSH naar elke host in het patroon en verzamelt feiten, dus een host die down is, zorgt voor het falen van de dry run. Dat is op zichzelf een nuttig signaal, en het is ook de reden waarom bepalen wat een playbook moet doen bij onbereikbare hosts van belang is voordat u een dry run in CI opneemt.

Waarom de check-modus faalt op een nieuwe server

Dit playbook is correct. Voer het uit met --check op een server waar nginx nog niet op staat, en het grootste deel zal falen.

- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present

- name: Write the site config
  ansible.builtin.template:
    src: site.conf.j2
    dest: /etc/nginx/conf.d/site.conf

- name: Start and enable nginx
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true

De taak apt rapporteert changed, en dat klopt: het pakket is afwezig, dus een echte uitvoering zou het installeren. De check-modus heeft het niet geïnstalleerd. De taak template faalt vervolgens, omdat /etc/nginx/conf.d/ niet bestaat op deze host en niets het heeft aangemaakt. De taak service faalt eveneens, omdat er geen nginx-unit is om op te vragen. Geen van deze fouten is een bug in het playbook. De dry run kwam de benodigde status tekort; dit is wat de documentatie bedoelt wanneer wordt gewaarschuwd dat de check-modus geen bruikbare output kan genereren voor een taak waarvan de input afhankelijk is van de wijziging door een voorgaande taak.

De eerlijke versie van de regel is dus: de check-modus is accuraat bij een host die het playbook al heeft geconvergeerd, en geeft ruis bij een nieuwe host. Een --check-run waarbij elke taak ok rapporteert, is een valide uitspraak over een geconvergeerde host, omdat het betekent dat er niets zou veranderen. Op een gloednieuwe host vertelt --check u vooral dat de host nieuw is. Wanneer u uw eerste Ansible playbook tegen een VPS schrijft, verwacht dan dat de eerste dry run een muur van rood laat zien, en beoordeel het playbook op basis van de tweede run.

Waarom command- en shell-taken worden overgeslagen in de check-modus

ansible.builtin.command en ansible.builtin.shell weten niet wat uw commando uitvoert. Er bestaat geen alleen-lezenmethode om een willekeurig binair bestand uit te voeren, dus in de check-modus weigert de module dit uit te voeren. Het taakresultaat bevat skipped: true en de melding Command would have run if not in check mode, en uw uitvoer toont skipping: [web1].

De documentatie van de module noemt de ondersteuning voor de check-modus "gedeeltelijk", en de workaround die wordt genoemd is creates en removes. Geef de taak een creates-pad en de check-modus kan op zijn minst de bestandstest evalueren:

- name: Extract the release bundle
  ansible.builtin.command: /usr/bin/tar xf /tmp/app.tar.gz -C /opt/app
  args:
    creates: /opt/app/bin/app

Als /opt/app/bin/app al bestaat, rapporteert de check-modus Would not run command since '/opt/app/bin/app' exists, wat een valide antwoord is. Als het pad ontbreekt, krijgt u Command would have run if not in check mode, wat eveneens een valide antwoord is. Zonder creates is die taak een leegte in uw dry run.

Het neveneffect is erger dan de leegte. Een overgeslagen taak registreert nog steeds een resultaat, maar het resultaat is een skip-resultaat en het bevat geen stdout-sleutel. De voorwaarde van de volgende taak faalt vervolgens tijdens de evaluatie, met een foutmelding die lijkt op 'dict object' has no attribute 'stdout'. Uw playbook werkt tijdens een echte uitvoering, maar breekt tijdens de dry run; dit is de meest verwarrende fout binnen deze gehele functionaliteit.

check_mode: false, en de enige plek waar dit thuishoort

check_mode: false bij een taak betekent "voer dit echt uit, zelfs onder --check". Dit is de oplossing voor het probleem waarbij commando's worden overgeslagen, en het is alleen veilig bij een taak die gegevens leest.

- name: Read the installed app version
  ansible.builtin.command: /usr/local/bin/app --version
  register: app_version
  check_mode: false
  changed_when: false

Die taak is eerlijk in beide modi. Hij leest een versie en schrijft nooit; changed_when: false voorkomt dat de taak een wijziging rapporteert die niet heeft plaatsgevonden, en check_mode: false zorgt ervoor dat app_version.stdout bestaat tijdens een dry run, zodat de voorwaarden die daarop gebaseerd zijn nog steeds geëvalueerd kunnen worden.

Lees het trefwoord letterlijk voordat u het ergens anders plakt. Een taak met check_mode: false schrijft naar uw servers tijdens ansible-playbook --check. Plaats het op een apt-taak of een template-taak om een dry run er netter uit te laten zien, en uw dry run is niet langer een dry run. Wanneer een schrijvende taak niet veilig kan worden gemaakt, beveilig deze dan in plaats daarvan:

- name: Apply the database migration
  ansible.builtin.command: /usr/local/bin/app migrate --apply
  when: not ansible_check_mode

ansible_check_mode is een magische variabele die Ansible op true zet tijdens een check run. Het omgekeerde trefwoord bestaat ook. check_mode: true dwingt een taak altijd in check mode, zelfs tijdens een echte run, wat het verandert in een drift-probe: registreer het resultaat, en een changed-rapport betekent dat de host niet langer overeenkomt met wat de taak vereist.

Waarom een taak bij elke uitvoering 'changed' rapporteert

Voer de playbook twee keer achter elkaar uit, zonder wijzigingen tussendoor. Elke taak zou bij de tweede uitvoering ok moeten rapporteren. Elke taak die nog steeds changed rapporteert, geeft aan dat er een van de twee volgende situaties aan de hand is: de module kan de status die hij beheert niet inzien, of de input die u aanlevert is niet stabiel. Beide zijn oplosbaar en geen ruis die onderdrukt moet worden.

  • command en shell zonder creates, removes of changed_when rapporteren elke keer changed, omdat de module geen manier heeft om te weten of er iets is gebeurd. Voeg creates toe, of stel changed_when in op basis van een string in de output.
  • ansible.builtin.file met state: touch rapporteert bij ontwerp elke keer changed, omdat het aanraken van een bestand de tijdstempels bijwerkt. Gebruik state: file als u enkel de eigenaar of de rechten wilde instellen.
  • Een template waarvan de gegenereerde output verandert, herschrijft het bestand bij elke uitvoering. Een tijdstempel van ansible_date_time, een aanroep naar now(), of een wachtwoord dat telkens opnieuw wordt gegenereerd, produceren allemaal verschillende bytes, waardoor de module terecht een wijziging rapporteert. Verwijder de variabele waarde uit de template.
  • ansible.builtin.user met password: "{{ pw | password_hash('sha512') }}" verandert bij elke uitvoering, omdat password_hash telkens een willekeurige salt kiest, waardoor de resulterende hash nooit overeenkomt met degene die al in /etc/shadow staat. Geef een expliciete salt mee die is afgeleid van iets stabiels.
  • state: latest op een pakketmodule rapporteert changed zodra er een upgrade beschikbaar is. Dat is correct gedrag. Dit is ook de reden waarom state: latest een playbook oplevert waarvan u het resultaat niet kunt voorspellen. Gebruik state: present en voer upgrades bewust uit.
  • ansible.builtin.unarchive die naar een URL wijst zonder creates haalt het bestand telkens opnieuw op en pakt het opnieuw uit. Geef een creates pad op.

--diff is de snelste manier om het verschil te zien. Als een taak changed aangeeft en de diff toont bytes die verschillen, dan is uw input instabiel. Als de taak changed aangeeft en de diff toont helemaal niets, dan kan de module niet uitdrukken wat er is gewijzigd; dit duidt meestal op een command taak of een schrijfactie die enkel metadata zoals een tijdstempel betreft.

Gebruik niet changed_when: false om een luidruchtige taak stil te krijgen. Dit onderdrukt de rapportage, waardoor notify nooit wordt geactiveerd en de handler die de service herstart nooit draait. Los in plaats daarvan de taak zelf op.

Verklein de impact: --limit, --tags en --step

De check-modus toont u wat er zou veranderen. Deze vlaggen bepalen hoeveel machines tegelijkertijd worden aangepast.

--limit beperkt de uitvoering tot een subset van de inventory. Het accepteert dezelfde patronen als hosts:, dus zowel --limit web1 als --limit 'webservers:!web3' werken. Plaats het patroon tussen aanhalingstekens. Een niet-gequote ! in een interactieve bash-sessie activeert history expansion op het uitroepteken, waardoor uw shell de opdracht herschrijft voordat Ansible deze ontvangt.

Controleer het patroon voordat u het vertrouwt. ansible-playbook site.yml --limit 'webservers:!web3' --list-hosts toont de gematchte hosts en sluit af zonder met een van hen te verbinden. Een patroon dat niets matcht is veilig, omdat Ansible niet terugvalt op de volledige inventory. Het geeft een waarschuwing dat het hostpatroon niet kon worden gematcht en sluit vervolgens af met een foutmelding dat de hosts en --limit met geen enkele host overeenkomen. Weten hoe het inventory-bestand deze groepen definieert maakt een patroon voorspelbaar.

--tags deploy voert alleen de getagde taken uit, en --skip-tags packages voert al het overige uit. --list-tags toont welke tags beschikbaar zijn. Tags zijn nuttig zodra een play te groot wordt om in zijn geheel uit te voeren; dit is tevens een van de redenen voor het opsplitsen van een lang playbook in rollen.

--start-at-task "Write the site config" hervat een mislukte run vanaf een specifieke taak. Gebruik dit voor herstel, maar wees u bewust van de consequenties: alles vóór die taak wordt overgeslagen, inclusief taken die feiten instellen of variabelen registreren die latere taken nodig hebben.

--step vraagt om bevestiging vóór elke taak en wacht op uw antwoord: ja, nee of doorgaan. Dit is traag, maar het is het juiste hulpmiddel wanneer u voor het eerst een destructieve actie uitvoert, omdat u tussen twee taken kunt stoppen in plaats van pas na twintig taken.

Voer de wijziging uit met serial

Standaard voert Ansible elke taak uit op alle hosts in de play voordat de volgende taak wordt gestart. Dat is snel, maar het betekent ook dat een foutieve taak het gehele machinepark in dezelfde seconde bereikt. Tegen de tijd dat u de foutmelding heeft gelezen en op Ctrl-C heeft gedrukt, is de wijziging al overal doorgevoerd.

serial verdeelt de play in batches. De volledige play wordt uitgevoerd op de eerste batch, daarna op de volgende.

- name: Roll out the web tier
  hosts: webservers
  serial: [1, 5, "30%"]
  max_fail_percentage: 0
  tasks:
    - name: Deploy the release
      ansible.builtin.include_role:
        name: webapp

De eerste batch bestaat uit één host. Als deze de wijziging overleeft, bestaat de tweede batch uit vijf hosts en elke daaropvolgende batch uit 30 procent van de hosts in de play. max_fail_percentage: 0 beëindigt de play zodra een host in een batch faalt, waardoor een defecte release stopt bij één machine. any_errors_fatal: true is de bottere variant, die de play voor iedereen beëindigt bij de eerste fout op een host.

Het eerst uitvoeren op één host is geen paranoia; de reden is specifiek. Inventory-groepen raken na verloop van tijd uit sync. Een server die zes maanden na de andere is toegevoegd, draait mogelijk een andere distributie-release, bevat een handmatig geïnstalleerde service of heeft een andere schijfindeling. Het playbook is correct voor de groep, maar onjuist voor die ene host, en een dry run op een geconvergeerde host zal dit niet aan het licht brengen. Het beheren van een vloot Linux-servers is grotendeels de kunst van het vinden van de afwijkende host voordat de wijziging dat doet.

De volgorde voor het uitvoeren van taken

  1. ansible-playbook site.yml --syntax-check detecteert YAML- en structuurfouten zonder dat er een netwerkverbinding nodig is.
  2. ansible-playbook site.yml --limit web1 --list-hosts controleert of uw patroon overeenkomt met wat u verwacht.
  3. ansible-playbook site.yml --limit web1 --check --diff is de testrun. Controleer de diff.
  4. ansible-playbook site.yml --limit web1 --diff past de wijzigingen toe op die specifieke host.
  5. Voer stap 4 opnieuw uit. Alles zou de status ok moeten rapporteren. Alles wat nog steeds changed is, is een taak die moet worden opgelost voordat deze op de rest van het serverpark wordt toegepast.
  6. ansible-playbook site.yml --check --diff over de gehele inventaris geeft nu een zinvol resultaat, omdat de geconvergeerde hosts geen wijzigingen meer rapporteren en wat overblijft het werkelijke verschil is.

Eén waarschuwing met betrekking tot stap 3. --diff print de inhoud van bestanden naar uw terminal en naar uw CI-joblog. Een template die een databasewachtwoord rendert, zal dat wachtwoord dus in het logbestand tonen. Stel diff: false in op die taak om de uitvoer te onderdrukken, of gebruik no_log: true om het volledige resultaat te verbergen. Bewaar de waarde zelf in een versleuteld Ansible Vault-bestand in plaats van in de repository.

FAQ

Wijzigt ansible-playbook --check iets op de server?

Nee, met één uitzondering die u zelf beheert. In de check-modus wordt elke module gevraagd om te rapporteren in plaats van te schrijven; modules die dit niet kunnen, rapporteren niets en voeren niets uit. De uitzondering is het taak-trefwoord check_mode: false, dat afdwingt dat die specifieke taak daadwerkelijk wordt uitgevoerd, zelfs tijdens een --check-run. Doorzoek uw playbooks en rollen op check_mode: false voordat u vertrouwt op een dry run, en bevestig dat elke match een taak is die enkel de status leest.

Wat is het verschil tussen --check en --diff?

--check bepaalt of er daadwerkelijk iets wordt uitgevoerd. --diff bepaalt hoeveel details u ziet. --check op zichzelf geeft aan dat een bestand zou veranderen. --diff op zichzelf past de wijziging toe en toont de regels die zijn gewijzigd. Gebruik ze samen voor een dry run die u daadwerkelijk kunt lezen, en laat --diff ook aanstaan voor echte runs door always = true in te stellen onder [diff] in ansible.cfg.

Waarom rapporteert mijn Ansible-taak bij elke run 'changed'?

Omdat de module de status die hij beheert niet kan inzien, of omdat de waarde die u meegeeft telkens anders is. command en shell rapporteren altijd changed, tenzij u creates of changed_when toevoegt. file met state: touch wijzigt per ontwerp. Een template die een tijdstempel of een vers gegenereerd wachtwoord rendert, produceert bij elke run andere bytes, waardoor het bestand daadwerkelijk wordt overschreven. Voer het playbook twee keer achter elkaar uit: alles wat bij de tweede run nog steeds changed is, is de taak die u moet corrigeren.

Waarom worden mijn command- en shell-taken overgeslagen tijdens een dry run?

Omdat er geen alleen-lezen manier is om een willekeurig commando uit te voeren. In de check-modus stelt de command-module skipped: true in met de melding Command would have run if not in check mode. Voeg creates of removes toe zodat de check-modus in plaats daarvan de bestandstest kan evalueren. Voor een taak die enkel de status leest, stelt u check_mode: false in samen met changed_when: false, zodat het geregistreerde resultaat tijdens de dry run blijft bestaan en de condities die daarop gebaseerd zijn, blijven werken.

Waarom faalt de check-modus op een nieuwe server, maar slaagt deze op een bestaande?

Omdat de check-modus niet de status creëert waar latere taken van afhankelijk zijn. Een dry run op een host zonder nginx rapporteert de installatie als changed en faalt vervolgens op de taak die naar /etc/nginx/conf.d/ schrijft, omdat die map nooit is aangemaakt. Dit is verwacht gedrag. De check-modus is een drift-detector voor hosts die het playbook al hebben geconvergeerd. Het kan geen eerste run valideren. Pas op een nieuwe host het playbook toe op één machine en lees in plaats daarvan de tweede run.

#ansible#check-mode#idempotency#automation#safety