mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-02 12:50:54 +00:00
krata: fix conformance to container startup
This commit is contained in:
parent
6d6610ca2f
commit
8b57c16f0a
6
DEV.md
6
DEV.md
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
krata is composed of three major executables:
|
krata is composed of four major executables:
|
||||||
|
|
||||||
| Executable | Runs On | User Interaction | Dev Runner | Code Path |
|
| Executable | Runs On | User Interaction | Dev Runner | Code Path |
|
||||||
| ---------- | ------- | ---------------- | ------------------------ | ----------------- |
|
| ---------- | ------- | ---------------- | ------------------------ | ----------------- |
|
||||||
@ -54,10 +54,10 @@ $ cd krata
|
|||||||
6. Build a guest kernel image:
|
6. Build a guest kernel image:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ ./hack/kernel/build.sh -j4
|
$ ./hack/kernel/build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
7. Copy the guest kernel image at `target/kernel/kernel` to `/var/lib/krata/guest/kernel` to have it automatically detected by kratad.
|
7. Copy the guest kernel image at `target/kernel/kernel-x86_64` to `/var/lib/krata/guest/kernel` to have it automatically detected by kratad.
|
||||||
8. Launch `./hack/debug/kratanet.sh` and keep it running in the foreground.
|
8. Launch `./hack/debug/kratanet.sh` and keep it running in the foreground.
|
||||||
9. Launch `./hack/debug/kratad.sh` and keep it running in the foreground.
|
9. Launch `./hack/debug/kratad.sh` and keep it running in the foreground.
|
||||||
10. Run kratactl to launch a guest:
|
10. Run kratactl to launch a guest:
|
||||||
|
@ -191,7 +191,10 @@ impl GuestReconciler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn destroy(&self, uuid: Uuid, guest: &mut Guest) -> Result<bool> {
|
async fn destroy(&self, uuid: Uuid, guest: &mut Guest) -> Result<bool> {
|
||||||
self.runtime.destroy(uuid).await?;
|
if let Err(error) = self.runtime.destroy(uuid).await {
|
||||||
|
warn!("failed to destroy runtime guest {}: {}", uuid, error);
|
||||||
|
}
|
||||||
|
|
||||||
info!("destroyed guest {}", uuid);
|
info!("destroyed guest {}", uuid);
|
||||||
guest.network = None;
|
guest.network = None;
|
||||||
guest.state = Some(GuestState {
|
guest.state = Some(GuestState {
|
||||||
|
@ -106,6 +106,7 @@ impl GuestInit {
|
|||||||
self.mount_kernel_fs("devtmpfs", "/dev", "mode=0755")?;
|
self.mount_kernel_fs("devtmpfs", "/dev", "mode=0755")?;
|
||||||
self.mount_kernel_fs("proc", "/proc", "")?;
|
self.mount_kernel_fs("proc", "/proc", "")?;
|
||||||
self.mount_kernel_fs("sysfs", "/sys", "")?;
|
self.mount_kernel_fs("sysfs", "/sys", "")?;
|
||||||
|
std::os::unix::fs::symlink("/proc/self/fd", "/dev/fd")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,6 +392,12 @@ impl GuestInit {
|
|||||||
cmd = launch.run.as_ref().unwrap().clone();
|
cmd = launch.run.as_ref().unwrap().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(entrypoint) = config.entrypoint() {
|
||||||
|
for item in entrypoint.iter().rev() {
|
||||||
|
cmd.insert(0, item.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cmd.is_empty() {
|
if cmd.is_empty() {
|
||||||
cmd.push("/bin/sh".to_string());
|
cmd.push("/bin/sh".to_string());
|
||||||
}
|
}
|
||||||
@ -433,8 +440,7 @@ impl GuestInit {
|
|||||||
working_dir = "/".to_string();
|
working_dir = "/".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::env::set_current_dir(&working_dir)?;
|
self.fork_and_exec(working_dir, path, cmd, env).await?;
|
||||||
self.fork_and_exec(path, cmd, env).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,23 +491,26 @@ impl GuestInit {
|
|||||||
|
|
||||||
async fn fork_and_exec(
|
async fn fork_and_exec(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
working_dir: String,
|
||||||
path: CString,
|
path: CString,
|
||||||
cmd: Vec<CString>,
|
cmd: Vec<CString>,
|
||||||
env: Vec<CString>,
|
env: Vec<CString>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match unsafe { fork()? } {
|
match unsafe { fork()? } {
|
||||||
ForkResult::Parent { child } => self.background(child).await,
|
ForkResult::Parent { child } => self.background(child).await,
|
||||||
ForkResult::Child => self.foreground(path, cmd, env).await,
|
ForkResult::Child => self.foreground(working_dir, path, cmd, env).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn foreground(
|
async fn foreground(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
working_dir: String,
|
||||||
path: CString,
|
path: CString,
|
||||||
cmd: Vec<CString>,
|
cmd: Vec<CString>,
|
||||||
env: Vec<CString>,
|
env: Vec<CString>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
GuestInit::set_controlling_terminal()?;
|
GuestInit::set_controlling_terminal()?;
|
||||||
|
std::env::set_current_dir(working_dir)?;
|
||||||
execve(&path, &cmd, &env)?;
|
execve(&path, &cmd, &env)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use backhand::{FilesystemCompressor, FilesystemWriter, NodeHeader};
|
|||||||
use log::{debug, trace, warn};
|
use log::{debug, trace, warn};
|
||||||
use oci_spec::image::{ImageConfiguration, ImageManifest};
|
use oci_spec::image::{ImageConfiguration, ImageManifest};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufReader, Cursor};
|
use std::io::{ErrorKind, Read};
|
||||||
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
|
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
@ -20,18 +20,7 @@ use walkdir::WalkDir;
|
|||||||
|
|
||||||
use crate::image::fetch::{OciImageDownloader, OciImageLayer};
|
use crate::image::fetch::{OciImageDownloader, OciImageLayer};
|
||||||
|
|
||||||
pub const IMAGE_SQUASHFS_VERSION: u64 = 1;
|
pub const IMAGE_SQUASHFS_VERSION: u64 = 2;
|
||||||
const LAYER_BUFFER_SIZE: usize = 128 * 1024;
|
|
||||||
|
|
||||||
// we utilize in-memory buffers when generating the squashfs for files
|
|
||||||
// under this size. for files of or above this size, we open a file.
|
|
||||||
// the file is then read during writing. we want to reduce the number
|
|
||||||
// of open files during squashfs generation, so this limit should be set
|
|
||||||
// to something that limits the number of files on average, at the expense
|
|
||||||
// of increased memory usage.
|
|
||||||
// TODO: it may be wise to, during crawling of the image layers, infer this
|
|
||||||
// value from the size to file count ratio of all layers.
|
|
||||||
const SQUASHFS_MEMORY_BUFFER_LIMIT: usize = 8 * 1024 * 1024;
|
|
||||||
|
|
||||||
pub struct ImageInfo {
|
pub struct ImageInfo {
|
||||||
pub image_squashfs: PathBuf,
|
pub image_squashfs: PathBuf,
|
||||||
@ -238,13 +227,24 @@ impl ImageCompiler<'_> {
|
|||||||
layer: &OciImageLayer,
|
layer: &OciImageLayer,
|
||||||
image_dir: &Path,
|
image_dir: &Path,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let uid = entry.header().uid()?;
|
||||||
|
let gid = entry.header().gid()?;
|
||||||
trace!(
|
trace!(
|
||||||
"unpack entry layer={} path={:?} type={:?}",
|
"unpack entry layer={} path={:?} type={:?} uid={} gid={}",
|
||||||
&layer.digest,
|
&layer.digest,
|
||||||
entry.path()?,
|
entry.path()?,
|
||||||
entry.header().entry_type()
|
entry.header().entry_type(),
|
||||||
|
uid,
|
||||||
|
gid,
|
||||||
);
|
);
|
||||||
entry.unpack_in(image_dir).await?;
|
entry.set_preserve_mtime(true);
|
||||||
|
entry.set_preserve_permissions(true);
|
||||||
|
entry.set_unpack_xattrs(true);
|
||||||
|
if let Some(path) = entry.unpack_in(image_dir).await? {
|
||||||
|
if !path.is_symlink() {
|
||||||
|
std::os::unix::fs::chown(path, Some(uid as u32), Some(gid as u32))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,14 +319,7 @@ impl ImageCompiler<'_> {
|
|||||||
} else if typ.is_dir() {
|
} else if typ.is_dir() {
|
||||||
writer.push_dir(rel, header)?;
|
writer.push_dir(rel, header)?;
|
||||||
} else if typ.is_file() {
|
} else if typ.is_file() {
|
||||||
if metadata.size() >= SQUASHFS_MEMORY_BUFFER_LIMIT as u64 {
|
writer.push_file(ConsumingFileReader::new(entry.path()), rel, header)?;
|
||||||
let reader =
|
|
||||||
BufReader::with_capacity(LAYER_BUFFER_SIZE, File::open(entry.path())?);
|
|
||||||
writer.push_file(reader, rel, header)?;
|
|
||||||
} else {
|
|
||||||
let cursor = Cursor::new(std::fs::read(entry.path())?);
|
|
||||||
writer.push_file(cursor, rel, header)?;
|
|
||||||
}
|
|
||||||
} else if typ.is_block_device() {
|
} else if typ.is_block_device() {
|
||||||
let device = metadata.dev();
|
let device = metadata.dev();
|
||||||
writer.push_block_device(device as u32, rel, header)?;
|
writer.push_block_device(device as u32, rel, header)?;
|
||||||
@ -338,8 +331,6 @@ impl ImageCompiler<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fs::remove_dir_all(image_dir)?;
|
|
||||||
|
|
||||||
let squash_file_path = squash_file
|
let squash_file_path = squash_file
|
||||||
.to_str()
|
.to_str()
|
||||||
.ok_or_else(|| anyhow!("failed to convert squashfs string"))?;
|
.ok_or_else(|| anyhow!("failed to convert squashfs string"))?;
|
||||||
@ -347,6 +338,46 @@ impl ImageCompiler<'_> {
|
|||||||
let mut file = File::create(squash_file)?;
|
let mut file = File::create(squash_file)?;
|
||||||
trace!("squash generate: {}", squash_file_path);
|
trace!("squash generate: {}", squash_file_path);
|
||||||
writer.write(&mut file)?;
|
writer.write(&mut file)?;
|
||||||
|
std::fs::remove_dir_all(image_dir)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ConsumingFileReader {
|
||||||
|
path: PathBuf,
|
||||||
|
file: Option<File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConsumingFileReader {
|
||||||
|
fn new(path: &Path) -> ConsumingFileReader {
|
||||||
|
ConsumingFileReader {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
file: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for ConsumingFileReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
if self.file.is_none() {
|
||||||
|
self.file = Some(File::open(&self.path)?);
|
||||||
|
}
|
||||||
|
let Some(ref mut file) = self.file else {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
ErrorKind::NotFound,
|
||||||
|
"file was not opened",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
file.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ConsumingFileReader {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let file = self.file.take();
|
||||||
|
drop(file);
|
||||||
|
if let Err(error) = std::fs::remove_file(&self.path) {
|
||||||
|
warn!("failed to delete consuming file {:?}: {}", self.path, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
const DOCKER_HUB_MIRROR: &str = "registry.docker.io";
|
const DOCKER_HUB_MIRROR: &str = "mirror.gcr.io";
|
||||||
const DEFAULT_IMAGE_TAG: &str = "latest";
|
const DEFAULT_IMAGE_TAG: &str = "latest";
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
Loading…
Reference in New Issue
Block a user