Skip to content

Commit

Permalink
patch: Add product documentation
Browse files Browse the repository at this point in the history
Signed-off-by: Vipul Gupta (@vipulgupta2048) <[email protected]>
  • Loading branch information
vipulgupta2048 authored and klutchell committed Dec 9, 2022
1 parent 0b95d62 commit a081ff2
Show file tree
Hide file tree
Showing 39 changed files with 3,436 additions and 0 deletions.
33 changes: 33 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,36 @@ Update balena-supervisor from 9.0.0 to 9.0.1
Change-type: patch
Signed-off-by: Joe Developer <[email protected]>
```

# Contribute to balenaOS

There are many ways to contribute to balenaOS, based on your skills and interests. Whether you are an embedded linux expert or a casual user, you can find areas where you can make a big difference!


## Testing and Bug Reports

Nobody likes buggy software! If you encounter any problems while using balenaOS, you can submit an [issue on github](https://github.com/balena-os/meta-balena/issues). Please include as much information about the problem as possible.

The same applies for the [balenaOS board support](https://github.com/balena-os?utf8=%E2%9C%93&query=balena-) repositories as well, if you are reporting issues specific to particular hardware.


## Submitting Fixes

If you check out the [issues](https://github.com/balena-os/meta-balena/issues) reported for balenaOS, and you find any that you feel you’d like to fix, you can submit a [Pull Request](https://help.github.com/articles/about-pull-requests/) with your proposed change. Please observe the [Contribution Guidelines](https://github.com/balena-os/meta-balena/blob/master/CONTRIBUTING.md).

The same applies for the [balenaOS board support](https://github.com/balena-os?utf8=%E2%9C%93&query=balena-) repositories as well, if you are fixing issues specific to particular hardware.


## Support for Additional Boards

BalenaOS is intended to work with any kind of board that fulfills some basic hardware requirements (see our Supported Boards list for [currently supported devices](https://www.balena.io/docs/reference/hardware/devices/)). If you have any other board that you’d like to see on that list, please check our [board support contribution guide](https://github.com/balena-os/meta-balena/blob/master/contributing-device-support.md) for more details. You’ll need to be familiar with [Yocto](http://www.yoctoproject.org/), and the board you are adding.


## Documentation

BalenaOS is quickly evolving, and having good documentation that keeps up with the changes is crucial. If you find any information that is incomplete or missing, you can submit improvements to our documentation on Github!

## Tutorials

Tutorials, how-to guides, and blog posts regarding using balenaOS are always welcome. This would help us figure out how to make balenaOS more usable for everyone, and also help new users to get started. If you have any tutorials and guides, please let us know in the [balena forums](https://forums.balena.io/).

322 changes: 322 additions & 0 deletions balena-docs/Getting Started/_getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
<h1>Getting Started on the {props.name}</h1>

In balenaOS all application logic and services are encapsulated in Docker containers. In this getting started guide we will walk you through setting up one of our pre-built development OS images and creating a simple application container. In the guide we will use the [balena CLI](https://www.npmjs.com/package/balena-cli) tool to make things super easy.

## Download an Image
To get a balenaOS device setup, we will first need to flash a system image on to the device, so head over to [balena.io/os](https://www.balena.io/os/#download) and grab the **development** OS for your board.

Once the download is finished, make sure to unzip the file and keep the resulting `balena.img` somewhere safe, we will need it very soon!

## Install the Balena CLI
The CLI is a collection of utilities which helps us to develop balenaOS based application containers.

Install the CLI with the following instructions, [available here](https://github.com/balena-io/balena-cli/blob/master/INSTALL.md).

## Configure the Image
To allow balenaOS images to be easily configurable before boot, some key config files are added to boot partition. In this step we will use the CLI to configure the network, set our hostname to `mydevice` and disable persistent logging, because we don’t want to kill our flash storage with excessive writes. If you are using ethernet you can skip this step, by default the device hostname will be `balena`.

**Note:** Check our list of supported [WiFi adapters](https://github.com/balena-os/meta-balena#recommended-wifi-usb-dongle) and [modems](https://github.com/balena-os/meta-balena#modems).

``` bash
$ sudo balena local configure ~/Downloads/balena.img
? Network SSID I_Love_Unicorns
? Network Key superSecretPassword
? Do you want to set advanced settings? Yes
? Device Hostname mydevice
? Do you want to enable persistent logging? no
Done!
```

## Get the Device Up and Running
Okay, so now we have a fully configured image ready to go, so let’s flash and boot this baby. For this step we recommend [balenaEtcher](https://www.balena.io/etcher/) a handy flashing utility. You can, however, flash this image using `dd` or any other SD card flashing utility, if you wish.

<h3>Flash {props.bootMedia}</h3>

Open [balenaEtcher](https://www.balena.io/etcher/) and use the blue "Select Image" button to find the disk image you downloaded earlier. Once you have selected the image you want to flash, insert your SD card or connect your device (in the case of a balenaFin) to your laptop and flash the image.

### Boot the device

Now power on and boot up your device, after about 10 seconds or so your device should be up and connected to your local network, you should see it broadcasting itself as `mydevice.local`. To check this, let’s try ping the device.

``` bash
$ ping mydevice.local
PING 192.168.1.111 (192.168.1.111): 56 data bytes
64 bytes from 192.168.1.111: icmp_seq=0 ttl=64 time=103.674 ms
64 bytes from 192.168.1.111: icmp_seq=1 ttl=64 time=9.723 ms
^C
--- 192.168.1.111 ping statistics ---
6 packets transmitted, 6 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 7.378/24.032/103.674/35.626 ms
```

Now if we want to poke around a bit inside balenaOS we can just ssh in with:
``` bash
$ balena ssh mydevice.local
Last login: Wed Sep 11 10:32:01 2019 from fe80::1420:6988:2a39:3d3d%wlan0
root@mydevice:~# uname -a
Linux mydevice 4.19.66 #1 SMP PREEMPT Wed Aug 28 16:57:11 UTC 2019 aarch64 aarch64 aarch64 GNU/Linux
root@mydevice:~# balena-engine version
Client:
Version: 18.09.8-dev
API version: 1.39
Go version: go1.10.8
Git commit: 80d443d400dcc85e87322f72866593b46bafb157
Built: Mon Aug 26 09:22:28 2019
OS/Arch: linux/arm64
Experimental: false

Server:
Engine:
Version: 18.09.8-dev
API version: 1.39 (minimum version 1.12)
Go version: go1.10.8
Git commit: 80d443d400dcc85e87322f72866593b46bafb157
Built: Mon Aug 26 09:22:28 2019
OS/Arch: linux/arm64
Experimental: true
```

## Running your first Container
### Clone a demo Project

``` bash
$ git clone https://github.com/balenalabs/multicontainer-getting-started.git
```

### Get some Containers Running
To launch containers on our device, we will use the [`balena push`](https://www.balena.io/docs/reference/cli/#push-applicationordevice) command. First we need to change directory into the project we cloned down and then issue the following command.
``` bash
$ balena push mydevice.local
```
This command will use the image specified by the `docker-compose.yml` or `Dockerfile` in the root of your project directory. The build of this project will happen on your balenaOS device and once completed, the command will start up a container from that newly built image(s). **Note:** The first time you push a project it will need to pull the base images, so it can take a few minutes. Later pushes will be much faster.

Once it's finished building, you should see `[Logs] ...` streaming back and if you visit http://mydevice.local/ you will get a pretty little webpage showing CPU load and memory usage. You will also notice that anytime you change the code in the `multicontainer-getting-started` code base it will hot reload your containers, syncing the new code changes on the device.

## Poking Around balenaOS
To help explore balenaOS devices and application containers more easily, the balena CLI has an ssh command which will help you connect either to the HostOS or a running container on the device.

#### To ssh into the hostOS:
``` bash
$ balena ssh mydevice.local
```

#### To ssh into a particular container:
``` bash
$ balena ssh mydevice.local <service_name>
```
Where `<service_name>` can be replaced with any of the containers defined in your `docker-compose.yml`. If you have a single container app that doesn't have a compose file, your service will get the default name of `main`, so you can access the container using `balena ssh mydevice.local main`.

## Going Further
### Advanced Settings

<span>Either mount the {props.bootMedia} and run:</span>

``` bash
$ sudo balena local configure /path/to/drive
```
And select `y` when asked if you want to add advanced settings.

<span>Alternatively you can add <code>“persistentLogging”: true</code> to <code>config.json</code> in your boot partition of the {props.bootMedia}</span>

To enable persistent logs in a running device, add `“persistentLogging”: true` to `/mnt/boot/config.json` and reboot.

The journal can be found at `/var/log/journal/` which is bind mounted to `root-overlay/var/log/journal` in the `resin-conf` partition.
When logging is not persistent, the logs can be found at `/run/log/journal/` and this log is volatile so you will lose all logs when you power the device down.

### Creating a Project from Scratch
Alright! So we have an awesome container machine up and running on our network. So let’s start pushing some application containers onto it. In this section we will do a quick walk through of setting up a `Dockerfile` and make a simple little node.js webserver.

To get started, let’s create a new project directory called “myapp” and create a new file called `Dockerfile`.

``` bash
$ mkdir -p myapp && touch Dockerfile
```

Now we will create a minimal node.js container based on the slim [Alpine Linux distro](https://hub.docker.com/r/balenalib/raspberrypi3-alpine-node). We do this by adding the following lines to our Dockerfile.

<pre>
FROM balenalib/{props.id}-alpine-node{'\n'}
CMD ["cat", "/etc/os-release"]
</pre>

The `FROM` tells Docker what our container will be based on. In this case an Alpine Linux userspace with just the bare essentials needed for the node.js runtime. The `CMD` just defines what our container runs on startup. In this case, it’s not very exciting yet.

Now to get our application running on our device we can use the `balena push mydevice.local` functionality.

<pre>
$ balena push mydevice.local{'\n'}
[Info] Starting build on device mydevice.local{'\n'}
[Info] Creating default composition with source: .{'\n'}
[Build] [main] Step 1/4 : FROM balenalib/{props.id}-alpine-node{'\n'}
[Build] [main] ---> 25d3c8c8d0af{'\n'}
[Build] [main] Step 2/4 : CMD ["cat", "/etc/os-release"]{'\n'}
[Build] [main] ---> Running in 449e2d11b48f{'\n'}
[Build] [main] Removing intermediate container 449e2d11b48f{'\n'}
[Build] [main] ---> fc19268cf077{'\n'}
[Build] [main] Step 3/4 : LABEL io.resin.local.image=1{'\n'}
[Build] [main] ---> Running in efbf5e0d1027{'\n'}
[Build] [main] Removing intermediate container efbf5e0d1027{'\n'}
[Build] [main] ---> da6d888c128f{'\n'}
[Build] [main] Step 4/4 : LABEL io.resin.local.service=main{'\n'}
[Build] [main] ---> Running in 4befab0ab3f3{'\n'}
[Build] [main] Removing intermediate container 4befab0ab3f3{'\n'}
[Build] [main] ---> 8351b83ebb73{'\n'}
[Build] [main] Successfully built 8351b83ebb73{'\n'}
[Build] [main] Successfully tagged local_image_main:latest{'\n'}

[Live] Waiting for device state to settle...{'\n'}
[Info] Streaming device logs...{'\n'}
[Live] Watching for file changes...{'\n'}
[Logs] [9/11/2019, 12:50:23 PM] Creating volume 'resin-data'{'\n'}
[Logs] [9/11/2019, 12:50:26 PM] Installing service 'main sha256:8351b83ebb73f49ae044237191307c3bfed80c2ace2535bf33e8e4361c43461d'{'\n'}
[Logs] [9/11/2019, 12:50:26 PM] Installed service 'main sha256:8351b83ebb73f49ae044237191307c3bfed80c2ace2535bf33e8e4361c43461d'{'\n'}
[Logs] [9/11/2019, 12:50:26 PM] Starting service 'main sha256:8351b83ebb73f49ae044237191307c3bfed80c2ace2535bf33e8e4361c43461d'{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] Started service 'main sha256:8351b83ebb73f49ae044237191307c3bfed80c2ace2535bf33e8e4361c43461d'{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] [main] NAME="Alpine Linux"{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] [main] ID=alpine{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] [main] VERSION_ID=3.10.2{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] [main] PRETTY_NAME="Alpine Linux v3.10"{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] [main] HOME_URL="https://alpinelinux.org/"{'\n'}
[Logs] [9/11/2019, 12:50:27 PM] [main] BUG_REPORT_URL="https://bugs.alpinelinux.org/"{'\n'}
</pre>

This command will start the build on your local balenaOS device from whatever you have in the current working directory. It will then start up all the containers and stream back the logs from each container to the terminal. We can see that for our local balenaOS device we have an app called `[main]` which will be created from an image called `local_image_main` and is associated to a container on our device called `main_1_1`. You will notice that the container keeps restarting over and over. This is due to the fact that the our main process of printing out the `os-release` file exits after running and by default our containers restart policy is to always restart containers.

So now that we are building, let’s start adding some actual code! We will just add `main.js` file in the root of our `myapp` directory.

``` javascript
//main.js
console.log("Hey… I’m a node.js app running in a container!!");
```

We then make sure our Dockerfile copies this source file into our container context by replacing our current `CMD ["cat","/etc/os-release"]` in our Dockerfile with the following.

<pre>
FROM balenalib/{props.id}-alpine-node{'\n'}
WORKDIR /usr/src/app{'\n'}
COPY . .{'\n'}
CMD ["node", "main.js"]{'\n'}
</pre>

This puts all the contents of our `myapp` directory into `/usr/src/app` in our running container and says we should start main.js when the container starts.

Alright, so we have a simple javascript container, but that’s pretty boring, let’s add some dependencies and complexity. To add dependencies in node.js we need a package.json, the easiest way to whip up one is to just run `npm init` in the root of our `myapp` directory. After a nice little interactive dialog we have the following `package.json` in directory.

``` json
{
"name": "myapp",
"version": "1.0.0",
"description": "a simple hello world webserver",
"main": "main.js",
"scripts": {
"test": "echo \"no tests yet\""
},
"repository": {
"type": "git",
"url": "none"
},
"author": "Shaun Mulligan <[email protected]>",
"license": "ISC"
}
```

Now it’s time to add some dependencies. For our little webserver, we will use the popular expressjs module. We can add it to the `package.json` after the `"license": "ISC"`, so it now looks like this:

``` json
{
"name": "myapp",
"version": "1.0.0",
"description": "a simple hello world webserver",
"main": "main.js",
"scripts": {
"test": "echo \"no tests yet\""
},
"repository": {
"type": "git",
"url": "none"
},
"author": "Shaun Mulligan <[email protected]>",
"license": "ISC",
"dependencies": {
"express": "^4.14.0"
}
}
```

Now all we need to do is add a few more lines of javascript to our main.js and we are off to the races.

``` javascript
//main.js

var express = require('express');
var app = express();

// reply to request with "Hello World!"
app.get('/', function (req, res) {
res.send("Hello World, I'm a container running on balenaOS!");
});

//start a server on port 80 and log its start to our console
var server = app.listen(80, function () {

var port = server.address().port;
console.log("Hey… I’m a node.js server running in a container and listening on port: ", port);
});
```

Great, so now we are almost ready to go, but we want to make sure our dependency gets installed when we build. We then need to run a `npm install` in our build, so we add a few lines to our Dockerfile.

<pre>
FROM balenalib/{props.id}-alpine-node{'\n'}
WORKDIR /usr/src/app{'\n'}
COPY package.json package.json{'\n'}
RUN npm install{'\n'}
COPY . .{'\n'}
CMD ["node", "main.js"]{'\n'}
</pre>

__NOTE:__ Add `node_modules` to your `.dockerignore` file, otherwise your local modules might be copied to the device with the above `Dockerfile`, and they are likely the wrong architecture for your application!

We can now deploy our new webserver container again with:

``` bash
$ balena push <DEVICE_IP>
```

You should now be able to point your web browser on your laptop to the IP address of your device and see the "Hello, World!" message.

### Configure balenaOS without the CLI

If you are not using the CLI, you will need to mount the boot partition of the image and edit the configuration manually.

Edit `/boot/config.json` so it looks like this:
``` json
{
"persistentLogging": false,
"hostname": "mydevice",
}
```

And create a file in `/boot/system-connections` called `my-wifi` with the following content and the `ssid` and `psk` values replaced as needed.

```bash
[connection]
id=my-wifi
type=wifi

[wifi]
mode=infrastructure
ssid=I_Love_Unicorns

[wifi-security]
auth-alg=open
key-mgmt=wpa-psk
psk=superSecretPassword

[ipv4]
method=auto

[ipv6]
addr-gen-mode=stable-privacy
method=auto
```
Loading

0 comments on commit a081ff2

Please sign in to comment.