Grow your SaaS organically with Programmatic SEO.
Try for free →mkdir saasbase-project
cd saasbase-project
mkdir saasbase-fe
mkdir saasbase-be
We've written a detailed guide on how to build and dockerize your Frontend React app here. Place the project in the saasbase-fe
folder.
docker login
docker build -t sssaini/saasbase-fe .
docker push sssaini/saasbase-fe:0.1
We've written a detailed guide on how to build and dockerize your Backend Node.js app here. Place the project in the saasbase-be
folder.
docker login
docker build -t sssaini/saasbase-be .
docker push sssaini/saasbase-be:0.1
Once you have deployed to Production, you should increase the node count to make the deployment more resilient.
saasbase-cluster
.
Congratulations! Your cluster is now created.
kubectl version
Move it to the correct folder by running:
mv saasbase-cluster-kubeconfig.yaml ~/.kube/config
You should be connected to the Digital Ocean cluster. Verify by running:
➜ ~ kubectl get nodes
NAME STATUS ROLES AGE VERSION
pool-h5wx2v1ut-cudd5 Ready <none> 57m v1.22.7
Create a file called fe.yaml
in the saasbase-project
folder. This will configure how our frontend deployment.
Notice that we're using the LoadBalancer type in the Service. This lets Digital Ocean know that we want an external IP for this service so we can view the app. In the next step, we will set up a custom domain that can be used to reach the app instead of an IP address.
apiVersion: apps/v1
kind: Deployment
metadata:
name: fe-deploy
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: saasbase-fe
template:
metadata:
labels:
app.kubernetes.io/name: saasbase-fe
spec:
containers:
- name: frontend
image: docker.io/sssaini/saasbase-fe:0.1
---
kind: Service
apiVersion: v1
metadata:
name: fe-service
spec:
selector:
app.kubernetes.io/name: saasbase-fe
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 3000
Deploy the Kube configuration by running:
➜ ~ kubectl apply -f fe.yaml
deployment.apps/fe-deploy created
service/fe-service created
Once running, we can get the IP address of the service by:
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
fe-deploy-8448fb4b97-6tgfj 1/1 Running 0 38s
Access the service by:
kubectl get services
The External IP takes about 5 mins to provision. Once assigned, you can view your application by opening the IP in your browser. For me it would be: http://143.198.246.142
Brilliant! I can see my React app running.
We can do exactly the same for our backend deployment. Create a file called
be.yaml
at the root level.
apiVersion: apps/v1
kind: Deployment
metadata:
name: be-deploy
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: saasbase-be
template:
metadata:
labels:
app.kubernetes.io/name: saasbase-be
spec:
containers:
- name: backend
image: docker.io/sssaini/saasbase-be:0.1
---
kind: Service
apiVersion: v1
metadata:
name: be-service
spec:
selector:
app.kubernetes.io/name: saasbase-be
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 7001
Apply the deployment by running:
kubectl apply -f be.yaml
deployment.apps/saasbase-be-deployment created
service/be-service created
Access the service by:
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
fe-deploy-8448fb4b97-kfzg9 1/1 Running 0 16m
be-deploy-5fcb68649d-vj9sp 1/1 Running 0 7m8s
➜ ~ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
be-service LoadBalancer 10.245.148.197 146.190.0.10 80:30791/TCP 6m2s
fe-service LoadBalancer 10.245.5.13 143.198.246.142 80:31387/TCP 15m
kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 91m
Same as before, I can now access by backend by going to the External IP as such:
http://146.190.0.10
Using the External IP works but it's not very user-friendly. We can buy a custom domain from Namecheap to access our services. I bought the domain: bearbill.com
.
We can verify that the Controller is successfully running with:
➜ ~ kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx ingress-nginx-controller-664d8d6d67-kvpkz 1/1 Running 0 85m
ingress-nginx ingress-nginx-controller-664d8d6d67-vkmnk 1/1 Running 0 85m
➜ ~ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.245.223.54 64.225.91.107 80:32152/TCP,443:31302/TCP 84m
ingress-nginx-controller-admission ClusterIP 10.245.98.130 <none> 443/TCP 84m
ingress-nginx-controller-metrics ClusterIP 10.245.188.111 <none> 10254/TCP 84m
bearbill.com
api.bearbill.com
Since we are going to be using the custom domain to access the services, we can update the deployed frontend and backend services to not provision an external IP.
This can be done by simply commenting out type: LoadBalancer
in the service.
Here's what my fe.yaml
looks like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fe-deploy
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: saasbase-fe
template:
metadata:
labels:
app.kubernetes.io/name: saasbase-fe
spec:
containers:
- name: frontend
image: docker.io/sssaini/saasbase-fe:0.1
---
kind: Service
apiVersion: v1
metadata:
name: fe-service
spec:
selector:
app.kubernetes.io/name: saasbase-fe
# type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 3000
Here's what my be.yaml
looks like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: be-deploy
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: saasbase-be
template:
metadata:
labels:
app.kubernetes.io/name: saasbase-be
spec:
containers:
- name: backend
image: docker.io/sssaini/saasbase-be:0.1
---
kind: Service
apiVersion: v1
metadata:
name: be-service
spec:
selector:
app.kubernetes.io/name: saasbase-be
# type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 7001
Apply this change by running:
kubectl apply -f fe.yaml
kubectl apply -f be.yaml
Perfect. Now there shouldn't be an External IP when we run:
kubectl get services
To make the apps accessible with custom domains, we need to set up NGINX so that the traffic can be correctly routed into their respective containers.
Create a deploy.yaml
:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-echo
namespace: default
spec:
tls:
- hosts:
- bearbill.com
- api.bearbill.com
rules:
- host: bearbill.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fe-service
port:
number: 3000
- host: api.bearbill.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: be-service
port:
number: 7001
ingressClassName: nginx
Deploy again with:
kubectl apply -f deploy.yaml
We can make sure that the ingress service was created by:
➜ ~ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-echo nginx bearbill.com,api.bearbill.com 64.225.91.107 80, 443 25m
The backend should now be accessible at: http://api.bearbill.com
. It should be accessible at: http://bearbill.com
.
I'm building a new SaaS to automate content marketing for your SaaS
Tools for SaaS Devs