Build an in-house collection and ship it with the version pinned
Goal
You learn to run one full cycle yourself: bundle several roles into one collection and give it a version, install the built bundle and call it by FQCN, and lock the installed version with a pinning file.
Why it matters
A role is a unit of reuse, and a collection is a unit of distribution. As teams grow, the problem shifts from "how do I use this role?" to "which version of the role is running on that server right now?" Collections introduced three things to answer that question. They write the origin in the name (FQCN), they attach a version to the bundle (SemVer), and they write the version to install in a file (requirements.yml). Without these three, the order of the search path decides what gets executed, and that order is decided not by people but by the environment. Add role argument specs on top, and wrong values are blocked before a task runs — this is something you should have in place before documentation when you send out code that others will use. This lab goes through that cycle using only a local bundle, with no internet.
Steps
- Use
ansible-galaxy collection initto create the skeleton of theacme.platformcollection under/root/ans/coll/src. Also create an inventory listing web1, web2, and db1 in/root/ans/inventory/hosts.ini(ansible_host=127.0.0.1,ansible_port=2222,ansible_user=root). - Set the
versionin/root/ans/coll/src/acme/platform/galaxy.ymlto1.2.0, fill indescription,authors, andrepositorywith in-house values, and putMITinlicenseandinfrastructureandlinuxintags. Also writerequires_ansibleas a version range inmeta/runtime.ymlof the same collection. - Create a role in
roles/motd/inside the collection. Putmotd_banner(defaultacme-platform),motd_owner(defaultplatform), andmotd_path(default/root/ans/coll/out/motd.txt) indefaults/main.yml, and filltasks/main.ymlwith a single named task that writes two lines,banner=<motd_banner>andowner=<motd_owner>, tomotd_path. Call the module by its FQCN. - Create
roles/motd/meta/argument_specs.ymland write ashort_descriptionand the three arguments for themainentry point. All three must betype: strand have adescription, and formotd_ownerallow only the two valuesplatformandsrethroughchoices. - Build the collection to create
/root/ans/coll/dist/acme-platform-1.2.0.tar.gz, and check that the bundle containsMANIFEST.json,FILES.json, and the role files. - Install the bundle you made into
/root/ans/coll/collections, and check thatacme.platformshows up as 1.2.0 from that path and that the arguments of theacme.platform.motdrole can be looked up withansible-doc. - Create
/root/ans/coll/use.ymlthat calls theacme.platform.motdrole by FQCN, passingmotd_bannerasacme-platform in productionandmotd_ownerassre. Run it againstweb1of the inventory to produce/root/ans/coll/out/motd.txt. - Raise the
galaxy.ymlversion of the source to1.3.0and build again (you now have two bundles), then pin the1.2.0bundle astype: filein/root/ans/coll/requirements.ymland install from that file. Also leave the installed version on one line in/root/ans/coll/out/pinned.txt.
Notes
- This Pod has no internet. Downloading from Galaxy and automatic resolution of dependencies in galaxy.yml cannot be reproduced in this environment, so they were left out of the lab. init, build, and installing a local bundle all work normally.
- Hundreds of collections from the distribution are already installed. When you look at the list, give both the name and the path, as in
ansible-galaxy collection list acme.platform -p <경로>(put the path in the placeholder). - When you install with
-p, a warning appears saying "it may be a location managed by pip." The installation is fine. - Common mistake: installing but not giving the search path when running. The place you installed to and the place you look at when running must be the same.
- Common mistake: writing the version in galaxy.yml with two parts. The build rejects it right there.
- Collections Guide · Collection structure · Distributing collections · Installing collections
Create the collection skeleton
Use ansible-galaxy collection init to create the skeleton of the acme.platform collection under /root/ans/coll/src. Also create an inventory listing web1, web2, and db1 in /root/ans/inventory/hosts.ini (ansible_host=127.0.0.1, ansible_port=2222, ansible_user=root).
Give the collection name as a single unit, <네임스페이스>.<이름> (namespace and name). Specify where to create it with --init-path, and the skeleton is created under it in two levels, namespace/name.
Fill in galaxy.yml and pin the minimum ansible version
Set the version in /root/ans/coll/src/acme/platform/galaxy.yml to 1.2.0, fill in description, authors, and repository with in-house values, and put MIT in license and infrastructure and linux in tags. Also write requires_ansible as a version range in meta/runtime.yml of the same collection.
If the example phrases the skeleton put in (your name, your collection description) remain, the build succeeds but the result cannot be used as a deliverable. runtime.yml is almost all comments, so it is faster to write just the keys you need from scratch.
Put a role inside the collection
Create a role in roles/motd/ inside the collection. Put motd_banner (default acme-platform), motd_owner (default platform), and motd_path (default /root/ans/coll/out/motd.txt) in defaults/main.yml, and fill tasks/main.yml with a single named task that writes two lines, banner=<motd_banner> and owner=<motd_owner>, to motd_path. Call the module by its FQCN.
The directory conventions for a role inside a collection are exactly the same as for an existing role. If you hard-code the values the role cannot be reused, so take all three values as variables. To write the two lines at once, pass a multi-line string (|) as the content.
Block wrong values with an argument spec
Create roles/motd/meta/argument_specs.yml and write a short_description and the three arguments for the main entry point. All three must be type: str and have a description, and for motd_owner allow only the two values platform and sre through choices.
A spec is validation, not documentation. Call the role with a value that is not in the list and check for yourself that it stops before the first task runs.
Build a bundle and check its contents
Build the collection to create /root/ans/coll/dist/acme-platform-1.2.0.tar.gz, and check that the bundle contains MANIFEST.json, FILES.json, and the role files.
Run the build inside the collection source directory. Specify where to put the result with --output-path. To see inside the bundle, you can just list it without extracting it.
Install to a local path and verify with the docs
Install the bundle you made into /root/ans/coll/collections, and check that acme.platform shows up as 1.2.0 from that path and that the arguments of the acme.platform.motd role can be looked up with ansible-doc.
Give the install path with -p. When you look things up, you have to make ansible see that path, and there is a separate environment variable that sets the search path. Hundreds of collections from the distribution are installed, so specify a name when you view the list.
Call and run it by FQCN
Create /root/ans/coll/use.yml that calls the acme.platform.motd role by FQCN, passing motd_banner as acme-platform in production and motd_owner as sre. Run it against web1 of the inventory to produce /root/ans/coll/out/motd.txt.
Values you pass when calling a role win over the role's defaults. If it says the collection cannot be found, check the search path first — the place you installed to and the place you look at when running must be the same.
Pin the installed version even when the source moves ahead
Raise the galaxy.yml version of the source to 1.3.0 and build again (you now have two bundles), then pin the 1.2.0 bundle as type: file in /root/ans/coll/requirements.yml and install from that file. Also leave the installed version on one line in /root/ans/coll/out/pinned.txt.
To point to a local bundle file, write the path in the name and state the type separately. Do not write the installed version by hand; extract it from the output of collection list.