Technocoder - x86-64Zola2021-12-07T00:00:00+00:00https://blog.techno.fish/tags/x86-64/atom.xmlRunning x86-64 Docker Images on ARM2021-12-07T00:00:00+00:002021-12-07T00:00:00+00:00https://blog.techno.fish/running-x86-64-docker-images-on-arm/<p>Need to run Intel-based Docker images on a Linux ARM machine? Run this command:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#eb6772;">docker</span><span style="color:#abb2bf;"> run</span><span style="color:#eb6772;"> --rm --privileged</span><span style="color:#abb2bf;"> aptman/qus</span><span style="color:#eb6772;"> -s</span><span style="color:#adb7c9;"> --</span><span style="color:#abb2bf;"> -p x86_64
</span></code></pre>
<p>That's it! Try running your Docker image. Keep reading to learn how it works and what you need to be aware of.</p>
<h2 id="how-it-works">How it works</h2>
<p>When you run any executable, it gets sent to the kernel so that it can check its architecture. You might have seen this error message before:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#abb2bf;">exec user process caused "exec format error"
</span></code></pre>
<p>This means the architecture didn't match up with what's supported for your system. The Linux kernel has a feature called <a href="https://en.wikipedia.org/wiki/Binfmt_misc"><code>binfmt_misc</code></a> which allows us to define our own handlers for arbitrary executable formats. We use this feature to load <code>x86-64</code> executables using QEMU.</p>
<p><a href="https://www.qemu.org">QEMU</a> is a suite of machine emulators for a huge variety of system architectures. In this case, we don't actually emulate a system with a kernel but instead, we use QEMU's <a href="https://www.qemu.org/docs/master/user/main.html">user space emulator</a>. This means it has much less overhead than emulating a complete system. Privileged actions like writing to files or sending network requests are delegated directly to the Linux kernel.</p>
<p>The <a href="https://github.com/dbhi/qus">qus</a> Docker image packages this whole setup so that all you need to do is run it as a container. It contains the required QEMU binaries and also automatically registers them into <code>binfmt_misc</code>. As it requires direct access to the Linux kernel, we use the <code>--privileged</code> Docker flag. You can learn more about it on its project page.</p>
<h2 id="caveats">Caveats</h2>
<h3 id="performance">Performance</h3>
<p>As we're emulating (not virtualizing!) the <code>x86-64</code> architecture, the performance penalty is going to be significant. That is, it could be up to an order of magnitude slower for some workloads. I've found that it works fine for a small web server.</p>
<h3 id="reboots">Reboots</h3>
<p>The Linux kernel resets any registered <code>binfmt_misc</code> handlers on system reboots. To persist these handlers across reboots, you can use <code>crontab</code> to run a script containing the Docker command at startup:</p>
<pre style="background-color:#2b303b;">
<code><span style="color:#eb6772;">$</span><span style="color:#abb2bf;"> crontab</span><span style="color:#eb6772;"> -e
</span></code></pre><pre style="background-color:#2b303b;">
<code><span style="font-style:italic;color:#5f697a;"># ...
</span><span style="color:#eb6772;">@reboot</span><span style="color:#abb2bf;"> sh $</span><span style="color:#eb6772;">HOME</span><span style="color:#abb2bf;">/script.sh
</span></code></pre><h2 id="further-resources">Further resources</h2>
<ul>
<li><code>qus</code> homepage: <a href="https://dbhi.github.io/qus/">https://dbhi.github.io/qus/</a></li>
<li><code>multiarch/qemu-user-static</code> - a similar tool for <code>x86-64</code> hosts: <a href="https://github.com/multiarch/qemu-user-static">https://github.com/multiarch/qemu-user-static</a></li>
</ul>