I am using Strapi within a digital-ocean Kubernetes cluster. The public folder images are handled by a persistent volume claim(PVC). After a redeploy, the images are visible within Strapi and also from http://api.mywebsite.com/uploads/blabla.jpg. An Imaginary image processor located within the same cluster returns a 404 Error when trying to get the same images from Strapi.
What might be the cause of this?
I have tried to build an initContainer like written here https://medium.com/faun/digitalocean-kubernetes-and-volume-permissions-820f46598965 but it did not help.
initContainers:
- name: data-permissions-fix
image: busybox
command: ["/bin/chmod","-R","777", "/backend/public/uploads"]
volumeMounts:
- name: backend-images
mountPath: /backend/public/uploads
The flow is like this: frontend -> ingress -> image-processor (Fastify server) -> imaginary -> backend
Backend:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.18.0 ()
creationTimestamp: null
labels:
io.kompose.service: backend
name: backend
spec:
replicas: 1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
io.kompose.service: backend
spec:
containers:
image: backend
name: backend
ports:
- containerPort: 1337
resources: {}
volumeMounts:
- mountPath: /backend/public/uploads
name: backend-images
readOnly: false
initContainers:
- name: data-permissions-fix
image: busybox
command: ["/bin/chmod","-R","777", "/backend/public/uploads"]
volumeMounts:
- name: backend-images
mountPath: /backend/public/uploads
volumes:
- name: backend-images
persistentVolumeClaim:
claimName: backend-images
initContainers:
- name: init-db
image: busybox
command: ['sh', '-c', 'until nc -z db:5432; do echo waiting for db; sleep 2; done;']
restartPolicy: Always
status: {}
Backend PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: backend-images
name: backend-images
spec:
accessModes:
- ReadWriteOnce
storageClassName: do-block-storage
# persistentVolumeReclaimPolicy: Recycle
resources:
requests:
storage: 1Gi
status: {}
Describe backend pod:
Name: backend-5f-vhx48
Namespace: default
Priority: 0
PriorityClassName: <none>
Node: pool-1-xveq/10.135.181.55
Start Time: Thu, 27 Jun 2019 19:07:31 +0200
Labels: io.kompose.service=backend
pod-template-hash=5f9fb4fbb6
Annotations: <none>
Status: Running
IP: 10.244.1.92
Controlled By: ReplicaSet/backend-5f9fbb6
Init Containers:
init-db:
Container ID: docker://e4728305d970fb2d76f1f203271d3ce902a5ef56
Image: busybox
Image ID: docker-pullable://busybox@sha256:7a4d4ed96e15da96906910d57fc4a13210160
Port: <none>
Host Port: <none>
Command:
sh
-c
until nc -z db:5432; do echo waiting for db; sleep 2; done;
State: Terminated
Reason: Completed
Exit Code: 0
Started: Thu, 27 Jun 2019 19:07:39 +0200
Finished: Thu, 27 Jun 2019 19:07:39 +0200
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-fl98h (ro)
Containers:
backend:
Container ID: docker://b42bea24655d3d40e59985f8fff96bce
Image: backend
Image ID: docker-pullable://backend@sha25663765ef8841b45e4717f047b71446c1058d2
Port: 1337/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 27 Jun 2019 19:07:41 +0200
Ready: True
Restart Count: 0
Environment:
Mounts:
/usr/src/backend/public/uploads from backend-images-teuberkohlhoff (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-fl98h (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
backend-images-teuberkohlhoff:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: backend-images
ReadOnly: false
default-token-fl98h:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-flh72
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events: <none>
Describe PVC:
Name: backend-images
Namespace: default
StorageClass: do-block-storage
Status: Bound
Volume: pvc-de757a78-8b8a-364b3aed3
Labels: io.kompose.service=backend-images
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"creationTimestamp":null,"labels":{"io.kompose.service":"ba...
pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: dobs.csi.digitalocean.com
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Gi
Access Modes: RWO
VolumeMode: Filesystem
Events: <none>
Mounted By: backend-5f-vhx48
Image-processor:
const imaginary = require('imaginary');
const fastify = require('fastify')({ logger: true });
const imageServer = 'http://imaginary:9000/';
fastify.get('*', async (request, reply) => {
const {
filename, type: format, width: imageWidth, url: imageUrl,
} = request.query;
const imageStream = imaginary()
.server(imageServer)
.resize({ width: imageWidth, url: imageUrl, type: format })
.on('error', (err) => {
console.error('Cannot resize the image:', err);
});
reply
.header('Content-Disposition', `attachment; filename="${filename}.${format}"`)
.header('Content-Type', `image/${format}`)
.send(imageStream);
});
const start = async () => {
try {
await fastify.listen(9009, '0.0.0.0');
fastify.log.info(`server listening on ${fastify.server.address().port}`);
} catch (err) {
fastify.log.error('ERROR', err);
process.exit(1);
}
};
start();
The frontend img-url is
I am sorry, it was my error. The Ingress controller was hitting the wrong URL. I will just leave the question in case others are searching for how to setup image processing.
@webdev asked for the Dockerfile:
FROM node:10-alpine
WORKDIR /usr/src/app/backend
RUN echo "unsafe-perm = true" >> ~/.npmrc
RUN apk add --no-cache \
autoconf \
automake \
gcc \
libc-dev \
libtool \
make \
nasm \
zlib-dev
RUN npm install -g strapi@beta
COPY . .
# COPY strapi.sh ./
RUN chmod +x ./strapi.sh
EXPOSE 1337
# COPY healthcheck.js ./
HEALTHCHECK --interval=15s --timeout=5s --start-period=30s \
CMD node /usr/src/api/healthcheck.js
CMD ["./strapi.sh"]
Strapi.sh:
#!/bin/sh
set -ea
_stopStrapi() {
echo "Stopping strapi"
kill -SIGINT "$strapiPID"
wait "$strapiPID"
}
trap _stopStrapi TERM INT
cd /usr/src/app/backend
APP_NAME=${APP_NAME:-strapi-app}
DATABASE_CLIENT=${DATABASE_CLIENT:-mongo}
DATABASE_HOST=${DATABASE_HOST:-localhost}
DATABASE_PORT=${DATABASE_PORT:-27017}
DATABASE_NAME=${DATABASE_NAME:-strapi}
DATABASE_SRV=${DATABASE_SRV:-false}
EXTRA_ARGS=${EXTRA_ARGS:-}
FRESH_BOOTSTRAP=false
if [ ! -f "$APP_NAME/package.json" ]
then
strapi new ${APP_NAME} --dbclient=$DATABASE_CLIENT --dbhost=$DATABASE_HOST --dbport=$DATABASE_PORT --dbsrv=$DATABASE_SRV --dbname=$DATABASE_NAME --dbusername=$DATABASE_USERNAME --dbpassword=$DATABASE_PASSWORD --dbssl=$DATABASE_SSL --dbauth=$DATABASE_AUTHENTICATION_DATABASE $EXTRA_ARGS
strapi new "${APP_NAME}" \
"--dbclient=$DATABASE_CLIENT" \
"--dbhost=$DATABASE_HOST" \
"--dbport=$DATABASE_PORT" \
"--dbsrv=$DATABASE_SRV" \
"--dbname=$DATABASE_NAME" \
"--dbusername=$DATABASE_USERNAME" \
"--dbpassword=$DATABASE_PASSWORD" \
"--dbssl=$DATABASE_SSL" \
"--dbauth=$DATABASE_AUTHENTICATION_DATABASE" \
$EXTRA_ARGS \
--dbforce
FRESH_BOOTSTRAP=true
elif [ ! -d "$APP_NAME/node_modules" ]
then
npm install --prefix "./$APP_NAME"
FRESH_BOOTSTRAP=true
fi
cd $APP_NAME
if [ "$NODE_ENV" = "production" ]
then
strapi start &
else
strapi develop &
fi
strapiPID=$!
wait "$strapiPID"