Skip to content

Commit

Permalink
add CNI bridge network provider
Browse files Browse the repository at this point in the history
This adds a new network configuration based on CNI bridge.
Unlike the existing CNI provider this does not require
user to provide CNI configuration and plugins externally.

The minimal set of plugins required to use the network mode
is provided together with buildkit. Currently, bridge
mode is opt-in but intention is to make it default after
a testing period of one release cycle.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
  • Loading branch information
tonistiigi committed Oct 19, 2023
1 parent 8dfc926 commit 16c853b
Show file tree
Hide file tree
Showing 98 changed files with 23,523 additions and 36 deletions.
65 changes: 39 additions & 26 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ARG CONTAINERD_VERSION=v1.7.7
ARG CONTAINERD_ALT_VERSION_16=v1.6.24
ARG REGISTRY_VERSION=2.8.0
ARG ROOTLESSKIT_VERSION=v1.0.1
ARG CNI_VERSION=v1.2.0
ARG CNI_VERSION=v1.3.0
ARG STARGZ_SNAPSHOTTER_VERSION=v0.14.3
ARG NERDCTL_VERSION=v1.6.2
ARG DNSNAME_VERSION=v1.3.1
Expand Down Expand Up @@ -115,10 +115,48 @@ RUN --mount=target=. --mount=target=/root/.cache,type=cache \
xx-go build ${GOBUILDFLAGS} -gcflags="${GOGCFLAGS}" -ldflags "$(cat /tmp/.ldflags) -extldflags '-static'" -tags "osusergo netgo static_build seccomp ${BUILDKITD_TAGS}" -o /usr/bin/buildkitd ./cmd/buildkitd && \
xx-verify ${VERIFYFLAGS} /usr/bin/buildkitd

# dnsname source
FROM git AS dnsname-src
ARG DNSNAME_VERSION
WORKDIR /usr/src
RUN git clone https://github.com/containers/dnsname.git dnsname \
&& cd dnsname && git checkout -q "$DNSNAME_VERSION"

# build dnsname CNI plugin for testing
FROM gobuild-base AS dnsname
WORKDIR /go/src/github.com/containers/dnsname
ARG TARGETPLATFORM
RUN --mount=from=dnsname-src,src=/usr/src/dnsname,target=.,rw \
--mount=target=/root/.cache,type=cache \
CGO_ENABLED=0 xx-go build -o /usr/bin/dnsname ./plugins/meta/dnsname && \
xx-verify --static /usr/bin/dnsname

FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS cni-plugins
RUN apk add --no-cache curl
COPY --from=xx / /
ARG CNI_VERSION
ARG TARGETOS
ARG TARGETARCH
ARG TARGETPLATFORM
WORKDIR /opt/cni/bin
RUN curl -Ls https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-${TARGETOS}-${TARGETARCH}-${CNI_VERSION}.tgz | tar xzv
RUN xx-verify --static bridge
COPY --link --from=dnsname /usr/bin/dnsname /opt/cni/bin/

FROM scratch AS cni-plugins-export
COPY --link --from=cni-plugins /opt/cni/bin/bridge /buildkit-cni-bridge
COPY --link --from=cni-plugins /opt/cni/bin/loopback /buildkit-cni-loopback
COPY --link --from=cni-plugins /opt/cni/bin/host-local /buildkit-cni-host-local

FROM scratch AS cni-plugins-export-squashed
COPY --from=cni-plugins-export / /


FROM scratch AS binaries-linux
COPY --link --from=runc /usr/bin/runc /buildkit-runc
# built from https://github.com/tonistiigi/binfmt/releases/tag/buildkit%2Fv7.1.0-30
COPY --link --from=tonistiigi/binfmt:buildkit-v7.1.0-30@sha256:45dd57b4ba2f24e2354f71f1e4e51f073cb7a28fd848ce6f5f2a7701142a6bf0 / /
COPY --link --from=cni-plugins-export-squashed / /
COPY --link --from=buildctl /usr/bin/buildctl /
COPY --link --from=buildkitd /usr/bin/buildkitd /

Expand Down Expand Up @@ -253,31 +291,6 @@ FROM binaries AS buildkit-windows
# this is not in binaries-windows because it is not intended for release yet, just CI
COPY --link --from=buildkitd /usr/bin/buildkitd /buildkitd.exe

# dnsname source
FROM git AS dnsname-src
ARG DNSNAME_VERSION
WORKDIR /usr/src
RUN git clone https://github.com/containers/dnsname.git dnsname \
&& cd dnsname && git checkout -q "$DNSNAME_VERSION"

# build dnsname CNI plugin for testing
FROM gobuild-base AS dnsname
WORKDIR /go/src/github.com/containers/dnsname
ARG TARGETPLATFORM
RUN --mount=from=dnsname-src,src=/usr/src/dnsname,target=.,rw \
--mount=target=/root/.cache,type=cache \
CGO_ENABLED=0 xx-go build -o /usr/bin/dnsname ./plugins/meta/dnsname && \
xx-verify --static /usr/bin/dnsname

FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS cni-plugins
RUN apk add --no-cache curl
ARG CNI_VERSION
ARG TARGETOS
ARG TARGETARCH
WORKDIR /opt/cni/bin
RUN curl -Ls https://github.com/containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-$TARGETOS-$TARGETARCH-$CNI_VERSION.tgz | tar xzv
COPY --link --from=dnsname /usr/bin/dnsname /opt/cni/bin/

FROM buildkit-base AS integration-tests-base
ENV BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR="1000:1000"
RUN apk add --no-cache shadow shadow-uidmap sudo vim iptables ip6tables dnsmasq fuse curl git-daemon openssh-client \
Expand Down
2 changes: 2 additions & 0 deletions cmd/buildkitd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type NetworkConfig struct {
CNIConfigPath string `toml:"cniConfigPath"`
CNIBinaryPath string `toml:"cniBinaryPath"`
CNIPoolSize int `toml:"cniPoolSize"`
BridgeName string `toml:"bridgeName"`
// BridgeSubnet string `toml:"bridgeSubnet"`
}

type OCIConfig struct {
Expand Down
3 changes: 3 additions & 0 deletions cmd/buildkitd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,9 @@ func setDefaultNetworkConfig(nc config.NetworkConfig) config.NetworkConfig {
if nc.CNIBinaryPath == "" {
nc.CNIBinaryPath = appdefaults.DefaultCNIBinDir
}
if nc.BridgeName == "" {
nc.BridgeName = "buildkit0"
}
return nc
}

Expand Down
1 change: 1 addition & 0 deletions cmd/buildkitd/main_containerd_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([
ConfigPath: common.config.Workers.Containerd.CNIConfigPath,
BinaryDir: common.config.Workers.Containerd.CNIBinaryPath,
PoolSize: common.config.Workers.Containerd.CNIPoolSize,
BridgeName: common.config.Workers.Containerd.BridgeName,
},
}

Expand Down
1 change: 1 addition & 0 deletions cmd/buildkitd/main_oci_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
ConfigPath: common.config.Workers.OCI.CNIConfigPath,
BinaryDir: common.config.Workers.OCI.CNIBinaryPath,
PoolSize: common.config.Workers.OCI.CNIPoolSize,
BridgeName: common.config.Workers.OCI.BridgeName,
},
}

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ require (
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531
github.com/urfave/cli v1.22.12
github.com/vishvananda/netlink v1.2.1-beta.2
go.etcd.io/bbolt v1.3.7
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.40.0
Expand Down Expand Up @@ -152,6 +153,7 @@ require (
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/metric v0.37.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1241,8 +1241,13 @@ github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaW
github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
Expand Down Expand Up @@ -1507,6 +1512,7 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
2 changes: 1 addition & 1 deletion hack/shell
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ if [ -n "$MOUNT_BUILDKIT_SOURCE" ]; then
fi

set -x
docker run $SSH $volumes -it --privileged -v /tmp --net=host -e BUILDKIT_REGISTRY_MIRROR_DIR=/root/.cache/registry --rm $(cat $iidfile) ash
docker run $SSH $volumes -it --privileged -v /tmp -e BUILDKIT_REGISTRY_MIRROR_DIR=/root/.cache/registry --rm $(cat $iidfile) ash
150 changes: 150 additions & 0 deletions util/network/cniprovider/bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package cniprovider

import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"

cni "github.com/containerd/go-cni"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/network"
"github.com/pkg/errors"
"github.com/vishvananda/netlink"
)

func NewBridge(opt Opt) (network.Provider, error) {
cniOptions := []cni.Opt{cni.WithInterfacePrefix("eth")}
bridgeBinName := "bridge"
loopbackBinName := "loopback"
hostLocalBinName := "host-local"
var setup bool
// binaries shipping with buildkit
for {
var dirs []string

bridgePath, err := exec.LookPath("buildkit-cni-bridge")
if err != nil {
break
}
var bridgeDir string
bridgeDir, bridgeBinName = filepath.Split(bridgePath)
dirs = append(dirs, bridgeDir)

loopbackPath, err := exec.LookPath("buildkit-cni-loopback")
if err != nil {
break
}
var loopbackDir string
loopbackDir, loopbackBinName = filepath.Split(loopbackPath)
if loopbackDir != bridgeDir {
dirs = append(dirs, loopbackDir)
}

hostLocalPath, err := exec.LookPath("buildkit-cni-host-local")
if err != nil {
break
}
var hostLocalDir string
hostLocalDir, hostLocalBinName = filepath.Split(hostLocalPath)
if hostLocalDir != bridgeDir && hostLocalDir != loopbackDir {
dirs = append(dirs, hostLocalDir)
}

cniOptions = append(cniOptions, cni.WithPluginDir(dirs))
setup = true
break //nolint: staticcheck
}

if !setup {
fn := filepath.Join(opt.BinaryDir, "bridge")
if _, err := os.Stat(fn); err != nil {
return nil, errors.Wrapf(err, "failed to find CNI bridge %q or buildkit-cni-bridge", fn)
}

cniOptions = append(cniOptions, cni.WithPluginDir([]string{opt.BinaryDir}))
}

cniOptions = append(cniOptions, cni.WithConfListBytes([]byte(fmt.Sprintf(`{
"cniVersion": "1.0.0",
"name": "buildkit",
"plugins": [
{
"type": "%s"
},
{
"type": "%s",
"bridge": "%s",
"isDefaultGateway": true,
"ipMasq": true,
"ipam": {
"type": "%s",
"ranges": [
[
{ "subnet": "10.10.0.0/16" }
]
]
}
}
]
}`, loopbackBinName, bridgeBinName, opt.BridgeName, hostLocalBinName))))

unlock, err := initLock()
if err != nil {
return nil, err
}
defer unlock()

createBridge := true
if _, err := bridgeByName(opt.BridgeName); err == nil {
createBridge = false
}

cniHandle, err := cni.New(cniOptions...)
if err != nil {
return nil, err
}
cp := &cniProvider{
CNI: cniHandle,
root: opt.Root,
}

if createBridge {
cp.release = func() error {
if err := removeBridge(opt.BridgeName); err != nil {
bklog.L.Errorf("failed to remove bridge %q: %v", opt.BridgeName, err)
}
return nil
}
}

cleanOldNamespaces(cp)

cp.nsPool = &cniPool{targetSize: opt.PoolSize, provider: cp}
if err := cp.initNetwork(false); err != nil {
return nil, err
}
go cp.nsPool.fillPool(context.TODO())
return cp, nil
}

func bridgeByName(name string) (*netlink.Bridge, error) {
l, err := netlink.LinkByName(name)
if err != nil {
return nil, errors.Wrapf(err, "could not lookup %q", name)
}
br, ok := l.(*netlink.Bridge)
if !ok {
return nil, errors.Errorf("%q already exists but is not a bridge", name)
}
return br, nil
}

func removeBridge(name string) error {
br, err := bridgeByName(name)
if err != nil {
return err
}
return netlink.LinkDel(br)
}
32 changes: 24 additions & 8 deletions util/network/cniprovider/cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Opt struct {
ConfigPath string
BinaryDir string
PoolSize int
BridgeName string
}

func New(opt Opt) (network.Provider, error) {
Expand Down Expand Up @@ -61,7 +62,7 @@ func New(opt Opt) (network.Provider, error) {
cleanOldNamespaces(cp)

cp.nsPool = &cniPool{targetSize: opt.PoolSize, provider: cp}
if err := cp.initNetwork(); err != nil {
if err := cp.initNetwork(true); err != nil {
return nil, err
}
go cp.nsPool.fillPool(context.TODO())
Expand All @@ -70,17 +71,18 @@ func New(opt Opt) (network.Provider, error) {

type cniProvider struct {
cni.CNI
root string
nsPool *cniPool
root string
nsPool *cniPool
release func() error
}

func (c *cniProvider) initNetwork() error {
if v := os.Getenv("BUILDKIT_CNI_INIT_LOCK_PATH"); v != "" {
l := flock.New(v)
if err := l.Lock(); err != nil {
func (c *cniProvider) initNetwork(lock bool) error {
if lock {
unlock, err := initLock()
if err != nil {
return err
}
defer l.Unlock()
defer unlock()
}
ns, err := c.New(context.TODO(), "")
if err != nil {
Expand All @@ -91,9 +93,23 @@ func (c *cniProvider) initNetwork() error {

func (c *cniProvider) Close() error {
c.nsPool.close()
if c.release != nil {
return c.release()
}
return nil
}

func initLock() (func() error, error) {
if v := os.Getenv("BUILDKIT_CNI_INIT_LOCK_PATH"); v != "" {
l := flock.New(v)
if err := l.Lock(); err != nil {
return nil, err
}
return l.Unlock, nil
}
return func() error { return nil }, nil
}

type cniPool struct {
provider *cniProvider
mu sync.Mutex
Expand Down
Loading

0 comments on commit 16c853b

Please sign in to comment.