This topic describes how to use the server
workload type.
The server
workload type allows you to deploy traditional network applications on
Tanzu Application Platform.
Using an application workload specification, you can build and deploy application
source code to a manually-scaled Kubernetes deployment which exposes an in-cluster Service endpoint.
If required, you can use environment-specific LoadBalancer Services or Ingress resources to
expose these applications outside the cluster.
The server
workload is suitable for traditional applications, including HTTP applications,
which have the following characteristics:
- Store state locally
- Run background tasks outside of requests
- Provide multiple network ports or non-HTTP protocols
- Are not a good match for the
web
workload type
An application using the server
workload type has the following features:
- Does not natively autoscale, but you can use these applications with the Kubernetes Horizontal Pod Autoscaler.
- By default, is exposed only within the cluster using a
ClusterIP
service. - Uses health checks if defined by a convention.
- Uses a rolling update pattern by default.
When creating a workload with the tanzu apps workload create
command, you can use the
--type=server
argument to select the server
workload type.
For more information, see Use the server Workload Type later in this topic.
You can also use the apps.tanzu.vmware.com/workload-type:server
annotation in the
YAML workload description to support this deployment type.
The spring-sensors-consumer-web
workload in
Bind an application workload to the service instance
in the Get started guide is a good match for the server
workload type.
This is because it runs continuously to extract information from a RabbitMQ queue, and stores the resulting data locally in memory and presents it through a web UI.
In the Services Toolkit example in Bind an application workload to the service instance, you can update the spring-sensors-consumer-web
workload to use the server
supply
chain by changing the workload:
tanzu apps workload apply spring-sensors-consumer-web --type=server
This shows the change in the workload label and prompts you to accept the change. After the workload completes the new deployment, there are a few differences:
-
The workload no longer exposes a URL. It's available within the cluster as
spring-sensors-consumer-web
within the namespace, but you must usekubectl port-forward service/spring-sensors-consumer-web 8080
to access the web service on port 8080.You can set up a Kubernetes Ingress rule to direct traffic from outside the cluster to the workload. Use an Ingress rule to specify that specific host names or paths must be routed to the application. For more information about Ingress rules, see the Kubernetes documentation
-
The workload no longer autoscales based on request traffic. For the
spring-sensors-consumer-web
workload, this means that it never spawns a second instance that consumes part of the request queue. Also, it does not scale down to zero instances.
In addition to the common supply chain parameters, server
workloads can expose one or more network
ports from the application to the Kubernetes cluster by using the ports
parameter.
This parameter is a list of port objects, similar to a Kubernetes service specification.
If you do not configure the ports
parameter, the applied container conventions in the cluster
establishes the set of exposed ports.
The following configuration exposes two ports on the Kubernetes cluster under the my-app
host name:
apiVersion: carto.run/v1alpha1
kind: Workload
metadata:
name: my-app
labels:
apps.tanzu.vmware.com/workload-type: server
spec:
params:
- name: ports
value:
- containerPort: 2025
name: smtp
port: 25
- port: 8080
...
This snippet configures:
- One service on port 25, which is redirected to port 2025 on the application.
- One service on port 8080, which is routed to port 8080 on the application.
You can set the ports
parameter from the tanzu apps workload create
command
as --param-yaml 'ports=[{"port": 8080}]'
.
The following values are valid within the ports
argument:
Field | Value |
---|---|
port |
The port on which the application is exposed to the rest of the cluster |
containerPort |
The port on which the application listens for requests. Defaults to port if not set. |
name |
A human-readable name for the port. Defaults to port if not set. |
Expose HTTP server
workloads by creating an Ingress resource and using cert-manager to provision
TLS signed certificates.
-
Use the
spring-sensors-consumer-web
workload as an example from Bind an application workload to the service instance. Create the followingIngress
:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: spring-sensors-consumer-web namespace: DEVELOPER-NAMESPACE annotations: cert-manager.io/cluster-issuer: tap-ingress-selfsigned ingress.kubernetes.io/force-ssl-redirect: "true" kubernetes.io/ingress.class: contour kubernetes.io/tls-acme: "true" spec: tls: - secretName: spring-sensors-consumer-web hosts: - "spring-sensors-consumer-web.INGRESS-DOMAIN" rules: - host: "spring-sensors-consumer-web.INGRESS-DOMAIN" http: paths: - pathType: Prefix path: / backend: service: name: spring-sensors-consumer-web port: number: 8080
- Replace
DEVELOPER-NAMESPACE
with your developer namespace - Replace
INGRESS-DOMAIN
with the domain name defined intap-values.yaml
during the installation. - Set the annotation
cert-manager.io/cluster-issuer
to theshared.ingress_issuer
value configured during installation or leave it astap-ingress-selfsigned
to use the default one. - Update the port exposed by your
Service
resource, in the previous snippet it is set to8080
.
- Replace
-
Access the
server
workload with https:curl -k https://spring-sensors-consumer-web.INGRESS-DOMAIN
Tanzu Application Platform allows you to create new workload types. Start by taking the existing
server-template
ClusterConfigTemplate
and edit it to add an Ingress
resource when this new
type of workload is created.
Before you begin:
- Make sure you delete the
Ingress
resource previously created. - Install the
yq
cli on your computer.
Procedure
-
Save the existing
server-template
in a local file:kubectl get ClusterConfigTemplate server-template -oyaml > secure-server-template.yaml
-
Extract
.spec.ytt
field from this file and create another file:yq eval '.spec.ytt' secure-server-template.yaml > spec-ytt.yaml
-
In the next step, you will add the
Ingress
resource snippet tospec-ytt.yaml
. This step provides a sampleIngress
resource snippet below. Make the following edits before adding theIngress
resource snippet tospec-ytt.yaml
:- Replace
INGRESS-DOMAIN
with the Ingress domain you set during the installation. - Set the annotation
cert-manager.io/cluster-issuer
to theshared.ingress_issuer
value configured during installation or leave it astap-ingress-selfsigned
to use the default one. - This configuration is based on your workload service running on port
8080
.
The
Ingress
resource snippet looks like this:--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: #@ data.values.workload.metadata.name annotations: cert-manager.io/cluster-issuer: tap-ingress-selfsigned ingress.kubernetes.io/force-ssl-redirect: "true" kubernetes.io/ingress.class: contour kubernetes.io/tls-acme: "true" kapp.k14s.io/change-rule: "upsert after upserting Services" labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name }) spec: tls: - secretName: #@ data.values.workload.metadata.name hosts: - #@ data.values.workload.metadata.name + ".INGRESS-DOMAIN" rules: - host: #@ data.values.workload.metadata.name + ".INGRESS-DOMAIN" http: paths: - pathType: Prefix path: / backend: service: name: #@ data.values.workload.metadata.name port: number: 8080
- Replace
-
Add the above
Ingress
resource snippet to thespec-ytt.yaml
file and save. Look for theService
resource, and insert the snippet before the last#@ end
. For example:# THE TOP OF THE FILE IS NOT SHOWN --- apiVersion: v1 kind: Service metadata: name: #@ data.values.workload.metadata.name labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name }) spec: selector: #@ data.values.config.metadata.labels ports: #@ hasattr(data.values.params, "ports") and len(data.values.params.ports) or assert.fail("one or more ports param must be provided.") #@ declared_ports = {} #@ if "ports" in data.values.params: #@ declared_ports = data.values.params.ports #@ else: #@ declared_ports = struct.encode([{ "containerPort": 8080, "port": 8080, "name": "http"}]) #@ end #@ for p in merge_ports(declared_ports, data.values.config.spec.containers): - #@ p #@ end # NEW INGRESS RESOURCE --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: #@ data.values.workload.metadata.name annotations: cert-manager.io/cluster-issuer: tap-ingress-selfsigned ingress.kubernetes.io/force-ssl-redirect: "true" kubernetes.io/ingress.class: contour kubernetes.io/tls-acme: "true" kapp.k14s.io/change-rule: "upsert after upserting Services" labels: #@ merge_labels({ "app.kubernetes.io/component": "run", "carto.run/workload-name": data.values.workload.metadata.name }) spec: tls: - secretName: #@ data.values.workload.metadata.name hosts: - #@ data.values.workload.metadata.name + ".INGRESS-DOMAIN" rules: - host: #@ data.values.workload.metadata.name + ".INGRESS-DOMAIN" http: paths: - pathType: Prefix path: / backend: service: name: #@ data.values.workload.metadata.name port: number: 8080 # END NEW INGRESS RESOURCE #@ end --- apiVersion: v1 kind: ConfigMap metadata: name: #@ data.values.workload.metadata.name + "-server" labels: #@ merge_labels({ "app.kubernetes.io/component": "config" }) data: delivery.yml: #@ yaml.encode(delivery())
-
Add the above to the
.spec.ytt
property insecure-server-template.yaml
:SPEC_YTT=$(cat spec-ytt.yaml) yq eval -i '.spec.ytt |= strenv(SPEC_YTT)' secure-server-template.yaml
-
Change the name of the
ClusterConfigTemplate
tosecure-server-template
:yq eval -i '.metadata.name = "secure-server-template"' secure-server-template.yaml
-
Create the new
ClusterConfigTemplate
:kubectl apply -f secure-server-template.yaml
-
Verify the new
ClusterConfigTemplate
is in the cluster:kubectl get ClusterConfigTemplate
Expected output:
kubectl get ClusterConfigTemplate NAME AGE api-descriptors 82m config-template 82m convention-template 82m secure-server-template 22s server-template 82m service-bindings 82m worker-template 82m
-
Add the new workload type to the
tap-values.yaml
. The new workload type is namedsecure-server
and thecluster_config_template_name
issecure-server-template
.ootb_supply_chain_basic: supported_workloads: - type: web cluster_config_template_name: config-template - type: server cluster_config_template_name: server-template - type: worker cluster_config_template_name: worker-template - type: secure-server cluster_config_template_name: secure-server-template
-
Update your Tanzu Application Platform installation as follows:
tanzu package installed update tap -p tap.tanzu.vmware.com --values-file "/path/to/your/config/tap-values.yaml" -n tap-install
-
Give privileges to the
deliverable
role to manageIngress
resources:cat <<EOF | kubectl apply -f - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: deliverable-with-ingress labels: apps.tanzu.vmware.com/aggregate-to-deliverable: "true" rules: - apiGroups: - networking.k8s.io resources: - ingresses verbs: - get - list - watch - create - patch - update - delete - deletecollection EOF
-
Update the workload type to
secure-server
:Note If you created the
Ingress
resource manually in the previous section, delete it before this.tanzu apps workload apply spring-sensors-consumer-web --type=secure-server
-
After the process finishes, you will see these resources: Deployment, Service and Ingress.
kubectl get ingress,svc,deploy -l carto.run/workload-name=spring-sensors-consumer-web
Expected output:
kubectl get ingress,svc,deploy -l carto.run/workload-name=tanzu-java-web-app-js NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/spring-sensors-consumer-web <none> spring-sensors-consumer-web.INGRESS-DOMAIN 34.111.111.111 80, 443 37s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/spring-sensors-consumer-web ClusterIP 10.32.15.194 <none> 8080/TCP 36m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/spring-sensors-consumer-web 1/1 1 1 37s
Access your
secure-server
workload with https:curl -k https://spring-sensors-consumer-web.INGRESS-DOMAIN