nautobot / helm-charts

Helm Charts for using Nautobot in Kubernetes environments.
https://docs.nautobot.com/projects/helm-charts/en/stable/
Apache License 2.0
23 stars 18 forks source link

Plugin Support #8

Open asergeant01 opened 3 years ago

asergeant01 commented 3 years ago

In order to support plugins within containerised deployments, a user should have 2 options: 1) Use their own infrastructure to rebuild the nautobot container including their plugins 2) Have their plugin loaded into the default nautobot container before execution

Option 1 is currently available to users and requires no further development from this side, however option 2 is not currently available and requires further discussion on viability and implementation.

You can consider a Pod to be a self-contained, isolated "logical host" that contains the systemic needs of the application it serves. You describe a desired state in a Deployment which will orchestrate pods/containers to meet that state declaration. Deployments should be idempotent and therefore installing dependencies at runtime is not recommended.

I believe a possible solution to this is to build containers with simply the plugin code and its associated dependencies. This would achieve a versioned, idempotent container, that could be run to pre-populate a shared volume with the nautobot container.

To give an example / idea - consider a plugin build using the following Dockerfile and the nautobot chart deployed using the following values:

FROM my_repo/alpine:latest

# Install Pip to be able to resolve dependencies
RUN apk add py3-pip

# Make Directories
RUN mkdir /my_plugin/
RUN mkdir /my_plugin/app
RUN mkdir /my_plugin/dependencies

WORKDIR /my_plugin/

# Copy App from local filesystem to container
COPY . /my_plugin/app

# Download pip requirements into container for offline install
RUN pip download /my_plugin/app -d /my_plugin/dependencies

# Assume /plugins will be a PV mounted to the this container & make dirs within it
RUN mkdir /plugins
RUN mkdir /plugins/nautobot-plugin-awx-jobs
RUN mkdir /plugins/nautobot-plugin-awx-jobs-dependencies

# Copy this plugins versioned code and dependencies to the PV
CMD cp -R /my_plugin/app /plugins/nautobot-plugin-awx-jobs && cp -R /my_plugin/dependencies /plugins/nautobot-plugin-awx-jobs-dependencies
nautobot:
  extraVolumes:
    - name: nautobot-plugins
      emptyDir: {}

  extraVolumeMounts:
    - name: nautobot-plugins
      mountPath: /plugins

  initContainers:
    - name: nautobot-plugin-awx-jobs
      image: artifactory/nautobot-plugin-awx-jobs:latest
      imagePullPolicy: Always
      volumeMounts:
        - name: nautobot-plugins
          mountPath: /plugins

When the nautobot container starts, the plugins directory is available to it:

nautobot@nautobot-97c7fc76c-4ff7d:/plugins$ ls
nautobot-plugin-awx-jobs  nautobot-plugin-awx-jobs-dependencies

What this gives is 2 folders within the main nautobot container that the docker-entrypoint.sh script could look into and install.

nniehoff commented 3 years ago

I'm curious if we need to build this custom init container or if we can use something like python:3. IMO if we are asking users to build a custom image to build/install plugins, why don't we just continue to ask them to build a custom Nautobot container with the plugins pre-installed. Ultimately I'd really like to have a helm value of nautobot.plugins which is a list of packages to install. We still would have to figure out the PLUGINS and PLUGINS_CONFIG values in nautobot_config.py which #12 could work as a stop gap for.

asergeant01 commented 3 years ago

@nniehoff I'm not sure i understand what you mean by using something like python:3?

With regards to initContainers, my thinking is that each plugin has its own versioned container with its dependencies. If plugins get built into a custom nautobot container, the container isn't really nautobot:1.1.2, it's more like nautobot:1.1.2pluginA1.0pluginB0.5pluginC2.0.

I also think about upgrades, if a new version of nautobot is released, i don't want to have to rebuild containers if my plugin code hasn't been modified.

initContainers i believe to be preferable over pip installing plugins from a pypi/artificatory repository as they do not need to be fetched every time, the initContainer has a user defined imagePullPolicy.

I totally agree with your desire to have a specific helm value of nautobot.plugins.

nniehoff commented 3 years ago

@nniehoff I'm not sure i understand what you mean by using something like python:3?

I guess my point here is I'd like to use a pre-built container that we don't have to push to build. If a user has to build a container and push it to a registry to use as an init container I see little benefit here. In this case, a user should just build and push a custom Nautobot container.

We could benefit from some docs on the Nautobot side of the recommended method of building a custom Nautobot container with custom plugins. It's fairly straight forward actually.

With regards to initContainers, my thinking is that each plugin has its own versioned container with its dependencies. If plugins get built into a custom nautobot container, the container isn't really nautobot:1.1.2, it's more like nautobot:1.1.2pluginA1.0pluginB0.5pluginC2.0.

correct, we tend to go with something like projectx/nautobot:1.1.2-yyyymmdd-commithash

I also think about upgrades, if a new version of nautobot is released, i don't want to have to rebuild containers if my plugin code hasn't been modified.

The problem here is just because the nautobot:1.1.2pluginA1.0pluginB0.5pluginC2.0 combination worked it doesn't mean that (take the extreme example) nautobot:2.0.0pluginA1.0pluginB0.5pluginC2.0 will work with the same plugins. The benefit of building a custom projectx/nautobot:1.1.2-yyyymmdd-commithash container would be it can have it's own CI pipeline to do any testing necessary to validate that Nautobot version X + plugin A1.0 and plugin B0.5 work together.

initContainers i believe to be preferable over pip installing plugins from a pypi/artificatory repository as they do not need to be fetched every time, the initContainer has a user defined imagePullPolicy.

Completely agree, I like the initContainers approach is the place to handle new installs. I see the option of being able to dynamically install plugins as a convenience feature for beginning users. Enterprise users should be encouraged NOT to use this approach as there are performance and security implications which they should consider.

If we look at this as a convenience I don't think we can expect the user to build and push an image to a repo.

I'm going to schedule some time with the Nautobot core team for their ideas too, helm with k8s is a cool way to address this issue but it doesn't address other use cases.

asergeant01 commented 2 years ago

@nniehoff I tried a slightly different approach and have a branch for your thoughts/comments.

It basically follows on from your approach with nautobot_config.py by simply inserting a requirements.txt file generated from the helm chart values.

To test it out, i built my own nautobot container and edited the docker-entrypoint.sh with the following:

# Load any attached Plugins
FILE=/opt/nautobot/plugins/requirements.txt
if [[ -f "$FILE" ]]; then
    pip install -r $FILE
fi
xXAzazelXx commented 2 months ago

Is there are better way 3 years later, like enabling plugins in values.yaml?