Files
weave-scope/Makefile
Iago López Galeiras 9920c4ea48 Add eBPF connection tracking without dependencies on kernel headers
Based on work from Lorenzo, updated by Iago, Alban, Alessandro and
Michael.

This PR adds connection tracking using eBPF. This feature is not enabled by default.
For now, you can enable it by launching scope with the following command:

```
sudo ./scope launch --probe.ebpf.connections=true
```

This patch allows scope to get notified of every connection event,
without relying on the parsing of /proc/$pid/net/tcp{,6} and
/proc/$pid/fd/*, and therefore improve performance.

We vendor https://github.com/iovisor/gobpf in Scope to load the
pre-compiled ebpf program and https://github.com/weaveworks/tcptracer-bpf
to guess the offsets of the structures we need in the kernel. In this
way we don't need a different pre-compiled ebpf object file per kernel.
The pre-compiled ebpf program is included in the vendoring of
tcptracer-bpf.

The ebpf program uses kprobes/kretprobes on the following kernel functions:
- tcp_v4_connect
- tcp_v6_connect
- tcp_set_state
- inet_csk_accept
- tcp_close

It generates "connect", "accept" and "close" events containing the
connection tuple but also pid and netns.
Note: the IPv6 events are not supported in Scope and thus not passed on.

probe/endpoint/ebpf.go maintains the list of connections. Similarly to
conntrack, it also keeps the dead connections for one iteration in order
to report short-lived connections.

The code for parsing /proc/$pid/net/tcp{,6} and /proc/$pid/fd/* is still
there and still used at start-up because eBPF only brings us the events
and not the initial state. However, the /proc parsing for the initial
state is now done in foreground instead of background, via
newForegroundReader().

NAT resolution on connections from eBPF works in the same way as it did
on connections from /proc: by using conntrack. One of the two conntrack
instances is only started to get the initial state and then it is
stopped since eBPF detects short-lived connections.

The Scope Docker image size comparison:
- weaveworks/scope in current master:  22 MB (compressed),  68 MB
  (uncompressed)
- weaveworks/scope with this patchset: 23 MB (compressed), 69 MB
  (uncompressed)

Fixes #1168 (walking /proc to obtain connections is very expensive)

Fixes #1260 (Short-lived connections not tracked for containers in
shared networking namespaces)

Fixes #1962 (Port ebpf tracker to Go)

Fixes #1961 (Remove runtime kernel header dependency from ebpf tracker)
2017-03-08 22:11:12 +01:00

226 lines
8.7 KiB
Makefile

.PHONY: all deps static clean client-lint client-test client-sync backend frontend shell lint ui-upload
# If you can use Docker without being root, you can `make SUDO= <target>`
SUDO=$(shell docker info >/dev/null 2>&1 || echo "sudo -E")
DOCKERHUB_USER=weaveworks
SCOPE_EXE=prog/scope
SCOPE_EXPORT=scope.tar
CLOUD_AGENT_EXPORT=cloud-agent.tar
SCOPE_UI_BUILD_IMAGE=$(DOCKERHUB_USER)/scope-ui-build
SCOPE_UI_BUILD_UPTODATE=.scope_ui_build.uptodate
SCOPE_BACKEND_BUILD_IMAGE=$(DOCKERHUB_USER)/scope-backend-build
SCOPE_BACKEND_BUILD_UPTODATE=.scope_backend_build.uptodate
SCOPE_VERSION=$(shell git rev-parse --short HEAD)
WEAVENET_VERSION=1.9.0
DOCKER_VERSION=1.13.1
DOCKER_DISTRIB=.pkg/docker-$(DOCKER_VERSION).tgz
DOCKER_DISTRIB_URL=https://get.docker.com/builds/Linux/x86_64/docker-$(DOCKER_VERSION).tgz
RUNSVINIT=vendor/runsvinit/runsvinit
CODECGEN_DIR=vendor/github.com/ugorji/go/codec/codecgen
CODECGEN_EXE=$(CODECGEN_DIR)/bin/codecgen_$(shell go env GOHOSTOS)_$(shell go env GOHOSTARCH)
GET_CODECGEN_DEPS=$(shell find $(1) -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' -not -name '*.codecgen.go' -not -name '*.generated.go')
CODECGEN_TARGETS=report/report.codecgen.go render/detailed/detailed.codecgen.go
RM=--rm
RUN_FLAGS=-ti
BUILD_IN_CONTAINER=true
GO_ENV=GOGC=off
GO_BUILD_INSTALL_DEPS=-i
GO_BUILD_TAGS='netgo unsafe'
GO_BUILD_FLAGS=$(GO_BUILD_INSTALL_DEPS) -ldflags "-extldflags \"-static\" -X main.version=$(SCOPE_VERSION) -s -w" -tags $(GO_BUILD_TAGS)
GOOS=$(shell go tool dist env | grep GOOS | sed -e 's/GOOS="\(.*\)"/\1/')
ifeq ($(GOOS),linux)
GO_ENV+=CGO_ENABLED=1
endif
ifeq ($(GOARCH),arm)
ARM_CC=CC=/usr/bin/arm-linux-gnueabihf-gcc
endif
GO=env $(GO_ENV) $(ARM_CC) go
NO_CROSS_COMP=unset GOOS GOARCH
GO_HOST=$(NO_CROSS_COMP); env $(GO_ENV) go
WITH_GO_HOST_ENV=$(NO_CROSS_COMP); $(GO_ENV)
IMAGE_TAG=$(shell ./tools/image-tag)
all: $(SCOPE_EXPORT)
$(DOCKER_DISTRIB):
curl -o $(DOCKER_DISTRIB) $(DOCKER_DISTRIB_URL)
docker/weave:
curl -L https://github.com/weaveworks/weave/releases/download/v$(WEAVENET_VERSION)/weave -o docker/weave
chmod u+x docker/weave
docker/weaveutil:
$(SUDO) docker run --rm --entrypoint=cat weaveworks/weaveexec:$(WEAVENET_VERSION) /usr/bin/weaveutil > $@
chmod +x $@
docker/%: %
cp $* docker/
docker/docker: $(DOCKER_DISTRIB)
tar -xvzf $(DOCKER_DISTRIB) docker/docker
%.tar: docker/Dockerfile.%
$(SUDO) docker build -t $(DOCKERHUB_USER)/$* -f $< docker/
$(SUDO) docker tag $(DOCKERHUB_USER)/$* $(DOCKERHUB_USER)/$*:$(IMAGE_TAG)
$(SUDO) docker save $(DOCKERHUB_USER)/$*:latest > $@
$(CLOUD_AGENT_EXPORT): docker/Dockerfile.cloud-agent docker/$(SCOPE_EXE) docker/docker docker/weave docker/weaveutil
$(SCOPE_EXPORT): docker/Dockerfile.scope $(CLOUD_AGENT_EXPORT) docker/$(RUNSVINIT) docker/demo.json docker/run-app docker/run-probe docker/entrypoint.sh
$(RUNSVINIT): vendor/runsvinit/*.go
$(SCOPE_EXE): $(shell find ./ -path ./vendor -prune -o -type f -name *.go) prog/staticui/staticui.go prog/externalui/externalui.go $(CODECGEN_TARGETS)
report/report.codecgen.go: $(call GET_CODECGEN_DEPS,report/)
render/render.codecgen.go: $(call GET_CODECGEN_DEPS,render/)
render/detailed/detailed.codecgen.go: $(call GET_CODECGEN_DEPS,render/detailed/)
static: prog/staticui/staticui.go prog/externalui/externalui.go
prog/staticui/staticui.go: client/build/index.html
prog/externalui/externalui.go: client/build-external/index.html
ifeq ($(BUILD_IN_CONTAINER),true)
$(SCOPE_EXE) $(RUNSVINIT) lint tests shell prog/staticui/staticui.go prog/externalui/externalui.go: $(SCOPE_BACKEND_BUILD_UPTODATE)
@mkdir -p $(shell pwd)/.pkg
$(SUDO) docker run $(RM) $(RUN_FLAGS) \
-v $(shell pwd):/go/src/github.com/weaveworks/scope \
-v $(shell pwd)/.pkg:/go/pkg \
--net=host \
-e GOARCH -e GOOS -e CIRCLECI -e CIRCLE_BUILD_NUM -e CIRCLE_NODE_TOTAL \
-e CIRCLE_NODE_INDEX -e COVERDIR -e SLOW -e TESTDIRS \
$(SCOPE_BACKEND_BUILD_IMAGE) SCOPE_VERSION=$(SCOPE_VERSION) GO_BUILD_INSTALL_DEPS=$(GO_BUILD_INSTALL_DEPS) $@
else
$(SCOPE_EXE): $(SCOPE_BACKEND_BUILD_UPTODATE)
time $(GO) build $(GO_BUILD_FLAGS) -o $@ ./$(@D)
@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
rm $@; \
echo "\nYour go standard library was built without the 'netgo' build tag."; \
echo "To fix that, run"; \
echo " sudo go clean -i net"; \
echo " sudo go install -tags netgo std"; \
false; \
}
%.codecgen.go: $(CODECGEN_EXE)
rm -f $@; $(GO_HOST) build $(GO_BUILD_FLAGS) ./$(@D) # workaround for https://github.com/ugorji/go/issues/145
cd $(@D) && $(WITH_GO_HOST_ENV) $(shell pwd)/$(CODECGEN_EXE) -rt $(GO_BUILD_TAGS) -u -o $(@F) $(notdir $(call GET_CODECGEN_DEPS,$(@D)))
$(CODECGEN_EXE): $(CODECGEN_DIR)/*.go
mkdir -p $(@D)
$(GO_HOST) build $(GO_BUILD_FLAGS) -o $@ ./$(CODECGEN_DIR)
$(RUNSVINIT): $(SCOPE_BACKEND_BUILD_UPTODATE)
time $(GO) build $(GO_BUILD_FLAGS) -o $@ ./$(@D)
shell: $(SCOPE_BACKEND_BUILD_UPTODATE)
/bin/bash
tests: $(SCOPE_BACKEND_BUILD_UPTODATE)
./tools/test -no-go-get
lint: $(SCOPE_BACKEND_BUILD_UPTODATE)
./tools/lint -ignorespelling "agre " -ignorespelling "AGRE " .
./tools/shell-lint tools
prog/staticui/staticui.go: $(SCOPE_BACKEND_BUILD_UPTODATE)
mkdir -p prog/staticui
esc -o $@ -pkg staticui -prefix client/build client/build
prog/externalui/externalui.go: $(SCOPE_BACKEND_BUILD_UPTODATE)
mkdir -p prog/externalui
esc -o $@ -pkg externalui -prefix client/build-external -include '\.html$$' client/build-external
endif
ifeq ($(BUILD_IN_CONTAINER),true)
client/build/index.html: $(shell find client/app -type f) $(SCOPE_UI_BUILD_UPTODATE)
mkdir -p client/build
$(SUDO) docker run $(RM) $(RUN_FLAGS) -v $(shell pwd)/client/app:/home/weave/app \
-v $(shell pwd)/client/build:/home/weave/build \
$(SCOPE_UI_BUILD_IMAGE) npm run build
client/build-external/index.html: $(shell find client/app -type f) $(SCOPE_UI_BUILD_UPTODATE)
mkdir -p client/build
$(SUDO) docker run $(RM) $(RUN_FLAGS) -v $(shell pwd)/client/app:/home/weave/app \
-v $(shell pwd)/client/build-external:/home/weave/build-external \
$(SCOPE_UI_BUILD_IMAGE) npm run build-external
client-test: $(shell find client/app/scripts -type f) $(SCOPE_UI_BUILD_UPTODATE)
$(SUDO) docker run $(RM) $(RUN_FLAGS) -v $(shell pwd)/client/app:/home/weave/app \
-v $(shell pwd)/client/test:/home/weave/test \
$(SCOPE_UI_BUILD_IMAGE) npm test
client-lint: $(SCOPE_UI_BUILD_UPTODATE)
$(SUDO) docker run $(RM) $(RUN_FLAGS) -v $(shell pwd)/client/app:/home/weave/app \
-v $(shell pwd)/client/test:/home/weave/test \
$(SCOPE_UI_BUILD_IMAGE) npm run lint
client-start: $(SCOPE_UI_BUILD_UPTODATE)
$(SUDO) docker run $(RM) $(RUN_FLAGS) --net=host -v $(shell pwd)/client/app:/home/weave/app \
-v $(shell pwd)/client/build:/home/weave/build -e WEBPACK_SERVER_HOST \
$(SCOPE_UI_BUILD_IMAGE) npm start
tmp/weave-scope.tgz: $(shell find client/app -type f) $(SCOPE_UI_BUILD_UPTODATE)
$(sudo) docker run $(RUN_FLAGS) \
-v $(shell pwd)/client/app:/home/weave/app \
-v $(shell pwd)/tmp:/home/weave/tmp \
$(SCOPE_UI_BUILD_IMAGE) \
npm run bundle
else
client/build/index.html:
cd client && npm run build
client/build-external/index.html:
cd client && npm run build-external
endif
$(SCOPE_UI_BUILD_UPTODATE): client/Dockerfile client/package.json client/webpack.local.config.js client/webpack.production.config.js client/server.js client/.eslintrc
$(SUDO) docker build -t $(SCOPE_UI_BUILD_IMAGE) client
touch $@
$(SCOPE_BACKEND_BUILD_UPTODATE): backend/*
$(SUDO) docker build -t $(SCOPE_BACKEND_BUILD_IMAGE) backend
touch $@
ui-upload: client/build-external/index.html
AWS_ACCESS_KEY_ID=$$UI_BUCKET_KEY_ID \
AWS_SECRET_ACCESS_KEY=$$UI_BUCKET_KEY_SECRET \
aws s3 cp client/build-external/ s3://static.weave.works/scope-ui/ --recursive --exclude '*.html'
ui-pkg-upload: tmp/weave-scope.tgz
AWS_ACCESS_KEY_ID=$$UI_BUCKET_KEY_ID \
AWS_SECRET_ACCESS_KEY=$$UI_BUCKET_KEY_SECRET \
aws s3 cp tmp/weave-scope.tgz s3://weaveworks-js-modules/weave-scope/weave-scope.tgz
clean:
$(GO) clean ./...
# Don't actually rmi the build images - rm'ing the .uptodate files is enough to ensure
# we rebuild the images, and rmi'ing the images causes us to have to redownload a lot of stuff.
# $(SUDO) docker rmi $(SCOPE_UI_BUILD_IMAGE) $(SCOPE_BACKEND_BUILD_IMAGE) >/dev/null 2>&1 || true
rm -rf $(SCOPE_EXPORT) $(SCOPE_UI_BUILD_UPTODATE) $(SCOPE_BACKEND_BUILD_UPTODATE) \
$(SCOPE_EXE) $(RUNSVINIT) prog/staticui/staticui.go prog/externalui/externalui.go client/build/*.js client/build-external/*.js docker/weave .pkg \
$(CODECGEN_TARGETS) $(CODECGEN_DIR)/bin
clean-codecgen:
rm -rf $(CODECGEN_TARGETS) $(CODECGEN_DIR)/bin
# Dependencies are intentionally build without enforcing any tags
# since they are build on the host
deps:
$(GO) get -u -f \
github.com/FiloSottile/gvt \
github.com/mattn/goveralls \
github.com/weaveworks/github-release \
github.com/2opremio/trifles/wscat