Containers with Read-Only Root Filesystem
By default, CloudQuery requires a read-write file system to create a socket file used by the integrations to communicate with the CLI. It is however not possible to create a read-write socket on a read-only file system. For this to work, an integration needs to be started as a gRPC server locally.
Creating a container that works with a read-only file system
To create a container that works with a read-only file system, you will need the following:
- a CloudQuery configuration file that is used to install the required integrations
- a CloudQuery configuration file that contains the specs needed for the sync
- a start script used as an entry point for the container that will start the integrations and run the sync
Downloading integrations requires users to be authenticated, normally this means running
cloudquery login
but that is not doable in a CI environment or inside of a docker build process. The recommended way to handle this is to use an API key. More information on generating an API Key can be found here
In the example below, we will use the XKCD (opens in a new tab) source integration and PostgreSQL (opens in a new tab) destination integration.
Create a configuration file for the installation
This configuration file should reference the integrations that you want to install with the actual source where they should be downloaded from:
kind: source
spec:
name: xkcd
path: hermanschaaf/xkcd
version: v2.0.0
registry: github
destinations: ["postgresql"]
tables: ["*"]
---
kind: destination
spec:
name: "postgresql"
path: "cloudquery/postgresql"
version: "v8.7.5"
Create a config file for the sync
This config file should reference the integrations running as gRPC servers (fill in the connection_string
). We will run XKCD on port 7778 and PostgreSQL on port 7779:
kind: source
spec:
name: xkcd
registry: grpc
path: localhost:7778
destinations: ["postgresql"]
tables: ["*"]
---
kind: destination
spec:
name: "postgresql"
registry: grpc
path: localhost:7779
spec:
connection_string: "[REDACTED]"
Create a start script to start the integrations
This shell script will start the integrations and the sync with the provided parameters when we run the container. To make it easier to maintain, we will use the find
command to get the path to the actual integration binary. This way, we don't have to worry about the version of the integration.
#!/bin/sh
# get path to the xkcd integration:
xkcd_plugin_path=$(find .cq -regex ".*/xkcd/.*/plugin")
# get path to the postgresql integration:
postgresql_plugin_path=$(find .cq -regex ".*/postgresql/.*/plugin")
# start the integrations and run the sync with the provided parameters:
sh -c "${xkcd_plugin_path} serve --address localhost:7778" & \
sh -c "${postgresql_plugin_path} serve --address localhost:7779" & \
/app/cloudquery $@
Connecting it all together
We will use the ghcr.io/cloudquery/cloudquery:latest
container as a base and add all the files in it. Then we will override the entry point with our start script:
FROM ghcr.io/cloudquery/cloudquery:latest as build
WORKDIR /app
# this is the install.yaml file we created above
COPY ./install.yaml /app/install.yaml
ARG CLOUDQUERY_API_KEY
# install the integrations
RUN /app/cloudquery plugin install install.yaml
FROM ghcr.io/cloudquery/cloudquery:latest
WORKDIR /app
# Copy the .cq directory which contains the integrations
COPY --from=build /app/.cq /app/.cq
# add the sync config file
COPY ./sync.yaml /app/sync.yaml
# add the start script
COPY --chmod=0755 ./start.sh /app/start.sh
# override the start script
ENTRYPOINT [ "/app/start.sh"]
Run the Container
First, build the container as you would normally do:
docker build --build-arg CLOUDQUERY_API_KEY=<your-api-key> ./ -t my-cq-container:latest
Run the container with --read-only
option and a command to pass to our start script. You will also need to add the --no-log-file
option to skip logging to a file. Here is an example:
docker run --read-only --add-host=host.docker.internal:host-gateway my-cq-container:latest --no-log-file sync sync.yaml