Git functions (Designer Save) from Ignition Docker container

I’m trying to use Git to manage code and have a Dev environment set up using a Docker container (using either the official Ignition image or the kcollins image).

Following the best practice guide, I’m trying to do an auto commit to Git when pressing the Save button in the Ignition Designer.

From what I can tell, this executes a command script that requires Git to be installed on the OS that is running Ignition.

Since Ignition is running in a container does this mean Git needs to be present in the container also?

I assume that neither of the Ignition docker images mentioned above have Git installed.

Is there a way to get this auto-commit functionality to work using a container?

At the moment I have the container data exposed as a persistent named volume to the host Linux machine (where I have installed Git), and I do the Git commands manually from the host on this persistent data volume.

Yes.

Yes.

You want to look into making a ‘derived’ Docker image - you start from an official Ignition image as a template, then run instructions (such as “install git”, in whatever package manage flavor is required) and at the end have a local image that can be used in whatever container(s) you want.

I’m sure @kcollins1 has some kind of resource available :slight_smile:

This post has an example of extending the "entrypoint" functionality of the container through the use of a derived image:

If you just need to ensure additional packages are baked into the image, a Dockerfile like below would probably suffice (since the upstream uses Ubuntu, we'll be using apt as the package manager):

ARG IGNITION_VERSION="8.1.15"
FROM inductiveautomation/ignition:${IGNITION_VERSION}

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        git && \
    rm -rf /var/lib/apt/lists/*

Let me know if you need help building a derived image!

5 Likes

Thanks Kevin

I’m a bit of a Docker / Linux / git noob so really appreciate the worked example.

I wasn’t even aware it was possible build on images in this way!

Using your sample code, I’ve now managed to build a new image (based on the kcollins image as it seems to be the only one I can get to work with named volumes!) with git added to it.

I’ve continued with the Deployment Best Practices guide and added the git-auto-commit.sh script to the container (manually via the container shell for now), and when I execute it direct from the container shell it works and does the git commit.

However when I try to execute the same script from the Designer Save it does not appear to work.

I’ve put logger calls both before and after the system.util.execute command in the Gateway Update Event, and both pre and post logger entries appear in the Gateway logs, but the shell script file actions do not appear to have executed (no git commit appears in source control).

Any ideas on how to proceed or debug?

There are definitely some differences that exist between the kcollins/ignition and inductiveautomation/ignition images. I’m not sure why you’d be having trouble with named volumes on either one (assuming you respect the different locations that you need to use between the two images–kcollins/ignition uses /var/lib/ignition/data as the target location for named volume versus inductiveautomation/ignition needing /usr/local/bin/ignition/data.

Next, let’s talk about why your script might behave differently (under kcollins/ignition image) between a direct shell execution and from under the gateway. When you start a container, the entrypoint is called and launches your application as the configured user (set in the image, see here). The entrypoint for both inductiveautomation/ignition and kcollins/ignition is run as root, but kcollins/ignition steps down (via this logic and the use of gosu) to a normal user called ignition (default UID/GID of 999) to launch the gateway instead of running as root. So unless you docker exec -it -u ignition <container name> bash, you’ll be running as root instead of what the gateway is running at… Sometimes pictures help:

So I suppose where I’d start is to make sure you’re running it as the same user as the gateway is running. It is possible you might have some permissions problems.

Note that as of 8.1.x, inductiveautomation/ignition official image still runs as root.

@kcollins1, I’m also a Docker/Linux/git noob. my goal is the same as gino’s but i’m using a docker-compose.yml file to build my docker container. I don’t understand how i would do the same thing using a .yml file. Could you explain or provide an example?

@Smiths Not sure if this answers your question, but I built a custom image called “ignition_git” which includes git, using Kevin’s DockerFile example above, then created a docker-compose.yml file (below) to launch a container based on the new image (using “docker compose up -d”):

services:
  gateway:
    image: ignition_git:8.1.13
    volumes:
      - ./gateway/data:/data
    labels:
      - traefik.http.routers.gateway-sparc-git.rule=Host(`sparc-git.devmachine.mycompany.com`)
      - traefik.http.routers.gateway-sparc-git.entrypoints=gw,gwsec
      - traefik.port=8088
    networks:
      - external

networks:
  external:
    external: true

In the ‘services’ section I have defined a service called ‘gateway’ based on the igntion_git image I created.

The ‘volumes’ section maps a host folder to the container’s gateway data folder to persist the gateway data.

The ‘labels’ section is configuration that is picked up automatically by my Traefik Reverse Proxy container. This allow the gateway to be found at ‘sparc-git.devmachine.mycompany.com:8088’.

The ‘networks’ section is an external docker network called ‘external’ that I have configured manually so all my containers can communicate with the Traefik reverse proxy. The Traefik container then handles communications with the host machine.

If you are not using Traefik then you would need to add custom host:container port mappings to the docker-compose.yml file above.

I’ve been working on (though it has been sitting idle for some time, gotta rejuvenate the efforts :slight_smile: ) a more comprehensive “packaged up” Git demo, but not ready yet. To supplement @gino.miucci 's response, you can also integrate the building of that image via some modifications to his Compose definition:

services:
  gateway:
    build:
      context: gw-build
    image: ignition_git:8.1.13
    volumes:
      - ./gateway/data:/data
    labels:
      - traefik.http.routers.gateway-sparc-git.rule=Host(`sparc-git.devmachine.mycompany.com`)
      - traefik.http.routers.gateway-sparc-git.entrypoints=gw,gwsec
      - traefik.port=8088
    networks:
      - external

networks:
  external:
    external: true

With the build declaration, you can place the Dockerfile (and other supporting files as needed) in a sub-folder of your Compose solution (in the example above, it is called gw-build). This way you can use Compose to build+run your image (and manage that build aspect). By supplying both build and image, Compose will also tag the resultant build based on your image definition.

I’m excited to see more people using docker with Ignition and I appreciate people sharing their experience. I’m new to docker and linux sysadmin in general and am building out a stack of tools to support Ignition development with Docker at the core. Portainer is a very helpful tool so far, and I’m hoping to use custom images and stacks to quickly stand up dev environments for various client sites/projects with our current Ignition base, git, a database, etc. Traefik is the other hurdle I’m working on right now, so thanks for sharing about that.

Thanks for the help @gino.miucci and @kcollins1! I haven't tried integrating the build into the compose definition yet, but I will.

I’ve successfully built a derived image with git installed but i’m running into a brick wall trying to add a .gitignore file to the image. I’m attempting to COPY the .gitignore file into the container directory but i think i keep running into issues with white space in the file path or maybe i’m not understanding how the shell command interprets relative paths. I keep getting errors saying "failed to compute cache key." It looks like it's not finding the file at all. Here’s my directory setup:

-path\docker testing
__.gitignore
__Dockerfile

Here's the shell command i'm using and the results:

PS C:\path\docker testing> docker build .     
[+] Building 4.7s (6/7)
 => [internal] load build definition from Dockerfile                                                                                                       0.0s 
 => => transferring dockerfile: 32B                                                                                                                        0.0s 
 => [internal] load .dockerignore                                                                                                                          0.0s 
 => => transferring context: 2B                                                                                                                            0.0s 
 => [internal] load metadata for docker.io/inductiveautomation/ignition:8.1.15                                                                             4.6s 
 => [internal] load build context                                                                                                                          0.0s 
 => => transferring context: 2B                                                                                                                            0.0s 
 => CACHED [1/3] FROM docker.io/inductiveautomation/ignition:8.1.15@sha256:0ef0e5465207e7da091341829cb50edbd55ddd9b18af7ee174d8bcf5c55ab879                0.0s 
 => ERROR [2/3] COPY C:\path\docker testing\.gitignore /usr/local/bin/ignition/data/.gitig  0.0s 
------
 > [2/3] COPY C:\path\docker testing\.gitignore /usr/local/bin/ignition/data/.gitignore:
------
failed to compute cache key: "/C:\\path\\docker testing\\.gitignore" not found: not found  

Here's the Dockerfile script:

ARG IGNITION_VERSION="8.1.15"

FROM inductiveautomation/ignition:${IGNITION_VERSION}

ENV FILE_PATH="C:\path\docker testing\.gitignore"

COPY ${FILE_PATH} /usr/local/bin/ignition/data/.gitignore

RUN apt-get update && \

    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \

        git && \

    rm -rf /var/lib/apt/lists/* &&\

I've also somewhat tried using docker cp and committing the changes to a new image but to be honest i want to understand why my currents steps won't work. Anything jumping out that could be hanging me up?

I think I can help address some of the bumps you’re running into here… Let me present a modified version of your Dockerfile with comments embedded:

# This creates an argument you can supply at time of `docker build`
ARG IGNITION_VERSION="8.1.15"

# This consumes that argument to parameterize the upstream base image
FROM inductiveautomation/ignition:${IGNITION_VERSION}

# This is going to add an environment variable _inside_ the image--you don't need this
# ENV FILE_PATH="C:\path\docker testing\.gitignore"

# When you issue a docker build against a _context_ (which you're specifying 
# as the current directory with `docker build .`), all of the files that
# are not masked via `.dockerignore` file are included in that context.  You 
# can then use those files directory in statements like COPY
#COPY ${FILE_PATH} /usr/local/bin/ignition/data/.gitignore
COPY .gitignore /usr/local/bin/ignition/data/

# I adjusted this to get rid of the trailing `&& \` which is what we're using
# to join all of these statements into a chain (which will stop if any of
# them return non-zero exit codes).  The reason we do this is because
# each `RUN` statement generates a separate image layer.  You can read more
# about the reasoning behind this strategy in various best-practices docs.
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        git && \
    rm -rf /var/lib/apt/lists/*

awesome, that explains the issue . Thanks @kcollins1!.

Hi Gino,

Did you manage to execute the git-update eventually? I’m experiencing the same thing as you described here. In Ignition, I am only able to execute files on my Windows machine, not my docker container.

@kcollins1, I did manage to run the .sh file as the ignition user in the terminal, but my Ignition gateway is still unable to access the file (but can access my Windows folder…)

Bumping this to see if anyone was able to work through this? I would like to use Docker with Git as well but not if there are issues with the docker setup.

Hi Brendon,

We did manage to execute the files in the end, using this code:

def push():
	"""Give the command to push the project changes to Bitbucket"""
	pb = ProcessBuilder(["/bin/bash", "/usr/local/bin/ignition/data/projects/.git-auto/push.sh"])
	process = pb.start()
	output = String(process.getInputStream().readAllBytes())
	logger.info(output)
	return None
	
def commit(actor, project):
	"""Give the command to commit the project changes locally"""
	# LOG MESSAGE
	logger.debug(actor + ' is committing changes on project ' + project)
	# BASH COMMAND WITH PROCESSBUILDER
	pb = ProcessBuilder(["/bin/bash", "/usr/local/bin/ignition/data/projects/.git-auto/commit.sh", actor, project])
	process = pb.start()
	output = String(process.getInputStream().readAllBytes())
	# LOG OUTPUT MESSAGE
	logger.debug(output)
	return None
1 Like