Skip to content

Commit

Permalink
ci(deploy): #39 Add azure-deploy infrastructure (#40)
Browse files Browse the repository at this point in the history
* ci(deploy): #39 Add azure-deploy infrastructure

* ci(deploy): #39 use branch for testing

* feat(app): #39 Change timestamp format

* ci(app): #39 Change container group naming

* build(app): #39 Change file paths

Update file paths to reflect actual directory structure

* Update GECO filepaths

* Inherit log level from envvar

* ci(deploy): #39 Update readonly settting for source files

* do not use branch by default

* Add simple az steps

* fix: Change file name

* fix: inherit secrets in workflow

* fix: update permissions for `id-token`

* Update README

* docs: fix typo

* run workflow in containers

* Add Resource Group input

* Update permissions instructions

* docs: more permissions

* feat: Name deployments to avoid conflict

* Parameterize image tag

* fix: syntax

* Pass only tag, not full image name

* Restore previous timestamp format
  • Loading branch information
AlexAxthelm authored Apr 17, 2024
1 parent 295bb27 commit c786327
Show file tree
Hide file tree
Showing 7 changed files with 417 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,10 @@ jobs:
needs: [build-docker-image]
with:
header: "Docker build status"

run-scenario-prep:
uses: ./.github/workflows/run-scenario-preparation.yml
needs: [build-docker-image]
secrets: inherit
with:
image-tag: ${{ needs.build-docker-image.outputs.full-image-name }}
69 changes: 69 additions & 0 deletions .github/workflows/run-scenario-preparation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
name: Run scenario preparation

# This workflow is based on, but diverges from the example here:
# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners

on:
workflow_call:
inputs:
image-tag:
description: Tag for docker image
required: true
type: string
resource-group:
description: Resource Group to deploy container
required: false
default: RMI-SP-PACTA-PROD
type: string

jobs:
prep:
name: Run workflow
runs-on: ubuntu-latest
strategy:
# fail if any build fails
fail-fast: false
# build amd64 and arm64 in parallel
matrix:
config_active:
- "2022Q4"
- "2023Q4"
# write to packages (ghcr.io)
permissions:
packages: read
contents: read
id-token: write

steps:

- name: Checkout repo
uses: actions/checkout@v4

# https://github.com/Azure/login?tab=readme-ov-file#login-with-openid-connect-oidc-recommended
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

# https://github.com/marketplace/actions/azure-cli-action#workflow-to-execute-an-azure-cli-script-of-a-specific-cli-version
- name: Start Container Instance
uses: azure/CLI@v2
env:
FULL_IMAGE_NAME: ${{ inputs.image-tag }}
R_CONFIG_ACTIVE: ${{ matrix.config_active }}
RESOURCEGROUP: ${{ inputs.resource-group }}
with:
# azcliversion: 2.30.0
inlineScript: |
az --version
IMAGE_TAG=$(echo $FULL_IMAGE_NAME | sed 's/^.*://')
az deployment group create \
--name "workflow.scenario.preparation-$R_CONFIG_ACTIVE" \
--resource-group "$RESOURCEGROUP" \
--template-file azure-deploy.json \
--parameters azure-deploy.rmi-pacta.parameters.json \
--parameters imageTag="$IMAGE_TAG" \
--parameters rConfigActive="$R_CONFIG_ACTIVE"
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,47 @@ Alternatively, you can read in the `.env` file (specified above for the Docker p
```r
readRenviron(".env"); source("main.R")
```

⚠️ When opening a built-in Terminal pane in RStudio, RStudio copies in any environment variables that were available when RStudio starts. That can have the effect of overwriting/ignoring the environment variables in the `.env` file if you try to build/run the Docker container from there.

## Running on Azure Container Instances

A parameter file with the values that the RMI-PACTA team uses for extracting data is available at [`azure-deploy.rmi-pacta.parameters.json`](azure-deploy.rmi-pacta.parameters.json).

```sh
# run from repo root

# change this value as needed.
RESOURCEGROUP="RMI-SP-PACTA-DEV"

# Users with access to the RMI-PACTA Azure subscription can run:
az deployment group create --resource-group "$RESOURCEGROUP" --template-file azure-deploy.json --parameters azure-deploy.rmi-pacta.parameters.json

```

For security, the RMI-PACTA parameters file makes heavy use of extracting secrets from an Azure Key vault, but an example file that passes parameters "in the clear" is available as [`azure-deploy.example.parameters.json`](azure-deploy.example.parameters.json)

Non RMI-PACTA users can define their own parameters and invoke the ARM Template with:

```sh
# Otherwise:
# Prompts for parameters without defaults
az deployment group create --resource-group "$RESOURCEGROUP" --template-file azure-deploy.json

# if you have created your own parameters file:
az deployment group create --resource-group "$RESOURCEGROUP" --template-file azure-deploy.json --parameters @azure-deploy.parameters.json
```

### Preparing GitHub Actions Runner

The GitHub Actions workflow to run this workflow starts an Azure Container Instance.
To prepare the Azure landscape:

1. Create a User Assigned Managed identity for the repo as described [here](https://github.com/marketplace/actions/azure-login#login-with-openid-connect-oidc-recommended)
2. Manually start a container group with `azure-deploy.json` as documented above
3. Grant `Contributor` role on the new Container Group to the Managed Identity
4. Grant `Managed Application Contributor Role` Role to the Managed Identity for the Resource Group in which the Container Group will run
5. Ensure the Managed identity [has deploy permissions](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#grant-deployment-access-to-the-secrets) to the key vault (if needed)
6. Ensure the Managed Identity has the `Managed Identity Operator` Role for the managed idenity used by the container group (specified with the `identity` parameter in the deploy template).

See the [Microsoft documentation](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-github-action?tabs=userlevel) for more information on setting up GH Actions.
236 changes: 236 additions & 0 deletions azure-deploy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "0.0.0.5",

"parameters": {

"containerGroupPrefix": {
"type": "string",
"defaultValue": "workflow.scenario.preparation",
"metadata": {
"description": "The name of the container group."
}
},
"identity": {
"type": "string",
"metadata": {
"description": "The ID of the user assigned identity to use for the container group."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"restartPolicy": {
"type": "string",
"defaultValue": "Never",
"allowedValues": [
"Always",
"Never",
"OnFailure"
],
"metadata": {
"description": "The behavior of Azure runtime if container has stopped."
}
},
"starttime": {
"type": "string",
"defaultValue": "[utcNow()]",
"metadata": {
"description": "The time this template is deployed."
}
},

"imageTag": {
"type": "string",
"defaultValue": "main",
"metadata": {
"description": "Image tag for the loader container."
}
},

"logWorkspaceId": {
"type": "string",
"metadata": {
"description": "The ID for a Log Analytics Workspace."
}
},
"logWorkspaceKey": {
"type": "securestring",
"metadata": {
"description": "The key for a Log Analytics Workspace."
}
},

"storageAccountKeySources": {
"type": "securestring",
"metadata": {
"description": "The storage account key for the storage account for source files."
}
},
"storageAccountNameSources": {
"type": "string",
"metadata": {
"description": "The storage account name for the storage account for source files."
}
},
"storageAccountShareSources": {
"type": "string",
"metadata": {
"description": "The file share name for the source files."
}
},

"storageAccountKeyOutputs": {
"type": "securestring",
"metadata": {
"description": "The storage account key for the storage account for output files."
}
},
"storageAccountNameOutputs": {
"type": "string",
"metadata": {
"description": "The storage account name for the storage account for output files."
}
},
"storageAccountShareOutputs": {
"type": "string",
"metadata": {
"description": "The file share name for the output files."
}
},

"rConfigActive": {
"type": "string",
"allowedValues": [
"2022Q4",
"2023Q4"
],
"metadata": {
"description": "Active configuration from config.yml"
}
}

},

"variables": {

"containerregistry": "ghcr.io/rmi-pacta",
"machineCpuCoresLimit": 1,
"machineCpuCoresRequest": 1,
"machineMemoryInGBLimit": 4,
"machineMemoryInGBRequest": 4,
"mountPathSources": "/mnt/scenario-sources",
"mountPathOutputs": "/mnt/workflow-scenario-preparation-outputs",
"containerGroupName": "[concat(parameters('containerGroupPrefix'), '-', parameters('rConfigActive'))]"

},

"functions": [],

"resources": [

{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2021-09-01",
"name": "[variables('containerGroupName')]",
"location": "[parameters('location')]",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[parameters('identity')]": {}
}
},
"properties": {
"diagnostics": {
"logAnalytics": {
"logType": "ContainerInstanceLogs",
"workspaceId": "[parameters('logWorkspaceId')]",
"workspaceKey": "[parameters('logWorkspaceKey')]"
}
},
"containers": [
{
"name": "scenario-prep-runner",
"properties": {
"image": "[concat(variables('containerregistry'),'/workflow.scenario.preparation:', parameters('imageTag'))]",
"ports": [],
"resources": {
"limits": {
"cpu": "[variables('machineCpuCoresLimit')]",
"memoryInGB": "[variables('machineMemoryInGBLimit')]"
},
"requests": {
"cpu": "[variables('machineCpuCoresRequest')]",
"memoryInGB": "[variables('machineMemoryInGBRequest')]"
}
},
"environmentVariables": [
{
"name": "DEPLOY_START_TIME",
"value": "[parameters('starttime')]"
},
{
"name": "MACHINE_CORES",
"value": "[variables('machineCpuCoresRequest')]"
},
{
"name": "LOG_LEVEL",
"value": "TRACE"
},
{
"name": "SCENARIO_PREPARATION_INPUTS_PATH",
"value": "[variables('mountPathSources')]"
},
{
"name": "SCENARIO_PREPARATION_OUTPUTS_PATH",
"value": "[variables('mountPathOutputs')]"
},
{
"name": "R_CONFIG_ACTIVE",
"value": "[parameters('rConfigActive')]"
}
],
"volumeMounts": [
{
"name": "scenario-sources",
"mountPath": "[variables('mountPathSources')]"
},
{
"name": "scenario-preparation-outputs",
"mountPath": "[variables('mountPathOutputs')]"
}
]
}
}
],
"restartPolicy": "[parameters('restartPolicy')]",
"osType": "Linux",
"volumes": [
{
"name": "scenario-sources",
"azureFile": {
"shareName": "[parameters('storageAccountShareSources')]",
"readOnly": true,
"storageAccountName": "[parameters('storageAccountNameSources')]",
"storageAccountKey": "[parameters('storageAccountKeySources')]"
}
},
{
"name": "scenario-preparation-outputs",
"azureFile": {
"shareName": "[parameters('storageAccountShareOutputs')]",
"readOnly": false,
"storageAccountName": "[parameters('storageAccountNameOutputs')]",
"storageAccountKey": "[parameters('storageAccountKeyOutputs')]"
}
}
]
}
}
],
"outputs": {}
}
Loading

0 comments on commit c786327

Please sign in to comment.