Zaprojektowaliśmy i zaimplementowaliśmy platformę, która umożliwia stworzenie cyfrowego banku od podstaw, a także może pomóc istniejącym bankom wprowadzić innowacje z wybranych wertykałach. banqUP jest przykładem stworzenia banku SME, który jest wertykalnie/horyzontalnie-inteligentny. To nasze własny przykład użycia dla platformy.

Kluczowe technologie:

Zespół projektowy

  • Tomasz Wiśniewski - Technical Evangelist, Microsoft
  • Dariusz Porowski - Senior Technical Evangelist, Microsoft
  • Krzysztof Pulkiewicz - Chief Executive Officer, banqUP
  • Anna Ciesielska - Board Member, banqUP
  • Łukasz Chmielewski - Chief Technology Officer, banqUP
  • Natalia Zienkowicz - Project Manager, banqUP
  • Bartłomiej Zalewski - Software Developer, banqUP

Obrazek 1. Członkowie warsztatów

Członkowie warsztatów

Profil firmy

banqUP jest cyfrowym bankiem fintech nakierowanym na SME. Dostarczamy jedno miejsce dla kombinacji najważniejszych produktów bankowych (konta biznesowe, MasterCard) i zintegrowane usługi fintech. banqUP to międzynarodowy zespół złożony z konsultantów oraz przedsiębiorców z doświadczeniem w cyfrowej bankowości, finctech i architekturze IT. Misją banqUP jest przekształcenie tego jak wygląda bankowość SME i usługi finansowe na terenie Europy. http://banqup.com/ banqUP jest przykładem stworzenia banku SME, który jest wertykalnie/horyzontalnie-inteligentny.

Zarys problemu

banqUP posiadał wiele problematycznych rejonów, które chciał zaadresować za pomoca Azure i innych technologii:

  • Usprawnienie procesu developerskiego poprzez automatyzacje kompilacji oraz wdrażania aplikacji z jednego centralnego miejsca a nie z komputerów developerów
  • Użycie Azure Web App do hostowania frontendu swojej aplikacji, co daje skalowalność, bezpieczeństwo i możliwość wdrażania do różnych regionów na świecie
  • Aplikacja posiada i będzie posiadała konektory do wielu serwisów i dodatkowe funkcjonalności co sprawia iż Azure Functions lub Azure Web Jobs sa idealnym kandydatek dla tego komponentu. W zależności od wymagań konektora jedna badź druga technologia może zostać wybrana.

Rozwiązanie, kroki oraz dostarczenie

Kroki podjęte w celu dostarczenia rozwiązania korespondują bezpośrednio z tym co zostało opisane powyżej i były następujące:

  • Architektura
  • Value Stream Mapping
  • Stworzenie procesu kompilacji i wdrożenia dla Azure Functions
  • Wykorzystanie Azure Web Apps dla frontendu
  • Monitorowanie aplikacji za pomocą Application Insights

Architektura

Prace rozpoczęliśmy od dyskusji na temat istniejącej architektury. Zespół banqUP opisuje jak działa ich rozwiązanie, jakie komponenty są wykorzystywane w platformie oraz jak wszystko jest ze sobą połączone.

Obrazek 2. Architektura

Architecture

Ta aktywność była bardzo przydatna dla każdego z obecnych na warsztatach, ponieważ wszyscy wiedzieli dokładnie jak wygląda proces - wlicząjac w to członków banqUP.

Value Stream Mapping

Po przekazaniu wiedzy na temat architektury dokonaliśmy zadania zwanego Value Stream Mapping (VSM) dla obecnego procesu developmentu oraz wdrażania aplikacji.

Obrazek 3. Value Stream Mapping

Value Stream Mapping

Proces developerki w banqUP nie był zdefiniowany w żaden konkretny sposób. Dlatego VSM było bardzo trudnym ćwiczeniem do wykonania. W tym ćwiczeniu znaleźliśmy kilka punktów straty i postanowiliśmy skupić się na jednym z najważniejszych problemów dla zespołu banqUP - CI/CD dla Azure Functions. Podsumowując zdecydowaliśmy się zaimplementować kilka praktyk DevOps:

  • Infrastructure as Code (IaC) dla zarzadzania infrastruktura w deklaratywny sposób, używajac tego samego systemu wersjonowania, które używa zespół developerski.
  • Continous Integration (CI) dla automatyzacji procesu kompilacji i testowania kodu za każdym razem kiedy członek zespołu wgra zmiany do repozytorium.
  • Continous Deployment (CD) dla każdej kompilacji konfiguracja i wdrożenie tej kompilacji na różne środowiska.

Proces kompilacji i wdrażania dla Azure Functions

Zespół banqUP tworzył Azure Functions w portalu App Service Editor bezpośrednio przez każdego developera bez przechowywania go w repozytorium kodu. Po stworzeniu jakiejś funkcji w środowisku developerskim, programista kopiował i wklejał nowy kod do drugiego środowiska. Aby uniknać sytuacji ręcznych zadań i niezarządzanego procesu przygotowaliśmy całe rozwiązanie używąjac kilku praktyk DevOps, takich jak Continous Integration, Continous Deployment oraz Infrastructure as Code.

Continuous Integration

Proces kompilacji dla CI nie był zbyt skomplikowany, a poniżej znajduj się kroki oraz szczegóły.

Obrazek 4. VSTS Build - Tasks

VSTS Build - Tasks

  1. Pobierz źródła z repozytorium Git
    Task: Get sources
    Title: Get sources
    Repository: banqUP
    Branch: master
    
  2. Skopiuj wszystkie szablony ARM Templates
    Task: Copy Files
    Display name: Prepare ARM Templates
    Source Folder: AzureFunctions/AzureResourceGroup1/Templates
    Contents: *.json
    Target Folder: $(Build.ArtifactStagingDirectory)\Templates
    
  3. Skopiuj wszystkie skrypty
    Task: Copy Files
    Display name: Prepare Scripts
    Source Folder: AzureFunctions/AzureResourceGroup1/Scripts
    Contents: **
    Target Folder: $(Build.ArtifactStagingDirectory)\Scripts
    
  4. Skopiuj wszystkie pliki Azure Function wykluczają pliki projektu, takie jak .local.json, appsettings.json i .fanproj
    Task: Copy Files
    Display name: Prepare temporary folder with Azure Functions
    Source Folder: AzureFunctions/FunctionApp1
    Contents: **\!(*.locak.json|appsettings.json|*.funproj)
    Target Folder: $(System.DefaultWorkingDirectory)\Functions-Temp
    
  5. Utwórz archiwum ZIP z aplikacją Azure Functions
    Task: Archive Files
    Display name: Archive Azure Functions from temporary folder
    Archive Azure Function from temporary folder: $(System.DefaultWorkingDirectory)\Functions-Temp
    Prefix root folder name to archive paths: false
    Archive type: zip
    Archive file to create: $(Build.ArtifactStagingDirectory)\AzureFunctions.zip
    
  6. Publikuj artefakty
    Task: Publish Build Artifacts
    Display name: Publish Artifact: drop
    Publish Build Artifacts: $(Build.ArtifactStagingDirectory)
    Artifact Name: drop
    Artifact Type: Server
    

Repozytorium Git trzyma kod źródłowy nie tylko dla Azure Functions, ale w przypadku procesu kompilacje powinien on być uruchamiany tylko w momencie zmian w którejś z Azure Functions. Aby to osiągnać wykorzystaliśmy filtry dla ścieżek (path filters). Wszystkie Azure Functions są umieszone w folderze AzureFunctions.

Obrazek 5. VSTS Continuous Integration - Path filters

VSTS Continuous Integration - Path filters

Continuous Deployment

Proces wdrażania jest automatycznie uruchamianiu po udanym procesie kompilacji (Continous Deployment) i jest rozłożony na dwa środowiska. W tym wypadku zespół banqUP chce proces CI/CD tylko dla tych środowisk, ponieważ środowisko produkcyjnie nie zostało jeszcze wydane.

  1. Dev - dedykowane środowisko dla podstawowej weryfikacji prac zespołu programistów
  2. QA - dedykowane dla testerów w celu wykonywania planów testowych

Application monitoring

Jednym z najważniejszych aspektów pełnego cyklu DevOps i wdrażania na jakiekolwiek środowisko - w tym także Azure - jest monitorowanie aplikacji na produkcji. banqUP w pierwszej kolejności wykorzystywał rozwiązanie firmy trzeciej do monitorowania Web Apps oraz zintegrowania monitorowania dla Azure Functions. To podejścia posiadało dwie podstawowe wady:

  1. Potrzeba wykorzystania dwóch narzędzi do monitorowania.
  2. Zintegrowany monitoring dla Azure Functions nie dostarczał wszystkich wymaganych elementów.

Application Insights umożliwiają monitorowanie zarówno Web Apps jak i Azure Functions i są bardzo łatwe w konfiguracji.

  1. Aby dodać Application Insights do aplikacji www wystarczy otworzyć solucję w Visual Studio, kliknąć prawym przyciskiem myszy na projekt i wybrać opcję Configure Application Insights… i wykonać kroki w kreatorze.

Obrazek 6. Visual Studio - ConObrazek Application Insights

Visual Studio - Configure Application Insights

  1. Dodanie Application Insights do Azure Functions jest w chwili pisania tego dokumentu trochę bardziej skomplikowane a samo rozwiązanie nie jest jeszcze w pełni dostępne produkcyjnie, ale posiada tyle zalet, że przewyższają one problemy, które mogą czasami wystąpić. Aby dodać monitorowanie do Azure Functions należy:

  2. Stworzenie nowej instancji Application Insights
  3. Pobrać instrumentation key z portalu Azure z sekcji Application Insights

Obrazek 7. Visual Studio - Application Insights instrumentation key

Application Insights instrumentation key

  1. Dodać nowa zmienna w ustawieniach Azure Functions w formacie
APPINSIGHTS_INSTRUMENTATIONKEY = {Instrumentation Key}
Zmienne

Definicja wdrożenia używa wielu zmiennych zdefiniowanych dla wszystkich środowisk i dla każdego ze środowisk z osobna.

Obrazek 8. VSTS Release Management - Variables for whole definition

VSTS Release Management - Variables for whole definition

Obrazek 9. VSTS Release Management - Variables for Dev environment

VSTS Release Management - Variables for Dev environment

Zmienne dla środowiska QA sa praktycznie takie same. Rożnią się tylko wartościami.

Środowiska Dev i QA

Środowiska Dev i QA maja takie same procesy. Poniżej znajdują się kroki dla tych procesów.

Obrazek 10. VSTS Release Management - Definicja dla środowisk Dev i QA

VSTS Release Management - Definicja dla środowisk Dev i QA

  1. Utwórz lub zaktualizuj grupę zasobów opartą na szablonach ARM Templates (Infrastructure as Code).
    Task: Azure Resource Group Deployemnt
    Title: Create or Update Azure Resources
    Action: Create or update resource group
    Resource group: $(ResourceGroupName)
    Location: $(ResourceGroupLocation)
    Template location: Linked artifact
    Template: $(System.DefaultWorkingDirectory)/CI.AzureFunctions/drop/Templates/banqupAzureFunctions.json
    Template parameters: $(System.DefaultWorkingDirectory)/CI.AzureFunctions/drop/Templates/banqupAzureFunctions.parameters.json
    Override template parameters: -functionAppName $(AppServiceName)
    Deployment mode: Incremental
    
  2. Wdróż Azure Functions poprzez Web Deploy.
    Task: Azure App Service Deployment
    Title: Deploy Azure Functions to Azure App Service
    App Service name: $(AppServiceName)
    Package or Folder: $(System.DefaultWorkingDirectory)/CI.AzureFunctions/drop/AzureFunctions.zip
    Publish using Web Deploy: true
    Remove additional files at destination: true
    
Infrastructure as Code

W celu uniknięcia ręcznego tworzenia zasobów w Azure dla środowisk napisaliśmy szablony ARM dla Azure Functions w celu utrzymania spójności pomiędzy środowiskami i developerami. Ponieważ Azure Functions oparte sa na Azuer App Swervice szablon ARM jest bardzo podobny do standardowego zasobu Azure Web App. Jest tylko kilka elementów, które musza zostać zmienione jak np. kind parametry to w tym wypadku functionapp oraz sekcja z appsettings.

banqupAzureFunctions.json

{
   "$schema":"http://schemas.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
   "contentVersion":"1.0.0.0",
   "parameters":{
      "functionAppName":{
         "type":"string",
         "minLength":3,
         "maxLength":19,
         "metadata":{
            "description":"The name of the function app that you wish to create."
         }
      }
   },
   "variables":{
      "functionAppName":"[toLower(parameters('functionAppName'))]",
      "hostingPlanName":"[variables('functionAppName')]",
      "storageAccountName":"[concat(variables('functionAppName'), 'storage')]",
      "storageAccountType":"Standard_LRS"
   },
   "resources":[
      {
         "type":"Microsoft.Storage/storageAccounts",
         "name":"[variables('storageAccountName')]",
         "apiVersion":"2016-12-01",
         "sku":{
            "name":"[variables('storageAccountType')]"
         },
         "location":"[resourceGroup().location]",
         "kind":"Storage"
      },
      {
         "type":"Microsoft.Web/serverfarms",
         "apiVersion":"2016-09-01",
         "kind":"functionapp",
         "name":"[variables('hostingPlanName')]",
         "location":"[resourceGroup().location]",
         "sku":{
            "name":"Y1",
            "tier":"Dynamic",
            "size":"Y1",
            "family":"Y",
            "capacity":0
         },
         "properties":{
            "name":"[variables('hostingPlanName')]",
            "numberOfWorkers":0
         }
      },
      {
         "apiVersion":"2016-08-01",
         "type":"Microsoft.Web/sites",
         "name":"[variables('functionAppName')]",
         "location":"[resourceGroup().location]",
         "kind":"functionapp",
         "properties":{
            "name":"[variables('functionAppName')]",
            "serverFarmId":"[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
            "clientAffinityEnabled":false
         },
         "resources":[
            {
               "apiVersion":"2016-03-01",
               "name":"appsettings",
               "type":"config",
               "dependsOn":[
                  "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
               ],
               "properties":{
                  "AzureWebJobsStorage":"[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2016-12-01').keys[0].value,';')]",
                  "AzureWebJobsDashboard":"[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2016-12-01').keys[0].value,';')]",
                  "FUNCTIONS_EXTENSION_VERSION":"latest",
                  "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING":"[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listkeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2016-12-01').keys[0].value,';')]",
                  "WEBSITE_CONTENTSHARE":"[concat(variables('functionAppName'), 'share')]"
               }
            }
         ],
         "dependsOn":[
            "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
            "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
         ]
      }
   ],
   "outputs":{
      "functionAppDefaultHostName":{
         "type":"string",
         "value":"[reference(resourceId('Microsoft.Web/sites', variables('functionAppName'))).defaultHostName]"
      },
      "functionAppOutboundIpAddresses":{
         "type":"string",
         "value":"[reference(resourceId('Microsoft.Web/sites', variables('functionAppName'))).outboundIpAddresses]"
      },
      "functionApp":{
         "type":"object",
         "value":"[reference(resourceId('Microsoft.Web/sites', variables('functionAppName')))]"
      },
      "functionAppHostingPlan":{
         "type":"object",
         "value":"[reference(resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName')))]"
      }
   }
}

Pełen szablon ARM można znaleźć w tym repozytorium GitHub: banqupAzureFunctions.json

Podsumowanie i kroki na przyszłość

Obecnie banqUP pracuje nad doprowadzeniem aplikacji do statusu produkcyjnego wraz ze wszystkimi wymaganymi funkcjonalnościami mając na uwadze architekturę, praktyki DevOps i technologie takie jak Azure Functions w centrum. W przyszłości banqUP będzie tworzył aplikacje mobilna, która będzie wykorzystywała Sztuczna Inteligencje w celu dokonywania predykcji na różnych poziomach systemu, conversation as a platform oraz big data.

Fakty i usprawnienia

Kilka interesujących faktów i usprawnień po hackfeście:

  • Zaprojektowaliśmy podstawowy proces developerski od podstaw dzięki czemu każdy z członków zespołu wie za co jest odpowiedzialny oraz jak wszystko działa od A do Z. Wyeliminowało to przekazywanie zadań i usprawniło wydajność.
  • Usprawniliśmy proces wdrożenia Azure App Service co umożliwiło developerom na wdrażanie i testowanie ich rozwiazań w bardzije zarzadzalny i przewidywalny sposób.
  • Zautomatyzowaliśmy proces walidacji nowych pull request co zredukowało ogólny czas (praca ręczna + oczekiwanie) z około 4-8 godzin do 15-30 minut.
  • Zoptymalizowaliśmy monitorowanie rozwiązania poprzez migracje z zbudowane monitorowania Azure Functions do Application Insights co daje developerom dużo lepszy wgląd w kondycję ich aplikacji.

banqUP - własnymi słowami

“Zdecydowaliśmy się zbudować nasza platformę w oparciu o chmurę Microsoft Azure, aby wykorzystać bogaty zestaw usług i zapewnić skalowalność i bezpieczeństwo. Dla nas warsztat był idealną możliwością na przegląd architektury, sprawdzeniem czy wykorzystujemy usługi w najbardziej optymalny sposób oraz aby zbudować techniczna roadmape dla dynamicznego wzrostu banqUP.”

Krzysztof Pulkiewicz, CEO firmy banqUP

“Projekt był bardzo przydatnym doświadczeniem. Pozwolił nam nie tylko lepiej poznać Microsoft Azure, ale także usprawnić nasz proces DevOps i wprowadził go na właściwe tory. Praca z inżynierami Microsoft, którzy odpowiadali na pytania zespołu programistów oraz ich bliska współpraca z nami rozwiązała wiele problemów, które napotkaliśmy budując nowoczesna platformę bankowości w chmurze Microsoft Azure.”

Łukasz Chmielewski, CTO firmy banqUP

Dodatkowe zasoby

Więcej informacji na temat DevOps:

Punkt startowy na temat technologii wykorzystywanych w projekcie: