20

I am unable to properly mount volumes using HostPath within Kubernetes running in Docker and WSL 2. This seems to be a WSL 2 issue when mounting volumes in Kubernetes running in Docker. Anyone know how to fix this?

Here are the steps:

Deploy debug build to Kubernetes for my app. Attach Visual Studio Code using the Kubernetes extension Navigate to the project folder for my application that was attached using the volume mount <= Problem Right Here

When you go and look at the volume mount nothing is there.

C:\Windows\System32>wsl -l -v

NAME STATE VERSION
Ubuntu Running 2
docker-desktop-data Running 2
docker-desktop Running 2
Docker Desktop v2.3.0.3
Kubernetes v1.16.5
Visual Studio Code v1.46.1
====================================================================
Dockerfile
====================================================================
#
# Base image for deploying and running based on Ubuntu
#
# Support ASP.NET and does not include .NET SDK or NodeJs
# 
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
#
# Base image for building .NET based on Ubuntu
#
# 1. Uses .NET SDK image as the starting point 
# 2. Restore NuGet packages
# 3. Build the ASP.NET Core application
#
# Destination is /app/build which is copied to /app later on
#
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build
WORKDIR /src
COPY ["myapp.csproj", "./"]
RUN dotnet restore "./myapp.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "myapp.csproj" -c Release -o /app

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS debug
RUN curl --silent --location https://deb.nodesource.com/setup_12.x | bash -
RUN apt-get install --yes nodejs
ENTRYPOINT [ "sleep", "infinity" ]

#
# Base image for building React based on Node/Ubuntu
#
# Destination is /app/ClientApp/build which is copied to /clientapp later
#
# NOTE: npm run build puts the output in the build directory
#
FROM node:12.18-buster-slim AS clientbuild
WORKDIR /src
COPY ./ClientApp /app/ClientApp
WORKDIR "/app/ClientApp"
RUN npm install
RUN npm run build
#
# Copy clientbuild:/app/ClientApp to /app/ClientApp
#
# Copy build:/app to /app
#
FROM base as final
WORKDIR /app/ClientApp
COPY --from=clientbuild /app/ClientApp .
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "myapp.dll"]

====================================================================
Kubernetes Manifest
====================================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: myapp
  replicas: 1
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: localhost:6000/myapp
        ports:
        - containerPort: 5001
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /local
          name: local
        resources: {}        
      volumes:
      - name: local
        hostPath:
          path: /C/dev/myapp
          type: DirectoryOrCreate
      hostname: myapp
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  type: LoadBalancer
  ports:
  - name: http
    protocol: TCP
    port: 5001
    targetPort: 5001
  selector:
    app: myapp
Richard Crane
  • 1,110
  • 3
  • 9
  • 17
  • 1
    Kubernetes is really bad as a live development environment; the YAML to mount a host directory into a pod are more or less as long as the entire rest of the pod configuration, and then doesn't actually work on non-developer Kubernetes installations. I'd suggest building and testing your application locally and only bringing Kubernetes into the picture when you're starting on integration testing. – David Maze Jul 09 '20 at 10:39
  • Thanks, but this is something we were able to do successfully prior to WSL 2. We went back and verified that it works, just not on WSL 2. The value to getting this to "work again" is very high since we have a microservice architecture and developing within a Kubernetes cluster is very valuable. – Richard Crane Jul 09 '20 at 14:34

2 Answers2

60

According to the following thread, hostPath volumes are not officially supported for wsl2, yet. They do suggest a workaround, though I had trouble getting it to work. I have found that prepending /run/desktop/mnt/host/c seems to work for me.

// C:\someDir\volumeDir
hostPath:
  path: /run/desktop/mnt/host/c/someDir/volumeDir
  type: DirectoryOrCreate

Thread Source: https://github.com/docker/for-win/issues/5325
Suggested workaround from thread: https://github.com/docker/for-win/issues/5325#issuecomment-567594291

Ryan Darnell
  • 601
  • 4
  • 3
  • 2
    Thanks! I wasted one hour in that. On Wsl distrib `/mnt/c/...` works but from Windows (cmd/powershell) I was strugging. – Animesh Sahu Dec 29 '20 at 14:02
  • this was the only way I could make it work, not even using /mnt/c/ works inside the linux subsystem. – Daniel Arechiga Mar 22 '21 at 21:48
  • My code isn't in /c but under `/home/justin` what would be the right path to map a folder under that location? – justin.m.chase May 02 '21 at 00:00
  • 1
    This really should be better documented by Docker - there seems to be about 3 or 4 possible hostpath configurations for Windows depending on which version of Docker/WSL you're using. – Alex Ward May 11 '21 at 08:40
  • Of course we just needed to prepend "/run/desktop/mnt/host" to the windows path. It's so obvious once you know :) Thanks for posting :) – Martin Bamford Oct 13 '21 at 12:14
1

Using @RyanDarnell's excellent answer above, here is what worked for me

Objective: get message-db with password using docker build --secret running in local docker-desktop kubernetes with StatefulSet and StorageClass using Skaffold

$kubectl get nodes -o wide
NAME             STATUS   ROLES                  AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                   CONTAINER-RUNTIME
docker-desktop   Ready    control-plane,master   21h   v1.21.1   <internalip>   <none>        Docker Desktop   5.4.72-microsoft-standard-WSL2   docker://20.10.7
#./k8s/storage-class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
  labels:
    app: postgres-database
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
#./k8s/persistent-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv1
  labels:
    app: postgres-database
spec:
  capacity:
    storage: 128Mi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /run/desktop/mnt/host/c/kubernetes-mount-path # created this folder at C:\kubernetes-mount-path
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - docker-desktop
#./k8s/stateful-set.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-database
spec:
  selector:
    matchLabels:
      app: postgres-database
  serviceName: postgres-service
  replicas: 1
  template:
    metadata:
      labels:
        app: postgres-database
    spec:
      containers:
      - name: message-db-container
        image: message-db-test
        volumeMounts:
        - name: postgres-disk
          mountPath: /var/lib/postgresql/data
        env:
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        - name: POSTGRES_PASSWORD
          value: <postgres-password>
  volumeClaimTemplates:
  - metadata:
      name: postgres-disk
      labels:
        app: postgres-database
    spec:
      selector:
        matchLabels:
          app: postgres-database
      storageClassName: local-storage
      accessModes: 
      - ReadWriteOnce
      resources:
        requests:
          storage: 128Mi
#./k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: postgres-loadbalancer
spec:
  selector:
    app: postgres-database
  type: LoadBalancer
  ports:
    - port: 5432
      targetPort: 5432
#./Dockerfile
# syntax = docker/dockerfile:1.3
FROM postgres:13.3-alpine3.14

RUN apk add --no-cache curl tar
RUN mkdir -p /usr/src/eventide \
  && curl -L https://github.com/message-db/message-db/tarball/v1.2.6 -o /usr/src/eventide/message-db.tgz
RUN tar -xf /usr/src/eventide/message-db.tgz --directory /usr/src/eventide

# change message_store login password
RUN --mount=type=secret,id=message-db-pass sed -i "s/WITH LOGIN/WITH LOGIN ENCRYPTED PASSWORD '$(cat /run/secrets/message-db-pass)'/g" /usr/src/eventide/message-db-message-db-759a4f3/database/roles/message-store.sql

RUN echo -e "#!/bin/sh\ncd /usr/src/eventide/message-db-message-db-759a4f3/database\n./install.sh" > /docker-entrypoint-initdb.d/rundbscripts.sh 

RUN docker-entrypoint.sh postgres --version

ENTRYPOINT docker-entrypoint.sh postgres
#./secret.txt
<postgres-password>
#./skaffold.yaml
kind: Config
apiVersion: skaffold/v2beta20
build:
  artifacts:
    - image: message-db-test
      context: .
      docker:
        secret:
          id: message-db-pass
          src: secret.txt
  local: 
    useBuildkit: true
deploy:
  kubectl:
    manifests:
    - k8s/*.yaml

run skaffold debug -v debug to startup

nurseybushc
  • 364
  • 3
  • 10