Code to Cloud deployment

Ballerina Code to Cloud is designed to allow developers to write code without thinking about the deployment platform.

This greatly simplifies the experience of developing and deploying Ballerina code in the cloud. It also enables using cloud-native technologies easily without in-depth knowledge.

As of now, Ballerina Code to Cloud supports generating the deployment artifacts of the platforms below.

  1. Docker
  2. Kubernetes

Set up the prerequisites

To complete this tutorial, you need:

  1. Docker installed and configured in your machine
  2. Kubectl installed and configured in a Kubernetes cluster

How Code to Cloud works

Code to cloud builds the containers and required artifacts by deriving the required values from the code. This process happens when the package is being compiled. To override the default values given by the compiler, the Cloud.toml file needs to be created in the package directory.

Package layout

Ballerina encourages having one microservice per package. To adhere to that rule, Code to Cloud generates only one container per package. These artifacts can be found in the target directory of the package. A complete representation of the package layout is as follows.

├── Cloud.toml                               
├── Ballerina.lock
├── Ballerina.toml
├── entry.bal                          
└── target
    ├── bala
    │   └── module-0.0.1.bala
    ├── bin
    │   └── <module>.jar
    ├── docker
    │       └── Dockerfile                        
    └── kubernetes
            └── <module>-0.0.1.yaml 

Cloud.toml

The configuration file is used to override the default values generated by Code to Cloud.

Note: The Cloud.toml file is completely optional and you only have to specify the values you need to override. If the value is not specified for a certain field, the compiler will take the default value. All the supported properties can be found in the Code to Cloud specification.

Ballerina.toml

Contains metadata about the Ballerina package. This file is used to fetch defaults for deployment artifacts generation.

Note: The Ballerina.toml file can be used to specify the build option.

entry.bal

Represents any .bal file that has an entry point. The compiler will be using this file to retrieve service-related information for the deployment artifacts.

target/docker/

Contains the Docker artifacts generated by Code to Cloud. These artifacts will be required to build the Docker image.

target/kubernetes/

Contains the Kubernetes artifacts generated by Code to Cloud. These artifacts will be required to deploy the Ballerina application in Kubernetes.

Docker deployment

Create the Ballerina package

  1. Execute the bal new hello_docker command to create a new package named hello_docker.

  2. Replace the content of the ./hello_docker/main.bal file with the content below.

    Note: The source is completely focused on the business logic.

    main.bal

  3. Add cloud = "docker" under the [build-options] table into the Ballerina.toml file in the package.

    Ballerina.toml

  4. Create a file named Cloud.toml in the package directory and add the content below.

    Cloud.toml

Generate the artifacts

Execute the bal build command to build the Ballerina package and view the output below. If you haven't added the cloud option to Ballerina.toml file, you can execute bal build --cloud=docker to generate the artifacts and build the package by providing the build option inline.

$> bal build
Compiling source
        wso2/hello_docker:0.1.0

Generating executable

Generating artifacts...

        @kubernetes:Docker                       - complete 2/2 

        Execute the below command to run the generated docker image: 
        docker run -d -p 9090:9090 wso2inc/hello:v0.1.0

        target/bin/hello_docker.jar

Before invoking the container, let’s observe the Dockerfile below that has been generated. This is a Dockerfile created by the compiler extension from your code to run your HTTP service in the Docker environment easily.

target/docker/hello/Dockerfile

# Auto Generated Dockerfile
FROM ballerina/jre11:v1

LABEL maintainer="dev@ballerina.io"

COPY ballerina-http-2.2.0.jar /home/ballerina/jars/ 
COPY ballerina-io-1.2.0.jar /home/ballerina/jars/ 
COPY wso2-hello_docker-0.1.0.jar /home/ballerina/jars/ 
...

RUN addgroup troupe \
    && adduser -S -s /bin/bash -g 'ballerina' -G troupe -D ballerina \
    && apk add --update --no-cache bash \
    && chown -R ballerina:troupe /usr/bin/java \
    && rm -rf /var/cache/apk/*

WORKDIR /home/ballerina
COPY wso2-hello_docker-0.1.0.jar /home/ballerina

EXPOSE  9090
USER ballerina

CMD java -Xdiag -cp "wso2-hello_docker-0.1.0.jar:jars/*" 'wso2/hello_docker/0/$_init' || cat ballerina-internal.log

Execute the Docker image

Follow the steps below to execute the Docker image separately.

  1. Execute the docker images command to verify if the Docker image is generated.

    $> docker images
    REPOSITORY                    TAG                 IMAGE ID            CREATED              SIZE
    wso2inc/hello                 v0.1.0              60d95f0928b2        About a minute ago   228MB
    
  2. Execute the docker run -d -p 9090:9090 wso2inc/hello:v0.1.0 command to run the generated Docker image.

    $> docker run -d -p 9090:9090 wso2inc/hello:v0.1.0
    c04194eb0b4d0d78cbc8ca55e0527d381d8ab4a1a68f8ea5dd3770a0845d5fbb
    
  3. Execute the curl http://localhost:9090/helloWorld/sayHello command to access the service.

    $> curl http://localhost:9090/helloWorld/sayHello
    Hello, Docker!
    

Kubernetes deployment

Create the Ballerina package

Below sample describes a Ballerina application that reads a greeting string from a config map and greets the user upon HTTP request. By following the steps below, you can make a Kubernetes deployment that has container resource limits, horizontal pod autoscaling, config maps and liveness, readiness probes with the help of Code to Cloud.

  1. Execute the bal new hello_k8s command to create a new package named hello_k8s and go inside that directory.

  2. Replace the content of the main.bal file with the content below.

    main.bal

  3. Create probes.bal with the following contents.

    probes.bal

  4. Create resources directory in the package, create a config.json file within it and paste the following contents inside.

    resources/config.json

  5. Add cloud = "k8s" under the [build-options] table into the Ballerina.toml file in the package.

    Ballerina.toml

  6. Create a file named Cloud.toml in the package directory and add the content below. You can use some properties from the docker sample here since cloud="k8s" option builds both Docker image and Kubernetes. You can see a brief description of the properties in the comments.

    Cloud.toml

Generate the artifacts

Once you build the Ballerina package, the compiler extension will generate the artifacts required for k8s deployment.

Tip: If you are using Minikube, execute the eval $(minikube docker-env) command before building the image if you don't want to push the container to the Docker registry.

Tip: If you don't have the cloud="k8s" entry on the Ballerina.toml you can execute bal build --cloud=k8s command to provide the build option inline.

$ bal build
Compiling source
        wso2/hello_k8s:0.1.0

Generating executable

Generating artifacts...

        @kubernetes:Service                      - complete 1/2
        @kubernetes:Service                      - complete 2/2
        @kubernetes:ConfigMap                    - complete 1/1
        @kubernetes:Deployment                   - complete 1/1
        @kubernetes:HPA                          - complete 1/1
        @kubernetes:Docker                       - complete 2/2 

        Execute the below command to deploy the Kubernetes artifacts: 
        kubectl apply -f /home/wso2/c2c-guide/hello_k8s/target/kubernetes/hello_k8s

        Execute the below command to access service via NodePort: 
        kubectl expose deployment hello-k8s-deployment --type=NodePort --name=hello-k8s-svc-local

        target/bin/hello_k8s.jar

Note: Before invoking the Kubernetes service, observe the Kubernetes yamls that have been generated. You should be able to find services for ports that have been exposed, HorizontalPodAutoscaler for scaling and Deployment, and Config maps.

target/kubernetes/hello_k8s

Execute the Kubernetes service

Follow the steps below to execute the Kubernetes service.

  1. Execute docker push <repository>/<name>:<tag> to push the container to docker hub. You can skip this step if you executed eval $(minikube docker-env) before building the container.

    $ docker push wso2inc/hello-k8s:v0.1.0
    
  2. Execute the kubectl apply -f target/kubernetes/hello_k8s command to execute the service.

    $ kubectl apply -f target/kubernetes/hello_k8s
    service/hello-k8s-svc created
    configmap/hello-k8s-config-json created
    deployment.apps/hello-k8s-deployment created
    horizontalpodautoscaler.autoscaling/hello-k8s-hpa created
    
  3. Execute the kubectl get pods command to verify the Kubernetes pods.

    $ kubectl get pods
    NAME                                        READY   STATUS    RESTARTS   AGE
    hello-k8s-deployment-577d8dbf8-p8zg5   1/1     Running   0          37s
    hello-k8s-deployment-577d8dbf8-p8zg5   1/1     Running   0          57s
    
  4. Execute the kubectl expose deployment hello-k8s-deployment --type=NodePort --name=hello-k8s-svc-local command to expose the service via NodePort to test in the development environment.

    $ kubectl expose deployment hello-k8s-deployment --type=NodePort --name=hello-k8s-svc-local
    service/hello-k8s-svc-local exposed
    
  5. Execute the kubectl get svc command to get the EXTERNAL-IP and port of the Kubernetes service.

    $ kubectl get svc
    NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
    hello-k8s-svc         ClusterIP   10.96.173.207   <none>        9090/TCP,9091/TCP               5m11s
    hello-k8s-svc-local   NodePort    10.99.245.41    <none>        9090:30342/TCP,9091:30515/TCP   66s
    kubernetes            ClusterIP   10.96.0.1       <none>        443/TCP                         130m
    

    Tip: If you are using Minikube, execute the minikube ip command to get the IP address.

    $ minikube ip
    192.168.49.2
    
  6. Execute the curl http://192.168.49.2:30342/helloWorld/sayHello command to access the deployed service via cURL.

    $ curl http://192.168.49.2:30342/helloWorld/sayHello
    Hello, Kubernetes!
    

Note: You can visit the Code to Cloud specification for detailed information about all the supported features. As mentioned in the beginning, Code to Cloud is a tool created to make the development process easier. It does not cover the operational properties in Kubernetes. If you want to have these additional features in your deployment, or you need to change the generated Kubernetes artifacts, you can use Kustomize for modifying the generated YAML.