Erpnext
Picture from erpnext.com
To keep in line with the instructions in the erpnext helm repo, we will be using helm and namespaces.
Note, make sure longhorn is installed. It is possible to avvoid it by pre-creating a local-storage pvc and referencing it in erpnext_values
, but for testing I found it easier to install longhorn and reference it as storageClass instead. This way you will have RWX (the volume cna be mounted as read-write by many nodes) compatible volumes ready for scaling as well.
Add repo
helm repo add frappe https://helm.erpnext.com && \
helm repo update
Create namespace
kubectl create namespace erpnext
Install with helm
helm install frappe-bench -n erpnext -f erpnext_values.yaml frappe/erpnext
erpnext_values.yaml
persistence:
worker:
storageClass: "longhorn"
mariadb:
enabled: true
global:
storageClass: "longhorn"
Watch creation
Wait for the containers to be created and settle down
kubectl get pods -n erpnext --watch
You should eventually see something like this
NAME READY STATUS RESTARTS AGE
frappe-bench-erpnext-conf-bench-20230204220652-rjtxx 0/1 Completed 0 100s
frappe-bench-erpnext-gunicorn-7989dfb6c8-p7njf 1/1 Running 0 100s
frappe-bench-erpnext-nginx-684d567845-5f845 1/1 Running 0 100s
frappe-bench-erpnext-scheduler-86648bd8d7-h6vrf 1/1 Running 3 (62s ago) 100s
frappe-bench-erpnext-socketio-75bd4648b6-gsbqx 1/1 Running 3 (49s ago) 100s
frappe-bench-erpnext-worker-d-74f5b8fdd4-xg6rp 1/1 Running 3 (55s ago) 100s
frappe-bench-erpnext-worker-l-5dcbbc79d8-8bd8g 1/1 Running 3 (51s ago) 100s
frappe-bench-erpnext-worker-s-54968545c4-2mtkv 1/1 Running 3 (59s ago) 100s
frappe-bench-mariadb-0 1/1 Running 0 100s
frappe-bench-redis-cache-master-0 1/1 Running 0 100s
frappe-bench-redis-queue-master-0 1/1 Running 0 100s
frappe-bench-redis-socketio-master-0 1/1 Running 0 100s
Create a new site template
helm template frappe-bench -n erpnext frappe/erpnext -f erpnext_newsite_values.yaml -s templates/job-create-site.yaml > erpnext_newsite_manifest.yaml
The values:
erpnext_newsite_values.yaml
persistence:
worker:
storageClass: "longhorn"
global:
storageClass: "longhorn"
jobs:
createSite:
enabled: true
siteName: "erpnext.k3s"
adminPassword: "secret"
Templated manifest:
erpnext_newsite_manifest.yaml
---
# Source: erpnext/templates/job-create-site.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: frappe-bench-erpnext-new-site-20230204221209
labels:
helm.sh/chart: erpnext-6.0.5
app.kubernetes.io/name: erpnext
app.kubernetes.io/instance: frappe-bench
app.kubernetes.io/version: "v14.15.1"
app.kubernetes.io/managed-by: Helm
spec:
backoffLimit: 0
template:
spec:
serviceAccountName: frappe-bench-erpnext
securityContext:
supplementalGroups:
- 1000
initContainers:
- name: validate-config
image: "frappe/erpnext:v14.15.1"
imagePullPolicy: IfNotPresent
command: ["bash", "-c"]
args:
- >
export start=`date +%s`;
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( `date +%s`-start > 600 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
resources: {}
securityContext:
capabilities:
add:
- CAP_CHOWN
volumeMounts:
- name: sites-dir
mountPath: /home/frappe/frappe-bench/sites
containers:
- name: create-site
image: "frappe/erpnext:v14.15.1"
imagePullPolicy: IfNotPresent
command: ["bash", "-c"]
args:
- >
bench new-site $(SITE_NAME)
--no-mariadb-socket
--db-type=$(DB_TYPE)
--db-host=$(DB_HOST)
--db-port=$(DB_PORT)
--admin-password=$(ADMIN_PASSWORD)
--mariadb-root-username=$(DB_ROOT_USER)
--mariadb-root-password=$(DB_ROOT_PASSWORD)
--install-app=erpnext
;rm -f currentsite.txt
env:
- name: "SITE_NAME"
value: "erpnext.k3s"
- name: "DB_TYPE"
value: mariadb
- name: "DB_HOST"
value: frappe-bench-mariadb
- name: "DB_PORT"
value: "3306"
- name: "DB_ROOT_USER"
value: "root"
- name: "DB_ROOT_PASSWORD"
valueFrom:
secretKeyRef:
key: mariadb-root-password
name: frappe-bench-mariadb
- name: "ADMIN_PASSWORD"
value: "secret"
resources: {}
securityContext:
capabilities:
add:
- CAP_CHOWN
volumeMounts:
- name: sites-dir
mountPath: /home/frappe/frappe-bench/sites
- name: logs
mountPath: /home/frappe/frappe-bench/logs
restartPolicy: Never
volumes:
- name: sites-dir
persistentVolumeClaim:
claimName: frappe-bench-erpnext
readOnly: false
- name: logs
emptyDir: {}
Apply the templated manifest
kubectl apply -n erpnext -f erpnext_newsite_manifest.yaml
View site creation
Get jobs
kubectl get jobs -n erpnext
Result
NAME COMPLETIONS DURATION AGE
frappe-bench-erpnext-conf-bench-20230204220652 1/1 70s 9m25s
frappe-bench-erpnext-new-site-20230204221209 0/1 8s 8s
Get logs for the job
kubectl logs job/frappe-bench-erpnext-new-site-20230204221209 -f -n erpnext
Result
Defaulted container "create-site" out of: create-site, validate-config (init)
Installing frappe...
Updating DocTypes for frappe : [========================================] 100%
Updating country info : [========================================] 100%
Updating Dashboard for frappe
Installing erpnext...
Updating DocTypes for erpnext : [========================================] 100%
Updating customizations for Address
Updating customizations for Contact
Updating Dashboard for erpnext
*** Scheduler is disabled ***
Add ingress
Without tls
Apply ingress
kubectl apply -f erpnext_notls_ingress.yaml -n erpnext
Add fake domain to /etc/hosts
Add something like this to the last line (use your server ip and desired fake hostname):
12.345.67.89 erpnext.k3s
or with a commmand:
sudo -- sh -c "echo '<server ip> erpnext.k3s' >> /etc/hosts"
Go to site
http://erpnext.k3s
With tls
Note
If you first followed the steps above with a non tls version, you will have a site called erpnext.k3s. Erpnext expects a host header with this domain in it, and this is why we set it in /etc/hosts in the non-tls version.
So when you want a tls version, you have two choices.
Reinstall
Edit the values in the manifests used above to something that is correct, like erpnext.your.domain.com when installing / reinstalling.
OR
Add Middleware
Add a middleware that replaces erpnext.your.domain.com
with erpnext.k3s
:
kubectl apply -f erpnext_replace_host_header_middleware.yaml
erpnext_replace_host_header_middleware.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: replace-host
spec:
headers:
customRequestHeaders:
host: "erpnext.k3s"
Set environment variables
export DOMAIN=dog.example.com
export EMAIL=name@example.com
Apply manifest
cat ./manifests/erpnext_tls_ingress.yaml | envsubst | kubectl apply -f -
erpnext_tls_ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: erpnext-tls-ingress
annotations:
spec.ingressClassName: traefik
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd, default-replace-host@kubernetescrd
spec:
rules:
- host: erpnext.${DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frappe-bench-erpnext
port:
number: 8080
tls:
- secretName: erpnext-tls
hosts:
- erpnext.${DOMAIN}
Go to site
https://erpnext.yourdomain.com
Credentials
username: Administrator
password: secret