diff --git a/README.md b/README.md index 3dcea6a..aa04ffc 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,34 @@ and it will run the build step on all versions that starts with 6.0. The only other optional usefull argument is `--cpu=N` and it will set how many paralell processes will be used. By default you will use n - 1 number of cpu cores that is available on your system. Commands like pull and push aare not very cpu intensive so using a higher number here might speed things up if you have good network bandwidth. +## Multi-architecture Support (ARM64/x86) + +The build system now supports multi-architecture builds using Docker Buildx. You can build images for both AMD64 and ARM64 platforms: + +### Platform Options + +- `amd64` - Build for AMD64/x86_64 only +- `arm64` - Build for ARM64 only +- `both` or `multi` - Build for both platforms (default) + +### Building for ARM64 + +```bash +# Build for ARM64 only +invoke build 7.2.5 --platforms=arm64 + +# Build for both AMD64 and ARM64 (default) +invoke build 7.2.5 --platforms=both + +# Push multi-arch images +invoke push 7.2.5 --platforms=both + +# Use the dedicated buildx command +invoke buildx 7.2.5 --platforms=arm64 +``` + +The `buildx` command provides additional control over multi-architecture builds and uses Docker Buildx builder instances for cross-platform compilation. + ## Makefile (legacy) diff --git a/tasks.py b/tasks.py index a318792..409cad3 100644 --- a/tasks.py +++ b/tasks.py @@ -7,6 +7,23 @@ latest_version_string = "7.2.5" +# Platform mapping for simplified platform arguments +platform_mapping = { + "amd64": "linux/amd64", + "arm64": "linux/arm64", + "both": "linux/amd64,linux/arm64", + "multi": "linux/amd64,linux/arm64" +} + +def get_platforms(platform_arg): + """ + Convert simplified platform argument to full platform string + """ + if platform_arg in platform_mapping: + return platform_mapping[platform_arg] + # If it's already a full platform string, return as-is + return platform_arg + # Unpublished versions version_config_mapping = [] version_config_mapping += [f"3.0.{i}" for i in range(0, 8)] @@ -72,19 +89,23 @@ def _docker_build(config): """ Internal multiprocess method to run docker build command """ - c, version = config - print(f" -- Starting docker build for version : {version}") - build_command = f"docker build --build-arg redis_version={version} -t grokzen/redis-cluster:{version} ." + c, version, platforms = config + print(f" -- Starting docker build for version : {version} on platforms: {platforms}") + # Create buildx builder if it doesn't exist + c.run("docker buildx create --use --name redis-cluster-builder || docker buildx use redis-cluster-builder", warn=True) + build_command = f"docker buildx build --platform={platforms} --build-arg redis_version={version} -t grokzen/redis-cluster:{version} ." c.run(build_command) def _docker_push(config): """ - Internal multiprocess method to run docker push command + Internal multiprocess method to run docker push command with multi-arch support """ - c, version = config - print(f" -- Starting docker push for version : {version}") - build_command = f"docker push grokzen/redis-cluster:{version}" + c, version, platforms = config + print(f" -- Starting docker push for version : {version} with platforms: {platforms}") + # Use buildx to build and push multi-arch images + c.run("docker buildx create --use --name redis-cluster-builder || docker buildx use redis-cluster-builder", warn=True) + build_command = f"docker buildx build --platform={platforms} --build-arg redis_version={version} -t grokzen/redis-cluster:{version} --push ." c.run(build_command) @@ -103,33 +124,76 @@ def pull(c, version, cpu=None): @task -def build(c, version, cpu=None): - print(f" -- Docker building version : {version}") +def build(c, version, cpu=None, platforms="both"): + platforms = get_platforms(platforms) + print(f" -- Docker building version : {version} for platforms : {platforms}") pool = Pool(get_pool_size(cpu)) pool.map( _docker_build, [ - [c, version] + [c, version, platforms] for version in version_name_to_version(version) ], ) @task -def push(c, version, cpu=None): - print(f" -- Docker push version to docker-hub : {version}") +def push(c, version, cpu=None, platforms="both"): + platforms = get_platforms(platforms) + print(f" -- Docker push version to docker-hub : {version} for platforms : {platforms}") pool = Pool(get_pool_size(cpu)) pool.map( _docker_push, [ - [c, version] + [c, version, platforms] for version in version_name_to_version(version) ], ) +@task +def buildx(c, version, cpu=None, platforms="both"): + """ + Build multi-architecture images using docker buildx without pushing. + Use the separate push command to push the images after building. + + Usage: + invoke buildx 7.2.5 # Build both platforms (default) + invoke buildx 7.2.5 --platforms=amd64 # Build only AMD64 + invoke buildx 7.2.5 --platforms=arm64 # Build only ARM64 + """ + platforms = get_platforms(platforms) + print(f" -- Docker buildx for version : {version} on platforms : {platforms}") + + # Create buildx builder if it doesn't exist + c.run("docker buildx create --use --name redis-cluster-builder || docker buildx use redis-cluster-builder", warn=True) + + pool = Pool(get_pool_size(cpu)) + pool.map( + _docker_buildx, + [ + [c, version, platforms] + for version in version_name_to_version(version) + ], + ) + + +def _docker_buildx(config): + """ + Internal multiprocess method to run docker buildx command + """ + c, version, platforms = config + + # Build without loading to local Docker daemon by default + action = "" + + print(f" -- Starting docker buildx for version : {version} on platforms: {platforms}") + build_command = f"docker buildx build --platform={platforms} --build-arg redis_version={version} -t grokzen/redis-cluster:{version} ." + c.run(build_command) + + @task def list(c): from pprint import pprint