From Code to Cloud

In the past, developers simply wrote their program, built it and ran it. Today, developers also need to think of the various deployment options such as Docker, Kubernetes, serverless environments and service meshes. But this deployment process is not part of the programming experience for developers. They have to write code in a certain way to work well in a given execution environment, and removing this from the programming problem isn’t good.

Ballerina specializes in moving from code to cloud while providing a unique developer experience. Its compiler can be extended to read annotations defined in the source code and generate artifacts to deploy your code into different clouds. These artifacts can be Dockerfiles, Docker images, Kubernetes YAML files or serverless functions.

From Code to Docker

Agility is a key benefit of microservices-based application development and Docker plays a major role. Docker helps to package applications and their dependencies in a binary image that can run in various locations, whether on-premises, in a public cloud, or in a private cloud. To create optimized Docker images, developers need to follow best practices when creating a Dockerfile. This includes choosing a suitable image base, bundling all dependencies, copying the application binary and setting the execution command with proper permissions.

Ballerina supports generating Docker images and Dockerfiles based on annotations. The Ballerina compiler can do this out of the application code.

Get Started

The code snippet below shows how to bundle, package, and run a Ballerina hello service as a Docker container, all while following best practices.

import ballerina/http;
import ballerina/log;
import ballerina/docker;
 
@docker:Expose {}
listener http:Listener helloWorldEP = new(9090);
@docker:Config {
   name: "helloworld"
}
service hello on helloWorldEP {
 
resource function sayHello(http:Caller caller,
http:Request request) {
      var result = caller->respond("Hello World!");
      if (result is error) {
          log:printError("Error in responding ", err = result);
      }
  }
}

Adding the @docker:Config {} annotation to a service generates the Dockerfile and a Docker image and adding the @docker:Expose {} annotation to the listener object exposes the endpoint port by allowing incoming traffic to the container.

From Code to Kubernetes

Kubernetes is the preferred platform for running applications with multiple microservices in production. It can be used for automating deployment and scaling, and management of containerized applications. Kubernetes defines a set of unique building blocks that need to be defined as YAML files and deployed into the Kubernetes cluster.

However, in many cases, creating these YAML files is out of a developer’s comfort zone. The Ballerina compiler can create these YAML files while compiling the source code, so you don’t have to! The code below shows the annotations you need to use to do this:

Get Started

The following code snippet shows how Ballerina annotations can generate YAML files to deploy your code to Kubernetes.

import ballerina/http;
import ballerina/log;
import ballerina/kubernetes;
 
@kubernetes:Service {
   serviceType: "NodePort"
}
listener http:Listener helloWorldEP = new(9090);
 
@kubernetes:Deployment {
   name: "helloworld"
}
service hello on helloWorldEP {
 
resource function sayHello(http:Caller caller,
http:Request request) {
      var result = caller->respond("Hello World!");
      if (result is error) {
          log:printError("Error in responding ", err = result);
      }
  }
}

Adding the @kubernetes:Deployment{} annotation to the Ballerina service will generate the Kubernetes Deployment YAML that is required to deploy our hello application into Kubernetes. Adding the @kubernetes:Service{} annotation will generate the Kubernetes Service YAML. In this scenario, we have set serviceType as NodePort to access the hello service via the nodeIP:Port.

If you are an OpenShift user follow the example below to deploy your application.

From Code to Istio

Although microservice architecture increases agility, it comes with its own complexities. The Service Mesh is a modern software architecture that helps reduce the complexities that Docker and Kubernetes don’t. Istio is a leading open source service mesh which focuses on service discovery, load balancing, failure recovery, metrics, monitoring, A/B testing, canary rollouts, rate limiting, access control, and end-to-end authentication.

By adding two annotations, Ballerina can generate YAML files to deploy in Istio.

Get Started

The code snippet below shows how Ballerina helps deploy your code in Istio.

@istio:Gateway {}
@istio:VirtualService {}
@kubernetes:Service {
   serviceType: "NodePort"
}

listener http:Listener helloWorldEP = new(9090);

From Code to Knative

Serverless platforms like Knative enable developers to focus on writing code without having to worry about the “boring but difficult” parts of building, deploying and managing their applications. A key functionality of Knative is the ability to scale automatically from zero replicas and size workloads based on demand.

While compiling the source code, Ballerina can generate the artifacts necessary for deploying to Knative by simply annotating the code.

Get Started

The code snippet below shows the annotations needed in Ballerina to deploy your code to Knative.

import ballerina/http;
import ballerina/log;
import ballerina/knative;
 
@knative:Service {
   name: "helloworld"
}
listener http:Listener helloWorldEP = new(9090);
 
service hello on helloWorldEP {
 
resource function sayHello(http:Caller caller,
http:Request request) {
      var result = caller->respond("Hello World!");
      if (result is error) {
          log:printError("Error in responding ", err = result);
      }
  }
}

From Code to AWS Lambda

AWS Lambda is an event-driven, serverless computing platform. Ballerina functions can be deployed in AWS Lambda by annotating a Ballerina function with @awslambda:Function, which should have the function signature function (awslambda:Context, json) returns json|error.

CI/CD with GitHub Actions

In a microservice architecture, continuous integration and continuous delivery (CI/CD) is critical in creating an agile environment for incorporating incremental changes to your system. There are different technologies that provide this CI / CD functionality and very recently GitHub has introduced GitHub Actions, which is now available for general usage. GitHub Actions provides a convenient mechanism for implementing CI/CD pipelines using their workflows concept, right from our GitHub repositories.

With Ballerina GitHub Actions we can create a Ballerina development environment with built-in CI/CD. The following article has a comprehensive guideline:

Support for SaaS Connectors

We have discussed how Ballerina supports different technologies to automate cloud deployments. To obtain the full strength of the cloud, applications should be able to integrate with Software-as-a-Service (SaaS) provided by different cloud vendors.

Ballerina provides a simple workflow to connect and integrate with these SaaS services. For example, the following code snippet shows how to initialize and send out a tweet with the Twitter SaaS service:

import ballerina/config;
import ballerina/log;
import wso2/twitter;
// Twitter package defines this type of endpoint
// that incorporates the twitter API.
// We need to initialize it with OAuth data from apps.twitter.com.
// Instead of providing this confidential data in the code
// we read it from a toml file.
twitter:Client twitterClient = new ({
  clientId: config:getAsString("clientId"),
  clientSecret: config:getAsString("clientSecret"),
  accessToken: config:getAsString("accessToken"),
  accessTokenSecret: config:getAsString("accessTokenSecret"),
  clientConfig: {}
});
 
public function main() {
 
  twitter:Status|error status = twitterClient->tweet("Hello World!");
  if (status is error) {
      log:printError("Tweet Failed", status);
  } else {
      log:printInfo("Tweeted: " + <@untainted>status.id.toString());
  }
}

Ballerina has many out-of-the-box SaaS connectors, which you can find in Ballerina Central.

"“We were attracted by Ballerina’s cloud native features, such as the automatic generation of Docker, Kubernetes, and Helm artifacts, as well as its small footprint and faster boot times. It is also capable of running as a Lambda function (serverless) in AWS. Together, these capabilities have enabled us to easily run microservices in containers at scale and integrate with CI/CD tools with less effort.

Harsha Pulleti, integration architect and senior manager, Motorola

In the creation of Ballerina, we were inspired by so many technologies. Thank you to all that have come before us (and forgive us if we missed one): Java, Go, C, C++, D, Rust, Haskell, Kotlin, Dart, TypeScript, JavaScript, Python, Perl, Flow, Swift, Elm, RelaxNG, NPM, Crates, Maven, Gradle, Kubernetes, Docker, Envoy, Markdown, GitHub and WSO2.