Production server provision configures Staging envs if hosts are the same - Bug or Intention?

I’m not sure if this is my misunderstanding of host files or whether this is a bug. Here’s my situation:

In trellis/hosts/ :
Staging

[staging]
195.50.84.246

[web]
195.50.84.246

Production

[production]
195.50.84.246

[web]
195.50.84.246

The above configuration would assume that both staging and production are on the same server. With the latest HEAD version of Trellis, if I run a server provision for production using: ansible-playbook server.yml -e env=production

The above actually provisions the server for staging and not production. I.e. all my staging yaml configs are loaded and not my production files.

All other files are configured correctly as when I remove the IP decleration from the staging host file and run ansible-playbook server.yml -e env=production the production server is configured as expected using the production yaml files.

Is this expected behaviour?

@craigpearson Thank you for reporting this! It is definitely not the intention that Trellis will load staging information when you specify env=production. It’s an ‘unexpected behavior’ resulting from an unexpected usage of including your host 195.50.84.246 in multiple environment groups (in [staging] and [production] simultaneously). Ansible sees multiple groups for the host and picks one; not always the one you want.

Your unexpected usage is to have both staging and production on the same server. I realize there may be some cases/arguments for it, but generally the recommendation is to put them on separate servers. The $5/month DigitalOcean droplets make it feasible. I like having the freedom to mess with a site’s staging server without constraints. I wouldn’t have this freedom and full ability to test things if the server were serving the production site. I couldn’t reprovision, reboot, rebuild as much as I like to do.

Thinking about Ansible internals, I’m not sure we can just make this issue silently fix itself in the background. However, I’m pretty sure we can detect that a host is in multiple environment groups, halt the playbook, and present a message suggesting a couple options. You can choose one of these options.

Option 1 - Adjust host files, use same commands
I’d recommend this option. Adjust your hosts files like this:

# hosts/staging
my_staging_hostname ansible_host=195.50.84.246

[staging]
my_staging_hostname

[web]
my_staging_hostname

(Edit in file below, suggested by @dutchmichael here: [staging] should be [production])

# hosts/production
my_production_hostname ansible_host=195.50.84.246

[production]
my_production_hostname

[web]
my_production_hostname

You can edit my_staging_hostname and my_production_hostname to be whatever you want, so long as it is not an IP and the name is consistent across its three appearances in any given host file. This resolves the problem by assigning a hostname that differs between the [staging] group and the [production] group, even though the underlying ansible_host parameter is the same.

Then you can run ansible-playbook commands like normal.

Option 2 - Adjust your commands
As you know, the default inventory Trellis loads is the entire hosts directory. You can override this and make it load just the hosts/production file by adding the -i option (inventory file) to your command:

ansible-playbook server.yml -i hosts/production -e env=production

or

ansible-playbook server.yml -i hosts/staging -e env=staging
11 Likes

Thanks a lot for getting back to me on this, I really appreciate the time you’ve taken to explain not only the issue but also two solutions. Although my gift may not amount to much… here’s some virtual beers :beer: :beer:

I ran into the issue on a dreaded rushed job really, I wouldn’t normally attempt to have a staging environment and production environment on the same server. There was a bit of cross over on content population and database migration issues so I was looking at this setup as an interim “fix” in the project. Your solutions worked and you saved my bacon!

2 Likes

Hi @craigpearson

Which options works for you 1 or 2?

Thank you

Hi @fullyint,

Not sure to understand what you mean here: [quote=“fullyint, post:2, topic:6090”]
Option 2 - Adjust your commands As you know, the default inventory Trellis loads is the entire hosts directory.
[/quote]

Would you mean the files loads for staging and production if we didn’t add -i param even if we only choose env=staging?

I am looking to put the two environments on same vm instance: dev.site.com into a dev directory and site.com. to another dir.

Is the 1rst option you suggest will do that?

@RiFi2k Did you try that setup?

Thank you

@jecko

Background

Ansible uses your local “control machine” to run commands on other machines. The other machines can be a Vagrant VM or remote servers (e.g., at DigitalOcean). When you run an Ansible command on your local control machine, you must inform Ansible which machines (servers, “hosts”) should be controlled. This involves two steps.

1. Specify hosts. Trellis follows a common Ansible practice of specifying the “hosts” (machines, servers) affected in the playbook hosts parameter.

2. Specify inventory file(s) containing machine info (aka “hosts” file). You inform Ansible about the machines to be controlled by listing them in an “inventory” (aka “hosts”) file. You can have more than one inventory file. Trellis lists its inventory files in the hosts directory.

Ansible needs to know which inventory file(s) to use any time you run a playbook or command. The Trellis default instructs Ansible to read all the inventory files in the hosts directory.

The Trellis inventory files group machines by environment, with [group_name_in_brackets]. This group membership determines the variables (group_vars/environment) used for the machine when you run a playbook. The recommended Trellis setup is for any given machine to be in only one environment group.

Although the Trellis default is to make Ansible aware of machines in all environments by loading all files in the hosts dir, a machine’s membership in only one environment group ensures that the right group_vars will be used for that machine.

The original issue discussed in this thread was that a machine/server was listed in multiple environment groups and thus the wrong set of group_vars was being applied on occasion.

To get around the problem, option 1 avoids the problem by assigning a single machine/IP a different nickname to use in the different environment groups. The unique name per group is the solution.

In option 2, the -i (inventory) option was added to the command to specify only the inventory file for a given environment, overriding the Trellis default of reading all inventory files. Then Ansible doesn’t even know that a machine/IP is in any other environment groups and has no trouble picking the correct group_vars.

Your questions

Unless you specify -i all inventory files in hosts will load. This is fine and would even be desirable for playbooks that need to coordinate between servers in multiple environments. It is only a problem if a machine is listed in multiple environment groups, as discussed above.

When you include the extra-var env=staging, you are instructing Ansible to affect the machine(s) in the staging group. If that machine is also in the production group, Ansible will have to pick between the group_vars for production and staging and may not pick the one you expect. Using -i to only let Ansible know about the machine’s membership in staging is just one of the two options for getting around the problem.

Try using the standard example.com site key in production, but changing the staging site key to staging.example.com (also in vault.yml). Then, you’ll find your two different sites/envs on your server in separate directories:

srv/
  www/
    example.com/
    staging.example.com/

Also see this thread for notes on challenges of putting staging and production on the same server.

Finally, with all that said, I simply would not recommend doing any of this. I recommend that you do not put staging and production on the same server. See earlier comments in this thread for why not, and here and elsewhere on Roots discourse.

11 Likes

This is by far the most comprehensive answer I’ve ever read! This is getting bookmarked!

2 Likes

Thank you for this great, detailed, complete answer, and for your time, I really appreciate.

1 Like

For future reference, this also applies to web hosts that give an account one IP address, but individual stages are given separate port numbers to SSH into. For example, I’m using Trellis locally and Kinsta to host a client site. Each stage in Kinsta uses the same IP address, but different port number. Creating a custom host at the top of the hosts file like @fullyint demonstrated is the proper solution to this!

1 Like

Need to change [staging] to [production] in option 1 - Adjust your host file like this.

1 Like