Bazel Remote Output Service¶
Bazel since 7.2.0 supports a remote output service. A remote output service implements a virtual filesystem for bazel-out/
with FUSE on Linux and NFS on macOS. The output service shows outputs of remote actions as if they were built locally but only fetches their contents from the server if they are read. This is an alternative to Builds Without the Bytes.
Using the bb-clientd remote output service with EngFlow¶
bb-clientd
provides an implementation of the remote output service for Linux and macOS that is compatible with EngFlow.
- Download a
bb_clientd
binary. Click the most recent workflow run frombb-clientd
’s GitHub actions and download an artifact matching your operating system and architecture. - Create a
bb-clientd
configuration file,bb_client.jsonnet
. It is written in the Jsonnet configuration language. Here is a simple example, whereexample.cluster.engflow.com
,PATH_TO_ENGFLOW_TLS_CERTIFICATE
, andPATH_TO_ENGFLOW_TLS_PRIVATE_KEY
need to be replaced with the appropriate values for your cluster:Jsonnet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
local blobstoreConfig = { grpc: { address: 'example.cluster.engflow.com:443', tls: { clientKeyPair: { files: { certificatePath: 'PATH_TO_ENGFLOW_TLS_CERTIFICATE', privateKeyPath: 'PATH_TO_ENGFLOW_TLS_PRIVATE_KEY', refreshInterval: '10000s', }, }, }, keepalive: { time: '60s', timeout: '30s', }, }, }; local homeDirectory = std.extVar('HOME'); local cacheDirectory = homeDirectory + '/.cache/bb_clientd'; { casKeyLocationMapSizeBytes:: 512 * 1024 * 1024, casBlocksSizeBytes:: 100 * 1024 * 1024 * 1024, filePoolSizeBytes:: 100 * 1024 * 1024 * 1024, maximumMessageSizeBytes: 16 * 1024 * 1024, maximumTreeSizeBytes: 256 * 1024 * 1024, useNFSv4:: std.extVar('OS') == 'Darwin', blobstore: { actionCache: blobstoreConfig, contentAddressableStorage: { withLabels: { backend: { demultiplexing: { instanceNamePrefixes: { 'local': { backend: { label: 'localCAS' } }, '': { backend: { readCaching: { slow: { existenceCaching: { backend: { label: 'clustersCAS' }, // Assume that if FindMissingBlobs() reports a blob as being // present, it's going to stay around for five more minutes. // This significantly reduces the combined size of // FindMissingBlobs() calls generated by Bazel. existenceCache: { cacheSize: 1000 * 1000, cacheDuration: '300s', cacheReplacementPolicy: 'LEAST_RECENTLY_USED', }, }, }, // On-disk cache to speed up access to recently used objects. fast: { label: 'localCAS' }, replicator: { deduplicating: { // Bazel's -j flag not only affects the number of actions // executed concurrently, it also influences the concurrency // of ByteStream requests. Prevent starvation by limiting // the number of requests that are forwarded when cache // misses occur. concurrencyLimiting: { base: { 'local': {} }, maximumConcurrency: 100, }, }, }, } } }, } } }, labels: { // Let the local CAS consume up to 100 GiB of disk space. A 64 // MiB index is large enough to accommodate approximately one // million objects. localCAS: { 'local': { keyLocationMapOnBlockDevice: { file: { path: cacheDirectory + '/cas/key_location_map', sizeBytes: $.casKeyLocationMapSizeBytes, } }, keyLocationMapMaximumGetAttempts: 8, keyLocationMapMaximumPutAttempts: 32, oldBlocks: 1, currentBlocks: 5, newBlocks: 1, blocksOnBlockDevice: { source: { file: { path: cacheDirectory + '/cas/blocks', sizeBytes: $.casBlocksSizeBytes, } }, spareBlocks: 1, dataIntegrityValidationCache: { cacheSize: 100000, cacheDuration: '14400s', cacheReplacementPolicy: 'LEAST_RECENTLY_USED', }, }, persistent: { stateDirectoryPath: cacheDirectory + '/cas/persistent_state', minimumEpochInterval: '300s', }, } }, clustersCAS: blobstoreConfig, }, } }, }, grpcServers: [{ listenPaths: [cacheDirectory + '/grpc'], authenticationPolicy: { allow: {} }, maximumReceivedMessageSizeBytes: 16 * 1024 * 1024, }], mount: if $.useNFSv4 then { mountPath: homeDirectory + '/bb_clientd', nfsv4: { enforcedLeaseTime: '120s', announcedLeaseTime: '60s', darwin: { socketPath: cacheDirectory + '/nfsv4' }, }, } else { mountPath: homeDirectory + '/bb_clientd', fuse: { directoryEntryValidity: '300s', inodeAttributeValidity: '300s', }, }, filePool: { blockDevice: { file: { path: cacheDirectory + '/filepool', sizeBytes: $.filePoolSizeBytes, } }, }, outputPathPersistency: { stateDirectoryPath: cacheDirectory + '/outputs', maximumStateFileSizeBytes: 1024 * 1024 * 1024, maximumStateFileAge: '604800s', }, directoryCache: { maximumCount: 10000, maximumSizeBytes: 1024 * self.maximumCount, cacheReplacementPolicy: 'LEAST_RECENTLY_USED', }, maximumFileSystemRetryDelay: '300s', global: { logPaths: [cacheDirectory + '/log'], }, }
- Run the
bb_clientd
daemon: - Run a build with Bazel using the remote output service:
Bash
Note it may be necessary to run umount ~/bb_clientd
to rerun bb_clientd
after the daemon exits.