Series: HomeLab
In my last blog post, I discussed how you can install the official NGINX OSS package via script. Today, we’re going to walk through the steps required to leverage Ansible and the NGINX Ansible Collection to easily install, configure, upgrade, and remove NGINX.
This deployment uses Ubuntu and assumes that the user running all the commands is named ubuntu
and has full passwordless sudo
access. You can change this if you want, but that’s going to require some other bits being changed.
In order to run these tests you need to have an Azure account along with Terraform, Ansible, and and Azure CLI. There are a few different ways to go about this. My instructions below show how to do this with asdf, but you can use any package manager you wish. If you’re super hardcore, why not compile from source?
asdf plugin add ansible-base
asdf install ansible-base latest
asdf global ansible-base latest
asdf plugin add terraform
asdf install terraform latest
asdf global terraform latest
asdf plugin add azure-cli
asdf install azure-cli latest
asdf global azure-cli latest
ansible-galaxy collection install ansible.posix community.crypto community.general
ansible-galaxy collection install nginxinc.nginx_core
ansible-galaxy install nginxinc.nginx
asdf global <command> <version>
; if you have not used asdf
for this make sure you can run all your utilities.Log into Azure via az login
, which will setup the token used by Terraform.
Change to ./infrastructure
in the repository.
Edit the variables.tf
file to put your own values in for the location and the resource group prefix. Unless you’re me. If you’re me, you can use the defaults a shown below:
variable "resource_group_location" {
default = "westus"
description = "Location of the resource group."
}
variable "resource_group_name_prefix" {
default = "jay"
description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
}
While still in that directory, initialize the terraform deployment with terraform init
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/tls versions matching "~> 4.0"...
- Finding hashicorp/azurerm versions matching "~> 2.0"...
- Finding hashicorp/random versions matching "~> 3.0"...
- Installing hashicorp/tls v4.0.4...
- Installed hashicorp/tls v4.0.4 (signed by HashiCorp)
- Installing hashicorp/azurerm v2.99.0...
- Installed hashicorp/azurerm v2.99.0 (signed by HashiCorp)
- Installing hashicorp/random v3.4.3...
- Installed hashicorp/random v3.4.3 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
terraform plan
. I’ve hacked this output becuase there is no way you want to see all that here…$ terraform plan -out test.plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
<---------- SNIP SNIP SNIP SNIP ---------->
Terraform will perform the following actions:
Changes to Outputs:
+ nginx_address = (known after apply)
+ nginx_public_ip_address = (known after apply)
+ openssh_private_key = (sensitive value)
+ resource_group_name = (known after apply)
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Saved the plan to: test.plan
To perform exactly these actions, run the following command to apply:
terraform apply "test.plan"
$ terraform apply "test.plan"
random_pet.rg_name: Creating...
tls_private_key.nginx_ssh: Creating...
random_pet.rg_name: Creation complete after 0s [id=jay-inviting-panda]
tls_private_key.nginx_ssh: Creation complete after 1s [id=6fc7234ad9d6678c4e277b4f1a758b6295f71173]
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Creation complete after 0s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda]
azurerm_virtual_network.nginx_network: Creating...
azurerm_public_ip.nginx_public_ip: Creating...
random_id.random_id: Creating...
random_id.random_id: Creation complete after 0s [id=jI6IkdP3RJo]
azurerm_network_security_group.nginx_nsg: Creating...
azurerm_storage_account.nginx_storage_account: Creating...
azurerm_public_ip.nginx_public_ip: Creation complete after 3s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/publicIPAddresses/nginxPublicIP]
azurerm_virtual_network.nginx_network: Creation complete after 5s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/virtualNetworks/nginxVnet]
azurerm_subnet.nginx_subnet: Creating...
azurerm_network_security_group.nginx_nsg: Creation complete after 5s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/networkSecurityGroups/nginxNetworkSecurityGroup]
azurerm_subnet.nginx_subnet: Creation complete after 5s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/virtualNetworks/nginxVnet/subnets/nginxSubnet]
azurerm_network_interface.nginx_nic: Creating...
azurerm_storage_account.nginx_storage_account: Still creating... [10s elapsed]
azurerm_network_interface.nginx_nic: Creation complete after 1s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/networkInterfaces/nginxNIC]
azurerm_network_interface_security_group_association.nginx: Creating...
azurerm_network_interface_security_group_association.nginx: Creation complete after 2s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/networkInterfaces/nginxNIC|/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Network/networkSecurityGroups/nginxNetworkSecurityGroup]
azurerm_storage_account.nginx_storage_account: Still creating... [20s elapsed]
azurerm_storage_account.nginx_storage_account: Creation complete after 24s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Storage/storageAccounts/diag8c8e8891d3f7449a]
azurerm_linux_virtual_machine.nginx: Creating...
azurerm_linux_virtual_machine.nginx: Still creating... [10s elapsed]
azurerm_linux_virtual_machine.nginx: Still creating... [20s elapsed]
azurerm_linux_virtual_machine.nginx: Still creating... [30s elapsed]
azurerm_linux_virtual_machine.nginx: Still creating... [40s elapsed]
azurerm_linux_virtual_machine.nginx: Creation complete after 48s [id=/subscriptions/9bf57fa0-f5be-4dab-8f4b-364e77f7930e/resourceGroups/jay-inviting-panda/providers/Microsoft.Compute/virtualMachines/nginxVM]
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
Outputs:
nginx_address = "10.0.1.4"
nginx_public_ip_address = "20.245.80.251"
openssh_private_key = <sensitive>
resource_group_name = "jay-inviting-panda"
The first step is to generate the necessary config for Ansible; we are going to do that using a convenience script located in the root directory called create-configs.sh
.
$ ./create-configs.sh
Private key written to /Users/jschmidt/repos/nginx-ansible/files/nginx.pem
SSH configuration written
Configuration complete. Instructions for use:
To SSH into the nginx server, add the private key to your SSH agent:
ssh-add /Users/jschmidt/repos/nginx-ansible/files/nginx.pem
Then, use SSH with the config file:
ssh -F /Users/jschmidt/repos/nginx-ansible/files/nginx.ssh.config nginx
To test the Ansible inventory, run:
ansible-playbook -i /Users/jschmidt/repos/nginx-ansible/files/nginx.ansible.hosts /Users/jschmidt/repos/nginx-ansible/ansible/ansible-ping.yaml
Ensure there are no errors for proper configuration.
To install NGINX with ansible, run:
ansible-playbook -i /Users/jschmidt/repos/nginx-ansible/files/nginx.ansible.hosts /Users/jschmidt/repos/nginx-ansible/ansible/deploy-oss.yaml
To uninstall NGINX with ansible, run:
ansible-playbook -i /Users/jschmidt/repos/nginx-ansible/files/nginx.ansible.hosts /Users/jschmidt/repos/nginx-ansible/ansible/uninstall-oss.yaml
To upgrade NGINX with ansible, run:
ansible-playbook -i /Users/jschmidt/repos/nginx-ansible/files/nginx.ansible.hosts /Users/jschmidt/repos/nginx-ansible/ansible/upgrade-oss.yaml
Now load the SSH key using the command supplied in the output:
$ ssh-add /Users/jschmidt/repos/nginx-ansible/files/nginx.pem
Identity added: /Users/jschmidt/repos/nginx-ansible/files/nginx.pem (/Users/jschmidt/repos/nginx-ansible/files/nginx.pem)
Let’s test to make sure everything is where it’s supposed to be. Once again, we can use the command that was supplied in the output.
$ ansible-playbook -i /Users/jschmidt/repos/nginx-ansible/files/nginx.ansible.hosts /Users/jschmidt/repos/nginx-ansible/ansible/ansible-ping.yaml
PLAY [ping module demo] *********************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [nginx]
TASK [test connection] **********************************************************************************
ok: [nginx]
PLAY RECAP **********************************************************************************************
nginx : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now it’s time to install NGINX:
$ ansible-playbook -i /Users/jschmidt/repos/nginx-ansible/files/nginx.ansible.hosts /Users/jschmidt/repos/nginx-ansible/ansible/deploy-oss.yaml
PLAY [Install NGINX OSS] ********************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [nginx]
TASK [Set repo if Alpine] *******************************************************************************
skipping: [nginx]
TASK [Set repo if Debian] *******************************************************************************
ok: [nginx]
TASK [Set repo if Red Hat] ******************************************************************************
skipping: [nginx]
TASK [Set repo if SLES] *********************************************************************************
skipping: [nginx]
TASK [Install NGINX] ************************************************************************************
TASK [nginxinc.nginx : Validate distribution and role variables] ****************************************
included: /Users/jschmidt/.ansible/roles/nginxinc.nginx/tasks/validate/validate.yml for nginx
<--- SNIP SNIP SNIP SNIP --->
TASK [nginxinc.nginx : Install NGINX Amplify] ***********************************************************
skipping: [nginx]
RUNNING HANDLER [nginxinc.nginx : (Handler) Start logrotate] ********************************************
ok: [nginx]
RUNNING HANDLER [nginxinc.nginx : (Handler) Print logrotate error if config check fails] ****************
skipping: [nginx]
PLAY RECAP **********************************************************************************************
nginx : ok=28 changed=8 unreachable=0 failed=0 skipped=29 rescued=0 ignored=0
NGINX is now installed - provided there were no errors.
From this point, you can use NGINX as you would normally. This repository contains Ansible scripts that can be used to upgrade NGINX or uninstall NGINX. If you want to go deeper and do things like installing NGINX Plus, adding NGINX AppProtect, or configuring NGINX from Ansible why not hop over to Ansible Galaxy to review the available roles. Be sure to say hello to the NGINX Ansible Team (aka, Alessandro) when you visit!
If you’re not a fan of cut and paste, I’ve created a very bare-bones repo that hosts the files necessary to run this test at nginx-ansible.