While the application can be deployed in any server configuration that supports the application's dependencies, this project includes a Terraform project that you can use to easily and quickly deploy it using Microsoft Azure and its Kubernetes managed service, AKS.
Here is the list of technical dependencies for deploying the Marxan app using these infrastructure resources. Note that these requirements are for this particular deployment strategy, and not dependencies of the Marxan application itself - which can be deployed to other infrastructures.
Before proceeding, be sure you are familiar with all of these tools, as these instructions will skip over the basics, and assume you are conformable using all of them.
- Microsoft Azure
- Terraform v1.3.5
- Docker
- Kubernetes
- Helm
- Azure CLI
- Kubectl
- Github Actions
- Bastion host pattern
- An SSH client capable of establishing SSH Tunnels
- DNS management
- A purchased domain
Of the above, the following need to be set up prior to following the instructions in this document:
- An Azure account with a user with enough permissions to create a Resource Group, Apps and Service Principals, as well as multiple other resources in Azure.
- Azure CLI, Kubectl and an SSH client capable of establishing SSH Tunnels need to be installed locally.
- Access to managing Github Actions Secrets
Azure quotas for things like VMs are, by default, low, meaning you may see your cluster resources not be provisioned (either during deploy time, or later on as part of an autoscaling policy). It's advisable to review and adjust said quotas.
This project has 3 main sections, each of which with a folder named after it. Each of these sections has a Terraform project, that logically depends on their predecessors. There is a 4th component to this architecture, which is handled by Github Actions
Contains basic Azure resources, like a Resource Group or a Storage Account to store the Terraform remote state.
Contains multiple Azure resources needed for running Marxan on an AKS cluster.
These resources include, but are not limited to:
- An Azure Cache for Redis
- Kubernetes node pools
- Multiple VNets, Subnets and networking security rules
- A Bastion host
- An Azure DNS
- An Azure Container Registry to store Docker images.
- Github Actions secrets.
The output values include access data for some of the resources above.
When the 'base' is applied, Terraform will create a couple of service principals and so, az cli logged in user must have privileges to do so. When you don't have the privileges, you will get an error that looks like this: ApplicationsClient.BaseClient.Patch(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges to complete the operation
.
Important note: due to this bug, when running
Terraform commands for the base project, you need to have the GITHUB_OWNER
and GITHUB_TOKEN
environment variables set.
GITHUB_OWNER
: the name of the user/organization that owns the project on Github (vizzuality
,tnc-css
, etc)GITHUB_TOKEN
: a Github Access token
Contains the Kubernetes configuration to run Marxan on the resources created in the previous section, as well as some new resources:
- Kubernetes deployments for the Marxan app components
- Kubernetes secrets, namespaces and ingresses
- HTTPS certificate manager
- PostgreSQL database (as a Helm chart)
Important note: this project has a deep dependency graph that's not fully mapped out, meaning you may need to run terraform apply
multiple times for the resources to be fully provisioned. If you get an error when applying, simply try again, and odds
are more resources will be provisioned. Repeat until you get a successful apply, or until you get recurring error outputs.
Also be aware that, for this project to apply, the Github Actions pipeline for each key branch (staging
and main
)
must successfully run up to the point where images are pushed to the registry - said registry images are needed to deploy
the services on kubernetes, which is done by this plan.
As part of this infrastructure, Github Actions are used to automatically build and push Docker images to Azure ACR, and to redeploy Kubernetes pods once that happens. Said Github Actions depend on specific Github Secrets and Variables, that are listed below for reference.
Secrets and variables listed below are automatically created by the base
Terraform project, and do not need to be created manually. Their value often
depends on the outputs of other Terraform modules, so configuring all these via
Terraform (and avoiding to change them manually within the settings of the
relevant GitHub repository) guarantees that values available to GitHub actions
are always coherent with the state of the terraformed infrastructure.
For example, AKS-related variables depend on settings for the cluster name as well as the hostname of the AKS API server, which is assigned by Azure upon creation of an AKS cluster.
AZURE_CLIENT_ID
: The hostname for the Azure ACT. Get fromBase
'scontainer_registry_client_id
AZURE_SUBSCRIPTION_ID
: The Azure Subscription Id. Get fromBase
'sazure_subscription_id
AZURE_TENANT_ID
: The Azure Tenant Id. Get fromBase
'sazure_tenant_id
BASTION_SSH_PRIVATE_KEY
: The ssh private key to access the bastion host. Get it by connection to the bastion host using SSH, and generating a new public/private SSH key pair.REGISTRY_PASSWORD
: The password to access the Azure. Get fromBase
'scontainer_registry_password
AZURE_AKS_CLUSTER_NAME
: The name of the AKS cluster. Get fromBase
'sk8s_cluster_name
AZURE_AKS_HOST
: The AKS cluster hostname (without port or protocol). Get fromBase
'sk8s_cluster_private_fqdn
AZURE_RESOURCE_GROUP
: The AKS Resource Group name. Specified by you when setting up the infrastructure.BASTION_HOST
: The hostname for the bastion machine. Get fromBase
'sbastion_hostname
BASTION_USER
: By default this will beubuntu
if using the initial user created on bastion host instantiation. It is configurable in case infrastructure admins wish to configure a different user on the bastion host or the default distro user is renamed.REGISTRY_LOGIN_SERVER
: The hostname for the Azure ACR. Get fromBase
'scontainer_registry_hostname
REGISTRY_USERNAME
: The username for the Azure ACR. Get fromBase
'scontainer_registry_client_id
Additional Github Actions Secrets are needed, as required by the frontend application
and used by the corresponding Github workflow that builds
the Frontend app docker image. These secrets are named as <ENV VAR name>_<environment>
so, for example, to account for
the NEXT_PUBLIC_API_URL
frontend env var, both NEXT_PUBLIC_API_URL_STAGING
and NEXT_PUBLIC_API_URL_PRODUCTION
need
to be defined. Note that these values are passed onto the built docker images and used in the actual deployed applications.
The Terraform base
project already accounts for the production
and staging
frontend secrets.
Deploying the included Terraform project is done in steps:
- Terraform
apply
theRemote State
project. - Terraform
apply
theBase
project. - Modify your DNS Registrar configuration to use the just created Azure DNS zone as a name server.
- Configure your local
kubectl
(you can use this) - Configure network access to the AKS cluster and have a tunnel to AKS up and running (more on this below)
- Terraform
apply
theKubernetes
project.
For security reasons, most cloud resources are private, meaning they are attached to a private virtual network, and thus inaccessible from the internet. If you need access to these resources (for example, to configure Kubernetes, either directly or through Terraform), there is a Bastion host available. Through it, you can establish an SSH Tunnel that will set up a local port, that is securely proxied through said bastion, and can reach the desired target host.
Here's an example of how to run said tunnel on linux:
ssh -N -L <local port>:<target resource hostname>:<target resource port> <bastion user>@<bastion hostname>
You can now access the target cloud resource on your local host on the port specified above.
Since access to the Azure AKS cluster is done through HTTPS, we need not only an SSH tunnel, but also a way to match the hostname, so that the certificate can be validated successfully. There are a number of ways to tackle this, but one of them is as follows:
- Modify your hosts file (
/etc/hosts
on linux orC:\Windows\System32\drivers\etc\hosts
on Windows) to resolve the Kubernetes hostname to127.0.0.1
. That is, add127.0.0.1 ********.marxan.privatelink.********.azmk8s.io
to your hosts file. - Modify your
kubectl
configuration file to use a different port when reaching the AKS cluster (append:<port number>
to the cluster hostname). The config file is at~/.kube/config
. - Create an SSH tunnel to that hostname, using the above specified port as your local port.
You should now be able to use kubectl
to access your AKS cluster.
To enable error logging in Windows, export the TF_LOG environment variable to DEBUG
:
$env:TF_LOG="DEBUG"
And to redirect the output in Windows, use this:
$env:TF_LOG_PATH="~\Documents\logs\tf-db-deployment.log"