-
Notifications
You must be signed in to change notification settings - Fork 728
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
CRI-O container support #1310
CRI-O container support #1310
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks mostly okay, some concerns about the string matching function though
namespace libsinsp { | ||
namespace runc { | ||
|
||
/// runc-based runtimes (Docker, containerd, CRI-O, probably others) use the same two cgroup layouts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i appreciate the comment, but still slightly confused. maybe just an exapmle for each of the runc runtimes we support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you still find it confusing after looking at the instances of this struct (container_engine/cri.cpp
, container_engine/docker_linux.cpp
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
less so, but one don't want to have to dig into the implementation to understand the api.
namespace { | ||
|
||
const size_t CONTAINER_ID_LENGTH = 64; | ||
const size_t REPORTED_CONTAINER_ID_LENGTH = 12; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe a note what these values represent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what to add that wouldn't duplicate the constant names or the static_assert below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does "reported" mean here? is it what's reported to us? what we're reporting to someone else?
userspace/libsinsp/runc.cpp
Outdated
|
||
static_assert(REPORTED_CONTAINER_ID_LENGTH <= CONTAINER_ID_LENGTH, "Reported container ID length cannot be longer than actual length"); | ||
|
||
bool detect_one_container_id(const std::string& cgroup, const std::string& prefix, const std::string& suffix, std::string& container_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should these two functions be static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also quick comment on which are inputs and ouputs of this function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're already in a private namespace.
I'll add a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
must have missed that they're still in the NS. that's fine then.
const char* suffix; | ||
}; | ||
|
||
/// If any of the cgroups of the thread in `tinfo` matches the `layout`, set `container_id` to the found id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if multiple cgroups match the layout?
I assume we're assuming that impossible, and if that's the case ,we should perhaps note the assumption
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we take the first matching one (AFAICT, the iteration order matches contents of /proc//cgroups). If the cgroups are inconsistent (matching different containers), there's no single right answer we can choose. What do you propose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so long as we're okay with that, it's fine, but we should at least be explicit in that "if it matches multiple, we'll only return the first" or something
|
||
bool detect_container_id(const std::string& cgroup, const libsinsp::runc::cgroup_layout *layout, std::string &container_id) | ||
{ | ||
for(size_t i = 0; layout[i].prefix && layout[i].suffix; ++i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this way of doing bounds checking is somewhat risky, as it depends on someone putting null pointers at the end of arrays put in.
We should be able to statically determine the size of those arrays with sizeof(X)/sizeof(X[0]) and then that value can be passed in, or even better, we can wrap the array in a struct which stores the size of the array, calculated at compile time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sizeof
won't work across function calls. Given that we have only two of these arrays, hardcoded in the source and not modifiable at runtime, I'm not sure it's a big deal (if you insist, I'd probably go for std::array
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't think the number of instance is the issue as much as the fact that depending on the last entry of an array to be null in order to prevent a segfault is not really idiomatic, and there's no compelling reason to do it that way when safer options exist.
I didn't mean computing sizeof in the function itself, but along with the array
static const int foo = {1,5,7,2,7};
static const foo_length = sizeof(foo) / sizeof(foo[0]);
Then you use foo_length as your loop bounds, as it's clear and safe.
std::array is fine or even vector would work fine here as well
|
||
static_assert(std::string::npos == (size_t)(-1), "std::string::npos must be largest valid size_t"); | ||
size_t invalid_ch_pos = cgroup.find_first_not_of(CONTAINER_ID_VALID_CHARACTERS, pos); | ||
if (invalid_ch_pos < CONTAINER_ID_LENGTH) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just match the ID against [0-9a-fA-F]{64} ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is literally what this code does. Just not using regexes.
#include "sinsp.h" | ||
#include "sinsp_int.h" | ||
|
||
namespace { | ||
bool pod_uses_host_netns(const runtime::v1alpha2::PodSandboxStatusResponse& resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this common in the code base instead of static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got tired of your (plural you) review comments asking me to switch from static to namespace{}. Make up your mind, will you? :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah i don't really care either way, i was just asking to ensure it's consistent
|
||
constexpr const cgroup_layout DOCKER_CGROUP_LAYOUT[] = { | ||
{"/", ""}, // non-systemd docker | ||
{"/docker-", ".scope"}, // systemd docker |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How confident are we about the ".scope"
suffix of systemd.
this page here:
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/resource_management_guide/sec-default_cgroup_hierarchies
seems to suggest we could have ".slice"
too.
Will review the entire PR a little later. Wanted to capture this one thing first.
bool cri::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) | ||
{ | ||
sinsp_container_info container_info; | ||
|
||
if(docker::detect_docker(tinfo, container_info.m_id)) | ||
if(matches_runc_cgroups(tinfo, CRI_CGROUP_LAYOUT, container_info.m_id)) | ||
{ | ||
container_info.m_type = s_cri_runtime_type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the need here only to specify if it is a CRI-based run-time or not ? Do we not need to know if it is CT_CRIO
or CT_CONTAINERD
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we must be as specific as possible. i think we should report a container type being "CRI" only as a last resort.
@@ -79,32 +109,27 @@ bool parse_cri(sinsp_container_manager *manager, sinsp_container_info *container | |||
parse_cri_image(resp_container, container); | |||
parse_cri_mounts(resp_container, container); | |||
|
|||
const auto &info_it = resp.info().find("info"); | |||
if(info_it == resp.info().end()) | |||
if(parse_containerd(resp, container, tinfo)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this call seems out of place. why call logic specific to containerd unconditionally when the container type is known at this point. if there is an expectation that the logic in parse_containerd() could apply to cri-o as well, why not rename the function to be clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw, can't line 102 be removed since the same operation is done in the caller of this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About the duplicate assignment, good catch, removed.
parse_containerd
should be pretty cheap when the engine is not containerd compatible (no metadata in info
) and if some other runtime decides that this is actually worth following, it saves us two extra api calls.
I'd leave this as is (currently it only applies to containerd and I don't have a better idea for the name).
bool cri::resolve(sinsp_container_manager* manager, sinsp_threadinfo* tinfo, bool query_os_for_missing_info) | ||
{ | ||
sinsp_container_info container_info; | ||
|
||
if(docker::detect_docker(tinfo, container_info.m_id)) | ||
if(matches_runc_cgroups(tinfo, CRI_CGROUP_LAYOUT, container_info.m_id)) | ||
{ | ||
container_info.m_type = s_cri_runtime_type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we must be as specific as possible. i think we should report a container type being "CRI" only as a last resort.
userspace/libsinsp/filterchecks.cpp
Outdated
@@ -6151,6 +6151,9 @@ uint8_t* sinsp_filter_check_container::extract(sinsp_evt *evt, OUT uint32_t* len | |||
case sinsp_container_type::CT_CONTAINERD: | |||
m_tstr = "containerd"; | |||
break; | |||
case sinsp_container_type::CT_CRIO: | |||
m_tstr = "crio"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"cri-o"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (in both places). AFAIK this doesn't relate to the backend at all, just to container.type=cri-o
style filters and Lua chisels
userspace/libsinsp/chisel_api.cpp
Outdated
@@ -1193,6 +1193,10 @@ int lua_cbacks::get_container_table(lua_State *ls) | |||
{ | |||
lua_pushstring(ls, "containerd"); | |||
} | |||
else if(it->second.m_type == CT_CRIO) | |||
{ | |||
lua_pushstring(ls, "crio"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"cri-o"
@gnosek , i just saw your comment on how the container type is stringified in the backend. so if "cri-o" is not possible, then ignore my comments about renaming "crio". |
aa07d75
to
e4e4ae4
Compare
While containerd returns everything in the `info` dictionary, it's empty for CRIO, which means we need to get the IP address and the image ID from extra API calls. Unfortunately, it seems there's no way to get: * environment variables * privileged status * resource limits in any way from CRIO.
We set the container type in the calling method
CRI-O uses CRI so most of the work is done already but it differs in some important ways from containerd and has to be supported explicitly.