Need help to migrate from pm2 to Docker / Kubernetes

Hi everybody !

First at all, thank you for your work and for this amazing headless CMS, it is very nice to use this free app :blush:

So, my question :
I am using 2 strapi apps in production to manage 2 different websites.
The data about this apps :

System Information
  • Strapi Version: 3.0.2
  • Operating System: Ubuntu 18.04.4
  • Database: MongoDB on Atlas provider
  • Node Version: 12.18.1
  • NPM Version: 6.14.5

This 2 apps are hosted on an Azure virtual machine, using PM2 for the deployment.
MongoDB are hosted on MongoDB Atlas.

Now, we would like to migrate this app to a kubernetes service, to not have 1 billion of virtual machines, one for each service… :sweat_smile:

So, I am trying to migrate our strapi apps to a docker version of them.
My process :

  1. Run a strapi latest version with a local mongo database with docker-compose
  2. Run a strapi 3.0.2 version with a local mongo database with docker-compose
  3. Run a strapi 3.0.2 version connected to our MongoDB Atlas with docker-compose
  4. Try to deploy it on our kubernetes

The first step was done without any problem.

For the second step, I had to migrate to Strapi 3.0.6 because I had a dependencies problem with the 3.0.2 version. I did not upgrade to the last version because there are breaking changes, and I would like to not spend time on this.

The third step was more difficult, but I succeed to do it using this environment variables :

DATABASE_CLIENT: mongo
DATABASE_NAME: my_database_name
DATABASE_HOST: my_database_host
DATABASE_PORT: 27017
DATABASE_USERNAME: my_database_username
DATABASE_PASSWORD: my_database_password
DATABASE_SRV: 'true'
AUTHENTICATION_DATABASE: admin
DATABASE_SSL: 'true'

It works, I succeed to connect myself to my local strapi project but… I don’t have any content-type :scream:
I am not familiar about how strapi is coded, but what I understand is that content-type are hardcoded in my project, and data is stored in my mongo database (is it why I need to restart the server when I modify schema ? :thinking: )

So, I am a little bit lost :confused:
What is the good process ? Should I use docker-compose ?
I read the strapi-docker github repository, I read the strapi docker documentation too, but it still not easy for me.

Somebody could help me to define the process please ?

I add that this is my first time of using docker and kubernetes, I am learning this technologies.

Thank you the community :slight_smile:

1 Like

To share my improvement :

I succeed to run a the local version in a docker container !
I had to rename the folder of the strapi to “app” and define specific versions of node and npm in the packages.json :

"engines": {
  "node": "12.18.1",
  "npm": "6.14.5"
},

Let’s try to run this in Kubernetes :smiley:

I’ll close the topic when that will be finish :slight_smile:

Hi everybody !

I’m coming back about the deployment on kubernetes :slight_smile:

I wrote this manifest :

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: lfg-strapi-landing-page
  labels:
    environment: production
    app: lfg-strapi-landing-page
spec:
  replicas: 1
  selector:
    matchLabels:
      app: lfg-strapi-landing-page
  template:
    metadata:
      labels:
        app: lfg-strapi-landing-page
    spec:
      containers:
      - name: lfg-strapi-landing-page
        image: (my_azure_registry)/lfg-strapi-landing-page:3.0.6
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 250m
          limits:
            cpu: 500m
        env:
        - name: DATABASE_CLIENT
          value: "mongo"
        - name: DATABASE_NAME
          value: "strapi-gatsby"
        - name: DATABASE_HOST
          value: "(my_database_host)"
        - name: DATABASE_PORT
          value: "27017"
        - name: DATABASE_USERNAME
          value: "strapi"
        - name: DATABASE_PASSWORD
          value: "(my_database_password)"
        - name: DATABASE_SRV
          value: 'true'
        - name: AUTHENTICATION_DATABASE
          value: "admin"
        - name: DATABASE_SSL
          value: 'true'
---
apiVersion: v1
kind: Service
metadata:
  name: lfg-strapi-landing-page
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: lfg-strapi-landing-page

I’m not confident, but it looks like ok for me.
But, when I deploy it and read the logs, I got :

Loading…Using strapi 3.0.6
No project found at /srv/app. Creating a new strapi project
Creating a new Strapi application at /srv/app.
Creating a project from the database CLI arguments.
Creating files.

  • Installing dependencies:

And after the installation, it is running a new strapi projet, not mine :’(
It it the same problem than before in local, but I don’t know how to solve it on kubernetes :confused:

Does anybody have an idea ?

I see the question is quite old, but I’ve not seen a reply. If still looking for a reply, what I did is to have my Strapi project folder stored in a private git repo. I added the following dockerfile and build my custom image specific for my project and deployed it using an helm chart with this deployment definition.

dockerfile:
FROM node:14 as builderimage
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY . .
COPY .env.example .env
RUN yarn install
RUN yarn build && yarn cache clean

FROM node:14-slim as build
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY --from=builderimage /usr/src/app .
EXPOSE 80
CMD [ "yarn", "start" ]

Deployment manifest (in helm chart):
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: site-ingress
namespace: {{ .Values.namespace.name }}
annotations:
kubernetes.io/ingress.class: nginx
spec:
tls:
- hosts:
- {{ .Values.app.hostname }}
secretName: backend-secret-tls
rules:
- host: {{ .Values.app.hostname }}
http:
paths:
- path: /
backend:
serviceName: {{ .Values.app.name }}
servicePort: 80

apiVersion: v1
kind: Service
metadata:
name: {{ .Values.app.name }}
namespace: {{ .Values.namespace.name }}
labels:
app: {{ .Values.app.name }}
tier: web
spec:
ports:
- port: 80
selector:
app: {{ .Values.app.name }}
tier: web
type: ClusterIP

apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.app.name }}
namespace: {{ .Values.namespace.name }}
labels:
app: {{ .Values.app.name }}
tier: web
spec:
replicas: 1
selector:
matchLabels:
app: {{ .Values.app.name }}
tier: web
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 1
template:
metadata:
labels:
app: {{ .Values.app.name }}
tier: web
spec:
containers:
- image: {{ .Values.app.image }}:{{ .Values.app.tag }}
name: {{ .Values.app.name }}
env:
- name: DB_HOST
value: {{ .Values.app.db.host }}
- name: DB_NAME
value: {{ .Values.app.db.name }}
- name: DB_SRV
value: “false”
- name: DB_USER
value: {{ .Values.app.db.user }}
- name: DB_PASSWORD
value: {{ .Values.app.db.password }}
- name: CLOUDINARY_NAME
value: {{ .Values.app.cloudinary.name }}
- name: CLOUDINARY_KEY
value: {{ .Values.app.cloudinary.key }}
- name: CLOUDINARY_SECRET
value: {{ .Values.app.cloudinary.secret }}
- name: PORT
value: “{{ .Values.app.port }}”
- name: DB_SSL
value: “false”
readinessProbe:
httpGet:
path: /
port: {{ .Values.app.port }}
httpHeaders:
- name: Host
value: {{ .Values.app.hostname }}
initialDelaySeconds: 15
successThreshold: 1
failureThreshold: 3
timeoutSeconds: 5
periodSeconds: 15
ports:
- containerPort: {{ .Values.app.port }}
name: http

I am using Cloudinary, so I do not need a persistent volume locally mounted to store the content.