# ROS2와 Docker를 활용한 설치 및 환경 설정

#### Docker 활용 개요

ROS2를 Docker 환경에서 구동하면 호스트 시스템을 복잡하게 변경하지 않고도 표준화된 개발 환경을 구성할 수 있다. 이를 통해 운영체제나 의존성 패키지 버전 차이로 인한 문제를 줄이고, 여러 명이 동일한 환경에서 협업하기가 수월해진다. 또한 컨테이너 이미지를 활용하면 ROS2 Humble 버전을 쉽게 배포할 수 있으며, 디버깅 및 테스트에도 유리하다.

#### Docker 이미지 구성 시 고려 사항

Docker로 ROS2 Humble 환경을 구성할 때는 다음 사항을 유의해야 한다.

* **베이스 이미지(Base Image)** Ubuntu와 같은 리눅스 기반 이미지 위에 ROS2 Humble을 설치하는 것이 일반적이다. 예를 들어, `ubuntu:22.04` 베이스 이미지를 사용하여 ROS2 Humble 패키지를 설치할 수 있다.
* **ROS2 저장소 설정** 공식 ROS2 저장소(apt.ros.org) 또는 로컬 미러 저장소를 설정하여 필요한 패키지를 빠르게 설치할 수 있도록 한다.
* **런타임 의존성(Runtime Dependency)** GUI 도구(rviz2 등)를 사용하거나 하드웨어 가속(GPU 등)이 필요한 경우, 적절한 드라이버 또는 라이브러리를 이미지에 추가해야 한다.
* **환경 변수 설정** `ROS_DOMAIN_ID`와 같은 분산 환경 설정이 필요할 수 있다. 이때 Docker 컨테이너 내부에서 환경 변수를 설정하여 컨테이너별로 구분된 도메인 ID를 사용할 수도 있다.

#### 기본 Dockerfile 예시

아래는 가장 간단하게 ROS2 Humble을 설치하기 위한 Dockerfile 예시이다. 사용자는 실제 프로젝트에 맞춰 의존성을 추가로 설치해야 할 수 있다.

```dockerfile
FROM ubuntu:22.04

# 기본 패키지 업데이트 및 필수 도구 설치
RUN apt-get update && apt-get install -y \
    curl \
    gnupg2 \
    lsb-release

# ROS2 apt 저장소 추가
RUN curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add -
RUN sh -c 'echo "deb http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'

# ROS2 Humble 설치
RUN apt-get update && apt-get install -y \
    ros-humble-desktop

# ROS2 환경 설정
SHELL ["/bin/bash", "-c"]
RUN echo "source /opt/ros/humble/setup.bash" >> /root/.bashrc
```

이 Dockerfile은 다음 과정을 거친다.

1. **베이스 이미지**로 `ubuntu:22.04`를 사용한다.
2. ROS2 저장소를 추가하여 `apt` 명령어로 ROS2 Humble Desktop 버전을 설치한다.
3. 컨테이너 내에서 ROS2를 사용할 수 있도록 `setup.bash`를 `.bashrc`에 추가한다.

#### 환경 변수 및 런타임 설정

Docker 컨테이너에서 ROS2를 사용할 때는 환경 변수 설정에 유의해야 한다. 예를 들어, 다음과 같은 방법으로 `ROS_DOMAIN_ID`를 런타임에 설정 가능하다.

```bash
docker run -it --rm \
    -e ROS_DOMAIN_ID=30 \
    my_ros2_humble_image:latest \
    bash
```

만약 프로젝트별로 서로 다른 도메인 ID를 사용해야 한다면, 위 명령어에서 `-e ROS_DOMAIN_ID=<number>` 부분을 적절히 교체하여 컨테이너를 실행할 수 있다.

#### Docker로 분산 환경 구성 시 수학적 자원 모델

개별 컨테이너가 분산 환경에서 필요한 자원(메모리, CPU 코어, 대역폭 등)을 다음과 같은 자원 벡터 $\mathbf{R}$로 표현할 수 있다.

$$
\mathbf{R} =  \begin{bmatrix} r\_{\text{cpu}} \ r\_{\text{mem}} \ r\_{\text{net}} \ \vdots \end{bmatrix}
$$

이때 $\mathbf{R}$은 컨테이너 하나가 요청하는 자원 사용량을 나타내며, 여러 개의 컨테이너가 동시에 실행될 경우 전체 자원 사용량의 합은

$$
\mathbf{R}*{\text{total}} = \sum*{i=1}^{N} \mathbf{R}\_i
$$

와 같이 계산할 수 있다. 여기서 NN은 동시에 실행되는 컨테이너의 수를 의미한다.

#### Docker Networking과 ROS2 상호작용

Docker 컨테이너 간 통신을 위해 네트워크를 설정해야 한다. ROS2는 기본적으로 DDS(데이터 분산 서비스)를 사용하므로, 컨테이너 간 UDP 포트가 열려 있어야 한다. Docker에서는 컨테이너 실행 시 다음과 같은 옵션을 활용할 수 있다.

* **--net=host** 호스트 네트워크를 공유한다. 이 경우 컨테이너가 호스트와 동일한 IP 스택을 사용하므로 DDS 브로드캐스트나 멀티캐스트 패킷 교환에 편리하다. 그러나 모든 포트를 호스트와 공유하게 되므로 충돌 가능성이 있다.
* **bridge 네트워크** 디폴트 Docker 네트워크 방식이다. 컨테이너 간에는 bridge를 통해 격리되고 포트 매핑을 통해 통신한다. ROS2의 DDS 브로드캐스트 통신을 위해서는 멀티캐스트 설정이 필요하다.

  ```bash
  docker run -it --rm \
      --network bridge \
      -p 7400:7400/udp \
      -p 25000-25010:25000-25010/udp \
      my_ros2_humble_image:latest
  ```

  위 명령어와 같이 필요한 UDP 포트를 포워딩하여 DDS 통신이 가능하도록 설정할 수 있다.

#### Docker Compose를 이용한 멀티 컨테이너 구성

복수의 ROS2 노드를 각각 독립된 컨테이너로 구동해야 할 때 Docker Compose를 사용하면 편리하다. 예시로, `docker-compose.yaml` 파일을 통해 네 가지 컨테이너(예: talker, listener, bridge, db 등)를 동시에 배포할 수 있다.

```yaml
version: '3'
services:
  ros2_talker:
    image: my_ros2_humble_image:latest
    container_name: ros2_talker
    command: >
      bash -c "source /opt/ros/humble/setup.bash &&
               ros2 run demo_nodes_cpp talker"
    network_mode: host

  ros2_listener:
    image: my_ros2_humble_image:latest
    container_name: ros2_listener
    command: >
      bash -c "source /opt/ros/humble/setup.bash &&
               ros2 run demo_nodes_cpp listener"
    network_mode: host
```

위 예시에서는 `network_mode: host`를 사용하여 호스트 네트워크 환경을 그대로 사용한다. 이렇게 하면 DDS를 위한 멀티캐스트 패킷 교환 설정을 명시적으로 하지 않아도 된다. 단, 보안 및 포트 사용에 대한 고려가 필요하다.

#### GPU 지원을 위한 Docker 설정

ROS2에서 CUDA나 OpenCL 등 GPU 가속을 사용해야 하는 경우, nvidia-docker(또는 NVIDIA Container Toolkit)를 활용할 수 있다.

* **NVIDIA Container Toolkit 설치** 호스트에 NVIDIA 드라이버가 설치되어 있어야 하며, NVIDIA Container Toolkit을 설치해야 컨테이너 내부에서 GPU를 활용할 수 있다.
* 실행 옵션

  ```bash
  docker run -it --rm \
      --gpus all \
      my_ros2_humble_gpu_image:latest
  ```

  ```
  --gpus all
  ```

  옵션을 통해 호스트의 모든 GPU를 컨테이너에서 접근 가능하도록 설정한다.

#### 볼륨(Volume)을 통한 데이터 공유

ROS2 프로젝트에서 로그 파일이나 설정 파일 등을 컨테이너 외부에서 지속적으로 관리하고자 할 때, Docker 볼륨을 사용하면 좋다. 예를 들어,

```bash
docker run -it --rm \
    -v $(pwd)/config:/root/config \
    my_ros2_humble_image:latest \
    bash
```

처럼 `-v` 옵션으로 호스트 디렉터리를 컨테이너 내부에 마운트할 수 있다. 이 방법을 통해 개발 시 생성된 설정 파일을 호스트에서 바로 열람 가능하다.

#### 분산 멀티 호스트 환경에서의 수학적 모델

컨테이너가 여러 물리 호스트에 분산 실행될 경우, DDS 통신 모델을 고려한 네트워크 전송 지연을 다음과 같이 근사할 수 있다. 컨테이너 $i$에서 컨테이너 $j$로의 평균 전송 지연을 $\mathbf{D}\_{ij}$라 할 때,

$$
\mathbf{D}*{ij} = d*{\text{base}} + d\_{\text{net}}(\mathbf{R}\_i, \mathbf{R}\_j)
$$

로 나타낼 수 있다. 여기서 $d\_{\text{base}}$는 물리적 거리 및 기본 라우팅 지연에 의해 발생하는 상수 항이고, $d\_{\text{net}}$은 네트워크 트래픽 양($\mathbf{R}\_i$, $\mathbf{R}\_j$)에 따라 달라지는 변수 항이다.

$$
d\_{\text{net}}(\mathbf{R}\_i, \mathbf{R}\_j)  = \alpha \cdot (|\mathbf{R}\_i| + |\mathbf{R}\_j|)
$$

α\alpha는 트래픽에서 지연으로 전환되는 비례계수를 의미한다. $\mathbf{R}|$은 컨테이너 자원 벡터의 노름(예: $L\_1$ 혹은 $L\_2$ 노름)을 나타낸다.

#### 성능 튜닝 및 리소스 제한

Docker는 기본적으로 호스트 자원을 공유하지만, `--cpus`, `--memory` 등의 옵션을 통해 특정 컨테이너가 사용할 수 있는 CPU나 메모리를 제한할 수 있다. ROS2 노드가 실시간 처리를 요구하거나 일정 이상의 처리 속도를 유지해야 하는 경우, 적절한 리소스 할당으로 성능을 보장할 수 있다.

```bash
docker run -it --rm \
    --cpus="2.0" \
    --memory="4g" \
    my_ros2_humble_image:latest \
    bash
```

또한, 실시간 운영체제(RTOS) 기능을 사용하는 리눅스 커널(Preempt-RT 패치 등)을 활용하는 경우, 호스트 커널의 실시간 스케줄링이 Docker 컨테이너에도 적용되도록 cgroup 설정을 세부적으로 조정해야 한다.

#### Multi-stage Build를 활용한 이미지 최적화

ROS2 프로젝트가 커지면 Docker 이미지 용량이 커지기 쉽다. Multi-stage Build를 활용하면 빌드 과정에서만 필요한 의존 라이브러리와 실행 시점에 필요한 라이브러리를 분리하여 최종 이미지를 가볍게 만들 수 있다.

```dockerfile
# Stage 1: Build
FROM ubuntu:22.04 as builder

RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    git

# ROS2 소스 빌드 예시
RUN git clone https://github.com/ros2/demos.git /tmp/demos
WORKDIR /tmp/demos
RUN cmake . && make install

# Stage 2: Runtime
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
    ros-humble-ros-base

COPY --from=builder /usr/local/bin/some_ros2_executable /usr/local/bin/some_ros2_executable

ENTRYPOINT ["/usr/local/bin/some_ros2_executable"]
```

이렇게 두 단계(빌드 단계와 런타임 단계)로 구분함으로써, 최종 컨테이너에는 실행 바이너리만 포함되어 이미지를 최소화할 수 있다.

#### 멀티 아키텍처 지원

라즈베리 파이(Raspberry Pi) 등 ARM 아키텍처 환경에서 ROS2 Humble을 구동하려면, 멀티 아키텍처 이미지를 빌드하거나 QEMU를 이용해야 한다. Docker는 `buildx` 기능을 통해 서로 다른 아키텍처용 이미지를 하나의 리포지토리에 등록할 수 있다.

```bash
docker buildx create --use
docker buildx build \
    --platform linux/amd64,linux/arm64 \
    -t my_ros2_humble_image:latest \
    --push .
```

위 명령어로 동시에 x86\_64(amd64)와 ARM64용 이미지를 빌드하여 Docker Registry에 푸시할 수 있다. 이렇게 하면 동일한 이미지 이름을 사용하더라도 아키텍처에 맞는 이미지를 자동으로 받아 사용할 수 있다.

#### 시각적 네트워크 구조 표현

Docker 네트워킹과 ROS2 통신 구조를 시각화하기 위해 mermaid를 활용한 간단한 예시를 보면 아래와 같다.

{% @mermaid/diagram content="flowchart LR
subgraph Host
A\[Talker Container] -->|DDS| B\[Listener Container]
end
subgraph Bridge
C\[Docker Bridge Network]
end
A --> C
B --> C" %}

위 그림에서 **Talker Container**와 **Listener Container**는 동일 호스트 내에서 Bridge 네트워크를 통해 DDS로 통신한다. 여러 호스트를 연결하려면 각 호스트에 존재하는 브리지 네트워크를 Overlay 네트워크 등으로 확장하거나 `--net=host` 옵션을 사용하여 멀티캐스트가 원활히 동작하도록 구성해야 한다.

#### 컨테이너 간 DDS 도메인 분리

ROS2에서 DDS 도메인을 분리하여 여러 독립적인 시스템이 서로 간섭 없이 구동되도록 하려면 `ROS_DOMAIN_ID`를 다르게 설정하면 된다. 이를 Docker 환경에서 컨테이너 단위로 관리하면, 물리적으로 동일한 호스트 위에서도 별도의 네트워크 트래픽 분리가 가능하다.

$$
\mathbf{D}*{\text{collision}} = \sum*{i \neq j} f(\text{domain\_id}\_i, \text{domain\_id}\_j)
$$

여기서 $f(\cdot)$는 도메인 ID가 충돌할 경우에 발생하는 트래픽 간섭에 대한 함수다. 각 컨테이너의 도메인 ID가 상이하다면 $\mathbf{D}\_{\text{collision}}$ 항이 0에 가까워지고, DDS 브로드캐스트 충돌이 줄어든다.

#### 고가용성(HA) 및 확장성(Scalability)

ROS2 노드를 다중 컨테이너로 구성하여 특정 노드 장애 시 다른 노드가 대체하도록 하면 고가용성을 높일 수 있다. 이때 Docker Swarm 또는 Kubernetes를 통해 컨테이너를 오케스트레이션하면, 장애가 발생한 컨테이너를 재시작하거나 다른 호스트로 옮기는 작업이 자동화된다.

$$
\mathbf{N} =  \begin{bmatrix} n\_1 \ n\_2 \ \vdots \ n\_k \end{bmatrix}
$$

여기서 N\mathbf{N}은 각 물리 호스트의 컨테이너 수를 나타내는 벡터이며, 오케스트레이션 시스템은 $\mathbf{N}$의 일부 항목이 과도하게 증가하지 않도록 컨테이너를 분산 배치한다.

#### CI/CD 파이프라인에서 Docker 기반 ROS2 빌드

개발 환경에서 Docker를 사용하면, CI/CD(지속적 통합 및 지속적 배포) 파이프라인에서 일관된 ROS2 빌드 환경을 쉽게 재현할 수 있다. 특히 다음과 같은 장점을 얻을 수 있다.

* **일관성**: 모든 개발자와 빌드 서버가 동일한 Docker 이미지를 사용하므로, 환경 차이로 인한 빌드 실패를 방지한다.
* **재현 가능성**: 빌드 환경이 코드 레포지토리와 분리되어 있으므로, Docker 이미지만 있으면 어디서든 동일한 ROS2 환경을 구성할 수 있다.
* **격리성**: CI 시스템이 빌드 시 사용하는 라이브러리와 호스트 OS의 라이브러리가 충돌하지 않는다.

일반적인 CI/CD 구성 예:

```yaml
# 예시 GitLab CI configuration
stages:
  - build
  - test

build_ros2:
  stage: build
  image: my_ros2_humble_build_image:latest
  script:
    - source /opt/ros/humble/setup.bash
    - colcon build --symlink-install

test_ros2:
  stage: test
  image: my_ros2_humble_build_image:latest
  script:
    - source /opt/ros/humble/setup.bash
    - colcon test
    - colcon test-result
```

위와 같이 CI/CD에서 Docker 이미지를 지정해주면, ROS2 Humble 개발 환경이 세팅된 컨테이너에서 자동으로 빌드와 테스트를 진행할 수 있다.

#### Docker Logging과 컨테이너 로그 관리

ROS2 노드가 배포되는 Docker 컨테이너가 많아질수록, 시스템 전반의 로그를 모니터링하고 추적하는 일이 중요해진다. Docker는 기본적으로 `json-file` 드라이버를 통해 STDOUT, STDERR 스트림 로그를 관리하지만, 컨테이너별로 로그 옵션을 세부 조정할 수 있다.

* **로컬 파일 용량 제한**

  ```bash
  docker run -it --rm \
      --log-driver json-file \
      --log-opt max-size=10m \
      --log-opt max-file=3 \
      my_ros2_humble_image:latest
  ```

  이렇게 하면 컨테이너 로그가 10MB가 넘어가면 롤링 파일을 사용하며 최대 3개까지만 저장한다.
* **syslog, fluentd 등 외부 로그 수집 시스템** 대규모 시스템에서 로그를 중앙화하려면 `--log-driver=syslog` 또는 `--log-driver=fluentd` 등을 활용하여 로그를 한곳으로 모을 수 있다.

#### 컨테이너 디버깅 기법

ROS2 노드 컨테이너를 디버깅해야 할 때는 여러 가지 접근법이 있다.

* **컨테이너 쉘 접속**

  ```bash
  docker exec -it <container_name> bash
  ```

  이미 실행 중인 컨테이너에 접속하여 토픽이나 노드 상태를 확인할 수 있다.
* **Visual Studio Code Remote Containers** VSCode에서 Docker 컨테이너를 원격 개발 환경으로 사용하고, 디버깅 기능(GDB 등)을 통해 컨테이너 내에서 동작하는 ROS2 노드를 직접 디버깅할 수 있다.
* **ros2 CLI 사용** `ros2 topic list`, `ros2 node list` 등의 명령어를 사용하여 런타임 정보를 확인한다.

#### Docker 이미지 레이어링과 Best Practice

ROS2 Humble용 Docker 이미지를 만들 때, Docker 레이어 구조를 효율적으로 설계해야 빌드 시간을 단축하고 용량을 줄일 수 있다.

* **빈번히 변경되는 파일 최소화** Dockerfile의 상단에 자주 변하지 않는 의존 패키지 설치 단계를 두고, 하단에 프로젝트 소스 코드를 복사하도록 구성한다.
* **캐시 활용 극대화** `RUN apt-get update && apt-get install -y ...` 등은 최대한 한 줄로 작성하고, 빈번히 변동되는 파일(예: `requirements.txt`)은 하단에 COPY하여 캐시가 무효화되지 않도록 한다.
* **불필요한 파일 삭제** 로컬에서 빌드 시 생성되는 임시 파일은 빌드 후 이미지에 남겨두지 말고 `rm -rf`로 제거하여 최종 이미지 크기를 줄인다.

#### Docker Registry 운영

CI/CD와 연계하여 자주 빌드되는 ROS2용 Docker 이미지를 공유하기 위해, 사내 Docker Registry나 Docker Hub를 이용할 수 있다.

* 사내 Registry

  네트워크 대역폭, 보안 정책 등의 이유로 사내에 Private Registry를 구성한다.

  ```bash
  docker login myregistry.example.com
  docker tag my_ros2_humble_image:latest myregistry.example.com/ros2/humble:latest
  docker push myregistry.example.com/ros2/humble:latest
  ```
* **Docker Hub** 공개 배포가 필요한 경우, Docker Hub에 이미지를 올려 다른 사용자와 공유할 수 있다.

#### 에포크(Epoch) 및 이미지 프루닝

여러 버전의 ROS2 이미지가 빌드되다 보면 호스트 디스크 용량이 빠르게 소모된다. 정기적으로 오래된 이미지를 정리하기 위해 Docker prune 명령어를 사용한다.

```bash
docker system prune -af
```

* `-a` 옵션: 사용하지 않는 모든 이미지, 컨테이너, 네트워크, 볼륨을 제거한다.
* `-f` 옵션: 확인 메시지 없이 강제 삭제한다.

또한, CI 파이프라인에서 이미지 태그에 빌드 번호나 GIT SHA 등을 부여하여 버전을 관리하면 불필요한 이미지를 식별하는 데 도움이 된다.

#### 확장된 수학 모델: DDS 데이터 전송 동적 수요

ROS2 DDS 환경에서 토픽이 늘어나거나 데이터 전송량이 많아지면, 네트워크 병목이 생길 수 있다. 동적으로 증가하는 데이터 전송 수요 $\mathbf{S}(t)$를 다음과 같이 표현할 수 있다.

$$
\mathbf{S}(t) = \sum\_{k=1}^{M(t)} \mathbf{s}\_k(t)
$$

* $M(t)$: 시점 $t$에서 활성화된 ROS2 토픽의 개수
* $\mathbf{s}\_k(t)$: $k$번째 토픽의 전송 요구량 벡터(주파수, 데이터 사이즈 등)

각 컨테이너 $\mathbf{C}\_i$가 할당된 네트워크 대역폭 $\mathbf{B}\_i$를 넘어서는 수요가 발생하면 전송 지연이 급격히 증가하거나 데이터 유실이 발생할 수 있다. 따라서 오케스트레이션 도구나 로드 밸런서를 통해 $\mathbf{S}(t)$와 $\mathbf{B}\_i$의 균형을 맞추는 전략이 필요하다.

#### Docker 보안과 ROS2

Docker 컨테이너는 호스트 OS와 커널을 공유하므로, 완벽한 가상머신(VM) 수준의 격리를 제공하지 않는다. ROS2 노드가 실행되는 컨테이너에서 발생하는 보안 이슈를 최소화하기 위해 다음과 같은 방법을 고려할 수 있다.

* **AppArmor/SELinux** 리눅스 보안 모듈(LSM)을 활용해 컨테이너별 권한 범위를 제한할 수 있다. 예를 들어, Ubuntu에서 Docker는 기본적으로 AppArmor 프로파일을 적용하여 컨테이너가 호스트 파일 시스템에 무분별하게 접근하지 못하도록 한다.

  ```bash
  docker run -it --rm \
      --security-opt apparmor=unconfined \
      my_ros2_humble_image:latest
  ```

  위 예시는 AppArmor를 해제(unconfined)하는 명령어다. 반대로 안전한 운영을 위해서는 필요 최소한의 권한만 부여하는 커스텀 프로파일을 작성하여 적용해야 한다.
* **권한 분리(User Namespace)** Docker의 User Namespace 기능을 활성화하면, 컨테이너 내부의 root 사용자가 호스트 측에서는 일반 사용자 권한으로 매핑된다. 이는 호스트 시스템에 대한 피해 가능성을 줄여준다.

  ```bash
  dockerd --userns-remap=default
  ```

  이러한 설정을 통해 컨테이너 내부에서 root로 실행되더라도 호스트에 미치는 영향을 최소화할 수 있다.

#### 시간 동기화(Time Synchronization)

ROS2는 분산 환경에서 메시지 타임스탬프를 활용하므로, 컨테이너 간 시간 동기화가 중요하다. Docker 컨테이너는 호스트 커널의 시간을 직접 공유하지만, NTP나 Chrony 등을 통해 호스트 시간이 적절히 동기화되어야 전체 ROS2 시스템이 오차 없이 동작한다.

* **호스트 기반 시간 동기화** 호스트 OS 수준에서 NTP나 Chrony로 시간을 동기화한다.
* 컨테이너별 시간 확인

  ```bash
  docker exec -it <container_name> date
  ```

  명령어로 컨테이너 내부에서 시간이 정상적으로 호스트와 일치하는지 확인할 수 있다.

#### 실시간(Real-time) Docker 설정

ROS2에서 실시간 처리가 필요한 경우, 리눅스 커널의 PREEMPT\_RT 기능이나 특정 스케줄링 정책을 활용해야 한다. 일반 Docker 환경에서도 cgroup 설정과 함께 RT 우선순위를 일부 적용할 수 있다.

* **sysfs를 통한 RT 제한 해제** 호스트 커널이 RT 패치가 적용되어 있고, `/sys/fs/cgroup` 등이 적절히 설정되어 있어야 한다.
* 컨테이너 실행 시 --ulimit 옵션

  ```bash
  docker run -it --rm \
      --ulimit rtprio=99 \
      my_ros2_humble_rt_image:latest \
      bash
  ```

  ```
  rtprio=99
  ```

  는 리얼타임 우선순위를 최대치까지 허용한다는 뜻이다. 이런 설정이 없으면 ROS2 노드가 RT 스케줄링 정책을 사용할 수 없다.

#### Swarm/Kubernetes를 통한 확장

Docker Swarm, Kubernetes(K8s)와 같은 컨테이너 오케스트레이션 도구를 이용하면, 복수 호스트에 걸쳐 ROS2 컨테이너를 자동으로 배포하고 모니터링할 수 있다. 예를 들어 Kubernetes를 통해 ROS2 노드 컨테이너를 배포한다고 가정하자.

* Kubernetes Deployment 예시

  ```yaml
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: ros2-talker
  spec:
    replicas: 3
    selector:
      matchLabels:
        app: ros2-talker
    template:
      metadata:
        labels:
          app: ros2-talker
      spec:
        containers:
        - name: ros2-talker-container
          image: my_ros2_humble_image:latest
          command: ["/bin/bash", "-c"]
          args:
            - |
              source /opt/ros/humble/setup.bash &&
              ros2 run demo_nodes_cpp talker
  ```

  여기서

  ```
  replicas: 3
  ```

  은 동일한 노드(talker)를 3개 구동한다는 설정이다. DDS 멀티캐스트 통신에 맞춰 네트워크 설정을 따로 해줘야 할 수 있다.

#### 컨테이너 간 자원 경합 모델

멀티 호스트, 멀티 컨테이너 환경에서 CPU, 메모리, 네트워크 등 다양한 자원이 경합을 일으킬 수 있다. 이를 단순화해 자원 벡터 $\mathbf{R}$와 컨테이너 개수 $N$에 대해 아래와 같이 표현한다.

$$
\mathbf{R}*{\text{host}} \ge \sum*{i=1}^{N} \mathbf{R}\_i
$$

* $\mathbf{R}\_{\text{host}}$: 호스트 전체가 제공할 수 있는 자원 벡터
* $\mathbf{R}\_i$: 컨테이너 $i$가 소모하는 자원 벡터

ROS2 시스템에서 여러 노드가 동시에 동작하며 토픽을 교환할 때, 각 노드가 요구하는 CPU/GPU/메모리 사용량에 따라 스케줄링 및 배치를 최적화해야 한다. 오케스트레이션 툴이 $\mathbf{R}\_{\text{host}} \ge \sum \mathbf{R}\_i$를 만족하도록 자동으로 컨테이너를 할당하면, 특정 호스트에 과부하가 걸리는 사태를 방지할 수 있다.

#### 호스트-컨테이너 상호작용 모델

ROS2 런타임에서 호스트와 컨테이너의 상호작용 지연을 $\mathbf{T}\_{hc}$라 할 때, 이를 다음처럼 근사할 수 있다.

$$
\mathbf{T}*{hc} = t*{\text{syscall}} + t\_{\text{container}}
$$

* $t\_{\text{syscall}}$: 호스트 OS와 컨테이너 간 시스템 콜로 인한 오버헤드
* $t\_{\text{container}}$: 컨테이너 내부에서 발생하는 추가 처리(파일 시스템 레이어 접근, 네트워크 네임스페이스 변경 등)로 인한 지연

ROS2 노드가 빈번하게 파일 접근 또는 호스트 자원 접근을 요청하면 $t\_{\text{container}}$ 값이 증가해 전체 지연이 커질 수 있다. 이를 줄이기 위해서는 호스트와 컨테이너 간 상호작용을 최소화하거나, 필요한 데이터를 볼륨 마운트를 통해 효율적으로 접근하도록 구조를 설계하는 것이 좋다.

#### 크로스 컴파일(Cross Compile) 환경 구성

임베디드 디바이스(예: ARM 아키텍처 기반)에서 ROS2 Humble을 구동하기 위해서는 크로스 컴파일 환경이 필요하다. 이 과정을 Docker 컨테이너로 구성하면, 호스트(AMD64 등)에서 ARM용 바이너리를 빌드하여 라즈베리파이와 같은 장치에 배포할 수 있다.

* QEMU 에뮬레이션

  x86\_64 호스트에서 ARM 바이너리를 빌드하기 위해 QEMU 에뮬레이터를 설치하고 Docker 컨테이너가 이를 인식하도록 설정한다.

  ```bash
  apt-get install qemu-user-static
  docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
  ```
* **크로스 컴파일 툴체인 설치** 예를 들어, `gcc-arm-linux-gnueabihf` 등 필요한 툴체인을 Dockerfile에 추가하여 빌드 환경을 마련한다.
* **CMake 빌드 옵션** `-DCMAKE_TOOLCHAIN_FILE` 옵션을 사용해 크로스 컴파일 툴체인 파일을 지정하고, ROS2 소스 빌드 시 이에 맞춰 아키텍처를 설정한다.

#### 테스트 컨테이너의 네트워크 분할

ROS2 노드를 여러 컨테이너에서 테스트할 때, 일부 컨테이너는 완전히 격리된 네트워크에서 동작하고, 다른 컨테이너는 외부와 통신하도록 설정해야 할 수 있다. 예를 들어, `docker network create` 명령어로 별도의 네트워크를 생성한 뒤 컨테이너를 해당 네트워크에 연결해 DDS 멀티캐스트 트래픽을 분리할 수 있다.

```bash
docker network create --driver bridge net_ros2_test

docker run -d \
    --name ros2_isolated_node \
    --network net_ros2_test \
    my_ros2_humble_image:latest

docker run -d \
    --name ros2_public_node \
    --network bridge \
    my_ros2_humble_image:latest
```

이렇게 하면 `ros2_isolated_node`가 속한 네트워크와 `ros2_public_node`의 네트워크가 분리되어, DDS 브로드캐스트 충돌을 줄이거나 테스트 환경을 독립적으로 운영할 수 있다.

#### 매개변수 파일 및 런치 파일 관리

ROS2에서는 노드를 실행할 때 매개변수(파라미터) 파일을 불러와 설정할 수 있다. Docker 환경에서 이를 편리하게 관리하기 위해, 호스트 디렉터리를 컨테이너 내부로 볼륨 마운트하여 매개변수를 동적으로 변경할 수 있다.

```bash
docker run -it --rm \
    -v $(pwd)/params:/root/params \
    my_ros2_humble_image:latest \
    bash -c "
      source /opt/ros/humble/setup.bash &&
      ros2 run my_package my_node --ros-args --params-file /root/params/config.yaml
    "
```

런치 파일(`.launch.py`)도 같은 방식으로 관리하여, 컨테이너를 매번 재빌드하지 않고도 설정만 바꾸어 빠르게 테스트를 진행할 수 있다.

#### Ephemeral 컨테이너와 짧은 수명 노드

ROS2에서 주기적으로 실행 후 종료되는 노드(예: 데이터 처리 작업을 마친 뒤 자동 종료)는 에페멀(Ephemeral) 형태로 컨테이너를 구성할 수 있다.

* Docker의 --rm 옵션

  ```bash
  docker run --rm my_ros2_humble_image:latest ros2 run my_package ephemeral_node
  ```

  작업이 끝난 뒤 컨테이너가 자동으로 제거되므로, 불필요한 컨테이너 잔해가 남지 않는다.

#### 멀티마스터 환경(ROS2는 기본적으로 멀티마스터 구조가 불필요하지만, 특정 플러그인을 사용하는 경우)

ROS1과 달리 ROS2는 DDS 기반이라 별도의 멀티마스터 설정 없이도 분산 노드 간 통신이 가능하다. 그러나 ROS1/ROS2 브리지 또는 특정 요구 사항에 의해 멀티마스터 환경이 필요한 경우, Docker Compose나 Swarm을 통해 여러 개의 ROS Master(ROS1) 혹은 커스텀 오케스트레이션 노드를 병렬로 띄울 수 있다. 이때 각 마스터나 브리지 컨테이너에 별도 네트워크/포트 설정을 적용하여 통신을 확립한다.

#### 추가적인 모범 사례 정리

* **Immutable Infrastructure** 컨테이너 이미지는 변경 불가능(Immutable)하게 취급하고, 설정만 런타임에 주입한다. 이 방식으로 환경 일관성을 유지한다.
* **Layer Caching** Dockerfile 레이어 순서를 최적화하여, 자주 변경되는 부분(소스 코드)을 맨 아래쪽에 배치한다.
* **CI/CD 모듈화** 빌드, 테스트, 린트(lint) 등의 단계를 분리해, Docker 이미지를 다단계 빌드(Multi-stage)하거나 별도 스테이지로 관리한다.
