Building a Platform-Agnostic Container Image
- 1 Process
- 2 Example using a Python Micro-Service
- 2.1.1 Main steps
- 2.1.2 Manifest List and Image Layers
- 2.1.3 Source code
- 2.1.3.1 Code structure
- 2.1.3.2 Python App
- 2.1.3.3 Requirements
- 2.1.3.4 Dockerfile
- 2.2 Build arm image (A)
- 2.3 Push arm image to the registry
- 2.4 Build Intel image (B)
- 2.5 Push Intel image to the registry
- 2.6 Create a manifest list for image A and image B
- 2.7 Verify that the manifest describes a platform-agnostic container image.
- 2.8 Push the manifest list to the registry
- 3 Building Multi-CPU container images using CI/CD pipelines
- 4 Intermediate Image Naming Convention
Process
In general, the process to build a platform-agnostic container image follows the flow depicted on the following figure.
The commands needed to implement the flow are described, using an example, in the next section.
Example using a Python Micro-Service
Main steps
The following diagram captures the main steps we need to take to enable platform-agnostic containers:
(1) and (2) Build and push container images for each platform.
(3) Create and push a manifest list for the images above
(4) Pull and run the exact same image/tag on different platforms.
Manifest List and Image Layers
Digging a little bit deeper into step (4), the following diagram shows the relationship between a manifest list and image manifests for our platform-agnostic image (tag).
The following sections describe the commands needed to create a multi-cpu architecture container image. Let's call the image onap/py-app.
Note that this flow could be used by ONAP developers during the development-test-debug process.
For the release process, the flow will implemented using CI/CD pipelines as shown in the next section.
Source code
Code structure
.
├── app
│ ├── main.py
│ └── requirements.txt
└── Dockerfile
Python App
from flask import Flask
import platform
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello ONAP. I am a Python service running on " + platform.machine()
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True, port=5000)
Requirements
Flask==0.10.1Dockerfile
FROM python:2.7-alpine
LABEL maintainer="adolfo@orangemonk.net"
# Keep it simple
COPY ./app /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["main.py"]Build arm image (A)
Log into an arm server, copy the code into the structure depicted above.
cd to the root of the code tree above, then execute
docker build -t onap/py-app-arm64 .Push arm image to the registry
Once the image has been successfully built, push it to the repository.
Note that if you are using a private repository, you might need to "docker tag" the image before executing the next command.
docker push onap/py-app-arm64v8:latestBuild Intel image (B)
Log into an intel server, setup the code structure as before.
Let's now repeat the process for the intel layers of the multi-cpu container image.
docker build -t onap/py-app-amd64 .Push Intel image to the registry
docker push onap/py-app-amd64:latestCreate a manifest list for image A and image B
Now that we have built and pushed layers for each cpu architecture, we can create the manifest list to put the final container image together with
docker manifest create onap/py-app \
onap/py-app-arm64v8 \
onap/py-app-amd64 Verify that the manifest describes a platform-agnostic container image.
docker manifest inspect --verbose onap/py-app