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

generate accessed files list for container with NRI plugin #349

Merged
merged 11 commits into from
Feb 24, 2023

Conversation

sctb512
Copy link
Member

@sctb512 sctb512 commented Feb 8, 2023

Generate an in-order accessed files list for container workload. The accessed files list can be set as prefetch-patterns when converting OCI images to nydus format images.

Related to dragonflyoss/nydus#807

Requirements

  • NRI 2.0: Which has been integrated into containerd since v1.7.0-beta.1.

Workflow

optmizer_workflow

  1. Run the optimizer as an NRI plugin to run optimizer server when the StartContainer event occurs.
  2. Optimizer server joins container mount namespace and starts fanotify server.
  3. Optimizer server detects fanotify event and sends the accessed file descriptor to client.
  4. Optimizer client converts file descriptor to path.
  5. Generate list of accessed files on local disk.
  6. Convert OCI images with accessed files list to nydus image.

Generate an accessed files list

To install the optimizer and optimizer-server, all you need is the command:

make optimizer && make install-optimizer

Install containerd that supports NRI 2.0:

VERSION=1.7.0-beta.3

CONTAINERD_DIR=$(cat /lib/systemd/system/containerd.service | grep "ExecStart=" | awk -F= '{gsub("/containerd","",$2); print $2}')

wget https://github.com/containerd/containerd/releases/download/$VERSION/containerd-$VERSION-linux-amd64.tar.gz
tar -zxvf containerd-$VERSION-linux-amd64.tar.gz

sudo install -D -m 755 bin/* $CONTAINERD_DIR
rm -rf bin
$CONTAINERD_DIR/containerd --version

Modify containerd's toml configuration file to enable NRI.

sudo tee -a /etc/containerd/config.toml <<- EOF
[plugins."io.containerd.nri.v1.nri"]
  config_file = "/etc/nri/nri.conf"
  disable = false
  plugin_path = "/opt/nri/plugins"
  socket_path = "/var/run/nri.sock"
EOF

sudo tee /etc/nri/nri.conf <<- EOF
disableConnections: false
EOF

sudo systemctl restart containerd

Now, just start a container workload and you will get the list of accessed files in persistDir.

sudo tee nginx.yaml <<- EOF
metadata:
  name: nginx
image:
  image: nginx:latest
log_path: nginx.0.log
linux: {}
EOF

sudo tee pod.yaml <<- EOF
metadata:
  name: nginx-sandbox
  namespace: default
  attempt: 1
  uid: hdishd83djaidwnduwk28bcsb
log_directory: /tmp
linux: {}
EOF

crictl run nginx.yaml pod.yaml

The result file for the nginx image is /opt/nri/optimiser/results/nginx:latest.

Optimize a nydus image

Nydus provides a nydusify CLI tool to convert OCI images from the source registry or local file system to nydus format and push them to the target registry.

We can install the nydusify cli tool from the nydus package.

VERSION=v2.1.3

wget https://github.com/dragonflyoss/image-service/releases/download/$VERSION/nydus-static-$VERSION-linux-amd64.tgz
tar -zxvf nydus-static-$VERSION-linux-amd64.tgz
sudo install -D -m 755 nydus-static/nydusify /usr/local/bin/nydusify
sudo install -D -m 755 nydus-static/nydus-image /usr/local/bin/nydus-image

With the --prefetch-patterns argument, we can specify the list of files to be written in the front of the nydus image and be prefetched in order when starting a container.

sudo nydusify convert --nydus-image $(which nydus-image) --source nginx --target sctb512/nginx:optimized-nydusv6 --fs-version 6 --prefetch-patterns < /opt/nri/optimizer/results/nginx:latest

Make sure the nydusd and nydus-snapshotter environments are installed correctly. Then, start a container with an optimized nydus image.

sudo nerdctl --snapshotter nydus run --rm --net host -it sctb512/nginx:optimized-nydusv6

Signed-off-by: Bin Tang tangbin.bin@bytedance.com

@sctb512 sctb512 force-pushed the generate-accessed-files-list branch 2 times, most recently from 90deffb to a987375 Compare February 13, 2023 11:31
@sctb512 sctb512 changed the title generate accessed files list for container with OCI Hook generate accessed files list for container with NRI plugin Feb 13, 2023
@sctb512 sctb512 marked this pull request as ready for review February 13, 2023 11:38
@sctb512 sctb512 force-pushed the generate-accessed-files-list branch 4 times, most recently from be14df6 to 001eb62 Compare February 14, 2023 03:27
@codecov-commenter
Copy link

codecov-commenter commented Feb 14, 2023

Codecov Report

Base: 29.26% // Head: 29.32% // Increases project coverage by +0.06% 🎉

Coverage data is based on head (969dcac) compared to base (b010060).
Patch has no changes to coverable lines.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #349      +/-   ##
==========================================
+ Coverage   29.26%   29.32%   +0.06%     
==========================================
  Files          38       38              
  Lines        3810     3812       +2     
==========================================
+ Hits         1115     1118       +3     
+ Misses       2563     2562       -1     
  Partials      132      132              
Impacted Files Coverage Δ
pkg/daemon/daemon.go 0.00% <0.00%> (ø)
pkg/blob/blob_manager.go 32.65% <0.00%> (+3.06%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@sctb512 sctb512 force-pushed the generate-accessed-files-list branch 7 times, most recently from 1e4b537 to 358b9f9 Compare February 14, 2023 10:05
@imeoer
Copy link
Collaborator

imeoer commented Feb 15, 2023

Hi @sctb512 , any doc for this? Does it work on nydus image?

@sctb512
Copy link
Member Author

sctb512 commented Feb 15, 2023

Hi @sctb512 , any doc for this? Does it work on nydus image?

Work in progress. This NRI plugin is image format independent. And it works on nydus image.

@sctb512 sctb512 force-pushed the generate-accessed-files-list branch 6 times, most recently from 7639b1e to e0b20b3 Compare February 15, 2023 08:48
.PHONY: build
build:
GOOS=${GOOS} GOARCH=${GOARCH} ${PROXY} go build -ldflags "$(LDFLAGS)" -v -o bin/containerd-nydus-grpc ./cmd/containerd-nydus-grpc
${CARGO} fmt --manifest-path ${OPTIMIZER_SERVER_TOML} -- --check
${CARGO} build --release --manifest-path ${OPTIMIZER_SERVER_TOML} && cp ${OPTIMIZER_SERVER_BIN} ./bin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a new Makefile target for building the optimizer ? It will even better if we have its own Makefile

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, we should not build the optimizer as developers may not have rust environment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is reasonable. Fixed it by adding build-optimizer. Is it acceptable?

)

type config struct {
LogFile string `yaml:"logFile"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use the Linux naming style like log_file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed this style in NRI's example code: https://github.com/containerd/nri/blob/main/plugins/logger/nri-logger.go#L33. Maybe we could keep the same style with them?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway. It makes sense to use the same parameter style in this project. Already fixed.

Events []string `yaml:"events"`

ServerPath string `yaml:"serverPath"`
PersistDir string `yaml:"persistDir"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

ServerPath string `yaml:"serverPath"`
PersistDir string `yaml:"persistDir"`
Timeout int `yaml:"timeout"`
Overwrite bool `yaml:"overwrite"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, toml is preferred

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

return 0, fmt.Errorf("failed to parse events in configuration: %w", err)
}

if cfg.LogFile != oldCfg.LogFile {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When to close the file f

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the optimizer server will close the file descriptor after sending the path to the client.

Copy link
Member Author

@sctb512 sctb512 Feb 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emm... It is not the same issue. The file descriptor mentioned in last comment is created by optimizer server. So we close it after getting the path.

The file f should be closed before exiting this NRI plugin, i.e. in onClose event.

@@ -0,0 +1,128 @@
use fanotify::low_level::*;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add license claim here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

use std::io;
use std::io::Write;
use std::os::unix::io::AsRawFd;
use std::path::Path;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually put std dependencies to the top position and then the third-party dependencies and then then project internal dependenecies

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it.

.PHONY: build-optimizer
build-optimizer:
${CARGO} fmt --manifest-path ${OPTIMIZER_SERVER_TOML} -- --check
${CARGO} build --release --manifest-path ${OPTIMIZER_SERVER_TOML} && cp ${OPTIMIZER_SERVER_BIN} ./bin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please also add cargo clippy to lint the tool, which will give some wise suggestions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. I have added this.

)

const (
defaultLogFile = "/opt/nri/optimizer/optimizer.log"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logs should be stored in /var/log or just stream it to syslog service rather than set a file as default on at /opt

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, we redirect it to the syslog service.

oldCfg := cfg
tree, err := toml.Load(config)
if err != nil {
return 0, fmt.Errorf("failed to parse TOML: %w", err)
Copy link
Member

@changweige changweige Feb 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use errors.Errorf and errors.Wrapf ? %w is deprecated now in Golang

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

func (p *plugin) StartContainer(pod *api.PodSandbox, container *api.Container) error {
dump("StartContainer", "pod", pod, "container", container)

imageName := GetImageName(container.Annotations)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should validate the image. Containerd already provides a package to validate image name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return update, nil
}

func GetImageName(annotations map[string]string) string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There exists a standard method comes from contanerd package to extract the tagged image name. Can you try to use it.


let mut fds = [PollFd::new(fd.as_raw_fd(), PollFlags::POLLIN)];
loop {
let poll_num = poll(&mut fds, -1).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid directly unwrap unless we be sure that it's safe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it with match.

return err
}

f, err := os.OpenFile(fserver.PersistFile, os.O_CREATE|os.O_WRONLY, 0644)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should close the file f ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, now we close it before exiting the server.

@sctb512 sctb512 force-pushed the generate-accessed-files-list branch 2 times, most recently from 39d79ca to a317d7f Compare February 20, 2023 11:51
FAN_MARK_ADD | FAN_MARK_MOUNT,
FAN_OPEN | FAN_CLOSE_WRITE,
AT_FDCWD,
path,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FAN_OPEN_EXEC
No need to monitor event FAN_CLOSE_WRITE

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it.

@sctb512 sctb512 force-pushed the generate-accessed-files-list branch 3 times, most recently from ac67fb3 to 640064c Compare February 21, 2023 07:11
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Signed-off-by: Bin Tang <tangbin.bin@bytedance.com>
Copy link
Member

@changweige changweige left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is indeed a useful tool to help us build more efficient nydus images

@changweige changweige merged commit 6876c4f into containerd:main Feb 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants