Skip to main content

Workload

The Workload resource represents a scalable containerized service deployed and managed by a container orchestration system such as Kubernetes.

When running locally within the Wing Simulator, either during development or during builds, workloads are implemented using local docker images.

When running on the cloud, workloads become Kubernetes applications, built and published to an image registry and deployed to a Kubernetes cluster using Helm. We currently only support AWS/EKS but support for other platforms are planned.

It will also be possible for platforms to implement workloads using any other compatible container orchestration system such as Amazon ECS, fly.io or ControlPlane.

⚠️ This resource is still experimental. Please ping the team on Wing Discord if you encounter any issues or have any questions and let us know what you think. See roadmap below for more details about our plans.

Installation

For the time being, in order to use this resource you will first first need to install @winglibs/containers from npm:

npm i @winglibs/containers

You will also need Docker or OrbStack installed on your system in order for workloads to work in the Wing Simulator.

Usage

In your code, just bring containers and define workloads using the containers.Workload class.

Check out a few examples below or jump to the full API Reference.

Using an image from a registry

Let's start with a simple example which defines a workload based on the hashicorp/http-echo image. It is a simple HTTP server listening on port 5678 that responds with a message.

bring containers;

let hello = new containers.Workload(
name: "hello",
image: "hashicorp/http-echo",
port: 5678,
public: true,
args: ["-text=hello, wingnuts!"],
);

In order to test this workload, we can use publicUrl which resolves to a publicly accessible route into your container. And if you were wondering: Yes, this also works on the cloud! Every workload with public: true will have a URL that can be used to access it from the web.

bring http;
bring expect;

test "message is returned in http body" {
let url = hello.publicUrl ?? "FAIL";
let body = http.get(url).body ?? "FAIL";
log(body);
expect.equal(body, "hello, wingnuts!\n");
}

Building an image from source

Workloads can also be be based on an image defined through a dockerfile within your project. The image is automatically built during compilation and published to a container registry during deployment.

Let's define a workload which based on the docker image built from the dockerfile in the ./backend directory:

bring containers;

new containers.Workload(
name: "backend",
image: "./backend",
port: 3000,
public: true
);

Under ./backend, create:

backend/Dockerfile:

FROM node:20.8.0-alpine
EXPOSE 3000
ADD index.js /app/index.js
ENTRYPOINT [ "node", "/app/index.js" ]

backend/index.js:

const http = require('http');

process.on('SIGINT', () => process.exit(0));

const server = http.createServer((req, res) => {
console.log(`request received: ${req.method} ${req.url}`);
res.end('Hello, Wingnuts!');
});

console.log('listening on port 3000');
server.listen(3000);

Defining multiple workloads as microservices

Using privateUrl, it is possible to reach workloads without having to expose them publicly.

Let's combine the last two examples by deploying the http-echo container and ping it from within our docker image:

bring containers;

let echo = new containers.Workload(
name: "echo",
image: "hashicorp/http-echo",
port: 5678,
args: ["-text=hello, wingnuts!"],
) as "echo";

let backend = new containers.Workload(
name: "backend",
image: "./backend",
port: 3000,
public: true,
env: {
ECHO_URL: echo.internalUrl
}
) as "backend";

In backend/index.js file, we can access the internal URL of the echo workload through process.env.ECHO_URL.

Check out the full microservice example here.

API Reference

name: str

This is a required option and must be a a unique name for the workload within the application.

In the tf-aws target, this name will be used as the name of the Helm chart and the name of all the resources associated with the workload in your Kubernetes cluster.

image: str

This is another required option and can either be the name of a publicly available docker image or a relative path to a docker build context directory (with a Dockerfile in it).

port: num?

  • port: num? (optional): internal port number listening. This is required to connect to a server running inside the container.

public: bool?

If this option is enabled, this workload will be accessible from the public internet through the URL returned from publicUrl. When disabled, the container can only be accessed by other workloads in the application via its privateUrl.

When running in sim, the container will be accessible through a localhost port.

When running on tf-aws (EKS), an Ingress resource will be defined for this workload and an ALB (Application Load Balancer) will be allocated. The publicUrl of this workload will contain the fully qualified host name that can be used to access the container from the public internet.

By default, containers are only accessible from within the same application through their privateUrl.

When public is enabled, port must also be set.

readiness: str?

If this is specified, it is the URL path to send HTTP GET requests to in order to determine that the workload has finished initialization.

When deployed to Kubernetes, this is implemented using a readiness probe.

By default, readiness probes are disabled.

replicas: num?

Defines the number of container instances needed for this workload.

When running in the simulator, this option is ignored and there is always a single container.

When running in Kubernetes, this is implemented by setting replicas in the Deployment resource that defines this workload.

By default this is set to 1 replica.

sources: Array<str>?

A list of glob patterns which are used to match the source files of the container. If any of these files change, the image is rebuilt and invalidated. This is only relevant if the image is built from source.

By default, this is all the files under the image directory.

args and env

  • args: Array<str>? (optional): arguments to pass to the entrypoint.
  • env: Map<str>? (optional): environment variables.

Target-specific details

Simulator (sim)

When executed in the Wing Simulator, the workload is started within a local Docker container.

AWS (tf-aws)

Workloads are deployed to a Kubernetes cluster running on Amazon EKS.

For each application, a Helm chart is synthesized with a Deployment, Service and if the workload is public, an Ingress as well.

By default, a new Amazon EKS cluster will be provisioned for each Wing application. This might be okay in a situation where your cluster hosts only a single application, but it is very common to share a single cluster across multiple applications.

Creating a new EKS cluster

To share a single EKS cluster across multiple Wing applications, you will first need to create a cluster in your AWS account. If you already have a cluster, jump to Deploying into an existing cluster below.

To create a compatible EKS cluster manually, we recommend to use use the tfaws.Cluster resource:

eks.main.w:

bring containers;
new containers.Cluster("my-wing-cluster");

And provision it using Terraform (this operation could take up to 20 minutes...):

wing compile -t tf-aws eks.main.w
cd target/eks.main.tfaws
terraform init
terraform apply

To connect to our new cluster through kubectl, use update-kubeconfig:

aws eks update-kubeconfig --name my-wing-cluster

Then:

$ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 36m

Deploying into an existing EKS cluster

To deploy a workload into an the EKS cluster you just created or to an already existing cluster, you will need to set the following platform values (this can be done using -v X=Y or --values values.yml):

  • eks.cluster_name: The name of the cluster
  • eks.endpoint: The URL of the Kubernetes API endpoint of the cluster
  • eks.certificate: The certificate authority of this cluster.

You can use the eks-values.sh script to obtain the attributes of your EKS cluster.

Install:

$ curl https://raw.githubusercontent.com/winglang/containers/main/eks-values.sh > eks-values.sh
$ chmod +x ./eks-values.sh

Use:

$ ./eks-values.sh CLUSTER-NAME > values.yaml
$ wing compile -t tf-aws --values ./values.yaml main.w

Azure (tf-azure)

Not supported yet.

GCP (tf-gcp)

Not supported yet.

Roadmap

The following is a non-exhaustive list of capabilities we are looking to add to this resource:

Scaling

  • Constraints
  • Autoscaling

Networking

  • Access cloud.* resources from workloads (e.g. put an object in a bucket, poll a queue).
  • Access something like Redis from a workload (unify VPCs)
  • Access non-public workloads from cloud.Function

API

  • Allow defining workloads using inflights (cloud.Service)

Runtime

  • Logs
  • Sidecar containers

Endpoints

  • SSL
  • Custom domains

Platforms

  • ECS
  • GKE
  • AKS
  • fly.io
  • ControlPlane