Skip to main content

Command Palette

Search for a command to run...

Continuous Deployment with Kubernetes and Load Testing with JMeter

Updated
6 min read
Continuous Deployment with Kubernetes and Load Testing with JMeter

Challenge: Part 2 Continuous Deployment and Load Testing

Deploy the provided application to your chosen cloud provider using any service that runs the application as a Docker container. Then, create a test plan for the application's home page and generate a testing report using JMeter.

The application needs to:

Available for anyone with Internet access only during the challenge recording

Prerequisites

Step 1: Create Azure Container Registry and Azure Devops Connections

Create Azure Registry:

  1. Go to the Azure portal.

  2. Create a new resource group.

  3. Click on "Container registries".

  4. Click on "Create container registry".

  5. On the "Create container registry" page, enter the following information:

  • Registry name - Enter mdcregistryID. # The name needs to be unique change the last two letters by the initials of your name

  • Region - Select the region where you want to create your registry.

  • Pricing tier - Select the pricing tier that you want to use for your registry.

  • Security - Select the security options that you want to use for your registry.

  • Choose the Resource Group you created earlier

Azure Devops Connections:

  1. Navigate to Azure DevOps Service Connections

  2. Sign in to Azure DevOps.

  3. Select your organization and project.

  4. In the left sidebar, go to Project Settings.

  5. Under Pipelines, click Service Connections.

  6. Click New Service Connection.

  7. Choose Docker Registry

  8. Authentication Type Select Service Principal

  9. Enter your settings and click create

Step 2: Create Kubernetes

  1. Log in to Azure

  2. Log in to your Azure account via CLI: az login

  3. Create the Kubernetes cluster: az aks create --resource-group projeto-flask --name k8s-cluster --node-count 2 --enable-addons monitoring --generate-ssh-keys

  4. Step 3: Get ACR credentials and create a secret on Kubernetes

  5. Get ACR name: az acr list --query "[].{Name:name, ResourceGroup:resourceGroup}" --output table

  6. Get Docker Server Credential: az acr show --name Your_ACR_Name --query loginServer --output tsv

  7. Get Docker Username: az acr credential show --name Your_ACR_Name --query username --output tsv

  8. Get Docker Password: az acr credential show --name Your_ACR_Name --query passwords[0].value --output tsv

  9. Access your Kubernetes: az aks get-credentials --resource-group Your_Resource_Group_Name --name mdc-aks

  10. Create Secret:

      kubectl create secret docker-registry acr-secret \
              --docker-server=Your_Docker_Server \ 
              --docker-username=Your_Docker_Username \ 
              --docker-password=Your_Docker_Password \
    
  11. Configure Assign AcrPull Role:

    ACR_ID=$(az acr show --name mdcrepositorychiroli --query "id" -o tsv)
    
    AKS_ID=$(az aks show --resource-group projeto-flask --name k8scluster --query "identityProfile.kubeletidentity.objectId" -o tsv)
    
    az role assignment create --assignee $AKS_ID --role AcrPull --scope $ACR_ID
    

Step 4: Create Azure Resource Manager on Connections

  1. Navigate to Azure DevOps Service Connections

  2. Sign in to Azure DevOps.

  3. Select your organization and project.

  4. In the left sidebar, go to Project Settings.

  5. Under Pipelines, click Service Connections.

  6. Click New Service Connection.

  7. Choose Azure Resource Manager

  8. Enter your settings, you can use the default setting and click create.

Step 5: Create Dockerfile

  1. Access Azure Repo

  2. Go to: assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0

  3. Edit Dockerfile

# Etapa de build
FROM node:15 AS builder

# Define o diretório de trabalho
WORKDIR /app

# Copia os arquivos necessários
COPY package.json package-lock.json ./

# Instala dependências
RUN npm install

# Copia o restante dos arquivos da aplicação
COPY . .

# Faz o build da aplicação (se necessário)
RUN npm run build

# Etapa de produção
FROM node:15

# Define o diretório de trabalho
WORKDIR /app

# Copia arquivos do estágio anterior
COPY --from=builder /app /app

# Instala as dependências de produção
RUN npm install --only=production

# Define o comando para iniciar a aplicação
CMD ["npm", "start"]

# Exposição de porta (opcional)
EXPOSE 3000

Step 6: Create Deployment and Load Balancer

  1. Access Azure Repo

  2. Access the repository on the same place where azure-pipelines.yml and create the file deployment_service.yml

     apiVersion: apps/v1
     kind: Deployment
     metadata:
       name: nodejs-deployment
     spec:
       replicas: 1
       selector:
         matchLabels:
           app: nodejs
       template: 
         metadata:
           name: nodejs
           labels:
             app: nodejs  
         spec:
           containers:
             - name: nodejsapp
               image: mdcrepositorychiroli.azurecr.io/node.js:latest
               ports:
                 - containerPort: 3000
           imagePullSecrets:
             - name: acr-secret    
     ---
     apiVersion: v1
     kind: Service
     metadata:
       name: nodejs-service
       labels: 
         app: nodejs
     spec:
       type: LoadBalancer 
       selector:
         app: nodejs
       ports: 
         - port: 3000
           targetPort: 3000
    

Step 7: Create a file for the Jmeter test

  1. Access Azure Repo

  2. Access the repository on the same place where azure-pipelines.yml and create the file test.jmx

     <?xml version="1.0" encoding="UTF-8"?>
     <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
       <hashTree>
         <!-- Test Plan -->
         <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Basic Test Plan" enabled="true">
           <stringProp name="TestPlan.comments"></stringProp>
         </TestPlan>
         <hashTree>
           <!-- Thread Group -->
           <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Load Test Group" enabled="true">
             <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
    
             <!-- Adicionando o Loop Controller -->
             <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControllerGui" testclass="LoopController" testname="Loop Controller" enabled="true">
               <boolProp name="LoopController.continue_forever">false</boolProp>
               <stringProp name="LoopController.loops">1</stringProp>
             </elementProp>
    
             <stringProp name="ThreadGroup.num_threads">10</stringProp>
             <stringProp name="ThreadGroup.ramp_time">5</stringProp>
             <boolProp name="ThreadGroup.scheduler">false</boolProp>
           </ThreadGroup>
           <hashTree>
             <!-- HTTP Sampler -->
             <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
               <stringProp name="HTTPSampler.domain">${__P(host, localhost)}</stringProp>
               <stringProp name="HTTPSampler.port">3000</stringProp>
               <stringProp name="HTTPSampler.protocol">http</stringProp>
               <stringProp name="HTTPSampler.path">/</stringProp>
               <stringProp name="HTTPSampler.method">GET</stringProp>
             </HTTPSamplerProxy>
             <hashTree/>
           </hashTree>
         </hashTree>
       </hashTree>
     </jmeterTestPlan>
    

Step 7: Configure the Pipeline

  1. Access Azure Repo

  2. Edit file azure-pipelines.yml

  3. With the new configuration and the configuration of the last challenge the pipeline looks like this:

trigger:
- main

pr:
  branches:
    include:
    -  main

stages:
  - stage: BuildAndTest
    displayName: Build and Test Job
    pool:
      vmImage: 'ubuntu-latest'
    jobs:
    - job: Build
      displayName: 'Build and Test Job'
      steps:
      - checkout: self
        path: 'src'
        displayName: 'Checkout code'

      - task: UseNode@1
        inputs:
          version: '15.x'
        displayName: 'Install Node.js'

      - script: npm install
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Install dependencies'

      - script: npm run lint
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Linter (ESLint)'

      - script: npm install --save-dev prettier
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Install Formatter (Prettier)'

      - script: npm run prettier -- --write
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Format Code (Prettier)'

      - script: CI=true npm run test
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Test (Jest)'

      - script: npm run build
        workingDirectory: $(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0
        displayName: 'Run Build'

      - task: Docker@2
        inputs:
          containerRegistry: 'MdcRegistry'
          repository: 'Node.js'
          command: 'buildAndPush'
          Dockerfile: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0/Dockerfile'
          buildContext: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
          tags: 'latest'
        displayName: 'Build and Push Docker Image'
        continueOnError: true  

      - task: AzureCLI@2
        inputs:
          azureSubscription: 'MdcAzureRm'
          scriptType: 'bash'
          scriptLocation: 'inlineScript'
          inlineScript: |
            az aks get-credentials --resource-group projeto-flask --name k8scluster --overwrite-existing
            kubectl apply -f $(Build.SourcesDirectory)/deployment_service.yml
            kubectl get services
        displayName: 'Execute Kubernetes'
        continueOnError: true


      - script: |
          echo "Installing JMeter..."
          sudo apt-get update -y
          sudo apt-get install -y wget openjdk-11-jre  # Install Java and wget

          wget https://downloads.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
          tar -xvzf apache-jmeter-5.6.3.tgz
          sudo mv apache-jmeter-5.6.3 /opt/jmeter

          # Adiciona JMeter ao PATH corretamente
          echo "export PATH=/opt/jmeter/bin:\$PATH" | sudo tee -a /etc/profile
          export PATH=/opt/jmeter/bin:$PATH  # Atualiza PATH na sessão atual

          echo "JMeter installed successfully."
        displayName: 'Install JMeter'

      - script: |
          echo "Creating JMeter test file..."
          cp $(Build.SourcesDirectory)/test.jmx /opt/jmeter/test.jmx
        displayName: 'Create JMeter Test File'

      - task: AzureCLI@2
        inputs:
          azureSubscription: 'MdcAzureRm'
          scriptType: 'bash'
          scriptLocation: 'inlineScript'
          inlineScript: |
            NODEJS_IP=$(kubectl get svc nodejs-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
            echo "##vso[task.setvariable variable=NODEJS_IP]$NODEJS_IP"
            echo "Fetched IP: $NODEJS_IP"
        displayName: 'Retrieve NodeJS Service External IP'
        continueOnError: true

      - script: |
          export PATH=/opt/jmeter/bin:$PATH
          jmeter -v
          jmeter -n -t /opt/jmeter/test.jmx -l /opt/jmeter/results.jtl -j jmeter.log -Jhost=$NODEJS_IP
          cat /opt/jmeter/results.jtl
          cat jmeter.log
        displayName: 'Run JMeter'

Step 8: Access the application

  1. Log in to Azure

  2. Log in to your Azure account via CLI: az login

  3. Access your Kubernetes: az aks get-credentials --resource-group Your_Resource_Group_Name --name mdc-aks

  4. Execute the command az get services and access the page with the external IP

  5. Access http://Your_external_ip:3000/

Step 8: See the Jmeter test results

More from this blog

J

Joaochiroli

12 posts

Everything about Devops and SRE