Tutorial: Build Sample Docker Type IOx App Using Docker Toolchain

In this tutorial, we will go through how to build a sample docker type application using the Docker development environment and package it to an IOx application. For more details on the different types of IOx applications supported, refer to this section.

Requirements

  • Supported development environment architecture for ioxclient tool:
    • amd64
    • darwin_386 (MACOS 10)
    • darwin_amd64
    • linux_386
    • linux_amd64
    • windows_386
    • windows_amd64
  • Minimum Docker version 'Docker CE 17.05' (API 1.29). This tutorial utilizes the multistage build feature which is introduced in the Docker CE 17.05.
  • If you are using the IOx SDE VM, upgrade the Docker version to 17.05 or later. Refer to the Docker documentation.
  • Docker daemon is up and running.

Setup Docker Development Environment for IOx

Refer to this section to setup the Docker daemon on your development machine for authentication with the Cisco hosted DevHub repository.

Create Dockerfile

  1. Refer to the Minified docker base images from Cisco repository section to find complete list of Cisco hosted base OS images.
  2. For a given platform and architecture, it is recommended to choose a corresponding Cisco provided Docker base rootfs image from the Cisco DevHub repositroy, as this creates a minimal footprint for the final IOx app package. Use the path in the "Docker base image" column to pull Docker images with tags as needed. For example, the Cisco hosted Docker base OS image with the "latest" tag for an IR829 platform can be pulled using the below command in Dockerfile:
FROM devhub-docker.cisco.com/iox-docker/ir800/base-rootfs:latest
  1. Refer to this section to find the various hosted packages as part of the Cisco DevHub repository and how to install those packages on top of a Cisco hosted Docker image.

  2. Create a simple c based hello world application which logs the string "Hello World from IOx App" every few seconds to a persistent log file named helloworld.log.

IOx exposes the location of a peristent log directory for apps via the environment variable, CAF_APP_LOG_DIR. The default location of this directory is /data/logs. Refer to this section for more environment variables exposed by IOx to applications.

Printing to the console can overflow the buffer and hang the target application. It is recommended not to print infinitely to console.

Copy the below contents into a file called helloworld.c.

# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include<signal.h>

static FILE *fp = NULL;
static char *fullpath = NULL;

static void sig_handler(int signo)
{
  if (signo == SIGINT || signo == SIGKILL || signo == SIGTERM) {
      printf("received signal = %d\n", signo);
      if (fp)
          fclose(fp);
      /* use fullpath */
      if (fullpath)
          free(fullpath);
      exit(0);
  }
}

int main()
{
    signal(SIGINT, sig_handler);
    signal(SIGKILL, sig_handler);
    signal(SIGTERM, sig_handler);
    char* log_dir = getenv("CAF_APP_LOG_DIR");
    size_t dir_len = strlen(log_dir);
    const char *logfile = "helloworld.log";
    /* + 2 because of the '/' and the terminating null character */
    fullpath = malloc(dir_len + strlen(logfile) + 2);
    if (fullpath == NULL) {
        return -1;
    }
    sprintf(fullpath, "%s/%s", log_dir, logfile);
    //sleep(10);
    fp;
    fp = fopen(fullpath, "w+");
    while(1) {
        fprintf(fp, "Hello World from IOx App!!!\n");
        sleep(2);
        fflush(fp);
    }
    fclose(fp);
    /* use fullpath */
    free(fullpath);

}

Below is the Dockerfile you would need for an x86_64 architecture and building a hello world application. Here we are utilizing multi stage Docker builds to avoid including build tools in the final image and to reduce the overall footprint of the IOx application. Refer to this page for more details on multi-stage Docker builds.

Copy the below contents into a file called Dockerfile in the same directory as helloworld.c.

FROM devhub-docker.cisco.com/iox-docker/ir800/base-rootfs as builder
RUN opkg update
RUN opkg install iox-toolchain
RUN mkdir -p /var/helloworld/
COPY helloworld.c /var/helloworld/
WORKDIR /var/helloworld/
RUN gcc helloworld.c -o helloworld

FROM devhub-docker.cisco.com/iox-docker/ir800/base-rootfs
RUN mkdir -p /var/helloworld/
COPY --from=builder /var/helloworld/helloworld /var/helloworld
RUN chmod +x /var/helloworld/helloworld
CMD /var/helloworld/helloworld

Build Docker Image

Build a docker image named helloworld with version 1.0 using the previously created dockerfile. Prepend sudo if the Docker build command fails due to permission restrictions.

$ docker build -t helloworld:1.0 .

Verify Application Dependencies in the Docker Image - Optional

You can verify if the docker image has all of the applications's dependant libraries or modules by running the Docker container locally in a developement environment. Execute the below command to run the Docker container and getting access to shell session inside the container. For this tutorial, make sure that the application binary is copied under the /var/helloworld/ directory.

$ docker  run -it helloworld:1.0 /bin/sh

Create the IOx Package Descriptor File

The IOx package descriptor file contains metadata about the application, including the minimum resource requirements for running the application. Refer to this section for a detailed description of various attributes and schema for package descriptor contents. Note: In some cases the target field needs to be specified as an array. In the case of a python based target application, specify the target value as ["python", "/usr/bin/app.py"].

Copy the below contents into a file called package.yaml in the same directory as helloworld.c.

descriptor-schema-version: "2.4"

info:
  name: hello world
  description: "Hello world lxc type application"
  version: "1.0"
  author-link: "http://www.cisco.com"
  author-name: "Cisco Systems"

app:
  type: docker
  cpuarch: x86_64
  resources:
    profile: custom
    cpu: 200
    memory: 64
    disk: 2

    network:
      -
        interface-name: eth0
  # Specify runtime and startup
  startup:
    rootfs: rootfs.tar
    target: "/var/helloworld/helloworld"

NOTE: if multiple network interfaces are required, the below package.yaml syntax is used:

    network:
      -
        interface-name: eth0

      -
        interface-name: eth1

      -
        interface-name: eth2

Create the Final IOx Application Package

Download and install the latest version of ioxclient for your development environment from this location. Setup the ioxclient device profile by configuring device IP, credentials, and SSH port.

bash$ ioxclient profiles create

Use the below ioxclient command to build a final IOx application package named package.tar. Use the same Docker image name and version that has been previously used to build the image. Prepend sudo if the ioxclient command fails due to permission restrictions.

bash$ ioxclient docker package helloworld:1.0 .

Deploy/Activate/Start the Application

You can deploy the application onto a physical or virtual IOx device using either of the clients ioxclient or Local Manager, Field Network Director (FND), or Gateway Management Module (GMM). You can access device Local Manager UI using the URL path https://:8443 or https://:443. Use ioxclient to deploy this application to a device. Execute the below ioxclient commands to setup the device profile, install, activate and start the application. Refer to profiles and app management for more ioxclient details. Note: Here, we are using the default attributes for application activation.

bash$ ioxclient app install helloworld_app package.tar

bash$ ioxclient app activate helloworld_app

bash$ ioxclient app start helloworld_app

Verify the Application Is Running

Console into the application, and confirm that application is running successfully by logging the string at /data/logs/helloworld.log. Use the below ioxclient command to console into the application:

bash$ ioxclient app console helloworld_app

More Sample Applications

Refer to this section for more sample Docker applications.