파일시스템

파일시스템 (Filesystem)

이 페이지는 Nomad가 할당 작업 디렉터리(alloc working directory)를 사용해 작업 태스크 템플릿, 스토리지 볼륨, 아티팩트(artifact), 디스패치 페이로드(dispatch payload), 로그를 저장하는 방법에 대한 개념 정보를 제공해요. 이미지와 chroot 격리, 그리고 Nomad가 어떤 격리 모드도 사용하지 않는 경우를 검토해요.

출처: 문서

본문

Nomad는 클라이언트의 각 할당에 대해 작업 디렉터리를 만들어요. 이 디렉터리는 Nomad data_dir의 ./alloc/«alloc_id» 에서 찾을 수 있어요. 할당 작업 디렉터리는 Nomad가 태스크 디렉터리와 태스크 간 공유 디렉터리를 만들고, 태스크 로그를 기록하며, 아티팩트나 템플릿을 다운로드하는 곳이에요.

task1과 task2라는 두 태스크가 있는 할당은 이 예시와 같은 할당 디렉터리를 가져요.

.
├── alloc
│   ├── data
│   ├── logs
│   │   ├── task1.stderr.0
│   │   ├── task1.stdout.0
│   │   ├── task2.stderr.0
│   │   └── task2.stdout.0
│   └── tmp
├── task1
│   ├── local
│   ├── private
│   ├── secrets
│   └── tmp
└── task2
    ├── local
    ├── private
    ├── secrets
    └── tmp
  • alloc/ : 이 디렉터리는 할당의 모든 태스크가 공유하며, 로그 시퍼(shipper) 같은 여러 태스크가 사용해야 하는 데이터를 저장하는 데 사용할 수 있어요. 이 디렉터리가 NOMAD_ALLOC_DIR로 태스크에 제공돼요. 이 alloc/ 디렉터리는 최상위 디렉터리인 "할당 작업 디렉터리"와는 다르다는 점을 참고해 주세요. 태스크 그룹의 모든 태스크는 alloc/ 디렉터리에 읽고 쓸 수 있어요. 하지만 전체 호스트 경로는 태스크 드라이버의 파일시스템 격리 모드에 따라 달라질 수 있으므로, 태스크는 항상 NOMAD_ALLOC_DIR 환경 변수를 사용해 이 경로를 찾아야 하며 none, chroot, image 모드의 특정 구현에 의존해서는 안 돼요. alloc/ 디렉터리 안에는 세 가지 표준 디렉터리가 있어요:
    • alloc/data/ : ephemeral_disk 블록이 공유 데이터에 사용하는 위치예요.
    • alloc/logs/ : 할당 내 모든 태스크의 로그 파일 위치예요. nomad alloc logs 명령어가 이 파일들을 터미널로 스트리밍해요.
    • alloc/tmp/ : 태스크 드라이버가 스크래치 공간으로 사용하는 임시 디렉터리예요.
  • «taskname» : 각 태스크는 태스크와 같은 이름의 태스크 작업 디렉터리가 있어요. 태스크 그룹의 태스크는 서로의 태스크 작업 디렉터리를 읽을 수 없어요. 태스크 드라이버의 파일시스템 격리 모드에 따라 태스크가 태스크 작업 디렉터리에 접근하지 못할 수도 있어요. task/ 디렉터리 안에는 세 가지 표준 디렉터리가 있어요:
    • «taskname»/local/ : NOMAD_TASK_DIR로 태스크에 제공되는 위치예요. 이것은 "태스크 작업 디렉터리"와는 다르다는 점을 참고해 주세요. 이 디렉터리는 태스크에 비공개예요.
    • «taskname»/private/ : Vault 토큰 같은 할당 관련 비공개 파일을 Nomad가 저장하는 데 사용하는 디렉터리로, image 격리를 사용할 때 태스크와 공유되지 않아요. 이 디렉터리의 파일 내용은 nomad alloc fs 명령어나 Nomad API로 읽을 수 없어요.

      경고 — image 격리로 태스크와 공유되지는 않지만, 이 경로는 chroot나 none 격리를 사용하는 태스크에는 여전히 접근 가능해요.

    • «taskname»/secrets/ : NOMAD_SECRETS_DIR로 태스크에 제공되는 위치예요. 이 디렉터리의 파일 내용은 nomad alloc fs 명령어로 읽을 수 없어요. 태스크 밖에서 보이면 안 되는 시크릿 데이터를 저장하는 데 사용할 수 있어요. 가능한 경우 인메모리 파일시스템으로 뒷받침되고 noexec로 마운트돼요.
    • «taskname»/tmp/ : 태스크 드라이버가 스크래치 공간으로 사용하는 임시 디렉터리예요.

할당 작업 디렉터리는 nomad alloc fs 명령어를 사용할 때 보이는 디렉터리예요. 위의 작업 디렉터리를 만든 할당에 대해 nomad alloc fs를 실행하면 다음이 보여요.

$ nomad alloc fs c0b2245f
Mode        Size     Modified Time         Name
drwxrwxrwx  4.0 KiB  2020-10-27T18:00:39Z  alloc/
drwxrwxrwx  4.0 KiB  2020-10-27T18:00:32Z  task1/
drwxrwxrwx  4.0 KiB  2020-10-27T18:00:39Z  task2/

$ nomad alloc fs c0b2245f alloc/
Mode        Size     Modified Time         Name
drwxrwxrwx  4.0 KiB  2020-10-27T18:00:32Z  data/
drwxrwxrwx  4.0 KiB  2020-10-27T18:00:39Z  logs/
drwxrwxrwx  4.0 KiB  2020-10-27T18:00:32Z  tmp/

$ nomad alloc fs c0b2245f task1/
Mode         Size     Modified Time         Name
drwxrwxrwx   4.0 KiB  2020-10-27T18:00:33Z  local/
drwxrwxrwx   60 B     2020-10-27T18:00:32Z  private/
drwxrwxrwx   60 B     2020-10-27T18:00:32Z  secrets/
dtrwxrwxrwx  4.0 KiB  2020-10-27T18:00:32Z  tmp/

태스크 드라이버와 파일시스템 격리 모드 (Task Drivers and Filesystem Isolation Modes)

태스크 드라이버에 따라 태스크의 작업 디렉터리는 실행 중인 태스크의 루트 디렉터리가 될 수도 있어요. 이것은 태스크 드라이버의 파일시스템 격리 기능에 의해 결정돼요.

image 격리 (image isolation)

docker나 qemu 같은 태스크 드라이버는 이미지 격리를 사용하는데, 태스크 드라이버가 태스크 파일시스템을 머신 이미지로 격리해요. 이러한 파일시스템은 태스크 드라이버의 외부 프로세스가 소유하며 Nomad 자체는 소유하지 않아요. 이러한 파일시스템은 일반적으로 할당 작업 디렉터리의 어디에도 없어요. 예를 들어 Docker 컨테이너는 기본적으로 오버레이 파일시스템이 /var/run/docker/containerd/«container_id» 에 풀려요.

Nomad는 이미지 격리를 사용하는 태스크에 NOMAD_ALLOC_DIR, NOMAD_TASK_DIR, NOMAD_SECRETS_DIR을 제공하며, 일반적으로 태스크 드라이버의 파일시스템에 바인드 마운트해요.

다음 최소 작업을 실행해 image 격리의 예시를 볼 수 있어요.

job "example" {
  datacenters = ["dc1"]

  task "task1" {
    driver = "docker"

    config {
      image = "redis:6.0"
    }
  }
}

호스트에서 할당 작업 디렉터리를 보면 최소 파일시스템 트리가 보여요.

.
├── alloc
│   ├── data
│   ├── logs
│   │   ├── task1.stderr.0
│   │   └── task1.stdout.0
│   └── tmp
└── task1
    ├── local
    ├── private
    ├── secrets
    └── tmp

nomad alloc fs 명령어는 같은 비어 있는 디렉터리 트리를 보여줘요.

$ nomad alloc fs b0686b27
Mode        Size     Modified Time         Name
drwxrwxrwx  4.0 KiB  2020-10-27T18:51:54Z  alloc/
drwxrwxrwx  4.0 KiB  2020-10-27T18:51:54Z  task1/

$ nomad alloc fs b0686b27 task1
Mode         Size     Modified Time         Name
drwxrwxrwx   4.0 KiB  2020-10-27T18:51:54Z  local/
drwxrwxrwx   60 B     2020-10-27T18:51:54Z  private/
drwxrwxrwx   60 B     2020-10-27T18:51:54Z  secrets/
dtrwxrwxrwx  4.0 KiB  2020-10-27T18:51:54Z  tmp/

$ nomad alloc fs b0686b27 task1/local
Mode  Size  Modified Time  Name

생성된 Docker 컨테이너를 검사하면 컨테이너에 바인드 마운트된 세 개의 디렉터리가 보여요.

$ docker inspect 32e | jq '.[0].HostConfig.Binds'
[
  "/var/nomad/alloc/b0686b27-8af3-8252-028f-af485c81a8b3/alloc:/alloc",
  "/var/nomad/alloc/b0686b27-8af3-8252-028f-af485c81a8b3/task1/local:/local",
  "/var/nomad/alloc/b0686b27-8af3-8252-028f-af485c81a8b3/task1/secrets:/secrets"
]

컨테이너 안의 루트 파일시스템은 이 세 개의 마운트를 컨테이너 파일시스템의 나머지와 함께 볼 수 있어요.

$ docker exec -it 32e /bin/sh
# ls /
alloc  boot  dev  home  lib64  media  opt   root  sbin     srv  tmp  var
bin    data  etc  lib   local  mnt    proc  run   secrets  sys  usr

세 개의 디렉터리가 컨테이너 파일시스템에 바인드 마운트되기 때문에, 할당 작업 디렉터리의 다른 곳에서 그 세 디렉터리 밖에 작성된 것은 컨테이너 안에서 접근할 수 없다는 점을 참고해 주세요. 즉, image 격리를 사용하는 태스크의 템플릿, 아티팩트, 디스패치 페이로드는 NOMAD_ALLOC_DIR, NOMAD_TASK_DIR, NOMAD_SECRETS_DIR에 기록되어야 해요.

이 제한을 해결하려면 태스크 드라이버의 마운트 기능을 사용해 세 디렉터리 중 하나를 태스크의 다른 위치에 마운트할 수 있어요. 예를 들어 Docker 드라이버에서는 드라이버의 mounts 블록을 사용해 template 블록이 NOMAD_SECRETS_DIR에 기록한 시크릿을 태스크의 다른 곳의 구성 디렉터리에 바인드할 수 있어요.

job "example" {
  datacenters = ["dc1"]

  task "task1" {
    driver = "docker"

    config {
      image = "redis:6.0"
      mounts = [{
        type     = "bind"
        source   = "secrets"
        target   = "/etc/redis.d"
        readonly = true
      }]

      template {
        destination = "${NOMAD_SECRETS_DIR}/redis.conf"
        data        = <<EOT
{{ with secret "secrets/data/redispass" }}
requirepass {{- .Data.data.passwd -}}{{end}}
EOT

      }
    }
  }
}

상대 마운트 소스 경로는 태스크 작업 디렉터리 기준이므로, NOMAD_ALLOC_DIR을 마운트 소스로 바인드하려면 할당 작업 디렉터리로 올라가는 상대 경로를 사용해야 해요(예: source = "../alloc").

chroot 격리 (chroot isolation)

(Linux의) exec나 java 같은 태스크 드라이버는 chroot 격리를 사용하는데, 태스크 드라이버가 chroot나 pivot_root로 태스크 파일시스템을 격리해요. 이러한 격리된 파일시스템은 태스크 작업 디렉터리 안에 구축돼요.

Linux에서 다음 최소 작업을 실행해 chroot 격리의 예시를 볼 수 있어요.

job "example" {
  datacenters = ["dc1"]

  task "task2" {
    driver = "exec"

    config {
      command = "/bin/sh"
      args = ["-c", "sleep 600"]
    }
  }
}

호스트에서 할당 작업 디렉터리를 보면 NOMAD_ALLOC_DIR, NOMAD_TASK_DIR, NOMAD_SECRETS_DIR에 더해 태스크 드라이버의 chroot 내용이 채워진 파일시스템 트리가 보여요.

.
├── alloc
│   ├── container
│   ├── data
│   ├── logs
│   └── tmp
└── task2
    ├── alloc
    ├── bin
    ├── dev
    ├── etc
    ├── executor.out
    ├── lib
    ├── lib32
    ├── lib64
    ├── local
    ├── private
    ├── proc
    ├── run
    ├── sbin
    ├── secrets
    ├── sys
    ├── tmp
    └── usr

마찬가지로 태스크의 루트 디렉터리가 이제 nomad alloc fs 명령어 출력에서 확인할 수 있어요.

$ nomad alloc fs eebd13a7
Mode        Size     Modified Time         Name
drwxrwxrwx  4.0 KiB  2020-10-27T19:05:24Z  alloc/
drwxrwxrwx  4.0 KiB  2020-10-27T19:05:24Z  task2/

$ nomad alloc fs eebd13a7 task2
Mode         Size     Modified Time         Name
drwxrwxrwx   4.0 KiB  2020-10-27T19:05:24Z  alloc/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:24Z  bin/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:24Z  dev/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:22Z  etc/
-rw-r--r--   297 B    2020-10-27T19:05:24Z  executor.out
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:22Z  lib/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:22Z  lib32/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:22Z  lib64/
drwxrwxrwx   4.0 KiB  2020-10-27T19:05:22Z  local/
drwxrwxrwx   60 B     2020-10-27T19:05:22Z  private/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:24Z  proc/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:22Z  run/
drwxr-xr-x   12 KiB   2020-10-27T19:05:22Z  sbin/
drwxrwxrwx   60 B     2020-10-27T19:05:22Z  secrets/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:24Z  sys/
dtrwxrwxrwx  4.0 KiB  2020-10-27T19:05:22Z  tmp/
drwxr-xr-x   4.0 KiB  2020-10-27T19:05:22Z  usr/

Nomad는 chroot 격리를 사용하는 태스크에 NOMAD_ALLOC_DIR, NOMAD_TASK_DIR, NOMAD_SECRETS_DIR을 제공해요. 하지만 image 격리와 달리 Nomad는 NOMAD_TASK_DIR 디렉터리를 바인드 마운트할 필요가 없는데, chroot 안에 직접 만들 수 있기 때문이에요.

$ nomad alloc exec eebd13a7 /bin/sh
$ mount
...
/dev/mapper/root on /alloc type ext4 (rw,relatime,errors=remount-ro,data=ordered)
tmpfs on /private type tmpfs (rw,noexec,relatime,size=1024k)
tmpfs on /secrets type tmpfs (rw,noexec,relatime,size=1024k,noswap)
...

none 격리 (none isolation)

raw_exec 태스크 드라이버(또는 Windows의 java 태스크 드라이버)는 none 파일시스템 격리 모드를 사용해요. 즉, 태스크 드라이버가 태스크의 파일시스템을 격리하지 않고, 태스크는 Nomad를 실행하는 사용자가 할 수 있는 곳이면 어디든 읽고 쓸 수 있어요.

Linux 또는 Unix에서 다음 최소 raw_exec 작업을 실행해 none 격리의 예시를 볼 수 있어요.

job "example" {
  datacenters = ["dc1"]

  task "task3" {
    driver = "raw_exec"

    config {
      command = "/bin/sh"
      args = ["-c", "sleep 600"]
    }
  }
}

호스트에서 할당 작업 디렉터리를 보면 최소 파일시스템 트리가 보여요.

.
├── alloc
│   ├── data
│   ├── logs
│   │   ├── task3.stderr.0
│   │   └── task3.stdout.0
│   └── tmp
└── task3
    ├── executor.out
    ├── local
    ├── private
    ├── secrets
    └── tmp

nomad alloc fs 명령어는 같은 비어 있는 디렉터리 트리를 보여줘요.

$ nomad alloc fs 87ec7d12 task3
Mode         Size     Modified Time         Name
-rw-r--r--   140 B    2020-10-27T19:15:33Z  executor.out
drwxrwxrwx   4.0 KiB  2020-10-27T19:15:33Z  local/
drwxrwxrwx   60 B     2020-10-27T19:15:33Z  private/
drwxrwxrwx   60 B     2020-10-27T19:15:33Z  secrets/
dtrwxrwxrwx  4.0 KiB  2020-10-27T19:15:33Z  tmp/

하지만 nomad alloc exec를 사용해 컨테이너 안에서 파일시스템을 보면 태스크가 전체 루트 파일시스템에 접근할 수 있다는 것을 볼 수 있어요. NOMAD_ALLOC_DIR, NOMAD_TASK_DIR, NOMAD_SECRETS_DIR은 태스크 작업 디렉터리에 고정된 경로가 아니라 호스트의 파일 경로를 가리켜요. 그리고 태스크는 Nomad 클라이언트 에이전트가 root로 실행되기 때문에 root로 실행돼요. 이것이 raw_exec 드라이버가 기본적으로 비활성화되어 있는 이유예요.

$ nomad alloc exec 87ec7d12 /bin/sh
# ls /
bin   dev  home        lib    lib64   lost+found  mnt  proc  run   snap  sys  usr  vmlinuz
boot  etc  initrd.img  lib32  libx32  media       opt  root  sbin  srv   tmp  var

# echo $NOMAD_SECRETS_DIR
/var/nomad/alloc/87ec7d12-5e35-8fba-96cc-09e5376be15a/task3/secrets

# whoami
root

템플릿, 아티팩트, 디스패치 페이로드 (Templates, Artifacts, and Dispatch Payloads)

할당 작업 디렉터리의 다른 내용은 작업 스펙이 사용하는 기능에 따라 달라져요. 할당 작업 디렉터리는 다른 기능들이 특정 순서로 채워요.

  • 할당 작업 디렉터리가 생성돼요.
  • 임시 디스크 데이터가 이전 할당에서 마이그레이션돼요.
  • CSI 볼륨이 스테이징돼요.
  • 그런 다음 각 태스크에 대해: 태스크 작업 디렉터리가 생성돼요. 디스패치 페이로드가 기록돼요. 아티팩트가 다운로드돼요. 템플릿이 렌더링돼요. 태스크가 태스크 드라이버로 시작되며, 여기에는 모든 바인드 마운트와 볼륨 마운트가 포함돼요.

디스패치 페이로드, 아티팩트, 템플릿은 태스크가 시작되기 전에 태스크 작업 디렉터리에 기록돼요. 왜냐하면 결과 파일이 태스크가 실행하는 바이너리나 이미지일 수 있기 때문이에요. 예를 들어 아티팩트를 사용해 Docker 이미지나 .jar 파일을 다운로드하거나, 템플릿을 사용해 exec가 실행하는 셸 스크립트를 렌더링할 수 있어요.

artifact와 template 블록은 NOMAD_TASK_DIR이 아닌 태스크 작업 디렉터리 기준으로 destination에 데이터를 기록해요. 이미지 파일시스템 격리를 가진 태스크 드라이버의 경우, 이것은 destination 필드 경로에 NOMAD_TASK_DIR 또는 NOMAD_SECRETS_DIR을 접두사로 붙여야 함을 의미해요. 그렇지 않으면 결과 컨테이너 안에서 파일이 보이지 않아요. (dispatch_payload 블록은 항상 데이터를 NOMAD_TASK_DIR에 기록해요.)

CSI 볼륨의 경우 클라이언트는 태스크 작업 디렉터리를 설정하기 전에 볼륨을 스테이징해요. 스테이징은 일반적으로 볼륨을 CSI 플러그인의 태스크 디렉터리에 마운트하고, 플러그인에 명령을 보내 요구된 대로 볼륨을 포맷하며, Nomad 서버에 볼륨 클레임을 하는 것을 포함해요.

volume_mount 블록의 동작은 태스크 드라이버가 제어해요. 클라이언트는 호스트 볼륨 또는 CSI 볼륨을 설명하는 마운트 구성을 만들고 이를 태스크 드라이버에 전달해 실행해요. 태스크 드라이버가 볼륨을 마운트하므로, artifact, template, dispatch_payload 블록이 볼륨에 기록하는 것은 불가능해요.

더 알아보기 (Learn more)