|
| 1 | +# Deploy Outpost on Azure with Azure Service Bus |
| 2 | + |
| 3 | +This example demonstrates how to deploy Outpost on Azure, using Azure Service Bus as the message queue. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +Before you begin, ensure you have the following: |
| 8 | + |
| 9 | +* [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) installed. |
| 10 | +* You are logged into your Azure account (`az login`). |
| 11 | + |
| 12 | +## Architecture |
| 13 | + |
| 14 | +This example deploys a distributed architecture on Azure, leveraging managed services for dependencies and Azure Container Apps for the Outpost services. |
| 15 | + |
| 16 | +```mermaid |
| 17 | +graph TD |
| 18 | + subgraph "External Source" |
| 19 | + WebhookSource[Event Source] |
| 20 | + end |
| 21 | +
|
| 22 | + subgraph "Azure Container Apps" |
| 23 | + APIService["api service (Container)"] |
| 24 | + DeliveryService["delivery service (Container)"] |
| 25 | + LogService["log service (Container)"] |
| 26 | + end |
| 27 | +
|
| 28 | + subgraph "Azure Dependencies" |
| 29 | + PostgresDB["PostgreSQL (Log Storage)"] |
| 30 | + ServiceBusDelivery["Azure Service Bus (Delivery Queue)"] |
| 31 | + ServiceBusLog["Azure Service Bus (Log Queue)"] |
| 32 | + RedisCache["Redis (Entity Storage)"] |
| 33 | + end |
| 34 | +
|
| 35 | + subgraph "External Destinations" |
| 36 | + Delivery[Destination] |
| 37 | + end |
| 38 | +
|
| 39 | + WebhookSource -- "HTTPS Request" --> APIService |
| 40 | +
|
| 41 | + APIService -- " " --> PostgresDB |
| 42 | + APIService -- "Stores/Retrieves Session Data" --> RedisCache |
| 43 | + APIService -- "Enqueues Message" --> ServiceBusDelivery |
| 44 | +
|
| 45 | + ServiceBusDelivery -- "Consumes Message" --> DeliveryService |
| 46 | + DeliveryService -- " " --> RedisCache |
| 47 | + DeliveryService -- "Enqueues Log" --> ServiceBusLog |
| 48 | +
|
| 49 | + ServiceBusLog -- "Consumes Log" --> LogService |
| 50 | + LogService -- "Writes Log" --> PostgresDB |
| 51 | +
|
| 52 | + DeliveryService -- "Sends Events" --> Delivery |
| 53 | +``` |
| 54 | + |
| 55 | +### Components |
| 56 | + |
| 57 | +#### Dependencies |
| 58 | +The deployment relies on Azure-managed services for its core dependencies: |
| 59 | +* **PostgreSQL**: Used for persistent log storage (`log storage`). |
| 60 | +* **Redis**: Used for entity storage and caching (`entity storage`). |
| 61 | +* **Azure Service Bus**: Used as the message queue for both the delivery (`delivery queue`) and log (`log queue`) services. |
| 62 | + |
| 63 | +#### Outpost Services |
| 64 | +The Outpost application itself is deployed as three distinct services in Azure Container Apps: |
| 65 | +* **api**: The public-facing API that receives webhooks (`API Service`). |
| 66 | +* **delivery**: A backend service that processes and delivers webhooks from the queue (`Delivery Service`). |
| 67 | +* **log**: A backend service that processes and stores logs (`log service`). |
| 68 | + |
| 69 | +## Scripts |
| 70 | + |
| 71 | +This example includes three main scripts to manage the deployment: |
| 72 | + |
| 73 | +* `dependencies.sh`: Provisions all the necessary Azure resources, including PostgreSQL for storage, Redis for caching, and Azure Service Bus for the message queue. It also configures the required permissions for the services to interact with each other. If a PostgreSQL password is not provided via the `PG_PASS` environment variable, a secure one will be generated automatically. |
| 74 | +* `local-deploy.sh`: Deploys the Outpost services using Docker Compose. It uses the Azure resources provisioned by the `dependencies.sh` script. |
| 75 | +* `diagnostics.sh`: Runs checks to validate deployments. Use `--local` for the Docker deployment or `--azure` for the Azure Container Apps deployment. The script requires a webhook URL for testing, which can be provided via the `--webhook-url` flag or the `WEBHOOK_URL` environment variable. |
| 76 | +* `azure-deploy.sh`: Deploys the Outpost services to Azure Container Apps. |
| 77 | + |
| 78 | +## Deployment Steps using Outpost Locally |
| 79 | + |
| 80 | +To deploy Outpost, you must run the scripts in the following order: |
| 81 | + |
| 82 | +1. **Provision Dependencies:** |
| 83 | + ```bash |
| 84 | + # To use a specific password (optional): |
| 85 | + # export PG_PASS=<YOUR_POSTGRES_PASSWORD> |
| 86 | + ./dependencies.sh |
| 87 | + ``` |
| 88 | + |
| 89 | +2. **Deploy Outpost:** |
| 90 | + ```bash |
| 91 | + ./local-deploy.sh |
| 92 | + ``` |
| 93 | + |
| 94 | +3. **Run Diagnostics:** |
| 95 | + This command specifically targets the local Docker deployment. You will need a public webhook URL for the test. |
| 96 | + ```bash |
| 97 | + export WEBHOOK_URL=<YOUR_PUBLIC_WEBHOOK_URL> |
| 98 | + bash ./diagnostics.sh --local |
| 99 | + ``` |
| 100 | + Alternatively, you can use the `--webhook-url` flag: |
| 101 | + ```bash |
| 102 | + bash ./diagnostics.sh --local --webhook-url <YOUR_PUBLIC_WEBHOOK_URL> |
| 103 | + ``` |
| 104 | + |
| 105 | +## Deploying Outpost to Azure Container Apps |
| 106 | + |
| 107 | +### 1. Deploy with the Deployment Script (Recommended) |
| 108 | + |
| 109 | +1. **Prepare Environment Files:** |
| 110 | + Before deploying, you need the `.env.outpost` and `.env.runtime` files. |
| 111 | + |
| 112 | + * **To provision new dependencies (Recommended):** Run the scripts to generate the files automatically. |
| 113 | + ```bash |
| 114 | + # To use a specific password (optional): |
| 115 | + # export PG_PASS=<YOUR_POSTGRES_PASSWORD> |
| 116 | + ./dependencies.sh |
| 117 | + ./local-deploy.sh |
| 118 | + ``` |
| 119 | + > **Note:** The `local-deploy.sh` script will also start services locally via Docker Compose. You can stop them with `docker-compose down` after the script finishes if you only intend to deploy to ACA. |
| 120 | + |
| 121 | + * **To use existing dependencies:** Create `.env.outpost` and `.env.runtime` manually. Refer to the [Environment Variable Reference](#environment-variable-reference) section for details on the required variables. |
| 122 | + |
| 123 | +2. **Run the Deployment Script:** |
| 124 | + Once the environment files are generated, run the `azure-deploy.sh` script to deploy the Outpost services to Azure Container Apps. |
| 125 | + |
| 126 | + ```bash |
| 127 | + ./azure-deploy.sh |
| 128 | + ``` |
| 129 | + |
| 130 | +### 2. Deploy Manually |
| 131 | + |
| 132 | +These instructions outline how to manually deploy Outpost to Azure Container Apps (ACA). |
| 133 | + |
| 134 | +#### 1. Prepare Environment Files |
| 135 | + |
| 136 | +Before deploying manually, ensure you have the `.env.outpost` and `.env.runtime` files. |
| 137 | + |
| 138 | +* **To provision new dependencies:** Run the scripts to generate the files automatically. |
| 139 | + ```bash |
| 140 | + # To use a specific password (optional): |
| 141 | + # export PG_PASS=<YOUR_POSTGRES_PASSWORD> |
| 142 | + ./dependencies.sh |
| 143 | + ./local-deploy.sh |
| 144 | + ``` |
| 145 | + > **Note:** The `local-deploy.sh` script will also start services locally via Docker Compose. You can stop them with `docker-compose down` after the script finishes if you only intend to deploy to ACA. |
| 146 | + |
| 147 | +* **To use existing dependencies:** Create `.env.outpost` and `.env.runtime` manually. Refer to the [Environment Variable Reference](#environment-variable-reference) section for details on the required variables. |
| 148 | + |
| 149 | +#### 2. Load Environment Variables |
| 150 | + |
| 151 | +Load the environment variables from both `.env.outpost` and `.env.runtime` into your shell session. Sourcing `.env.runtime` last ensures that it can override any common variables. |
| 152 | +```bash |
| 153 | +source .env.outpost && source .env.runtime |
| 154 | +``` |
| 155 | + |
| 156 | +#### 3. Create Azure Container Apps Environment |
| 157 | + |
| 158 | +Create the ACA Environment using the variables loaded in the previous step. |
| 159 | + |
| 160 | +```bash |
| 161 | +az containerapp env create \ |
| 162 | + --name outpost-environment \ |
| 163 | + --resource-group $RESOURCE_GROUP \ |
| 164 | + --location $LOCATION |
| 165 | +``` |
| 166 | + |
| 167 | +#### 4. Deploy Each Container |
| 168 | + |
| 169 | +Deploy each service as a separate container app. First, the environment variables from `.env.runtime` must be formatted into a string that can be passed to the Azure CLI. |
| 170 | + |
| 171 | +```bash |
| 172 | +ENV_VARS_STRING=$(grep -v '^#' .env.runtime | sed 's/^export //g' | sed 's/"//g' | tr '\n' ' ') |
| 173 | +``` |
| 174 | + |
| 175 | +Now, deploy the containers using the created string. |
| 176 | + |
| 177 | +**Deploy `api` service:** |
| 178 | +```bash |
| 179 | +az containerapp create \ |
| 180 | + --name outpost-api \ |
| 181 | + --resource-group $RESOURCE_GROUP \ |
| 182 | + --environment outpost-environment \ |
| 183 | + --image hookdeck/outpost:v0.4.0 \ |
| 184 | + --target-port 3333 \ |
| 185 | + --ingress external \ |
| 186 | + --env-vars "SERVICE=api" $ENV_VARS_STRING |
| 187 | +``` |
| 188 | + |
| 189 | +**Deploy `delivery` service:** |
| 190 | +```bash |
| 191 | +az containerapp create \ |
| 192 | + --name outpost-delivery \ |
| 193 | + --resource-group $RESOURCE_GROUP \ |
| 194 | + --environment outpost-environment \ |
| 195 | + --image hookdeck/outpost:v0.4.0 \ |
| 196 | + --ingress internal \ |
| 197 | + --env-vars "SERVICE=delivery" $ENV_VARS_STRING |
| 198 | +``` |
| 199 | + |
| 200 | +**Deploy `log` service:** |
| 201 | +```bash |
| 202 | +az containerapp create \ |
| 203 | + --name outpost-log \ |
| 204 | + --resource-group $RESOURCE_GROUP \ |
| 205 | + --environment outpost-environment \ |
| 206 | + --image hookdeck/outpost:v0.4.0 \ |
| 207 | + --ingress internal \ |
| 208 | + --env-vars "SERVICE=log" $ENV_VARS_STRING |
| 209 | +``` |
| 210 | + |
| 211 | +### 3. Validate the Deployment |
| 212 | + |
| 213 | +After deploying with either method, you can validate the deployment by running the following script. You will need a public webhook URL for the test. |
| 214 | + |
| 215 | +```bash |
| 216 | +export WEBHOOK_URL=<YOUR_PUBLIC_WEBHOOK_URL> |
| 217 | +bash ./diagnostics.sh --azure |
| 218 | +``` |
| 219 | +Alternatively, you can use the `--webhook-url` flag: |
| 220 | +```bash |
| 221 | +bash ./diagnostics.sh --azure --webhook-url <YOUR_PUBLIC_WEBHOOK_URL> |
| 222 | +``` |
| 223 | + |
| 224 | +## Azure Deployment Options: Overview, Pros & Cons |
| 225 | + |
| 226 | +There are several ways to deploy Docker containers to Azure. Below is a summary of the main options, their advantages, and trade-offs: |
| 227 | + |
| 228 | +### 1. Azure Container Apps (ACA) |
| 229 | + |
| 230 | +**Description:** Managed service for running containerized applications with built-in scaling, ingress, and environment management. Each service is deployed as a separate container app. |
| 231 | +**Pros:** |
| 232 | +- No infrastructure management (serverless experience) |
| 233 | +- Built-in scaling and ingress |
| 234 | +- Easy integration with Azure resources |
| 235 | +- Good fit for microservices and distributed architectures |
| 236 | +**Cons:** |
| 237 | +- Limited support for multi-container orchestration compared to Docker Compose |
| 238 | +- Some advanced networking features may require extra configuration |
| 239 | +- Not suitable for very complex container topologies |
| 240 | + |
| 241 | +### 2. Azure Container Instances (ACI) |
| 242 | + |
| 243 | +**Description:** Directly run containers in Azure without managing VMs. Supports single containers or simple container groups. |
| 244 | +**Pros:** |
| 245 | +- Fast and simple deployment |
| 246 | +- No infrastructure management |
| 247 | +- Good for short-lived or simple workloads |
| 248 | +**Cons:** |
| 249 | +- Limited orchestration (multi-container support is basic) |
| 250 | +- No built-in scaling or ingress |
| 251 | +- Not ideal for production microservices |
| 252 | + |
| 253 | +### 3. Azure Kubernetes Service (AKS) |
| 254 | + |
| 255 | +**Description:** Full Kubernetes orchestration for complex, scalable deployments. Can convert Docker Compose files to Kubernetes manifests using tools like `kompose`. |
| 256 | +**Pros:** |
| 257 | +- Full orchestration and scaling |
| 258 | +- Advanced networking and service discovery |
| 259 | +- Suitable for large-scale, complex applications |
| 260 | +**Cons:** |
| 261 | +- Requires Kubernetes knowledge and management |
| 262 | +- More operational overhead |
| 263 | +- Overkill for simple deployments |
| 264 | + |
| 265 | +### 4. Azure Web App for Containers |
| 266 | + |
| 267 | +**Description:** Deploy container images to Azure App Service. Best for web apps and APIs. |
| 268 | +**Pros:** |
| 269 | +- Simple deployment for web-facing apps |
| 270 | +- Built-in scaling and monitoring |
| 271 | +**Cons:** |
| 272 | +- Limited control over networking and container lifecycle |
| 273 | +- Not suitable for multi-service architectures |
| 274 | + |
| 275 | +## Using Docker Compose for Azure Deployments |
| 276 | + |
| 277 | +Azure does not natively support `docker-compose.yml` for multi-container deployments in the same way as local Docker Compose. You can use conversion tools (e.g., `kompose` for AKS, or limited support for ACI) to generate Azure-compatible deployment files, but manual adjustments are often required. |
| 278 | + |
| 279 | +## Why `azure-deploy.sh` Is a Reasonable Approach |
| 280 | + |
| 281 | +The provided `azure-deploy.sh` script is a practical solution for deploying Outpost to Azure Container Apps because: |
| 282 | + |
| 283 | +- It automates the deployment of multiple services, mirroring the local Docker Compose setup. |
| 284 | +- It leverages environment files generated by your local setup, ensuring consistency between local and cloud deployments. |
| 285 | +- It uses Azure CLI commands, which are well-supported and documented. |
| 286 | +- It validates the deployment and provides immediate feedback (logs, health checks). |
| 287 | +- It aligns with ACA’s architecture by deploying each service as a separate container app, following best practices for microservices. |
| 288 | +- It handles Azure resource provider registration and environment creation, reducing manual setup steps and potential errors. |
| 289 | +- It supports repeatable, scriptable deployments, making it easy to update, redeploy, or share the deployment process with others. |
| 290 | +- It enables troubleshooting and monitoring by fetching logs and checking health endpoints immediately after deployment. |
| 291 | +- It is flexible and can be adapted for different environments or configurations by simply updating environment files or script parameters. |
| 292 | + |
| 293 | +**Summary:** |
| 294 | +For most users, `azure-deploy.sh` offers a balance of automation, reliability, maintainability, and alignment with Azure Container Apps best practices. It is recommended for Outpost deployments unless you require advanced orchestration (AKS) or have very simple workloads (ACI). |
| 295 | + |
| 296 | +**Summary:** |
| 297 | +For most users, `azure-deploy.sh` offers a balance of automation, reliability, and maintainability. It is recommended for Outpost deployments unless you require advanced orchestration (AKS) or have very simple workloads (ACI). |
| 298 | +## Environment Variable Reference |
| 299 | + |
| 300 | +If you are not using the `dependencies.sh` and `local-deploy.sh` scripts to provision your infrastructure, you will need to create the `.env.outpost` and `.env.runtime` files manually. |
| 301 | + |
| 302 | +See the [Configure Azure Service Bus as the Outpost Internal Message Queue](https://outpost.hookdeck.com/docs/guides/service-bus-internal-mq) guide for more details on the environment variables required for Outpost and how to create the values. |
| 303 | + |
| 304 | +### `.env.outpost` |
| 305 | + |
| 306 | +This file contains variables related to the Azure infrastructure where the services will be deployed. |
| 307 | + |
| 308 | +| Variable | Description | Example | |
| 309 | +| --- | --- | --- | |
| 310 | +| `LOCATION` | The Azure region for your resources. | `westeurope` | |
| 311 | +| `RESOURCE_GROUP` | The name of the Azure resource group. | `outpost-azure` | |
| 312 | +| `POSTGRES_URL` | The full connection URL for your PostgreSQL database. | `postgres://user:pass@host:5432/dbname` | |
| 313 | +| `REDIS_HOST` | The hostname of your Redis instance. | `outpost-redis.redis.cache.windows.net` | |
| 314 | +| `REDIS_PORT` | The port for your Redis instance. | `6379` | |
| 315 | +| `REDIS_PASSWORD` | The password for your Redis instance. | `your-redis-password` | |
| 316 | +| `REDIS_DATABASE` | The Redis database number. | `0` | |
| 317 | +| `AZURE_SERVICEBUS_CLIENT_ID` | The Client ID of the service principal for Service Bus access. | `...` | |
| 318 | +| `AZURE_SERVICEBUS_CLIENT_SECRET` | The Client Secret of the service principal. | `...` | |
| 319 | +| `AZURE_SERVICEBUS_SUBSCRIPTION_ID` | Your Azure Subscription ID. | `...` | |
| 320 | +| `AZURE_SERVICEBUS_TENANT_ID` | Your Azure Tenant ID. | `...` | |
| 321 | +| `AZURE_SERVICEBUS_NAMESPACE` | The name of the Service Bus namespace. | `outpost-internal` | |
| 322 | +| `AZURE_SERVICEBUS_RESOURCE_GROUP` | The resource group for the Service Bus. | `outpost-azure` | |
| 323 | +| `AZURE_SERVICEBUS_DELIVERY_TOPIC` | The name of the delivery topic. | `outpost-delivery` | |
| 324 | +| `AZURE_SERVICEBUS_DELIVERY_SUBSCRIPTION` | The name of the delivery subscription. | `outpost-delivery-sub` | |
| 325 | +| `AZURE_SERVICEBUS_LOG_TOPIC` | The name of the log topic. | `outpost-log` | |
| 326 | +| `AZURE_SERVICEBUS_LOG_SUBSCRIPTION` | The name of the log subscription. | `outpost-log-sub` | |
| 327 | + |
| 328 | +### `.env.runtime` |
| 329 | + |
| 330 | +This file contains secrets and runtime configuration for the Outpost services. It includes all variables from `.env.outpost` plus the following application-specific secrets. |
| 331 | + |
| 332 | +| Variable | Description | |
| 333 | +| --- | --- | |
| 334 | +| `API_KEY` | A secret key for securing the Outpost API. | |
| 335 | +| `API_JWT_SECRET` | A secret for signing JWTs. | |
| 336 | +| `AES_ENCRYPTION_SECRET` | A secret for data encryption. | |
0 commit comments