Search code examples
dockerkubernetessubdirectorydocker-volume

How to correctly use subdirs with projected volumes and configMaps


I have an nginx web server deployed with docker swarm, and I want to be able to deploy it also with kubernetes.

Right now I'm having trouble inserting the nginx configuration files into the container.

I'll first post here what I already do in docker swarm, and then what I tried in kubernetes.

Dockerfile:

FROM    "nginx:1.19.1-alpine"   AS nginx
[...]
RUN                                                             \
        rm -fv  /etc/nginx/nginx.conf                           && \
        ln -svT /usr/local/etc/nginx/nginx.conf                 \
                /etc/nginx/nginx.conf                           && \
        rm -frv /etc/nginx/conf.d                               && \
        ln -svT /usr/local/etc/nginx/conf.d                     \
                /etc/nginx/conf.d
[...]

Basically I set up the image so that I can place my custom nginx config files into /usr/local/etc/ instead of /etc/

Docker swarm:

docker-compose.yaml:

configs:
    nginx_conf:
        file: /run/configs/www/etc/nginx/nginx.conf
    nginx_conf_security-parameters:
        file: /run/configs/www/etc/nginx/conf.d/security-parameters.conf
    nginx_conf_server:
        file: /run/configs/www/etc/nginx/conf.d/server.conf

networks:
    alejandro-colomar:

services:
    www:
        configs:
        -
            mode: 0440
            source: nginx_conf
            target: /usr/local/etc/nginx/nginx.conf
        -
            mode: 0440
            source: nginx_conf_security-parameters
            target: /usr/local/etc/nginx/conf.d/security-parameters.conf
        -
            mode: 0440
            source: nginx_conf_server
            target: /usr/local/etc/nginx/conf.d/server.conf
        deploy:
            placement:
                constraints:
                -   node.role == worker
            replicas: 1
            restart_policy:
                condition: any
        image: "alejandrocolomar/www:0.16-a6-kube"
        networks:
        -
            "alejandro-colomar"
        ports:
        -   "32001:8080"

version: "3.8"

Here I take the nginx custom config files, which I first put in /run/configs/ with a script so that they are in ram, and introduce them in the container as configs in the right place (/usr/local/etc/nginx/ and its subdir conf.d/).

I'd like to do the same in kubernetes, and I read that I should use a projected volume for that (if it's doable with normal volumes or in some other way, without having to use any dirty workarounds, I'm also open to that), so I tried the following (after seeing some examples which weren't very clear to me):

config.sh:


kubectl create configmap "nginx-conf-cm"                        \
        --from-file "/run/configs/www/etc/nginx/nginx.conf"
kubectl create configmap "nginx-conf-security-parameters-cm"    \
        --from-file "/run/configs/www/etc/nginx/conf.d/security-parameters.conf"
kubectl create configmap "nginx-conf-server-cm"                 \
        --from-file "/run/configs/www/etc/nginx/conf.d/server.conf"

deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: www-deploy
spec:
    replicas: 1
    selector:
        matchLabels:
            service: www-svc
    template:
        metadata:
            labels:
                service: www-svc
        spec:
            containers:
            -
                image: "alejandrocolomar/www:0.16-a6-kube"
                name: www-container
                volumeMounts:
                -
                    mountPath: /usr/local/etc/nginx/
                    name: nginx-volume
                    readOnly: true
            volumes:
            -
                name: nginx-volume
                projected:
                    sources:
                    -
                        configMap:
                            name: nginx-conf-cm
                            path: "nginx.conf"
                    -
                        configMap:
                            name: nginx-conf-security-parameters-cm
                            path: "conf.d/security-parameters.conf"
                    -
                        configMap:
                            name: nginx-conf-server-cm
                            path: "conf.d/server.conf"

service.yaml: (This one I put it here only for completeness)

apiVersion: v1
kind: Service
metadata:
    name: www
spec:
    ports:
    -
        nodePort: 32001
        port: 8080
    selector:
        service: www-svc
    type: NodePort

The deployment failed, of course, but it wasn't very wrong I think. When I entered in the container to debug it, the problem was that the three files were placed into /usr/local/etc/nginx/, and the subdir conf.d/ was not created:

/usr/local/etc/nginx/nginx.conf
/usr/local/etc/nginx/security-parameters.conf
/usr/local/etc/nginx/server.conf

How should I fix that (presumably in deployment.yaml) so that I have the following files in the container?:

/usr/local/etc/nginx/nginx.conf
/usr/local/etc/nginx/conf.d/security-parameters.conf
/usr/local/etc/nginx/conf.d/server.conf

Solution

  • There are multiple ways to handle this. One solution would be to create 3 separate volumes from these individual config maps and mount each one into its appropriate destination file/folder.

    volumes:
            - name: nginx-cm
              configMap:
                  name: nginx-conf-cm
            - name: nginx-sec
              configMap:
                  name: nginx-conf-security-parameters-cm
            - name: nginx-serv
              configMap:
                  name: nginx-conf-server-cm
    
            ...
            containers:
                -
                    image: "alejandrocolomar/www:0.16-a6-kube"
                    name: www-container
                    volumeMounts:
                    -   mountPath: /usr/local/etc/nginx/nginx.conf
                        name: nginx-cm
                        subPath: nginx.conf
                        readOnly: true
                    -   mountPath: /usr/local/etc/nginx/conf.d/security-parameters.conf
                        name: nginx-sec
                        subPath: security-parameters.conf
                        readOnly: true
                    -   mountPath: /usr/local/etc/nginx/conf.d/server.conf
                        name: nginx-serv
                        subPath: server.conf
                        readOnly: true
    
    

    using mountPath and subPath in volumeMounts allows you to pick specific file from a given config map (doesn't matter much since you have on file per cm) and mount it as a file (not overriding the other content in the existing folder).

    To explain a bit the code above:

    -   mountPath: /usr/local/etc/nginx/nginx.conf
        name: nginx-cm
        subPath: nginx.conf
    

    tells kubernetes to use volume with the name of nignx-cm (defined in a volume section). Pick file nginx.conf (via subpath) that can be found in the associated config map and expose it in the container (mountPath) at the location /usr/local/etc/nginx/nginx.conf.

    I have not run the code so there might be typos

    PS: note that it might be better to create a custom configuration file inside of ../etc/nginx/conf.d/ instead of overriding ../etc/nginx/nginx.conf. That way you wouldn't need to worry about destroying the original files ../etc/nginx/ and you could mount the whole volume instead of using subpath (to avoid issues with config updates)