Skip to content

Commit 04d309b

Browse files
authored
Merge pull request #209 from arduino/benjamindannegard/x8-custom-container-tutorial
[PC-802] Benjamindannegard/x8 custom container tutorial
2 parents 3809e5b + 28777c6 commit 04d309b

File tree

7 files changed

+197
-0
lines changed

7 files changed

+197
-0
lines changed
Loading
Loading
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: Create and Upload a Custom Container to the Portenta X8
3+
difficulty: medium
4+
tags: [Linux, Python, Containers, ADB]
5+
description: This tutorial will show you how to create and upload your custom container to your Portenta X8
6+
author: Benjamin Dannegård
7+
hardware:
8+
- hardware/04.pro/boards/portenta-x8
9+
software:
10+
- adb
11+
---
12+
13+
## Overview
14+
15+
In this tutorial we will create a simple container that we can then upload to the Arduino Portenta X8. A container consists of an image file and all it's dependencies if there are any. This tutorial will go through the different files needed to create a container and their functions. Building this container locally and then uploading it to a Portenta X8. Using docker with ADB to build, run and attach our container to the Portenta X8.
16+
17+
## Goals
18+
19+
- Learn how to create a container for use with the Portenta X8
20+
- Learn how to upload a container to the Portenta X8
21+
22+
### Required Hardware and Software
23+
24+
- [Portenta X8](https://store.arduino.cc/portenta-x8)
25+
- ADB
26+
- USB-C cable (either USB-C to USB-A or USB-C to USB-C)
27+
28+
## Instructions
29+
30+
When running a container, it uses an isolated filesystem. This custom filesystem is provided by a container image. Since the image contains the container’s filesystem, it must contain everything needed to run an application - all dependencies, configuration, scripts, binaries, etc. The image also contains other configuration for the container, such as environment variables, a default command to run, and other metadata.
31+
32+
## Container File Structure
33+
34+
To create our container we need to collect our necessary files. Creating a folder called **x8-custom-test**, the following files needs to be in the folder:
35+
- docker-build.conf
36+
- docker-compose.yml
37+
- Dockerfile
38+
- requirements.txt
39+
- src folder
40+
- main.py (This file should be inside the src folder)
41+
42+
The complete folder will look like this:
43+
44+
![Folder structure for container](assets/custom-container-folder.png)
45+
46+
Lets go through what these files contain and do.
47+
48+
### Docker-build.conf
49+
A file containing the minimal "unit test" command to be executed on the container to prove it's working. Our file will make our containers minimal unit test a test of Python3 help command.
50+
51+
```python
52+
TEST_CMD="python3 --help"
53+
```
54+
55+
### Docker-compose.yml
56+
This file defines the app name through the factory, permissions and settings for the involved containers. The argument in the image tag will make it so our image file builds locally.
57+
58+
```python
59+
version: '3.6'
60+
61+
services:
62+
x8-custom-test:
63+
image: blob-opera:latest
64+
restart: always
65+
tty: true
66+
read_only: true
67+
user: "63"
68+
tmpfs:
69+
- /run
70+
- /var/lock
71+
- /var/log
72+
- /tmp
73+
```
74+
75+
### Dockerfile
76+
This is used to build the container.
77+
78+
```python
79+
FROM python:3-alpine3.15
80+
81+
# Set our working directory
82+
WORKDIR /usr/src/app
83+
84+
# Copy requirements.txt first for better cache on later pushes
85+
COPY requirements.txt requirements.txt
86+
87+
# pip install python deps from requirements.txt on the resin.io build server
88+
RUN pip install -r requirements.txt
89+
90+
# This will copy all files in our root to the working directory in the container
91+
COPY ./src/main.py ./
92+
93+
# Enable udevd so that plugged dynamic hardware devices show up in our container.
94+
ENV UDEV=1
95+
96+
# main.py will run when container starts up on the device
97+
CMD ["python","-u","main.py"]
98+
```
99+
100+
### Requirements.txt
101+
102+
```python
103+
Flask==0.12.3
104+
```
105+
106+
### Source
107+
Here we will keep source code of the app you want to run in the container or a startup script. We will create a file and name it **main.py** in this folder. This script will print "Hello World!" in the CLI window.
108+
109+
```python
110+
from flask import Flask
111+
app = Flask(__name__)
112+
113+
@app.route('/')
114+
def hello_world():
115+
return 'Hello World!'
116+
117+
if __name__ == '__main__':
118+
app.run(host='0.0.0.0', port=80)
119+
```
120+
121+
## Uploading the Container Folder
122+
123+
First, you have to have set up your board to a factory, as shown in the [Portenta X8 Out of the Box tutorial](https://docs.arduino.cc/tutorials/portenta-x8/out-of-the-box).
124+
125+
Once this is done, we will push our folder to a repository within the factory. Lets place our folder "x8-custom-test" inside the "containers.git" repository. You can find this repository inside your factory page, if you click on "Source". And then on "container.git", the url of this page will be used in the next command.
126+
127+
![Source on Foundries.io Factory page](assets/custom-factory-page.png)
128+
129+
![Where to find container.git](assets/custom-factory-git.png)
130+
131+
![Container.git page](assets/custom-git.png)
132+
133+
In order to pull or push repositories you have to generate an API key. This can be done by going to the user settings on the Factory page. First click on the user drop-down menu, then go into the tokens page and follow the steps of creating a new API key. This token will be used as the password for all git operations while the username can be anything, except an empty string.
134+
135+
![User settings on your Factory page](assets/factory-user-settings.png)
136+
137+
![Token section in user settings](assets/token-page.png)
138+
139+
Use the following command in a Linux shell, like ADB which the previously mentioned tutorial showed how to set up. To get the repository on your board, replace the "YOUR_FACTORY" with the name of your factory. The "-m" tag selects the manifest file within the repository. If no manifest name is selected, the default is "default.xml". And the "-b" tag specifies a revision. Running this command will get the container repository, where we will put our folder.
140+
141+
```python
142+
repo init -u https://source.foundries.io/factories/YOUR_FACTORY/containers.git -m arduino.xml -b devel
143+
```
144+
145+
We can also run ```repo sync``` to get the latest version of the repository. Put the "x8-custom-test" folder in the repository. If you push the commit to "container.git" a new target will automatically build on your Foundries.io Factory page.
146+
147+
***NOTE: The "repo sync" will at some point pause. This is because it is waiting for a username and password but the prompt will be hidden. Enter the username and password to move on.***
148+
149+
### Building and Running the Container
150+
151+
After uploading the folder to the repository. Navigate into the "x8-custom-test" folder, that should be located on your board now. This allows us to build our container with a simple command. Using ```docker build``` with a ```--tag``` will let us give the container a tag so we can easily keep track of what version of the build this is.
152+
153+
```python
154+
docker build --tag "x8-custom-test:latest" .
155+
```
156+
157+
Now that it is built we can run it with ```docker run```, finding it with the tag that we chose to give to the build we want to run. Here we will have to enter the user information into the --user tag. This information is found inside the "docker-compose.yml" file.
158+
159+
```python
160+
docker run -it --rm --user "63" x8-custom-test:latest
161+
```
162+
163+
### Using Docker-Compose
164+
165+
A option for testing an app or container is to use "docker-compose". This is helpful when we have a lot of settings in our "docker-compose.yml" file, since we don't have to use those settings in the run argument with this method. First navigate into the container folder.
166+
167+
```python
168+
cd /home/fio/x8-custom-test
169+
```
170+
171+
This docker-compose command will start your application and register it as a systemd service that will persist even when a reboot occurs. So at the next boot your docker-compose app will run automatically.
172+
173+
```python
174+
docker-compose up --detach
175+
```
176+
177+
To stop the docker-compose app from running, use the following command:
178+
179+
```python
180+
docker-compose stop
181+
```
182+
183+
## Conclusion
184+
185+
This tutorial went through what goes into a container, how the folder should be built and what files it should contain. It then explained what each files purpose is and what they should contain for this example. Then we went through how this relates back to the factory, and how Foundries.io makes the whole process easier for us. We then showed how to build the container and run it on the Portenta X8. Lastly, we showed a useful testing feature with docker-compose. Which lets us test our container with a faster process.
186+
187+
### Next Steps
188+
189+
To get a better understanding of how to manage containers with Docker, take a look at our [Managing Containers with Docker on Portenta X8](https://docs.arduino.cc/tutorials/portenta-x8/docker-container). This tutorial will show some useful commands to use with the docker service and ADB or SSH.
190+
191+
192+
## Troubleshooting
193+
194+
Here are some errors that might occur in the process of this tutorial:
195+
196+
- Make sure you have followed our other tutorials that shows how to set up the [Portenta X8 out of the box](https://docs.arduino.cc/tutorials/portenta-x8/out-of-the-box)
197+
- If you are having issues with the adb shell, don't forget to try and use `sudo` and `su`

0 commit comments

Comments
 (0)