Example: Deploying a Three-Tier Web Application¶
This document walks through a complete example of a typical three-tier web application (frontend, backend, database). It is not a step-by-step tutorial but rather a case study demonstrating how the platform's features and best practices work together.
This guide assumes you are familiar with the concepts covered in our other "how-to" guides. Before proceeding, we recommend you review:
- Running Stateless Applications
- Managing Application Secrets
- Configuring Pod Security Contexts
- Configuring Application Networking
- Exposing Applications to the Internet
- The service guide for the Databases Service.
Application Architecture¶
We will be deploying an application with the following architecture. An
HTTPProxy resource exposes the frontend to the internet. The frontend
communicates with the backend, which in turn communicates with a managed
PostgreSQL database. All components run within the same, secured namespace.
graph TD
subgraph Internet
User[User]
end
subgraph "The Contain Platform"
subgraph "Kubernetes Cluster"
subgraph "Application Namespace"
Ingress(HTTPProxy)
FrontendSvc[Frontend Service]
FrontendDeploy[Frontend Deployment]
BackendSvc[Backend Service]
BackendDeploy[Backend Deployment]
DatabaseCR(Database CR)
end
end
ManagedDB[(Managed<br>PostgreSQL<br>Instance)]
end
User --> Ingress;
Ingress --> FrontendSvc;
FrontendSvc --> FrontendDeploy;
FrontendDeploy --> BackendSvc;
BackendSvc --> BackendDeploy;
BackendDeploy --> ManagedDB;
DatabaseCR -.-> ManagedDB;
1. Provisioning the Database¶
First, we provision a PostgreSQL database using the managed Databases
Service. We create a Database custom resource, which tells the platform's
db-operator to create a new database and a secret containing the credentials.
For more details, see the Getting Started with Databases guide.
apiVersion: "kinda.rocks/v1beta1"
kind: "Database"
metadata:
name: "three-tier-app-db"
spec:
# Name of the secret the operator will create
secretName: db-credentials
# Name of the pre-existing database server instance
instance: prod1-dc4-dbaas01.netic-platform.shared.k8s.netic.dk
deletionProtected: true
backup:
enable: false
2. Deploying the Backend¶
The backend is a stateless application that connects to the database. Its
Deployment manifest includes several key configurations:
- A compliant
securityContext, as required by the platform. - An
envFromblock to consume the database credentials from theSecretcreated in Step 1.
For more details, see the Managing Application Secrets and Configuring Pod Security Contexts guides.
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
labels:
app.kubernetes.io/name: backend
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: backend
template:
metadata:
labels:
app.kubernetes.io/name: backend
spec:
securityContext:
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
runAsNonRoot: true
containers:
- name: backend
image: your-registry/backend:v1.0.0
ports:
- containerPort: 8080
name: http
# Consume the database credentials as environment variables
envFrom:
- secretRef:
name: db-credentials
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
3. Deploying the Frontend¶
The frontend is another stateless application. Its Deployment is similar to
the backend's but includes an additional label, netic.dk/network-ingress:
"contour", which is required to allow traffic from the ingress controller.
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
labels:
app.kubernetes.io/name: frontend
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: frontend
template:
metadata:
labels:
app.kubernetes.io/name: frontend
# This label is required to receive traffic from the ingress controller
netic.dk/network-ingress: "contour"
spec:
securityContext:
runAsUser: 1002
runAsGroup: 1002
fsGroup: 1002
runAsNonRoot: true
containers:
- name: frontend
image: your-registry/frontend:v1.0.0
ports:
- containerPort: 8080
name: http
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
4. Enabling Network Communication¶
By default, the platform's network is "default-deny." To allow the frontend and
backend to communicate, we create Service resources for each and a
LocalNetworkConfig to define the allowed traffic path.
For more details, see the Configuring Application Networking guide.
---
# Service for the Frontend
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
spec:
selector:
app.kubernetes.io/name: frontend
ports:
- protocol: TCP
name: http
port: 80
targetPort: 8080
---
# Service for the Backend
apiVersion: v1
kind: Service
metadata:
name: backend-svc
spec:
selector:
app.kubernetes.io/name: backend
ports:
- protocol: TCP
name: http
port: 80
targetPort: 8080
---
# LocalNetworkConfig to allow frontend to talk to backend
apiVersion: networking.tcs.trifork.com/v1alpha1
kind: LocalNetworkConfig
metadata:
name: frontend-to-backend-policy
spec:
components:
frontend:
podSelector:
matchLabels:
app.kubernetes.io/name: frontend
dependsOn:
- component: backend
port: 80
backend:
podSelector:
matchLabels:
app.kubernetes.io/name: backend
5. Exposing the Application to the Internet¶
Finally, we expose the frontend to the internet using the recommended approach
of an HTTPProxy resource for traffic routing and a Certificate resource for
automatic TLS.
For more details, see the Exposing Applications to the Internet guide.
---
# First, request the TLS certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: three-tier-app-certificate
spec:
secretName: three-tier-app-tls
dnsNames:
- my-three-tier-app.your-domain.com
issuerRef:
name: letsencrypt
kind: ClusterIssuer
---
# Next, create the HTTPProxy to route traffic
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
name: three-tier-app-httpproxy
spec:
virtualhost:
fqdn: my-three-tier-app.your-domain.com
tls:
secretName: three-tier-app-tls
routes:
- conditions:
- prefix: /
services:
- name: frontend-svc
port: 80
With all these components deployed via GitOps, the three-tier application is now securely running, connected, and exposed to the internet, following all platform best practices.