Skip to content
This repository was archived by the owner on Dec 14, 2022. It is now read-only.

[WIP] Option to create a deployable runtime image #200

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pip3 install --index-url https://test.pypi.org/simple/ ros_cross_compile
1. Build
1. Runs the "sysroot image" using QEmu emulation
1. `colcon build`
1. (Optional) Create runtime image
1. Creates a docker image that can be used on the target platform to run the build. See "Runtime Image" section.

## Usage

Expand Down Expand Up @@ -198,6 +200,35 @@ Now, during the sysroot creation process, you should see the contents of `someth
NOTE: for trivial text files, as in the preceding example, you could have created those files fully within the `--custom-setup-script`. But for large or binary data such as precompiled libraries, this feature comes to the rescue.


### Runtime Image

`ros_cross_compile` can optionally create and tag a Docker image that contains all runtime dependencies and the created build of the workspace.

The argument `--runtime-image` takes a single value, which is the tag used for the output image.

```
OUTPUT_IMAGE=my_registry/image_name:image_tag
ros_cross_compile $workspace --runtime-image $OUTPUT_IMAGE
```

Now, you can deploy this to any registry to be pulled onto a target platform

```
docker push $OUTPUT_IMAGE
```

The image contains the necessary emulation binaries - or in a native build you do not need them.
Either way, you can interactively try out the image locally.

```
docker run -it $OUTPUT_IMAGE
# In the shell inside the running container, the setup is already sourced for the default entrypoint
ros2 launch my_package my.launch.py
```

Note: Currently this feature is a thin layer on top of the image used for building, so it is not a fully minimal image - it contains build tools, build dependencies, and test dependencies in addition to the necessary runtime dependencies.


## Tutorial

For a new user, this section walks you through a representative use case, step by step.
Expand Down
14 changes: 14 additions & 0 deletions ros_cross_compile/docker/runtime.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This dockerfile creates an image containing the runtime environment for the workspace.
# For now this is a thin layer on top of the sysroot/buildroot environment used to build.
# It re-sets the entrypoint to a shell instead of a build script, and copies in the install.
# The eventual plan for it is to only contain `<exec_depends>` of the workspace

ARG BASE_IMAGE
FROM $BASE_IMAGE
WORKDIR /ros_ws

ARG INSTALL_PATH
COPY $INSTALL_PATH/ /ros_ws/install

RUN echo "source /ros_ws/install/setup.bash" >> ~/.bashrc
ENTRYPOINT /bin/bash
8 changes: 8 additions & 0 deletions ros_cross_compile/ros_cross_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from ros_cross_compile.platform import SUPPORTED_ARCHITECTURES
from ros_cross_compile.platform import SUPPORTED_ROS2_DISTROS
from ros_cross_compile.platform import SUPPORTED_ROS_DISTROS
from ros_cross_compile.runtime import create_runtime_image
from ros_cross_compile.sysroot_creator import create_workspace_sysroot_image
from ros_cross_compile.sysroot_creator import prepare_docker_build_environment

Expand Down Expand Up @@ -128,6 +129,10 @@ def parse_args(args: List[str]) -> argparse.Namespace:
default=[],
nargs='+',
help='Skip specified rosdep keys when collecting dependencies for the workspace.')
parser.add_argument(
'--create-runtime-image',
help='Create a Docker image with the specified name that contains all '
'runtime dependencies and the created "install" directory for the workspace.')

return parser.parse_args(args)

Expand Down Expand Up @@ -164,6 +169,9 @@ def cross_compile_pipeline(
assert_install_rosdep_script_exists(ros_workspace_dir, platform)
create_workspace_sysroot_image(docker_client, platform)
run_emulated_docker_build(docker_client, platform, ros_workspace_dir)
if args.create_runtime_image is not None:
create_runtime_image(
docker_client, platform, ros_workspace_dir, args.create_runtime_image)


def main():
Expand Down
41 changes: 41 additions & 0 deletions ros_cross_compile/runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import os
from pathlib import Path

from ros_cross_compile.docker_client import DockerClient
from ros_cross_compile.platform import Platform

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring please

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant L26. Can you add docstring to the function?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have a linter for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ament_flake8 disables the docstring requirement. I'll add it, good call 👍

def create_runtime_image(
docker_client: DockerClient, platform: Platform, workspace_dir: Path, image_tag: str
):
logger.info('----------------------')
logger.info('Building runtime image: {}'.format(image_tag))
logger.info('----------------------')

docker_client.build_image(
dockerfile_name=os.path.join(docker_client._default_docker_dir, 'runtime.Dockerfile'),
dockerfile_dir=workspace_dir,
tag=image_tag,
buildargs={
'BASE_IMAGE': platform.sysroot_image_tag,
'INSTALL_PATH': 'install_{}'.format(platform.arch),
},
)