External dependencies
The system requires access to the internet to download a number of "external" dependencies: Docker images, Maven artifacts, and JDBC drivers. Where the system needs to be deployed into an environment that does not have internet access, these dependencies must be downloaded on a "low-side" environment that does have internet access before they are transferred to the air-gapped "high-side" environment that does not have internet access.
This document outlines the steps to set up the ADT client and its dependencies in such an environment.
- The process starts on the low-side, which has internet access, and where any necessary resources are downloaded and processed.
- The resources are transferred to the high-side.
- Once the resources are on the high-side, ADT can be run without internet access.
The example commands in this document assume that the low-side and high-side are unix environments running Docker.
Where user-customization is required, the modifiable parts of the commands are marked with an end-of-line comment # User-editable
.
Low-side setup
Pull the required Docker images:
ALL_DOCKER_IMAGES=( docker/dockerfile:1.3 i2group/i2eng-analyze-containers-base:ubi-jdk17 i2group/i2eng-analyze-containers-client:3.1.0 i2group/i2eng-analyze-containers-connectors-base:18 i2group/i2eng-analyze-containers-connectors-base-minimal:18 i2group/i2eng-haproxy:2.9 i2group/i2eng-liberty:ubi-jdk17 i2group/i2eng-solr:9.7 i2group/i2eng-zookeeper:3.9 i2group/i2eng-connector-designer:1 i2group/i2eng-connector-designer-connectors-base:1 i2group/i2eng-grafana:11 i2group/i2eng-postgres:17 i2group/i2eng-prometheus:2.53 i2group/i2eng-sqlserver:2022-1 i2group/i2eng-textchart-data-access:7.5.0.6 i2group/i2eng-textchart-manager:7.5.2.0 i2group/i2eng-textchart-worker:7.5.2.0 # User-editable ) for DOCKER_IMAGE in "${ALL_DOCKER_IMAGES[@]}"; do docker pull "${DOCKER_IMAGE}" done
If you have connector images that you require then include those in the list, both here and in the later steps where
ALL_DOCKER_IMAGES
are processed.Run bootstrap to pull pre-requisites and create the
ac_m2
docker volume:./bootstrap
Note that this process will also create some other docker images but these will not be used on the low-side.
Data transfer
The precise method of data transfer from low-side to high-side will depend on the security requirements of the environment. For example, the data transfer may be done using removable storage, a secure file transfer protocol, or having a docker registry that's accessible by both the low-side and high-side (See How to run a local docker registry for more information on setting up a local docker registry) .
Choose the method that is most appropriate for your environment from the following options:
Data transfer using removable storage
Low-side actions for removable storage
Environment setup:
The example commands use an environment variable
REMOVABLE_STORAGE_MOUNT_POINT
to refer to the location of the removable storage. You must set this variable to the location of the removable storage before running the commands.The commands also use the AC_TOOLS_IMAGE variable to refer to the ac_tools image but this does not require user-editing.
REMOVABLE_STORAGE_MOUNT_POINT='/path/to/removable/storage' # User-editable ALL_DOCKER_IMAGES=( # Same as the list used to pull the images # User-editable ) AC_TOOLS_IMAGE=$( docker images --format "{{.Repository}}:{{.Tag}}" ac_tools )
You must run this in any terminal you wish to use the subsequent commands in. e.g. if you close one terminal session and open another then you will need to repeat this step in the newly-opened terminal.
The
ALL_DOCKER_IMAGES
variable content must be the same as the one used to pull the images during the Low-side setup step.Prepare the
ac_m2
docker volume for transfer:Create a tar file of the docker volume:
docker run --rm -v ac_m2:/mnt/ -u 0:0 --entrypoint /usr/bin/tar "${AC_TOOLS_IMAGE}" cf - -C /mnt/ . > "${REMOVABLE_STORAGE_MOUNT_POINT}/ac_m2.tar"
This command will create a tar file
ac_m2.tar
containing the contents of the docker volume in the removable storage.Prepare ADT pre-requisites for transfer:
Archive the files that you want to transfer by
cd
ing to the ADT directory and then running:ADT_FILES=( 'pre-reqs' ) # User-editable tar cf "${REMOVABLE_STORAGE_MOUNT_POINT}/adt-pre-reqs.tar" "${ADT_FILES[@]}"
Note: This command will only copy the pre-requisites; you will also need to copy ADT itself and your config separately. You can do this using a tar command like the one above if you choose.
Prepare docker images for transfer:
Create a tar file of the docker images using the
docker save
command:docker save -o "${REMOVABLE_STORAGE_MOUNT_POINT}/all_images.image.tar" "${ALL_DOCKER_IMAGES[@]}"
Note that this will create a single tar file containing all of the images listed. If you have images that you do not want to transfer, you can remove them from the list by modifying the
ALL_DOCKER_IMAGES
variable before running thedocker save
command.
Physically move the data
Remove the storage from the low-side (don't forget to unmount it first) and move it to the high-side.
High-side actions for removable storage
Extract all of the docker images:
If using removable storage, you must now restore the docker images from the tar file you created on the low-side:
REMOVABLE_STORAGE_MOUNT_POINT='/path/to/removable/storage' # User-editable for FILE in "${REMOVABLE_STORAGE_MOUNT_POINT}"/*.image.tar; do docker load -i "${FILE}" done
This will load each image from the removable storage into the local docker daemon on the high-side.
Optional: Verify docker images:
To verify that all of the images on the low-side are now on the high-side, you can run the following command on both sides and compare the output:
docker images --format "{{.ID}}\t{{.Repository}}:{{.Tag}}" | sort -k2
If the images are the same, the output will be identical on both sides. The
ID
field will be the same when the content of the image is the same; if the IDs are different, the content of the images is different.Create and populate the
ac_m2
docker volume:Extract the ac_m2 tar file you created into a new docker volume as follows:
docker volume rm -f ac_m2 docker volume create ac_m2 docker run --rm -i -v ac_m2:/mnt/ -u 0:0 --entrypoint /usr/bin/tar "${AC_TOOLS_IMAGE}" xf - -C /mnt/ < "${REMOVABLE_STORAGE_MOUNT_POINT}/ac_m2.tar"
This will extract the contents of the
ac_m2.tar
file into a newly-createdac_m2
docker volume, erasing any previous volume of that name.Extract the ADT files:
Extract the tar file of ADT files into the ADT directory as follows:
cd '/path/to/adt' # User-editable tar xf "${REMOVABLE_STORAGE_MOUNT_POINT}/adt-pre-reqs.tar'
Data transfer using network file transfer
If you are using a network file transfer method,
for example scp
, sftp
or rsync
(via e.g. ssh),
then the process is similar to that for removable storage.
Use the commands for
removable storage
with a few modifications:
Low-side actions for network file transfer
Follow the same steps as for removable storage
but set the REMOVABLE_STORAGE_MOUNT_POINT
variable to the location of an empty temporary directory.
Transfer the data using network file transfer
Use the network to transfer the files from the REMOVABLE_STORAGE_MOUNT_POINT
temporary directory
on the low-side to a temporary directory on the high-side.
High-side actions for network file transfer
Follow the same steps as for removable storage
but set the REMOVABLE_STORAGE_MOUNT_POINT
variable to the location of the temporary directory
where you transferred the files to.
Data transfer using a docker registry
If you have a docker registry that is accessible from both the low-side and high-side then you can use that as an intermediary, uploading the data as images from the low-side and pulling them from the registry on the high-side.
This makes it easier to transfer docker images (as they are already in image format) but you must encapsulate all other data in docker images too.
Low-side actions for docker registry
Environment setup:
The example commands use an environment variable
PRIVATE_REGISTRY
to refer to the private docker registry. You must set this variable before running the commands. If the registry requires authentication then you will need to dodocker login "${PRIVATE_REGISTRY}"
too.The commands also use the AC_TOOLS_IMAGE variable to refer to the ac_tools image but this does not require user-editing.
PRIVATE_REGISTRY='docker_registry_hostname:port' # User-editable ALL_DOCKER_IMAGES=( # Same as the list used to pull the images # User-editable ) AC_TOOLS_IMAGE=$( docker images --format "{{.Repository}}:{{.Tag}}" ac_tools )
You will need to run this in any terminal you wish to use the subsequent commands in. e.g. if you close one terminal session and open another then you will need to repeat this step in the newly-opened terminal.
The
ALL_DOCKER_IMAGES
variable content must be the same as the one used to pull the images during the Low-side setup step.Prepare the
ac_m2
docker volume for transfer:Encapsulate the docker volume in a docker image and push it to the registry.
docker rmi -f 'ac_m2_image:latest' docker rm -f 'ac_m2_container' docker run -v ac_m2:/mnt/ -u 0:0 --name 'temp_ac_m2_container' --entrypoint /usr/bin/tar "${AC_TOOLS_IMAGE}" cf /volume.tar -C /mnt/ . docker commit 'temp_ac_m2_container' 'ac_m2_image:latest' docker rm -f 'temp_ac_m2_container'
Prepare ADT pre-requisites for transfer:
Create a docker image containing the files that you want to transfer by
cd
ing to the ADT directory and then running:ADT_FILES=( 'pre-reqs' ) # User-editable docker rmi -f 'adt_pre_reqs_image:latest' docker rm -f 'temp_adt_pre_reqs_container' docker run -v "$(pwd):/mnt/" -u 0:0 --name 'temp_adt_pre_reqs_container' --entrypoint /usr/bin/tar "${AC_TOOLS_IMAGE}" cf /volume.tar -C /mnt/ "${ADT_FILES[@]}" docker commit 'temp_adt_pre_reqs_container' 'adt_pre_reqs_image:latest' docker rm -f 'temp_adt_pre_reqs_container'
Note: This command will only copy the pre-requisites; you will also need to copy ADT itself and your config separately. You can do this using a tar command like the one above if you choose.
Transfer the data using the docker registry
Push the images from the low-side to the registry:
IMAGES_TO_PUSH=( 'ac_m2_image:latest' 'adt_pre_reqs_image:latest' "${ALL_DOCKER_IMAGES[@]}" ) for DOCKER_IMAGE in "${IMAGES_TO_PUSH[@]}"; do docker tag "$DOCKER_IMAGE" "${PRIVATE_REGISTRY}/${DOCKER_IMAGE}" docker push "${PRIVATE_REGISTRY}/${DOCKER_IMAGE}" docker rmi "${PRIVATE_REGISTRY}/${DOCKER_IMAGE}" done
Extract all of the docker images on the high-side:
Pull the images from that into the local docker daemon. You must replace the
...
in the command below with the list of images you generated in the previous step.If your registry requires authentication then you will need to do
docker login "${PRIVATE_REGISTRY}"
first.ALL_DOCKER_IMAGES=( # Same as the list used to pull the images # User-editable ) IMAGES_TO_PULL=( 'ac_m2_image:latest' 'adt_pre_reqs_image:latest' "${ALL_DOCKER_IMAGES[@]}" ) for DOCKER_IMAGE in "${IMAGES_TO_PULL[@]}"; do docker pull "${PRIVATE_REGISTRY}/${DOCKER_IMAGE}" docker tag "${PRIVATE_REGISTRY}/${DOCKER_IMAGE}" "$DOCKER_IMAGE" docker rmi "${PRIVATE_REGISTRY}/${DOCKER_IMAGE}" done
The
ALL_DOCKER_IMAGES
variable content must be the same as the one used to pull the images during the Low-side setup step.Optional: Verify docker images:
To verify that all of the images on the low-side are now on the high-side, you can run the following command on both sides and compare the output:
docker images --format "{{.ID}}\t{{.Repository}}:{{.Tag}}" | sort -k2
If the images are the same, the output will be identical on both sides. The
ID
field will be the same when the content of the image is the same; if the IDs are different, the content of the images is different. Unwanted images can be removed using thedocker rmi
command.
High-side actions for docker registry
Extract the
ac_m2
docker volume from the image:docker volume rm -f ac_m2 docker volume create ac_m2 docker run --rm -v ac_m2:/mnt/ -u 0:0 --entrypoint /usr/bin/tar 'ac_m2_image:latest' xf /volume.tar -C /mnt/ docker rmi 'ac_m2_image:latest'
This will extract the contents of the tar file embedded in the
ac_m2_image:latest
image into a newly-createdac_m2
docker volume, erasing any previous volume of that name and erasing the image afterwards.Extract the ADT files from the image:
cd '/path/to/adt' # User-editable docker run --rm -v "$(pwd):/mnt/" -u 0:0 --entrypoint /usr/bin/tar 'adt_pre_reqs_image:latest' xf /volume.tar -C /mnt/ docker rmi 'adt_pre_reqs_image:latest'
High-side setup
After the data transfer is complete and the resources have been extracted, you can deploy "as normal" on the high-side, but with a few extra steps to ensure that the system does not try to pull any images from the internet.
Tell the high-side not to try to fetch the latest images.
Create a file
docker.env
(place it in the same directory as thebootstrap
script file) and populate it with contents:ALWAYS_PULL_IMAGES=false
This will tell the scripts to use the images that are already present on the high-side and not attempt to pull the latest images from the internet.
Deploy configuration
Do the deployment with USE_LOCAL_IMAGE set to true in order to avoid trying to pull newer images:
export USE_LOCAL_IMAGE=true deploy [-c <config_name>]
Footnotes
How to run a local docker registry
While a fully "air-gapped" environment is possible using some form of removable storage, it is often more convenient to have a minimal network connection between the low-side and high-side, and one common use case is to have a local docker registry that is accessible from both sides.
To create a Docker registry you will need to install Docker on a machine that is accessible from both the low-side and high-side, and then run the following command to create a local registry:
docker pull registry:2
docker run -d -p 5000:5000 --restart=always --name registry registry:2
This will create a local docker registry that is accessible on port 5000 of the machine it is running on,
so where the previous instructions said PRIVATE_REGISTRY='docker_registry_hostname:port'
you can set it to hostname:5000
for the hostname of the machine running the registry.
For information about deploying a Docker registry, see Deploy a registry server.
Docker image reference
ADT makes use of a number of docker images, some of which are always required for the system to deploy and run, and some of which are optional and may be required (or not) depending on the configuration being deployed. These are further subdivided into "external" images that are pulled from the (public) docker registry and "local" images that are built by this code (based on the external ones).
Externally-pulled images
External mandatory images
docker/dockerfile:1.3
i2group/i2eng-analyze-containers-base:ubi-jdk17
i2group/i2eng-analyze-containers-client:3.1.0
i2group/i2eng-analyze-containers-connectors-base:18
i2group/i2eng-analyze-containers-connectors-base-minimal:18
i2group/i2eng-haproxy:2.9
i2group/i2eng-liberty:ubi-jdk17
i2group/i2eng-solr:9.7
i2group/i2eng-zookeeper:3.9
External optional images
i2group/i2eng-connector-designer:1
i2group/i2eng-connector-designer-connectors-base:1
i2group/i2eng-grafana:11
i2group/i2eng-postgres:17
i2group/i2eng-prometheus:2.53
i2group/i2eng-sqlserver:2022-1
i2group/i2eng-textchart-data-access:7.5.0.6
i2group/i2eng-textchart-manager:7.5.2.0
i2group/i2eng-textchart-worker:7.5.2.0
Locally-built images
Local mandatory images
ac_tools:4.4.5.0
etlclient_redhat:4.4.5.0
i2a_tools_redhat:4.4.5.0
liberty_redhat:4.4.5.0
solr_client_redhat:4.4.5.0
solr_redhat:4.4.5.0
Local optional images
postgres_client_image:4.4.5.0
sqlserver_client_redhat:4.4.5.0
Full list of images as JSON
You can generate the full list of images required for the deployment by running the following command:
manage-environment -t create-image-list
This command creates a file called adt-image-list.json
in the ADT directory containing the full list of images.
The file is in JSON format and will look like this:
{
"i2group-core": [
{
"name": "docker/dockerfile",
"version": "1.3"
},
{
"name": "i2group/i2eng-analyze-containers-base",
"version": "ubi-jdk17"
},
{
"name": "i2group/i2eng-analyze-containers-client",
"version": "3.1.0"
},
{
"name": "i2group/i2eng-analyze-containers-connectors-base",
"version": "18"
},
{
"name": "i2group/i2eng-analyze-containers-connectors-base-minimal",
"version": "18"
},
{
"name": "i2group/i2eng-haproxy",
"version": "2.9"
},
{
"name": "i2group/i2eng-liberty",
"version": "ubi-jdk17"
},
{
"name": "i2group/i2eng-solr",
"version": "9.7"
},
{
"name": "i2group/i2eng-zookeeper",
"version": "3.9"
}
],
"i2group-optional": [
{
"name": "i2group/i2eng-connector-designer",
"version": "1"
},
{
"name": "i2group/i2eng-connector-designer-connectors-base",
"version": "1"
},
{
"name": "i2group/i2eng-grafana",
"version": "11"
},
{
"name": "i2group/i2eng-postgres",
"version": "17"
},
{
"name": "i2group/i2eng-prometheus",
"version": "2.53"
},
{
"name": "i2group/i2eng-sqlserver",
"version": "2022-1"
},
{
"name": "i2group/i2eng-textchart-data-access",
"version": "7.5.0.6"
},
{
"name": "i2group/i2eng-textchart-manager",
"version": "7.5.2.0"
},
{
"name": "i2group/i2eng-textchart-worker",
"version": "7.5.2.0"
}
],
"i2a-core": [
{
"name": "ac_tools",
"version": "4.4.5.0"
},
{
"name": "etlclient_redhat",
"version": "4.4.5.0"
},
{
"name": "i2a_tools_redhat",
"version": "4.4.5.0"
},
{
"name": "liberty_redhat",
"version": "4.4.5.0"
},
{
"name": "solr_client_redhat",
"version": "4.4.5.0"
},
{
"name": "solr_redhat",
"version": "4.4.5.0"
}
],
"i2a-optional": [
{
"name": "postgres_client_image",
"version": "4.4.5.0"
},
{
"name": "sqlserver_client_redhat",
"version": "4.4.5.0"
}
]
}