{"id":449,"date":"2019-11-18T07:30:55","date_gmt":"2019-11-18T13:30:55","guid":{"rendered":"https:\/\/jacobncalvert.com\/?p=449"},"modified":"2019-11-17T21:43:22","modified_gmt":"2019-11-18T03:43:22","slug":"virtualization-for-embedded-systems-series-containers-deep-dive","status":"publish","type":"post","link":"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/","title":{"rendered":"Virtualization for Embedded Systems Series: Containers Deep Dive"},"content":{"rendered":"\n<p>In <a href=\"https:\/\/jacobncalvert.com\/2019\/11\/11\/virtualization-for-embedded-systems-series-applications-in-the-real-world\/\">the previous post<\/a>, I looked at several real-world use cases for containers and hypervisors. This post will be a deep dive into containers and a how-to on using them. <\/p>\n\n\n\n<p><em><strong>Note: all the code, Dockerfiles, etc. are<\/strong><\/em><a href=\"https:\/\/github.com\/jacobcalvert\/virtualization-for-embedded-systems-series\"><em><strong> archived in a git repo at GitHub.<\/strong><\/em><\/a><\/p>\n\n\n\n<h2>Container History<\/h2>\n\n\n\n<h3>Origins<\/h3>\n\n\n\n<p>A little history is needed before we jump into building and deploying containers. Docker, which is likely still the largest container technology provider by a longshot, was originally released in 2013. In just 6 years a huge community using containers sprang up around the concept &#8211; check out <a href=\"https:\/\/hub.docker.com\/\">DockerHub<\/a> for a sense of scale in the community. Preceding the Docker phenomenon, we had LXC (Linux Containers) which had been <a href=\"https:\/\/en.wikipedia.org\/wiki\/LXC\">around since around 2008<\/a>, but it really never had the sticking power that Docker has enjoyed. Fast-forward to today, and there are a handful of competing container technologies, all with similar features, but some have better ecosystems or support than others. <\/p>\n\n\n\n<h3>Where We Are Today<\/h3>\n\n\n\n<p>Recently there has a push for a standardization of the container frameworks that makeup these ecosystems. The <a href=\"https:\/\/www.opencontainers.org\/\">Open Container Initiative<\/a> is  an open governance body for working towards standardization of the container framework. Many container systems have already adopted this and become OCI compliant or aligned. Docker has also bolstered the community by donating much of its container infrastructure components to the open source world for OCI to utilize. <\/p>\n\n\n\n<h3>Goal of the Open Container Initiative<\/h3>\n\n\n\n<p>I encourage you to go to the OCI&#8217;s website and read their mission documents in whole, but I&#8217;d like to provide the brief description here as it relates to our use of containers in embedded systems. The OCI intends to create a standard for representing containers and their basic runtime requirements and interfaces for portability. From their FAQs:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>The mission of the Open Container Initiative (OCI) is to promote a set of common, minimal, open standards and specifications around container technology.<\/p><cite>What is the mission of the OCI? FAQ (<a href=\"https:\/\/www.opencontainers.org\/faq#faq1\">https:\/\/www.opencontainers.org\/faq#faq1<\/a>)<\/cite><\/blockquote>\n\n\n\n<p>This is important to us in embedded engineering because we need this portability and compatibility to fully realize the use cases outlined in the last post. Since the OCI has not reached a sufficiently mature status, I will be sticking with Docker as the container ecosystem in this post. <\/p>\n\n\n\n<h2>Docker Containers: A Hands-On Example<\/h2>\n\n\n\n<h3>Objectives<\/h3>\n\n\n\n<p>In the following sections we will accomplish the following:<\/p>\n\n\n\n<ol><li>Create a basic container image from a Dockerfile<\/li><li>Create a container to build and test a custom application<\/li><li>Create a container to host the custom application<\/li><li>Export the container<\/li><li>Run the container on a different platform<\/li><\/ol>\n\n\n\n<h3>Creating a Basic Container<\/h3>\n\n\n\n<p>I&#8217;m starting with the assumption that Docker is installed and you&#8217;ve been able to run the Docker hello-world image. We will use a Dockerfile to compose our basic image. A Dockerfile is a script of sorts which instructs the engine to construct our container piece by piece.  I&#8217;ve put a (very) basic Dockerfile below. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n##########################################\n# File: Dockerfile\n# Author: Jacob Calvert &lt;jcalvert@jacobncalvert.com&gt;\n# Date: Nov-08-2019\n# \n# This is a basic Dockerfile \n#\n##########################################\n\n# start from the basic busybox image\nFROM busybox\n\n# put a file in our container\nCOPY hello-world.txt .\n<\/pre><\/div>\n\n\n<p>What I have in my workspace on my host machine is as follows:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ ls\nDockerfile  hello-world.txt\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ cat hello-world.txt\nhello, world!\n<\/pre><\/div>\n\n\n<p>To build the Docker image from the Dockerfile, you run a &#8216;docker build&#8217; command. The &#8216;-t&#8217; specifies a tag by which we&#8217;ll reference this image, and the &#8216;.&#8217; specifies the directory for the Dockerfile &#8211;  in this case, the current directory. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker build -t basic .\nSending build context to Docker daemon  3.072kB\nStep 1\/2 : FROM busybox\n ---&gt; 020584afccce\nStep 2\/2 : COPY hello-world.txt .\n ---&gt; 11e3eeda8f8e\nSuccessfully built 11e3eeda8f8e\nSuccessfully tagged basic:latest\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ \n<\/pre><\/div>\n\n\n<p>Let&#8217;s view and run our image now.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker image ls\nREPOSITORY                        TAG                 IMAGE ID            CREATED             SIZE\nbasic                             latest              11e3eeda8f8e        42 seconds ago      1.22MB\nubuntu                            latest              775349758637        8 days ago          64.2MB\nbusybox                           latest              020584afccce        9 days ago          1.22MB\nhello-world                       latest              f2a91732366c        23 months ago       1.85kB\nquantumobject\/docker-zoneminder   latest              469615ab191d        24 months ago       1.15GB\nmysql\/mysql-server                latest              a3ee341faefb        2 years ago         246MB\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker run -it basic\n\/ # ls\nbin              dev              etc              hello-world.txt  home             proc             root             sys              tmp              usr              var\n\/ # cat hello-world.txt \nhello, world!\n\/ # exit\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ \n<\/pre><\/div>\n\n\n<p>What are we looking at here? First we list our available local images with the <em>docker image ls<\/em> command. We can see that I have several images on my development machine, including <strong>basic<\/strong> which was created only a few moments ago from our <em>build<\/em> command. Next I run the image we created with <em>docker run -it &lt;image&gt;<\/em>. The <em>-it <\/em>flags are for &#8211;interactive and &#8211;tty. These two together essentially present the container&#8217;s console as a pseudo-tty device in your terminal, in interactive mode. It is as if you are sitting in front of another machine. <\/p>\n\n\n\n<p>Next, you can see that I have a different prompt. This is the container&#8217;s prompt. I type <em>ls<\/em> and we can see our hello-world.txt is there on the filesystem of our container as we desired via the COPY command in the Dockerfile. Lastly, we type <em>exit<\/em> which ends the busybox process and exits the container. <\/p>\n\n\n\n<p>Now let&#8217;s look at the persistent state parts of Docker. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker system info\nClient:\n Debug Mode: false\n\nServer:\n Containers: 1\n  Running: 0\n  Paused: 0\n  Stopped: 1\n Images: 6\n Server Version: 19.03.2\n Storage Driver: overlay2\n  Backing Filesystem: extfs\n  Supports d_type: true\n  Native Overlay Diff: true\n Logging Driver: json-file\n Cgroup Driver: cgroupfs\n Plugins:\n  Volume: local\n  Network: bridge host ipvlan macvlan null overlay\n  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog\n Swarm: inactive\n Runtimes: runc\n Default Runtime: runc\n Init Binary: docker-init\n containerd version: 894b81a4b802e4eb2a91d1ce216b8817763c29fb\n runc version: 425e105d5a03fabd737a126ad93d62a9eeede87f\n init version: fec3683\n Security Options:\n  apparmor\n  seccomp\n   Profile: default\n Kernel Version: 4.4.0-165-generic\n Operating System: Linux Mint 18\n OSType: linux\n Architecture: x86_64\n CPUs: 4\n Total Memory: 15.55GiB\n Name: jacob-aspire-mint\n ID: Z2GE:5Y2A:K4LP:UC4O:QV3A:4JMR:5RLW:2DHQ:WANN:2RA3:VKJ2:UZMI\n Docker Root Dir: \/var\/lib\/docker\n Debug Mode: false\n Registry: https:\/\/index.docker.io\/v1\/\n Labels:\n Experimental: false\n Insecure Registries:\n  127.0.0.0\/8\n Live Restore Enabled: false\n<\/pre><\/div>\n\n\n<p>Using <em>docker system info<\/em> we can see that there is 1 container and that it is stopped (not running). How do we see what that container is?<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker ps -a\nCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES\nc76e0de1a530        basic               &quot;sh&quot;                4 minutes ago       Exited (0) 3 minutes ago                       silly_kalam\n\n<\/pre><\/div>\n\n\n<p>We can use the <em>ps<\/em> command to see information about our containers. Notice the <strong>STATUS<\/strong> column. Our container&#8217;s execution has exited. This begs the question: can we restart a container and pick up where we left off? The answer of course is yes!<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/media\/jacob\/jacob\/Documents\/Workspaces\/Blog\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker container start -i silly_kalam \n\/ # ls\nbin              dev              etc              hello-world.txt  home             proc             root             sys              tmp              usr              var\n\/ # \n\n<\/pre><\/div>\n\n\n<p>Notice the command difference this time around. I am using <em>docker container start -i &lt;name&gt;<\/em> where the <strong>name<\/strong> is a name given to the container at run time. The container can be referenced by its human-friendly name as I have done here, or by its ID (the long UUID). Also note the -i flag; this is to open it as an interactive session again. No -t is needed since the TTY device has already been allocated. So can we work inside this running container like a real machine and see the state persist? Indeed we can as well! Let&#8217;s see it in action. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n\/ # cd home\/\n\/home # mkdir -p user\/workspace\/\n\/home # cd user\/workspace\/\n\/home\/user\/workspace # echo &quot;another file!&quot; &gt; another.file\n\/home\/user\/workspace # ls\nanother.file\n\/home\/user\/workspace # exit\n\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/basic $ docker container start -i silly_kalam \n\n\/ # ls\nbin              dev              etc              hello-world.txt  home             proc             root             sys              tmp              usr              var\n\/ # cd home\/user\/workspace\/\n\/home\/user\/workspace # ls\nanother.file\n\/home\/user\/workspace # cat another.file \nanother file!\n\/home\/user\/workspace # \n\n<\/pre><\/div>\n\n\n<p>It may be a little hard to follow, but here&#8217;s what has happened. From inside our previously started container named <em>silly_kalam<\/em>, I have created a directory <strong>\/home\/user\/workspace<\/strong>. Next, I created a file named <strong>another.file<\/strong> and filled it with &#8220;another file!&#8221;. I then exited the container. Next, I started the container again, changed directory to my created directory, and printed out the contents of <strong>another.file<\/strong>. <\/p>\n\n\n\n<p>Using this example we can see that the content inside a container is not purely ephemeral, but can persist over many starts\/stops. For more persistent storage check out <a href=\"https:\/\/docs.docker.com\/storage\/volumes\/\">Docker&#8217;s Volume subsystem.<\/a><\/p>\n\n\n\n<h3>Creating a Development Environment Container<\/h3>\n\n\n\n<p>Using the knowledge gained through building a basic container, we will now create a container to use as a development environment for our example application. <\/p>\n\n\n\n<h4>The Application Specs<\/h4>\n\n\n\n<p>We want to build a simple application which demonstrates the portability of containers and also does something we can test. We also want it to be a simple application for demo purposes. With this in mind, our application will be a simple data modem. It will take in data from one source medium and spit it out as another medium. Below is a list of basic requirements codified for this application:<\/p>\n\n\n\n<ul><li>Translate data from a serial device to UDP<ul><li>Serial will be at 115200\/8N1 configuration<\/li><li>UDP will listen on a configurable port<\/li><li>TTY device will be configurable<\/li><\/ul><\/li><li>Modem will log messages to container console<\/li><\/ul>\n\n\n\n<p>To satisfy these requirements we need to be able to build application code for the container. Why use a container to build applications? Why not just use your host machine&#8217;s development environment? Repeatability is the answer. Rather than struggle to maintain a development environment across multiple developers who have their own flavors of Linux distro and preferences and so on, we can distribute a &#8220;builder&#8221; container which has all the dependencies and tools needed to build the application (albeit a simple one in this case) from scratch and is completely independent of the host it runs on. <\/p>\n\n\n\n<h4>Building and Using the Container<\/h4>\n\n\n\n<p>For our &#8220;builder&#8221; container we create the following Dockerfile:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n##########################################\n# File: Dockerfile\n# Author: Jacob Calvert &lt;jcalvert@jacobncalvert.com&gt;\n# Date: Nov-09-2019\n# \n# This Dockerfile creates a development environment for \n# building a sample application\n#\n##########################################\n\n# start from the basic ubuntu image\nFROM ubuntu\n\n# install the needed tools in our container\nRUN apt-get update &amp;&amp; apt-get install build-essential git net-tools -y\n<\/pre><\/div>\n\n\n<p style=\"text-align:left\"> This Dockerfile starts with an Ubuntu base image, and adds the three common packages build-essential, git, and net-tools. I won&#8217;t show the entire build process but at the end you should be able to run the new container as we did in the basic use case. <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/dev-env $ docker run -it dev-env\nroot@32984d05f73f:\/# ifconfig \neth0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500\n        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255\n        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)\n        RX packets 19  bytes 2853 (2.8 KB)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 0  bytes 0 (0.0 B)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n\nlo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536\n        inet 127.0.0.1  netmask 255.0.0.0\n        loop  txqueuelen 1  (Local Loopback)\n        RX packets 0  bytes 0 (0.0 B)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 0  bytes 0 (0.0 B)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n\nroot@32984d05f73f:\/# gcc\ngcc: fatal error: no input files\ncompilation terminated.\nroot@32984d05f73f:\/# \n<\/pre><\/div>\n\n\n<p>Now in another tab let&#8217;s copy our source into our builder image (note: we could have simply git cloned it as well, but in many areas I work in, network access is a no-go).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/dev-env $ docker cp modem.c jovial_rhodes:\/\n<\/pre><\/div>\n\n\n<p>And now we can see this file in our running container.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nroot@32984d05f73f:\/# ls\nbin  boot  dev  etc  home  lib  lib64  media  mnt  modem.c  opt  proc  root  run  sbin  srv  sys  tmp  usr  var\nroot@32984d05f73f:\/# \n<\/pre><\/div>\n\n\n<p>So let&#8217;s build our application, and grab the resulting binary.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nroot@32984d05f73f:\/# gcc modem.c -o modem -lpthread\nroot@32984d05f73f:\/# ls\nbin  boot  dev  etc  home  lib  lib64  media  mnt  modem  modem.c  opt  proc  root  run  sbin  srv  sys  tmp  usr  var\nroot@32984d05f73f:\/# \n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/dev-env $ docker cp jovial_rhodes:\/modem .\/modem\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/dev-env $ ls\nDockerfile  modem  modem.c\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/dev-env $ \n<\/pre><\/div>\n\n\n<p>We have successfully used a container to build an image for deployment. The application is a simple one, but it illustrates the point of using a &#8220;builder&#8221; container for repeatable builds. Now we can move on to deploying our application. Of course there&#8217;d be rigorous testing in a real-world application, but we can neglect that for the purposes of demonstration.<\/p>\n\n\n\n<h3>Creating a Deployment Environment Container<\/h3>\n\n\n\n<p>We want to now create a container which will start our application on boot, and run that application until we exit the container. We will start with our basic Ubuntu, and add a few things as shown below.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n##########################################\n# File: Dockerfile\n# Author: Jacob Calvert &lt;jcalvert@jacobncalvert.com&gt;\n# Date: Nov-09-2019\n# \n# This Dockerfile creates a deployment environment for \n# the sample application\n#\n##########################################\n\n# start from the basic ubuntu image\nFROM ubuntu\n\n# create a app\/bin directory in our container\nRUN mkdir -p \/app\/bin \n\n# copy in the app and a startup script\nCOPY modem \/app\/bin\nCOPY start-modem.sh \/\n\n# set the entry point\nENTRYPOINT \/start-modem.sh\n\n<\/pre><\/div>\n\n\n<p>Notice a new directive here? The <strong>ENTRYPOINT<\/strong> directive is what will be run on startup of the container. Now let&#8217;s take a look at the contents of that script.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n#!\/bin\/sh\n\/app\/bin\/modem -d $TTY_DEVICE -p $PORT\n<\/pre><\/div>\n\n\n<p>Simple right? All it does is start our application with some environment variables as parameters. <\/p>\n\n\n\n<h4>Testing our Deployment Container<\/h4>\n\n\n\n<p>Here&#8217;s where the magic of containers will really start to shine.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/deploy-env $ sudo docker run -it -e TTY_DEVICE=\/dev\/ttyACM0 -e PORT=9000 --device=\/dev\/ttyACM0 deploy-env \n\n<\/pre><\/div>\n\n\n<p>This time we will run our container image with a few extra parameters. First, the <strong>-e<\/strong> flag will inject key=value pairs as environment variables. That&#8217;s how our script will know what those are at run time. Next we pass through the <em>\/dev\/ttyACM0<\/em> device from the host machine to the docker container. If we modify that line to <strong>&#8211;device=\/dev\/ttyACM0:&lt;custom container path&gt;<\/strong> we can give the serial device a specific path in the container, otherwise it just gets identity mapped.  Docker sets up a default NAT network on the host on the 172.17.0.0\/24 subnet, so we will have access to this container at an IP address in that range once we start it. Also, the device hanging on \/dev\/ttyACM0 is just printing out a sequence for test data every second. <\/p>\n\n\n\n<p>So what&#8217;s it look like when it runs? From the container you see:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n \nDevice selected is '\/dev\/ttyACM0'\nPort base selected is 9000\nUDP TX: '3\n'\nUDP TX: '0\n'\nUDP TX: '1\n'\nUDP TX: '2\n'\nUDP TX: '3\n'\nUDP TX: '4\n'\nUDP TX: '5\n'\nUDP TX: '6\n'\nUDP RX: 'hello world!\n'\nUDP TX: '7\n'\nUDP TX: '8\n'\nUDP TX: '9\n'\nUDP TX: '10\n'\nUDP TX: '11\n'\nUDP TX: '12\n'\nUDP TX: '13\n'\nUDP RX: 'hello virtualization!\n'\nUDP TX: '14\n'\nUDP TX: '15\n'\nUDP TX: '16\n'\nUDP TX: '17\n'\nUDP TX: '18\n'\nUDP TX: '19\n'\nUDP TX: '20\n'\nUDP TX: '21\n'\nUDP TX: '22\n'\n'DP TX: '23\nUDP TX: '\n'\nUDP TX: '24\n\n<\/pre><\/div>\n\n\n<p>And from the netcat (nc) session on my host:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\njacob@jacob-aspire-mint \/workspace\/virtualization-for-embedded-systems-series\/containers-deep-dive\/dev-env $ nc -u 172.17.0.2 9000\nhello world!\nhello virtualization!\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n^C\n<\/pre><\/div>\n\n\n<p>I&#8217;ve typed the &#8220;hello&#8221; statements into the netcat session and it is receiving the increment sequence from the serial device via UDP on the remote end. We have successfully created and deployed a containerized application! <\/p>\n\n\n\n<h3>Exporting the Containerized Application<\/h3>\n\n\n\n<p>Since we now have a complete application all self-contained in a container, we can export this to run on other systems. In Docker, this is as simple as:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ndocker save -o deployment-environment.tar deploy-env:latest\n<\/pre><\/div>\n\n\n<p>We now have a TAR archive of all the layers building up our container. This is a portable format you can move around to deploy on different machines by importing it onto another host and running it. <\/p>\n\n\n\n<h3>Running the Containerized Application on Another Machine<\/h3>\n\n\n\n<p>I used <em>scp<\/em> to copy my TAR file to another machine and imported it into Docker using:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\njacob@dev-ubuntu:~# docker load &lt; deployment-environment.tar \nbd59016c97ec: Loading layer &#91;==================================================&gt;]  2.048kB\/2.048kB\n8c558935f47c: Loading layer &#91;==================================================&gt;]   16.9kB\/16.9kB\n0cc408f84946: Loading layer &#91;==================================================&gt;]  2.048kB\/2.048kB\nLoaded image: deploy-env:latest\njacob@dev-ubuntu:~# docker image ls\nREPOSITORY                TAG                 IMAGE ID            CREATED             SIZE\ndeploy-env                latest              d427611b8747        About an hour ago   64.2MB\n\n<\/pre><\/div>\n\n\n<p>Now we can run the app on a different machine just like our original host machine. (I omitted any parameters, I just want to see the <em>modem<\/em> binary print out errors).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\njacob@dev-ubuntu:~# docker run -it deploy-env \nDevice selected is '-p'\nFailed to open device '-p'\n\n<\/pre><\/div>\n\n\n<h2>So what&#8217;s the point?<\/h2>\n\n\n\n<p>So what&#8217;s the point? Couldn&#8217;t we just copy our <em>modem<\/em> binary to the other machine and run it in the native host? Sure, for this application, because it has no special dependencies. But imagine if your application depends on QT5, TensorFlow, and a pre-trained Data Model? Making sure all that stuff gets installed on the target host machine is a nightmare. Having a single TAR file with all dependencies built in, with your application properly parameterized ready to launch makes portability and usabilty a much easier sell, especially for embedded systems. <\/p>\n\n\n\n<p><strong>Said another way: if the only requirement to upgrade your application or add additional applications to a fielded embedded system is that you have the right container infrastructure, it is much easier to rapidly update existing capabilities and to deploy new capabilities to the edge with a high confidence of success. <\/strong><\/p>\n\n\n\n<h2>Wrapping Up<\/h2>\n\n\n\n<p>In this post, we focused on a practical example of using containers. It&#8217;s easy to see how this technology has the ability to be incredibly impactful for embedded systems. In the next post, we will take a look at type-2 hypervisors and how to use them for embedded systems.<\/p>\n\n\n\n<p>I hope you enjoyed the content of this post! If you did, feel free to comment or shoot me a message over at <a href=\"https:\/\/jacobncalvert.com\/contact\/\">the contact page!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post, I looked at several real-world use cases for containers and hypervisors. This post will be a deep dive into containers and a how-to on using them. Note: all the code, Dockerfiles, etc. are archived in a git repo at GitHub. Container History Origins A little history is needed before we jump into building and deploying containers. Docker, which is likely still the largest container technology provider by a longshot, was originally released in 2013. In just&hellip;<\/p>\n","protected":false},"author":1,"featured_media":450,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[97,81,98],"tags":[101,94,96,82,95],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v16.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Virtualization for Embedded Systems Series: Containers Deep Dive - Jacob N Calvert<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Virtualization for Embedded Systems Series: Containers Deep Dive - Jacob N Calvert\" \/>\n<meta property=\"og:description\" content=\"In the previous post, I looked at several real-world use cases for containers and hypervisors. This post will be a deep dive into containers and a how-to on using them. Note: all the code, Dockerfiles, etc. are archived in a git repo at GitHub. Container History Origins A little history is needed before we jump into building and deploying containers. Docker, which is likely still the largest container technology provider by a longshot, was originally released in 2013. In just&hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/\" \/>\n<meta property=\"og:site_name\" content=\"Jacob N Calvert\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-18T13:30:55+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-11-18T03:43:22+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/jacobncalvert.com\/wp-content\/uploads\/2019\/11\/cargo-container-lot-906494.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"839\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/#website\",\"url\":\"https:\/\/jacobncalvert.com\/blog-archive\/\",\"name\":\"Jacob N Calvert\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/jacobncalvert.com\/blog-archive\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/jacobncalvert.com\/blog-archive\/wp-content\/uploads\/2019\/11\/cargo-container-lot-906494.jpg\",\"contentUrl\":\"https:\/\/jacobncalvert.com\/blog-archive\/wp-content\/uploads\/2019\/11\/cargo-container-lot-906494.jpg\",\"width\":1280,\"height\":839},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/#webpage\",\"url\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/\",\"name\":\"Virtualization for Embedded Systems Series: Containers Deep Dive - Jacob N Calvert\",\"isPartOf\":{\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/#primaryimage\"},\"datePublished\":\"2019-11-18T13:30:55+00:00\",\"dateModified\":\"2019-11-18T03:43:22+00:00\",\"author\":{\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/#\/schema\/person\/f4b22a996d41bf09ed2bbe22912a8c8a\"},\"breadcrumb\":{\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/\",\"url\":\"https:\/\/jacobncalvert.com\/blog-archive\/\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/2019\/11\/18\/virtualization-for-embedded-systems-series-containers-deep-dive\/#webpage\"}}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/#\/schema\/person\/f4b22a996d41bf09ed2bbe22912a8c8a\",\"name\":\"Jacob\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/jacobncalvert.com\/blog-archive\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/64a2dd1c00cb39dfc19bb1204c87efbc?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/64a2dd1c00cb39dfc19bb1204c87efbc?s=96&d=mm&r=g\",\"caption\":\"Jacob\"},\"sameAs\":[\"https:\/\/jacobncalvert.com\"],\"url\":\"https:\/\/jacobncalvert.com\/blog-archive\/author\/jcalvert\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/posts\/449"}],"collection":[{"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/comments?post=449"}],"version-history":[{"count":45,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/posts\/449\/revisions"}],"predecessor-version":[{"id":530,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/posts\/449\/revisions\/530"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/media\/450"}],"wp:attachment":[{"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/media?parent=449"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/categories?post=449"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jacobncalvert.com\/blog-archive\/wp-json\/wp\/v2\/tags?post=449"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}