{
    "componentChunkName": "component---src-pages-blog-post-tsx",
    "path": "/blog/2020-04-07/the-feature-docker-forgot",
    "result": {"data":{"site":{"siteMetadata":{"siteUrl":"https://www.architect.io"}},"allMdx":{"edges":[{"node":{"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"The feature Docker forgot\",\n  \"slug\": \"the-feature-docker-forgot\",\n  \"date\": \"2020-04-07T00:00:00.000Z\",\n  \"author\": \"David Thor\",\n  \"image\": \"./complex-app.png\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"It's been seven years since Docker was first announced at PyCon, and containers have since inspired\\nsweeping changes in the roles and responsibilities of DevOps engineers around the world. The\\nperformance optimizations of a shared OS with the help of an open-source engine gave many developers\\ntheir first taste of production-ready virtualization; making virtualization approachable to the devs\\nwriting the applications proved to be just the kick we needed to move us into the cloud-native\\nlandscape we see today.\"), mdx(\"p\", null, \"In recent years, containerization has become the go-to packaging mechanic enabling\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.architect.io/blog/the-importance-of-portability\"\n  }, \"portability\"), \" from local development to\\nproduction, and alleviating teams of several reasons why something might \\\"work on my machine\\\" but\\nnot anywhere else. The natural trust that teams get from this kind of portability has similarly\\ninspired us to be bolder with how we design applications - yielding a boom of microservice\\narchitectures, service-oriented design, and cloud-native development.\"), mdx(\"p\", null, \"While this new-found portability and trust has been a valuable enabler, our resulting pursuit of\\ncloud-native development has created its own set of hurdles. While we've developed trust in our\\nvirtualization and enabled developers to create their own services, the applications we've built\\nhave become vastly more complex. As we look back at these increasingly complex applications, we must\\nask ourselves if they're still as portable as they were seven years ago.\"), mdx(\"h2\", {\n    \"id\": \"are-our-apps-portable\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"Are our apps portable?\", mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#are-our-apps-portable\",\n    \"aria-label\": \"are our apps portable permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"The short answer: sometimes yes, sometimes no. While Docker has allowed us to create portable images\\nthat can run on a variety of hardware and operating systems (personal machines included), not every\\napp consists of a single Docker image.\"), mdx(\"h3\", {\n    \"id\": \"containers-are-portable\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"Containers are portable...\", mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#containers-are-portable\",\n    \"aria-label\": \"containers are portable permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"876px\"\n    }\n  }, \"\\n      \", mdx(\"a\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/45842c4f378a12dfbfa213ccf8c39e7d/1b1d5/simple-app.png\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }, \"\\n    \", mdx(\"span\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"38.8%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB0klEQVQoz22Qy2sTURSHz7RM/wEXUQgEim0y99ykLVrbRXfdiIpF1OIyunNXaEubWEloM3dmolN10UUFaXHhA3TlSlF8gJvS0lcycyeJmD6wO1cKEWe4ZTK0mMQD3+Zc+M7vdwEAgMxugJJZASW7BrE7XwGZBfF734EaZaBGpYWEuQeY24aB5wIGXwvomngLDXMkvPJXQO/CT0C1eBeZvYLM/oDM/tjEe9TsNVQLyfqBfKUtmvrSKqRGRfIToVoYR80WqPE69B/wGFuQuS0taFBuj05/ahauQ8+jAynxYN+v+4rqjqC6U0ONuwrjblTlLtG4S3XHRc2pIbMEmdvMUN0BapT+L0w8/CEl5vd84bIvjKr8z/DCN+/6UtUbXap658ySRxj3qM5ryGxBcls6NUpBwtTn1sp1YZDwpS9EzRG9+ZLoN8tiYL4seoxgV39jllCyq0lUC/5/t3VNvmsUKtlViJu7Uvx+FUhu20Bm7SOzikQt8lgugKhF298hs3bI7Eb2zLKAs0+FFLn5GPoWfzUKTwwlIXzDhPBoHkLnx9tPXkp3hC6m5MhIWu6+OiPHrs3I4ctpOXRhSj41kunoW/wNkVtP4PTYG+i8/QL6n4lj2SF5MwGr1QZZogAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"simple-app\",\n    \"title\": \"simple-app\",\n    \"src\": \"/static/45842c4f378a12dfbfa213ccf8c39e7d/1b1d5/simple-app.png\",\n    \"srcSet\": [\"/static/45842c4f378a12dfbfa213ccf8c39e7d/63868/simple-app.png 250w\", \"/static/45842c4f378a12dfbfa213ccf8c39e7d/0b533/simple-app.png 500w\", \"/static/45842c4f378a12dfbfa213ccf8c39e7d/1b1d5/simple-app.png 876w\"],\n    \"sizes\": \"(max-width: 876px) 100vw, 876px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n  \"), \"\\n    \")), mdx(\"p\", null, \"Apps that consist of a single container are already portable. Developers can make use of the Docker\\nengine to run the application as easily on private environments as can be done in production. This\\nmakes the container portable vertically through the release process as well as horizontally across\\ncloud providers.\"), mdx(\"h3\", {\n    \"id\": \"apps-are-not\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"...apps are not\", mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#apps-are-not\",\n    \"aria-label\": \"apps are not permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"1000px\"\n    }\n  }, \"\\n      \", mdx(\"a\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/71732bbf1b75d5b74cf9c7056cbc013e/fbae3/complex-app.png\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }, \"\\n    \", mdx(\"span\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"64.8%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADqUlEQVQ4y22Ta2hbdRjG35Yo2GmbZTJJGJZ1o83JOSTNrZf1iuJACxviLNQOpLWC1a1tXG/SlrY5/9s5TdK1a7OWtivToXOioxYGgo6qTE1vs0tyzsllN1B0Xj6I+MUP/UuHyGA+8Hz9PS+8zwPwgPrp9H/upPMAeBsOkRgIwS0oGv4eRHkLPHQLuGKD4/giQO/dXJdivOlSDLlUTSGJaE0P8qCPTv/raF4PPWsmTM1rpEtWv/zdEdfIt0fLUOzF5+kXRaMsbCFqaG9DcGm/WzX4s9M3edXpDHdg7auHgPcvZNNPdbKFfVyBR8roRmt5JM3rz2S5L5TiHrTe/nu4Mi9A56zH6PIuO0q0CCjRZUeJHod8ow5EouWIVDeJ1DCV0U1TmKEdqKWDnisAxMEdjPl9Y9qHz02lL7gV7bJXXi2HQQ4DbMrSgK7slqixUjuZ/dEXSv8sYG0RJKqBHetQjHQooRl4iSxDgM1ZZ2i/xU+vd/uU+FUv3Vp2oc3zpwbRnlllIL+DLjw5wsaLmsgnB0Sir9dOZO56x1K/2FEiAhLRSgSst5cgrU2i2usm+c/Hh9lpy0fqyXwBa1eqJrK8ZvImF4j+U58yUzCrDuW10/PmPha1QvffOQ6iNZfIyVMCTvaIOPkCCFhT3Gqal4UzXKQGr8bXai4obxcMDA1BYTB10EXiDZVkddg5utZ2EGeKbeSHXYfpCqTGDj92DC8VS8zY3nmKP5zhdqR9DQJK1DqZzjyqPizi5DulwTWzzMJ7T9BFG3Rz8OGNo05m8IpI+n5gOV5tcpH4iXKy/hbMcnDgZK+TaliimupA8SPgYBnwRG5DxcQdENRb8Ar5GHpp1NxG38/fg++BU970OpnxWd1kZlmi+rVSed0r4mRzJYkd56OQKxL9oi+U+sapGDEH1hQQlWyuK3zb5IncMfnVhGlCGYVOurAbGAc7SZWJ1JiWqB4qQVpIDF632kkKqpUN4AwefZl8WixS41bdZPYvbyi9LaDkIjz96gwUts5DYcs8dJMo9LMotLN3bXwJQMIJ5h5L80M7pSUGt2Pd7UerAJTnBNhCAQSyOQ6UaNw/fKOjRI53STjxzMPFZlEI0DnLWWXQXI+/rKyk6x1VbO2NChxrbcaXbKNsvKCLzpsRC1kb8eUDEjP+qJnMcm8ozQWsff6/01tk3dBKPzDBCAcbuwf7xn4DM/oVrkZeyw3QOdNJes7Ep6w59WjlCREnz1SPp97zqPoluxxv+QczpsYoqT8z3AAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"complex-app\",\n    \"title\": \"complex-app\",\n    \"src\": \"/static/71732bbf1b75d5b74cf9c7056cbc013e/00d43/complex-app.png\",\n    \"srcSet\": [\"/static/71732bbf1b75d5b74cf9c7056cbc013e/63868/complex-app.png 250w\", \"/static/71732bbf1b75d5b74cf9c7056cbc013e/0b533/complex-app.png 500w\", \"/static/71732bbf1b75d5b74cf9c7056cbc013e/00d43/complex-app.png 1000w\", \"/static/71732bbf1b75d5b74cf9c7056cbc013e/aa440/complex-app.png 1500w\", \"/static/71732bbf1b75d5b74cf9c7056cbc013e/e8950/complex-app.png 2000w\", \"/static/71732bbf1b75d5b74cf9c7056cbc013e/fbae3/complex-app.png 2260w\"],\n    \"sizes\": \"(max-width: 1000px) 100vw, 1000px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n  \"), \"\\n    \")), mdx(\"p\", null, \"Cloud-native, multi-service applications are not portable out of the box - at least not as a unit.\\nWhile each individual service can maintain it's own pipeline and be released into de-coupled and\\nautonomous scaling groups, the final user-facing application often has implicit dependencies on many\\ndifferent systems in order to fulfill its duties.\"), mdx(\"p\", null, \"This complexity is unreasonable, and often impossible, for any single developer to keep track of.\\nThey know how to run and operate the service(s) that their team created, but often rely on mock\\nresponses or shared QA environments for testing and debugging integrations with peers. This means\\nthat apps aren't being run end-to-end until all the services reach a shared, static environment, and\\nthis environment is locked to the tools and technology that were setup when it was created. As a\\nresult, these apps aren't portable vertically or horizontally like the containers that power each\\nservice are.\"), mdx(\"h2\", {\n    \"id\": \"make-apps-portable-with-network-awareness\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"Make apps portable with network-awareness\", mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#make-apps-portable-with-network-awareness\",\n    \"aria-label\": \"make apps portable with network awareness permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"If our individual containers are each portable on their own but together are not, what differences\\nexist between the final application and the individual containers that are preventing us from\\nvertical and horizontal mobility? The answer becomes clear when you step back and look at the two\\nexamples above \\u2013 the networking.\"), mdx(\"p\", null, \"In distilling the application down to the containers and relationships between them, we can see that\\nwhat we're left with is a series of nodes and edges \\u2013 just like any other graph. While the nodes of\\nthis graph are independently portable, the edges are the key bit of information preventing us from\\nachieving the full application portability we hoped to get from containerization.\"), mdx(\"p\", null, \"So how do we acquire the information about the relationships between these services? These\\nrelationships didn't materialize to power our applications by accident, so what source of truth\\nmaintains this knowledge, and how do we collect it so we can make this app portable?\"), mdx(\"p\", null, \"While there may not be a single source of truth like one would hope for end-to-end insights into\\napplication networking, this information can fortunately be derived from the many existing\\ncontributors to the application \\u2013 the creators of each service. By appending the awareness of\\ntraffic coming from each service to the existing Docker images, we can easily extrapolate global\\nrelationships for the application to get full insights into all expected traffic for the app as a\\nwhole.\"), mdx(\"h2\", {\n    \"id\": \"doesnt-this-sound-familiar\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"Doesn't this sound familiar?\", mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#doesnt-this-sound-familiar\",\n    \"aria-label\": \"doesnt this sound familiar permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"While the use case for cloud-native apps is heavily weighted toward networking, the concept we've\\njust described is one we use every day: \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"dependencies\"), \". Every language, framework, and operating\\nsystem has introduced one or more ways for developers to build on top of the work of others by\\nasserting and resolving dependencies, and it seems only natural that we adopt the same patterns for\\nbuilding upon cloud services. By re-using interface elements of solutions like\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/\"\n  }, \"NPM\"), \", \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://pypi.org/\"\n  }, \"pip\"), \", \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://rubygems.org/\"\n  }, \"RubyGems\"), \", and\\nothers, we can support structured relationships between microservices and traverse these\\nrelationships to derive everything each service needs in order to run!\"), mdx(\"h2\", {\n    \"id\": \"dependencies-cant-be-resolved-until-they-are-running\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"Dependencies can't be resolved until they are running\", mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#dependencies-cant-be-resolved-until-they-are-running\",\n    \"aria-label\": \"dependencies cant be resolved until they are running permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Even though these tools provide an ideal developer experience, they unfortunately were not designed\\nto support microservices dependencies. While you could repurpose the tools to host artifacts, serve\\nthem, and index the relationships between them, the container artifacts that would be downloaded\\ncan't simply be imported into your application like language-specific libraries can. When it comes\\nto microservices, each service is designed to be an independent unit that can run and scale entirely\\non its own. What this means for a dependency resolver is that services aren't ready to fulfill\\ntraffic and handle incoming requests simply because the artifacts have been downloaded - they have\\nto be running too.\"), mdx(\"h3\", {\n    \"id\": \"what-do-we-need-in-order-to-resolve-cloud-dependencies\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"What do we need in order to resolve cloud dependencies?\", mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#what-do-we-need-in-order-to-resolve-cloud-dependencies\",\n    \"aria-label\": \"what do we need in order to resolve cloud dependencies permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Since services have to be running before calls can be made to them, the earliest point in an\\napplication lifecycle in which we can safely resolve microservice dependencies is deploy-time. It is\\nat deploy time that we know both the relationships between services and the tools/state of the\\ntarget environment allowing us to properly resolve and de-duplicate resources and services. In order\\nto properly broker this deployment to account for multiple services working together, we need to set\\nup our pipeline with a few key expectations about the target environment:\"), mdx(\"h4\", {\n    \"id\": \"1-a-way-to-run-services-eg-container-platform\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"1. A way to run services (e.g. container platform)\", mdx(\"a\", {\n    parentName: \"h4\",\n    \"href\": \"#1-a-way-to-run-services-eg-container-platform\",\n    \"aria-label\": \"1 a way to run services eg container platform permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"As we stated earlier, each individual container is already portable and can be run on a variety of\\ndifferent hardware and cloud hosting providers. The process we use to start one container can\\ntherefore be performed on many containers sequentially to achieve the same results for multi-service\\napplications. Simply target your favorite container platform like Kubernetes, OpenShift, or AWS ECS,\\nand said platform will be able to setup replicasets for every service in your stack.\"), mdx(\"h4\", {\n    \"id\": \"2-a-way-to-connect-services-together-eg-service-discovery--service-mesh\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"2. A way to connect services together (e.g. service discovery / service mesh)\", mdx(\"a\", {\n    parentName: \"h4\",\n    \"href\": \"#2-a-way-to-connect-services-together-eg-service-discovery--service-mesh\",\n    \"aria-label\": \"2 a way to connect services together eg service discovery  service mesh permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Now that we have a way of running each service, we need a standardized way of connecting them\\ntogether over a network. Fortunately the problem of cloud networking has had a lot of love in recent\\nyears, and we now have a number of tools we can use to instrument\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/\"\n  }, \"service discovery\"), \"\\nand networking rules across our application. Most recently we've seen a wave of innovative solutions\\nin the newly defined category of\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.nginx.com/blog/what-is-a-service-mesh/\"\n  }, \"service meshes\"), \" that are designed specifically\\nto broker traffic between services inside an app safely and consistently.\"), mdx(\"p\", null, \"Armed with end-to-end knowledge of a dependency graph, service discovery or service mesh solutions\\ncan be easily configured and re-configured with each deploy to ensure that services can\\nautomatically connect with one another.\"), mdx(\"h3\", {\n    \"id\": \"what-else-does-a-dependency-graph-let-us-automate\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"What else does a dependency graph let us automate?\", mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#what-else-does-a-dependency-graph-let-us-automate\",\n    \"aria-label\": \"what else does a dependency graph let us automate permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Running and connecting nodes are the minimum viable components of a running application, but\\noperating highly available, production-grade systems takes a bit more effort. Fortunately, knowledge\\nof the relationships between services affords us the ability to automate a lot more than just the\\nprovisioning of the basic application:\"), mdx(\"h4\", {\n    \"id\": \"1-generate-network-policies--certificates\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"1. Generate network policies & certificates\", mdx(\"a\", {\n    parentName: \"h4\",\n    \"href\": \"#1-generate-network-policies--certificates\",\n    \"aria-label\": \"1 generate network policies  certificates permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Due to the difficulty brokering collaboration in cloud environments and properly locking down\\nnetwork traffic, most systems operate with the \\\"walled garden\\\" approach to cloud security \\u2013 heavily\\nsecuring traffic entering and exiting a firewall, but allowing free-flowing connections between\\nservices once inside. While this enables teams to make calls to peer services without heavy\\ncoordination, it also means that any weakness in the firewall exposes access to the entire system.\"), mdx(\"p\", null, \"Knowledge of a dependency graph and relationships between services in an application afford teams\\nthe ability to automatically set up ingress and egress policies between services and leverage a\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.paloaltonetworks.com/cyberpedia/what-is-a-zero-trust-architecture\"\n  }, \"zero-trust security\"), \"\\nmodel instead of the walled garden approach. Whether it's setting up\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://kubernetes.io/docs/concepts/services-networking/network-policies/\"\n  }, \"Kubernetes NetworkPolicy\"), \"\\nresources enforced by \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://cilium.io/\"\n  }, \"Cilium\"), \", or generating unique credentials for each\\nservice relationship, the only way to set up correct rules is by knowing what traffic patterns are\\nallowed \\u2013 something teams get automatically when mapping dependencies.\"), mdx(\"h4\", {\n    \"id\": \"2-automate-observability--traceability\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"2. Automate observability & traceability\", mdx(\"a\", {\n    parentName: \"h4\",\n    \"href\": \"#2-automate-observability--traceability\",\n    \"aria-label\": \"2 automate observability  traceability permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"An additional hurdle that gets introduced by distributed systems compared to their monolithic peers\\nis the new-found difficulty observing application behavior and tracing requests. Different\\nprogramming languages and frameworks have their own stack traces that can tell you what happened\\nduring a requests lifecycle, but when requests make calls to many different services to perform\\ntheir tasks, the stack trace of each becomes less and less valuable.\"), mdx(\"p\", null, \"Fortunately the cloud-native ecosystem has delivered a number of amazing observability tools to help\\nteams instrument tracing (check out the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://opentelemetry.io/\"\n  }, \"OpenTelemetry project\"), \"!). With\\nknowledge in advance of the dependency graph, these tools can be automatically instrumented with\\nyour deployments and configured to consolidate logs corresponding with requests that leap between\\nmultiple services.\"), mdx(\"h4\", {\n    \"id\": \"3-protect-against-version-shifting\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"3. Protect against version shifting\", mdx(\"a\", {\n    parentName: \"h4\",\n    \"href\": \"#3-protect-against-version-shifting\",\n    \"aria-label\": \"3 protect against version shifting permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Active cloud-native applications have hundreds of thousands of engineers collaborating on dozens of\\ndifferent microservices, and as such each is often released via independent deployment pipelines to\\ngive each team autonomy. When some of these services are consumed by others however, it is left up\\nto the service being called to either a) maintain its API contract perfectly, or b) collaborate with\\nevery upstream team to ensure they are prepared to handle impending changes. Without proper\\ncoordination, it's easy for breaking releases to make their way into shared environments and cripple\\nupstream callers that aren't prepared for the changes.\"), mdx(\"p\", null, \"Mitigating issues with service versioning and shifting has been a core value-add of dependency\\nmanagers for years, and can similarly be applied to cloud environments. With each upstream caller\\nciting the version (or \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://semver.org/\"\n  }, \"semantic version\"), \") of their dependencies, deploy\\nprocesses are able to triage these relationships and ensure that each caller is able to consume\\nchanges before rolling out updates that could otherwise tear down entire products or regions.\"), mdx(\"h2\", {\n    \"id\": \"in-closing\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, \"In closing...\", mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#in-closing\",\n    \"aria-label\": \"in closing permalink\",\n    \"className\": \"anchor after\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  })))), mdx(\"p\", null, \"Obviously Docker didn't \\\"forget\\\" to add network dependencies to their product, but their success has\\nlead to such widespread adoption of service-oriented design that we now find ourselves with new\\nproblems. It just so happens that these problems can be solved by continuing the pursuit of\\nportability that Docker and other virtualization tools started.\"), mdx(\"p\", null, \"Despite the literature around decoupling the world of microservices has been following, it's clearly\\nnatural for developers and teams to build upon the work of others to make bigger and better software\\ntogether. In an effort to maintain the portability that containers provided, we need to lean into\\nservice dependencies and setup the same systems and methods we have in lower level systems to help\\nus along the way. In doing so, we will not only achieve the collaboration benefits provided by\\npackage managers, but we'll finally be able to ship services that others can run! It's time we had a\\ndependency resolver for microservices.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"It's been seven years since Docker was first announced at PyCon, and containers have since inspired\nsweeping changes in the roles and responsibilities of DevOps engineers around the world. The…","tableOfContents":{"items":[{"url":"#are-our-apps-portable","title":"Are our apps portable?","items":[{"url":"#containers-are-portable","title":"Containers are portable..."},{"url":"#apps-are-not","title":"...apps are not"}]},{"url":"#make-apps-portable-with-network-awareness","title":"Make apps portable with network-awareness"},{"url":"#doesnt-this-sound-familiar","title":"Doesn't this sound familiar?"},{"url":"#dependencies-cant-be-resolved-until-they-are-running","title":"Dependencies can't be resolved until they are running","items":[{"url":"#what-do-we-need-in-order-to-resolve-cloud-dependencies","title":"What do we need in order to resolve cloud dependencies?","items":[{"url":"#1-a-way-to-run-services-eg-container-platform","title":"1. A way to run services (e.g. container platform)"},{"url":"#2-a-way-to-connect-services-together-eg-service-discovery--service-mesh","title":"2. A way to connect services together (e.g. service discovery / service mesh)"}]},{"url":"#what-else-does-a-dependency-graph-let-us-automate","title":"What else does a dependency graph let us automate?","items":[{"url":"#1-generate-network-policies--certificates","title":"1. Generate network policies & certificates"},{"url":"#2-automate-observability--traceability","title":"2. Automate observability & traceability"},{"url":"#3-protect-against-version-shifting","title":"3. Protect against version shifting"}]}]},{"url":"#in-closing","title":"In closing..."}]},"frontmatter":{"title":"The feature Docker forgot","description":null,"author":"David Thor","date":"2020-04-07","image":{"childImageSharp":{"gatsbyImageData":{"layout":"constrained","images":{"fallback":{"src":"/static/71732bbf1b75d5b74cf9c7056cbc013e/2d08a/complex-app.png","srcSet":"/static/71732bbf1b75d5b74cf9c7056cbc013e/09bf1/complex-app.png 250w,\n/static/71732bbf1b75d5b74cf9c7056cbc013e/ea36f/complex-app.png 500w,\n/static/71732bbf1b75d5b74cf9c7056cbc013e/2d08a/complex-app.png 1000w,\n/static/71732bbf1b75d5b74cf9c7056cbc013e/af926/complex-app.png 2000w","sizes":"(min-width: 1000px) 1000px, 100vw"},"sources":[{"srcSet":"/static/71732bbf1b75d5b74cf9c7056cbc013e/95a75/complex-app.webp 250w,\n/static/71732bbf1b75d5b74cf9c7056cbc013e/d1dd3/complex-app.webp 500w,\n/static/71732bbf1b75d5b74cf9c7056cbc013e/e1189/complex-app.webp 1000w,\n/static/71732bbf1b75d5b74cf9c7056cbc013e/4ec38/complex-app.webp 2000w","type":"image/webp","sizes":"(min-width: 1000px) 1000px, 100vw"}]},"width":1000,"height":650}}}}},"next":null,"previous":null}]}},"pageContext":{"slug":"the-feature-docker-forgot"}},
    "staticQueryHashes": ["764694655"]}