Repository: kubeflow/pipelines

Issue:
[sdk] Fail running Containerized Python components from kfp v2.0.0.b12 on Vertex
### Environment

*  KFP version: Running on Vertex

*  KFP SDK version: 
kfp = 2.0.0b12 

*  All dependencies version:
kfp = 2.0.0b12 \
kfp-pipeline-spec = 0.2.0 \
kfp-server-api = 2.0.0a6

### Steps to reproduce
create a simple component and decorate with vertex ai artifact registry uri, for example:
```
@component(
    base_image='python:3.7',
    target_image="us-central1-docker.pkg.dev/PROJECT_ID/REPO_NAME/COMPONENT_NAME:latest",
    packages_to_install=[]
    
)
```
run command to upload component container:

`kfp component build PATH_TO_COMPONENT --component-filepattern my_component.py --build-image --push-image --overwrite-dockerfile`

Load component as a pipeline operator 
`component_op = components.load_component_from_file('PATH_TO_COMPONENT_CONFIG.yaml')`

 run pipeline on Google cloud Vertex AI

### Error returned: 
component execution fails during setup with error : 
`exec /bin/sh: exec format error`

This error seems related to the component_config,yaml that is generated and points to the container commands that are executed:
```
deploymentSpec:
  executors:
    .....:
      container:
        args:
        - ...
        - ...
        command:
        - sh
        - -c
        - "\nif ! [ -x \"$(command -v pip)\" ]; then\n    python3 -m ensurepip ||\
          \ python3 -m ensurepip --user || apt-get install python3-pip\nfi\n\nPIP_DISABLE_PIP_VERSION_CHECK=1\
          \ python3 -m pip install --quiet     --no-warn-script-location 'google-cloud-aiplatform==1.22.0'\
          \ && \"$0\" \"$@\"\n"
        - python3
        - -m
        - kfp.components.executor_main
        
```

### Expected result
execut component successfully 

### Materials and Reference
N/A
---

<!-- Don't delete message below to encourage users to support your issue! -->
Impacted by this bug? Give it a 👍. 


Discussion:
Issue was because i was building the images on a Mac. Not sure if there is a workaround to this
@anifort, can you describe what the diff was when building on Mac? I am getting similar results on both Mac and Linux.
copying information from here: https://blog.jaimyn.dev/how-to-build-multi-architecture-docker-images-on-an-m1-mac/

"When I build a Docker image on my Mac – it builds an ARM version (specifically arm64). This means this image can’t run on any other device like a raspberry pi (linux/arm/v7) or a typical server (linux/amd64) as the application binaries inside are not compatible."

as per the article, there is a docker buildx experimental command that helps build for a different architecture
Would be nice if that was somehow incorporated in the kfp build cli

Yeah, exactly, I got the same problem on M1 Pro

Workaround command that works for me:
```bash
# Re-generate the Dockerfile if requirements to install or target image name have changed
# This will build an ARM image
kfp component build .

# Overwrite ARM image in Artifact Repository with amd64 version
docker buildx build --push --platform linux/amd64 --tag 'us-central1-docker.pkg.dev/some-image-name:some-tag' .
```

Thank you both for explaining, @anifort and @micmarty-deepsense!
@connor-mccarthy happy to take a look at this!

Resolving patch:
diff --git a/sdk/python/kfp/cli/component.py b/sdk/python/kfp/cli/component.py
--- a/sdk/python/kfp/cli/component.py
+++ b/sdk/python/kfp/cli/component.py
@@ -292,7 +292,7 @@ def maybe_generate_dockerfile(self, overwrite_dockerfile: bool = False):
         self._maybe_write_file(_DOCKERFILE, dockerfile_contents,
                                overwrite_dockerfile)
 
-    def build_image(self, push_image: bool):
+    def build_image(self, platform: str, push_image: bool):
         logging.info(f'Building image {self._target_image} using Docker...')
         client = docker.from_env()
 
@@ -305,6 +305,7 @@ def build_image(self, push_image: bool):
                 dockerfile='Dockerfile',
                 tag=self._target_image,
                 decode=True,
+                platform=platform,
             )
             for log in logs:
                 message = log.get('stream', '').rstrip('\n')
@@ -378,6 +379,11 @@ def component():
     is_flag=True,
     default=True,
     help='Build the container image.')
+@click.option(
+    '--platform',
+    type=str,
+    default='linux/amd64',
+    help='The platform to build the container image for.')
 @click.option(
     '--push-image/--no-push-image',
     type=bool,
@@ -386,7 +392,7 @@ def component():
     help='Push the built image to its remote repository.')
 def build(components_directory: str, component_filepattern: str, engine: str,
           kfp_package_path: Optional[str], overwrite_dockerfile: bool,
-          build_image: bool, push_image: bool):
+          build_image: bool, platform: str, push_image: bool):
     """Builds containers for KFP v2 Python-based components."""
 
     if build_image and engine != 'docker':
@@ -428,4 +434,4 @@ def build(components_directory: str, component_filepattern: str, engine: str,
     builder.maybe_generate_dockerignore()
     builder.maybe_generate_dockerfile(overwrite_dockerfile=overwrite_dockerfile)
     if build_image:
-        builder.build_image(push_image=push_image)
+        builder.build_image(platform=platform, push_image=push_image)


Test patch:
diff --git a/sdk/python/kfp/cli/component_test.py b/sdk/python/kfp/cli/component_test.py
--- a/sdk/python/kfp/cli/component_test.py
+++ b/sdk/python/kfp/cli/component_test.py
@@ -407,7 +407,13 @@ def test_docker_client_is_called_to_build_and_push_by_default(self):
         )
         self.assertEqual(result.exit_code, 0)
 
-        self._docker_client.api.build.assert_called_once()
+        self._docker_client.api.build.assert_called_once_with(
+            path=mock.ANY,
+            dockerfile='Dockerfile',
+            tag=mock.ANY,
+            decode=True,
+            platform='linux/amd64',
+        )
         self._docker_client.images.push.assert_called_once_with(
             'custom-image', stream=True, decode=True)