Running Docker Command From Svelte

What we’re trying to do

I have a setup where different docker containers are used in conjunction to provide a single application. More specifically, a database, a sveltekit app, and (at the moment just one) container to run specific analyses. The idea is that the sveltekit app in container 1 triggers a script in container 2 to change data in the database in container 3.

docker sequence diagram

Directory structure

I created the following directory structure for the app:

docker-compose.yml
analysis/
  Dockerfile
  run_analysis.py
frontend/
  Dockerfile
  build/
  public/
  node_modules/
  src/
  ...

The analysis container only contains a Dockerfile and the specific command to run the analysis; the frontend container holds the svelte-kit code.

Setting up the analysis container

The key in this solution is the shell2http script available from https://github.com/msoap/shell2http. This script enables you to attach a port on your machine to a specific script. For example, the command shell2http /ps "ps aux" will return the output of ps aux to your webbrowser if you go to http://localhost:8080/ps.

Let’s for example say that we have an ArangoDB collection in which we want to add a new key/value pair for the document with the id 1234. The following simple python script does this.

1
2
3
4
5
6
7
#!/usr/bin/env python
from arango import ArangoClient

client = ArangoClient(hosts="http://arangodb:8529")
db = client.db("oncoviz",username="<your_root_username>",password="<your_root_pwd>")
my_collection = db.collection("my_collection")
my_collection.update_match({'id':1234},{'newkey':'newvalue'})

Testing shell2http

The regular way of running the script (let’s name it run_analysis.py) would be python ./run_analysis.py. Therefore, we can make it available using shell2http with the following command: shell2http /analysis "python run_analysis.py". This will return the following:

2022/07/03 11:45:03 register: /analysis (python2 run_analysis.py)
2022/07/03 11:45:03 register: / (index page)
2022/07/03 11:45:03 listen http://localhost:8080/

It can now be executed by going to http://localhost:8080/analysis.

Creating the Dockerfile

To get this to work from docker, we’ll start from an ubuntu base, install go, shell2http and python, and copy our analysis script. The CMD needs to run the actual ssh2http command.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM ubuntu

RUN apt-get update -y && apt-get upgrade -y

# install shell2http (common for all analysis containers)
# source: https://github.com/msoap/shell2http
RUN apt-get install -y ca-certificates
RUN apt-get install -y golang
RUN go install github.com/msoap/shell2http@latest
RUN cp /root/go/bin/shell2http /usr/local/bin

# specific for this container
RUN apt-get install -y build-essential curl python2
RUN curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
RUN python2 get-pip.py
RUN pip install python-arango
WORKDIR /app
COPY run_analysis.py .

EXPOSE 8080

CMD ["/usr/local/bin/shell2http","/analyse","/usr/bin/python2 /app/run_analysis.py"]

Setting up the svelte-kit container

Similarly, we can set up the svelte-kit application. It doesn’t matter here what that thing looks like, as long as we can go to the link show above somewhere (i.e. http://localhost:8080/analyse).

The code below is taken from the src/routes/__layout.svelte file. It uses sveltestrap to incorporate bootstrap into the application, and shows a navigation bar with three links, including Analyse. This one will go to the URL that we specified above and which triggers the shell2http script in the other container.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
    import { Navbar, NavLink, NavbarBrand } from 'sveltestrap';
</script>

<Navbar color="light" light expand="md">
    <NavbarBrand href="/">My application</NavbarBrand>
    <NavLink href="/">Home</NavLink>
    <NavLink href="http://localhost:8080/analyse">Analyse</NavLink>
    <NavLink href="/overview">Overview</NavLink>
</Navbar>

Creating the Dockerfile

The Dockerfile for this svelte-kit application is the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
FROM node:16
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y sshpass ssh
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --production
ENV PORT 3000
EXPOSE 3000
CMD ["node","build"]

Docker compose

Let’s now combine everything into a single application, using docker compose. The docker-compose.yml file sets everything up:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
services:
  analysis:
    build:
      context: analysis
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    networks:
      - app-network
  frontend:
    build:
      context: frontend
      dockerfile: Dockerfile
    ports:
      - 5050:3000
    networks:
      - app-network
  arangodb:
    image: arangodb:3.9
    restart: always
    ports:
      - 8529:8529
    environment:
      - ARANGO_ROOT_PASSWORD=my_password
    networks:
      - app-network
    volumes:
      - arangodb_data_container:/var/lib/arangodb3
      - arangodb_apps_data_container:/var/lib/arangodb3-apps

networks:
  app-network:

volumes:
  arangodb_data_container:
  arangodb_apps_data_container:

We can run everything with docker-compose up --build.

This docker-compose.yml file has 3 sections, one for each docker container.

For the analysis and frontend sections, the build part defines in which directory we can find the Dockerfile for the analysis container. We also have to expose the port 8080 in the analysis container, and forward the internal port 3000 to port 5050 for the frontend (i.e. svelte-kit) container. So if we go to http://localhost:5050, it will actually go to port 3000 inside the container. Finally, we add the analysis and frontend container to the app-network network. This will allow us to refer to these containers from the other ones by their name.

We don’t build the arangodb container ourselves, but use the one from dockerhub. In volumes we describe where on our local machine the data is stored so that it is preserved whenever we have to restart containers.

That’s it.