I am trying to run Grav CMS on Kubernetes. However I am running into a permissions problem. Grav is not able to write to the mounted volume.
These are the relevant object definitions. The latest version of Grav is already extracted to the persistent volume bound to grav-data
.
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
volumes:
- name: shared-files
persistentVolumeClaim:
claimName: grav-data
- name: nginx-config-volume
configMap:
name: nginx-config
containers:
- name: app
image: php:7.4-fpm
imagePullPolicy: Always
volumeMounts:
- name: shared-files
mountPath: /var/www/html
- name: nginx
image: nginx:1.7
volumeMounts:
- name: shared-files
mountPath: /var/www/html
- name: nginx-config-volume
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: tty
image: busybox:latest
command: [ "/bin/sh", "-c", "sleep 6000" ]
volumeMounts:
- name: shared-files
mountPath: /var/www/html
---
# configMap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-config
data:
nginx.conf: |
events {}
http {
error_log /dev/stdout info;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /var/www/html;
index index.php;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}
}
From the definitions above, php-fpm should be running as root. But when I exec into the busybox container
$ k exec -it --container=tty web
$ wget -O - localhost
Connecting to localhost (127.0.0.1:80)
writing to stdout
<br />
<b>Fatal error</b>: Uncaught RuntimeException: Creating directory failed for /var/www/html/cache/compiled/files/40779d000b68629af00dd987148afc06.yaml.php in /var/www/html/ve
ndor/rockettheme/toolbox/File/src/File.php:325
Stack trace:
#0 /var/www/html/vendor/rockettheme/toolbox/File/src/PhpFile.php(31): RocketTheme\Toolbox\File\File->save(Array)
#1 /var/www/html/system/src/Grav/Common/File/CompiledFile.php(65): RocketTheme\Toolbox\File\PhpFile->save(Array)
#2 /var/www/html/system/src/Grav/Common/Config/Setup.php(215): Grav\Common\File\CompiledYamlFile->content()
#3 /var/www/html/system/src/Grav/Common/Service/ConfigServiceProvider.php(30): Grav\Common\Config\Setup->init()
#4 /var/www/html/vendor/pimple/pimple/src/Pimple/Container.php(118): Grav\Common\Service\ConfigServiceProvider->Grav\Common\Service\{closure}(Object(Grav\Common\Grav))
#5 /var/www/html/system/src/Grav/Common/Grav.php(166): Pimple\Container->offsetGet('setup')
#6 /var/www/html/system/src/Grav/Common/Grav.php(492): Grav\Common\Grav->Grav\Common\{closure}()
#7 /var/ in <b>/var/www/html/system/src/Grav/Common/File/CompiledFile.php</b> on line <b>81</b><br />
- 100% |******************************************************************************************************************************| 1167 0:00:00 ETA
written to stdout
I've tried adding a securityContext
to the pod spec just to make sure nginx and php are run by the same user, but this prevents nginx from binding on 80/443. I've also tried exec-ing into the pod and I am able to modify files on the PV manually from both containers. How can I set up my pod spec so that Grav can write to the persistent volume claim mounted to /var/www/html
?
UPDATE I don't have time to look further into it right now, but I suspect this is related to php-fpm
spawning child processes as www-data
user.
First of all, it might be easier to use the Docker provided by Grav.
I tried to reproduce your problem with the following manifest:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grav-data
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: standard
---
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
run: web
name: web
spec:
replicas: 1
selector:
matchLabels:
run: web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
run: web
spec:
volumes:
- name: shared-files
persistentVolumeClaim:
claimName: grav-data
- name: nginx-config-volume
configMap:
name: nginx-config
containers:
- name: app
image: php:7.4-fpm
imagePullPolicy: Always
volumeMounts:
- name: shared-files
mountPath: /usr/share/nginx/html:ro
- name: nginx
image: nginx:1.7
volumeMounts:
- name: shared-files
mountPath: /usr/share/nginx/html:ro
- name: nginx-config-volume
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: tty
image: busybox:latest
command: [ "/bin/sh", "-c", "sleep 6000" ]
volumeMounts:
- name: shared-files
mountPath: /usr/share/nginx/html:ro
---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-config
data:
nginx.conf: |
events {}
http {
error_log /dev/stdout info;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name localhost;
root /usr/share/www/html;
index index.php;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}
}
I'm using the Minikube StorageClass, so it provides me automatically with a PersistentVolume at /tmp/hostpath-provisioner/..., mounted at /usr/share/www/html:ro.
I've installed Grav with composer and ran:
wget -O - localhost
Connecting to localhost (127.0.0.1:80)
wget: server returned error: HTTP/1.1 404 Not Found
Same with localhost/grav. It would be great to have some additional details to reproduce the problem (503).
I think your life will be so much easier if you deploy PHP-FPM and NGINX in separate pods with the service abstraction, considering you need to configure communication through the tcp port (127.0.0.1:9000) or unix socket (/var/run/php-fpm.sock).
You're probably not the first to install PHP-FPM and NGINX, there's a great tutorial on Digital Ocean for this set-up.