Giter Site home page Giter Site logo

kubechain / funnel Goto Github PK

View Code? Open in Web Editor NEW
0.0 2.0 0.0 15 KB

A cone-shaped utensil with a tube at the apex for conducting data from volume to volume.

JavaScript 100.00%
funnel kubernetes volumes volume configmaps confimap transfer filters statefulset statefulsets

funnel's Introduction

Funnel

A cone-shaped utensil with a tube at the apex for conducting data from volume to volume.

Purpose

Funnel is meant to do one thing and one thing only. Act as a funnel between Kubernetes volumes. The author found that adding configuration to containers led to unexpected surprises. For instance, some applications do not read symbolic links. As a result Kubernetes ConfigMap volumes do not get read by said applications.

Pros

The functionality offered by funnel has several benefits, they are listed below:

  1. Funnel allows a Kubernetes user to mount ConfigMaps or Secrets on a PersistentVolume.
  • This ensures that the ConfigMaps or Secrets are not mounted as symbolic links. Which some applications might not read.
  1. Funnel allows a Kubernetes user to mount ConfigMaps to a specific Pod in a StatefulSet. See the HOSTNAME filter.
  • This feature does not exist in StatefulSets.

Cons

  1. IMPORTANT!: Updating a ConfigMap/Secret does not update the files funneled into a PersistentVolume!
  • Funnel does not watch files for changes. Which is why it runs as an initContainer rather than a container. It terminates as soon as its job is done.

Usage

Funnel is to be used as an init-container.

Volumes 'from' which files are to be conducted should be mounted in /usr/src/app/from/. Volumes 'to' which files are to be conducted should be mounted in /usr/src/app/to/.

Procedure

Funnel picks up all paths from /usr/src/app/from/** and maps them to /usr/src/app/to/**. This means you can use sub-paths in /usr/src/app/from/ and direct them to a specific sub-path in /usr/src/app/to/. For example, /usr/src/app/from/my-awesome-app-without-symlink-reading/ is mapped to /usr/src/app/to/my-awesome-app-without-symlink-reading/. Then, a regular container can use the volume with mountPath /usr/src/app/to/my-awesome-app-without-symlink-reading/.

Options

Filters

Version 1.1.0 introduced the FILTER_TYPE option. This option allows you to select a filter for funnel. The filter-types are:

  • NONE (default)
  • HOSTNAME

None filter

This is the default filter for funnel. When enabled funnel does not filter the data mounted in usr/src/app/from.

Hostname filter

The hostname filter was made with StatefulSets in mind. The way StatefulSets are designed makes it difficult to provide each individual pod with configuration. To solve this problem the hostname filter walks the ./from directory and filters any paths that do not match the following glob: **/HOSTNAME/**. Where HOSTNAME is retrieved by funnel and represent a pod's hostname. For StatefulSets the hostname would be $(statefulset name)-$(ordinal). Check the Kubernetes StatefulSet documentation for more information on pod naming.

Logging

By default only errors funnel only logs errors. However should you want to know if the copying of a file was done correctly you can specify a LOG_LEVEL. The LOG_LEVEL options are:

  • DEBUG
  • ERROR

Encoding

Funnel uses NodeJS to read and write files. This means that it's default encoding is utf-8. Currently there are no plans to support other encodings.

Trivia

  • When moving a liquid from one container to another container one often uses a funnel so the liquid does not spill.
  • Funneling is a term used for the allocation of a resource. For instance, 'The company was funneling all income towards the R&D department.'.
  • Volume is the quantity of three-dimensional space enclosed by a closed surface. Closed surfaces are otherwise known as containers.

Examples

Base usage

Funnel is mainly intended to map ConfigMaps to PersistentVolumes. Many permutations are possible. Some are listed below.

ConfigMap to PersistentVolume (root-root)

apiVersion: v1
data:
  content.txt: |
    Wait.. I feel I've been conducted.. BUT! IT CAN'T BE!
kind: ConfigMap
metadata:
  name: from-config
---

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: my-awesome-app-without-symlink-reading-deployment
  labels:
    app: my-awesome-app-without-symlink-reading
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-awesome-app-without-symlink-reading
  template:
    metadata:
      labels:
        app: my-awesome-app-without-symlink-reading
    spec:
      initContainers:
      - name: funnel
        image: robertdiebels/funnel
        volumeMounts:
        - mountPath: /usr/src/app/from/
          name: configmap-volume
        - mountPath: /usr/src/app/to/
          name: persistent-volume
      containers:
      - name: myapp-container
        image: busybox
        command: ['sh', '-c','echo "There should be something below this sentence"; ls /opt/share/config; echo "There should be nothing below this sentence."; ls -lR /opt/share/config | grep ^l']
        volumeMounts:
        - mountPath: /opt/share/config
          name: persistent-volume
      volumes:
      - name: configmap-volume
        configMap:
          name: from-config
      - name: persistent-volume
        persistentVolumeClaim:
          claimName: alimony
---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: alimony
spec:
 accessModes:
   - ReadWriteMany
 resources:
   requests:
     storage: 10Mi
---

ConfigMap to PersistentVolume (root-subpath)

apiVersion: v1
data:
  content.txt: |
    Wait.. I feel I've been conducted.. BUT! IT CAN'T BE!
kind: ConfigMap
metadata:
  name: from-config
---

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: my-awesome-app-without-symlink-reading-deployment
  labels:
    app: my-awesome-app-without-symlink-reading
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-awesome-app-without-symlink-reading
  template:
    metadata:
      labels:
        app: my-awesome-app-without-symlink-reading
    spec:
      initContainers:
      - name: funnel
        image: robertdiebels/funnel
        volumeMounts:
        - mountPath: /usr/src/app/from/
          name: configmap-volume
        - mountPath: /usr/src/app/to/
          name: persistent-volume
          subPath: /away/from/root/we/go/
      containers:
      - name: myapp-container
        image: busybox
        command: ['sh', '-c','echo "There should be something below this sentence"; ls /opt/share/config; echo "There should be nothing below this sentence."; ls -lR /opt/share/config | grep ^l']
        volumeMounts:
        - mountPath: /opt/share/config
          name: persistent-volume
          subPath: /away/from/root/we/go/
      volumes:
      - name: configmap-volume
        configMap:
          name: from-config
      - name: persistent-volume
        persistentVolumeClaim:
          claimName: i-am-root
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: i-am-root
spec:
 accessModes:
   - ReadWriteMany
 resources:
   requests:
     storage: 10Mi

Secret to PersistentVolume

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: my-awesome-app-without-symlink-reading-deployment
  labels:
    app: my-awesome-app-without-symlink-reading
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-awesome-app-without-symlink-reading
  template:
    metadata:
      labels:
        app: my-awesome-app-without-symlink-reading
    spec:
      initContainers:
      - name: funnel
        image: robertdiebels/funnel
        env:
          - name: LOG_LEVEL
            value: DEBUG
        volumeMounts:
        - mountPath: /usr/src/app/from/
          name: secret-volume
          readOnly: true
        - mountPath: /usr/src/app/to/
          name: persistent-volume
      containers:
      - name: myapp-container
        image: busybox
        command: ['sh', '-c','echo "There should be something below this sentence"; ls /opt/share/config; echo "There should be nothing below this sentence."; ls -lR /opt/share/config | grep ^l']
        volumeMounts:
        - mountPath: /opt/share/config
          name: persistent-volume
      volumes:
      - name: secret-volume
        secret:
          secretName: super-secret-secret
      - name: persistent-volume
        persistentVolumeClaim:
          claimName: i-am-root
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: i-am-root
spec:
 accessModes:
   - ReadWriteMany
 resources:
   requests:
     storage: 10Mi

---
apiVersion: v1
kind: Secret
metadata:
  name: super-secret-secret
type: Opaque
data:
  base64.txt: "bWFueSA2NCBzdWNoIGJhc2Ugd293"

Filters

Hostname filter

The example below show how to use the hostname filter in a StatefulSet.

apiVersion: v1
data:
  content.json: |
    {
    such: "content",
    many: "amaze"
    }
kind: ConfigMap
metadata:
  name: from-config-0-0
---

apiVersion: v1
data:
  content.json: |
    Helloooo?
kind: ConfigMap
metadata:
  name: from-config-0-1
---

apiVersion: v1
data:
  content.txt: |
    Wait.. I feel I've been conducted.. BUT! IT CAN'T BE!
kind: ConfigMap
metadata:
  name: from-config-1-0
---

apiVersion: v1
data:
  content.txt: |
    I must have called a thousand times!!
kind: ConfigMap
metadata:
  name: from-config-1-1
---

apiVersion: v1
kind: Service
metadata:
  name: funnel
  labels:
    app: funnel
spec:
  ports:
  clusterIP: None
  selector:
    app: funnel
---
# NOTE: The StatefulSet will crash because the busybox container terminates after executing a command.
# The container you put in place of busybox should not terminate unless an error occurs.

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: hostname
spec:
  serviceName: "funnel"
  podManagementPolicy: "Parallel"
  replicas: 2
  selector:
    matchLabels:
      app: funnel
  template:
    metadata:
      labels:
        app: funnel
    spec:
      initContainers:
      - name: funnel
        image: robertdiebels/funnel:nightly
        imagePullPolicy: "Always"
        env:
        - name: "FILTER_TYPE"
          value: "HOST_NAME"
        volumeMounts:
        - mountPath: /usr/src/app/from/hostname-0
          name: hostname-0-volume-0
        - mountPath: /usr/src/app/from/is-it-me-youre-looking-for
          name: hostname-0-volume-1
        - mountPath: /usr/src/app/from/pendant/hostname-1
          name: hostname-1-volume-0
        - mountPath: /usr/src/app/from/hello-how-are-you
          name: hostname-1-volume-1
        - mountPath: /usr/src/app/to/
          name: persistent-volume
      containers:
      - name: myapp-container
        image: busybox
        # NOTE: If you've used this example more than once and you've changed the mountPaths before. It may be that you see more folders than in this example.
        # Be sure to delete the persistentVolumes bound to the volumeClaimTemplates defined below and the volumeClaimTemplate themselves when you give this example several tries.
        command: ['sh', '-c','export HOST_NAME=`hostname`; echo "Should only allow files from directories named:" $HOST_NAME; echo "So, there should be something below this sentence"; ls /opt/share/config; ls /opt/share/config/pendant; echo "And, there should be nothing below this sentence."; ls -lR /opt/share/config | grep ^l']
        volumeMounts:
        - mountPath: /opt/share/config
          name: persistent-volume
      volumes:
      - name: hostname-0-volume-0
        configMap:
          name: from-config-0-0
      - name: hostname-0-volume-1
        configMap:
          name: from-config-0-0
      - name: hostname-1-volume-0
        configMap:
          name: from-config-1-0
      - name: hostname-1-volume-1
        configMap:
          name: from-config-1-1
  volumeClaimTemplates:
  - metadata:
      name: persistent-volume
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: standard
      resources:
        requests:
          storage: 10Mi

Logging

This example shows how to enable the LOG_LEVEL DEBUG in funnel.

initContainers:
  - name: funnel
    image: robertdiebels/funnel
    env:
    - name: LOG_LEVEL
      value: "DEBUG"
    volumeMounts:
    - mountPath: /usr/src/app/from/
      name: configmap-volume
    - mountPath: /usr/src/app/to/
      name: persistent-volume
      subPath: /away/from/root/we/go/

funnel's People

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.