FreeBSD jail과 Docker 컨테이너 차이점 비교
FreeBSD jail은 완전한 사용자 공간을 격리하고 Docker는 계층형 이미지를 통해 단일 프로세스를 실행합니다. 두 기술의 소프트웨어 관리, 상태 유지, 네트워크 구성 및 리소스 제한 방식의 근본적인 차이를 상세히 분석합니다.
FreeBSD jail과 Docker 컨테이너의 비교
FreeBSD jail과 Docker 컨테이너는 동일한 문제를 서로 다른 방식으로 해결합니다. 두 기술 모두 하나의 커널을 공유하며 격리된 사용자 공간(userland)을 실행하므로, 가상 머신(VM)과는 근본적으로 다릅니다. 차이점은 내부에 무엇을 담느냐에 있습니다. Docker 컨테이너는 레지스트리에서 가져온 계층형 이미지로부터 단일 프로세스를 실행합니다. 반면 jail은 완전한 FreeBSD 사용자 공간을 실행하며, 고유한 /etc, rc 시작 스크립트, pkg 데이터베이스, 그리고 원하는 만큼의 프로세스를 구동할 수 있습니다. 이 페이지에서 다루는 거의 모든 차이점은 바로 이 구조적 차이에서 비롯됩니다.
SSD Nodes는 FreeBSD 이미지를 제공하지 않습니다. 이 플랫폼에서는 FreeBSD 서버를 대여할 수 없으며, 아래 내용은 이 플랫폼에서 구매 가능한 장비를 위한 설치 가이드가 아닙니다. 이 글은 두 격리 모델을 비교하여 워크로드에 적합한 기술이 무엇인지 판단하고, FreeBSD 팀의 설정을 이해할 수 있도록 돕기 위해 작성되었습니다.
Jail의 실제 의미
Jail은 2000년 3월 FreeBSD 4.0에서 처음 도입되었습니다. 이는 cgroups보다 오래되었으며 Docker보다는 약 10년 앞선 기술입니다. 이 메커니즘은 하나의 커널 호출로 이루어집니다. jail(8)은 디렉터리 트리를 가져와 그 안에서 Jail ID가 부여된 프로세스를 시작하며, 커널은 해당 ID를 가진 프로세스에 대해 고정된 작업 집합을 거부합니다. Jail 내부의 프로세스는 Jail 외부의 프로세스를 볼 수 없으며, 파일 시스템을 마운트하거나 언마운트할 수 없고, 커널 모듈을 로드할 수 없으며, Jail에 할당되지 않은 네트워크 주소에 바인딩할 수 없습니다. 별도로 학습해야 할 네임스페이스 유형이나 기능별 선택 사항은 없습니다. 제약 조건은 Jail 설정의 매개변수에 의해 조정되는 하나의 단위로 적용됩니다.
호스트에서 jls는 실행 중인 Jail 목록을 보여주며, jexec web sh는 web이라는 이름의 Jail 내부 셸로 진입하게 합니다.
Jail을 구축하려면 FreeBSD 사용자 공간을 디렉터리에 배치해야 합니다. 베이스 시스템은 이를 자동으로 수행합니다.
sudo bsdinstall jail /usr/local/jails/containers/web이 명령은 해당 릴리스의 베이스 배포 세트를 가져와 일반적인 설치 후 단계를 실행합니다. 따라서 새 서버에서와 마찬가지로 root 암호를 설정하고 시간대를 선택하게 됩니다. 그 결과 폴더 안에 FreeBSD 설치가 완료됩니다. 그런 다음 /etc/jail.conf에 이를 기술합니다.
web {
host.hostname = "web.example.internal";
path = "/usr/local/jails/containers/web";
ip4.addr = "10.0.0.10";
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
mount.devfs;
}Jail을 시작한 뒤 확인합니다.
sudo service jail start web
jls이제 jls은 JID, 호스트 이름, IP 주소와 함께 web를 나열해야 합니다. Jail이 나타나지 않으면 sudo jail -c web을 직접 실행하십시오. 이 명령은 동일한 설정을 포그라운드에서 적용하며, 서비스 출력에 실패 기록을 남기는 대신 수락할 수 없는 매개변수를 출력합니다.
두 번 읽어볼 가치가 있는 줄은 exec.start = "/bin/sh /etc/rc"입니다. Jail을 시작하면 그 안에서 FreeBSD의 일반 부팅 스크립트가 실행되므로, Jail은 자체 /etc/rc.conf에서 활성화된 모든 서비스를 시작합니다. Docker 컨테이너는 이미지의 엔트리포인트 프로세스를 실행하고 해당 프로세스가 종료되면 함께 멈추기 때문에 이와 동일한 단계가 없습니다.
소프트웨어 도입 방식: 이미지와 레지스트리 대 직접 구성하는 사용자 공간
이는 첫날부터 체감하게 되는 차이점입니다.
Docker를 사용하면 소프트웨어 이름을 지정하여 가져옵니다. docker pull nginx은 타인이 빌드하고 테스트한 계층형 콘텐츠 주소 지정 이미지를 가져오며, docker compose up -d는 볼륨과 네트워크를 연결하여 해당 이미지를 실행합니다. 레지스트리가 곧 제품인 셈입니다. Docker 워크플로우의 가치는 수천 개의 프로젝트가 작동하는 이미지를 배포한다는 점에 있으며, 덕분에 VPS에서 Docker 실행하기는 거창한 프로젝트가 아닌 짧은 작업이 됩니다.
FreeBSD는 기본적으로 제공되는 jail 이미지 공개 레지스트리가 없습니다. 빈 사용자 공간을 생성하고 그 안에 직접 설치해야 하며, 이는 베어 메탈 서버를 설정하는 방식과 동일합니다. 타이핑할 양은 더 많지만, jail 내부에서 실행되는 것은 호스트와 동일한 패키지 세트에서 pkg가 직접 설치한 것이므로 투명성이 더 높습니다.
도구를 사용하면 이 과정을 단축할 수 있습니다. BastilleBSD는 일반적인 jail 관리자이며 패키지로 제공됩니다.
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup은 네트워킹, 스토리지, 방화벽을 자동으로 구성합니다. bastille bootstrap은 릴리스를 한 번 다운로드하면 이후 생성하는 모든 jail에서 이를 재사용합니다. FreeBSD 15.1은 2026년 6월에 출시된 현재 운영 릴리스이며, 사용 중인 릴리스에 맞춰 변경하십시오.
이제 jail 생성은 한 번의 명령어로 가능하며, 채우는 과정도 한 번의 명령어로 끝납니다.
sudo bastille create web 15.1-RELEASE 10.17.89.10/24
sudo bastille pkg web install nginx
sudo bastille service web nginx start
sudo bastille console webbastille console web은 jail 내부의 로그인 셸을 제공하며, bastille list는 호스트에 존재하는 항목을 보여줍니다. 빌드를 반복하려면 Bastille 템플릿을 사용하여 단계를 파일에 저장하고 jail에 적용합니다. 이는 이 환경에서 Dockerfile과 가장 유사한 개념입니다. 템플릿은 각 jail에서 재실행됩니다. 미리 빌드된 상태로 도착하는 것은 없습니다.
결론은 간단합니다. Docker는 타인이 빌드한 결과물을 제공하고, Jails는 직접 설치한 결과물을 제공합니다. 만약 후보 소프트웨어가 컨테이너 이미지로만 배포된다면, 다른 고려 사항을 따질 필요 없이 Docker를 선택하게 될 것입니다.
상태와 업그레이드: ZFS가 변화를 만드는 부분
Docker는 의도적으로 상태를 분리합니다. 컨테이너 파일 시스템은 일회용이며, 데이터는 명명된 볼륨(named volume)이나 바인드 마운트(bind mount)에 저장됩니다. 업그레이드는 docker compose pull을 수행한 뒤 docker compose up -d을 실행하는 과정입니다. 컨테이너는 교체되며, 볼륨에 저장하지 않은 모든 데이터는 사라집니다. 이 규칙을 따르면 기능이 되지만, 잊어버리면 데이터 손실 사고가 발생합니다. 이것이 바로 바인드 마운트와 명명된 볼륨 사이의 선택이 Compose 스택에서 매우 중요한 이유입니다.
Jail은 상태를 분리하지 않으며, ZFS가 그 방식을 가능하게 합니다. Jail 전체가 하나의 데이터셋입니다.
sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgrade이 명령을 실행하기 전에 zfs list로 실제 데이터셋 이름을 확인하십시오. 위 경로는 핸드북에서 사용하는 레이아웃입니다. 스냅샷은 약 1초가 소요되며 Jail의 내용이 변경되기 전까지는 공간을 거의 차지하지 않습니다. 업그레이드로 서비스가 중단되면 롤백을 통해 패키지 데이터베이스와 새벽 2시에 직접 편집한 설정 파일을 포함한 전체 사용자 공간(userland)을 이전 상태로 되돌릴 수 있습니다. Docker에는 내장된 대응 기능이 없는데, 이는 Docker 모델이 애초에 그런 기능을 원하지 않는다고 가정하기 때문입니다.
zfs clone은 나머지 절반입니다. 스냅샷의 클론은 부모와 변경되지 않은 블록을 공유하는 새로운 쓰기 가능한 Jail입니다. 따라서 3 GB Jail의 스테이징 복사본은 변경을 시작하기 전까지 디스크 공간을 거의 차지하지 않습니다. 이것이 FreeBSD 관리자가 업그레이드를 연습하기 위해 "운영 환경과 동일한" Jail을 구축하는 방식입니다.
기본 시스템 업그레이드는 패키지와 별개입니다. 자체 사용자 공간 복사본을 유지하는 Jail의 경우 다음과 같습니다.
sudo freebsd-update -b /usr/local/jails/containers/web fetch installThin Jail은 이러한 반복 작업을 피합니다. Thin Jail은 nullfs를 통해 공유된 읽기 전용 베이스를 하나 마운트하고 각 Jail에 작은 쓰기 가능 레이어를 제공합니다. 따라서 베이스를 한 번만 패치하면 모든 Jail이 그 결과를 확인하게 됩니다. Bastille은 기본적으로 Thin Jail을 생성합니다.
네트워킹: 포트 게시와 주소 지정 결정
Docker는 사용자를 대신하여 네트워킹을 결정하고 예외적인 경우에만 포트를 게시하도록 요구합니다. 컨테이너는 브리지에 배치되며, 사용자 정의 네트워크상에서 서비스 이름을 통해 서로 통신합니다. 이때 -p 8080:80는 컨테이너 중 하나를 호스트에 노출합니다. Docker는 이를 위해 자체 패킷 필터 규칙을 작성하는데, 이것이 바로 게시된 컨테이너 포트가 ufw를 우회하는 방식입니다.
Jail은 시작 단계에서 모델을 선택해야 하며, 두 가지 방식이 있습니다.
Shared IP. ip4.addr = "10.0.0.10"는 기존 호스트 인터페이스에 해당 주소를 추가하고 Jail을 그 주소로 제한합니다. Jail은 자체 네트워크 스택을 가지지 않으므로 독자적인 방화벽을 실행할 수 없습니다. 또한 모든 주소에 바인딩할 수도 없습니다. Jail 내부의 소켓이 0.0.0.0을 요청하면 커널이 이를 Jail의 주소로 재작성합니다. 두 개의 Jail이 동일한 주소의 80번 포트를 동시에 리스닝할 수 없으므로, 각 Jail에 주소를 할당하거나 앞에 리버스 프록시를 배치해야 합니다.
VNET. Jail에 vnet;을 추가하면 Jail은 자체 인터페이스, 라우팅 테이블, 방화벽 규칙을 포함한 완전한 네트워크 스택을 갖게 됩니다. 양쪽 끝에 하나씩 연결된 가상 케이블인 epair을 사용하여 호스트와 연결하고, 호스트 측을 브리지에 배치합니다. 이는 Docker가 제공하는 방식과 가장 유사하며, Bastille의 -V 및 -B Jail 유형이 사용하는 방식입니다.
호스트 포트를 Jail로 포워딩하는 것은 pf 리다이렉트 규칙입니다. Bastille은 이를 다음과 같이 래핑합니다.
sudo bastille rdr web tcp 80 80EXPOSE는 없으며 자동 게시 기능도 없습니다. Jail의 주소나 리다이렉트 규칙이 허용하지 않는 한 그 어떤 트래픽도 Jail에 도달할 수 없습니다. 이는 초기 설정은 더 느리지만 훨씬 더 조용한 방화벽 환경을 제공합니다.
리소스 제한: cgroups와 rctl 비교
Docker는 cgroups를 사용하여 컨테이너를 제한하며, 이 제한 설정은 컨테이너가 정의된 곳에 위치합니다. 명령줄의 --memory=1g --cpus=1.5 또는 Compose 파일의 대응하는 키가 이에 해당합니다. 만약 스택을 VPS의 Docker Compose 파일로 관리하고 있다면, 제한 설정은 적용 대상 서비스 옆에 위치하며 git을 통해 함께 이동합니다.
FreeBSD는 rctl를 사용하며, 이는 사용자가 직접 활성화해야 하는 하위 시스템입니다. 리소스 계정 관리는 모든 할당 시 약간의 비용이 발생하므로 기본적으로 꺼져 있습니다. /boot/loader.conf에 튜너블(tunable)을 추가하고 재부팅하십시오:
kern.racct.enable=1그런 다음 규칙을 설정하고 모니터링하십시오:
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webrctl -hu jail:web은 jail의 현재 사용량을 사람이 읽기 쉬운 단위로 출력하므로, 문제가 발생하기 전에 제한치에 얼마나 근접했는지 확인할 수 있습니다. deny 동작은 제한을 초과하는 할당이 jail 내부에서 실패하도록 만듭니다. 따라서 호스트에서 kill 메시지를 보는 대신 애플리케이션 자체의 할당 오류를 확인할 수 있습니다.
rctl -a로 추가한 규칙은 다음 재부팅 시 사라집니다. FreeBSD의 rctl 서비스는 /etc/rctl.conf에서 규칙을 다시 불러오므로, 해당 파일에 규칙을 작성하고 서비스를 활성화하십시오:
sudo sysrc rctl_enable=YES이 지점이 Docker가 확실히 더 편리한 부분입니다. Compose 파일의 제한 설정은 해당 설정이 제약하는 서비스와 함께 검토됩니다. 반면 rctl 규칙은 다른 곳에 정의된 jail을 지칭하는 별도 파일의 한 줄일 뿐입니다.
가상 머신이 답일 때: bhyve
Jail은 호스트 커널을 공유하므로 일부 기능은 영구적으로 사용할 수 없습니다. Jail은 다른 커널 버전을 실행하거나, 커널 모듈을 로드하거나, Linux 컨테이너처럼 Linux 바이너리를 실행할 수 없습니다. FreeBSD에는 linuxulator라는 Linux 호환 계층이 있지만, 이는 Linux 시스템 호출의 일부만 구현한 것이며 임의의 Linux 이미지를 실행하기 위한 일반적인 해결책은 아닙니다.
bhyve는 FreeBSD의 하이퍼바이저이며, 다른 운영 체제, 다른 커널, 또는 커널을 공유하고 싶지 않은 테넌트와 같이 확실한 머신 경계가 필요할 때 사용하는 적절한 도구입니다. 이 경우 메모리를 공유하는 대신 예약해야 하며, 패치해야 할 커널이 하나 더 늘어나는 비용이 발생합니다. 이는 Linux에서 컨테이너와 전체 가상 머신 사이에서 내리는 결정과 동일하며, 중첩 가상화를 지원하는 VPS가 필요한지 여부를 결정하는 기준이 됩니다.
생태계, 대부분의 팀이 Docker를 사용하는 실질적인 이유
위의 모든 내용은 모델에 관한 것입니다. 대부분의 팀이 선택을 내리는 기준은 각 기술을 둘러싼 생태계의 규모입니다.
Docker는 Docker Hub와 GHCR, docker compose, 단일 서버로 부족할 때 사용하는 Kubernetes, 이미 컨테이너 지원이 내장된 CI 러너, 그리고 거의 모든 프로젝트의 README에 포함된 한 줄짜리 명령어로 시작하는 퀵스타트를 제공합니다. Jails는 규모가 크고 세심하게 관리되는 FreeBSD ports 트리를 제공하지만, 즉시 실행 가능한 애플리케이션 번들 세트는 훨씬 작습니다. 프로젝트가 컨테이너 이미지만 배포할 때, FreeBSD 방식은 문서를 읽고 직접 구성 요소를 조립하는 것입니다.
Jails는 이러한 트레이드오프의 반대편에서 그 가치를 증명합니다. 이미 ZFS를 운영 중이며 전체 서비스의 스냅샷과 롤백을 중요하게 생각할 때, 서비스가 FreeBSD 네이티브일 때, 단일 프로세스가 아닌 테넌트별로 완전한 사용자 공간(userland)을 원할 때, 또는 커널, 패킷 필터, 파일 시스템 및 문서가 하나의 시스템으로 통합 관리되기를 원할 때 Jails를 선택합니다. 마지막 항목은 사람들이 FreeBSD를 일관성 있다고 부르는 이유이며, 이에 대한 자세한 내용은 서버 플랫폼으로서의 Linux와 FreeBSD 비교와 FreeBSD 15에서 서버 사용을 위해 변경된 점에서 더 다룹니다.
마지막 결론입니다. 팀이 이미 Docker에 익숙하다면, 전환 비용은 실재하며 그에 따른 보상은 구체적이어야 합니다. 격리 품질 때문에 전환하지 마십시오. 두 모델은 충분히 유사하므로 구성 방식이 더 중요합니다. ZFS 기반의 전체 서비스 롤백을 원하거나, 이미 FreeBSD를 사용 중인 경우에만 전환하십시오.
FAQ
FreeBSD에서 Docker 이미지를 실행할 수 있습니까?
Linux 이미지는 실행할 수 없으며, 공식적으로 지원되는 방식도 아닙니다. FreeBSD는 OCI 컨테이너를 지원합니다. sudo pkg install -y podman-suite을 설치하면 ocijail를 통해 컨테이너를 실행할 수 있는데, 이 런타임은 내부적으로 실제 jail을 생성합니다. 컨테이너 모니터를 위해 fdescfs를 /dev/fd에 마운트해야 하며, 컨테이너 NAT(네트워크 주소 변환)를 위해 pf이 필요합니다. FreeBSD 네이티브 OCI 이미지가 가장 잘 작동합니다. Linux 이미지는 추가로 Linux 호환성 계층이 필요하며, 2026년 8월 기준으로 FreeBSD의 Podman 포트는 여전히 실험적인 단계로 분류됩니다. 배포하려는 서비스가 Linux 이미지 스택이라면 Linux에서 실행하십시오.
FreeBSD jail이 Docker 컨테이너보다 더 안전합니까?
둘 다 하나의 호스트 커널을 공유하므로 커널 버그는 양쪽 모두에게 위험 요소이며, 신뢰할 수 없는 코드를 격리하기 위한 경계로 적합하지 않습니다. 차이점은 시작 방식에 있습니다. jail은 기본적으로 대부분의 작업을 거부한 상태에서 시작하며, 필요한 매개변수를 하나씩 허용하는 방식입니다. 반면 Docker 컨테이너는 root 권한으로 네임스페이스 내에서 시작하며 일부 기능을 제한하는 방식이므로, 추가적인 보안 강화는 사용자가 직접 설정해야 합니다. 실제로는 모델보다 설정이 보안을 결정합니다. allow.mount 및 allow.raw_sockets가 활성화된 jail이 세심하게 설정된 컨테이너보다 더 안전하다고 볼 수 없습니다.
jail을 백업하려면 어떻게 해야 합니까?
데이터셋을 스냅샷으로 생성한 뒤 전송하십시오. sudo zfs snapshot zroot/jails/containers/web@backup을 실행한 다음, 해당 스냅샷을 zfs send을 사용하여 다른 풀로 보내거나 외부로 복사할 파일로 만듭니다. jail은 전체 사용자 공간을 하나의 데이터셋에 보관하므로, 스냅샷을 통해 설치된 패키지와 데이터를 일관된 시점에 백업할 수 있으며 직접 수정한 모든 설정 파일도 함께 저장됩니다. 이는 명명된 볼륨과 Compose 파일을 백업하고 나머지는 이미지에서 다시 빌드하는 Docker의 방식과는 반대입니다.
BastilleBSD가 꼭 필요합니까, 아니면 기본 시스템만으로 충분합니까?
기본 시스템만으로도 충분하며, 처음 시작할 때는 기본 시스템을 사용하는 것이 더 좋습니다. jail.conf, jls, jexec 및 service jail start 명령이 전체 모델을 다루므로, 이 명령들을 익히면 특정 호스트의 도구를 따로 배우지 않아도 어떤 FreeBSD 호스트든 관리할 수 있습니다. Bastille은 그 위에 구축된 편의 계층입니다. 릴리스를 부트스트랩하고, thin jail을 생성하며, 템플릿을 적용하고, pf 리다이렉트 규칙을 자동으로 작성해 줍니다. 먼저 기본 명령어를 익히고, jail의 개수가 늘어나 타이핑이 번거로워질 때 Bastille을 도입하십시오.