네이티브 앱 vs 도커 컨테이너(and Host vs Bridge)

Heechan Kang·2024년 6월 14일
1

Docker Container Benchmarking

개요

간단한 Node.js 서버를 활용하여 Docker Container의 성능을 벤치마킹하는 프로젝트입니다. 네트워크, CPU, 디스크 I/O 성능을 측정하여 네이티브 환경과 도커 컨테이너 환경의 성능 차이를 비교/분석하고, 최적의 배포 환경을 알아보고자 합니다.

세부사항

서버 스펙

  • Host: AWS EC2 t2.xlarge(4 vCPU, 16GB RAM), Ubuntu 24.04 LTS
    • Node.js: v18.19.1
  • Docker: Docker version 24.0.7, build 24.0.7-0ubuntu4
    • Node.js: v18.20.3
  • Client: Apple MacBook M1 Pro(8-core CPU, 16GB RAM)

테스트 엔드포인트

// app.js
app.get('/network', (req, res) => {
  res.send('Network endpoint response');
});

app.get('/disk-io', (req, res) => {
  const filePath = 'testfile.txt';
  const fileContent = 'Some content to write to the file';

  fs.writeFile(filePath, fileContent, (err) => {
    if (err) {
      res.status(500).send('Error writing file');
      return;
    }

    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        res.status(500).send('Error reading file');
        return;
      }

      res.send(`File content: ${data}`);
    });
  });
});

app.get('/cpu', (req, res) => {
  const start = Date.now();

  for (let i = 0; i < 1e5; i++) {
    crypto.createHash('sha256').update('test').digest('hex');
  }

  const end = Date.now();
  res.send(`CPU intensive task completed in ${end - start} ms`);
});
  • /network: 네트워크 성능 측정. 다른 영향을 최소화하도록 간단한 응답을 반환합니다.
  • /disk-io: 디스크 I/O 성능 측정. 파일을 생성하고 읽어서 응답을 반환합니다.
  • /cpu: CPU 성능 측정. CPU 집약적인 해싱 작업을 반복하여 응답을 반환합니다.

테스트 조건

  • 동일한 Node.js 코드를 각각 Native, Docker container(bridge, host)에서 실행

  • wrk(벤치마킹 툴)를 활용한 아래의 스크립트로 각 엔드포인트에 대한 요청을 1분간 수행

    #!/bin/bash
    
    DURATION="1m"
    THREADS=32
    CONNECTIONS=400
    
    wrk -t$THREADS -c$CONNECTIONS -d$DURATION http://0.0.0.0:3000/network > ./host/host_network.txt
    
    wrk -t$THREADS -c$CONNECTIONS -d$DURATION http://0.0.0.0:3000/disk-io > ./host/host_disk_io.txt
    
    DURATION="1m"
    THREADS=2
    CONNECTIONS=4
    
    wrk -t$THREADS -c$CONNECTIONS -d$DURATION http://0.0.0.0:3000/cpu > ./host/host_cpu.txt

실행 방법

  1. Native

    node app.js # 3000번 포트로 서버 실행
  2. Docker container(bridge)

    docker run \
      --rm \
      -v /home/ubuntu:/usr/src/app \
      -w /usr/src/app \
      -p 3000:3000  # 포트 매핑, 브릿지 네트워크 사용 \
      node:18 \
      node app.js
  3. Docker container(host)

    docker run \
      --rm \
      -v /home/ubuntu:/usr/src/app \
      -w /usr/src/app \
      --network host  # 호스트 네트워크 사용 \
      node:18 \
      node app.js

예상 결과

  • 네트워크 성능
    • NativeDocker container(host) > Docker container(bridge)
  • CPU 성능
    • Native > Docker container(host)Docker container(bridge)
  • 디스크 I/O 성능
    • Native > Docker container(host)Docker container(bridge)

실제 결과(3회 평균)

  • 네트워크 성능 (Requests/sec)

    테스트네이티브Docker (bridge)Docker (host)
    1회차5731.097008.737718.26
    2회차5785.426923.707485.36
    3회차5559.806961.607580.54
    평균5692.106964.687594.72
  • CPU 성능 (Requests/sec)

    테스트네이티브Docker (bridge)Docker (host)
    1회차3.514.234.21
    2회차3.464.164.16
    3회차3.514.164.24
    평균3.494.184.20
  • 디스크 I/O 성능 (Requests/sec)

    테스트네이티브Docker (bridge)Docker (host)
    1회차4326.903965.714291.90
    2회차4434.903971.724234.98
    3회차4262.513926.304349.31
    평균4341.443954.584292.06
  • 단순 요약

    • 네트워크 성능
      • Docker container(host) > Docker container(bridge) > Native
    • CPU 성능
      • Docker container(bridge)Docker container(host) > Native
    • 디스크 I/O 성능
      • Native > Docker container(host) > Docker container(bridge)

총평

  • Latency

  • RPS

네트워크 성능

네트워크 성능 측면에서 Docker container(host) 설정이 가장 우수한 성능을 보였습니다. 이는 아무래도 Docker container(bridge)와 비교했을 때, 호스트 네트워크 인터페이스를 직접 사용하는 host 모드의 이점 때문으로 보입니다.

  • Docker container(host): 호스트의 네트워크 인터페이스를 공유하여 네트워크 오버헤드가 거의 없습니다.
  • Docker container(bridge): 네트워크 패킷이 브리지 네트워크를 통해 라우팅되는 과정을 거치며 미세한 오버헤드가 발생합니다. 미미하다고 볼 수도 있지만, 네트워크 성능이 중요한 서비스라면 고려해야 할 수준으로 보입니다.
  • Native: 정말 모르겠습니다. 다만, 아마도 Docker container(host)에서 사용한 Node:18 공식 이미지에 뭔가 최적화 튜닝이 되어있지 않았을까 하는 생각이 듭니다.

CPU 성능

CPU 성능은 의외로 Native에서 가장 좋지 않았습니다. 이는 아무래도 Node:18 공식 이미지의 최적화와 관련이 있어 보입니다.

  • Docker container: Node:18 이미지 내의 최적화와 컨테이너 환경의 경량화가 CPU 성능 에 유리한 효과를 준 것 같습니다. 다만 확인이 필요합니다.
  • Native: 가장 우수할것이라는 예상과는 반대로, 가장 떨어지는 성능을 보였습니다. 정직하게 정석대로 설치된 Ubuntu 24.04 환경이 원인일 수도 있겠습니다.

디스크 I/O 성능

디스크 I/O 성능에서는 Native 환경이 미세하게 더 나은 성능을 보였습니다. 아무래도 Native 환경이 파일 시스템에 직접 접근할 수 있다는 게 큰 이유로 보입니다.

  • Native: 파일 시스템에 직접 접근하므로 가장 우수한 성능을 보인 것 같습니다.
  • Docker container(host): Native와 거의 유사한 성능을 보였습니다. 네트워크 인터페이스 뿐 아닌 파일 시스템에도 직접 접근할 수 있는것인지 확인해 보아야겠습니다.
  • Docker container(bridge): 브릿지 모드와 호스트 모드는 네트워크에만 있는 것으로 알고있었는데, 디스크 I/O 성능에서도 꽤나 큰 차이를 보였습니다. 원인은 아직 모르겠습니다.

아쉬운 점

  1. 버전의 일관성이 부족했습니다. 사용한 Node.js 버전부터 운영체제의 버전까지 미묘하게 달라, 꽤나 영향을 끼쳤을 수 있습니다.
  2. 디스크 I/O 성능에서 호스트 모드와 브릿지 모드의 성능 차이의 원인을 전혀 모르겠습니다.
  3. 테스트의 자동화가 부족했습니다. 실제로 수동으로 테스트를 진행하다보니, 예상치 못한 변수(네트워크 변경 등)가 발생해서 테스트를 여러 번 다시 시작해야 했습니다.

+@ 브리지 vs 호스트 모드의 DiskIO 차이의 원인(by ChatGPT)

Docker 컨테이너의 호스트 모드와 브리지 모드는 주로 네트워크 인터페이스에 영향을 미치는 설정입니다. 그러나 디스크 I/O 성능에서도 차이가 나는 이유는 몇 가지가 있을 수 있습니다.

1. 컨테이너 스토리지 드라이버

Docker는 컨테이너의 파일 시스템을 관리하기 위해 다양한 스토리지 드라이버를 사용합니다. 가장 일반적인 드라이버는 overlay2입니다. 스토리지 드라이버는 컨테이너의 파일 읽기 및 쓰기 작업의 성능에 큰 영향을 미칩니다.

호스트 네트워크 모드와 브리지 네트워크 모드에서 스토리지 드라이버의 동작이 약간 다를 수 있으며, 이로 인해 디스크 I/O 성능에도 차이가 발생할 수 있습니다. 예를 들어, 호스트 모드에서 네트워크 관련 오버헤드가 줄어들면서 디스크 I/O 작업에 더 많은 자원이 할당될 수 있습니다.

2. 네트워크 스택의 간섭

브리지 네트워크 모드는 네트워크 패킷을 브리지 네트워크를 통해 라우팅해야 하므로 추가적인 네트워크 오버헤드가 발생합니다. 이 오버헤드는 네트워크 트래픽이 많은 경우 디스크 I/O 작업에도 영향을 미칠 수 있습니다. 예를 들어, 디스크 I/O 작업 중 네트워크 호출이 발생하면 브리지 네트워크 모드의 추가적인 오버헤드로 인해 디스크 I/O 성능이 저하될 수 있습니다.

3. CPU 및 메모리 자원 경쟁

네트워크 모드에 따라 CPU 및 메모리 자원의 사용 패턴이 달라질 수 있습니다. 호스트 네트워크 모드는 네트워크 오버헤드가 적어 CPU와 메모리를 더 효율적으로 사용할 수 있습니다. 반면, 브리지 네트워크 모드는 네트워크 트래픽을 처리하는 데 더 많은 자원을 사용하게 되어 디스크 I/O 작업에 사용될 수 있는 자원이 줄어들 수 있습니다.

4. 컨테이너 내 I/O 경로

호스트 모드에서는 컨테이너가 호스트의 네트워크 인터페이스를 직접 사용하여 네트워크 경로가 단축되므로, I/O 경로의 복잡성이 줄어듭니다. 이로 인해 네트워크 트래픽과 디스크 I/O 작업이 동시에 발생할 때, 호스트 모드에서 더 효율적인 자원 관리를 통해 디스크 I/O 성능이 향상될 수 있습니다.

5. 실제 네트워크 트래픽

실제 네트워크 트래픽이 디스크 I/O 성능에 간접적으로 영향을 미칠 수 있습니다. 브리지 모드에서는 네트워크 트래픽이 더 많은 오버헤드를 유발하여 디스크 I/O 성능을 저하시킬 수 있습니다. 반면, 호스트 모드에서는 네트워크 트래픽 처리가 더 효율적이므로 디스크 I/O 성능에 덜 영향을 미칠 수 있습니다.

결론

네트워크 모드가 디스크 I/O 성능에 미치는 영향은 여러 요인에 의해 복합적으로 발생할 수 있습니다. 네트워크 스택의 간섭, 스토리지 드라이버의 동작 차이, 자원 경쟁 등의 이유로 인해 디스크 I/O 성능이 달라질 수 있습니다. 따라서, 최적의 성능을 위해서는 애플리케이션의 특성과 사용 사례에 따라 적절한 네트워크 모드를 선택하는 것이 중요합니다.

profile
안녕하세요!

0개의 댓글