Adding your own Docker container or stack to PS4
What is it
While PS4 comes with quite a number of preassembled software stacks you might have your own image which you want to run alongside the other stacks.
There's more than one way to achieve that - in fact PS4 offers three different ways, each with its own benefits and drawbacks:
- Use the ase-docker stack (still under construction). It is a stand-alone Docker with a neat Portainer web user interface for managing your containers. It features its own landing page with tiles similar to those you see on the PS4 entry page. Such a setup is commonly called DinD - Docker in Docker. Find more at ase-docker stack page.
- Use the ase-k3s which runs Docker container in a Kubernetes environment. Its setup needs a bit more effort but then provides a clean REST-API for the management of your containers. Note that such a single server setup lacks the prime Kubernetes benefits: There is no high-availability and just a bit of scalability because all worker nodes execute on the same virtual machine. For details see the ase-kubernetes stack page.
- Run your container(s) alongside the other PS4 stacks. This even allows to add them to the PS4 entry page as an additional tile if the container provides a Web UI. This is the way we detail in the following.
Should you have an application which is not containerized yet you may leverage the ps-service stack page. This is a stack which has everything to integrate well into PS but is otherwise empty, waiting for you to install your application. It is possible to run several ps-service containers (each under its individual name) if you have more than one application you want to make available this way.
Prerequisites
Have a Docker container image available, typically downloadable from a Docker registry or as a Docker export archive. You must know on which network ports the container provides its service, usually it is just one port. Running services on several ports complicates integration into PS4 considerably and is not covered by this HowTo.
Additionally you should know whether your Web UI insists on running in the document root of the URL, i.e. https://<hostname>. Well designed web applications do not force you to do so, i.e. they feel comfortable if their home is a URL path, i.e. https://<hostname>/<path>. Usually this involves a specific setting which provides this exact path to the application. PS4 can accommodate both kinds but it is considerably easier to integrate applications which can cope with such a URL path.
How to achieve it
You have to accomplish two things:
- run the container(s)
- provide all data necessary to integrate the container into PS4
If your application consists of a single container
Both things are achieved by one single docker run command. All necessary information to integrate your container into your PS4 instance is provided through environment variable settings. PS4 automatically sees these variables and acts accordingly. Execute the command as user root on your PS4 server. Here is an example:
docker run \
-d \
--restart always \
-e PROXY_SRV_NAME="Sample" \
-e PROXY_SRV_DESC="This text appears on the tile." \
-e PROXY_URL_SCHEME="https" \
-e PROXY_URL_HOST="fully.qualified.host.name" \
-e PROXY_URL_PATH="/sample" \
-e PROXY_URL_MODE="make" \
-e PROXY_DST_PATH="/sample" \
-e PROXY_DST_PORT="8080" \
--expose 8080
<image>
Let's dissect this command:
-d | causes the container to execute in the background, decoupled from your terminal |
--rm | removes the container once it has stopped; thus avoids that your disk fills up with once run containers |
--restart always | causes your container to automatically start after failures or when you reboot your PS4 server |
-e PROXY_SRV_NAME="..." | sets the application name on the tile one the landing page |
-e PROXY_SRV_DESC="..." | sets the short decription text below the application name on the tile one the landing page |
-e PROXY_URL_SCHEME="..." | sets the URL scheme ("protocol") to be used in URLs when contacting the application |
-e PROXY_URL_HOST="..." | sets the host name to be used in URLs when contacting the application |
-e PROXY_URL_PATH="..." | sets the path to be used in URLs when contacting the application |
-e PROXY_URL_MODE="..." | sets the TLS mode ("make", "fake", "take" or "none"), typically the one you used when you installed PS4 |
-e PROXY_DST_PATH="..." | optional: the URL path the application container itself uses internally when being contacted (default: "/", see below) |
-e PROXY_DST_PORT="..." | optional: the port the application container itself uses internally when being contaced (default: the container's first exposed port, see below) |
--expose 8080 | the port where the applications's web UI can be reached |
<image> | this specifies your Docker image in one of the ways shown in the docker run documentation |
Integration into PS4 basically means to configure the PS4 reverse proxy in a way that it knows about the additional container(s) and understands how to contact them. The reverse proxy receives a URL from the outside, then rewrites and forwards it in a way that the proper container receives it. This information is provided by the set of PROXY_... variables.
The PROXY_URL_... variables deal with the URL you provide in your browser to contact the application. With the exception of PROXY_URL_MODE they name the parts of the URL. Example:
In a URL like https://myps4.unit.msg.team/vcs-gitea the https is the scheme, myps4.unit.msg.team is the hostname and vcs-gitea is the path. PROXY_URL_MODE simply tells the reverse proxy how to deal with TLS connections (formerly known an SSL): If you use SSL a forward rule is established which redirects all http requests to https.
The PROXY_DST_PATH variable is only necessary when an application does not understand that it runs in a URL path other than "/" (i.e. the document root). The variable then contains the proper path (usually the same as in PROXY_URL_PATH) and the application in the container must be set up to execute in this path.
The PROXY_DST_PORT variable has to be explicitely provided in cases where a container exposes more that one port (which is unusual) and the port to contact the web UI is not the first one mentioned in the container's Dockerfile. As the first exposed port is used by the reverse proxy as destination for all URL requests this variable setting then makes sure that the reverse proxy finds the right port.
If your application consists of multiple containers
This is called a container stack. To integrate your stack into Project Server you use the same environment variables as described above. But simple docker commands are no longer a good idea as you need a Composefile to connect these containers together, and that's also where you place your environment variables. Here is an example:
services:
# the frontend container
my-sample:
container_name: my-sample
image: docker.registry.com/area/my-sample:latest
init: true
restart: always
environment:
PROXY_SRV_NAME: My-Sample
PROXY_SRV_DESC: "This text appears on the tile."
PROXY_URL_SCHEME: https
PROXY_URL_HOST: fully.qualified.host.name
PROXY_URL_PATH: /sample
PROXY_URL_MODE: make
PROXY_DST_PATH: /sample
PROXY_DST_PORT: 8080
...
Everything what was said about the environment variables above applies here, too. It is just that you now have a Composefile. Therefore you use the docker-compose up -d -f <composefile> command to start your application containers.
Good to know
Here are some details to better understand what the PROXY_DST_PATH setting is good for:
Project Server's reverse proxy provides a central entry point for all web traffic to your applications. For that it rewrites the incoming URLs from the external view in "path style notation" (e.g. <hostname>/<application>) to the internal view, i.e. what the concrete application expects. In most cases applications are used to run in the document root of their URL namespace and that is exactly what the reverse proxy aims at by default.
But the application also returns links to the user's browser, often not visible in some static HTML but assembled dynamically in JavaScript code. The reverse proxy can do nothing to rewrite those URLs back to the "external view" as it never sees them because the JavaScript executes on the client (the user's browser) and not on the server.
Therefore the application should have a configuration setting to tell it which HTTP path it should add to URLs returned to the browser. Many have but a few don't, unfortunately. In these cases let the application run the respective path in its container (and not in the document root) and set PROXY_DST_PATH to tell Project Server's reverse proxy to rewrite incoming URLs to this path and not simply to "/".