Docker for mac의 bind mount 속도 개선 이야기

똑같이 도커 컨테이너에서 돌리는데…

AWS Codebuild 리눅스 호스트에서는 5분만에 전체 테스트가 돌아가는데 제 맥북에서는 25분이 걸립니다. 아래는 AWS 코드 빌드 general1.small(3 GB RAM, 2 vCPU ) 에서의 실행 결과입니다.

Finished in 5 minutes 17 seconds (files took 6.32 seconds to load)
1965 examples, 0 failures

원인

도커 컨테이너가 MacOS 호스트에 있는 파일을 읽고 쓸 때는 시스템 콜 변환 과정을 거칩니다. 시스템 콜이 즉각적이라는 가정 하에 작성된 소프트웨어들은 이 사실을 모르기 때문에 수 만건 반복 요청을 보내도 합니다. 그래서 Docker-for-mac의 bind mount엔 작업에 따라 속도 저하 문제가 생길 수 있습니다.

잠깐! bind mount와 volume

둘 다 도커 호스트 머신에 파일을 저장하는 방법입니다. Manage data in Docker에 따르면 다음과 같은 차이가 있습니다.

volumes

bind mounts

해결

정말 답답해서 눈물이 나와 nfs 등 여러 가지를 시도해보았고 docker-sync에 정착했습니다. 개인 맥북(2.7 GHz dual-core Intel Core i5, 8GB RAM, Docker에 2cpu, 4GB RAM 할당) 에선 전체 테스트 실행에 10분이 걸립니다. 개발할 때는 테스트 파일을 하나씩만 돌리니 유사 TDD를 할 만했습니다.

Step1. Bind mount를 external로 선언

docker-compose.override.yml.gitignore에 추가하면 docker-sync를 사용하지 않는 개발자들과의 호환성도 유지할 수 있습니다.

# docker-compose.override.yml
version: '3.4'
services:
  app: &app
    volumes:
      - app-code/:/var/www/app

volumes:
  app-code:
    external: true

Step2. Install docker-sync

docker-sync 를 다운로드합니다.

$ gem install docker-sync
$ docker-sync start

Step3. docker-sync.yml 설정 파일 작성

version: "2"
options:
  verbose: true
syncs:
  app-code:
    src: './'
    sync_strategy: 'native_osx'
    sync_excludes: ['.git', '*.log', 'public/uploads']

Step4. 기존 컨테이너 삭제 후 재생성

docker-compose down;docker-compose up -d

문제 해결

1. “xxx file not found” 에러가 발생합니다.

기존 볼륨을 지운 뒤 다시 Step 4번을 진행해주세요.

docker volume rm app-code

2. 도커 데몬을 껐다 켰더니 동기화가 안 됩니다.

docker-sync가 별도의 컨테이너를 띄워서 bind mount와 내부 volume을 동기화해주는 방식으로 동작합니다. Docker-for-mac을 재시작한 경우 docker-sync start를 하지 않으면 파일이 업데이트되지 않습니다. 업데이트 내역은 docker-sync logs 로 확인할 수 있습니다. 자세한 원리는 docker-sync 공식 문서를 참고하세요.

비교

레일즈 프로젝트에서 rspec을 실행한 결과입니다. 둘 다 Spring preloader를 사용했습니다.

  1. Default volume
    $ bin/rspec spec/api/v1/task_boards/index_spec.rb
    Finished in 14.64 seconds (files took 6.91 seconds to load)
    
  2. docker-sync
    $ bin/rspec spec/api/v1/task_boards/index_spec.rb
    Finished in 6.38 seconds (files took 0.7989 seconds to load)
    

Q & A

cached 또는 delegated 설정은 어떨까요?

delegated를 적용하면 container의 파일 변화를 host에 전달하는 데 시간차가 생깁니다. 반대로 cached 를 적용하면 host의 파일 변화가 container에 즉시 전달되지 않을 수 있습니다. 우리는 호스트의 코드 변경 사항이 바로 컨테이너에 반영되기를 원하는 것이므로 어느 경우에도 해당되지 않습니다.