Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

image pull errors #60

Closed
mythi opened this issue Nov 9, 2022 · 42 comments · Fixed by #77
Closed

image pull errors #60

mythi opened this issue Nov 9, 2022 · 42 comments · Fixed by #77
Assignees
Labels
bug Something isn't working

Comments

@mythi
Copy link
Contributor

mythi commented Nov 9, 2022

CoCo quickstart documentation uses bitnami/nginx:1.22.0 image as an example and I gave it a try. I'm seeing different image pull errors:

Failed to pull image "docker.io/bitnami/nginx:1.22.0": rpc error: code = Internal desc = failed to mount "unionfs" to "/run/enclave-cc/containers/nginx_1.22.0/rootfs", with error: EIO: I/O error

to debug this in more details, I ran enclave-agent (sudo runc run 123) from the bundle with OCCLUM_LOG_LEVEL=debug and used the "async-client" to debug. This time I'm getting different errors:

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_3a52f76b4a6462386fe51fabf6cc829dbdef540dc64cdcc809f907bfb6c68195\" already exists")) ended: 5.798667018s

The latter blocks me from investigating the former error in details.

@haosanzi
Copy link
Contributor

haosanzi commented Nov 9, 2022

I have met this problem too.
I think it's still a problem that the agent enclave has insufficient support for pulling multi-layer images from occlum Libos.

I have also try the docker.io/redis:latest image. The error is:

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "failed to mount \"unionfs\" to \"/run/enclave-cc/containers/redis_latest/rootfs\", with error: EIO: I/O error")) ended: 16.988494372s

When I try to pull redis again, the error is

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_f2cd78d6f24cd252df46286ce2bc350dd58893be20a39202b22e7e60b5c763d2\" already exists")) ended: 4.419962025s

In addition, pull docker.io/huaijin20191223/scratch-base:v1.8 works.

cargo build --example async-client
Client connect to tcp://127.0.0.1:7788
Green Thread 1 - image.pull_image() started: 64.422µs
Green Thread 1 - pull_image -> Ok(image_ref: "docker.io/huaijin20191223/scratch-base:v1.8") ended: 4.964946274s
pull_image - docker.io/huaijin20191223/scratch-base:v1.8

@qzheng527
Copy link

@haosanzi Can you help capture Occlum log when pulling redis? I can triage from Occlum side.

@mythi
Copy link
Contributor Author

mythi commented Nov 10, 2022

@qzheng527 my logs (last 100 lines):

[2022-11-10T05:57:23.702Z][DEBUG][T3][#2692704][··Munmap] munmap: addr: 0x7f40e2000000, size: 0x2ac9000
[2022-11-10T05:57:23.708Z][DEBUG][T3][#2692705][····Stat] fstatat: fs_path: FsPath { Absolute("/run/enclave-cc/containers/nginx_1.22.0/rootfs") }, flags: (empty)
[2022-11-10T05:57:23.708Z][DEBUG][T3][#2692705][····Stat] lookup_inode: cwd: "/", path: "/run/enclave-cc/containers/nginx_1.22.0/rootfs"
[2022-11-10T05:57:23.708Z][DEBUG][T3][#2692706][···Mount] mount: source: unionfs, target: /run/enclave-cc/containers/nginx_1.22.0/rootfs, flags: (empty), options: UnionFS(UnionFSMountOptions { lower_dir: "/images
/nginx_1.22.0/sefs/lower", upper_dir: "/images/nginx_1.22.0/sefs/upper", key: Some([199, 50, 179, 237, 68, 223, 236, 123, 37, 45, 154, 50, 56, 141, 88, 97]) })
[2022-11-10T05:57:23.709Z][ WARN][T3][#2692706][···Mount] HostFS: sync is unimplemented
[2022-11-10T05:57:23.710Z][ERROR][T3][#2692706][···Mount] SGX protected file I/O error: EWOULDBLOCK (#41, Unknown error): Resource deadlock avoided (os error 11)
[2022-11-10T05:57:23.710Z][ERROR][T3][#2692706][···Mount] Error = EIO (#5, I/O error): DeviceError(41)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692709][EpollCtl] epoll_ctl: epfd: 5, op: 2, fd: 13
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692709][EpollCtl] epoll control: cmd = Del(13)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692710][···Close] close: fd: 13
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692712][EpollCtl] epoll_ctl: epfd: 5, op: 2, fd: 11
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692712][EpollCtl] epoll control: cmd = Del(11)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692713][···Close] close: fd: 11
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692715][EpollCtl] epoll_ctl: epfd: 5, op: 2, fd: 14
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692715][EpollCtl] epoll control: cmd = Del(14)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692716][···Close] close: fd: 14
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692718][EpollCtl] epoll_ctl: epfd: 5, op: 2, fd: 12
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692718][EpollCtl] epoll control: cmd = Del(12)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692719][···Close] close: fd: 12
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692721][EpollWait] epoll_wait: epfd: 3, max_events: 1024, timeout_ms: 0
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692721][EpollWait] epoll_wait: epfd: 3, len: 1024, timeout: 0
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692721][EpollWait] epoll wait: timeout = Some(0ns)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692724][EpollCtl] epoll_ctl: epfd: 5, op: 2, fd: 15
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692724][EpollCtl] epoll control: cmd = Del(15)
[2022-11-10T05:57:23.711Z][DEBUG][T3][#2692725][···Close] close: fd: 15
[2022-11-10T05:57:23.712Z][DEBUG][T3][#2692726][EpollWait] epoll_wait: epfd: 3, max_events: 1024, timeout_ms: -1
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692726][EpollWait] epoll_wait: epfd: 3, len: 1024, timeout: -1
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692726][EpollWait] epoll wait: timeout = None
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692729][EpollCtl] epoll_ctl: epfd: 5, op: 2, fd: 10
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692729][EpollCtl] epoll control: cmd = Del(10)
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692730][···Close] close: fd: 10
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692731][EpollWait] epoll_wait: epfd: 3, max_events: 1024, timeout_ms: -1
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692731][EpollWait] epoll_wait: epfd: 3, len: 1024, timeout: -1
[2022-11-10T05:57:23.713Z][DEBUG][T3][#2692731][EpollWait] epoll wait: timeout = None
[2022-11-10T05:57:33.092Z][DEBUG][T200][#114][Sigaltstack] do_sigaltstack: new_ss:Some(SigStack { sp: 0, flags: SS_DISABLE, size: 8192 })
[2022-11-10T05:57:33.092Z][DEBUG][T200][#115][··Munmap] munmap: addr: 0x7f40d3995000, size: 0x3000
[2022-11-10T05:57:33.092Z][ERROR][T200][#116][·Madvise] Error = ENOSYS (#38, Function not implemented): Unimplemented or unknown syscall [line = 965, file = src/syscall/mod.rs]
[2022-11-10T05:57:33.092Z][DEBUG][T200][#117][····Exit] exit: 0
[2022-11-10T05:57:33.092Z][DEBUG][T200][#117][····Exit] futex_wake_bitset addr: 0x7f40d39949d0, max_count: 1, bitset: 0xffffffff
[2022-11-10T05:57:33.092Z][DEBUG][T200][#117][····Exit] wake the rubust_list: 0x7f40d39949e0
[2022-11-10T05:57:33.092Z][DEBUG][T200][#117][····Exit] futex_wake_bitset addr: 0x7f3fb4dae0b0, max_count: 1, bitset: 0xffffffff
[2022-11-10T05:57:33.092Z][ INFO][T200][#117][····Exit] Thread exited: tid = 200, status = 0
[2022-11-10T05:57:33.092Z][ INFO][T0][#117][····Exit] Idle process reaps zombie children pid = []
[2022-11-10T05:57:33.093Z][ WARN][T0][#117][····Exit] HostFS: sync is unimplemented

@qzheng527
Copy link

@mythi @haosanzi What is the sequence of below three?

  1. mount an empty unionfs to /run/enclave-cc/containers/nginx_1.22.0/rootfs.
  2. pulling the nginx docker image layers and do unpacking.
  3. Package and copy the nginx layers content to previous mount point.

1,2,3 or 2,1,3?

@mythi It would be better to capture the log with "trace" log level.

@mythi
Copy link
Contributor Author

mythi commented Nov 10, 2022

1,2,3 or 2,1,3?

2,1,3 as far as I can see from the code

@mythi It would be better to capture the log with "trace" log level.

there's not much more info (I cut some lines)

2022-11-10T07:12:01.688Z][TRACE][T3][#262967][Recvfrom] Syscall { num = Recvfrom, fd = 15, base = 0x7fbfc146b858, len = 16400, flags = 0, addr = 0x0, addr_len = 0x0 }
[2022-11-10T07:12:01.688Z][TRACE][T3][#262967][Recvfrom] Retval = 0x4010
[2022-11-10T07:12:01.688Z][TRACE][T3][#262968][Recvfrom] Syscall { num = Recvfrom, fd = 15, base = 0x7fbfc146b853, len = 5, flags = 0, addr = 0x0, addr_len = 0x0 }
[2022-11-10T07:12:01.688Z][TRACE][T3][#262968][Recvfrom] Retval = 0x5
[2022-11-10T07:12:01.688Z][TRACE][T3][#262969][Recvfrom] Syscall { num = Recvfrom, fd = 15, base = 0x7fbfc146b858, len = 16400, flags = 0, addr = 0x0, addr_len = 0x0 }
[2022-11-10T07:12:01.688Z][TRACE][T3][#262969][Recvfrom] Retval = 0x4010
[2022-11-10T07:12:01.689Z][TRACE][T3][#262970][Recvfrom] Syscall { num = Recvfrom, fd = 15, base = 0x7fbfc146b853, len = 5, flags = 0, addr = 0x0, addr_len = 0x0 }
[2022-11-10T07:12:01.689Z][TRACE][T3][#262970][Recvfrom] Retval = 0x5
[2022-11-10T07:12:01.689Z][TRACE][T3][#262971][Recvfrom] Syscall { num = Recvfrom, fd = 15, base = 0x7fbfc146b858, len = 1782, flags = 0, addr = 0x0, addr_len = 0x0 }
[2022-11-10T07:12:01.689Z][TRACE][T3][#262971][Recvfrom] Retval = 0x6f6
[2022-11-10T07:12:01.689Z][TRACE][T3][#262972][Recvfrom] Syscall { num = Recvfrom, fd = 15, base = 0x7fbfc146b853, len = 5, flags = 0, addr = 0x0, addr_len = 0x0 }
[2022-11-10T07:12:01.689Z][ERROR][T3][#262972][Recvfrom] Error = EAGAIN (#11, Try again): libc error [line = 87, file = src/net/socket/host/recv.rs]
[2022-11-10T07:12:01.689Z][TRACE][T3][#262972][Recvfrom] Retval = 0xfffffffffffffff5
[2022-11-10T07:12:01.689Z][TRACE][T3][#262973][ClockGettime] Syscall { num = ClockGettime, clockid = 1, ts_u = 0x7fbfbcd4a330 }
[2022-11-10T07:12:01.689Z][TRACE][T3][#262973][ClockGettime] Retval = 0x0
[2022-11-10T07:12:01.689Z][TRACE][T3][#262974][····Stat] Syscall { num = Stat, path = 0x7fbfc14e9580, stat_buf = 0x7fbfbcd46040 }
[2022-11-10T07:12:01.689Z][DEBUG][T3][#262974][····Stat] fstatat: fs_path: FsPath { Absolute("/run/enclave-cc/containers/nginx_1.22.0/rootfs") }, flags: (empty)
[2022-11-10T07:12:01.689Z][DEBUG][T3][#262974][····Stat] lookup_inode: cwd: "/", path: "/run/enclave-cc/containers/nginx_1.22.0/rootfs"
[2022-11-10T07:12:01.689Z][TRACE][T3][#262974][····Stat] Retval = 0x0
[2022-11-10T07:12:01.689Z][TRACE][T3][#262975][···Mount] Syscall { num = Mount, source = 0x7fbfbcd440e8, target = 0x7fbfbcd450e8, fs_type = 0x7fbfbcd42018, flags = 0, options = 0x7fbfbcd43018 }
[2022-11-10T07:12:01.690Z][DEBUG][T3][#262975][···Mount] mount: source: unionfs, target: /run/enclave-cc/containers/nginx_1.22.0/rootfs, flags: (empty), options: UnionFS(UnionFSMountOptions { lower_dir: "/images/
nginx_1.22.0/sefs/lower", upper_dir: "/images/nginx_1.22.0/sefs/upper", key: Some([199, 50, 179, 237, 68, 223, 236, 123, 37, 45, 154, 50, 56, 141, 88, 97]) })
[2022-11-10T07:12:01.696Z][ WARN][T3][#262975][···Mount] HostFS: sync is unimplemented
[2022-11-10T07:12:01.701Z][ERROR][T3][#262975][···Mount] SGX protected file I/O error: EWOULDBLOCK (#41, Unknown error): Resource deadlock avoided (os error 11)
[2022-11-10T07:12:01.701Z][ERROR][T3][#262975][···Mount] Error = EIO (#5, I/O error): DeviceError(41)
[2022-11-10T07:12:01.701Z][TRACE][T3][#262975][···Mount] Retval = 0xfffffffffffffffb
[2022-11-10T07:12:01.701Z][TRACE][T3][#262976][··Sendto] Syscall { num = Sendto, fd = 10, base = 0x7fbfc142cfe0, len = 121, flags = 16384, addr = 0x0, addr_len = 0 }
[2022-11-10T07:12:01.701Z][TRACE][T3][#262976][··Sendto] Retval = 0x79
[2022-11-10T07:12:01.701Z][TRACE][T3][#262977][··Sendto] Syscall { num = Sendto, fd = 15, base = 0x7fbfc146b853, len = 23, flags = 16384, addr = 0x0, addr_len = 0 }
[2022-11-10T07:12:01.701Z][TRACE][T3][#262977][··Sendto] Retval = 0x17

@qzheng527
Copy link

qzheng527 commented Nov 10, 2022

@mythi It is weird. The mount should have nothing to do with the application docker image. Why it is successful for hello-world but failed for nginx? Can you check if they are all empty folers, target, upper and lower?

And EWOULDBLOCK usually means some file is used by others. Are you sure there is only one enclave instance running in the same time?

@mythi
Copy link
Contributor Author

mythi commented Nov 10, 2022

the trace log file is huge (1M lines ~140MB). Some potential errors:

^[[0m[2022-11-10T11:36:32.381Z][DEBUG][T3][#169240][···Lstat] lookup_inode: cwd: "/", path: "/var/lib/image-rs/layers/sha256_1d8866550bdd02551d6874e99aa828dd95b7434c6a25168765d46251cc230469/bin"^[[0m
^[[0m[2022-11-10T11:36:32.381Z][TRACE][T3][#169240][···Lstat] Retval = 0x0^[[0m
^[[0m[2022-11-10T11:36:32.381Z][TRACE][T3][#169241][···Lstat] Syscall { num = Lstat, path = 0x7f6f354cfc30, stat_buf = 0x7f6f31e20c10 }^[[0m
^[[0m[2022-11-10T11:36:32.381Z][DEBUG][T3][#169241][···Lstat] fstatat: fs_path: FsPath { Absolute("/run/enclave-cc/containers/nginx_1.22.0/rootfs/bin/rbash") }, flags: AT_SYMLINK_NOFOLLOW^[[0m
^[[0m[2022-11-10T11:36:32.381Z][DEBUG][T3][#169241][···Lstat] lookup_inode_no_follow: cwd: "/", path: "/run/enclave-cc/containers/nginx_1.22.0/rootfs/bin/rbash"^[[0m
^[[0m[2022-11-10T11:36:32.381Z][DEBUG][T3][#169241][···Lstat] lookup_inode: cwd: "/", path: "/run/enclave-cc/containers/nginx_1.22.0/rootfs/bin"^[[0m
^[[31m[2022-11-10T11:36:32.381Z][ERROR][T3][#169241][···Lstat] Error = ENOENT (#2, No such file or directory): EntryNotFound^[[0m
^[[0m[2022-11-10T11:36:32.381Z][TRACE][T3][#169241][···Lstat] Retval = 0xfffffffffffffffe^[[0m

@mythi
Copy link
Contributor Author

mythi commented Nov 10, 2022

All errors from the last 3000 lines:

$ tail -3000 log.txt | grep ERROR
[2022-11-10T11:37:21.036Z][ERROR][T3][#254661][Recvfrom] Error = EAGAIN (#11, Try again): libc error [line = 87, file = src/net/socket/host/recv.rs]
[2022-11-10T11:37:21.045Z][ERROR][T3][#254665][···Mount] SGX protected file I/O error: EWOULDBLOCK (#41, Unknown error): Resource deadlock avoided (os error 11)
[2022-11-10T11:37:21.045Z][ERROR][T3][#254665][···Mount] Error = EIO (#5, I/O error): DeviceError(41)

@qzheng527
Copy link

@mythi We found the root cause. It was due to the diretory (upper/lower layers) used for mount haven't been created in advance.
@haosanzi Please give more detail comments on this issue.

@HaokunX-intel
Copy link
Contributor

CoCo quickstart documentation uses bitnami/nginx:1.22.0 image as an example and I gave it a try. I'm seeing different image pull errors:

Failed to pull image "docker.io/bitnami/nginx:1.22.0": rpc error: code = Internal desc = failed to mount "unionfs" to "/run/enclave-cc/containers/nginx_1.22.0/rootfs", with error: EIO: I/O error

to debug this in more details, I ran enclave-agent (sudo runc run 123) from the bundle with OCCLUM_LOG_LEVEL=debug and used the "async-client" to debug. This time I'm getting different errors:

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_3a52f76b4a6462386fe51fabf6cc829dbdef540dc64cdcc809f907bfb6c68195\" already exists")) ended: 5.798667018s

The latter blocks me from investigating the former error in details.

For the first log info, I have two comments:

  1. When launch the enclave agent by rune or runc, we need to add image bundle directories in manual. The shim should be responsible for the mission in normal workflow currently. Given that the bundle of the enclave agent is in the directory agent_bundle, where config.json and rootfs reside. Before we pull docker.io/nginx, we need to create several directories under rootfs/images, looks like
rootfs
|-images
  |-nginx
    |-sefs
      |-lower
      |-upper

The lower and upper is empty and will be filled with encrypted layers by the enclave agent soon.

  1. To pull a image with lots of files, such as redis or ngnix, we need to modify the value of rlimits in the config.json, it looks like
"rlimits": [
            {
                "type": "RLIMIT_NOFILE",
                "hard": 65535,
                "soft": 65535
            }
        ],

For the second log info, I have two comments:

  1. The enclave agent will save all changes after you Ctrl+C to stop it, that is to say, it is a stateful container. If we want to reproduce some scenarios, we should rebuild the enclave agent bundle.
  2. The enclave agent will create a directory named by the digest of the image layer to store each downloaded image layer, and remove these directories only when the mount operation is successful. Unfortunately, if the any steps fail before the mount operation, these directories will retain. If we pull the same image in the same agent bundle, the enclave agent will complain that the layer already exists.

At last, it seems the enclave agent sometimes cannot recognize a image with a version tag such as docker.io/nginx:v1, it will complain that the container ID is invalid. You can try pull the image without tags.

Hope these comments can help you @mythi.

@haosanzi
Copy link
Contributor

@HaokunX-intel Thank you for your comment.
I suggest enclave-agent README and design document are required in the enclave-cc repo.

  • The README document guides how to use enclave-agent, including how to do some simple image pull operations and run functions.
  • Design document including design ideas of enclave-agent.

@mythi
Copy link
Contributor Author

mythi commented Nov 11, 2022

Thanks all! I will give these a try later today.

@HaokunX-intel
Copy link
Contributor

@HaokunX-intel Thank you for your comment. I suggest enclave-agent README and design document are required in the enclave-cc repo.

  • The README document guides how to use enclave-agent, including how to do some simple image pull operations and run functions.
  • Design document including design ideas of enclave-agent.

Good suggestions. I plan to draft a guideline and introduce some debug tools later.

@mythi
Copy link
Contributor Author

mythi commented Nov 11, 2022

CoCo quickstart documentation uses bitnami/nginx:1.22.0 image as an example and I gave it a try. I'm seeing different image pull errors:

Failed to pull image "docker.io/bitnami/nginx:1.22.0": rpc error: code = Internal desc = failed to mount "unionfs" to "/run/enclave-cc/containers/nginx_1.22.0/rootfs", with error: EIO: I/O error

to debug this in more details, I ran enclave-agent (sudo runc run 123) from the bundle with OCCLUM_LOG_LEVEL=debug and used the "async-client" to debug. This time I'm getting different errors:

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_3a52f76b4a6462386fe51fabf6cc829dbdef540dc64cdcc809f907bfb6c68195\" already exists")) ended: 5.798667018s

The latter blocks me from investigating the former error in details.

For the first log info, I have two comments:

1. When launch the enclave agent by `rune` or `runc`, we need to add image bundle directories in manual. The `shim` should be responsible for the mission in normal workflow currently. Given that the bundle of the enclave agent is in the directory `agent_bundle`, where `config.json` and `rootfs` reside. Before we pull `docker.io/nginx`, we need to create several directories under `rootfs/images`, looks like
rootfs
|-images
  |-nginx
    |-sefs
      |-lower
      |-upper

I have these created but I can still see errors:

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_0df440342e265c89de536643c3376dadf44c810fe2fb2b2ee44711f8661ce531\" already exists")) ended: 1.962322887s                                                                                      
pull_image - docker.io/nginx 

The lower and upper is empty and will be filled with encrypted layers by the enclave agent soon.

They remain empty.

2. To pull a image with lots of files, such as redis or ngnix, we need to modify the value of `rlimits` in the `config.json`, it looks like
"rlimits": [
            {
                "type": "RLIMIT_NOFILE",
                "hard": 65535,
                "soft": 65535
            }
        ],

We have this set in enclave-cc config.json.template.

For the second log info, I have two comments:

1. The enclave agent will save all changes after you `Ctrl+C` to stop it, that is to say, it is a stateful container. If we want to reproduce some scenarios, we should rebuild the enclave agent bundle.

Where are these stored? I've deleted the agent-enclave runtime dirs created by shim-rune under /run/containerd/agent-enclave and I still see errors.

2. The enclave agent will create a directory named by the digest of the image layer to store each downloaded image layer, and remove these directories only when the mount operation is successful. Unfortunately, if the any steps fail before the mount operation, these directories will retain. If we pull the same image in the same agent bundle, the enclave agent will complain that the layer already exists.

This is not correct. Images with different names can still have layers with the same digest, e.g., the base layer.

At last, it seems the enclave agent sometimes cannot recognize a image with a version tag such as docker.io/nginx:v1, it will complain that the container ID is invalid. You can try pull the image without tags.

OK I will submit an issue about it so it gets fixed. We cannot ask users to change their deployments.

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Nov 11, 2022

Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_0df440342e265c89de536643c3376dadf44c810fe2fb2b2ee44711f8661ce531\" already exists")) ended: 1.962322887s                                                                                      
pull_image - docker.io/nginx

The problem is usually caused by previously pulling failure in the same enclave agent bundle. We need to "rebuild" the enclave agent bundle, The "rebuild" means running occlum new, occlum package and so on to create a brand-new bundle.

I've deleted the agent-enclave runtime dirs created by shim-rune under /run/containerd/agent-enclave and I still see errors.

Deleting a part of dirs in the agent bundle maybe cannot solve the problems. Can you specify the names of these runtime dirs? I want to reproduce the problem in local.

@mythi
Copy link
Contributor Author

mythi commented Nov 11, 2022

I've deleted the agent-enclave runtime dirs created by shim-rune under /run/containerd/agent-enclave and I still see errors.

Deleting a part of dirs in the agent bundle maybe cannot solve the problems. Can you specify the names of these runtime dirs? I want to reproduce the problem in local.

@HaokunX-intel it looks shim-rune uses the agent bundle as is and what you are saying is that the Occlum instance is stateful and that an error somewhere makes it unusable (it cannot recover from the failures). Are there some limitations why enclave agent behaves like this?

@qzheng527
Copy link

@mythi @HaokunX-intel The agent enclave is stateful. The previous pulling/unpacking stored the content in the Occlum unionfs. Instead of the occlum new operation (which I think is not applicable on practical env) to start a fresh agent enclave, why not every time copy the template agent enclave to a working dir (shim do this?) as a new clean start?

@qzheng527
Copy link

I've deleted the agent-enclave runtime dirs created by shim-rune under /run/containerd/agent-enclave and I still see errors.

Deleting a part of dirs in the agent bundle maybe cannot solve the problems. Can you specify the names of these runtime dirs? I want to reproduce the problem in local.

@HaokunX-intel it looks shim-rune uses the agent bundle as is and what you are saying is that the Occlum instance is stateful and that an error somewhere makes it unusable (it cannot recover from the failures). Are there some limitations why enclave agent behaves like this?

The application (image-rs?) running in agent-enclave can (or should) cover this failures. Before doing a new pull/unpack, delete the content left by preivous attempt.

@mythi
Copy link
Contributor Author

mythi commented Nov 11, 2022

I totally agree it needs to be made more robust but we should explore the options more closely.

Based on this thread my summary is there are few areas that need fixing:

  • layer snapshots are not preserved ("directory named by the digest of the image layer to store each downloaded image layer, and remove these directories only when the mount operation is successful")
  • errors in pull/unpack + SEFS mount result in a dead-lock for that image (or any other image that has the same layer digests)
  • image tag detection is unstable

@hairongchen hairongchen added the bug Something isn't working label Nov 16, 2022
@dcmiddle
Copy link
Member

@arronwy fyi in case this is an error propagation condition in image-rs

@hairongchen
Copy link
Contributor

I totally agree it needs to be made more robust but we should explore the options more closely.

Based on this thread my summary is there are few areas that need fixing:

  • layer snapshots are not preserved ("directory named by the digest of the image layer to store each downloaded image layer, and remove these directories only when the mount operation is successful")
  • errors in pull/unpack + SEFS mount result in a dead-lock for that image (or any other image that has the same layer digests)
  • image tag detection is unstable

first issue related to image/layer sharing, need to further check with image-rs community
second and third issues, will file a bug in image-rs and fix it

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Nov 18, 2022

  • image tag detection is unstable

When the enclave agent got the image pulling request, it checks the container id (cid) firstly. Typically, if the request has a cid, the cid's pattern is image_name:tag. Then the function verify_id defined here is used to verify the cid. The verify_id regards the character : as an invalid one.

One solution is to replace the : with _ in the original cid as the convention before validation.

We propose a PR to fix the bug.

@Xynnn007
Copy link
Member

Hi, some related error has been fixed in image-rs. confidential-containers/guest-components#76 Please try to use the newest rev of image-rs. Hope to help!

@HaokunX-intel
Copy link
Contributor

Hi, some related error has been fixed in image-rs. confidential-containers/image-rs#76 Please try to use the newest rev of image-rs. Hope to help!

It seems this issue is related to other problems.
We rebuild the agent with the newest rev of the image-rs.

RUSTFLAGS="-C link-args=-Wl,-rpath=/usr/local/lib/rats-tls:/usr/local/lib" cargo build --release
    Updating git repository `https://github.com/confidential-containers/image-rs`
   Compiling ocicrypt-rs v0.1.0 (https://github.com/confidential-containers/ocicrypt-rs?tag=v0.2.0#2a09bd03)
   Compiling attestation_agent v1.0.0 (https://github.com/confidential-containers/attestation-agent?rev=3b4716dd3d8bbf0d5f8cec7bc0d528421f00fd06#3b4716dd)
   Compiling image-rs v0.1.0 (https://github.com/confidential-containers/image-rs#76e6abed)
   Compiling enclave-agent v0.1.0 (/root/haokun/github.com/confidential-containers/enclave-cc/src/enclave-agent)
    Finished release [optimized] target(s) in 33.22s

And we pull the image docker.io/busybox which only has one layer. Firstly, we download the image successfully, but fail to mount it. Then we pull it again, it complains the layer already exists.

sock:tcp://0.0.0.0:6661, image:docker.io/busybox
Green Thread 1 - image.pull_image() started: 1.049µs
Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "failed to mount \"unionfs\" to \"/run/enclave-cc/containers/busybox/rootfs\", with error: EIO: I/O error")) ended: 4.93641527s
sock:tcp://0.0.0.0:6661, image:docker.io/busybox
Green Thread 1 - image.pull_image() started: 898ns
Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "unpack destination \"/var/lib/image-rs/layers/sha256_405fecb6a2fa4f29683f977e7e3b852bf6f8975a2aba647d234d2371894943da\" already exists")) ended: 4.874438845s

According to the code, we guess the image layer is cached whatever the image is mounted successfully.

We want to know if the image-rs plans to provide some mechanism, looks like

  • a pulling option that allow it to override the existing cached layers
  • a fallback function invoked once failures happen from pulling to mounting, which cleans caches.

We think these will make the image-rs more user-friendly.

@Xynnn007
Copy link
Member

cc @arronwy

@arronwy
Copy link
Member

arronwy commented Nov 29, 2022

Hi @HaokunX-intel , can you have a try with latest image-r, this PR is merged: confidential-containers/guest-components#79, layer db should correctly updated when layer is pulled success even have some error in following operation.

@HaokunX-intel
Copy link
Contributor

Hi @HaokunX-intel , can you have a try with latest image-r, this PR is merged: confidential-containers/image-rs#79, layer db should correctly updated when layer is pulled success even have some error in following operation.

We rebuild the agent agent.

Compiling image-rs v0.1.0 (https://github.com/confidential-containers/image-rs?rev=6eb98e04e965220ebc934a8c6cee160053e398cd#6eb98e04)

The PR confidential-containers/guest-components#79 actually skips the pulled layer.

sock:tcp://0.0.0.0:6661, image:docker.io/busybox
Green Thread 1 - image.pull_image() started: 1.246µs
Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: "failed to mount \"unionfs\" to \"/run/enclave-cc/containers/busybox/rootfs\", with error: EIO: I/O error")) ended: 8.375950974s
sock:tcp://0.0.0.0:6661, image:docker.io/busybox
Green Thread 1 - image.pull_image() started: 1.095µs
Green Thread 1 - pull_image -> Err(RpcStatus(code: INTERNAL message: " 1 layers failed to pull")) ended: 6.953775164s
pull_image - docker.io/busybox

However, it also skips the mount operation.
In the successful image pulling, the image will be unpacked and mounted to a point, it looks like

$tree -L 3 redis/
redis/
└── sefs
    ├── lower
    │   ├── fc0ff5a217e6840dd8a658e1e81e5dd6
    │   └── metadata
    └── upper
        ├── 0004fc0c3ee5fc78ab0bd11b0a5fcd7e
        ├── 00116102d3d28899313067ff6b904dcd
...

Currently, it looks like

 $ tree -L 2 redis/
redis/
└── sefs
    ├── lower
    └── upper

The bundle is empty.

Maybe we can info on existing layers instead of erroring on the them to allow the mount operation to be executed. Or we can pass the control to the application by providing some pulling options.

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Dec 1, 2022

@arronwy helps me summarize current problems.

  • layer snapshots are not preserved ("directory named by the digest of the image layer to store each downloaded image layer, and remove these directories only when the mount operation is successful")

I misunderstand the image-rs workflow. The image-rs allows different images share the same layers, and the cached layer won't be removed after mount operation. But there is one thing we need to pay attention to. When we pull two images with same layers in parallel, the function unpack will raise errors, because it can not unpack layers sharing the same digest at the same time. We need to guarantee that the images are pulled one by one, as the kata agent does here.

  • errors in pull/unpack + SEFS mount result in a dead-lock for that image (or any other image that has the same layer digests)

confidential-containers/guest-components#79 fixes the second problem in most scenarios. When the agent runs continuously, previously failures on image pulling will not result in the dead-lock. Except that we terminate the agent after failure on the image pulling and re-launch the agent to pull the same image. It is a strange problem, but in the usual case, the agent won't be re-launched.

  • image tag detection is unstable

The third problem is fixed by #61.

In the conclusion, the third problem is fixed. We need to decide if we accept the following features,

  • images need to pulled one by one
  • re-launching the terminated agent may raise pulling error

@mythi
Copy link
Contributor Author

mythi commented Dec 1, 2022

re-launching the terminated agent may raise pulling error

isn't this a very likely scenario to happen (like with some kubernetes controllers that retry pod re-creation on errors)?

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Dec 2, 2022

re-launching the terminated agent may raise pulling error

isn't this a very likely scenario to happen (like with some kubernetes controllers that retry pod re-creation on errors)?

If I understand correctly, the shim will create a brand new agent bundle by mounting here upon the pod re-creation /cc @haosanzi .

@mythi
Copy link
Contributor Author

mythi commented Dec 2, 2022

@HaokunX-intel but the current implementation seems to use the same stateful Occlum instance for all pods (via agentContainerPath lower dir)

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Dec 2, 2022

@HaokunX-intel but the current implementation seems to use the same stateful Occlum instance for all pods (via agentContainerPath lower dir)

Traditionally, in the overlay filesystem, lower dir is mounted as a read-only layer, all changes are saved in the upper dir or work dir, which do not impact the lower dir.

@mythi
Copy link
Contributor Author

mythi commented Dec 2, 2022

@HaokunX-intel but the current implementation seems to use the same stateful Occlum instance for all pods (via agentContainerPath lower dir)

Traditionally, in the overlay filesystem, lower dir is mounted as a read-only layer, all changes are saved in the upper dir or work dir, which do not impact the lower dir.

Right, yes. How about the case where a pod restart with the same image (and cid) ends up using the same runtime dir.

@HaokunX-intel
Copy link
Contributor

@HaokunX-intel but the current implementation seems to use the same stateful Occlum instance for all pods (via agentContainerPath lower dir)

Traditionally, in the overlay filesystem, lower dir is mounted as a read-only layer, all changes are saved in the upper dir or work dir, which do not impact the lower dir.

Right, yes. How about the case where a pod restart with the same image (and cid) ends up using the same runtime dir.

Actually, each pod has an independent agent bundle. The runtime dir (upper and work) of the agent bundle is placed at [agentContainerRootDir]/[rand_id]/merged as shown here ( [agentContainerRootDir] is configed at here). The pulled image and unpacked bundle are stored in the runtime dir for different pod.

And the agent is bind with the sandbox in the same pod, we can not restart the agent on an existing bundle but we can start a new agent by starting a new sandbox. The mentioned case can not happen.

As @hairongchen mentioned in #18 , a part of the status and actions on the agent is undefined. I think it is out of the scope of the issue.

@mythi
Copy link
Contributor Author

mythi commented Dec 5, 2022

@HaokunX-intel sounds good, thanks. Can you update image-rs in this repo so we get to verify the fixes?

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Dec 6, 2022

@HaokunX-intel sounds good, thanks. Can you update image-rs in this repo so we get to verify the fixes?

The update is to remove the tag = "v0.2.0" in the image-rs reference of the src/enclave-agent/Cargo.toml. But the latest image-rs depends on the rats-tls, we need to modify the enclave-agent.yaml to copy the rats-tls to the occlum instance during the building.

And I find that the issue #48 is importing the rats-tls and it also need to update the image-rs reference. Therefore the #48 can fix this issue.

@mythi
Copy link
Contributor Author

mythi commented Dec 7, 2022

The update is to remove the tag = "v0.2.0" in the image-rs reference of the src/enclave-agent/Cargo.toml. But the latest image-rs depends on the rats-tls, we need to modify the enclave-agent.yaml to copy the rats-tls to the occlum instance during the building.

We want to update to known revs and not just drop the tag. Can you explain what's the dependency to rats-tls (that is, why is it not possible to use image-rs without rats-tls anymore).

@HaokunX-intel
Copy link
Contributor

The update is to remove the tag = "v0.2.0" in the image-rs reference of the src/enclave-agent/Cargo.toml. But the latest image-rs depends on the rats-tls, we need to modify the enclave-agent.yaml to copy the rats-tls to the occlum instance during the building.

We want to update to known revs and not just drop the tag. Can you explain what's the dependency to rats-tls (that is, why is it not possible to use image-rs without rats-tls anymore).

To fix this issue, the rev of the image-rs need to be equal to or newer than 6eb98e04e965220ebc934a8c6cee160053e398cd.

The reason why the the image-rs can not be used without rats-tls is that, after tag = "v0.2.0 and before 6eb98e04e965220ebc934a8c6cee160053e398cd, the PR confidential-containers/guest-components#60 supports signature verification with a native client and has been merged into the main. The PR is aimed to enable the image-rs to directly connect to the KBS with a native EAA KBC, which is supported by the crate attestation-agent, instead of connecting to a running aa server and using it as a intermediate KBC to communicate with KBS.

The crate attestation-agent provides the native EAA KBC to connect to the KBS Verdictd. They exchange data on the secured channel based on rats-tls. The dependency chain looks like this.

enclave agent (a native signature client) ---> image-rs (a native eaa client) ---> attestation-agent (secured channel) ---> rats-tls

@mythi
Copy link
Contributor Author

mythi commented Dec 7, 2022

The PR is aimed to enable the image-rs to directly connect to the KBS with a native EAA KBC

We also need to leave the possibility to use occlum_feature without dependencies to eaa_kbc.

@HaokunX-intel
Copy link
Contributor

HaokunX-intel commented Dec 7, 2022

The PR is aimed to enable the image-rs to directly connect to the KBS with a native EAA KBC

We also need to leave the possibility to use occlum_feature without dependencies to eaa_kbc.

Right. It is our final target, customers can choose different combinations. But currently the dependency chain only supports the combination occlum_feature + (native) eaa_kbc. In the future, we can use conditional compile to decouple them without consuming too much efforts. Given that we are in the every early stage, should we work on the fixed combination temporarily and decouple them when we plan to support other kinds of KBCs ?

In the other side, it raises another problem. Is our bundle an omniscient one or a tailored one? The omniscient bundle containes all dependencies and supports all combinations, customers can choose any combination without reinstalling the bundle. The tailored bundle only supports several features the customer choose previously, and if they want to select other combinations, they should reinstall the bundle.

If our bundle is omniscient, importing rats-tls currently is acceptable. If not, we need to think about how to tailor the bundle for specific demand, such as occlum_feature + (native) eaa_kbc.

@fidencio fidencio moved this to 🆕 New in CoCo Releases Dec 7, 2022
@mythi
Copy link
Contributor Author

mythi commented Dec 7, 2022

I'm not yet ready with #48 but I want this issue verified and closed so I'd propose we decouple eaa_kbc from occlum_feature.

@mythi mythi moved this from 🆕 New to 🏗 In progress in CoCo Releases Dec 7, 2022
@hairongchen
Copy link
Contributor

hairongchen commented Dec 8, 2022

I'd agreed with Haokun's analyze and I'd suggest bellow:

  • we open a issue in image-rs to track this requirement to decouple the EAA KBC feature and implement it later(maybe comes with the timing of new KBC and new dependency)
  • let's verify this issue after build EAA/RATS-TLS #48 is done as both this issue and build EAA/RATS-TLS #48 is required done in V0.3
  • if we really want to verify this issue, we verify it use dev mode first and verify it again in Integration phase before release together with build EAA/RATS-TLS #48

Thanks!

@mythi mythi closed this as completed in #77 Jan 9, 2023
@dcmiddle dcmiddle moved this from 🏗 In progress to 👀 In review in CoCo Releases Jan 12, 2023
@dcmiddle dcmiddle moved this from 👀 In review to ✅ Done in CoCo Releases Jan 12, 2023
@mythi mythi removed this from CoCo Releases Mar 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants