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 <[email protected]>
  • Loading branch information
tonistiigi committed Dec 5, 2023
1 parent 06c971f commit feaf181
Show file tree
Hide file tree
Showing 100 changed files with 23,588 additions and 47 deletions.
64 changes: 39 additions & 25 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,49 @@ RUN --mount=target=. --mount=target=/root/.cache,type=cache \
if [ "$(xx-info os)" = "linux" ]; then /usr/bin/buildkitd --version; fi
EOT

# 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 loopback host-local
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
COPY --link --from=cni-plugins /opt/cni/bin/firewall /buildkit-firewall

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 @@ -263,31 +302,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
6 changes: 6 additions & 0 deletions cmd/buildkitd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,12 @@ func setDefaultNetworkConfig(nc config.NetworkConfig) config.NetworkConfig {
if nc.CNIBinaryPath == "" {
nc.CNIBinaryPath = appdefaults.DefaultCNIBinDir
}
if nc.BridgeName == "" {
nc.BridgeName = "buildkit0"
}
if nc.BridgeSubnet == "" {
nc.BridgeSubnet = "10.10.0.0/16"
}
return nc
}

Expand Down
10 changes: 6 additions & 4 deletions cmd/buildkitd/main_containerd_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,12 @@ func containerdWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([
nc := netproviders.Opt{
Mode: common.config.Workers.Containerd.NetworkConfig.Mode,
CNI: cniprovider.Opt{
Root: common.config.Root,
ConfigPath: common.config.Workers.Containerd.CNIConfigPath,
BinaryDir: common.config.Workers.Containerd.CNIBinaryPath,
PoolSize: common.config.Workers.Containerd.CNIPoolSize,
Root: common.config.Root,
ConfigPath: common.config.Workers.Containerd.CNIConfigPath,
BinaryDir: common.config.Workers.Containerd.CNIBinaryPath,
PoolSize: common.config.Workers.Containerd.CNIPoolSize,
BridgeName: common.config.Workers.Containerd.BridgeName,
BridgeSubnet: common.config.Workers.Containerd.BridgeSubnet,
},
}

Expand Down
10 changes: 6 additions & 4 deletions cmd/buildkitd/main_oci_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,12 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker
nc := netproviders.Opt{
Mode: common.config.Workers.OCI.NetworkConfig.Mode,
CNI: cniprovider.Opt{
Root: common.config.Root,
ConfigPath: common.config.Workers.OCI.CNIConfigPath,
BinaryDir: common.config.Workers.OCI.CNIBinaryPath,
PoolSize: common.config.Workers.OCI.CNIPoolSize,
Root: common.config.Root,
ConfigPath: common.config.Workers.OCI.CNIConfigPath,
BinaryDir: common.config.Workers.OCI.CNIBinaryPath,
PoolSize: common.config.Workers.OCI.CNIPoolSize,
BridgeName: common.config.Workers.OCI.BridgeName,
BridgeSubnet: common.config.Workers.OCI.BridgeSubnet,
},
}

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.45.0
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.45.0
Expand Down Expand Up @@ -153,6 +154,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.3 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
golang.org/x/mod v0.11.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1238,8 +1238,13 @@ github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXST
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 @@ -1497,6 +1502,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
168 changes: 168 additions & 0 deletions util/network/cniprovider/bridge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//go:build linux
// +build linux

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"
firewallBinName := "firewall"
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)
}

firewallPath, err := exec.LookPath("buildkit-cni-firewall")
if err != nil {
break
}
var firewallDir string
firewallDir, firewallBinName = filepath.Split(firewallPath)
if firewallDir != bridgeDir && firewallDir != loopbackDir && firewallDir != hostLocalDir {
dirs = append(dirs, firewallDir)
}

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": "%s" }
]
]
}
},
{
"type": "%s",
"ingressPolicy": "same-bridge"
}
]
}`, loopbackBinName, bridgeBinName, opt.BridgeName, hostLocalBinName, opt.BridgeSubnet, firewallBinName))))

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)
}
Loading

0 comments on commit feaf181

Please sign in to comment.