<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>3-musketeers on Andrew Khoury</title><link>https://www.drewkhoury.com/tags/3-musketeers/</link><description>Recent content in 3-musketeers on Andrew Khoury</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>Copyright © 2021, Andrew Khoury; all rights reserved.</copyright><lastBuildDate>Fri, 01 Sep 2023 19:22:11 +0000</lastBuildDate><atom:link href="https://www.drewkhoury.com/tags/3-musketeers/index.xml" rel="self" type="application/rss+xml"/><item><title>Tips for how to use 3 Musketeers to supercharge your Developer Experince</title><link>https://www.drewkhoury.com/post/2023-09-13_3-musketeers-docker-make-compose-tips/</link><pubDate>Fri, 01 Sep 2023 19:22:11 +0000</pubDate><guid>https://www.drewkhoury.com/post/2023-09-13_3-musketeers-docker-make-compose-tips/</guid><description>
&lt;p>&lt;a href="https://3musketeersdev.netlify.app/">3 Musketeers&lt;/a> is a pattern popularized by Frederic L where you can Test, build, and deploy your apps from anywhere, the same way.&lt;/p>
&lt;p>&lt;em>Note: Frederic's website was preivously available via 3musketeers.io but is now &lt;a href="https://3musketeersdev.netlify.app/">https://3musketeersdev.netlify.app/&lt;/a>&lt;/em>&lt;/p>
&lt;p>The key benifits include being able to have a mix of Linux, Mac, and Windows workstations. You also get consistency with pipeline/CI tools that also run the same steps in the same way.&lt;/p>
&lt;p>&lt;strong>More Info:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://3musketeersdev.netlify.app/guide/patterns.html">https://3musketeersdev.netlify.app/guide/patterns.html&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/liatrio/3musketeers">https://github.com/liatrio/3musketeers&lt;/a> (brief intro, and install guide)&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/katacoda-scenarios-contino">https://github.com/drewkhoury/katacoda-scenarios-contino&lt;/a> - interactive lessons (before katacoda got taken down) - but you can look at the readme's for each lesson to see the steps&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/minimum-viable-delivery/blob/main/cicd-spec.md#portability-directive-runtime-must-be-independent-of-pipelinebuilddeploy">https://github.com/drewkhoury/minimum-viable-delivery/blob/main/cicd-spec.md#portability-directive-runtime-must-be-independent-of-pipelinebuilddeploy&lt;/a> - CI/CD spec I wrote a while back to explain the philosophy/reason behind it&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Examples that also include docs in their READMEs:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/drewkhoury/gsd-hello-world">https://github.com/drewkhoury/gsd-hello-world&lt;/a> - demo (Golang) and docs (and link to detailed blog)&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/3-musketeers-base">https://github.com/drewkhoury/3-musketeers-base&lt;/a> - setup, env, aws profile, python and docs&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/static-site">https://github.com/drewkhoury/static-site&lt;/a> - Demo of a static site using AWS CDK with Python&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>More Examples:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/liatrio/3musketeers/tree/main/examples/cloud-in-a-box">https://github.com/liatrio/3musketeers/tree/main/examples/cloud-in-a-box&lt;/a> (LocalStack w/EKS cluster)&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/pact-5-minute-getting-started-guide/blob/main/README-demo.md">https://github.com/drewkhoury/pact-5-minute-getting-started-guide/blob/main/README-demo.md&lt;/a> (PACT dockerized)&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/cucumber-declarative-gherkin">https://github.com/drewkhoury/cucumber-declarative-gherkin&lt;/a> (shows chrome headless testing of apps, w/screenshots through selenium web driver, in containers - also happens to be a full BDD example with more than just some trivial tests)&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/backstage-3m">https://github.com/drewkhoury/backstage-3m&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/communities">https://github.com/drewkhoury/communities&lt;/a> - static site with hugo&lt;/li>
&lt;/ul>
&lt;h1 id="when-to-use-3-musketeers">When to use 3 musketeers&lt;/h1>
&lt;p>It's important to understand the goals of &lt;em>Consistancy, Control and Confidence&lt;/em> and the part each tool plays in the pattern.&lt;/p>
&lt;h2 id="cant-i-just-skip-this-and-use-my-tools-directly">Can't I just skip this and use my tools directly?&lt;/h2>
&lt;p>The benfitis of using these tools together might not be obvious if you've only worked in a single project, or in a small team or if you haven't seen a project grow in complexity over time.&lt;/p>
&lt;p>You might also be new to tools such as &lt;code>make&lt;/code>, &lt;code>compose&lt;/code> and &lt;code>docker&lt;/code> and as such the idea of introducing them into your pipeline might seem unessasary. This might be especially true if your team is working with legacy applications and you don't have any container presense in your organization or team.&lt;/p>
&lt;p>Only your organization and your team can decide if the goals of &lt;em>Consistancy, Control and Confidence&lt;/em> are worth the investment in pipeline standarization.&lt;/p>
&lt;p>These patterns and tools truely shine when:&lt;/p>
&lt;ul>
&lt;li>Developers start to seek &amp;quot;fast feedback&amp;quot; by &amp;quot;shifting left&amp;quot; - ie wanting to run some tests early (before they commit code)&lt;/li>
&lt;li>Working in larger teams&lt;/li>
&lt;li>With different underlying development machines - or with developers who install different versions of software (how much time did a new developer spend getting a working build env?)&lt;/li>
&lt;li>Across multiple teams in large oragnizations&lt;/li>
&lt;li>With CI/CD agents managed by other teams where changes to agents are not easy and you don't have permission/flexibility (did someone upgrade a build agent and break how java works for you? how would you know?)&lt;/li>
&lt;li>When having to share code between vendors&lt;/li>
&lt;li>The more complex the application gets, or as more changes are made to the application and it's build steps (having to refactor or share build steps either within your team or between teams)&lt;/li>
&lt;li>Testing your pipeline code (how many times have you done 20 check-ins of code just to test one bit of functionaility &amp;quot;on the build server&amp;quot;)&lt;/li>
&lt;/ul>
&lt;h1 id="the-3-tools">The 3 tools&lt;/h1>
&lt;p>As the name implies 3 musketeers is made up of 3 tools: &lt;a href="https://3musketeersdev.netlify.app/about/tools.html">https://3musketeersdev.netlify.app/about/tools.html&lt;/a>&lt;/p>
&lt;h2 id="docker">Docker&lt;/h2>
&lt;p>Docker is the &lt;strong>most important musketeer of the three&lt;/strong>.&lt;/p>
&lt;p>Many tasks such as testing, building, running, and deploying can all be done inside a lightweight Docker container.&lt;/p>
&lt;p>The &lt;strong>portability of Docker&lt;/strong> ensures you can &lt;strong>execute the same tasks, the same way, on different environments&lt;/strong> like MacOS, Linux, Windows, and CI/CD tools.&lt;/p>
&lt;p>Example: Docker can be invoked directly from &lt;code>make&lt;/code>, or can be invoked from a &lt;code>docker-compose&lt;/code> command in &lt;code>make&lt;/code>.&lt;/p>
&lt;h2 id="make">Make&lt;/h2>
&lt;p>Make is a cross-platform build tool to test and build software and it is used as an &lt;strong>interface between the CI/CD server and the application code&lt;/strong>.&lt;/p>
&lt;p>A single Makefile per application defines and encapsulates all the steps for testing, building, and deploying that application.&lt;/p>
&lt;p>Of course other tools like rake or ant can be used to achieve the same goal, but having Makefile pre-installed in many OS distributions makes it a convenient choice.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="ln">1&lt;/span>&lt;span class="c1"># Makefile&lt;/span>
&lt;span class="ln">2&lt;/span>echo:
&lt;span class="ln">3&lt;/span> docker-compose run --rm alpine &lt;span class="nb">echo&lt;/span> &lt;span class="s1">&amp;#39;Hello, World!&amp;#39;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="compose">Compose&lt;/h2>
&lt;p>Docker Compose, or simply Compose, manages Docker containers in a very neat way.&lt;/p>
&lt;p>It allows multiple Docker commands to be written as a single one, which &lt;strong>allows our Makefile to be a lot cleaner and easier to maintain&lt;/strong>.&lt;/p>
&lt;p>Testing also often involves &lt;strong>container dependencies&lt;/strong>, such as a database, which is an area where Compose really shines. No need to create the database container and link it to your application code container manually — Compose takes care of this for you.&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="ln">1&lt;/span>&lt;span class="c1"># docker-compose.yml&lt;/span>
&lt;span class="ln">2&lt;/span>version: &lt;span class="s1">&amp;#39;3&amp;#39;&lt;/span>
&lt;span class="ln">3&lt;/span>services:
&lt;span class="ln">4&lt;/span> alpine:
&lt;span class="ln">5&lt;/span> image: alpine
&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="cant-i-choose-my-own-mukseteers">Can't I choose my own mukseteers?&lt;/h1>
&lt;p>Who says &lt;code>make&lt;/code>, &lt;code>compose&lt;/code>, &lt;code>docker&lt;/code> are the perfect combo?&lt;/p>
&lt;p>The key to &lt;em>Consistancy, Control and Confidence&lt;/em> is not &lt;strong>only&lt;/strong> in the specific combination of &lt;code>make&lt;/code>, &lt;code>compose&lt;/code>, &lt;code>docker&lt;/code> but actually the agreement of your team and your organization to use a standard that's easy for everyone to follow.&lt;/p>
&lt;p>Consider:&lt;/p>
&lt;ul>
&lt;li>Do most of your development teams have easy access to &lt;code>make&lt;/code>, &lt;code>compose&lt;/code>, &lt;code>docker&lt;/code> - or can you agree/reach a state where this is possible?&lt;/li>
&lt;li>Do you want to replace &lt;code>make&lt;/code> with something else? (zeus, python, shell) Sure thing, just agree as a team what would be more easily to install and maintain&lt;/li>
&lt;li>Do you want to start with &lt;code>Make&lt;/code> and &lt;code>Docker&lt;/code> and introduce &lt;code>Compose&lt;/code> only when the complexity demands it?&lt;/li>
&lt;/ul>
&lt;h1 id="q--a">Q &amp;amp; A&lt;/h1>
&lt;h2 id="are-there-pre-reqs-what-if-my-workload-or-app-isnt-docker-based">Are there pre-reqs? What if my workload or app isn't docker based?&lt;/h2>
&lt;p>This pattern doesn't operate at your workload or application/deployment architecture level, it's all about the CI steps (like build, test, publish, deploy etc).&lt;/p>
&lt;p>The workload could be anything, that scope is outside of the pattern. It could be the case that your workload is containerized but that's unrelated to the higher-level pattern and how the local/CI steps are run.&lt;/p>
&lt;p>You need to agree on having Make, Compose and Docker available on developer workstations, and for the CI tool.&lt;/p>
&lt;h2 id="make-is-difficult-to-use-and-hard-to-learn">Make is difficult to use, and hard to learn&lt;/h2>
&lt;p>&lt;code>make&lt;/code> is a powerful tool that can do a lot of things. However, the intent is to use &lt;code>make&lt;/code> as a simple interface between your build tool and your build steps.&lt;/p>
&lt;p>As you should only be using the most basic features of &lt;code>make&lt;/code> it shouldn't be difficult to implement. If you're having a lot of difficulties, consider if you're over complicating your task.&lt;/p>
&lt;p>Consider some real-life simple make files (easy to read, maintain and run):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/drewkhoury/devyo/blob/master/makefile">https://github.com/drewkhoury/devyo/blob/master/makefile&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/drewkhoury/buildkite/blob/master/Makefile">https://github.com/drewkhoury/buildkite/blob/master/Makefile&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>You could consider using &lt;a href="https://3musketeersdev.netlify.app/guide/make.html#multiple-makefiles">multiple makefiles&lt;/a> or consider &lt;a href="https://3musketeersdev.netlify.app/guide/make.html#complex-targets">another language&lt;/a> if you have complex logic that demands it.&lt;/p>
&lt;h2 id="i-really-dont-want-to-use-docker-compose---im-not-even-deploying-anything--im-using-k8s">I really don't want to use docker compose - I'm not even deploying anything / I'm using k8s&lt;/h2>
&lt;p>Don't confuse using &lt;code>docker-compose&lt;/code> during your build/test steps with deploying your application in production. With 3musketers we're looking at the patterns we use to inteface with our build tool (like building and testing your application in your pipeline).&lt;/p>
&lt;p>People often use compose files (with 3musketeers) to avoid having long and messy make files.&lt;/p>
&lt;h2 id="to-compose-or-not-to-compose">To Compose or Not to Compose?&lt;/h2>
&lt;p>If your use of docker is simple for a particular task, feel free to skip the compose file.&lt;/p>
&lt;p>&lt;a href="https://3musketeersdev.netlify.app/guide/patterns.html#docker">https://3musketeersdev.netlify.app/guide/patterns.html#docker&lt;/a>&lt;/p>
&lt;p>Make calls directly Docker instead of Compose. Everything that is done with Compose can be done with Docker. Using Compose helps to keep the Makefile clean.&lt;/p>
&lt;p>When you have a multi-line docker invocation, or 5 lines of docker commands in a single task, consider if compose could simplify things for you.&lt;/p>
&lt;p>&lt;a href="https://3musketeersdev.netlify.app/guide/patterns.html#task-management-tool">https://3musketeersdev.netlify.app/guide/patterns.html#task-management-tool&lt;/a> shows an example of how to implement an &amp;quot;npm&amp;quot; command &amp;quot;cleanly&amp;quot;.&lt;/p>
&lt;p>Notice that the compose file does all the volume mounting, workdir (and would do all the env injection etc).&lt;/p>
&lt;p>Consider an even more complex senerio that invovled multiple services, logs, naming of containers, starting and stopping containers, cleanup when done.&lt;/p>
&lt;h2 id="how-will-i-manage-container-versionsinstances">How will I manage container versions/instances&lt;/h2>
&lt;p>Without container images in your CI pipeline you may have managed binaries and software through an artifact store like Artifactory. You would use a similar approach for containers too.&lt;/p>
&lt;p>Your organization will need to decide how teams can pull images (directly from the Internet, or through Artifactory as a proxy etc), and have a model for developers to be able to maintain and update docker images. Often docker images that need customization will need their own pipelines and independent lifecycle management (assuming you need to extend the features of a docker image maintained somewhere like Docker Hub).&lt;/p>
&lt;p>This concern also exists without the use of 3 Muskteers, if your organization uses docker images in anywhere in any capacity (ie for application deployment or in any other part of the organization). As long as docker images aren't banned in your organization, and you have any need to modify or update a docker image anywhere, you'll have to solve for this.&lt;/p>
&lt;h2 id="wont-i-have-to-do-more-scripting-to-do-things-like-export-the-results-of-scan-details-and-test-results-out-to-another-location">Won't I have to do more scripting to do things like export the results of scan details and test results out to another location?&lt;/h2>
&lt;p>Yes, there's an extra layer with docker that introduces volume mounts, a more careful look at permissions and ownership, and files need to be stored/saved outside the container run. That's an overhead and a bit of a learning journey which can be painful to begin with, especially without docker knowledge. Generally once you get those patterns working well, you have less issues moving forward (and you can repeat/share the patterns), and imprpoving your docker skills may be useful if you need them for your app development too.&lt;/p>
&lt;p>This question ends up being one about tradeoffs as you get more consistency and reuse across Linux/Mac/Windows and your CI tool, you make life easier if you ever move CI tools (think about moves to GitLab, Azure, GitHub etc), and setup is a breeze for new devs joining the company or the team.&lt;/p>
&lt;p>The key advice here is to use small containers that have a single binary and a single purpose, when they do an operation that produces an output it should be available via a volume mount that allows easy access either via your workstation or CI tool, after that it's functionally the same as having run it without docker.&lt;/p>
&lt;p>&lt;em>Alternative:&lt;/em> In theory you could cheat the pattern a little here by using native container image steps in a tool like GitLab/GitHub to reduce the work you need to do to mount files and export them, but it does make it less portable for local usage or if you ever move CI tools.&lt;/p>
&lt;p>&lt;em>Alternative:&lt;/em> If you were to not use containers at all you wouldn't be able to run everywhere as easily and as consistently, which would arguably shift some toil back to developers.&lt;/p>
&lt;h2 id="whos-responsibility-is-it">Who's responsibility is it?&lt;/h2>
&lt;p>3musketeers &lt;strong>will not&lt;/strong> stop you from writing bad pipelines or bad code.&lt;/p>
&lt;p>3musketeers &lt;strong>will not&lt;/strong> write your build steps for you.&lt;/p>
&lt;p>3musketeers &lt;strong>will not&lt;/strong> replace your build process or your build tool.&lt;/p>
&lt;p>3musketeers &lt;strong>will not&lt;/strong> deploy your application for you.&lt;/p>
&lt;p>3musketeers &lt;strong>will not&lt;/strong> store metadata or artifacts during your build process.&lt;/p>
&lt;p>For example, if your project, team or organization creates a docker container to provide some sort of functionality (e.g a sonar scanner image) this docker image should have it's own:&lt;/p>
&lt;ul>
&lt;li>documentation&lt;/li>
&lt;li>development lifecycle&lt;/li>
&lt;li>versioning / latest tags in git and in it's artifact store&lt;/li>
&lt;li>versions should be immutable - never override a version with new code&lt;/li>
&lt;li>if the container is run without the approriate environment variables, it should set sensible defaults where it can, and fail with explict error messages where it can't set a defaults&lt;/li>
&lt;li>requirements about connectitity should be well documented (and properly testing with proper errors when there's lack of connecivity)&lt;/li>
&lt;li>auth should be clearly documented and variablized&lt;/li>
&lt;li>if volume mounts are required and if files are outputted, this should be clearly articulated&lt;/li>
&lt;/ul>
&lt;p>Making changes:&lt;/p>
&lt;ul>
&lt;li>scripts/containers should allow you to override config files and/or supply additional config files or variables to override defaults (thus reducing the need to change the container/script itself each time)&lt;/li>
&lt;li>if your container is really a packaging mechinisum for a script consider how the script will be used and if it's better to be baked into the container (ie each change to the script should cause a new version of the container) or if you'd like users to maintain the script themselves and simply call the container with the script mounted into it&lt;/li>
&lt;/ul></description></item></channel></rss>