{"title":"Tianon's Ramblings \u273f","link":[{"@attributes":{"href":"https:\/\/ram.tianon.xyz\/"}},{"@attributes":{"type":"application\/atom+xml","rel":"self","href":"https:\/\/ram.tianon.xyz\/atom.xml"}}],"updated":"2026-05-20T19:42:39-07:00","id":"https:\/\/ram.tianon.xyz","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"entry":[{"id":"https:\/\/ram.tianon.xyz\/post\/2026\/05\/20\/container-security","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2026\/05\/20\/container-security.html"}},"title":"Containers Are a Security Boundary (some assembly required)","published":"2026-05-20T00:00:00-07:00","updated":"2026-05-20T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I've heard \"containers are not a security boundary\" enough times that it's started to feel like received wisdom, and my honest read (after 13+ years) is that it's <em>technically<\/em> defensible but practically sloppy \u2013 and the sloppiness matters.<\/p>\n\n<p>The part that's true: containers share a kernel, and a kernel exploit crosses the container boundary where a VM would not.  That difference is real and non-trivial, and the CVE history backs it up \u2013 CVE-2019-5736, CVE-2022-0492, and CVE-2024-21626 all happened in \"correctly configured\" production containers.<\/p>\n\n<p>The part I'd push back on is that the comparison point is almost never stated.  \"Containers aren't a security boundary\" is being used as shorthand for \"containers aren't a VM boundary\" \u2013 but the conclusion people seem to draw from that is \"therefore don't bother\", which doesn't actually follow.  The more honest version is that <em>default<\/em> Docker doesn't provide strong isolation between mutually untrusting parties, but a hardened configuration does.<\/p>\n\n<p>What ships by default in Moby is actually a pretty reasonable foundation: seccomp is enabled (with a builtin profile blocking ~50 syscalls \u2013 credit where it's due: this is mostly <a href=\"https:\/\/github.com\/jessfraz\">@jessfraz<\/a>'s work; she even ran <a href=\"https:\/\/github.com\/genuinetools\/contained.af\">contained.af<\/a> as a public CTF for years daring people to escape a container under her seccomp profile, and to my knowledge it was never claimed), AppArmor is enabled (the <code class=\"language-plaintext highlighter-rouge\">docker-default<\/code> profile), and several sensitive <code class=\"language-plaintext highlighter-rouge\">\/proc<\/code> paths are masked.  What's <em>not<\/em> on by default: <code class=\"language-plaintext highlighter-rouge\">no-new-privileges<\/code> (setuid binaries inside can escalate), <code class=\"language-plaintext highlighter-rouge\">CAP_NET_RAW<\/code> is still granted to every container (even though the kernel has supported unprivileged ICMP sockets for over a decade, meaning most modern distributions no longer need <code class=\"language-plaintext highlighter-rouge\">CAP_NET_RAW<\/code> for <code class=\"language-plaintext highlighter-rouge\">ping<\/code>), and user namespace remapping \u2013 though user namespaces aren't quite the silver bullet they might sound like; Debian <a href=\"https:\/\/bugs.debian.org\/898446\">left them disabled by default for years<\/a> because the kernel attack surface they exposed hadn't been hardened against unprivileged callers.<\/p>\n\n<p>The boundary isn't absent \u2013 it doesn't come completely pre-assembled.  With VMs, the hypervisor is there whether you asked for it or not; with containers, assembling the boundary is left as an exercise for the operator.  That's a much more solvable problem than \"the technology is incapable\", but it does mean the work falls to whoever's running the containers.<\/p>\n\n<p>So, some things you can do today without waiting for defaults to change:<\/p>\n\n<p><strong><code class=\"language-plaintext highlighter-rouge\">--user<\/code> (or <code class=\"language-plaintext highlighter-rouge\">USER<\/code> in your Dockerfile)<\/strong> is worth calling out specifically, because I think it's arguably <em>stronger<\/em> than user namespace remapping in one important way \u2013 and partly for the same reason Debian was hesitant about user namespaces in the first place.  User namespace remapping protects the host from a root-in-container escape: if you do escape, you land as an unprivileged user on the host.  But you were still root inside the container the whole time.  Running as a non-root user means you were never root anywhere.  The blast radius of a compromised process is limited whether or not it escapes, including for things like reading secrets, modifying container contents, or lateral movement within the container itself.  Most application containers have no legitimate reason to be root.<\/p>\n\n<p>Beyond that, a short list of things that are easy to enable and hard to justify leaving off:<\/p>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">--security-opt no-new-privileges<\/code> \u2013 prevents setuid binaries from escalating; can also be set daemon-wide in <code class=\"language-plaintext highlighter-rouge\">daemon.json<\/code> with <code class=\"language-plaintext highlighter-rouge\">\"no-new-privileges\": true<\/code><\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--read-only<\/code> \u2013 a read-only root filesystem means a compromised process can't easily persist tooling or modify the container (pair with a writable <code class=\"language-plaintext highlighter-rouge\">tmpfs<\/code> mount for <code class=\"language-plaintext highlighter-rouge\">\/tmp<\/code> etc as needed)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">--cap-drop NET_RAW<\/code> \u2013 or <code class=\"language-plaintext highlighter-rouge\">--cap-drop ALL<\/code> and add back only what you actually need; <code class=\"language-plaintext highlighter-rouge\">CAP_NET_RAW<\/code> is almost never legitimately needed by application containers<\/li>\n  <li>never <code class=\"language-plaintext highlighter-rouge\">--privileged<\/code> \u2013 if something seems to require it, the right answer is almost always a more targeted capability grant or bind mount, not the nuclear option<\/li>\n<\/ul>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"se\">\\<\/span>\n  <span class=\"nt\">--user<\/span> 1234:5678 <span class=\"se\">\\<\/span>\n  <span class=\"nt\">--security-opt<\/span> no-new-privileges <span class=\"se\">\\<\/span>\n  <span class=\"nt\">--read-only<\/span> <span class=\"se\">\\<\/span>\n  <span class=\"nt\">--tmpfs<\/span> \/tmp <span class=\"se\">\\<\/span>\n  <span class=\"nt\">--cap-drop<\/span> ALL <span class=\"se\">\\<\/span>\n  acme\/untrusted-workload:latest\n<\/code><\/pre><\/div><\/div>\n\n<p>None of these require a daemon restart or infrastructure changes, and stacked together they go a long way toward actually building the boundary that the defaults leave unbuilt.<\/p>\n\n<p><small>(this post was written with the assistance of \"claude my eyes right out\" but all thoughts and understanding are Tianon's)<\/small><\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2025\/07\/10\/yubi-whati","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2025\/07\/10\/yubi-whati.html"}},"title":"Yubi Whati? (YubiKeys, ECDSA, and X.509)","published":"2025-07-10T00:00:00-07:00","updated":"2025-07-10T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>Off-and-on over the last several weeks, I've been spending time trying to learn\/understand <a href=\"https:\/\/en.wikipedia.org\/wiki\/YubiKey\">YubiKeys<\/a> better, especially from the perspective of ECDSA and signing. \ud83d\udd0f<\/p>\n\n<p>I <em>had<\/em> a good mental model for how <a href=\"https:\/\/developers.yubico.com\/PIV\/Introduction\/Certificate_slots.html\">\"slots\"<\/a> work (canonically referenced by their hexadecimal names such as <code class=\"language-plaintext highlighter-rouge\">9C<\/code>), but found that it had a gap related to \"objects\"; while closing that, I was annoyed that the main reference table for this gap lives primarily in either <a href=\"https:\/\/nvlpubs.nist.gov\/nistpubs\/SpecialPublications\/NIST.SP.800-73-4.pdf#page=38\">a PDF<\/a> or <a href=\"https:\/\/github.com\/go-piv\/go-ykpiv\/blob\/aa2213243953b0862ec99abc1823240bcd4807e9\/slot.go#L56-L102\">inside<\/a> <a href=\"https:\/\/github.com\/go-piv\/piv-go\/blob\/2fae46569ad594c2c4bdd57f696967ac396e1d5e\/v2\/piv\/key.go#L377-L414\">several<\/a> <a href=\"https:\/\/github.com\/Yubico\/yubico-piv-tool\/blob\/73db815e8927028c51ba71771a6737efaa238a62\/lib\/util.c#L1400-L1432\">implementations<\/a>, so I figured I should create the reference I want to see in the world, but that it would also be useful to write down some of my understanding for my own (and maybe others') future reference. \ud83d\ude0e<\/p>\n\n<p>So, to that end, I'm going to start with a bit (\u2757) of background information, with the heavy caveat that this only applies to \"PIV\" (<a href=\"https:\/\/en.wikipedia.org\/wiki\/FIPS_201\">\"FIPS 201\"<\/a>) usage of YubiKeys, and that I only actually care about ECDSA, although I've been reassured that it's the same for at least RSA (anything outside this is firmly Here Be Not Tianon; <a href=\"https:\/\/www.urbandictionary.com\/define.php?term=gl%20hf%20dd\">\"gl hf dd\"<\/a>). \ud83d\udc4d<\/p>\n\n<p><small>(Incidentally, learning all this helped me actually appreciate the simplicity of cloud-based KMS solutions, which was an unexpected side effect. \ud83d\ude2c)<\/small><\/p>\n\n<p>At a really high level, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Elliptic_Curve_Digital_Signature_Algorithm\">ECDSA<\/a> is like many other (asymmetric) cryptographic solutions \u2013 you've got a public key and a private key, the private key can be used to \"sign\" data (tiny amounts of data, in fact, like P-256 can only reasonably sign 256 bits of data, which is where cryptographic hashes like SHA256 come in as secure analogues for larger data in small bit sizes), and the public key can then be used to verify that the data was indeed signed by the private key, and only someone with the private key could've done so.  There's some complex math and RNGs involved, but none of that's actually relevant to this post, so find that information elsewhere. \ud83d\ude48<\/p>\n\n<p>Unfortunately, this is where things go off the rails: PIV is X.509 (\"x509\") heavy, and there's no X.509 in the na\u00efve view of my use case. \ud83d\ude1e<\/p>\n\n<p>In a YubiKey <small>(or any other PIV-signing-supporting smart card? do they actually <em>have<\/em> competitors in this specific niche? \ud83e\udd14)<\/small>, a given \"slot\" can hold one single private key.  There are ~24 slots which can hold a private key and be used for signing, although \"Slot 9c\" is officially designated as the \"Digital Signature\" slot and is encouraged for signing purposes. \ud83c\udf08\u2b50<\/p>\n\n<p>One of the biggest gotchas is that with pure-PIV (and older YubiKey firmware \ud83e\udd2c) the <em>public key<\/em> for a given slot is <em>only<\/em> available at the time the key is generated, and the whole point of the device in the first place is that the private key is never, ever available from it (all cryptographic operations happen <em>inside<\/em> the device), so if you don't save that public key when you first ask the device to generate a private key in a particular slot, the public key is lost forever (<a href=\"https:\/\/developers.yubico.com\/PIV\/Introduction\/PIV_attestation.html\">asterisk<\/a>). \ud83d\ude4a<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># generate a new ECDSA P-256 key in \"slot 9c\" (\"Digital Signature\")<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># WARNING: THIS WILL GLEEFULLY WIPE SLOT 9C WITHOUT PROMPTING<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span>yubico-piv-tool <span class=\"nt\">--slot<\/span> 9c <span class=\"nt\">--algorithm<\/span> ECCP256 <span class=\"nt\">--action<\/span> generate\n<span class=\"go\">-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtGoWRGyjjUlJFXpu8BL6Rnx8jjKR\n5+Mzl2Vepgor+k7N9q7ppOtSMWefjFVR0SEPmXqXINNsCi6LpLtNEigIRg==\n-----END PUBLIC KEY-----\nSuccessfully generated a new private key.\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># this is the only time\/place we (officially) get this public key<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>With that background, now let's get to the second aspect of \"slots\" and how <a href=\"https:\/\/en.wikipedia.org\/wiki\/X.509\">X.509<\/a> fits.  For every aforementioned slot, there is a corresponding \"object\" (read: place to store arbitrary data) which is corresponding <em>only by convention<\/em>.  For all these \"key\" slots the (again, by convention) corresponding \"object\" is explicitly supposed to be an X.509 certificate (see also the PDF reference linked above). \ud83d\ude49<\/p>\n\n<p>It turns out this is a useful and topical place to store that public key we need to keep handy!  It's also an interesting place to shove additional details about what the key in a given slot is being used for, if that's your thing.  Converting the raw public key into a (likely self-signed) X.509 certificate is an exercise for the reader, but if you want to follow the conventions, you need some way to convert a given \"slot\" to the corresponding \"object\", and <em>that<\/em> is the lookup table I wish existed in more forms. \ud83d\udd73<\/p>\n\n<p>So, without further ado, here is the anti-climax: \ud83d\udcab<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>Slot<\/th>\n      <th>Object<\/th>\n      <th>Description<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">9A<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC105<\/code><\/td>\n      <td>X.509 Certificate for PIV Authentication<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">9E<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC101<\/code><\/td>\n      <td>X.509 Certificate for Card Authentication<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">9C<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC10A<\/code><\/td>\n      <td>X.509 Certificate for Digital Signature<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">9D<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC10B<\/code><\/td>\n      <td>X.509 Certificate for Key Management<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">82<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC10D<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 1<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">83<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC10E<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 2<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">84<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC10F<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 3<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">85<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC110<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 4<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">86<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC111<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 5<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">87<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC112<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 6<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">88<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC113<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 7<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">89<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC114<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 8<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">8A<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC115<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 9<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">8B<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC116<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 10<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">8C<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC117<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 11<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">8D<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC118<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 12<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">8E<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC119<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 13<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">8F<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC11A<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 14<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">90<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC11B<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 15<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">91<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC11C<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 16<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">92<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC11D<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 17<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">93<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC11E<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 18<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">94<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC11F<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 19<\/td>\n    <\/tr>\n    <tr>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">95<\/code><\/td>\n      <td><small>0x<\/small><code class=\"language-plaintext highlighter-rouge\">5FC120<\/code><\/td>\n      <td>Retired X.509 Certificate for Key Management 20<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n<p>See also <a href=\"\/json\/piv-objects.json\">\"piv-objects.json\"<\/a> for a machine-readable copy of this data. \ud83d\udc40\ud83e\udd16\ud83d\udcbb\ud83d\udcbe<\/p>\n\n<p><small>(Major thanks to <a href=\"https:\/\/pault.ag\">paultag<\/a> and <a href=\"https:\/\/jon.dag.dev\/\">jon<\/a> <a href=\"https:\/\/dag.dev\">gzip<\/a> <a href=\"https:\/\/github.com\/jonjohnsonjr\">johnson<\/a> for helping me learn and generally putting up with me, but especially dealing with my live-stream-of-thoughts while I stumble through the dark. \ud83d\udc96)<\/small><\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2021\/03\/16\/docker-setup-reredux","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2021\/03\/16\/docker-setup-reredux.html"}},"title":"My Docker Install Process (re-redux)","published":"2021-03-16T00:00:00-07:00","updated":"2021-03-16T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>See <a href=\"\/post\/2016\/12\/07\/docker-setup.html\">\"My Docker Install Process\"<\/a> and <a href=\"\/post\/2017\/05\/18\/docker-setup-redux.html\">\"My Docker Install Process (redux)\"<\/a>.  This one's going to be even more to-the-point.<\/p>\n\n<h1 id=\"grab-dockers-apt-repo-gpg-key\">grab Docker's APT repo GPG key<\/h1>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">GNUPGHOME<\/span><span class=\"o\">=<\/span><span class=\"s2\">\"<\/span><span class=\"si\">$(<\/span><span class=\"nb\">mktemp<\/span> <span class=\"nt\">-d<\/span><span class=\"si\">)<\/span><span class=\"s2\">\"<\/span><span class=\"p\">;<\/span> <span class=\"nb\">export <\/span>GNUPGHOME\ngpg <span class=\"nt\">--keyserver<\/span> keyserver.ubuntu.com <span class=\"nt\">--recv-keys<\/span> 9DC858229FC7DD38854AE2D88D81803C0EBFCD88\n<span class=\"nb\">sudo mkdir<\/span> <span class=\"nt\">-p<\/span> \/etc\/apt\/tianon.gpg.d\ngpg <span class=\"nt\">--export<\/span> <span class=\"nt\">--armor<\/span> 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 | <span class=\"nb\">sudo tee<\/span> \/etc\/apt\/tianon.gpg.d\/docker.gpg.asc\n<span class=\"nb\">rm<\/span> <span class=\"nt\">-rf<\/span> <span class=\"s2\">\"<\/span><span class=\"nv\">$GNUPGHOME<\/span><span class=\"s2\">\"<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"add-dockers-apt-source\">add Docker's APT source<\/h1>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source<\/span> \/etc\/os-release\n<span class=\"nb\">echo<\/span> <span class=\"s2\">\"deb [ arch=amd64 signed-by=\/etc\/apt\/tianon.gpg.d\/docker.gpg.asc ] https:\/\/download.docker.com\/linux\/debian <\/span><span class=\"nv\">$VERSION_CODENAME<\/span><span class=\"s2\"> stable\"<\/span> | <span class=\"nb\">sudo tee<\/span> \/etc\/apt\/sources.list.d\/docker.list\n<\/code><\/pre><\/div><\/div>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt update\n<span class=\"c\">...\n<\/span><span class=\"go\">Get:6 https:\/\/download.docker.com\/linux\/debian buster\/stable amd64 Packages [17.8 kB]\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">Reading package lists... Done\n<\/span><\/code><\/pre><\/div><\/div>\n\n<h1 id=\"exclude-unwated-cli-plugins\">exclude (unwated) CLI plugins<\/h1>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo<\/span> <span class=\"s1\">'path-exclude \/usr\/libexec\/docker\/cli-plugins\/*'<\/span> | <span class=\"nb\">sudo tee<\/span> \/etc\/dpkg\/dpkg.cfg.d\/unwanted-docker-cli-plugins\n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"pin-docker-versions\">pin Docker versions<\/h1>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">sudo <\/span>vim \/etc\/apt\/preferences.d\/docker.pref\n<\/code><\/pre><\/div><\/div>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">Package<\/span><span class=\"pi\">:<\/span> <span class=\"nv\">*aufs<\/span><span class=\"err\">*<\/span> <span class=\"nv\">*rootless<\/span><span class=\"err\">*<\/span> <span class=\"s\">cgroupfs-mount docker-*-plugin<\/span>\n<span class=\"na\">Pin<\/span><span class=\"pi\">:<\/span> <span class=\"s\">version *<\/span>\n<span class=\"na\">Pin-Priority<\/span><span class=\"pi\">:<\/span> <span class=\"s\">-10<\/span>\n\n<span class=\"na\">Package<\/span><span class=\"pi\">:<\/span> <span class=\"s\">docker*<\/span>\n<span class=\"na\">Pin<\/span><span class=\"pi\">:<\/span> <span class=\"s\">version 5:20.10*<\/span>\n<span class=\"na\">Pin-Priority<\/span><span class=\"pi\">:<\/span> <span class=\"m\">999<\/span>\n\n<span class=\"na\">Package<\/span><span class=\"pi\">:<\/span> <span class=\"s\">containerd*<\/span>\n<span class=\"na\">Pin<\/span><span class=\"pi\">:<\/span> <span class=\"s\">version 1.4*<\/span>\n<span class=\"na\">Pin-Priority<\/span><span class=\"pi\">:<\/span> <span class=\"m\">999<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"pre-configure-docker\">pre-configure Docker<\/h1>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">sudo mkdir<\/span> <span class=\"nt\">-p<\/span> \/etc\/docker\n<span class=\"nb\">sudo <\/span>vim \/etc\/docker\/daemon.json\n<\/code><\/pre><\/div><\/div>\n\n<div class=\"language-json highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">{<\/span><span class=\"w\">\n\t<\/span><span class=\"nl\">\"storage-driver\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"overlay2\"<\/span><span class=\"w\">\n<\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/code><\/pre><\/div><\/div>\n\n<h1 id=\"configure-boot-parameters\">configure boot parameters<\/h1>\n\n<blockquote>\n  <p>I usually set a few boot parameters as well (in <code class=\"language-plaintext highlighter-rouge\">\/etc\/default\/grub<\/code>'s <code class=\"language-plaintext highlighter-rouge\">GRUB_CMDLINE_LINUX_DEFAULT<\/code> option \u2013 run <code class=\"language-plaintext highlighter-rouge\">sudo update-grub<\/code> after adding these, space-separated).<\/p>\n<\/blockquote>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">cgroup_enable=memory<\/code> \u2013 enable \"memory accounting\" for containers (allows <code class=\"language-plaintext highlighter-rouge\">docker run --memory<\/code> for setting hard memory limits on containers)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">swapaccount=1<\/code> \u2013 enable \"swap accounting\" for containers (allows <code class=\"language-plaintext highlighter-rouge\">docker run --memory-swap<\/code> for setting hard swap memory limits on containers)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">vsyscall=emulate<\/code> \u2013 allow older binaries to run (<code class=\"language-plaintext highlighter-rouge\">debian:wheezy<\/code>, etc.; see <a href=\"https:\/\/github.com\/docker\/docker\/issues\/28705\">docker\/docker#28705<\/a>)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">systemd.legacy_systemd_cgroup_controller=yes<\/code> \u2013 newer versions of systemd <em>may<\/em> disable the legacy cgroup interfaces Docker currently uses; this instructs systemd to keep those enabled (for more details, see <a href=\"https:\/\/github.com\/systemd\/systemd\/pull\/4628\">systemd\/systemd#4628<\/a>, <a href=\"https:\/\/github.com\/opencontainers\/runc\/issues\/1175\">opencontainers\/runc#1175<\/a>, <a href=\"https:\/\/github.com\/docker\/docker\/issues\/28109\">docker\/docker#28109<\/a>)\n    <ul>\n      <li>NOTE: this one gets more complicated in Debian 11+ (\"Bullseye\"); possibly worth switching to cgroupv2 and <code class=\"language-plaintext highlighter-rouge\">systemd.unified_cgroup_hierarchy=1<\/code><\/li>\n    <\/ul>\n  <\/li>\n<\/ul>\n\n<p>All together:<\/p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>...\n<span class=\"nv\">GRUB_CMDLINE_LINUX_DEFAULT<\/span><span class=\"o\">=<\/span><span class=\"s2\">\"cgroup_enable=memory swapaccount=1 vsyscall=emulate systemd.legacy_systemd_cgroup_controller=yes\"<\/span>\n...\n<\/code><\/pre><\/div><\/div>\n\n<p>(Don't forget to <code class=\"language-plaintext highlighter-rouge\">sudo update-grub<\/code> and potentially reboot \u2013 check <code class=\"language-plaintext highlighter-rouge\">\/proc\/cmdline<\/code> to verify.)<\/p>\n\n<h1 id=\"install-docker\">install Docker!<\/h1>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt-get <span class=\"nb\">install<\/span> <span class=\"nt\">-V<\/span> docker-ce\n<span class=\"c\">...\n<\/span><span class=\"go\">Unpacking containerd.io (1.4.4-1) ...\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">Unpacking docker-ce-cli (5:20.10.5~3-0~debian-buster) ...\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">Unpacking docker-ce (5:20.10.5~3-0~debian-buster) ...\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>usermod <span class=\"nt\">-aG<\/span> docker <span class=\"s2\">\"<\/span><span class=\"si\">$(<\/span><span class=\"nb\">id<\/span> <span class=\"nt\">-un<\/span><span class=\"si\">)<\/span><span class=\"s2\">\"<\/span>\n<\/code><\/pre><\/div><\/div>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2018\/01\/11\/iscsi","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2018\/01\/11\/iscsi.html"}},"title":"iSCSI in Debian","published":"2018-01-11T00:00:00-08:00","updated":"2018-01-11T00:00:00-08:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I've recently been playing with Debian's iSCSI support, and it's pretty neat.<\/p>\n\n<p>It was a little esoteric to set things up, so I figured I'd write up a quick blog post of exactly what I did both for my own future-self's sake and for the sake of anyone else trying to do something similar.<\/p>\n\n<p>The most \"followable\" guide I found was <a href=\"https:\/\/www.certdepot.net\/rhel7-configure-iscsi-target-initiator-persistently\/\">https:\/\/www.certdepot.net\/rhel7-configure-iscsi-target-initiator-persistently\/<\/a> (which the below is probably really similar to).<\/p>\n\n<p>The exact details of what I was trying to accomplish are as follows:<\/p>\n\n<ul>\n  <li>100GB \"sparse\" file on <code class=\"language-plaintext highlighter-rouge\">my-desktop<\/code><\/li>\n  <li>presented as an iSCSI target<\/li>\n  <li>mounted on <code class=\"language-plaintext highlighter-rouge\">my-rpi3<\/code> as <code class=\"language-plaintext highlighter-rouge\">\/var\/lib\/docker<\/code> (preferably with <code class=\"language-plaintext highlighter-rouge\">discard<\/code> enabled so the file on <code class=\"language-plaintext highlighter-rouge\">my-desktop<\/code> stays sparse)<\/li>\n<\/ul>\n\n<p>On <code class=\"language-plaintext highlighter-rouge\">my-desktop<\/code>, I used the <code class=\"language-plaintext highlighter-rouge\">targetcli-fb<\/code> package to configure my iSCSI target:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt <span class=\"nb\">install <\/span>targetcli-fb\n<span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># create the sparse file<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">mkdir<\/span> <span class=\"nt\">-p<\/span> \/home\/tianon\/iscsi\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">truncate<\/span> <span class=\"nt\">--size<\/span><span class=\"o\">=<\/span>100G \/home\/tianon\/iscsi\/my-rpi3-docker.img\n<span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># launch \"targetcli\" to configure the iSCSI bits<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>targetcli\n<span class=\"go\">\n<\/span><span class=\"gp\">#<\/span><span class=\"w\"> <\/span>create a <span class=\"s2\">\"fileio\"<\/span> object connected to the new sparse file\n<span class=\"gp\">\/&gt;<\/span><span class=\"w\"> <\/span>\/backstores\/fileio create <span class=\"nv\">name<\/span><span class=\"o\">=<\/span>my-rpi3-docker <span class=\"nv\">file_or_dev<\/span><span class=\"o\">=<\/span>\/home\/tianon\/iscsi\/my-rpi3-docker.img\n<span class=\"go\">\n<\/span><span class=\"gp\">#<\/span><span class=\"w\"> <\/span><span class=\"nb\">enable<\/span> <span class=\"s2\">\"emulated TPU\"<\/span> <span class=\"o\">(<\/span><span class=\"nb\">enable <\/span>TRIM \/ UNMAP \/ DISCARD<span class=\"o\">)<\/span>\n<span class=\"gp\">\/&gt;<\/span><span class=\"w\"> <\/span>\/backstores\/fileio\/my-rpi3-docker <span class=\"nb\">set <\/span>attribute <span class=\"nv\">emulate_tpu<\/span><span class=\"o\">=<\/span>1\n<span class=\"go\">\n<\/span><span class=\"gp\">#<\/span><span class=\"w\"> <\/span>create iSCSI storage object\n<span class=\"gp\">\/&gt;<\/span><span class=\"w\"> <\/span>\/iscsi create iqn.1992-01.com.example.my-desktop:storage:my-rpi3-docker\n<span class=\"go\">\n<\/span><span class=\"gp\">#<\/span><span class=\"w\"> <\/span>create <span class=\"s2\">\"LUN\"<\/span> assigned to the <span class=\"s2\">\"fileio\"<\/span> object\n<span class=\"gp\">\/&gt;<\/span><span class=\"w\"> <\/span>\/iscsi\/iqn.1992-01.com.example.my-desktop:storage:my-rpi3-docker\/tpg1\/luns create \/backstores\/fileio\/my-rpi3-docker\n<span class=\"go\">\n<\/span><span class=\"gp\">#<\/span><span class=\"w\"> <\/span>create an ACL <span class=\"k\">for <\/span>my-rpi3 to connect\n<span class=\"gp\">\/&gt;<\/span><span class=\"w\"> <\/span>\/iscsi\/iqn.1992-01.com.example.my-desktop:storage:my-rpi3-docker\/tpg1\/acls create iqn.1992-01.com.example:node:my-rpi3\n<span class=\"gp\">#<\/span><span class=\"w\"> <\/span>and <span class=\"nb\">set <\/span>a CHAP username and password, <span class=\"k\">for <\/span>security\n<span class=\"gp\">\/&gt;<\/span><span class=\"w\"> <\/span>\/iscsi\/iqn.1992-01.com.example.my-desktop:storage:my-rpi3-docker\/tpg1\/acls\/iqn.1992-01.com.example:node:my-rpi3 <span class=\"nb\">set <\/span>auth <span class=\"nv\">userid<\/span><span class=\"o\">=<\/span>rpi3 <span class=\"nv\">password<\/span><span class=\"o\">=<\/span>holy-cow-this-iscsi-password-is-so-secret-nobody-will-evvvvvvvvver-guess-it\n<\/code><\/pre><\/div><\/div>\n\n<p>Additionally, I've been experimenting with <code class=\"language-plaintext highlighter-rouge\">firewalld<\/code> on <code class=\"language-plaintext highlighter-rouge\">my-desktop<\/code>, so I had to add the <code class=\"language-plaintext highlighter-rouge\">iscsi-target<\/code> service to my <code class=\"language-plaintext highlighter-rouge\">internal<\/code> zone to allow the traffic from <code class=\"language-plaintext highlighter-rouge\">my-rpi3<\/code>.<\/p>\n\n<p>On <code class=\"language-plaintext highlighter-rouge\">my-rpi3<\/code>, I used the <code class=\"language-plaintext highlighter-rouge\">open-iscsi<\/code> package to configure my iSCSI initiator:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt <span class=\"nb\">install <\/span>open-iscsi\n<span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># update \"InitiatorName\" to match the value from our ACL above<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>vim \/etc\/iscsi\/initiatorname.iscsi\n<span class=\"go\">InitiatorName=iqn.1992-01.com.example:node:my-rpi3\n\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># update \"node.startup\" and \"node.session.auth.*\" for our CHAP credentials from above<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>vim \/etc\/iscsi\/iscsid.conf\n<span class=\"c\">...\n<\/span><span class=\"go\">node.startup = automatic\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">node.session.auth.authmethod = CHAP\nnode.session.auth.username = rpi3\nnode.session.auth.password = holy-cow-this-iscsi-password-is-so-secret-nobody-will-evvvvvvvvver-guess-it\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">\n<\/span><span class=\"gp\">#<\/span><span class=\"w\"> <\/span>restart iscsid so all that takes effect <span class=\"o\">(<\/span>especially the InitiatorName change<span class=\"o\">)<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>systemctl restart iscsid\n<span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>iscsiadm <span class=\"nt\">--mode<\/span> discovery <span class=\"nt\">--type<\/span> sendtargets <span class=\"nt\">--portal<\/span> my-desktop-ip-address\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>iscsiadm <span class=\"nt\">--mode<\/span> node <span class=\"nt\">--targetname<\/span> iqn.1992-01.com.example.my-desktop:storage:my-rpi3-docker <span class=\"nt\">--portal<\/span> my-desktop-ip-address <span class=\"nt\">--login<\/span>\n<span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>lsblk <span class=\"nt\">--scsi<\/span>\n<span class=\"go\">NAME HCTL       TYPE VENDOR   MODEL             REV TRAN\nsda  0:0:0:0    disk LIO-ORG  my-rpi3-docker   4.0  iscsi\n\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>fdisk \/dev\/sda\n<span class=\"c\">...\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>mkfs.ext4 <span class=\"nt\">-T<\/span> news <span class=\"nt\">-L<\/span> my-rpi3-docker \/dev\/sda1\n<span class=\"c\">...\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>lsblk | <span class=\"nb\">grep <\/span>my-rpi3-docker\n<span class=\"go\">... UUID=\"xxx\" ...\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>vim \/etc\/fstab\n<span class=\"c\">...\n<\/span><span class=\"go\">UUID=\"xxx\" \/var\/lib\/docker ext4 noatime,discard,_netdev 0 0\n<\/span><span class=\"c\">...\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>systemctl stop docker\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>mount \/var\/lib\/docker\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>systemctl start docker\n<span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"c\"># yay, profit (and should auto-remount properly on boot and everything, too)<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(Obviously, replace <code class=\"language-plaintext highlighter-rouge\">iqn.1992-01.com.example<\/code> with an appropriate IQN for your own domain <a href=\"https:\/\/en.wikipedia.org\/wiki\/ISCSI#Addressing\">as described on Wikipedia<\/a>, and other values as appropriate like the username\/password, hostnames, IPs, etc.)<\/p>\n\n<p>As for speed, I was able to get the following result from a very simplified <code class=\"language-plaintext highlighter-rouge\">dd<\/code>-based speed test \u2013 YMMV:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">dd <\/span><span class=\"k\">if<\/span><span class=\"o\">=<\/span>\/dev\/zero <span class=\"nv\">of<\/span><span class=\"o\">=<\/span>\/var\/lib\/docker\/testfile <span class=\"nv\">bs<\/span><span class=\"o\">=<\/span>100M <span class=\"nv\">count<\/span><span class=\"o\">=<\/span>10 <span class=\"nv\">oflag<\/span><span class=\"o\">=<\/span>direct\n<span class=\"go\">10+0 records in\n10+0 records out\n1048576000 bytes (1.0 GB, 1000 MiB) copied, 97.9608 s, 10.7 MB\/s\n<\/span><\/code><\/pre><\/div><\/div>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2017\/12\/26\/dockerize-compiled-software","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2017\/12\/26\/dockerize-compiled-software.html"}},"title":"Dockerizing Compiled Software","published":"2017-12-26T00:00:00-08:00","updated":"2017-12-26T00:00:00-08:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I recently went through a stint of closing a <em>huge<\/em> number of issues in the <a href=\"https:\/\/github.com\/docker-library\/php\">docker-library\/php<\/a> repository, and one of the oldest (and longest) discussions was related to installing dependencies for a compiling extensions, and I wrote a <a href=\"https:\/\/github.com\/docker-library\/php\/issues\/75#issuecomment-353673374\">semi-long comment<\/a> explaining how I do this in a general way for any software I wish to Dockerize.<\/p>\n\n<p>I'm going to copy most of that comment here and perhaps expand a little bit more in order to have a better\/cleaner place to link to!<\/p>\n\n<p>The first step I take is to write the na\u00efve version of the <code class=\"language-plaintext highlighter-rouge\">Dockerfile<\/code>: download the source, run <code class=\"language-plaintext highlighter-rouge\">.\/configure &amp;&amp; make<\/code> etc, clean up.  I then try building my na\u00efve creation, and in doing so hope for an error message. (yes, really!)<\/p>\n\n<p>The error message will usually take the form of something like <code class=\"language-plaintext highlighter-rouge\">error: could not find \"xyz.h\"<\/code> or <code class=\"language-plaintext highlighter-rouge\">error: libxyz development headers not found<\/code>.<\/p>\n\n<p>If I'm building in Debian, I'll hit <code class=\"language-plaintext highlighter-rouge\">https:\/\/packages.debian.org\/file:xyz.h<\/code> (replacing \"xyz.h\" with the name of the header file from the error message), or even just Google something like \"xyz.h debian\", to figure out the name of the package I require.<\/p>\n\n<p>If I'm building in Alpine, I'll use <a href=\"https:\/\/pkgs.alpinelinux.org\/contents\">https:\/\/pkgs.alpinelinux.org\/contents<\/a> to perform a similar search.<\/p>\n\n<p>The same works to some extent for \"libxyz development headers\", but in my experience Google works better for those since different distributions and projects will call these development packages by different names, so sometimes it's a little harder to figure out exactly which one is the \"right\" one to install.<\/p>\n\n<p>Once I've got a package name, I add that package name to my <code class=\"language-plaintext highlighter-rouge\">Dockerfile<\/code>, rinse, and repeat.  Eventually, this usually leads to a successful build.  Occasionally I find that some library either isn't in Debian or Alpine, or isn't new enough, and I've also got to build it from source, but those instances are rare in my own experience \u2013 YMMV.<\/p>\n\n<p>I'll also often check the source for the Debian (via <a href=\"https:\/\/sources.debian.org\">https:\/\/sources.debian.org<\/a>) or Alpine (via <a href=\"https:\/\/git.alpinelinux.org\/cgit\/aports\/tree\">https:\/\/git.alpinelinux.org\/cgit\/aports\/tree<\/a>) package of the software I'm looking to compile, especially paying attention to <code class=\"language-plaintext highlighter-rouge\">Build-Depends<\/code> (ala <a href=\"https:\/\/sources.debian.org\/src\/php7.0\/7.0.26-1\/debian\/control\/\"><code class=\"language-plaintext highlighter-rouge\">php7.0=7.0.26-1<\/code>'s <code class=\"language-plaintext highlighter-rouge\">debian\/control<\/code> file<\/a>) and\/or <code class=\"language-plaintext highlighter-rouge\">makedepends<\/code> (ala <a href=\"https:\/\/git.alpinelinux.org\/cgit\/aports\/tree\/community\/php7\/APKBUILD?id=d0ca197f031f96d4664cafaa618aeccf94640a1e\"><code class=\"language-plaintext highlighter-rouge\">php7<\/code>'s <code class=\"language-plaintext highlighter-rouge\">APKBUILD<\/code> file<\/a>) for package name clues.<\/p>\n\n<p>Personally, I find this sort of detective work interesting and rewarding, but I realize I'm probably a bit of a unique creature.  Another good technique I use occasionally is to determine whether anyone else has already Dockerized the thing I'm trying to, so I can simply learn directly from their <code class=\"language-plaintext highlighter-rouge\">Dockerfile<\/code> which packages I'll need to install.<\/p>\n\n<p>For the specific case of PHP extensions, there's almost always someone who's already figured out what's necessary for this or that module, and all I have to do is some light detective work to find them.<\/p>\n\n<p>Anyways, that's my method!  Hope it's helpful, and happy hunting!<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2017\/05\/23\/debuerreotype","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2017\/05\/23\/debuerreotype.html"}},"title":"Debuerreotype","published":"2017-05-23T00:00:00-07:00","updated":"2017-05-23T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>Following in the footsteps of one of my favorite Debian Developers, <a href=\"https:\/\/github.com\/lamby\">Chris Lamb \/ lamby<\/a> (who is <a href=\"https:\/\/bugs.debian.org\/from:lamby@debian.org\">quite prolific<\/a> in the reproducible builds effort within Debian), I've started a new project based on <a href=\"http:\/\/snapshot.debian.org\">snapshot.debian.org (time-based snapshots of the Debian archive)<\/a> and some of <a href=\"https:\/\/github.com\/lamby\/debootstrap\/commit\/66b15380814aa62ca4b5807270ac57a3c8a0558d\">lamby's work<\/a> for creating reproducible Debian (<a href=\"https:\/\/www.debian.org\/releases\/stretch\/amd64\/apds03.html.en#idp54701872\"><code class=\"language-plaintext highlighter-rouge\">debootstrap<\/code><\/a>) rootfs tarballs.<\/p>\n\n<p>The project is named <a href=\"https:\/\/github.com\/debuerreotype\/debuerreotype\">\"Debuerreotype\"<\/a> as an homage to the photography roots of the word \"snapshot\" and the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Daguerreotype\">daguerreotype<\/a> process which was an early method of taking photographs.  The essential goal is to create \"photographs\" of a minimal Debian rootfs, so the name seemed appropriate (even if it's a bit on the \"mouthful\" side).<\/p>\n\n<p>The end-goal is to create and release Debian rootfs tarballs for a given point-in-time (especially for use in Docker) which should be fully reproducible, and thus improve confidence in the provenance of the <a href=\"https:\/\/hub.docker.com\/_\/debian\/\">Debian Docker base images<\/a>.<\/p>\n\n<p>For more information about reproducibility and why it matters, see <a href=\"https:\/\/reproducible-builds.org\/\">reproducible-builds.org<\/a>, which has more thorough explanations of the why and how and links to other important work such as the <a href=\"https:\/\/tests.reproducible-builds.org\/debian\/\">reproducible builds effort in Debian (for Debian package builds)<\/a>.<\/p>\n\n<p>In order to verify that the tool actually works as intended, I ran builds against seven explicit architectures (<code class=\"language-plaintext highlighter-rouge\">amd64<\/code>, <code class=\"language-plaintext highlighter-rouge\">arm64<\/code>, <code class=\"language-plaintext highlighter-rouge\">armel<\/code>, <code class=\"language-plaintext highlighter-rouge\">armhf<\/code>, <code class=\"language-plaintext highlighter-rouge\">i386<\/code>, <code class=\"language-plaintext highlighter-rouge\">ppc64el<\/code>, <code class=\"language-plaintext highlighter-rouge\">s390x<\/code>) and eight explicit suites (<code class=\"language-plaintext highlighter-rouge\">oldstable<\/code>, <code class=\"language-plaintext highlighter-rouge\">stable<\/code>, <code class=\"language-plaintext highlighter-rouge\">testing<\/code>, <code class=\"language-plaintext highlighter-rouge\">unstable<\/code>, <code class=\"language-plaintext highlighter-rouge\">wheezy<\/code>, <code class=\"language-plaintext highlighter-rouge\">jessie<\/code>, <code class=\"language-plaintext highlighter-rouge\">stretch<\/code>, <code class=\"language-plaintext highlighter-rouge\">sid<\/code>).<\/p>\n\n<p>I used a timestamp value of <code class=\"language-plaintext highlighter-rouge\">2017-05-16T00:00:00Z<\/code>, and skipped combinations that don't exist (such as <code class=\"language-plaintext highlighter-rouge\">wheezy<\/code> on <code class=\"language-plaintext highlighter-rouge\">arm64<\/code>) or aren't supported anymore (such as <code class=\"language-plaintext highlighter-rouge\">wheezy<\/code> on <code class=\"language-plaintext highlighter-rouge\">s390x<\/code>).  I ran the scripts repeatedly over several days, using <a href=\"https:\/\/diffoscope.org\/\"><code class=\"language-plaintext highlighter-rouge\">diffoscope<\/code><\/a> to compare the results.<\/p>\n\n<p>While doing said testing, I ran across <a href=\"https:\/\/bugs.debian.org\/857803\">#857803<\/a>, and <a href=\"https:\/\/github.com\/debuerreotype\/debuerreotype\/commit\/c90f2e5e6c319c31f9668cec10e93b86b46d9417#diff-70efd6067d981af974e9424ee04ca8b6\">added a workaround<\/a>.  There's also <a href=\"https:\/\/github.com\/debuerreotype\/debuerreotype\/blob\/e208ee09d83f1101aa378aa6e5a697e8ee3f0cbc\/README.md#why-isnt-wheezy-reproducible\">a minor outstanding issue with <code class=\"language-plaintext highlighter-rouge\">wheezy<\/code>'s reproducibility<\/a> that I haven't had a chance to dig deep very deeply into yet (but it's pretty benign and Wheezy's LTS support window ends <a href=\"https:\/\/wiki.debian.org\/LTS\">2018-05-31<\/a>, so I'm not too stressed about it).<\/p>\n\n<p>I've also packaged the tool for Debian, and submitted it into the <a href=\"https:\/\/ftp-master.debian.org\/new.html\">NEW queue<\/a>, so hopefully the <a href=\"https:\/\/ftp-master.debian.org\/\">FTP Masters<\/a> will look favorably upon this being a tool that's available to install from the Debian archive as well. \ud83d\ude07<\/p>\n\n<p>Anyhow, please <a href=\"https:\/\/github.com\/debuerreotype\/debuerreotype\/blob\/e208ee09d83f1101aa378aa6e5a697e8ee3f0cbc\/README.md#usage\">give it a try<\/a>, have fun, and as always, <a href=\"https:\/\/github.com\/debuerreotype\/debuerreotype\/issues\">report bugs<\/a>!<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2017\/05\/18\/docker-setup-redux","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2017\/05\/18\/docker-setup-redux.html"}},"title":"My Docker Install Process (redux)","published":"2017-05-18T00:00:00-07:00","updated":"2017-05-18T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>Since I wrote <a href=\"\/post\/2016\/12\/07\/docker-setup.html\">my first post on this topic<\/a>, Docker has switched from <a href=\"https:\/\/apt.dockerproject.org\/repo\">apt.dockerproject.org<\/a> to <a href=\"https:\/\/download.docker.com\/linux\/debian\">download.docker.com<\/a>, so this post revisits my original steps, but tailored for the new repo.<\/p>\n\n<p>There will be less commentary this time (straight to the beef).  For further commentary on \"why\" for any step, see my previous post.<\/p>\n\n<blockquote>\n  <p>These steps <em>should<\/em> be fairly similar to what's found in <a href=\"https:\/\/docs.docker.com\/engine\/installation\/linux\/debian\/\">upstream's \"Install Docker on Debian\" document<\/a>, but do differ slightly in a few minor ways.<\/p>\n<\/blockquote>\n\n<h1 id=\"grab-dockers-apt-repo-gpg-key\">grab Docker's APT repo GPG key<\/h1>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># \"Docker Release (CE deb)\"<\/span>\n\n<span class=\"nb\">export <\/span><span class=\"nv\">GNUPGHOME<\/span><span class=\"o\">=<\/span><span class=\"s2\">\"<\/span><span class=\"si\">$(<\/span><span class=\"nb\">mktemp<\/span> <span class=\"nt\">-d<\/span><span class=\"si\">)<\/span><span class=\"s2\">\"<\/span>\ngpg <span class=\"nt\">--keyserver<\/span> keyserver.ubuntu.com <span class=\"nt\">--recv-keys<\/span> 9DC858229FC7DD38854AE2D88D81803C0EBFCD88\n\n<span class=\"c\"># stretch+<\/span>\ngpg <span class=\"nt\">--export<\/span> <span class=\"nt\">--armor<\/span> 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 | <span class=\"nb\">sudo tee<\/span> \/etc\/apt\/trusted.gpg.d\/docker.gpg.asc\n\n<span class=\"c\"># jessie<\/span>\n<span class=\"c\"># gpg --export 9DC858229FC7DD38854AE2D88D81803C0EBFCD88 | sudo tee \/etc\/apt\/trusted.gpg.d\/docker.gpg &gt; \/dev\/null<\/span>\n\n<span class=\"nb\">rm<\/span> <span class=\"nt\">-rf<\/span> <span class=\"s2\">\"<\/span><span class=\"nv\">$GNUPGHOME<\/span><span class=\"s2\">\"<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(<strong>Update 2017-09-29:<\/strong> If you're installing EE, the key changes to <code class=\"language-plaintext highlighter-rouge\">DD911E995A64A202E85907D6BC14F10B6D085F96<\/code>.)<\/p>\n\n<p>Verify:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>apt-key list\n<span class=\"c\">...\n<\/span><span class=\"go\">\n\/etc\/apt\/trusted.gpg.d\/docker.gpg.asc\n-------------------------------------\npub   rsa4096 2017-02-22 [SCEA]\n      9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88\n<\/span><span class=\"gp\">uid           [ unknown] Docker Release (CE deb) &lt;docker@docker.com&gt;<\/span><span class=\"w\">\n<\/span><span class=\"go\">sub   rsa4096 2017-02-22 [S]\n\n<\/span><span class=\"c\">...\n<\/span><\/code><\/pre><\/div><\/div>\n\n<h1 id=\"add-dockers-apt-source\">add Docker's APT source<\/h1>\n\n<p>With the switch to download.docker.com, HTTPS is now mandated:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>apt-get update <span class=\"o\">&amp;&amp;<\/span> apt-get <span class=\"nb\">install <\/span>apt-transport-https\n<\/code><\/pre><\/div><\/div>\n\n<p>Setup <code class=\"language-plaintext highlighter-rouge\">sources.list<\/code>:<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo<\/span> <span class=\"s2\">\"deb [ arch=amd64 ] https:\/\/download.docker.com\/linux\/debian stretch stable\"<\/span> | <span class=\"nb\">sudo tee<\/span> \/etc\/apt\/sources.list.d\/docker.list\n<\/code><\/pre><\/div><\/div>\n\n<p>Add <code class=\"language-plaintext highlighter-rouge\">edge<\/code> component for every-month releases and <code class=\"language-plaintext highlighter-rouge\">test<\/code> for release candidates (ie, <code class=\"language-plaintext highlighter-rouge\">... stretch stable edge<\/code>).\nReplace <code class=\"language-plaintext highlighter-rouge\">stretch<\/code> with <code class=\"language-plaintext highlighter-rouge\">jessie<\/code> for Jessie installs.<\/p>\n\n<p>(<strong>Update 2017-09-29:<\/strong> If you're installing EE, replace <code class=\"language-plaintext highlighter-rouge\">https:\/\/download.docker.com\/linux\/debian<\/code> with your <code class=\"language-plaintext highlighter-rouge\">&lt;DOCKER-EE-SUBSCRIPTION-URL&gt;\/ubuntu<\/code> and use an Ubuntu suite like <code class=\"language-plaintext highlighter-rouge\">xenial<\/code> which matches your host.)<\/p>\n\n<p>At this point, you should be safe to run <code class=\"language-plaintext highlighter-rouge\">apt-get update<\/code> to verify the changes:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt-get update\n<span class=\"c\">...\n<\/span><span class=\"go\">Get:5 https:\/\/download.docker.com\/linux\/debian stretch\/stable amd64 Packages [1227 B]\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">Reading package lists... Done\n<\/span><\/code><\/pre><\/div><\/div>\n\n<p>(There shouldn't be any warnings or errors about missing keys, etc.)<\/p>\n\n<h1 id=\"configure-docker\">configure Docker<\/h1>\n\n<blockquote>\n  <p>This step could be done after Docker's installed (and indeed, that's usually when I do it because I forget that I should until I've got Docker installed and realize that my configuration is suboptimal), but doing it before ensures that Docker doesn't have to be restarted later.<\/p>\n<\/blockquote>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">sudo mkdir<\/span> <span class=\"nt\">-p<\/span> \/etc\/docker\n<span class=\"nb\">sudo <\/span>sensible-editor \/etc\/docker\/daemon.json\n<\/code><\/pre><\/div><\/div>\n\n<blockquote>\n  <p>(<code class=\"language-plaintext highlighter-rouge\">sensible-editor<\/code> can be replaced by whatever editor you prefer, but that command should choose or prompt for a reasonable default)<\/p>\n<\/blockquote>\n\n<blockquote>\n  <p>I then fill <code class=\"language-plaintext highlighter-rouge\">daemon.json<\/code> with at least a default <code class=\"language-plaintext highlighter-rouge\">storage-driver<\/code>.  Whether I use <code class=\"language-plaintext highlighter-rouge\">aufs<\/code> or <code class=\"language-plaintext highlighter-rouge\">overlay2<\/code> depends on my kernel version and available modules \u2013 if I'm on Ubuntu, AUFS is still a no-brainer (since it's included in the default kernel if the <code class=\"language-plaintext highlighter-rouge\">linux-image-extra-XXX<\/code>\/<code class=\"language-plaintext highlighter-rouge\">linux-image-extra-virtual<\/code> package is installed), but on Debian AUFS is only available in either 3.x kernels (<code class=\"language-plaintext highlighter-rouge\">jessie<\/code>'s default non-backports kernel) or recently in the <code class=\"language-plaintext highlighter-rouge\">aufs-dkms<\/code> package (as of this writing, still only available on <code class=\"language-plaintext highlighter-rouge\">stretch<\/code> and <code class=\"language-plaintext highlighter-rouge\">sid<\/code> \u2013 no <code class=\"language-plaintext highlighter-rouge\">jessie-backports<\/code> option).<\/p>\n<\/blockquote>\n\n<blockquote>\n  <p>If my kernel is 4.x+, I'm likely going to choose <code class=\"language-plaintext highlighter-rouge\">overlay2<\/code> (or if that errors out, the older <code class=\"language-plaintext highlighter-rouge\">overlay<\/code> driver).<\/p>\n<\/blockquote>\n\n<blockquote>\n  <p>Choosing an appropriate storage driver is a fairly complex topic, and I'd recommend that for serious production deployments, more research on pros and cons is performed than I'm including here (especially since AUFS and OverlayFS are <em>not<\/em> the only options \u2013 they're just the two I personally use most often).<\/p>\n<\/blockquote>\n\n<div class=\"language-json highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">{<\/span><span class=\"w\">\n\t<\/span><span class=\"nl\">\"storage-driver\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"overlay2\"<\/span><span class=\"w\">\n<\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/code><\/pre><\/div><\/div>\n\n<h1 id=\"configure-boot-parameters\">configure boot parameters<\/h1>\n\n<blockquote>\n  <p>I usually set a few boot parameters as well (in <code class=\"language-plaintext highlighter-rouge\">\/etc\/default\/grub<\/code>'s <code class=\"language-plaintext highlighter-rouge\">GRUB_CMDLINE_LINUX_DEFAULT<\/code> option \u2013 run <code class=\"language-plaintext highlighter-rouge\">sudo update-grub<\/code> after adding these, space-separated).<\/p>\n<\/blockquote>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">cgroup_enable=memory<\/code> \u2013 enable \"memory accounting\" for containers (allows <code class=\"language-plaintext highlighter-rouge\">docker run --memory<\/code> for setting hard memory limits on containers)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">swapaccount=1<\/code> \u2013 enable \"swap accounting\" for containers (allows <code class=\"language-plaintext highlighter-rouge\">docker run --memory-swap<\/code> for setting hard swap memory limits on containers)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">systemd.legacy_systemd_cgroup_controller=yes<\/code> \u2013 newer versions of systemd <em>may<\/em> disable the legacy cgroup interfaces Docker currently uses; this instructs systemd to keep those enabled (for more details, see <a href=\"https:\/\/github.com\/systemd\/systemd\/pull\/4628\">systemd\/systemd#4628<\/a>, <a href=\"https:\/\/github.com\/opencontainers\/runc\/issues\/1175\">opencontainers\/runc#1175<\/a>, <a href=\"https:\/\/github.com\/docker\/docker\/issues\/28109\">docker\/docker#28109<\/a>)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">vsyscall=emulate<\/code> \u2013 allow older binaries to run (<code class=\"language-plaintext highlighter-rouge\">debian:wheezy<\/code>, etc.; see <a href=\"https:\/\/github.com\/docker\/docker\/issues\/28705\">docker\/docker#28705<\/a>)<\/li>\n<\/ul>\n\n<p>All together:<\/p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>...\n<span class=\"nv\">GRUB_CMDLINE_LINUX_DEFAULT<\/span><span class=\"o\">=<\/span><span class=\"s2\">\"cgroup_enable=memory swapaccount=1 systemd.legacy_systemd_cgroup_controller=yes vsyscall=emulate\"<\/span>\n...\n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"install-docker\">install Docker!<\/h1>\n\n<p>Finally, the time has come.<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt-get <span class=\"nb\">install<\/span> <span class=\"nt\">-V<\/span> docker-ce\n<span class=\"c\">...\n<\/span><span class=\"go\">   docker-ce (17.03.1~ce-0~debian-stretch)\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>docker version\n<span class=\"go\">Client:\n Version:      17.03.1-ce\n API version:  1.27\n Go version:   go1.7.5\n Git commit:   c6d412e\n Built:        Mon Mar 27 17:07:28 2017\n OS\/Arch:      linux\/amd64\n\nServer:\n Version:      17.03.1-ce\n API version:  1.27 (minimum version 1.12)\n Go version:   go1.7.5\n Git commit:   c6d412e\n Built:        Mon Mar 27 17:07:28 2017\n OS\/Arch:      linux\/amd64\n Experimental: false\n\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>usermod <span class=\"nt\">-aG<\/span> docker <span class=\"s2\">\"<\/span><span class=\"si\">$(<\/span><span class=\"nb\">id<\/span> <span class=\"nt\">-un<\/span><span class=\"si\">)<\/span><span class=\"s2\">\"<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(<strong>Update 2017-09-29:<\/strong> If you're installing EE, the package changes to <code class=\"language-plaintext highlighter-rouge\">docker-ee<\/code>.)<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2016\/12\/07\/docker-setup","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2016\/12\/07\/docker-setup.html"}},"title":"My Docker Install Process","published":"2016-12-07T00:00:00-08:00","updated":"2016-12-07T00:00:00-08:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I've had several requests recently for information about how I personally set up a new machine for running Docker (especially since I don't use the infamous <code class=\"language-plaintext highlighter-rouge\">curl get.docker.com | sh<\/code>), so I figured I'd outline the steps I usually take.<\/p>\n\n<p>For the purposes of simplicity, I'm going to assume Debian (specifically <code class=\"language-plaintext highlighter-rouge\">stretch<\/code>, the upcoming Debian stable release), but these should generally be easily adjustable to <code class=\"language-plaintext highlighter-rouge\">jessie<\/code> or Ubuntu.<\/p>\n\n<p>These steps <em>should<\/em> be fairly similar to what's found in <a href=\"https:\/\/docs.docker.com\/engine\/installation\/linux\/debian\/\">upstream's \"Install Docker on Debian\" document<\/a>, but do differ slightly in a few minor ways.<\/p>\n\n<h1 id=\"grab-dockers-apt-repo-gpg-key\">grab Docker's APT repo GPG key<\/h1>\n\n<p>The way I do this is probably a bit unconventional, but the basic gist is something like this:<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">export <\/span><span class=\"nv\">GNUPGHOME<\/span><span class=\"o\">=<\/span><span class=\"s2\">\"<\/span><span class=\"si\">$(<\/span><span class=\"nb\">mktemp<\/span> <span class=\"nt\">-d<\/span><span class=\"si\">)<\/span><span class=\"s2\">\"<\/span>\ngpg <span class=\"nt\">--keyserver<\/span> keyserver.ubuntu.com <span class=\"nt\">--recv-keys<\/span> 58118E89F3A912897C070ADBF76221572C52609D\ngpg <span class=\"nt\">--export<\/span> <span class=\"nt\">--armor<\/span> 58118E89F3A912897C070ADBF76221572C52609D | <span class=\"nb\">sudo tee<\/span> \/etc\/apt\/trusted.gpg.d\/docker.gpg.asc\n<span class=\"c\"># gpg --export 58118E89F3A912897C070ADBF76221572C52609D | sudo tee \/etc\/apt\/trusted.gpg.d\/docker.gpg &gt; \/dev\/null<\/span>\n<span class=\"nb\">rm<\/span> <span class=\"nt\">-rf<\/span> <span class=\"s2\">\"<\/span><span class=\"nv\">$GNUPGHOME<\/span><span class=\"s2\">\"<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(On <code class=\"language-plaintext highlighter-rouge\">jessie<\/code> or another release whose APT doesn't support <code class=\"language-plaintext highlighter-rouge\">.asc<\/code> files in <code class=\"language-plaintext highlighter-rouge\">\/etc\/apt\/trusted.gpg.d<\/code>, I'd drop <code class=\"language-plaintext highlighter-rouge\">--armor<\/code> and the <code class=\"language-plaintext highlighter-rouge\">.asc<\/code> and go with simply <code class=\"language-plaintext highlighter-rouge\">\/...\/docker.gpg<\/code>.)<\/p>\n\n<p>This creates me a new GnuPG directory to work with (so my personal <code class=\"language-plaintext highlighter-rouge\">~\/.gnupg<\/code> doesn't get cluttered with this new key), downloads Docker's signing key from the keyserver gossip network (verifying the fetched key via the full fingerprint I've provided), exports the key into APT's keystore, then cleans up the leftovers.<\/p>\n\n<p>For completeness, other popular ways to fetch this include:<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">sudo <\/span>apt-key adv <span class=\"nt\">--keyserver<\/span> keyserver.ubuntu.com <span class=\"nt\">--recv-keys<\/span> 58118E89F3A912897C070ADBF76221572C52609D\n<\/code><\/pre><\/div><\/div>\n\n<p>(worth noting that <code class=\"language-plaintext highlighter-rouge\">man apt-key<\/code> discourages the use of <code class=\"language-plaintext highlighter-rouge\">apt-key adv<\/code>)<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>wget <span class=\"nt\">-qO-<\/span> <span class=\"s1\">'https:\/\/apt.dockerproject.org\/gpg'<\/span> | <span class=\"nb\">sudo <\/span>apt-key add -\n<\/code><\/pre><\/div><\/div>\n\n<p>(no verification of the downloaded key)<\/p>\n\n<p>Here's the relevant output of <code class=\"language-plaintext highlighter-rouge\">apt-key list<\/code> on a machine where I've got this key added in the way I outlined above:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>apt-key list\n<span class=\"c\">...\n<\/span><span class=\"go\">\n\/etc\/apt\/trusted.gpg.d\/docker.gpg.asc\n-------------------------------------\npub   rsa4096 2015-07-14 [SCEA]\n      5811 8E89 F3A9 1289 7C07  0ADB F762 2157 2C52 609D\n<\/span><span class=\"gp\">uid           [ unknown] Docker Release Tool (releasedocker) &lt;docker@docker.com&gt;<\/span><span class=\"w\">\n<\/span><span class=\"go\">\n<\/span><span class=\"c\">...\n<\/span><\/code><\/pre><\/div><\/div>\n\n<h1 id=\"add-dockers-apt-source\">add Docker's APT source<\/h1>\n\n<p>If you prefer to fetch sources via HTTPS, install <code class=\"language-plaintext highlighter-rouge\">apt-transport-https<\/code>, but I'm personally fine with simply doing GPG verification of fetched packages, so I forgo that in favor of less packages installed.  YMMV.<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo<\/span> <span class=\"s1\">'deb http:\/\/apt.dockerproject.org\/repo debian-stretch main'<\/span> | <span class=\"nb\">sudo tee<\/span> \/etc\/apt\/sources.list.d\/docker.list\n<\/code><\/pre><\/div><\/div>\n\n<p>Hopefully it's obvious, but <code class=\"language-plaintext highlighter-rouge\">debian-stretch<\/code> in that line should be replaced by <code class=\"language-plaintext highlighter-rouge\">debian-jessie<\/code>, <code class=\"language-plaintext highlighter-rouge\">ubuntu-xenial<\/code>, etc. as desired.  It's also worth pointing out that this will <em>not<\/em> include Docker's release candidates.  If you want those as well, add <code class=\"language-plaintext highlighter-rouge\">testing<\/code> after <code class=\"language-plaintext highlighter-rouge\">main<\/code>, ie <code class=\"language-plaintext highlighter-rouge\">... debian-stretch main testing' | ...<\/code>.<\/p>\n\n<p>At this point, you should be safe to run <code class=\"language-plaintext highlighter-rouge\">apt-get update<\/code> to verify the changes:<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt-get update\n<span class=\"c\">...\n<\/span><span class=\"go\">Hit:1 http:\/\/apt.dockerproject.org\/repo debian-stretch InRelease\n<\/span><span class=\"c\">...\n<\/span><span class=\"go\">Reading package lists... Done\n<\/span><\/code><\/pre><\/div><\/div>\n\n<p>(There shouldn't be any warnings or errors about missing keys, etc.)<\/p>\n\n<h1 id=\"configure-docker\">configure Docker<\/h1>\n\n<p>This step could be done after Docker's installed (and indeed, that's usually when I do it because I forget that I should until I've got Docker installed and realize that my configuration is suboptimal), but doing it before ensures that Docker doesn't have to be restarted later.<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">sudo mkdir<\/span> <span class=\"nt\">-p<\/span> \/etc\/docker\n<span class=\"nb\">sudo <\/span>sensible-editor \/etc\/docker\/daemon.json\n<\/code><\/pre><\/div><\/div>\n\n<p>(<code class=\"language-plaintext highlighter-rouge\">sensible-editor<\/code> can be replaced by whatever editor you prefer, but that command should choose or prompt for a reasonable default)<\/p>\n\n<p>I then fill <code class=\"language-plaintext highlighter-rouge\">daemon.json<\/code> with at least a default <code class=\"language-plaintext highlighter-rouge\">storage-driver<\/code>.  Whether I use <code class=\"language-plaintext highlighter-rouge\">aufs<\/code> or <code class=\"language-plaintext highlighter-rouge\">overlay2<\/code> depends on my kernel version and available modules \u2013 if I'm on Ubuntu, AUFS is still a no-brainer (since it's included in the default kernel if the <code class=\"language-plaintext highlighter-rouge\">linux-image-extra-XXX<\/code>\/<code class=\"language-plaintext highlighter-rouge\">linux-image-extra-virtual<\/code> package is installed), but on Debian AUFS is only available in either 3.x kernels (<code class=\"language-plaintext highlighter-rouge\">jessie<\/code>'s default non-backports kernel) or recently in the <code class=\"language-plaintext highlighter-rouge\">aufs-dkms<\/code> package (as of this writing, still only available on <code class=\"language-plaintext highlighter-rouge\">stretch<\/code> and <code class=\"language-plaintext highlighter-rouge\">sid<\/code> \u2013 no <code class=\"language-plaintext highlighter-rouge\">jessie-backports<\/code> option).<\/p>\n\n<p>If my kernel is 4.x+, I'm likely going to choose <code class=\"language-plaintext highlighter-rouge\">overlay2<\/code> (or if that errors out, the older <code class=\"language-plaintext highlighter-rouge\">overlay<\/code> driver).<\/p>\n\n<p>Choosing an appropriate storage driver is a fairly complex topic, and I'd recommend that for serious production deployments, more research on pros and cons is performed than I'm including here (especially since AUFS and OverlayFS are <em>not<\/em> the only options \u2013 they're just the two I personally use most often).<\/p>\n\n<div class=\"language-json highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">{<\/span><span class=\"w\">\n\t<\/span><span class=\"nl\">\"storage-driver\"<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">\"overlay2\"<\/span><span class=\"w\">\n<\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/code><\/pre><\/div><\/div>\n\n<h1 id=\"configure-boot-parameters\">configure boot parameters<\/h1>\n\n<p>I usually set a few boot parameters as well (in <code class=\"language-plaintext highlighter-rouge\">\/etc\/default\/grub<\/code>'s <code class=\"language-plaintext highlighter-rouge\">GRUB_CMDLINE_LINUX_DEFAULT<\/code> option \u2013 run <code class=\"language-plaintext highlighter-rouge\">sudo update-grub<\/code> after adding these, space-separated).<\/p>\n\n<ul>\n  <li><code class=\"language-plaintext highlighter-rouge\">cgroup_enable=memory<\/code> \u2013 enable \"memory accounting\" for containers (allows <code class=\"language-plaintext highlighter-rouge\">docker run --memory<\/code> for setting hard memory limits on containers)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">swapaccount=1<\/code> \u2013 enable \"swap accounting\" for containers (allows <code class=\"language-plaintext highlighter-rouge\">docker run --memory-swap<\/code> for setting hard swap memory limits on containers)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">systemd.legacy_systemd_cgroup_controller=yes<\/code> \u2013 newer versions of systemd <em>may<\/em> disable the legacy cgroup interfaces Docker currently uses; this instructs systemd to keep those enabled (for more details, see <a href=\"https:\/\/github.com\/systemd\/systemd\/pull\/4628\">systemd\/systemd#4628<\/a>, <a href=\"https:\/\/github.com\/opencontainers\/runc\/issues\/1175\">opencontainers\/runc#1175<\/a>, <a href=\"https:\/\/github.com\/docker\/docker\/issues\/28109\">docker\/docker#28109<\/a>)<\/li>\n  <li><code class=\"language-plaintext highlighter-rouge\">vsyscall=emulate<\/code> \u2013 allow older binaries to run (<code class=\"language-plaintext highlighter-rouge\">debian:wheezy<\/code>, etc.; see <a href=\"https:\/\/github.com\/docker\/docker\/issues\/28705\">docker\/docker#28705<\/a>)<\/li>\n<\/ul>\n\n<p>All together:<\/p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>...\n<span class=\"nv\">GRUB_CMDLINE_LINUX_DEFAULT<\/span><span class=\"o\">=<\/span><span class=\"s2\">\"cgroup_enable=memory swapaccount=1 systemd.legacy_systemd_cgroup_controller=yes vsyscall=emulate\"<\/span>\n...\n<\/code><\/pre><\/div><\/div>\n\n<h1 id=\"install-docker\">install Docker!<\/h1>\n\n<p>Finally, the time has come.<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>apt-get <span class=\"nb\">install<\/span> <span class=\"nt\">-V<\/span> docker-engine\n<span class=\"c\">...\n<\/span><span class=\"go\">\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>docker version\n<span class=\"go\">Client:\n Version:      1.12.3\n API version:  1.24\n Go version:   go1.6.3\n Git commit:   6b644ec\n Built:        Wed Oct 26 21:45:16 2016\n OS\/Arch:      linux\/amd64\n\nServer:\n Version:      1.12.3\n API version:  1.24\n Go version:   go1.6.3\n Git commit:   6b644ec\n Built:        Wed Oct 26 21:45:16 2016\n OS\/Arch:      linux\/amd64\n\n<\/span><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">sudo <\/span>usermod <span class=\"nt\">-aG<\/span> docker <span class=\"s2\">\"<\/span><span class=\"si\">$(<\/span><span class=\"nb\">id<\/span> <span class=\"nt\">-un<\/span><span class=\"si\">)<\/span><span class=\"s2\">\"<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(Reboot or logout\/login to update your session to include <code class=\"language-plaintext highlighter-rouge\">docker<\/code> group membership and thus no longer require <code class=\"language-plaintext highlighter-rouge\">sudo<\/code> for using <code class=\"language-plaintext highlighter-rouge\">docker<\/code> commands.)<\/p>\n\n<p>Hope this is useful to someone!  If nothing else, it'll serve as a concise single-page reference for future-tianon. \ud83d\ude07<\/p>\n\n<ul>\n  <li><strong>Updated 2017-04-11<\/strong>: adjusted some commands to be easier to munge for other platforms (especially so I stop screwing up the <code class=\"language-plaintext highlighter-rouge\">gpg --export<\/code> line and getting garbage to my terminal)<\/li>\n<\/ul>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2016\/03\/03\/vultr-docker-ipv6","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2016\/03\/03\/vultr-docker-ipv6.html"}},"title":"Docker on VULTR + IPv6","published":"2016-03-03T00:00:00-08:00","updated":"2016-03-03T00:00:00-08:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I've been using <a href=\"https:\/\/www.vultr.com\">VULTR<\/a> for a little while now and have been generally very pleased (especially with the very recent facelift the management portal received).  I don't want to waste too much time talking about it, but the \"killer feature\" for me (over some of their competitors like <a href=\"https:\/\/www.digitalocean.com\">DigitalOcean<\/a>) is that I can provide a raw ISO and provision my VM directly using it as I would any local VM (which also means that once my VM is up and working, I get to use the OS's standard kernel, which is especially important for using Debian Unstable well).<\/p>\n\n<p>Anyhow, already too much about that \u2013 let's get to the cool stuff.<\/p>\n\n<p>Getting right down to the beef, let's assume I've got a VULTR instance already created, my OS is already installed and working, I've enabled IPv6 within VULTR, ensured that my VM is able to <code class=\"language-plaintext highlighter-rouge\">ping6 google.com<\/code> (to verify at least basic routability), <em>and<\/em> have Docker version 1.10.2 installed.<\/p>\n\n<p>For the sake of demonstration, we'll assume that VULTR has assigned my IPv6 as follows: (available under the VM details via <code class=\"language-plaintext highlighter-rouge\">Settings &gt; IPv6<\/code>)<\/p>\n\n<ul>\n  <li>Default IP: <code class=\"language-plaintext highlighter-rouge\">2001:db8::5400:00ff:fe20:2295<\/code><\/li>\n  <li>Network: <code class=\"language-plaintext highlighter-rouge\">2001:db8::<\/code><\/li>\n  <li>CIDR: 64<\/li>\n<\/ul>\n\n<p>(The astute reader may recognize <a href=\"https:\/\/tools.ietf.org\/html\/rfc3849\">RFC3849<\/a> here. \ud83d\ude0f)<\/p>\n\n<p>The relevant documentation which helped me get to the working state outlined below is in <a href=\"https:\/\/docs.docker.com\/engine\/userguide\/networking\/default_network\/ipv6\/\">the \"IPv6 with Docker\" section<\/a>.<\/p>\n\n<p>The first step I took was creating a systemd drop-in file so that I could modify the daemon startup parameters (to include <code class=\"language-plaintext highlighter-rouge\">--ipv6<\/code> and <code class=\"language-plaintext highlighter-rouge\">--fixed-cidr-v6<\/code>):<\/p>\n\n<div class=\"language-ini highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># \/etc\/systemd\/system\/docker.service.d\/ipv6.conf\n<\/span><span class=\"nn\">[Service]<\/span>\n<span class=\"py\">ExecStart<\/span><span class=\"p\">=<\/span>\n<span class=\"py\">ExecStart<\/span><span class=\"p\">=<\/span><span class=\"s\">\/usr\/bin\/docker daemon -H fd:\/\/ --ipv6 --fixed-cidr-v6 2001:db8::\/80<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>I chose to use just <code class=\"language-plaintext highlighter-rouge\">\/80<\/code> for Docker \u2013 any other reasonable prefix (assuming it is routed to your host \/ host network) should also work; the documentation I linked above has an example using a <code class=\"language-plaintext highlighter-rouge\">\/125<\/code>, for example.<\/p>\n\n<p>With this half in place, I can <code class=\"language-plaintext highlighter-rouge\">systemctl daemon-reload<\/code> and <code class=\"language-plaintext highlighter-rouge\">systemctl restart docker.service<\/code>, and when I start a container it will be automatically assigned an IPv6 address from within that prefix.  Excellent.<\/p>\n\n<p>An important caveat to note is that this <em>will<\/em> break discovery on our host due to Docker enabling forwarding for us, so (assuming your \"internet-facing\" interface is named <code class=\"language-plaintext highlighter-rouge\">ens3<\/code> for the sake of illustration; it might just as easily be <code class=\"language-plaintext highlighter-rouge\">eth0<\/code>, <code class=\"language-plaintext highlighter-rouge\">eth1<\/code>, <code class=\"language-plaintext highlighter-rouge\">enps3<\/code>, <code class=\"language-plaintext highlighter-rouge\">lan0<\/code>, <code class=\"language-plaintext highlighter-rouge\">wlan0<\/code>, etc) I had to <code class=\"language-plaintext highlighter-rouge\">sysctl net.ipv6.conf.ens3.accept_ra=2<\/code>, and I added it to <code class=\"language-plaintext highlighter-rouge\">\/etc\/sysctl.d\/docker-ipv6.conf<\/code> for good measure (so that I don't lose it after I reboot).<\/p>\n\n<p>The second half of our IPv6 to containers problem is routing.  The nitty-gritty details of this are discussed in <a href=\"https:\/\/docs.docker.com\/engine\/userguide\/networking\/default_network\/ipv6\/#using-ndp-proxying\">the \"Using NDP proxying\" section<\/a> of the documentation, but the gist is that my containers have IPv6 addresses, but the outside world doesn't have a route that leads to them, and that we need to tell the kernel to respond to solicitations for our container's IPv6 addresses appropriately.<\/p>\n\n<p>The kernel has a mechanism for doing so (via <code class=\"language-plaintext highlighter-rouge\">ip -6 neigh ...<\/code>), but it is limited to individual addresses and is thus not especially great for having a solution that works \"magically\" without further manual labor per-container.<\/p>\n\n<p>This is where <a href=\"https:\/\/github.com\/DanielAdolfsson\/ndppd\">ndppd<\/a> (also <a href=\"https:\/\/packages.debian.org\/sid\/ndppd\">packaged for Debian as <code class=\"language-plaintext highlighter-rouge\">ndppd<\/code><\/a>) came in.<\/p>\n\n<div class=\"language-nginx highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># \/etc\/ndppd.conf<\/span>\n<span class=\"k\">proxy<\/span> <span class=\"s\">ens3<\/span> <span class=\"p\">{<\/span>\n\t<span class=\"kn\">rule<\/span> <span class=\"mi\">2001<\/span><span class=\"p\">:<\/span><span class=\"s\">db8::\/80<\/span> <span class=\"p\">{<\/span>\n\t<span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>After getting this configuration in place and restarting <code class=\"language-plaintext highlighter-rouge\">ndppd<\/code> (<code class=\"language-plaintext highlighter-rouge\">systemctl restart ndppd<\/code>), magic happened.  My containers could <code class=\"language-plaintext highlighter-rouge\">ping6 google.com<\/code>, and my other IPv6 hosts could <code class=\"language-plaintext highlighter-rouge\">ping6<\/code> the IPv6 addresses of my individual containers!<\/p>\n\n<p>You've probably noted that this configuration isn't exactly secure, since it means that each of my individual containers has a <em>publicly<\/em> routable IPv6 address, but for this specific use case, I'm OK with that! \ud83c\udf66<\/p>\n\n<p><strong>Update<\/strong> (2015-03-09): thanks to \u0410\u043b\u0435\u043a\u0441\u0435\u0439 \u0428\u0438\u043b\u0438\u043d for correcting my systemd drop-in file usage! \u2665<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2015\/05\/28\/dep8-tldr","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2015\/05\/28\/dep8-tldr.html"}},"title":"DEP8 - TL;DR","published":"2015-05-28T00:00:00-07:00","updated":"2015-05-28T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>DEP stands for \"Debian Enhancement Proposals\".  <a href=\"http:\/\/dep.debian.net\/deps\/dep8\/\">DEP8<\/a> is about package testing, specifically post-install (as opposed to <code class=\"language-plaintext highlighter-rouge\">dh_auto_test<\/code> which runs during package build, usually for unit tests).  It's great for integration tests, etc. that have more interesting requirements for running than unit tests normally do.<\/p>\n\n<p>The problem is that <a href=\"https:\/\/salsa.debian.org\/ci-team\/autopkgtest\/-\/blob\/master\/doc\/README.package-tests.rst\">the spec<\/a> is a little bit long in the tooth for casual reading \/ understanding-at-a-glance.<\/p>\n\n<p>What follows is my own personal TL;DR version.<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">cd <\/span>your-package\/\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">mkdir<\/span> <span class=\"nt\">-p<\/span> debian\/tests\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span>vim debian\/tests\/control\n<\/code><\/pre><\/div><\/div>\n\n<p>(editor of your choice)<\/p>\n\n<div class=\"language-email highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\">Tests<\/span><span class=\"o\">:<\/span><span class=\"na\"> my-test<\/span>\n<span class=\"nt\">Depends<\/span><span class=\"o\">:<\/span><span class=\"na\"> hello, @<\/span>\n<span class=\"nt\">Restrictions<\/span><span class=\"o\">:<\/span><span class=\"na\"> needs-root<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(see <a href=\"https:\/\/salsa.debian.org\/ci-team\/autopkgtest\/-\/blob\/master\/doc\/README.package-tests.rst\">the spec<\/a> for more info about what these mean and what valid values are)<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">touch <\/span>debian\/tests\/my-test\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span><span class=\"nb\">chmod<\/span> +x debian\/tests\/my-test\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span>vim debian\/tests\/my-test\n<\/code><\/pre><\/div><\/div>\n\n<p>(editor of your choice)<\/p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\">#!\/bin\/bash<\/span>\n<span class=\"nb\">set<\/span> <span class=\"nt\">-e<\/span>\n\nhello\n\n<span class=\"c\"># other bits testing your actual package (installed because of \"@\" in \"Depends:\")<\/span>\n<\/code><\/pre><\/div><\/div>\n\n<p>(again, see the spec linked above for details of how this script should behave \u2013 in general, non-zero exit code or stderr output mean failure)<\/p>\n\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>apt-get <span class=\"nb\">install <\/span>autopkgtest <span class=\"c\"># if not already installed<\/span>\n<span class=\"gp\">$<\/span><span class=\"w\"> <\/span>adt-run <span class=\"nt\">--unbuilt-tree<\/span> <span class=\"nb\">.<\/span> <span class=\"nt\">---<\/span> VIRT-SERVER\n<\/code><\/pre><\/div><\/div>\n\n<p>Where <code class=\"language-plaintext highlighter-rouge\">VIRT-SERVER<\/code> is one of: (as of this writing and installed by default with <code class=\"language-plaintext highlighter-rouge\">autopkgtest<\/code> \u2013 YMMV)<\/p>\n\n<ul>\n  <li><a href=\"http:\/\/manpages.debian.org\/cgi-bin\/man.cgi?manpath=Debian+unstable+sid&amp;query=adt-virt-chroot\"><code class=\"language-plaintext highlighter-rouge\">chroot<\/code><\/a><\/li>\n  <li><a href=\"http:\/\/manpages.debian.org\/cgi-bin\/man.cgi?manpath=Debian+unstable+sid&amp;query=adt-virt-lxc\"><code class=\"language-plaintext highlighter-rouge\">lxc<\/code><\/a><\/li>\n  <li><a href=\"http:\/\/manpages.debian.org\/cgi-bin\/man.cgi?manpath=Debian+unstable+sid&amp;query=adt-virt-null\"><code class=\"language-plaintext highlighter-rouge\">null<\/code><\/a><\/li>\n  <li><a href=\"http:\/\/manpages.debian.org\/cgi-bin\/man.cgi?manpath=Debian+unstable+sid&amp;query=adt-virt-qemu\"><code class=\"language-plaintext highlighter-rouge\">qemu<\/code><\/a><\/li>\n  <li><a href=\"http:\/\/manpages.debian.org\/cgi-bin\/man.cgi?manpath=Debian+unstable+sid&amp;query=adt-virt-schroot\"><code class=\"language-plaintext highlighter-rouge\">schroot<\/code><\/a><\/li>\n  <li><a href=\"http:\/\/manpages.debian.org\/cgi-bin\/man.cgi?manpath=Debian+unstable+sid&amp;query=adt-virt-ssh\"><code class=\"language-plaintext highlighter-rouge\">ssh<\/code><\/a><\/li>\n<\/ul>\n\n<p><strong>Update<\/strong> (2024-12-19): update links from anonscm to salsa<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2015\/05\/25\/dns.he.net-dd-wrt","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2015\/05\/25\/dns.he.net-dd-wrt.html"}},"title":"DD-WRT + dns.he.net (DDNS \/ inadyn)","published":"2015-05-25T00:00:00-07:00","updated":"2015-05-25T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>The DD-WRT wiki hilariously has <a href=\"https:\/\/www.dd-wrt.com\/wiki\/index.php\/Dynamic_DNS#he.net\">a page about this<\/a>, but it's not very helpful and <a href=\"https:\/\/www.dd-wrt.com\/phpBB2\/viewtopic.php?t=137570\">account creation is entirely disabled<\/a>, so here goes a blog post for my own future reference:<\/p>\n\n<p>In the <a href=\"https:\/\/dns.he.net\">dns.he.net control panel<\/a>, enable the hostname (<code class=\"language-plaintext highlighter-rouge\">HOSTNAME<\/code>) for \"dynamic dns\".  Click the DDNS icon and generate a \"key\" (<code class=\"language-plaintext highlighter-rouge\">KEY<\/code>), which will be used as the password for updating.<\/p>\n\n<p>In the DD-WRT control panel, under \"Setup &gt; DDNS\" (at least in <code class=\"language-plaintext highlighter-rouge\">Firmware: DD-WRT v24-sp2 (02\/19\/14) std<\/code>):<\/p>\n\n<ul>\n  <li>DDNS Service: <code class=\"language-plaintext highlighter-rouge\">Custom<\/code><\/li>\n  <li>DYNDNS Server: <code class=\"language-plaintext highlighter-rouge\">dyn.dns.he.net<\/code><\/li>\n  <li>Username: <code class=\"language-plaintext highlighter-rouge\">HOSTNAME<\/code><\/li>\n  <li>Password: <code class=\"language-plaintext highlighter-rouge\">KEY<\/code><\/li>\n  <li>Hostname: <code class=\"language-plaintext highlighter-rouge\">HOSTNAME<\/code><\/li>\n  <li>URL: <code class=\"language-plaintext highlighter-rouge\">\/nic\/update?hostname=<\/code><\/li>\n<\/ul>\n\n<p>Whala.<\/p>\n\n<p><strong>Update<\/strong> (2015-08-19):<\/p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ddclient.conf<\/code>:<\/p>\n\n<div class=\"language-ini highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"py\">protocol<\/span><span class=\"p\">=<\/span><span class=\"s\">dyndns2<\/span>\n<span class=\"py\">use<\/span><span class=\"p\">=<\/span><span class=\"s\">if<\/span>\n<span class=\"py\">if<\/span><span class=\"p\">=<\/span><span class=\"s\">eth0<\/span>\n<span class=\"py\">server<\/span><span class=\"p\">=<\/span><span class=\"s\">dyn.dns.he.net<\/span>\n<span class=\"py\">ssl<\/span><span class=\"p\">=<\/span><span class=\"s\">no<\/span>\n<span class=\"py\">login<\/span><span class=\"p\">=<\/span><span class=\"s\">HOSTNAME<\/span>\n<span class=\"py\">password<\/span><span class=\"p\">=<\/span><span class=\"s\">KEY<\/span>\n<span class=\"err\">HOSTNAME<\/span>\n<\/code><\/pre><\/div><\/div>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2014\/11\/22\/internets-own-boy","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2014\/11\/22\/internets-own-boy.html"}},"title":"The Internet's Own Boy","published":"2014-11-22T00:00:00-08:00","updated":"2014-11-22T00:00:00-08:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I am really late coming to this train.  This has been on my list since the\nday it was released, and today I finally found the time to sit alone and\ndigest.<\/p>\n\n<p>To say that it was \"emotionally moving\" would be a gross misrepresentation\nof the film.  There were clearly some aspects added for dramatic effect, but\nif you strip away (for example) the demonization of the opposition's\nactions, the proceedings remain absolutely <em>astounding<\/em> and the results\nentirely heartbreaking.<\/p>\n\n<p>That a society which claims to be as advanced as ours does (technologically,\nmorally, socially, militarily, etc) could demoralize an individual in this\nmanner seems terminally and criminally corrupt.<\/p>\n\n<p>When my filesystem gets corrupted, I try to desperately salvage any of the\nuseful information (usually while trying to salvage the entire filesystem),\nand once my efforts have proven to have provided all the fruits they\npossibly can, I proceed to format the drive and begin again.  Sometimes, a\nsystem needs to be rebooted.  A fresh install of the operating system\nusually extends the life of a system by a measurable amount of time.<\/p>\n\n<p>Perhaps reinstalling our base operating system is worth a try?<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2014\/08\/30\/debconf14","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2014\/08\/30\/debconf14.html"}},"title":"DebConf14","published":"2014-08-30T00:00:00-07:00","updated":"2014-08-30T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>I was given the opportunity to attend DebConf in Portland this year, and I\nmust say that it took me entirely by surprise (as a first-time attendee).<\/p>\n\n<p>Most conferences have several strong talk tracks and you end up spending a\nlot of time sitting in talks wondering when they'll be over.  At DebConf,\nthere's an entirely different dynamic, with a strong focus on what they like\nto call \"the hallway track\" (which this year has taken place a lot in the\nhacklabs, too).  Everyone here wants to either talk about building cool\nstuff, or sit down and actually build some cool stuff.  A large number of\nthe talks are just launchpads for informal Q+As or actual hack sessions.<\/p>\n\n<p>By coming, I've learned a lot about the Debian community and how it operates\nas a whole, and managed to meet a lot of very interesting and cool people\n(not to mention getting a bunch of them to sign my GPG key, which is also\nnice).<\/p>\n\n<p>Hopefully I'll be able to attend more DebConfs in the future, because I've\nhad a great time!<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2014\/05\/17\/docker-on-gentoo","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2014\/05\/17\/docker-on-gentoo.html"}},"title":"Love is a Battlefield (Docker on Gentoo)","published":"2014-05-17T00:00:00-07:00","updated":"2014-05-17T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>Docker on Gentoo can be a beautiful thing, but it can also be a challenge\nnavigating some of the trade-offs.<\/p>\n\n<p>The hardest decision to make, in my opinion, is which storage backend to\nuse.  Each one has ups and downs, and some of them have ups and downs that\nare more specific to Gentoo than others.<\/p>\n\n<h1 id=\"aufs\">\"aufs\"<\/h1>\n\n<p>Normally for an out-of-kernel module (even a filesystem), it would be a\nsimple matter to simply compile said module against the proper kernel\nsources and load it up; no harm, no foul.  What's particularly needling\nabout AUFS is that it requires patches to the kernel proper (which, I might\nadd, were submitted for inclusion in the kernel and rejected).<\/p>\n\n<p>The quandary that's most interesting about AUFS is that it's currently the\nrecommended Docker backend.  For Ubuntu and Debian users, this isn't a\nproblem since the AUFS patches are included in the main kernels and so the\n<code class=\"language-plaintext highlighter-rouge\">aufs<\/code> module is merely a single <code class=\"language-plaintext highlighter-rouge\">apt-get install<\/code> away.<\/p>\n\n<p>As you might imagine, these patches make a bit of a stir for someone who\nbuilds their own kernels (like, say, a Gentoo user), and there are two main\nways to get them.<\/p>\n\n<h2 id=\"sys-kernelaufs-sources\"><code class=\"language-plaintext highlighter-rouge\">sys-kernel\/aufs-sources<\/code><\/h2>\n\n<p>I'll start with the easy way.  If you <code class=\"language-plaintext highlighter-rouge\">emerge sys-kernel\/aufs-sources<\/code>,\nyou'll get <code class=\"language-plaintext highlighter-rouge\">sys-kernel\/gentoo-sources<\/code> with the AUFS patches pre-applied.\nChoosing this method, it's merely a matter of making sure <code class=\"language-plaintext highlighter-rouge\">CONFIG_AUFS_FS<\/code>\nis enabled in your <code class=\"language-plaintext highlighter-rouge\">.config<\/code> and you're good to go.  If you're already using\nstock <code class=\"language-plaintext highlighter-rouge\">sys-kernel\/gentoo-sources<\/code> and\/or are not averse to a slight change,\nthis will be the easiest, cleanest, and most importantly the least\nerror-prone option by far.<\/p>\n\n<h2 id=\"sys-fsaufs3\"><code class=\"language-plaintext highlighter-rouge\">sys-fs\/aufs3<\/code><\/h2>\n\n<p>The alternative is to use <code class=\"language-plaintext highlighter-rouge\">sys-fs\/aufs3<\/code>.  This package provides both the\nnecessary kernel patches and compiles the <code class=\"language-plaintext highlighter-rouge\">aufs<\/code> module, making it much more\nsuitable to <code class=\"language-plaintext highlighter-rouge\">sys-kernel\/vanilla-sources<\/code> and the like.  The <code class=\"language-plaintext highlighter-rouge\">aufs<\/code> module\nwill only load on a kernel compiled with the AUFS patches.  This ebuild\nincludes a <code class=\"language-plaintext highlighter-rouge\">kernel-patch<\/code> use flag that will automatically apply the patches\nto <code class=\"language-plaintext highlighter-rouge\">\/usr\/src\/linux<\/code> at merge time, which is the simplest way to ensure they\nare applied.<\/p>\n\n<p>Note that in my experience, this method is very human error-prone.  Using\n<code class=\"language-plaintext highlighter-rouge\">sys-kernel\/aufs-sources<\/code>, portage tracks the patches.  Using\n<code class=\"language-plaintext highlighter-rouge\">sys-fs\/aufs3<\/code>, it's all up to you.  I wish I could get back the lost time\nrebooting into a new kernel only to realize I hadn't recompiled it again\nafter re-emerging <code class=\"language-plaintext highlighter-rouge\">sys-fs\/aufs3<\/code>.<\/p>\n\n<h1 id=\"btrfs\">\"btrfs\"<\/h1>\n\n<p>BTRFS is fun.  It's speedy, it's hip, it's experimental.  The obvious\ndownside to using it as your Docker backend is that most of us don't have\nour root filesystem on it, which means we either have to reinstall our OS,\nmake a new partition\/drive\/loopback for Docker, or choose a different\nbackend.<\/p>\n\n<p>Note that if you <em>do<\/em> have BTRFS as your root filesystem, you want to make\nsure you <em>do not<\/em> use the AUFS backend.  AUFS on top of BTRFS has lots and\nlots of strange issues.<\/p>\n\n<h1 id=\"devicemapper\">\"devicemapper\"<\/h1>\n\n<p>The LVM\/devicemapper backend is especially cool because the kernel features\nit requires are enabled in a wide variety of pre-compiled kernels, making\nthis by far the easiest backend to get started with.  Also, it doesn't\nplay foul with any known filesystems since it effectively mounts containers\nin loopback, avoiding potential issues with filesystems interfering.<\/p>\n\n<p>However, unless you configure it to use a raw physical disk partition, the\nperformance will likely leave much to be desired.<\/p>\n\n<h1 id=\"vfs\">\"vfs\"<\/h1>\n\n<p>What we lovingly refer to as \"vfs\" is an interesting driver.  It's what's\nused for volumes, and is essentially a reference implementation for graph\ndrivers.  It has no \"copy on write\" at all, and is essentially just \"copy\nthe entire rootfs for each new layer\", so is perfectly suited for volumes,\nbut is not at all well-suited for being the general daemon backend.<\/p>\n"},{"id":"https:\/\/ram.tianon.xyz\/post\/2014\/04\/19\/getting-high-on-hy","link":{"@attributes":{"type":"text\/html","rel":"alternate","href":"https:\/\/ram.tianon.xyz\/post\/2014\/04\/19\/getting-high-on-hy.html"}},"title":"Getting High on Hy","published":"2014-04-19T00:00:00-07:00","updated":"2014-04-19T00:00:00-07:00","author":{"name":"Tianon Gravi","email":"admwiggin@gmail.com","uri":"https:\/\/ram.tianon.xyz"},"content":"<p>My good friend <a href=\"http:\/\/pault.ag\">Paul Tagliamonte<\/a> recently gave a talk at PyCon\n2014 about his language, Hy.  It's effectively Lisp implemented inside Python\n(so cleanly that Python itself doesn't care to differentiate the result from any\nother Python code).<\/p>\n\n<p>It's a solid talk that covers a lot of good ground about some of the cool stuff\nyou can do with Hy, and especially about the internals of exactly how Hy works,\nwhich is really fascinating stuff.  There's even a shout-out to our shared love,\n<a href=\"https:\/\/www.docker.io\">Docker<\/a>!<\/p>\n\n<p>The video can be found on\n<a href=\"https:\/\/www.youtube.com\/watch?v=AmMaN1AokTI\">YouTube<\/a>, but I've also embedded\nit below for your viewing pleasure.<\/p>\n\n<iframe width=\"100%\" height=\"400\" src=\"\/\/www.youtube.com\/embed\/AmMaN1AokTI?start=115&amp;html5=1&amp;rel=0\" frameborder=\"0\" allowfullscreen=\"\"><\/iframe>\n\n<p>If you'd like to give Hy a try, you can check it out with\n<a href=\"http:\/\/try-hy.appspot.com\">try-hy<\/a>, which is Hy running sandboxed on Google App\nEngine so you can play with it freely inside your browser.<\/p>\n\n<p><a href=\"https:\/\/github.com\/hylang\/hy\/blob\/master\/eg\/sh\/tagwords.hy\">sh\/tagwords.hy<\/a>:<\/p>\n\n<div class=\"language-hylang highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">;; python-sh from hy<\/span><span class=\"w\">\n\n<\/span><span class=\"p\">(<\/span><span class=\"k\">import<\/span><span class=\"w\"> <\/span><span class=\"p\">[<\/span><span class=\"n\">sh<\/span><span class=\"w\"> <\/span><span class=\"p\">[<\/span><span class=\"n\">cat<\/span><span class=\"w\"> <\/span><span class=\"n\">grep<\/span><span class=\"p\">]])<\/span><span class=\"w\">\n<\/span><span class=\"p\">(<\/span><span class=\"nf\">print<\/span><span class=\"w\"> <\/span><span class=\"s\">\"Words that end with `tag`:\"<\/span><span class=\"p\">)<\/span><span class=\"w\">\n<\/span><span class=\"p\">(<\/span><span class=\"nf\">print<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nb\">-&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nf\">cat<\/span><span class=\"w\"> <\/span><span class=\"s\">\"\/usr\/share\/dict\/words\"<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nf\">grep<\/span><span class=\"w\"> <\/span><span class=\"s\">\"-E\"<\/span><span class=\"w\"> <\/span><span class=\"s\">\"tag$\"<\/span><span class=\"p\">)))<\/span><span class=\"w\">\n<\/span><\/code><\/pre><\/div><\/div>\n<div class=\"language-console highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"gp\">$<\/span><span class=\"w\"> <\/span>hy sh\/tagwords.hy\n<span class=\"go\">Words that end with `tag`:\nBundestag\nMaytag\nReichstag\nSontag\nragtag\nstag\ntag\n<\/span><\/code><\/pre><\/div><\/div>\n"}]}