Getting a handle on CI cost and duration using an internal developer portal

October 2, 2023

Getting a handle on CI cost and duration using an internal developer portal

Ready to start?

Introduction

Port’s software engineering team is always on the lookout for new ways to use Port. While we’re aware of the possibility to integrate Port with FinOps tools to create a shared responsibility for the organization's cloud computing infrastructure and costs, we wanted to take a slightly different approach and use Port as the FinOps tool itself.

Our goal is to get a view on CI costs and CI duration, from a job, workflow, branch and feature perspective. This approach can bring a lot of insight to developers and teams.

But first, let’s begin with a few words about FinOps in the internal developer portal.

FinOps in the internal developer portal

In the world of FinOps, time is money, and money is time, it all comes down to who you ask. Different personas in an organization need access to different types of resolutions when looking at the organization’s investments in the project management lifecycle.

Internal developer portals are a perfect solution to the problem, since they are built to contain all the relevant data about the SDLC and any other relevant app, from AppSec to incident management and more, and to abstract that data for developers. Adding FinOps cost data to the internal developer portal creates a real time snapshot of everything cost in the engineering environment, and allocates the cost to the right microservice and developer in a fraction of a second, based on the definitions in Port’s blueprints. Once data is unified into the portal, data can be presented to developers in a way that is relevant to their flow of work and that reduces cognitive load. Just like developers track bugs in one central place, they can see the cost implications of their work. 

In this example, we’ll show FinOps data coming from an internal table, but the same can be achieved with an integration with a FinOps tool (e.g. cloud costs).

CI costing and duration

Can we extend the same benefits of using FinOps in the developer portal to understand CI costing and duration at all levels that matter to developers, team leads and management? The answer is yes, and this is the central point of this blog.

In this blog, we will showcase a practical example of connecting Jira and GitHub to the internal developer portal to provide developers and teams with insights about the cost of CI. These principles can also be implemented when looking at adding FinOps data to your internal developer portal. 

Let’s dive in.

How much does a Jira feature cost us in terms of CI/CD runtime costs?

To answer this question, let’s first begin with the views we would need to provide developers and teams so that they can easily understand CI costs and duration. Our internal developer portal would need information about:

  1. All CI/CD jobs related to a specific branch, and their duration.
  2. All CI/CD jobs, related to each different branch related to a Jira ticket, and their duration.

To do that we also would need:

  • A convenient way to calculate the cost of a job, in accordance with all of the different types and costs of different runners.
  • A convenient way to aggregate costs related to each branch scope and Jira ticket.

To achieve this, we will:

  • First, build our software catalog by connecting our CI/CD and Git platforms to our Jira environment. This will provide us with the information about the relevant entities for which we’d like to track CI cost and duration data.
  • Second, for aggregations and cost calculations, we will make use of Port’s relations and calculated properties so that the CI cost and duration will be calculated by entity. As for the source of the cost data, we just added a static table with costs to GitHub.

Our use case uses the GitHub Actions CI/CD platform, but the same logic can be applied to any CI/CD solution.

Setting up the software catalog

In Port, setting up a software catalog begins with defining the metadata that needs to be brought into it, and this is done with blueprints. Blueprints are the foundational building blocks of the internal developer portal. They hold the schema of the entities you wish to represent in the software catalog.

Let’s look at the blueprints we’ll use to realize this use case:

  • The Job blueprint provides information about specific tasks or sets of tasks that need to be performed within a GitHub Actions workflow with properties such as duration.
  • The Runner Type blueprint provides information about the specifications, capabilities, and associated costs of the machines used to execute workflow runs in GitHub Actions with properties such as Cost per minute
    (Note: The Runner Type blueprint contains cost data for each specific type of machine dedicated to each job that ran.)
  • The Workflow blueprint provides information about specific workflows in your GitHub repository. This could include information such as code owners, creation dates etc...
  • The Workflow Run blueprint provides information about the execution of defined sets of automated tasks within GitHub Actions workflows with properties such as Run Attempts, Triggering Actor, Starting Time.
  • The Branch blueprint provides information about git branches with properties such as name, lastUpdate and url.
  • The Jira Issue blueprint provides information about issues in Jira with properties such as status, creator, reporter.

The lines between the blueprints are the relations between them, so that developers get CI cost data related to each job, workflow, branch. 

Once we connect the portal to GitHub and Jira - the blueprint schemas will be populated with metadata and the software catalog will be automatically created, using autodiscovery. Once the data is populated, we’ll see the data we want about  our CI/CD runs for each one of the blueprint types.

Here is the view of a Jira Issue entity - our New Awesome Feature - for which we want to gain insight with regards to the CI cost and duration. At the bottom of the image we can also see its related entities, the backend and the frontend of the feature. We can get data about their CI cost and duration too.

This visualization offers a clear view of how much each feature has cost in terms of the CI process, enabling better cost analysis and decision-making.

Analyzing total Workflow Run duration

Understanding the time taken by specific workflow runs over a defined period is crucial for identifying bottlenecks and optimizing processes. To do this we’ll add data to the workflow run blueprints. We can then filter for completed workflow runs (using the “success” or “failed” status). Once we do this, we can create a calculated property in Port that will display the total duration of all workflow runs of this type.

CI cost and duration data can be presented to teams, users, or any other custom blueprint you want. This integration opens doors to a comprehensive understanding of the financial impact of CI as well as duration (which is a blocker of sorts) enabling better decision-making, resource allocation, and processes.

{{cta_1}}

Check out Port's pre-populated demo and see what it's all about.

Check live demo

No email required

{{cta_2}}

Contact sales for a technical product walkthrough

Let’s start
{{cta_3}}

Open a free Port account. No credit card required

Let’s start
{{cta_4}}

Watch Port live coding videos - setting up an internal developer portal & platform

Let’s start
{{cta_5}}

Check out Port's pre-populated demo and see what it's all about.

(no email required)

Let’s start
{{cta_6}}

Contact sales for a technical product walkthrough

Let’s start
{{cta_7}}

Open a free Port account. No credit card required

Let’s start
{{cta_8}}

Watch Port live coding videos - setting up an internal developer portal & platform

Let’s start
{{cta-demo}}
{{reading-box-backstage-vs-port}}

Example JSON block

{
  "foo": "bar"
}

Order Domain

{
  "properties": {},
  "relations": {},
  "title": "Orders",
  "identifier": "Orders"
}

Cart System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Cart",
  "title": "Cart"
}

Products System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Products",
  "title": "Products"
}

Cart Resource

{
  "properties": {
    "type": "postgress"
  },
  "relations": {},
  "icon": "GPU",
  "title": "Cart SQL database",
  "identifier": "cart-sql-sb"
}

Cart API

{
 "identifier": "CartAPI",
 "title": "Cart API",
 "blueprint": "API",
 "properties": {
   "type": "Open API"
 },
 "relations": {
   "provider": "CartService"
 },
 "icon": "Link"
}

Core Kafka Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Kafka Library",
  "identifier": "CoreKafkaLibrary"
}

Core Payment Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Payment Library",
  "identifier": "CorePaymentLibrary"
}

Cart Service JSON

{
 "identifier": "CartService",
 "title": "Cart Service",
 "blueprint": "Component",
 "properties": {
   "type": "service"
 },
 "relations": {
   "system": "Cart",
   "resources": [
     "cart-sql-sb"
   ],
   "consumesApi": [],
   "components": [
     "CorePaymentLibrary",
     "CoreKafkaLibrary"
   ]
 },
 "icon": "Cloud"
}

Products Service JSON

{
  "identifier": "ProductsService",
  "title": "Products Service",
  "blueprint": "Component",
  "properties": {
    "type": "service"
  },
  "relations": {
    "system": "Products",
    "consumesApi": [
      "CartAPI"
    ],
    "components": []
  }
}

Component Blueprint

{
 "identifier": "Component",
 "title": "Component",
 "icon": "Cloud",
 "schema": {
   "properties": {
     "type": {
       "enum": [
         "service",
         "library"
       ],
       "icon": "Docs",
       "type": "string",
       "enumColors": {
         "service": "blue",
         "library": "green"
       }
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "system": {
     "target": "System",
     "required": false,
     "many": false
   },
   "resources": {
     "target": "Resource",
     "required": false,
     "many": true
   },
   "consumesApi": {
     "target": "API",
     "required": false,
     "many": true
   },
   "components": {
     "target": "Component",
     "required": false,
     "many": true
   },
   "providesApi": {
     "target": "API",
     "required": false,
     "many": false
   }
 }
}

Resource Blueprint

{
 “identifier”: “Resource”,
 “title”: “Resource”,
 “icon”: “DevopsTool”,
 “schema”: {
   “properties”: {
     “type”: {
       “enum”: [
         “postgress”,
         “kafka-topic”,
         “rabbit-queue”,
         “s3-bucket”
       ],
       “icon”: “Docs”,
       “type”: “string”
     }
   },
   “required”: []
 },
 “mirrorProperties”: {},
 “formulaProperties”: {},
 “calculationProperties”: {},
 “relations”: {}
}

API Blueprint

{
 "identifier": "API",
 "title": "API",
 "icon": "Link",
 "schema": {
   "properties": {
     "type": {
       "type": "string",
       "enum": [
         "Open API",
         "grpc"
       ]
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "provider": {
     "target": "Component",
     "required": true,
     "many": false
   }
 }
}

Domain Blueprint

{
 "identifier": "Domain",
 "title": "Domain",
 "icon": "Server",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {}
}

System Blueprint

{
 "identifier": "System",
 "title": "System",
 "icon": "DevopsTool",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "domain": {
     "target": "Domain",
     "required": true,
     "many": false
   }
 }
}
{{tabel-1}}

Microservices SDLC

  • Scaffold a new microservice

  • Deploy (canary or blue-green)

  • Feature flagging

  • Revert

  • Lock deployments

  • Add Secret

  • Force merge pull request (skip tests on crises)

  • Add environment variable to service

  • Add IaC to the service

  • Upgrade package version

Development environments

  • Spin up a developer environment for 5 days

  • ETL mock data to environment

  • Invite developer to the environment

  • Extend TTL by 3 days

Cloud resources

  • Provision a cloud resource

  • Modify a cloud resource

  • Get permissions to access cloud resource

SRE actions

  • Update pod count

  • Update auto-scaling group

  • Execute incident response runbook automation

Data Engineering

  • Add / Remove / Update Column to table

  • Run Airflow DAG

  • Duplicate table

Backoffice

  • Change customer configuration

  • Update customer software version

  • Upgrade - Downgrade plan tier

  • Create - Delete customer

Machine learning actions

  • Train model

  • Pre-process dataset

  • Deploy

  • A/B testing traffic route

  • Revert

  • Spin up remote Jupyter notebook

{{tabel-2}}

Engineering tools

  • Observability

  • Tasks management

  • CI/CD

  • On-Call management

  • Troubleshooting tools

  • DevSecOps

  • Runbooks

Infrastructure

  • Cloud Resources

  • K8S

  • Containers & Serverless

  • IaC

  • Databases

  • Environments

  • Regions

Software and more

  • Microservices

  • Docker Images

  • Docs

  • APIs

  • 3rd parties

  • Runbooks

  • Cron jobs

Starting with Port is simple, fast and free.

Let’s start