Earlier, developers simply wrote their program, built it and ran it. Today, developers need to also think of the various ways of running it whether it be as a binary on a machine (virtual most likely), by packaging it into a container, by making that container a part of a bigger deployment (K8s) or by deploying it into a serverless environment or a service mesh. However, these deployment options are not part of the programming experience for a developer. The developer has 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. is an open source programming language that 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. Ballerina From Code to Docker Agility is a key benefit that we expect from microservices-based application development and plays a major role here. 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 a Docker image, developers have to create a Dockerfile by choosing a suitable base image, bundling all dependencies, copying the application binary and setting the execution command with proper permissions. To create optimized images, developers have to follow a set of , otherwise, the image that is built will be large in size, less secure and have many other shortcomings. Docker best practices The Ballerina compiler is capable of creating optimized Docker images out of the application source code. The following code illustrates how to bundle, package and run a Ballerina hello service as a Docker container. ballerina/http; ballerina/log; ballerina/docker; :Expose {} listener http:Listener helloWorldEP = ( ); :Config { name: } service hello on helloWorldEP { { result = caller->respond( ); (result is error) { log:printError( , err = result); } } } import import import @docker new 9090 @docker "helloworld" resource function sayHello (http:Caller caller, http:Request request) var "Hello World!" if "Error in responding " Adding the to a , generates the Dockerfile and a Docker image and adding the annotation to the object exposes the endpoint port by allowing incoming traffic to the container. @docker:Config {} service @docker:Expose {} listener Let’s build the source file. $ ballerina build hello.bal Compiling hello.bal Generating executables hello.jar Generating docker artifacts... @docker - complete 2/2 Run the following to start a Docker container: docker run -d -p 9090:9090 helloworld:latest source command Created Coder image: $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE helloworld latest 0f68d7fea5e8 1 minutes ago 133MB Generated Dockerfile: ballerina/jre8:v1 ballerina FROM LABEL maintainer= "dev@ballerina.io" RUN addgroup troupe \ && adduser -S -s /bin/bash -g -G troupe -D ballerina \ && apk add --update --no-cache bash \ && chown -R ballerina:troupe /usr/bin/java \ && rm -rf /var/cache/apk/* 'ballerina' WORKDIR /home/ballerina COPY hello.jar /home/ballerina EXPOSE 9090 USER CMD java -jar hello.jar The created Docker image follows image building best practices and the developer can just run the Docker container by using the command. docker run run -d -p 9090:9090 helloworld:latest aa63c1e101317630c9e86b9ae0b424f406fde81073e859c66d7173b965a2039a $ curl http://localhost:9090/hello/sayHello Hello World! $docker Ballerina has comprehensive support for Docker functionality. The following list provides working samples for different use cases: Change Docker image's tag and registry with annotations Push Docker image to Docker registry Remote debug Ballerina service with Docker Copy files to Docker image Multiple modules with Docker annotation Run Ballerina service in HTTP and HTTPS with Docker Override CMD of the generated Dockerfile Docker Hello World Main function Copy files with Main function From Code to Kubernetes Docker helps to package the application and to perform some developer testing. But to run an application with multiple microservices in production, I would recommend using a platform like Kubernetes. is an open source platform for automating deployment, and scaling and management of containerized applications. Kubernetes defines a set of unique building blocks that collectively provide mechanisms to deploy, maintain and scale applications. A is a logical group of containers that are guaranteed running in co-located on the host machine. A Kubernetes provides discovery, routing and load balancing capabilities to the set of pods that it constitutes. Kubernetes is a set of pods in conjunction with a defined replica set, health check, and rolling update mechanisms. All of these Kubernetes objects need to be defined as YAML files and deployed into the Kubernetes cluster. Kubernetes Pod Service Deployment Even though developers want to run their application in a Kubernetes platform, in many cases, creating these YAML files is out of a developer’s comfort zone. The Ballerina compiler is capable of creating these YAML files while compiling the source code. Let's modify the above sample to generate Kubernetes artifacts. ballerina/http; ballerina/log; ballerina/kubernetes; :Service { serviceType: } listener http:Listener helloWorldEP = ( ); :Deployment { name: } service hello on helloWorldEP { { result = caller->respond( ); (result is error) { log:printError( , err = result); } } } import import import @kubernetes "NodePort" new 9090 @kubernetes "helloworld" resource function sayHello (http:Caller caller, http:Request request) var "Hello World!" if "Error in responding " Adding the annotation to the Ballerina service will generate the Kubernetes Deployment YAML that is required to deploy our hello application into Kubernetes. Adding the annotation will generate the Kubernetes Service YAML. In this scenario, we have set as to access the hello service via the . @kubernetes:Deployment{} @kubernetes:Service{} serviceType `NodePort` nodeIP:Port $ ballerina build hello.bal Compiling hello.bal Generating executables hello.jar Generating artifacts... @kubernetes:Service - complete 1/1 @kubernetes:Deployment - complete 1/1 @kubernetes:Docker - complete 2/2 @kubernetes:Helm - complete 1/1 Run the following to deploy the Kubernetes artifacts: kubectl apply -f hello/kubernetes Run the following to install the application using Helm: helm install --name helloworld hello/kubernetes/helloworld source command command The Ballerina compiler generates the Dockerfile, Docker image, hello.yaml file (with Kubernetes Deployment and Service), and helm chart YAML in addition to the hello.jar binary. $ tree . ├── docker │ └── Dockerfile ├── hello.bal ├── hello.jar └── kubernetes ├── hello.yaml └── helloworld ├── Chart.yaml └── templates └── hello.yaml 4 directories, 6 files Generated hello.yaml file --- apiVersion: "v1" kind: "Service" metadata: annotations: {} labels: app: "hello" name: "helloworldep-svc" spec: ports: - name: "http-helloworldep-svc" port: 9090 protocol: "TCP" targetPort: 9090 selector: app: "hello" type: "NodePort" --- apiVersion: "apps/v1" kind: "Deployment" metadata: annotations: {} labels: app: "hello" name: "helloworld" spec: replicas: 1 selector: matchLabels: app: "hello" template: metadata: annotations: {} labels: app: "hello" spec: containers: - image: "hello:latest" imagePullPolicy: "IfNotPresent" name: "helloworld" ports: - containerPort: 9090 protocol: "TCP" nodeSelector: {} Developers can use the generated Kubernetes artifacts to deploy applications in any Kubernetes platform. $ kubectl apply -f hello/kubernetes service/helloworldep-svc created deployment.apps/helloworld created $ kubectl get all NAME READY STATUS RESTARTS AGE pod/helloworld-696ff58c79-txlbk 1/1 Running 0 41s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/helloworldep-svc NodePort 10.110.217.216 <none> 9090:31833/TCP 41s service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 44d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/helloworld 1/1 1 1 41s NAME DESIRED CURRENT READY AGE replicaset.apps/helloworld-696ff58c79 1 1 1 41s Let’s access the hello service via the node port: $ curl http://localhost:31833/hello/sayHello Hello World! Ballerina provides comprehensive Kubernetes support that is required to run an application in a Kubernetes platform. The following list provides working samples for different Kubernetes scenarios required to run in production: Kubernetes Hello World with livenessProbe and hostname mapping Ballerina program with multiple services running in multiple ports Kubernetes Hello World Secured s Ballerina service with HTTP and HTTPS endpoint Kubernetes Hello World in Google Cloud Environment Mount secret volumes to deployment Mount config map volumes to deployment Mount PersistentVolumeClaim to deployment Ballerina module with Kubernetes annotations Kubernetes Hello World with Ballerina Function Copy External files to Docker Image Ballerina modules with dependencies Deploy Ballerina service in a namespace Resource quotas for namespaces In addition to generic Kubernetes support, if you wish to deploy a Ballerina application into OpenShift, read this sample, which illustrates . OpenShift Build Configs and Routes From Code to Istio Microservice architecture offers many advantages to developers to make their development agile, which leads to faster innovation. But it comes with its own complexity. Docker and Kubernetes solves some of these complexities. The service mesh is a modern software architecture that tries to reduce some more complexities running on top of platforms like Kubernetes. is an open source service mesh implementation. Service discovery, load balancing, failure recovery, metrics, and monitoring are its main focus areas. Istio also supports complex operational requirements like A/B testing, canary rollouts, rate limiting, access control, and end-to-end authentication. Istio Istio has introduced a few additional unique concepts in addition to the Kubernetes objects. and play a major role among them. A VirtualService defines a set of traffic routing rules to achieve the above complex operational requirements. Istio Gateway is a load balancer operating at the edge of the mesh receiving incoming or outgoing HTTP/TCP connections. VirtualService Gateway Ballerina is capable of generating artifacts to deploy and by adding two annotations on top of the Ballerina object. The following code snippet shows two annotations that define the Istio support: VirtualService Gateway listener :Gateway {} :VirtualService {} :Service { serviceType: } listener http:Listener helloWorldEP = ( ); @istio @istio @kubernetes "NodePort" new 9090 Building the source code will generate the Istio and artifacts. VirtualService Gateway $ ballerina build hello.bal Compiling hello.bal Generating executables hello.jar Generating artifacts... @kubernetes:Service - complete 1/1 @kubernetes:Deployment - complete 1/1 @kubernetes:Docker - complete 2/2 @kubernetes:Helm - complete 1/1 @istio:Gateway - complete 1/1 @istio:VirtualService - complete 1/1 Run the following to deploy the Kubernetes artifacts: kubectl apply -f kubernetes Run the following to install the application using Helm: helm install --name helloworld kubernetes/helloworld source command command You can find the full working source code in the following git repository: Istio Gateway and Virtual Service Generation From Code to Knative is a serverless platform created originally by Google with contributions from over 50 different companies. It uses Kubernetes platform capabilities to build a serverless platform that enables developers to focus on writing code without the need to worry about the “boring but difficult” parts of building, deploying, and managing their application. One of the key functionalities of Knative is scaling automatically from zero replicas and sizing workloads based on demand. Knative Knative also has its own object model and artifacts. Knative is defined by a and a , which have the same name as the Service contained in a YAML file. Every time the Configuration is updated, a new Revision is created. Service Route Configuration Ballerina is capable of generating these necessary artifacts while compiling the source code. The only requirement is to add a simple annotation in the code. ballerina/http; ballerina/log; ballerina/knative; :Service { name: } listener http:Listener helloWorldEP = ( ); service hello on helloWorldEP { { result = caller->respond( ); (result is error) { log:printError( , err = result); } } } import import import @knative "helloworld" new 9090 resource function sayHello (http:Caller caller, http:Request request) var "Hello World!" if "Error in responding " Adding this will generate the following artifacts that are required to deploy our application in serverless mode in a Knative cluster. $ ballerina build hello.bal Compiling hello.bal Generating executables hello.jar Generating Knative artifacts... @knative:Service - complete 1/1 @knative:Docker - complete 2/2 Run the following to deploy the Knative artifacts: kubectl apply -f kubernetes Run the following to install the application using Helm: helm install --name helloworld kubernetes/helloworld source command command Generated hello.yaml for knative deployment: --- apiVersion: "serving.knative.dev/v1alpha1" kind: "Service" metadata: annotations: {} labels: {} name: "helloworld" spec: template: spec: containerConcurrency: 100 containers: - image: "hello:latest" name: "helloworld" ports: - containerPort: 9090 protocol: "TCP" From Code to AWS Lambda is an event-driven, serverless computing platform. Ballerina functions can be deployed in AWS Lambda by annotating a Ballerina function with “ ”, which should have the function signature AWS Lambda @awslambda:Function function (awslambda:Context, json) returns json|error You can find a comprehensive sample in the following Ballerina by Example: AWS Lambda Deployment CI/CD with GitHub Action 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 , 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. GitHub Actions With the , which is available in the GitHub Marketplace, we can create a Ballerina development environment with built-in CI/CD. The following article has a comprehensive guideline: Ballerina GitHub Action Effective Microservices CI/CD With GitHub Actions and Ballerina 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: ballerina/config; ballerina/log; wso2/twitter; twitter:Client twitterClient = ({ clientId: config:getAsString( ), clientSecret: config:getAsString( ), accessToken: config:getAsString( ), accessTokenSecret: config:getAsString( ), clientConfig: {} }); { twitter:Status|error status = twitterClient->tweet( ); (status is error) { log:printError( , status); } { log:printInfo( + < >status.id.toString()); } } import import import // 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. new "clientId" "clientSecret" "accessToken" "accessTokenSecret" function public main () "Hello World!" if "Tweet Failed" else "Tweeted: " @untainted Ballerina has many out-of-the-box SaaS connectors. The following list shows a few Ballerina SaaS connectors: Connect to Google Spreadsheets from Ballerina Connect to Gmail from Ballerina Connect to Bigquery from Ballerina Connect to Azure CV service through Ballerina Connect to Amazon S3 from Ballerina Connect to Amazon SQS service Connect to Salesforce from Ballerina Key Takeaways Earlier, developers simply wrote their program, built it and ran it. But today, developers have various ways to run it. Cloud-native platforms like Docker, Kubernetes, Service Mesh and Serverless play a major role in modern platforms that support deployment automation. However, these deployment options are not a part of the programming experience for a developer. Ballerina is an open source programming language that 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.