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

Add support for docker autodiscover to monitor containers on host network #6708

Merged
merged 2 commits into from
Jun 20, 2018

Conversation

vjsamuel
Copy link
Contributor

@vjsamuel vjsamuel commented Mar 30, 2018

In today's approach autodiscover docker provider can not monitor containers running on Host network. If a container runs on host network it would not have .NetworkSettings.IPAddress as seen below:

# docker inspect --format '{{ .NetworkSettings.IPAddress }}' jobmanager #runs as --net=host

# docker inspect --format '{{ .NetworkSettings.IPAddress }}' filebeat #normal container
172.17.0.2

To be able to monitor these containers we detect a host address that can be used be the docker auto discover as follows:

  • If filebeat/metricbeat runs as a container, try to check with the the Docker daemon if the hostname is the prefix of one of the running containers. If yes, get the Gateway of the network interface. That will be the host address that can be used.
  • If filebeat/metricbeat are running as processes on the host, get an IP address on the host that is not a loop back address and use them to monitor.

@elasticmachine
Copy link
Collaborator

Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually?


// GetHostIp can look at the hostname and give the container gateway/host IP based on where the given
// beat is running.
func GetHostIp(watcher Watcher) string {

Choose a reason for hiding this comment

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

func GetHostIp should be GetHostIP

@@ -65,13 +66,16 @@ func AutodiscoverBuilder(bus bus.Bus, c *common.Config) (autodiscover.Provider,
return nil, err
}

hostIp := docker.GetHostIp(watcher)

Choose a reason for hiding this comment

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

var hostIp should be hostIP

@@ -24,6 +24,7 @@ type Provider struct {
appenders autodiscover.Appenders
watcher docker.Watcher
templates *template.Mapper
hostIp string

Choose a reason for hiding this comment

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

struct field hostIp should be hostIP

@vjsamuel vjsamuel force-pushed the add_host_network_support branch 2 times, most recently from 53cb8cf to 3c1afca Compare March 30, 2018 06:34
@ruflin
Copy link
Member

ruflin commented Mar 30, 2018

As we already had this feature in 6.2, could you add a CHANLOGE entry? As far as I understand the change, it's an addition / enhancement and will not break anything existant.

For the data.host change: This change is only indirectly related to the previous one. So probably worth 2 changelog entries?

@ruflin
Copy link
Member

ruflin commented Mar 30, 2018

jenkins, test it

@vjsamuel
Copy link
Contributor Author

@ruflin i think it is necessary to break this PR into 2. Otherwise you wont be able to back port this fix to 6.2. Thoughts?

@ruflin
Copy link
Member

ruflin commented Mar 30, 2018

I'm always up for 2 PR's if they can be separated, especially if one of them needs backport.

@vjsamuel vjsamuel force-pushed the add_host_network_support branch 2 times, most recently from a668182 to 9b02d7d Compare April 2, 2018 00:11
@vjsamuel
Copy link
Contributor Author

vjsamuel commented Apr 2, 2018

@ruflin this PR has to be back ported to 6.2 and I have pulled out the hints builder changes to a separate PR[https://github.com//pull/6727] to allow the same.

@ruflin ruflin added the needs_backport PR is waiting to be backported to other branches. label Apr 3, 2018
Copy link
Member

@ruflin ruflin left a comment

Choose a reason for hiding this comment

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

LGTM. @jsoriano would you mind having a look?

@ruflin
Copy link
Member

ruflin commented Apr 3, 2018

jenkins, test it

// Gateway address of a container is one of the interfaces on the host that can be used to connect to
// containers that are being run on host network.
for cid, container := range watcher.Containers() {
if strings.Index(cid, hostname) == 0 && container != nil {
Copy link
Member

Choose a reason for hiding this comment

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

In docker inspect the hostname is available under Config, I guess that it is also in the API, could we store it when requesting containers information so we don't need to assume anything? 🙂

for _, address := range addrs {
// Check the address type and if it is not a loop back then return it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Any reason to limit to IPv4 only?


if net.Gateway != "" {
gateways = append(gateways, net.Gateway)
}
Copy link
Member

Choose a reason for hiding this comment

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

Here, when requesting information from the daemon we could also get the Hostname, at least if we use inspect, not sure with list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we wont get a hostname for a container running on host network.

Copy link
Member

Choose a reason for hiding this comment

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

A container running in host network gets the hostname of the host.

return getLocalIP()
}

func getLocalIP() string {
Copy link
Member

Choose a reason for hiding this comment

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

This method is a bit tricky, are we sure that net.InterfaceAddrs() always returns the addresses in the same order? what happens if an address is added or removed?

I think there is no safe method to guess the address of a host with multiple addresses 😐 maybe for that case we should allow to configure a host address in metricbeat (e.g. sensu has the address setting, and kubelet has the --node-ip flag), and take it from there @ruflin wdyt?

Copy link
Member

Choose a reason for hiding this comment

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

I like the idea of making it configurable. We can try to guess by default but we allow the user to overwrite it if the guessing is wrong. Normally I would suggest to go the other way and have the configuration by default and require the user to enable the magic part but as this is part of auto discovery the user expects no configuration and magic I would say.

Copy link
Member

Choose a reason for hiding this comment

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

I talked with @vjsamuel offline and sory, I had misunderstood something, as the IP is only required to connect with the host namespace then yes, any IP of the host is equally valid :)

@vjsamuel vjsamuel force-pushed the add_host_network_support branch 2 times, most recently from e9775f1 to 04bd3ed Compare April 5, 2018 21:36
@ruflin
Copy link
Member

ruflin commented Apr 6, 2018

jenkins, test it

if len(ipaddresses) == 0 {
info, err := w.client.ContainerInspect(w.ctx, c.ID)
if err == nil {
ipaddresses = append(ipaddresses, info.Config.Hostname)
Copy link
Member

Choose a reason for hiding this comment

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

metricbeat running in docker will only be able to resolve the hostname of the host if it is in the DNS or if the host exists in /etc/hosts in the container, what is not always necessarily true.

I think we should check here if metricbeat is running in docker (or in a network namespace).
If it is, then we use its gateway, that should be an IP in the host, as you proposed in a previous version, but now we are sure of knowing the hostname.
If it isn't running as docker it is also running in host mode and localhost or a local IP can be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i checked this behavior and it seems to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

neither approach work (using hostname and gateway ip) on mac however

@ruflin
Copy link
Member

ruflin commented Apr 10, 2018

@jsoriano @vjsamuel Can you push this one forward together? @exekias FYI

@exekias
Copy link
Contributor

exekias commented Apr 10, 2018

I'm ok with this change if we don't find something more reliable. I was planning to do some research on this and try to come up with some alternative way to retrieve host IP, not sure it's possible though :)

@exekias
Copy link
Contributor

exekias commented Apr 10, 2018

I've been researching this with @vjsamuel, it seems we can get host IP by checking the default gateway:

root@85ae06759fea:/# netstat -nr | grep '^0\.0\.0\.0' | awk '{print $2}'
172.17.0.1

It should be possible to read it from proc filesystem, I guess this will support more use cases, still to be tested

@vjsamuel
Copy link
Contributor Author

@exekias i was revisiting the code today, i feel that this might be the cleanest solution code-wise. what do you think? if you are not convinced, i can make the above discussed change

Copy link
Contributor

@exekias exekias left a comment

Choose a reason for hiding this comment

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

LGTM @vjsamuel, this already improves the situation. I left some comments

@@ -161,6 +161,9 @@ https://github.com/elastic/beats/compare/v6.2.3...master[Check the HEAD diff]
- Add experimental Jolokia Discovery autodiscover provider. {pull}7141[7141]
- Add owner object info to Kubernetes metadata. {pull}7231[7231]
- Add beat export dashboard command. {pull}7239[7239]
- Add a default seccomp (secure computing) filter on Linux that prohibits
execve, execveat, fork, and vfork syscalls. A custom policy can be configured. {issue}5213[5213]
- Add support for docker autodiscover to monitor containers on host network {pull}6708[6708]
Copy link
Contributor

Choose a reason for hiding this comment

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

some entries sneaked in here

if err == nil {
ipaddresses = append(ipaddresses, info.Config.Hostname)
} else {
logp.Debug("docker", "unable to inspect container %s due to error %v", c.ID, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

this should probably be a warning message? as it should not happen frequently, and can cause some debugging nightmares

Copy link
Contributor

@exekias exekias left a comment

Choose a reason for hiding this comment

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

WFG

@exekias
Copy link
Contributor

exekias commented Jun 20, 2018

jenkins, test this please

@exekias exekias merged commit 443c39d into elastic:master Jun 20, 2018
@vjsamuel vjsamuel deleted the add_host_network_support branch June 20, 2018 23:12
@exekias exekias removed the needs_backport PR is waiting to be backported to other branches. label Jun 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants