Skip to content

Deployment

During your CoreConnect installation, you will be able to choose a pre-configured infrastructure template to deploy and provision your CoreConnect environment. CoreConnect’s infrastructure templates are based on Pulumi, an open-source infrastructure as code tool that allows you to define, deploy, and manage cloud infrastructure using familiar programming languages. Although you are not required to use these templates, they are recommended for a quick and easy deployment of your CoreConnect environment.

fly.io

Fly.io is a global edge platform that makes it easy to deploy, run, and scale your applications. Unfortunately fly.io does not currently support Pulumi as a deployment method, but we have prepared a guide with some example files to help you easily deploy your CoreConnect application on fly.io.

Technical details

Deploying CoreConnect’s back-end services on fly.io

This guide will help you deploy CoreConnect’s back-end services on fly.io and set up continuous deployment using GitHub Actions in the absence of Pulumi providers. Note that fly.io does not currently support Pulumi as a deployment method, there are some terraform providers available but they are not officially supported by fly.io or compatible with Pulumi.

Prerequisites

This guide assumes that you have already set up your fly.io account and have the fly CLI installed on your local machine. If you haven’t done so, you can follow the instructions on the fly.io documentation.

Our example will go through the steps needed to deploy RabbitMQ, Gateway, and Functions applications on fly.io based on a typical CoreConnect repository structure from the CLI template. If your project is not set up in the same way, you may need to adjust the steps accordingly.

.
+-- .github
| +-- workflows
| | +-- dev-deploy-functions.yml (copied from infrastructure/fly.io/github-workflows)
| | +-- dev-deploy-gateway.yml (copied from infrastructure/fly.io/github-workflows)
+-- backend
| +-- CoreConnect.Gateway
| | +-- fly.toml (copied from infrastructure/fly.io/gateway)
| | +-- Dockerfile (copied from infrastructure/fly.io/gateway)
| +-- CoreConnect.Functions
| | +-- fly.toml (copied from infrastructure/fly.io/functions)
| | +-- Dockerfile (copied from infrastructure/fly.io/functions)
| +-- CoreConnect.sln
+-- frontend
| +-- storefront
+-- infrastructure
| +-- fly.io
| | +-- github-workflows
| | | +-- dev-deploy-functions.yml
| | | +-- dev-deploy-gateway.yml
| | +-- gateway
| | | +-- fly.toml
| | | +-- Dockerfile
| | +-- functions
| | | +-- fly.toml
| | | +-- Dockerfile
| | +-- rabbitMQ
| | | +-- fly.toml
| | | +-- Dockerfile

Deploying RabbitMQ image

Copy the files (Dockerfile, fly.toml and prod.conf) from infrastructure/fly.io/rabbitMQ to the respective application directory and make the necessary changes (if any) to those files.

fly cli

  1. Login in the fly.io
    Terminal window
    fly auth login
  2. Create, configure the rabbit app in fly.io and create the toml file
    Terminal window
    fly launch --no-deploy
  3. Set the rabbitMQ user password secret
    Terminal window
    RABBITMQ_DEFAULT_PASS=[userpassword]
  4. Deploy the rabbitMQ app in fly.io
    Terminal window
    fly deploy

Example of rabbitMQ toml file

# fly.toml app configuration file generated for rabbit-cfb on 2024-05-15T16:41:38+01:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'rabbit-cfb'
primary_region = 'mad'
[build]
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
[env]
RABBITMQ_DEFAULT_USER="admin"
[[services]]
protocol = "tcp"
internal_port = 5672
[[services.tcp_checks]]
interval = "15s"
timeout = "1m0s"
grace_period = "1s"
[[services]]
protocol = "tcp"
internal_port = 15672
[[services.ports]]
port = 15672
handlers = ["tls", "http"]
[[services.tcp_checks]]
interval = "15s"
timeout = "1m0s"
grace_period = "1s"

Miscelaneous

Connect to RabbitMQ management

Create a proxy (rabbit-getsales is the app name):

Terminal window
fly proxy -a rabbit-getsales 15672

Now, the RabbitMQ management can be connected through http://localhost:15672/

Remove the rabbitMQ app

If the app needs to be removed:

Terminal window
fly apps destroy rabbit-getsales

Deploying Gateway and Functions applications

The Gateway and Functions applications are deployed using the same steps. The only difference is the Dockerfile and fly.toml files used for each application. First we will copy the files from the infrastructure/fly.io/gateway and infrastructure/fly.io/functions directories to the respective application directories. You might need to replace the existing Dockerfiles but that’s okay as the original ones are set up for Azure. After the files have been copied, we modify the fly.toml files to match the desired application name and non-sensitive environment variables.

Here’s an example of a fly.toml file for the Gateway application:

# fly.toml app configuration file generated for coreconnect-gateway on 2024-05-13T10:17:57-06:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'coreconnect-gateway'
primary_region = 'ams'
[build]
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
[env]
Logging__LogLevel__Default = "Information"
AllowedHosts = "*"
Plugins = "CoreConnect.Commerce.CommerceTools, CoreConnect.Commerce.GraphQL, CoreConnect.Payments.PayNL, CoreConnect.Queue.RabbitMQ, CoreConnect.SimpleTimer"
PayNL__ServiceLocation = "SL-2047-3902"
PayNL__ApiUrl = "https://rest.pay.nl/"
PayNL__ImagesUrl = "https://raw.githubusercontent.com/paynl/payment-images/7754adb88592eb53e8217f780aba5ba44c0fc8a0/payment_profile_brands/100x100/"
PayNL__TestMode = "true"
PayNL__OrderPaidApiUrl = "https://9f68-2001-8a0-67d7-2e00-6c3b-ad1e-c5e8-68ee.ngrok-free.app/api/payments/paynl"
PayNL__CountrySpecificServiceLocations__0__Locale = "NL"
PayNL__CountrySpecificServiceLocations__0__ServiceLocation = "SL-2047-3902"
PayNL__CountrySpecificServiceLocations__1__Locale = "PT"
PayNL__CountrySpecificServiceLocations__1__ServiceLocation = "SL-5114-8542"
CommerceTools__AuthorizationBaseAddress = "https://auth.europe-west1.gcp.commercetools.com/"
CommerceTools__Scope = "manage_project:rb2-demo"
CommerceTools__ProjectKey = "rb2-demo"
CommerceTools__ApiBaseAddress = "https://api.europe-west1.gcp.commercetools.com/"
TokenValidation__Issuer = "coreconnect"
TokenValidation__Audience = "commercetools"
RabbitMQ__Host = "amqp://admin:[email protected]:5672/"
RabbitMQ__UserName = "admin"

Note the RabbitMQ__Host environment variable, which should be set to the RabbitMQ service (that we deployed on the previous step) internal’s URL.

First deployment

Before deploying the first version of the services’ docker images, we need to create the applications on fly.io. To do this, we open the terminal on the corresponding application directory and run the following command:

Terminal window
fly launch --no-deploy

Follow the instructions on the terminal to create the application making sure to select the desired region and organization. After the application has been created, it’s a good idea to add the sensitive environment variables as secrets to the application. This can be done by either running the following command for each secret:

Terminal window
fly secrets set SECRET_NAME=SECRET_VALUE

Or directly on the fly.io dashboard:

Fly.io secrets

Note that the secrets will only be available to the application after the first deployment.

GitHub Actions for continuous deployment

Once the applications have been created and the secrets have been set, we can set up GitHub Actions to deploy the applications on each push to the main branch. For standard CoreConnect repositories, we have provided example GitHub Actions workflows that can be copied to the .github/workflows directory and run without any modification.

Let’s take a look at the dev-deploy-gateway.yml file:

name: Dev Deploy - Gateway
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "backend/CoreConnect.Gateway/**"
- ".github/workflows/dev-deploy-gateway.yml"
jobs:
deploy:
name: Deploy app
runs-on: ubicloud
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: 'dotnet nuget update source github-rb2 -u unused -p ${{ secrets.GH_PAT }} --store-password-in-clear-text --configfile ./nuget.config'
working-directory: ./backend
env:
GH_PAT: ${{ secrets.GH_PAT }}
- name: Build and Push Docker Image
working-directory: ./backend
run: flyctl deploy --config ./CoreConnect.Gateway/fly.toml --dockerfile ./CoreConnect.Gateway/Dockerfile --wait-timeout "10m"
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

As you can see, the workflow is triggered on each push to the main branch and only runs when changes are made to the Gateway application (including the fly.toml file where the env variables are set) or the workflow file itself. There are 2 mandatory secrets that will be needed for the workflow to run, these are the github Personal Access Token (GH_PAT) with access to the CoreConnect NuGet packages and the Fly.io API token (FLY_API_TOKEN) for the deployment.

Deploying to the production environment

Once you are ready to go live, you will want to create new environments on fly.io for the production/staging applications. To do this you can just create a new toml file for each application (e.g. fly-prod.toml) and a corresponding GitHub Actions workflow file (e.g. prod-deploy-gateway.yml) pointing to this new file and a specific branch (e.g. production). You can now follow the same steps as before to deploy the applications to the new environments, just make sure to specify the correct toml file on your fly launch command (e.g. fly launch --no-deploy --config ./fly-prod.toml).

Notes about the production environment: The provided fly.io configuration files are set up on a basic infrastructure level and should be ok for the dev environment. However, for production, you might want to consider setting up a more robust infrastructure with multiple regions, autoscaling, and other features that fly.io provides. We will provide more information on this in the future.

Azure ContainerApps

By choosing this infrastructure template, you will be able to deploy your CoreConnect environment on Azure using ContainerApps and Azure Service Bus.

Technical details

Core Connect Infrastructure as Code

Infrastructure as Code for Azure Container Apps

The structure of the pulumi project

The index.ts file is the entry point of the pulumi project. Inside this file, you can find the following:

  • First we set up a Azure Resource Group
  • Then we set up a Azure Vault
  • Then we set up a container registiry
  • Then we set up the Azure Service Bus (ASB) using ./setupASB.ts

Prerequisites

Install Pulumi

Install pulumi: https://www.pulumi.com/docs/get-started/install/

  • Install Pulumi CLI on macOS using Homebrew:
Terminal window
brew install pulumi/tap/pulumi

Install Azure CLI

  • Install Azure CLI on macOS using Homebrew:
Terminal window
brew install azure-cli

Authenticate to Azure & Pulumi

  • The Azure CLI’s default authentication method for logins uses a web browser and access token to sign in
Terminal window
az login
  • Set the default subscription for all Azure operations
Terminal window
az account set -s 'rb2 sandbox'
  • Manage your Pulumi stacks by logging in.
Terminal window
pulumi login
  • Set the default destination org for all stack operations
Terminal window
pulumi org set-default rb2
  • List stacks
Terminal window
pulumi stack ls
  • Choose a stack, or create a new one
Terminal window
pulumi stack <create a new stack>

e.g. pulumi stack rb2/dev


How to provision the infrastructure

  • To create or update the stack, run pulumi up and select yes to perform the deployment. Make sure you’re using the correct stack and have the right Azure subscription selected.
Terminal window
pulumi up

How to destroy the infrastructure

⚠️ Be very cautious here, this action will destroy all resources

  • Run pulumi stack ls to list all the stacks inside the pulumi state (cloud)
  • Run pulumi stack select <stack-name> to select the stack you want to destroy
  • Run pulumi destroy to destroy the infrastructure of the selected stack before removing the stack from Pulumi Cloud

How to remove the Pulumi Stack from the Pulumi Cloud

⚠️ Be very cautious here, this will remove the stack from the pulumi state (cloud) and you will not be able to recover it withouth a Pulumi Enterprise license.

  • Run pulumi stack ls to list all the stacks inside the pulumi state (cloud)
Terminal window
pulumi stack ls
  • Run pulumi stack select <stack-name> to select the stack you want to remove
Terminal window
pulumi stack select <stack-name>
  • Run pulumi stack rm to remove the stack from the pulumi state (cloud)
Terminal window
pulumi stack rm

Storing secrets inside the pulumi config

To store secrets inside the pulumi config, you need to use the --secret flag. For storing secrets in the nested object, see below:

How to store secrets inside nested objects of the pulumi config

Normally, you can just follow the basic docs to store secrets. But when storing secrets inside a nested config, you need to do the following:

Use the following command for example to store a encrypted secret inside the config:

Terminal window
pulumi config set --secret --path --secret commerce-platform:databaseConfig.administratorLoginPassword VERY_SECRET_VALUE

This will create the following inside the Pulumi.dev.yaml file:

config:
commerce-platform:databaseConfig:
administratorLoginPassword:
secure: <some encrypted value>

To access this value inside the code:

interface DatabaseConfig {
administratorLoginPassword: any;
}
const globalConfig = new pulumi.Config();
const databaseConfig: DatabaseConfig =
globalConfig.requireObject("databaseConfig");
const administratorLoginPassword = pulumi.secret(
databaseConfig.administratorLoginPassword
);

Relevant docs