Generating Config Files With Jinja2
Goal
You break a configuration file that differs per environment into one template and data, and make sure the rendered result is put in place clean and validated.
Why it matters
If you keep as many configuration files as there are environments, those files will inevitably diverge. A template removes that divergence with "one frame, many sets of data." What ruins templates in practice is not syntax but two things. One is whitespace — blank lines left by control statements break a YAML configuration. The other is a rendered result that changes every time — because of one timestamp line, the file changes on every run, the handler runs every time, and the service restarts every time. Finally, validate is the cheapest safeguard that blocks the path by which a bad configuration kills the service. Once you turn on automatic deployment, this option is no longer optional.
Steps
- This lab uses a single playbook,
/root/ans/tpl/site.yml. Use/root/ans/tpl/templates/basic.j2to create/root/ans/tpl/out/basic.conf. The result must contain the lineservice = checkout. - Use
/root/ans/tpl/templates/filters.j2to create/root/ans/tpl/out/filters.conf. It must contain three lines:UPPER=CHECKOUT,TIMEOUT=30(the default for an undefined variable), andREPLICAS=3. - Use
/root/ans/tpl/templates/upstream.j2to create/root/ans/tpl/out/upstream.conf. The 3 backends must each appear on its own line in the formserver 10.0.0.11:8080;. - Render the same template with different variables to create
/root/ans/tpl/out/prod.conf(tls = on, no debug) and/root/ans/tpl/out/dev.conf(tls = off, with debug). - Control whitespace so that
upstream.confhas no blank lines at all and no unnecessary indentation before theserverlines. You can use{%- -%}, or turn ontrim_blocks/lstrip_blocksin thetemplatetask of/root/ans/tpl/site.yml. - Use
/root/ans/tpl/templates/node.j2to create/root/ans/tpl/out/node.conf.hostnameandarchcome from facts, andgenerated_forcomes frominventory_hostname(=web1). - Add
validateandbackup: trueto thetemplatetask in/root/ans/tpl/site.ymlto create/root/ans/tpl/out/validated.conf. Its content is a singlekey=valueline. - Create
/root/ans/tpl/out/site.nginx. Inside theupstream checkout_backend {block, put 3serverlines indented by 4 spaces, and thenlisten 8080;andserver_name checkout.labhub.internal;, each indented by 4 spaces. There must be at most 1 blank line.
Notes
- Lab Pods start fresh for every lab. If
/root/ans/inventory/hosts.iniis missing, first recreate the same inventory you made in the first lab (web1, web2, db1,ansible_host=127.0.0.1,ansible_port=2222,ansible_user=root, with web and db in[prod:children]). For the structure, refer to/opt/lab/fixtures/ansible/inventory.sample.ini. - The
templatemodule takes a path relative to the role/playbook insrcand the destination path indest. - You can chain filters:
{{ name | default('unknown') | upper }} - Common mistake 1: leaving
{% for %}as is, so blank lines remain in the output. - Common mistake 2: putting a time or a random value in the template so that the result changes every time. That breaks idempotency.
Render a basic template with a variable
This lab uses a single playbook, /root/ans/tpl/site.yml. Use /root/ans/tpl/templates/basic.j2 to create /root/ans/tpl/out/basic.conf. The result must contain the line service = checkout.
Use {{ 변수 }} (the Korean placeholder stands for the variable name) to print a value. If Jinja2 syntax remains in the result, it was not rendered.
Use the upper and default filters
Use /root/ans/tpl/templates/filters.j2 to create /root/ans/tpl/out/filters.conf. It must contain three lines: UPPER=CHECKOUT, TIMEOUT=30 (the default for an undefined variable), and REPLICAS=3.
Filters are chained with a pipe. Think about which filter gives a default value to an undefined variable.
Add a loop over a list
Use /root/ans/tpl/templates/upstream.j2 to create /root/ans/tpl/out/upstream.conf. The 3 backends must each appear on its own line in the form server 10.0.0.11:8080;.
Put the line to repeat between {% for %} and {% endfor %}. The 3 backends must each become one line.
Emit different configuration depending on the environment
Render the same template with different variables to create /root/ans/tpl/out/prod.conf (tls = on, no debug) and /root/ans/tpl/out/dev.conf (tls = off, with debug).
Render the same template twice with different variables. debug must not appear in prod.
Remove blank lines with whitespace control
Control whitespace so that upstream.conf has no blank lines at all and no unnecessary indentation before the server lines. You can use {%- -%}, or turn on trim_blocks/lstrip_blocks in the template task of /root/ans/tpl/site.yml.
Use {%- / -%} or the trim_blocks/lstrip_blocks of the template module. The result must have no blank lines at all.
Use facts and inventory_hostname
Use /root/ans/tpl/templates/node.j2 to create /root/ans/tpl/out/node.conf. hostname and arch come from facts, and generated_for comes from inventory_hostname (=web1).
Fact variables start with ansible_. The name of the target is inventory_hostname.
Add validate and backup
Add validate and backup: true to the template task in /root/ans/tpl/site.yml to create /root/ans/tpl/out/validated.conf. Its content is a single key=value line.
In validate, the path of a temporary file goes where %s is. If the check fails, the file is not put in place.
Generate a whole nginx configuration from group variables
Create /root/ans/tpl/out/site.nginx. Inside the upstream checkout_backend { block, put 3 server lines indented by 4 spaces, and then listen 8080; and server_name checkout.labhub.internal;, each indented by 4 spaces. There must be at most 1 blank line.
The server lines inside the upstream block are indented by 4 spaces. You need to use whitespace control and a loop together.