<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Devteds</title><description>Products and insights from Devteds — building tools for families and developers.</description><link>https://devteds.com/</link><item><title>I Replaced My Entire Dev Workflow with Claude Code. Here&apos;s What Actually Works.</title><link>https://devteds.com/claude-code-replaced-my-dev-workflow/</link><guid isPermaLink="true">https://devteds.com/claude-code-replaced-my-dev-workflow/</guid><description>After two months of using Claude Code across multiple projects, from a simple Astro site to complex Docker+Rails setups, here&apos;s what changed in my workflow and what still needs a human.</description><pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Two months ago I switched from Cursor to Claude Code. Fully. Not “trying it on the side” but “this is how I code now.”&lt;/p&gt;
&lt;p&gt;I’d been using Cursor for a while and it was fine. Good autocomplete, decent chat. But Claude Code changed how I think about the work itself, not just how I type it. The difference isn’t speed. It’s that I stopped thinking about writing code and started thinking about describing what I want built.&lt;/p&gt;
&lt;p&gt;I use it across everything. At my day job, I use Claude (enterprise) on a large codebase that spans APIs, a React frontend, and a CMS across multiple product layers. On the side, I use it for the Devteds website (Astro + Tailwind), Wistale (an AI storybook product), Node.js services, and a Rails app with a Docker setup that’s genuinely painful on Mac when you’re juggling multiple worktrees and parallel containers. The range matters. What I’ve learned working on a large production codebase at work directly informs how I use Claude on smaller projects, and vice versa.&lt;/p&gt;
&lt;h2 id=&quot;claudemd-is-the-real-product&quot;&gt;CLAUDE.md is the real product&lt;/h2&gt;
&lt;p&gt;If you’re using Claude Code without a CLAUDE.md file, you’re using maybe 30% of it.&lt;/p&gt;
&lt;p&gt;CLAUDE.md is a markdown file at the root of your project. It gives Claude persistent context: your stack, your conventions, your file structure, where things live. It loads into every conversation automatically.&lt;/p&gt;
&lt;p&gt;Writing it felt more like product design than configuration. I was defining how an AI agent should think about my project. What matters. What doesn’t. What the rules are.&lt;/p&gt;
&lt;p&gt;For the Devteds site, mine includes the stack (Astro, Tailwind v4, TypeScript strict), URL conventions, where content and images live, the old Jekyll project path for migration reference, brand color, hosting details. Under 50 lines. Shorter is better. Every vague instruction dilutes the specific ones.&lt;/p&gt;
&lt;p&gt;For my Rails project, the CLAUDE.md is different. It describes the Docker Compose setup, which containers talk to which, how to run migrations, which worktree maps to which branch. That project has a lot of moving parts and Claude needs to know the topology before it touches anything.&lt;/p&gt;
&lt;h2 id=&quot;skills-where-it-gets-genuinely-useful&quot;&gt;Skills: where it gets genuinely useful&lt;/h2&gt;
&lt;p&gt;A skill is a markdown file in &lt;code&gt;.claude/skills/&lt;/code&gt; that gives Claude a playbook for a specific task. You invoke it like a slash command. No SDK, no API, no build step. Just a folder with a SKILL.md and optionally some reference files. That’s the entire abstraction.&lt;/p&gt;
&lt;p&gt;The first skills I built were for content (generating blog ideas, drafting posts). Those were useful. But the skills that actually changed how I work are the ones that wrap the development process itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;/start-task&lt;/code&gt;&lt;/strong&gt; is what I run at the beginning of every session. It checks the current code state, fetches latest from the repo, reads the roadmap, reviews recent commits, checks what’s deployed, and suggests what to work on next. It’s like a standup meeting with your codebase. Before this, I’d spend the first 10 minutes of every session figuring out where I left off. Now I just type &lt;code&gt;/start-task&lt;/code&gt; and get a summary with clear next steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;/wrap-up&lt;/code&gt;&lt;/strong&gt; handles end-of-session bookkeeping. It updates the roadmap with what got done, stages and commits the right files, pushes to the remote, and optionally creates and merges a PR. All the stuff I used to forget or skip when I was tired at the end of a session. The roadmap used to get stale within a week. Now it stays current because closing the loop is one command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;/seo-review&lt;/code&gt;&lt;/strong&gt; runs a Lighthouse audit, analyzes the scores, identifies specific fixes with priorities, and adds tasks to the roadmap. Instead of me squinting at Lighthouse reports and manually tracking what needs fixing, Claude reads the numbers and turns them into actionable work items. This one paid for itself immediately on the Devteds rebuild when we needed to hit 95+ across all categories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;/new-idea&lt;/code&gt;&lt;/strong&gt; is for when I’m thinking about a product idea and want to pressure-test it quickly. It researches the competitive landscape, assesses technical feasibility, estimates MVP scope, and gives a verdict (worth exploring, park it, or pass). If it’s worth exploring, it adds a section to the roadmap. I built this because I was losing ideas in random notes. Now they either make it into the roadmap with context or get explicitly passed on.&lt;/p&gt;
&lt;p&gt;I’ve built similar skills for Wistale and it’s been very efficient. The pattern is the same across projects: codify the process you already follow, but make it consistent and automatic. Good practices that you’d skip when you’re rushing now happen every time because the skill enforces them.&lt;/p&gt;
&lt;h2 id=&quot;how-these-skills-connect-into-a-workflow&quot;&gt;How these skills connect into a workflow&lt;/h2&gt;
&lt;p&gt;The real value isn’t any single skill. It’s that they chain together into a rhythm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start a session with &lt;code&gt;/start-task&lt;/code&gt;. Know exactly where you are and what’s next.&lt;/li&gt;
&lt;li&gt;Do the work. Claude handles the code, you handle the decisions.&lt;/li&gt;
&lt;li&gt;If a new idea comes up mid-session, run &lt;code&gt;/new-idea&lt;/code&gt; to capture and assess it without losing context.&lt;/li&gt;
&lt;li&gt;Periodically run &lt;code&gt;/seo-review&lt;/code&gt; to catch regressions.&lt;/li&gt;
&lt;li&gt;End the session with &lt;code&gt;/wrap-up&lt;/code&gt;. Roadmap updated, code pushed, nothing forgotten.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This loop keeps the project healthy. The roadmap is always current. Commits are clean. Performance doesn’t silently degrade. Ideas don’t get lost in a notes app somewhere.&lt;/p&gt;
&lt;p&gt;Before skills, all of this was manual discipline. And manual discipline fails when you’re tired, or rushing, or juggling three projects.&lt;/p&gt;
&lt;h2 id=&quot;what-actually-changed-across-projects&quot;&gt;What actually changed across projects&lt;/h2&gt;
&lt;p&gt;The Devteds rebuild was the most dramatic example. I migrated 12 Jekyll blog posts by pointing Claude at the old project directory and describing the new frontmatter schema. Ten minutes of my time, mostly reviewing output. Product pages for Wistale were describe-and-iterate. Lighthouse optimization went from a manual chore to a conversation.&lt;/p&gt;
&lt;p&gt;But the Rails project is where Claude Code proved itself differently. That codebase has Docker containers for the app, the database, Redis, background workers. Running parallel worktrees means parallel sets of containers. On a Mac, that’s a mess. Claude handles the Docker Compose juggling, knows which port maps where, and doesn’t get confused when I switch branches. Before this, I kept a cheat sheet. Now Claude is the cheat sheet.&lt;/p&gt;
&lt;p&gt;The Node.js projects are simpler but still benefit. Scaffolding, test setup, dependency management. The kind of work that’s not hard but eats time.&lt;/p&gt;
&lt;h2 id=&quot;where-claude-code-falls-apart&quot;&gt;Where Claude Code falls apart&lt;/h2&gt;
&lt;p&gt;I want to be honest about the failure modes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Visual design is still a human job.&lt;/strong&gt; Claude can implement a design you describe, but it can’t originate one that feels polished. Every first version looked like a template. I pushed hard on spacing, typography, and visual hierarchy. Even then, I was making most of the aesthetic decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Complex multi-step refactors get messy.&lt;/strong&gt; When I asked Claude to restructure the entire layout system across multiple components simultaneously, it lost track of dependencies. Smaller, focused tasks worked much better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It doesn’t know when to stop.&lt;/strong&gt; Ask Claude to “improve” something and it’ll add error handling, type annotations, comments, and abstractions you didn’t ask for. I learned to be specific: “change X to Y” not “make this better.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Context drift in long sessions.&lt;/strong&gt; After 30+ exchanges, Claude starts losing track of earlier decisions. I start fresh sessions with &lt;code&gt;/clear&lt;/code&gt; for each distinct task. CLAUDE.md carries the persistent context.&lt;/p&gt;
&lt;h2 id=&quot;why-i-switched-from-cursor&quot;&gt;Why I switched from Cursor&lt;/h2&gt;
&lt;p&gt;Cursor is a good tool. The autocomplete is fast, the inline chat works well for small edits. I still open it occasionally for quick stuff, though that’s happening less and less.&lt;/p&gt;
&lt;p&gt;But Claude Code operates at a different level. Cursor helps you write code faster. Claude Code helps you think about code differently. The terminal-first approach felt weird at first, then liberating. I describe what I want, Claude reads the project context, makes the changes across files, and I review the diff.&lt;/p&gt;
&lt;p&gt;The skills system is what sealed it. There’s nothing like it in Cursor. Being able to type &lt;code&gt;/start-task&lt;/code&gt; and get a full project status with recommendations, or &lt;code&gt;/wrap-up&lt;/code&gt; and have all the session cleanup handled, that’s not autocomplete. That’s a development process.&lt;/p&gt;
&lt;h2 id=&quot;the-patterns-that-stuck&quot;&gt;The patterns that stuck&lt;/h2&gt;
&lt;p&gt;After two months across multiple projects:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep CLAUDE.md ruthlessly short.&lt;/strong&gt; Project-level rules only. Task-specific stuff goes in skills.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One task per conversation.&lt;/strong&gt; Don’t ask Claude to migrate blog posts AND redesign the footer in the same session.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Review every diff.&lt;/strong&gt; The moment you stop reviewing is the moment Claude introduces a subtle bug or an unwanted “improvement.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Build skills for your process, not just your code.&lt;/strong&gt; The most valuable skills I’ve written don’t generate code at all. They manage the workflow around the code. Start-of-session, end-of-session, audits, idea capture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Each project teaches you something new.&lt;/strong&gt; The CLAUDE.md that works for a static site doesn’t work for a Rails monolith. The skills that help with one project’s workflow don’t map directly to another. You learn what Claude needs to know by watching where it gets confused.&lt;/p&gt;
&lt;h2 id=&quot;what-this-actually-means-for-solo-developers&quot;&gt;What this actually means for solo developers&lt;/h2&gt;
&lt;p&gt;I don’t think Claude Code replaces developers. But it changes what “development” means day to day.&lt;/p&gt;
&lt;p&gt;I spent more time this month on product decisions, content strategy, and design direction than on actual code. The code itself (Astro components, Tailwind styling, Docker configs, content migration) was mostly handled by Claude with my review.&lt;/p&gt;
&lt;p&gt;For a solo developer running a product studio, that’s a real shift. I’m not faster at coding. I’m spending less time on code and more time on the things that actually differentiate the product.&lt;/p&gt;
&lt;p&gt;The biggest surprise wasn’t the code generation. It was that skills turned my ad-hoc habits into a reliable process. I used to forget to update the roadmap. I used to let Lighthouse scores drift. I used to lose track of where I left off between sessions. Now there’s a command for each of those things, and it works the same way every time.&lt;/p&gt;
&lt;p&gt;If you want to try it, start small. Add a CLAUDE.md to one project. Use Claude Code for one real task. Then write one skill for something you do repeatedly. See if it earns your trust.&lt;/p&gt;
&lt;p&gt;That’s how it started for me. Two months later, I can’t imagine going back.&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2026/claude-code-dev-workflow.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2026/claude-code-dev-workflow.webp"/><category>AI Tools</category><category>Developer Workflow</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Multi-Container Local Development Setup using DevContainers in VSCode</title><link>https://devteds.com/devcontainers-for-developers-multi-container-local-dev-setup/</link><guid isPermaLink="true">https://devteds.com/devcontainers-for-developers-multi-container-local-dev-setup/</guid><description>Discover how to establish a multi-container local development environment using the Dev Containers extension within VSCode with Docker Compose.</description><pubDate>Mon, 13 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As an application or web developer, it’s crucial to have an isolated yet seamless local development setup, especially when managing multiple applications or codebases across various microservice apps built on different programming languages, versions, or frameworks.&lt;/p&gt;
&lt;p&gt;If you’ve used Docker for local development, you might be familiar with Docker Compose for handling multiple container setups. While this is helpful in many ways, it requires an extra step of entering a running container to execute ad hoc commands.&lt;/p&gt;
&lt;p&gt;For instance, if you’re a Rails developer, you often need to run commands like rails generators, db migrations, rails runner, and especially the rails console. Similar challenges exist for most frameworks.&lt;/p&gt;
&lt;p&gt;Imagine if you could open your editor in a container mode that loads the application environment and allow us to run application commands in the embedded terminal, without needing to run commands like &lt;code&gt;docker run&lt;/code&gt;, &lt;code&gt;docker exec&lt;/code&gt;, etc. This would make working within a containerized environment much easier.&lt;/p&gt;
&lt;p&gt;One option that has worked well for my local development setup is the Dev Containers extension in VSCode. Not only does it allow you to load the application in VSCode within a container context, but you can also run dependency services (or other tiers of the application stack) as containers. The Dev Containers extension integrates smoothly with Docker Compose.&lt;/p&gt;
&lt;p&gt;In this short tutorial, I’ll guide you through setting up a local development environment for an application that uses a database, both running as containers using Docker Compose and Dev Containers in VSCode. Here’s a quick breakdown of the steps you’ll learn in the tutorial:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start by creating a Dev Container setup for your application&lt;/li&gt;
&lt;li&gt;Create a web application using the Dev Container&lt;/li&gt;
&lt;li&gt;Configure a multi-container setup with Dev Container for the application and database containers&lt;/li&gt;
&lt;li&gt;Test and verify, mainly database container, using Docker Desktop&lt;/li&gt;
&lt;li&gt;Link the application running as a container with the database running as another container&lt;/li&gt;
&lt;li&gt;Conclude with a note on &lt;a href=&quot;/multi-stage-docker-build-tutorial/&quot;&gt;Multi-Stage Docker Build&lt;/a&gt; with DevContainer &amp;#x26; Docker Compose&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Setting up a reliable local environment can significantly enhance productivity for app developers. I hope this short video tutorial proves helpful!&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2024/2024-05-13-devcontainers-for-developers-multi-container-local-dev-setup.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2024/2024-05-13-devcontainers-for-developers-multi-container-local-dev-setup.webp"/><category>Learn Docker</category><category>Dev Containers on VSCode</category><category>Docker Tutorial</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>VPC Basics for Kubernetes on Amazon EKS</title><link>https://devteds.com/aws-vpc-for-kubernetes-on-amazon-eks/</link><guid isPermaLink="true">https://devteds.com/aws-vpc-for-kubernetes-on-amazon-eks/</guid><description>Explore the fundamentals of VPC and its application in Kubernetes clusters on AWS EKS.</description><pubDate>Tue, 05 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As Kubernetes gains traction in the tech world, many companies leveraging AWS infrastructure are delving into Amazon EKS (Elastic Kubernetes Service) either to dip their toes or to dive in deep. Whether you’re diving headfirst into EKS, AWS’s managed Kubernetes cluster, or crafting your own Kubernetes setup on AWS, understanding the VPC and the networking requirements is essential.&lt;/p&gt;
&lt;p&gt;For DevOps engineers and application developers, networking setups might not be your main focus, but understanding the basics can be incredibly helpful. In this article, I’ll give you a simple overview of AWS VPC and using it for Kubernetes on EKS. This won’t cover all the technical details, but it’s a friendly guide for anyone starting out with EKS or Kubernetes on AWS&lt;/p&gt;
&lt;h2 id=&quot;vpc-understanding-the-basics&quot;&gt;VPC: Understanding the Basics&lt;/h2&gt;
&lt;p&gt;VPC, or Virtual Private Cloud, is a fundamental component of AWS that enables users to create a private section of cloud space within the larger public cloud environment. This private section is isolated from other users in the cloud and provides controls to manage access to and from the internet.&lt;/p&gt;
&lt;p&gt;With VPC, users are allocated a specific range of IP addresses, allowing them to host a predetermined number of resources or compute instances (such as EC2 instances, Load Balancers etc.) within the confines of their VPC.&lt;/p&gt;
&lt;p&gt;Moreover, VPC facilitates the creation of subnets, which are used to group resources and control access. Subnets enable users to designate which groups of resources can be publicly accessible via public IP addresses, which should be strictly internal with internet connectivity but no incoming connections (private subnets), and which are solely for internal communication (intra subnets). Users have the flexibility to create multiple subnets to suit their needs.&lt;/p&gt;
&lt;p&gt;Additionally, VPC allows users to define auto-failover setups at both the VPC and subnet levels by distributing resources across two or more Availability Zones (AZs). In AWS, an AZ represents a distinct physical location within a region, and each AWS region typically consists of two or more AZs. For instance, if you choose “North Virginia” as the AWS region for your VPC, you would configure your setup to span at least two AZs within that region.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/images/post/2024/vpc-basics-3-subnets.webp&quot; alt=&quot;AWS VPC - A basic overview&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;kubernetes-and-amazon-eks&quot;&gt;Kubernetes and Amazon EKS&lt;/h2&gt;
&lt;p&gt;Kubernetes, commonly known as K8S, is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications. It provides a rich set of features for managing containerized workloads, including automated rollouts, self-healing, and scaling based on resource usage.&lt;/p&gt;
&lt;p&gt;In Kubernetes infrastructure, two critical components play pivotal roles: the control plane and the worker nodes. The control plane serves as the brain of the Kubernetes cluster, orchestrating and coordinating various tasks. Conversely, the worker nodes constitute the deployment environment for containerized applications. Managing the control plane is often a complex undertaking in Kubernetes deployments, requiring meticulous attention to detail. This is where Amazon EKS steps in to alleviate the burden, particularly for users operating within the AWS ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/images/post/2024/k8s-cluster-control-plane-and-worker-nodes.webp&quot; alt=&quot;Kubernetes Cluster - Control Plane &amp;#x26; Worker Nodes&quot;&gt;&lt;/p&gt;
&lt;p&gt;Amazon EKS, also known as Elastic Kubernetes Service, is AWS’s managed solution for Kubernetes. It simplifies the deployment, management, and scaling of Kubernetes clusters on AWS. With EKS, users can focus on developing and innovating their applications without worrying about managing the complexities of Kubernetes clusters, mainly the Control Plane part. Additionally, EKS seamlessly integrates with various AWS services like Load Balancers, IAM, and CloudWatch, making it easier to manage security, monitor performance, and distribute traffic efficiently.&lt;/p&gt;
&lt;h2 id=&quot;vpc-for-kubernetes-on-amazon-eks&quot;&gt;VPC for Kubernetes on Amazon EKS&lt;/h2&gt;
&lt;p&gt;When configuring a VPC for a Kubernetes cluster on Amazon EKS, it’s beneficial to establish three types of subnets, each serving distinct purposes: Private Subnets, Public Subnets and Intra Subnets.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/images/post/2024/vpc-for-k8s-on-aws-eks.webp&quot; alt=&quot;VPC &amp;#x26; Subnets for Kubernetes Cluster on AWS EKS&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;private-subnets&quot;&gt;Private Subnets&lt;/h3&gt;
&lt;p&gt;Private subnets primarily host the worker nodes or the compute instances running the actual application containers. Even applications requiring external access for end-users or customers typically operate within private subnets. In most scenarios, applications need to communicate beyond the VPC boundaries, whether for downloading resources or sending HTTP requests. Therefore, making private subnets the primary environment for worker nodes is often preferred. While exceptions may exist, this setup generally ensures smooth operation.&lt;/p&gt;
&lt;h3 id=&quot;public-subnets&quot;&gt;Public Subnets&lt;/h3&gt;
&lt;p&gt;Public subnets accommodate resources that necessitate internet access, enabling users from the internet to interact with them. For instance, a website accessible via a web browser typically resides in a public subnet. In the Kubernetes context, containers rarely serve as direct public-facing web servers. Instead, public-facing applications are typically served through Reverse Proxies and Load Balancers. These Load Balancers can be deployed in public subnets.&lt;/p&gt;
&lt;p&gt;Utilizing AWS Load Balancer service in Kubernetes via Kubernetes Ingress Controller seamlessly integrates with Amazon EKS. Placing Load Balancers in public subnets enables end-users to access the externally facing Load Balancer’s IP or hostname. The Load Balancer then proxies requests to the target container applications running within the worker node space in private subnets.&lt;/p&gt;
&lt;h3 id=&quot;intra-subnets&quot;&gt;Intra Subnets&lt;/h3&gt;
&lt;p&gt;The control plane, the heart of the Kubernetes cluster, is ideally housed within intra subnets. From a security standpoint and best practices perspective, restricting internet access to intra subnets is advisable. This setup ensures that the control plane remains isolated and secure, minimizing potential vulnerabilities.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;To wrap up, if AWS is your go-to cloud provider and you’re considering Kubernetes, EKS is a solid choice to kickstart your journey. While there are pros and cons to using EKS versus managing your Kubernetes cluster, EKS typically offers a hassle-free experience. In this brief article, I’ve provided a high-level overview of VPC requirements for Kubernetes. For those eager to dive deeper, I recommend delving into VPC intricacies and exploring EKS further.&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2024/2024-03-05-vpc-for-kubernetes-on-aws-eks-2.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2024/2024-03-05-vpc-for-kubernetes-on-aws-eks-2.webp"/><category>AWS</category><category>Kubernetes</category><category>Cloud Engineering</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>DevOps Local Setup: Docker &amp; Dev-Containers in VSCode</title><link>https://devteds.com/devops-local-setup-with-docker-and-devcontainers-vscode/</link><guid isPermaLink="true">https://devteds.com/devops-local-setup-with-docker-and-devcontainers-vscode/</guid><description>Learn how to setup an efficient and consistent local environment for DevOps, Platform or Cloud Engineering workstation, using Docker and Dev Containers in VSCode.</description><pubDate>Wed, 15 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As an app developer or a DevOps, Platform, or Cloud Engineer, having a smooth and reliable local setup makes a huge difference in productivity gain. If you’ve ever wondered about improving your workstation setup for devops or cloud projects, this short video tutorial will definitely help you.&lt;/p&gt;
&lt;p&gt;Imagine you’re working on a project that requires a few different CLI tools for managing applications &amp;#x26; deployment environments and the cloud infrastructure such as AWS and Kubernetes. That’ll require you to set up your local environment with all the necessary tools and CLIs.&lt;/p&gt;
&lt;p&gt;There are a couple of ways to go about it. One approach is to individually install all these tools on your machine, whether it’s a Mac, Windows, or Linux. The catch is that your teammates will likely do the same and there is a good chance that it may lead to variations in tool versions and operating systems and that’ll make workstation setups across the team inconsistent.&lt;/p&gt;
&lt;p&gt;Alternatively, you can consider using Docker Containers. In this scenario, you create a Docker image containing all the necessary tools and have your entire team adopt the same Docker image. However, it’s easier said than done, as maintaining a uniform container environment setup can become quite challenging.&lt;/p&gt;
&lt;p&gt;In this short tutorial, I will walk you through setting up local environment for devs in the DevOps, Platform or Cloud Engineering space. Here’s a quick breakdown of the steps you’ll learn in the tutorial:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configuring the Dev Containers extension for our project&lt;/li&gt;
&lt;li&gt;Installing tools such as AWS CLI and CDK using Dev Containers features&lt;/li&gt;
&lt;li&gt;Installing additional CLI tools such as Terraform and Kubectl by editing Dockerfile&lt;/li&gt;
&lt;li&gt;Using Docker Volume mount for managing keys such as AWS Auth Credentials&lt;/li&gt;
&lt;li&gt;Testing AWS CLI from inside the Dev Container&lt;/li&gt;
&lt;li&gt;Test running Terraform code against AWS S3 from inside the Dev Container&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Setting up a reliable local environment can significantly enhance productivity for app developers, DevOps, Platform, and Cloud Engineers. I trust this short video tutorial proves helpful!&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2023/2023-11-15-devops-local-setup-with-docker-and-devcontainers-vscode.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2023/2023-11-15-devops-local-setup-with-docker-and-devcontainers-vscode.webp"/><category>Learn Docker</category><category>Dev Containers on VSCode</category><category>Docker Tutorial</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>The Flipside of Microservices: Knowing When to Embrace or Pass</title><link>https://devteds.com/microservices-when-to-embrace-or-pass/</link><guid isPermaLink="true">https://devteds.com/microservices-when-to-embrace-or-pass/</guid><description>Microservices offer a way to make flexible, scalable apps. But are they right for all? Discover how to find the right balance for your app.</description><pubDate>Wed, 23 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Many teams explore the idea of breaking their applications into smaller services. This brings up a question: are they doing this to create a complete Microservices system, or just trying out the idea of making smaller apps to make them easier to handle? But going into the world of Microservices can be complicated and tricky, needing careful planning and a lot of effort to set up the necessary tools and structure to make it work well.&lt;/p&gt;
&lt;p&gt;However, at the heart of “Microservices” is a really interesting idea — it’s a way to create apps that can grow and adapt easily, especially when it comes to handling more users. But there’s still a puzzle to solve: are these Microservices good for everyone? suitable for all situations? And as time goes on, what problems might come up when it’s about keeping everything working smoothly?&lt;/p&gt;
&lt;h2 id=&quot;breaking-things-down&quot;&gt;Breaking Things Down&lt;/h2&gt;
&lt;p&gt;Microservices break down apps into smaller, specialized parts. This is great because you can work on one part without messing up the whole project. It speeds up development and lets teams work independently on different parts, which is awesome for scaling things up.&lt;/p&gt;
&lt;p&gt;But, here’s the thing: Microservices mean lots of separate projects. That means separate repos, setups, and rules to manage. If you’ve got a bunch of these Microservices, it’s like trying to remember which remote controls belong to which gadgets. Keeping everything updated and in line can turn into a puzzle that’s not always fun to solve.&lt;/p&gt;
&lt;p&gt;Real challenge is “how granualar should teams or companies go in breaking down the larger application or system?”. When a team that is responsible for a decent size application decides to break it down into many smaller size apps (with the idea of Microservices), that will soon become challenging for the long run.&lt;/p&gt;
&lt;h2 id=&quot;puzzle-of-micro-frontends&quot;&gt;Puzzle of Micro-Frontends&lt;/h2&gt;
&lt;p&gt;Picture this: you take the idea of “Microservices” and apply it to how the front part of a website works — the micro-frontends. Sounds cool, right? It offers flexibility and lets you reuse things.&lt;/p&gt;
&lt;p&gt;Now, imagine this: instead of having just one big piece, you break it down into lots of tiny pieces. Each of these pieces has its own place to stay (code repository), its own way of being built (language, framework, version, etc.), and its own path to get out into the world (deployment). The process of working on them locally, testing them, and getting them ready to show to the world? It’s like getting caught in a web of complexity. And here’s the twist — it can become pretty tricky for the developers to keep up with all these small front-end pieces.&lt;/p&gt;
&lt;p&gt;It’s like trying to keep track of a whole bunch of puzzle pieces, each with its own unique shape and place.&lt;/p&gt;
&lt;h2 id=&quot;tackling-the-puzzle-of-updates--upgrades&quot;&gt;Tackling the Puzzle of Updates &amp;#x26; Upgrades&lt;/h2&gt;
&lt;p&gt;Imagine being in a situation where you’re tasked with updating a foundational library that’s spread across multiple microservices. This whole affair is often triggered by the stringent demands of InfoSec or Security &amp;#x26; Compliance standards. You’re looking at upgrades for Node or Ruby, possibly a shift in Rails, and don’t forget those client library upgrades to keep up with the shifting sands of services like RDS/MySQL/PostgreSQL, message brokers, and versions of other application dependencies.&lt;/p&gt;
&lt;p&gt;But here’s the kicker: what looks like a simple update job can quickly turn into a complex process of testing and checking. Each of those microservices has its own set of needs and things it depends on. And this is where things get tricky — making sure that all these pieces fit together without causing chaos.&lt;/p&gt;
&lt;p&gt;And let’s not even start talking about the sheer number of tasks that suddenly pile up on your to-do list. It’s not just one or two tasks — you’re looking at a bunch of them, all waiting to be taken care of. Now, the team that’s already familiar with these new microservices might handle it fine, but for those who are new, it can be a lot to handle. The flood of tasks, the growing list of releases, the planning, the constant testing — it can get pretty overwhelming.&lt;/p&gt;
&lt;p&gt;It would be great if there is great deal of automation for testing, releases (CI/CD) and a great source code branching strategy, etc. but let’s be real — as nice as that sounds, turning this into reality isn’t all that easy.&lt;/p&gt;
&lt;h2 id=&quot;transition-troubles&quot;&gt;Transition Troubles&lt;/h2&gt;
&lt;p&gt;Imagine this: organizations have to think about moving their code homes from one place to another. Why? Well, maybe to save money or to have better ways (features) of working together.&lt;/p&gt;
&lt;p&gt;Now, hold on tight, because here comes the real head-scratcher. Moving a big bunch of code is already kind of complicated, right? But what if you’re not just moving one thing — you’re moving lots of smaller things, like these microservices? That’s like adding an extra layer of confusion. Going from one platform to another, or trying to put all your code in one place, is like trying to solve a really tough puzzle. And you can’t afford to lose any of the puzzle pieces. It takes super careful planning and making sure everything happens just right.&lt;/p&gt;
&lt;h2 id=&quot;the-crucial-role-of-appropriate-tools-and-setup&quot;&gt;The Crucial Role of Appropriate Tools and Setup&lt;/h2&gt;
&lt;p&gt;Imagine this: someone using your app says something isn’t working right. Now, where do you start looking to fix it? Is it in a main part of the app, or could it be in a separate part that does its thing later? Figuring out what went wrong in the whole process can be like solving a puzzle. Finding the real reason might take some time. It might not seem like a big deal at first, but it could become a bigger problem later. It all depends on how many parts your app has, how they talk to each other, how well you’re keeping track of things, and how easily you can see what’s happening.&lt;/p&gt;
&lt;p&gt;But here’s the bottom line: if you don’t have the right tools and setup to manage all these parts of your app, then going for the microservices way might not be a good idea. It’s like trying to go on a big trip without the things you need for the journey. It can make things much harder than they should be, and you might not get the good results you were hoping for.&lt;/p&gt;
&lt;h2 id=&quot;striking-the-balance&quot;&gt;Striking the Balance&lt;/h2&gt;
&lt;p&gt;Here’s the deal: Microservices and micro-frontends aren’t magical solutions. Yes, they bring some powerful tricks, but they also bring some complications. Finding the right balance between being organized and being able to handle things is super important. You don’t want too many of these small services, or it might end up causing more problems than it solves.&lt;/p&gt;
&lt;h3 id=&quot;picking-the-right-way-to-build&quot;&gt;Picking the Right Way to Build&lt;/h3&gt;
&lt;p&gt;When you’re thinking about how to put together your project, you’ve got to think it through. Here are some extra tips for making the right choice, especially for teams that are in charge of one or a few parts of a big company system:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Size and Complication:&lt;/strong&gt; Think about how big and complex your application is. If it’s not too huge, you might just need to keep it the way it is. Or maybe split it into 2 or 3 pieces. But don’t go wild. For big and complicated things, microservices work better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teams Doing Their Thing:&lt;/strong&gt; If you’ve got different teams working on different parts, and they all want their space, microservices could be the way. But if your teams can cooperate without messing things up, keeping it all in one piece might be smoother.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Growing When Needed:&lt;/strong&gt; If you’re planning for some parts of your app to grow a lot, extracting that as a separate service makes sense. But if growing isn’t a big deal and it doesn’t really add value to business, keeping everything together can be easier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What You Know and Have:&lt;/strong&gt; Think about what your team knows. Microservices ask for more knowledge about tricky things like distributed systems and how stuff is put together. Check if your team can handle it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How Your Team Works:&lt;/strong&gt; Take a look at how your team is set up and talks to each other. If your team is spread out and chatty, microservices could fit in. But if things are more centered, keeping it all as one might work better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Right tooling and Infrastructure:&lt;/strong&gt; Does your company have the right tooling (for build, deployments, code scanning, log analysis, transaction tracing, etc.)? If not, solve for that before attempting to break the app down into smaller sizes.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;more-pointers&quot;&gt;More Pointers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;If there’s a real, real need for breaking a big application into small pieces, think about just doing it a bit — like 2 or 3 pieces, not going all-in for Microservices.&lt;/li&gt;
&lt;li&gt;Microservices are something that needs a whole-company or org effort, not just a bunch of teams doing their own thing. You’ve got to think about finding problems, checking logs, and looking at how everything’s working together when stuff goes wrong, mainly in production&lt;/li&gt;
&lt;li&gt;To take an example - when a team of 5 folks decides to break a big app into 20 tiny apps, guess what? You’ll need more folks to look after those 20 apps now.&lt;/li&gt;
&lt;li&gt;If your team is responsible for one of the apps that is part of a larger enterprise level system, there is definitely need for interservice communications between your app and some of the related apps or components within that system. You may call it Microservices or simply Service Oriented Architecture. Communication can happen via REST or SOAP API calls, GraphQL or Message Brokers, etc. But within your app, need for breaking down the app to smaller size ones, is where you give enough thoughts.&lt;/li&gt;
&lt;li&gt;Here’s food for thought: delve into the “majestic monolith” concept. Check out this link: &lt;a href=&quot;https://m.signalvnoise.com/the-majestic-monolith-can-become-the-citadel/&quot;&gt;The Majestic Monolith&lt;/a&gt;. This method shines for apps raking in revenue with bustling user bases, like Basecamp.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2023/microservices-downsides.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2023/microservices-downsides.webp"/><category>Microservices</category><category>Software Architecture</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Create React.js App using Docker</title><link>https://devteds.com/create-react-app-with-docker/</link><guid isPermaLink="true">https://devteds.com/create-react-app-with-docker/</guid><description>Learn how to create a new React.js app without installing Node on your host OS using Docker.</description><pubDate>Thu, 29 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While Docker may not have gained significant popularity in front-end development, I believe it can greatly benefit engineering teams, including front-end heavy devs. In my personal experience, front-end developers working with React.js or Angular tend to avoid using Docker.&lt;/p&gt;
&lt;p&gt;In the past year, I have actively assisted developers in adopting Docker for their local front-end development environments. This has been particularly helpful in teams where developers are expected to work with multiple front-end apps, frameworks and node versions, etc. Docker’s ability to isolate development setups has made front-end app development smoother and more efficient.&lt;/p&gt;
&lt;p&gt;By dockerizing the development environment, another notable advantage is the reduction in the necessity for specific node and build tool installations within the CI (Continuous Integration) environment. While the end result of the build process typically consists of front-end assets like JS bundles, CSS, images, and HTML files, which are ultimately shipped or pushed to a web server like Nginx or hosted on S3, it is important to note that the build pipeline still heavily relies on build tools. However, by dockerizing this process, the development environment is significantly simplified, alleviating the burden of manual tool installation and configuration. This streamlined approach ensures a more efficient and hassle-free build pipeline.&lt;/p&gt;
&lt;p&gt;In this article, we will delve into the process of leveraging Docker to create a new ReactJS app.&lt;/p&gt;
&lt;h2 id=&quot;create-react-app&quot;&gt;Create React App&lt;/h2&gt;
&lt;p&gt;To begin, navigate to your projects directory by executing the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Navigate to your projects directory&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd ~/my-projects&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a Dockerfile with the following content. This file will serve as the blueprint for building an image to be used as the builder image for creating the React app:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM node:20&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN apt-get update&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN npm install -g npm@9.7.2 &amp;#x26;&amp;#x26; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    npm install -g create-react-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the Dockerfile is created, it’s time to build the builder image using the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker build -t react-app-builder .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To create a React app using the builder image built above, run the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker run  -it --rm \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            -v &quot;$PWD&quot;:/app:rw \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            -w /app  \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            react-app-builder \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            npx create-react-app my-react-app --template=typescript&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command executes a Docker container based on the builder image (&lt;code&gt;react-app-builder&lt;/code&gt;) with the necessary configurations. It mounts the current directory (&lt;code&gt;$PWD&lt;/code&gt;) as a volume inside the container, sets the working directory to &lt;code&gt;/app&lt;/code&gt;, and runs the command &lt;code&gt;npx create-react-app my-react-app --template=typescript&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;During this process, you may encounter an error related to initializing git which may not be an issue. The remaining directory structure of the React app will be created successfully.&lt;/p&gt;
&lt;p&gt;The command &lt;code&gt;npx create-react-app my-react-app --template=typescript&lt;/code&gt; will create a new React app and directory under &lt;code&gt;/app/my-react-app&lt;/code&gt;. Since the &lt;code&gt;/app&lt;/code&gt; directory is mapped to the host directory &lt;code&gt;~/my-projects&lt;/code&gt;, the resulting app directory will be located at &lt;code&gt;~/my-projects/my-react-app&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Creating the React app using Docker without the need for Node installation made the process straightforward. This approach can be applied to create apps with other frameworks too, like Angular, Vue.js, Ruby on Rails, and more. Docker provides a versatile solution that eliminates the requirement for specific tools on your local machine, making app creation hassle-free and applicable across various frameworks.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd my-react-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have completed the initial setup, we can proceed to create a Dockerfile specifically for the app. Follow these steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Delete the builder &lt;code&gt;~/my-projects/Dockerfile&lt;/code&gt; as it is no longer needed.&lt;/li&gt;
&lt;li&gt;Create a new Dockerfile for the app at &lt;code&gt;my-react-app/Dockerfile&lt;/code&gt; with the following content:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Development stage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM node:20 as dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN mkdir /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;WORKDIR /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY package*.json ./app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN npm install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CMD [&quot;npm&quot;, &quot;start&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Build stage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM node:20 as build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN mkdir /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;WORKDIR /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY package*.json ./app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN npm install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY . .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN npm build&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Production stage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM nginx:alpine as prod&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY --from=build /app/build /usr/share/nginx/html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;EXPOSE 80&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s a quick overview of this Dockerfile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It utilizes Docker’s multi-stage build approach, which allows us to differentiate the tooling and requirements for different stages: local development, build pipeline, and the final artifact deployment.&lt;/li&gt;
&lt;li&gt;The development stage (&lt;code&gt;dev&lt;/code&gt;) is intended for local development and sets up the necessary environment for running the app.&lt;/li&gt;
&lt;li&gt;The build stage (&lt;code&gt;build&lt;/code&gt;) is an intermediate stage that prepares the final artifact for deployment.&lt;/li&gt;
&lt;li&gt;The production stage (&lt;code&gt;prod&lt;/code&gt;) creates an Nginx image with the built React app. This image is used to run the React app as a container within any of the app’s environments.&lt;/li&gt;
&lt;li&gt;You can refer to this article that explains the need and use of Multi-Stage Docker Builds: &lt;a href=&quot;/multi-stage-docker-build-tutorial/&quot;&gt;Multi-Stage Docker Build Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;build-and-run-locally&quot;&gt;Build and Run Locally&lt;/h2&gt;
&lt;p&gt;Let’s build the app image locally and run it&lt;/p&gt;
&lt;p&gt;Navigate to the my-react-app directory:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd my-react-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build the app image targeting the &lt;code&gt;dev&lt;/code&gt; stage in the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cd my-react-app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker build -t my-react-app-dev --target dev .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command builds an image named &lt;code&gt;my-react-app-dev&lt;/code&gt; specifically for the development stage.&lt;/p&gt;
&lt;p&gt;Run the React app as a container:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker run --rm -it -p 3000:3000 -v $PWD:/app my-react-app-dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command executes the app container and runs the command &lt;code&gt;npm start&lt;/code&gt; as specified in the Dockerfile under the &lt;code&gt;dev&lt;/code&gt; stage. The React app will be accessible at &lt;code&gt;http://localhost:3000&lt;/code&gt; in your browser. Note that the &lt;code&gt;port 3000&lt;/code&gt; inside the container is mapped to port &lt;code&gt;3000 on the host&lt;/code&gt; machine.&lt;/p&gt;
&lt;p&gt;Additionally, the volume mount (&lt;code&gt;-v $PWD:/app&lt;/code&gt;) ensures that the app code is mounted from the host to the container’s working directory. This allows you to make code changes on the host machine, and the changes will be reflected inside the running container.&lt;/p&gt;
&lt;p&gt;You can now make code changes to your React app directly on the host machine, and the changes will be automatically reflected in the running container.&lt;/p&gt;
&lt;p&gt;To build the Docker image for production and publish it to a Docker registry as part of your CI/CD pipeline:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker build -t MYPRIAVTE_REGISTRY/my-react-app-deployable:v1.1 --target prod .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command builds an image named MYPRIAVTE_REGISTRY/my-react-app-deployable:v1.1 specifically for the production stage. Replace MYPRIAVTE_REGISTRY with your private registry URL.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker push MYPRIAVTE_REGISTRY/my-react-app-deployable:v1.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command pushes the image to the specified Docker registry. Replace MYPRIAVTE_REGISTRY with your private registry URL.&lt;/p&gt;
&lt;p&gt;In local development, you also have the option to use docker-compose instead of the plain docker command, which can simplify the setup. Here’s an example &lt;code&gt;docker-compose.yml&lt;/code&gt; file that should be placed at the root of the React app code directory, alongside the Dockerfile:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;version: &quot;3.8&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;services:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  app:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    build:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      context: .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ports:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - &quot;3000:3000&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    volumes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - .:/app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using this docker-compose.yml file, you can run the React app locally with a single command: docker-compose up. The app will be accessible at &lt;a href=&quot;http://localhost:3000&quot;&gt;http://localhost:3000&lt;/a&gt; as before.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this blog post, we explored how Docker can be used to create a React app and demonstrated the use of Multi-Stage Docker Build to separate the local development and build/deployment stages. The instructions provided in the Dockerfile can be customized to suit your specific needs, such as using different versions of npm or yarn, building more than just assets, and more.&lt;/p&gt;
&lt;p&gt;To further expand your knowledge of Docker, I encourage you to try Dockerizing the application you’re currently working on or creating a new one using Docker. The following articles can help you get started with Docker in your local development environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/beginners-guide-to-docker/&quot;&gt;A Beginner’s Guide to Docker: Understanding the Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/dockerize-an-existing-application-ruby-on-rails/&quot;&gt;Dockerize your current application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/create-ruby-on-rails-application-using-docker/&quot;&gt;Start a new application project using Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/multi-stage-docker-build-tutorial/&quot;&gt;Multi-Stage Docker Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/mysql-and-postgresql-with-docker-in-development/&quot;&gt;Using Docker for databases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2023/create-react-js-app-with-docker.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2023/create-react-js-app-with-docker.webp"/><category>Learn Docker</category><category>React on Docker</category><category>Docker Tutorial</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>A Beginner&apos;s Guide to Docker: Understanding the Basics</title><link>https://devteds.com/beginners-guide-to-docker/</link><guid isPermaLink="true">https://devteds.com/beginners-guide-to-docker/</guid><description>Docker has become a widely-used containerization platform, streamlining the process of developing and deploying applications. In this post, we&apos;ll cover basics of Docker, Dockerfile, Docker Engine and CLI.</description><pubDate>Sat, 01 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My goal is to make Docker basics easy to understand for beginners. If you’re new to Docker or not sure where to begin, start by using Docker to containerize your application in a development environment. Keep reading to learn more.&lt;/p&gt;
&lt;h2 id=&quot;what-is-docker&quot;&gt;What is Docker?&lt;/h2&gt;
&lt;p&gt;“Docker is a tool that simplifies the process of creating, deploying, and running applications using isolated environments called containers”.&lt;/p&gt;
&lt;p&gt;If you haven’t used Docker before, this may not make much sense now. If you’ve used Vagrant with any of the virtualization tools such as VirtualBox or VMWare, you can relate to this to an extent.&lt;/p&gt;
&lt;p&gt;Let’s avoid comparing Docker to virtual machines and instead look at an example to understand the usefulness of Docker.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Say I’m building an API app using Node 19, Yarn for packaging, and I want it to run on a secure Linux OS (Alpine Linux). The thing is, I’m on a Mac with an older version of Node &amp;#x26; Yarn installed, and I don’t want the new setup conflict with the currenly installed node or yarn. Plus, I need to make sure that the app runs on the same versions of OS, system libs, and Node no matter where it’s deployed - from my local machine to production. Also, in the staging &amp;#x26; production environments, I should be able to run the application on shared servers without having to install any tools (OS, libs and runtime) specific this new application.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With Docker one can achieve this easily. To get started with Docker for our app, we first write a Dockerfile.&lt;/p&gt;
&lt;h2 id=&quot;dockerfile&quot;&gt;Dockerfile&lt;/h2&gt;
&lt;p&gt;Dockerfile contains a bunch of instructions, commands, and settings to package the app and create Docker Image. The image built will include everything you need to run your app, like the OS/libraries, runtime, application build files, and dependencies such as node packages, ruby gems, etc. Check out the example below for a Node Application.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Base image from DockerHub (node image with alpine linux)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;FROM node:19-alpine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Directory inside the container to be used as workdirectory &apos;/app&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;WORKDIR /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Copy the package.json and yarn.lock files to the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY package*.json yarn.lock ./&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Install any dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RUN yarn install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Copy the app code to image, under current work directory (. is /app)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;COPY . .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Start-up command: &quot;yarn start&quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CMD [&quot;yarn&quot;, &quot;start&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This Dockerfile will be copied to the root directory of the application’s source code. And this file will be used to &lt;strong&gt;build&lt;/strong&gt; a Docker Image for the application. Let’s take a brief look at the contents of the Dockerfile.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first line &lt;code&gt;FROM node:19-alpine&lt;/code&gt; sets the foundation in which &lt;code&gt;node:19-alpine&lt;/code&gt; is a &lt;a href=&quot;https://hub.docker.com/_/node&quot;&gt;Docker Image&lt;/a&gt; from the &lt;a href=&quot;https://hub.docker.com/&quot;&gt;DockerHub&lt;/a&gt; and it comes with all the OS, libs, runtime/nodejs and yarn installed. While there are many variants of Node image on DockerHub, we picked the one built on Alpine Linux OS.&lt;/li&gt;
&lt;li&gt;Whether the app is run on local machine, physical server in prod or an isolated environment as container, we need a work directory. The line &lt;code&gt;WORKDIR /app&lt;/code&gt; indicates that &lt;code&gt;/app&lt;/code&gt; will be the work directory when the app is run as container using image built using this Dockerfile.&lt;/li&gt;
&lt;li&gt;The line &lt;code&gt;COPY package*.json ./&lt;/code&gt; is to copy the package.json and lock file to the image so that we can run the command &lt;code&gt;yarn install&lt;/code&gt; to install all the package dependencies. The next line &lt;code&gt;RUN yarn install&lt;/code&gt; does that. These two instructions, &lt;code&gt;COPY&lt;/code&gt; &amp;#x26; &lt;code&gt;RUN&lt;/code&gt;, are the build time instructions.&lt;/li&gt;
&lt;li&gt;The line &lt;code&gt;COPY . .&lt;/code&gt; is another build time instruction that copies all the files &amp;#x26; directories from the current directory (application’s source code) to the current directory (same as the work directory) inside the image/container. Optionally, there may be command &lt;code&gt;RUN yarn build&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The last line, &lt;code&gt;CMD [&quot;yarn&quot;, &quot;start&quot;]&lt;/code&gt; is for the runtime. When a container is started using the final Docker Image, &lt;code&gt;yarn start&lt;/code&gt; will be run as boot up command.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before starting to build Docker Image for the app, let’s quickly get an idea of Docker Engine and Docker CLI.&lt;/p&gt;
&lt;h2 id=&quot;docker-engine-and-cli&quot;&gt;Docker Engine and CLI&lt;/h2&gt;
&lt;p&gt;Docker Engine is essentially a background process/service that can perform two primary actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build a Docker Image, commonly referred to as Container Image, using the Dockerfile&lt;/li&gt;
&lt;li&gt;Run containers using the Docker Images or Container Images&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Docker Client or Docker CLI interacts with Docker Engine to perform various tasks. Tasks such as building docker image, running container by providing an image, listing available docker images, listing actively running containers, stop/restart containers, etc.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/images/post/2023/docker-client-server-local.webp&quot; alt=&quot;Docker Engine &amp;#x26; CLI on Local&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Docker Engine can run on various machines, including local development machines, laptops, and virtual or physical application servers. It is compatible with several operating systems, such as Windows, macOS, and various Linux distributions. The machine or OS on which Docker Engine runs is commonly referred to as the &lt;code&gt;Docker Host&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/assets/images/post/2023/docker-client-server-remote.webp&quot; alt=&quot;Remote Docker Engine &amp;#x26; CLI&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;build-application-image&quot;&gt;Build Application Image&lt;/h2&gt;
&lt;p&gt;To build a Docker Image, for your application, you use the following CLI command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker build -t myapp .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;In the example above for a Node application, make sure to place the Dockerfile under the code directory, &lt;code&gt;cd&lt;/code&gt; into the directory and run the command &lt;code&gt;docker build ...&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;The Docker CLI communicates with Docker Engine to build a docker image the application. The build command requires two inputs: (1) the image name (mostly referred to as the &lt;code&gt;Image Tag&lt;/code&gt;) and (2) the directory where the Dockerfile is located. In this case, &lt;code&gt;Image Tag&lt;/code&gt; is &lt;strong&gt;myapp&lt;/strong&gt; and the current directory is used as the location of the Dockerfile&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;When running docker commands locally, the docker CLI communicates with Docker Engine through a Unix socket. However, the Docker Engine can also listen on a TCP port. This means that the Docker Engine can be located on a different machine than the one where the docker CLI command is executed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;run-containers&quot;&gt;Run Containers&lt;/h2&gt;
&lt;p&gt;Once we build a Docker Image using the &lt;code&gt;docker build ...&lt;/code&gt; command, we can run a container using that image by executing the following CLI command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# docker run &amp;#x3C;options&gt; &amp;#x3C;docker-image&gt; &amp;#x3C;optional-Start-up-command&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker run --rm -it myapp yarn start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command directs Docker Engine to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;myapp&lt;/code&gt; image that we just built&lt;/li&gt;
&lt;li&gt;Create a new container and in that container run &lt;code&gt;yarn start&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If a startup command is already defined in the Dockerfile, it’s optional to specify it when running the container. In the above example, we included &lt;code&gt;yarn start&lt;/code&gt; as the startup command in the Dockerfile, so we don’t need to specify it again when running the container with the command &lt;code&gt;docker run --rm -it myapp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After starting the container and the service with yarn start, it may not be accessible yet because the environment is isolated to the container. For instance, if the service is listening on port 3000 inside the container, the host machine where Docker is running doesn’t have access to that port by default. However, we can map the container port to a port on the host machine using the -p flag in the Docker run command.&lt;/p&gt;
&lt;p&gt;For example, to map port 3000 in the container to port 3001 on the Docker host, we can use the following command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;docker run --rm -it -p 3001:3000 myapp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command maps port &lt;code&gt;3001 on the host&lt;/code&gt; machine to port &lt;code&gt;3000 in the container&lt;/code&gt;, allowing us to access the service at &lt;code&gt;http://localhost:3001&lt;/code&gt; on the host machine’s browser.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Containers can be run using pre-built images, whether they are for the application or other software. These images can come from a local machine or a remote registry, with DockerHub being one of the most popular. In fact, many of the base images we use, like the &lt;code&gt;node:19-alpine&lt;/code&gt; image in our example, are downloaded from DockerHub. Other popular remote registries include Gitlab Registry, Github Artifactory, AWS ECR, and more.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;docker-images-vs-docker-containers&quot;&gt;Docker Images vs Docker Containers&lt;/h2&gt;
&lt;p&gt;By now, you should have a grasp of the fundamental concepts of Dockerfile, Docker Engine, and Docker CLI, as well as how to build an image and run a container.&lt;/p&gt;
&lt;p&gt;To summarize, &lt;strong&gt;Docker Images&lt;/strong&gt; act as blueprints for containers, containing the code/files/builds, dependencies, runtime, system tools, libraries, and settings. &lt;strong&gt;Docker Containers&lt;/strong&gt; (or containers in general) are dynamic, running instances of those images, providing an isolated and self-contained environment that runs the application and its dependencies.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Here is a longer answer of what docker is and I think it will make sense now.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker can be used by developers to run and package applications (web apps, APIs, background workers, etc) built using any programming language/framework such as Ruby &amp;#x26; Ruby on Rails, Java &amp;#x26; SpringBoot, and Node &amp;#x26; Express, among others.&lt;/li&gt;
&lt;li&gt;Docker allows developers to choose an OS foundation for the container, such as Ubuntu, Alpine, etc.&lt;/li&gt;
&lt;li&gt;With Docker containers, applications can be built and run consistently across different systems, reducing compatibility issues.&lt;/li&gt;
&lt;li&gt;When an application is built with Docker, a container image is also created. The container image built will have everything it needs to run the application, simplifying the deployment process.&lt;/li&gt;
&lt;li&gt;The container image can then be provided to the Docker Engine to run the application locally, streamlining the development and deployment process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope that helps get an idea of how Docker helps in the development and to an extent of deployments. But to realize the actual benefits, one should put this into practice and start using Docker, at least in the local development setup.&lt;/p&gt;
&lt;p&gt;To learn more about Docker, try Dockerizing the application you’re working on or creating a new one using Docker. Here are some articles to help you get started with Docker in your local development environment.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/dockerize-an-existing-application-ruby-on-rails/&quot;&gt;Dockerize your current application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/create-ruby-on-rails-application-using-docker/&quot;&gt;Start a new application project using Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/multi-stage-docker-build-tutorial/&quot;&gt;Multi-Stage Docker Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/mysql-and-postgresql-with-docker-in-development/&quot;&gt;Using Docker for databases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2023/docker-beginners-guide.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2023/docker-beginners-guide.webp"/><category>Learn Docker</category><category>Learn Containerization</category><category>Docker Tutorial</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Building Production-Ready Docker Images With Multi-Stage Builds</title><link>https://devteds.com/multi-stage-docker-build-tutorial/</link><guid isPermaLink="true">https://devteds.com/multi-stage-docker-build-tutorial/</guid><description>Discover how to use Docker&apos;s Multi-Stage Build process to create production-ready Docker images with a step-by-step guide.</description><pubDate>Wed, 11 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’re creating a container image (Docker image) for production, image size and security are two essential considerations. Fortunately, Docker’s Multi-Stage builds can help address both concerns.&lt;/p&gt;
&lt;p&gt;In the development environment, container images often include development and build tools, as well as dependencies necessary for the development and build process. However, these tools and dependencies can increase the image size and pose a security risk.&lt;/p&gt;
&lt;p&gt;By using Docker’s Multi-Stage Build feature, we can separate the development and build stage from the final production stage of creating container images. This way, the image built for production or deployment to any environment, will only contain the necessary components for the app to run in production.&lt;/p&gt;
&lt;p&gt;In this short video tutorial, you’ll learn how to write a multi-stage Dockerfile to create both a local development and a &lt;strong&gt;production-ready&lt;/strong&gt;, deployable Docker image.&lt;/p&gt;
&lt;p&gt;Here’s a breakdown of the steps you’ll learn in the tutorial:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Containerize an existing web application using Ruby and Ruby on Rails&lt;/li&gt;
&lt;li&gt;Start with a simple Dockerfile to build for local development and test running containers locally&lt;/li&gt;
&lt;li&gt;Update the Dockerfile to add a multi-stage structure, starting with 2 stages for local development setup&lt;/li&gt;
&lt;li&gt;Test running the container locally, built using a multi-stage Dockerfile&lt;/li&gt;
&lt;li&gt;Add more stages to package and build a final, production-ready and deployable Docker image&lt;/li&gt;
&lt;li&gt;Test running the production-ready image locally using Docker Compose&lt;/li&gt;
&lt;/ol&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2023/2023-01-11-multi-stage-docker-build-tutorial-2.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2023/2023-01-11-multi-stage-docker-build-tutorial-2.webp"/><category>Learn Docker</category><category>Learn Containerization</category><category>Docker Tutorial</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Create Ruby On Rails Application Using Docker</title><link>https://devteds.com/create-ruby-on-rails-application-using-docker/</link><guid isPermaLink="true">https://devteds.com/create-ruby-on-rails-application-using-docker/</guid><description>A guide for creating a new Ruby on Rails application using Docker, without installing Ruby or Rails directly on your OS.</description><pubDate>Thu, 30 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Thinking of creating a new Ruby on Rails application and possibly run it as container on Kubernetes, Amazon ECS, etc? If so, do your requirements align with the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I want to create a new Ruby on Rails application and build it using the latest stable Ruby and Ruby on Rails&lt;/li&gt;
&lt;li&gt;I currently have an older version of Ruby installed for my other Rails applications and do NOT want to install a new version directly on my work machine for the new application&lt;/li&gt;
&lt;li&gt;The new app will use a specific version of MySQL, and I already have an older version of MySQL installed on my local machine for other applications. Installing a new version may lead to conflicts or may not work at all&lt;/li&gt;
&lt;li&gt;I require an isolated environment for my new Rails application and its database setup. While options such as RVM or rbenv exist for the Ruby portion, but I still need to solve for running multiple versions of MySQL servers&lt;/li&gt;
&lt;li&gt;I have some experience in Docker but not a pro. &lt;a href=&quot;/dockerize-an-existing-application-ruby-on-rails/&quot;&gt;Dockerizing an existing Rails application&lt;/a&gt; seems straightforward but creating a new Rails project without a specific Ruby or installed Rails, seems a bit complicated. I need a Get Started guide on creating a new Rails project using Docker&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your requirements align with the above, this post will guide you through creating a new Rails application using Docker without installing Ruby or Rails directly on your OS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisite:&lt;/strong&gt; Ensure that you have Docker Engine installed on your machine. I have Docker Desktop (Docker for Mac) installed on my machine; Docker version is 20.10.11&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Check out this new short course on &lt;a href=&quot;/devcontainers-for-developers-multi-container-local-dev-setup/&quot;&gt;Dockerizing Rails Development Environment using Docker &amp;#x26; DevContainers&lt;/a&gt; extension in VSCode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;create-project-directory&quot;&gt;Create project directory&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my-rails-app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my-rails-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a project directory on your host machine. At the end of this article, this directory will contain a Rails application code files &amp;#x26; folders.&lt;/p&gt;
&lt;h2 id=&quot;ruby-version&quot;&gt;Ruby Version&lt;/h2&gt;
&lt;p&gt;Before we create a Rails project using a specific Ruby version, we should have an environment with that version of Ruby installed. I am going to use &lt;code&gt;Ruby version 3.1.2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead of installing Ruby on my machine or on OS directly, I will run a docker container using a Docker image containing Ruby 3.1.2 and then shell into the container.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --rm&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -it&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; $PWD&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;:/app:rw&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ruby:3.1.2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ruby -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ruby&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 3.1.2p20&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; gem -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;3.3.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bundle -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Bundler&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; version&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2.3.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s breakdown the docker command in the above &lt;code&gt;docker run ...&lt;/code&gt; command,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We run this command from inside the project directory &lt;code&gt;my-rails-app&lt;/code&gt; and we mount the current directory to a path &lt;code&gt;/app&lt;/code&gt; inside the container, with read-write (&lt;code&gt;RW&lt;/code&gt;) permission. With this, when the container writes any files to &lt;code&gt;/app&lt;/code&gt;, those get written to the project directory &lt;code&gt;my-rails-app&lt;/code&gt; on host&lt;/li&gt;
&lt;li&gt;We use the Docker Image &lt;code&gt;ruby:3.1.2&lt;/code&gt; from DockerHub. When this container is run, we should be able to run any ruby commands inside that container&lt;/li&gt;
&lt;li&gt;Last part of the command is &lt;code&gt;/bin/bash&lt;/code&gt;. This instructs the Docker Engine to run the command &lt;code&gt;/bin/bash&lt;/code&gt; inside the container when the container starts up. This is how you would shell into a new container&lt;/li&gt;
&lt;li&gt;The option &lt;code&gt;-i&lt;/code&gt; enables an interactive session with container shell. The option &lt;code&gt;-t&lt;/code&gt; allocates puedo-TTY. The option &lt;code&gt;--rm&lt;/code&gt; ensures that the container is destroyed when we exit from the container shell&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once we’re inside the container shell, you may verify the ruby, bundler, gem, etc. And when you’re done, just type &lt;code&gt;exit&lt;/code&gt; to end the session and that should delete the container.&lt;/p&gt;
&lt;h2 id=&quot;install-rails-gem&quot;&gt;Install Rails Gem&lt;/h2&gt;
&lt;p&gt;We will be installing the Rails gem inside the container while we’re still on the container shell. Let’s run the &lt;code&gt;gem install ...&lt;/code&gt;  command inside the container shell. If you exited from that shell, run the above &lt;code&gt;docker run ...&lt;/code&gt; command again.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; gem install -v 7.0.3 --no-prerelease --no-document rails&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Fetching&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tzinfo-2.0.4.gem&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Successfully&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; installed&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; actioncable-7.0.3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Successfully&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; installed&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; rails-7.0.3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;35&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; gems&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; installed&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; rails -v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Rails&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 7.0.3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have Rails 7 installed inside the container. &lt;em&gt;However, if you exit from the container shell, you’ll lose this installation and you will have to run it again. That is because, on exiting the container shell the docker engine will delete the container and all its data&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;create-a-new-project---ruby-on-rails&quot;&gt;Create a new project - Ruby on Rails&lt;/h2&gt;
&lt;p&gt;Now that we are inside the container shell, let’s create a new Ruby on Rails app; provide the directory &lt;code&gt;/app&lt;/code&gt; as the project directory which maps to the current project directory, &lt;code&gt;my-rails-app&lt;/code&gt;, where we ran docker run  command to shell into container.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; rails new --skip-bundle --database&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;mysql&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Initialized&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; empty&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; repository&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /app/.git/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      create&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      create&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  app/assets/config/manifest.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;      ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ls /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Gemfile&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    Rakefile&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  bin&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;     config.ru&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  lib&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  public&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   test&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  vendor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;README.md&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  app&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       config&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  db&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;         log&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  storage&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  tmp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It created a new rails project and the code is at &lt;code&gt;/app&lt;/code&gt; inside the container. Because we have &lt;code&gt;/app&lt;/code&gt; mounted from &lt;code&gt;my-rails-app&lt;/code&gt; directory on the host machine, with read-write permissions, we would expect the project directory structure with all the files/folders is actually written to &lt;code&gt;my-rails-app&lt;/code&gt;. And the folders and files inside the &lt;code&gt;my-rails-app&lt;/code&gt; directory should NOT be deleted if I exit from the container shell.&lt;/p&gt;
&lt;p&gt;Let’s exit from the container shell and list the content of &lt;code&gt;my-rails-app&lt;/code&gt; directory on host.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; exit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;exit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Gemfile&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;         app&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;             db&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;              storage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Gemfile.lock&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    bin&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;             lib&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;             test&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;README.md&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       config&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          log&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;             tmp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Rakefile&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;        config.ru&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       public&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;          vendor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;$&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how you can create the Rails project structure using Docker, without having to install specific versions of Ruby or Rails on your work machine.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;To run this new Rails application as a container, there are a few things we need to do first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a Dockerfile that uses the same Docker image for ruby:3.1.2&lt;/li&gt;
&lt;li&gt;Optionally, create a docker-compose.yml file to provide the container with run-time configurations&lt;/li&gt;
&lt;li&gt;Run a MySQL database as a container instead of installing a MySQL server directly on the host machine&lt;/li&gt;
&lt;li&gt;Build the Docker image for the Rails application&lt;/li&gt;
&lt;li&gt;Finally, run the application and build some amazing things!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need more details on these steps, I’ve covered them in the following blog post titled &lt;a href=&quot;/dockerize-an-existing-application-ruby-on-rails/&quot;&gt;Dockerize An Existing Ruby on Rails Application&lt;/a&gt;. Now that Rails project has been created, one should be able to follow the same instructions to &lt;a href=&quot;/dockerize-an-existing-application-ruby-on-rails/&quot;&gt;Dockerize and Run as docker containers&lt;/a&gt;.&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2022/2022-06-30-create-ruby-on-rails-application-using-docker-1.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2022/2022-06-30-create-ruby-on-rails-application-using-docker-1.webp"/><category>Learn Docker</category><category>Learn Containerization</category><category>Docker Tutorial</category><category>Docker &amp; Ruby on Rails</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Dockerize An Existing Ruby On Rails Application</title><link>https://devteds.com/dockerize-an-existing-application-ruby-on-rails/</link><guid isPermaLink="true">https://devteds.com/dockerize-an-existing-application-ruby-on-rails/</guid><description>Learn how to dockerize a Ruby on Rails application with Docker Compose, including running MySQL as a container for your local development environment.</description><pubDate>Wed, 15 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Whether it is a new Ruby project or an existing one, I Dockerize it first. I run pretty much all of the applications as containers on my local as it helps keeping local development setups isolated between the projects. While in production environment, some of those get deployed to Kubernetes or ECS and some go to non-container environments. Regardless, my local development environment is always runs as containers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; Check out this new short course on &lt;a href=&quot;/devcontainers-for-developers-multi-container-local-dev-setup/&quot;&gt;Dockerizing Rails Development Environment using Docker &amp;#x26; DevContainers&lt;/a&gt; extension in VSCode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post will be useful to you if,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want to dockerize the local development environment for your Rails application&lt;/li&gt;
&lt;li&gt;Your organization or company is moving toward container deployments and you need to start to dockerize your applications&lt;/li&gt;
&lt;li&gt;You know a little about docker or at least the theory part of it and you would like dockerize your Rails application but not sure where to start&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re looking to create a new Rails application using Docker, I suggest you read the following blog post first: &lt;a href=&quot;/create-ruby-on-rails-application-using-docker/&quot;&gt;Create Ruby on Rails application using Docker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this article, we’ll take a look at Dockerizing an existing Ruby on Rails application. Let’s get started.&lt;/p&gt;
&lt;p&gt;The application we’re going to Dockerize is a simple blog application, written in Rails 7 and it uses MySQL database. Let’s get the code from &lt;a href=&quot;https://github.com/devteds/example-rails-app&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; git@github.com:devteds/example-rails-app.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example-rails-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;dockerfile&quot;&gt;Dockerfile&lt;/h2&gt;
&lt;p&gt;The first thing we need is the Dockerfile. This is the file where we define all the necessary OS needs, libraries, work directory, and tools necessary to build the docker image for the application. Dockerfile will be placed at the root of the project folder. In this case, at example-rails-app/Dockerfile.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;dockerfile&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# example-rails-app/Dockerfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ruby:3.1.2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mkdir /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;WORKDIR&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; apt-get update -qq &amp;#x26;&amp;#x26; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    apt-get -y install build-essential&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Gemfile /app/Gemfile&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Gemfile.lock /app/Gemfile.lock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;RUN&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; bundle install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;CMD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [ &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;bundle&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;exec&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;rails&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;-b&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A quick walkthrough of the Dockerfile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses the base image from DockerHub ruby:3.1.2. This base image is built on Debian and has ruby, bundle, and basic OS libraries necessary to run a ruby application&lt;/li&gt;
&lt;li&gt;Creates a directory for the app. This directory, /app, is where the application build/code will be copied to and where the rails commands will be run from when the container is run.&lt;/li&gt;
&lt;li&gt;Installs some build tools. A most commonly used one is build-essential which Rails developers often need to install some gems&lt;/li&gt;
&lt;li&gt;Copy the Gemfile and Gemfile.lock from the code on Host (Mac in my case) to the app directory inside the container image&lt;/li&gt;
&lt;li&gt;Run bundle install. Bundler expects Gemfile and Gemfile.lock in the app directory and that’s why, in the previous step, we copied those files to container’s app directory. - We don’t yet need the source code copied to the container image&lt;/li&gt;
&lt;li&gt;And last one is the command that will be run when a container is created using the image built from this Dockerfile. Here we run the rails server and have it listen on the default port 3000. One bit here is the address binding option which is set to 0.0.0.0 meaning that when rails server runs inside the container, it allows us access the service from the host (Mac in my case)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;build-docker-image&quot;&gt;Build Docker Image&lt;/h2&gt;
&lt;p&gt;Our Dockerfile is ready and we can now build a Docker image. There are a couple of different ways we can go about to docker image. First and most common one is by using docker client and the command will look something the below,&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; build&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; example-rails-app&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, I am going to use Docker Compose. I prefer Docker Compose for most of local setup. It helps define the build inputs, container services, and dependencies better, mainly useful for local development environments&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create docker-compose.yml . This file will be placed at the root of the application code directory&lt;/li&gt;
&lt;li&gt;Compose file is a YAML and starts with this version line. See the &lt;a href=&quot;https://docs.docker.com/compose/compose-file/compose-versioning/&quot;&gt;versioning details&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Besides the version, the more common one you’ll see in docker-compose.yml is the services section. This is where you define all the container services.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# example-rails-app/docker-compose.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;3.8&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-app&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The docker-compose.yml above has just one service, blog-app. The build is the directory relative to the current directory where you have the source code, Dockerfile and where you’ll run docker or docker-compose commands from.&lt;/p&gt;
&lt;p&gt;This should be good enough for us to build the docker image.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker-compose&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above command builds an image. The image name will be example-rails-app_blog-app in which example-rails-app is the directory where you have the source code and blog-app is the service name as defined in the docker-compose.yml file.&lt;/p&gt;
&lt;p&gt;The docker image we built has all the necessary OS libraries, ruby, directory structure, and bundled gems. What is missing in the image is the application code.&lt;/p&gt;
&lt;p&gt;We don’t need to copy the source code to the image for running the application locally; instead, we could mount volume or directory between the host OS (Mac in my case) and the running container.&lt;/p&gt;
&lt;p&gt;And there is one more thing missing, that is the database MySQL. Can we install MySQL as well in the same docker image? Beginners tend to do that but that’s not how you want to do it. Because database runs on a different host independent of your application in staging/production, let’s treat it the same way on local.&lt;/p&gt;
&lt;p&gt;We can run MySQL also as a container. We don’t want to create a Dockerfile for MySQL or  worry about the OS or libraries it requires. Instead, we will use the MySQL Docker image from &lt;a href=&quot;https://hub.docker.com/_/mysql&quot;&gt;DockerHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;mysql-as-container&quot;&gt;MySQL as Container&lt;/h2&gt;
&lt;p&gt;Let’s add a new service in the docker-compose.yml for the database: blog-db&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# example-rails-app/docker-compose.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;3.8&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-app&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-db&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;mysql:5.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      MYSQL_DATABASE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s take a quick look at the blog-db service:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This service uses the docker image from DockerHub &lt;a href=&quot;https://hub.docker.com/_/mysql&quot;&gt;mysql:5.7&lt;/a&gt;. Unlike the blog-app service, there is nothing to build for this service and it uses a pre-built docker image from DockerHub&lt;/li&gt;
&lt;li&gt;Sets a few environment variables that mysql container reads before booting up mysql-server inside the container. Those environment variables, MYSQL_*, are specific to the docker image we used.&lt;/li&gt;
&lt;li&gt;When the blog-db container starts up it creates a database, blog (value of MYSQL_DATABASE); it uses password (value of MYSQL_ROOT_PASSWORD) as root password.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s all for the blog-db service. If you would like to learn more about running MySQL as container, watch the following screencast blog: &lt;a href=&quot;/mysql-and-postgresql-with-docker-in-development/&quot;&gt;MySQL And PostgreSQL With Docker In Development&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;runtime-configs-for-application&quot;&gt;Runtime Configs for Application&lt;/h2&gt;
&lt;p&gt;Now that we have the database container service defined, let’s provide the database configuration to the application (blog-app) container or service. Similar to the blog-db service, let’s provide the environment variables for the application inside the container to read from. And then we will make this app read database configs from environment variables.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# example-rails-app/docker-compose.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;3.8&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-app&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_USER&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;root&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_NAME&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_HOST&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog-db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_PORT&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;3306&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-db&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;mysql:5.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      MYSQL_DATABASE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The application that will run inside the blog-app container is supplied with a few environment variables for it’s database configs&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses MySQL’s root user &amp;#x26; password, as configured for the MySQL container service blog-db&lt;/li&gt;
&lt;li&gt;DB_NAME is blog as configured in the MySQL container service blog-db. DB_PORT is the default, 3306 for mysql.&lt;/li&gt;
&lt;li&gt;The value of DB_HOST is the name of database service blog-db. This is one of the reasons why I like docker-compose that it makes is easy to do service linking and works well when there involves inter-service communication between containers services. Among the services defined in docker-compose.yml, one service can talk to another using the service name as host name.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that the application is supplied with these environment variables, let’s update database.yml to read the configs from environment variables. Note, this is for local environment and there may be security concerns if you use environment variables like this in prod.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For developers that aren’t familiar with Rails, the database.yml, is the config file that a Rails application reads database configs from. It doesn’t have to hardcode all the values.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It’s nice that database.yml in Rails works nicely with embedded ruby (ERB). That makes it all easy to configure this.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# config/database.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  adapter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;mysql2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  encoding&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;utf8mb4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  pool&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#x3C;%= ENV.fetch(&quot;RAILS_MAX_THREADS&quot;) { 5 } %&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  database&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#x3C;%= ENV[&apos;DB_NAME&apos;] %&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  username&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#x3C;%= ENV[&apos;DB_USER&apos;] %&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  password&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#x3C;%= ENV[&apos;DB_PASSWORD&apos;] %&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  host&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#x3C;%= ENV[&apos;DB_HOST&apos;] %&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  port&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&amp;#x3C;%= ENV[&apos;DB_PORT&apos;] %&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;development&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;test&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;production&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;default&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3 more things we need updated in the docker-compose.yml for the blog-app service,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Application Code&lt;/strong&gt;: The docker image that we build will not have the source code and that because the Dockerfile does not have any instruction to copy the source code to the container image. More importantly, we don’t want the docker image to have the source code for running it locally. Instead we can mount our local directory (from the host machine, Mac in my case) to the work-directory inside the container when running the container or when running the service&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Port&lt;/strong&gt;: When we run the application using docker or docker-compose, it runs inside a container. Rails service listens at the port 3000 by default and but that port will NOT be available to host (Mac in my case), to browse from the browser. That means I cannot hit &lt;a href=&quot;http://localhost:3000&quot;&gt;http://localhost:3000&lt;/a&gt; from my browser. In order to allow that, we need a port map and provide the port that you want to access the service on your browser and the port at which the service inside the container listens to. Here is I set the host port as 3300 which maps to port 3000 of the container. And then I will be able to access the app service at &lt;a href=&quot;http://localhost:3300&quot;&gt;http://localhost:3300&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Dependency&lt;/strong&gt;: we need the database service be ready before rails container starts up. Put that dependency using depends_on. Note: when mysql container status is Ready/Running, it is possible that the mysql server is not fully started up. That means, depends_on, may not work in all cases&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# example-rails-app/docker-compose.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;version&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;3.8&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-app&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    build&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    volumes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.:/app:rw&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_USER&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;root&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_NAME&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      DB_HOST&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog-db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    depends_on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog-db&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    ports&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;3300:3000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;  blog-db&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    image&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;mysql:5.7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;    environment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;password&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;      MYSQL_DATABASE&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;blog&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All set! Our docker-compose.yml has both the container services (blog-app and blog-db) defined.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker-compose&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; up&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leave the above command running in the foreground and run the following on another terminal window or tab to verify the container service status. This should list both the services with some details such as the service status and port-mapping information.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker-compose&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that both the services, the application blog-app and the database blog-db, are running, I should be able to hit the application from browser at &lt;code&gt;http://localhost:3300&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But it will fails with an error on querying some tables in blog-db. That’s because we haven’t created any tables. Let’s run a script create necessary tables. In Rails, we manage all the database schema changes in the application code and run rails migration commands.&lt;/p&gt;
&lt;p&gt;The application code has necessary schema migration code. Let’s run the command rails db:migrate to run those and create necessary tables. We can either run this command inside the container service that is already running that serves web requests or spin off a temporary container for blog-app service to run this command. I always prefer the latter and let’s do that.&lt;/p&gt;
&lt;p&gt;Leave the docker-compose up running, and run the database migration and reload the page,&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;docker-compose&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --rm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; blog-app&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; rails&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; db:migrate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above command will create a new container for blog-app, using its definition from the docker-compose.yml and in that container it runs the rails db:migrate command and exit. The —rm makes the container be killed after the completion of the command it run.&lt;/p&gt;
&lt;p&gt;You should now be able to hit the application from browser and it should respond 200OK.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://localhost:3300/posts&quot;&gt;http://localhost:3300/posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://localhost:3300/admin&quot;&gt;http://localhost:3300/admin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use the admin interface to populate some posts and verify the API response for posts.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That’s all about containerizing a Rails application along with running MySQL as container. Again, this is good for running your application locally using docker.&lt;/p&gt;
&lt;p&gt;We have some more work to do in Dockerfile to make the docker image production-ready or even to be able to deploy on non-production environments on platforms such as Kubernetes, Amazon ECS, etc. I will create a separate post to cover some of the basics involved in creating a docker image for production.&lt;/p&gt;
&lt;p&gt;I hope you found this post helpful. I appreciate you sharing with your network that may find it useful.&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2022/2022-06-15-dockerize-an-existing-application-ruby-on-rails-1.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2022/2022-06-15-dockerize-an-existing-application-ruby-on-rails-1.webp"/><category>Learn Docker</category><category>Learn Containerization</category><category>Docker Tutorial</category><category>Docker &amp; Ruby on Rails</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Docker On Amazon ECS Using CloudFormation</title><link>https://devteds.com/docker-on-amazon-ecs-using-cloudformation/</link><guid isPermaLink="true">https://devteds.com/docker-on-amazon-ecs-using-cloudformation/</guid><description>Learn how to deploy Docker containers on Amazon ECS with AWS Fargate using AWS CloudFormation.</description><pubDate>Fri, 03 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This tutorial will guide you through the process of deploying and running Docker containers on Amazon Elastic Container Service (ECS) with AWS Fargate using AWS CloudFormation stacks. To accomplish this, we’ll be creating Elastic Container Registry (ECR) and multiple CloudFormation stacks for groups of resources on the cloud, including VPC, subnets, load balancers, CloudWatch, ECS Cluster, container tasks, and services.&lt;/p&gt;
&lt;p&gt;Here are the steps we’ll follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Containerize a simple REST API application by building a Docker image.&lt;/li&gt;
&lt;li&gt;Use AWS CLI to create a repository on Amazon Elastic Container Registry (Docker Registry).&lt;/li&gt;
&lt;li&gt;Push the Docker image to the repository on Amazon ECR.&lt;/li&gt;
&lt;li&gt;Define and run a CloudFormation stack to create VPC and all network components.&lt;/li&gt;
&lt;li&gt;Define and run a CloudFormation stack to create IAM roles.&lt;/li&gt;
&lt;li&gt;Define and run a CloudFormation stack to create ECS Cluster, Load balancer, CloudWatch Log Group, and Security Groups.&lt;/li&gt;
&lt;li&gt;Define and run a CloudFormation stack to deploy the Docker application, including creating a task for the container, a service to schedule the task, a load balancer target group for container services, and a load balancer listener rule for container service.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Upon completing this tutorial, you will be able to create ECS Fargate infrastructure using CloudFormation code, and deploy and execute Docker containers on ECS.&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2018/docker-cloudformation-ecs.jpeg"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2018/docker-cloudformation-ecs.jpeg"/><category>Docker Tutorial</category><category>Docker on AWS</category><category>Infrastructure as Code</category><category>DevOps</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>MySQL and PostgreSQL with Docker in Development</title><link>https://devteds.com/mysql-and-postgresql-with-docker-in-development/</link><guid isPermaLink="true">https://devteds.com/mysql-and-postgresql-with-docker-in-development/</guid><description>Learn how to set up multiple versions of MySQL and Postgres databases using Docker Compose for your development environment.</description><pubDate>Thu, 12 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this tutorial, we will go over the process of setting up and running MySQL and PostgreSQL databases using Docker Compose in a development environment. Docker Compose allows us to define container services for multiple versions of these databases in a YAML file, making it easy to create, configure, and manage them.&lt;/p&gt;
&lt;p&gt;Here are the steps we will cover:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define a Docker Compose YAML file for a simple MySQL container.&lt;/li&gt;
&lt;li&gt;Test connecting to the containerized MySQL server using MySQL Workbench on the host OS.&lt;/li&gt;
&lt;li&gt;Test connecting to the containerized MySQL server database using another container.&lt;/li&gt;
&lt;li&gt;Test connecting to the containerized MySQL server database from within the container.&lt;/li&gt;
&lt;li&gt;Run multiple MySQL databases of different versions as containers.&lt;/li&gt;
&lt;li&gt;Configure volume mounts between the host OS and container to share the MySQL server configuration file.&lt;/li&gt;
&lt;li&gt;Configure volume mounts between the host OS and container to share the data directory.&lt;/li&gt;
&lt;li&gt;Manage containerized databases using a database management web tool, such as adminer.&lt;/li&gt;
&lt;li&gt;Run a containerized PostgreSQL database along with multiple MySQL databases.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end of this tutorial, you will have a solid understanding of how to use Docker Compose to set up and manage multiple MySQL and PostgreSQL databases in a development environment. This knowledge will enable you to create complex, multi-database applications with ease.&lt;/p&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2017/mysql-postgresql-on-docker-video.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2017/mysql-postgresql-on-docker-video.webp"/><category>Learn Docker</category><category>Docker &amp; Databases</category><category>Docker Tutorial</category><author>contact@devteds.com (Chandra Shettigar)</author></item><item><title>Create AWS CloudFormation Stack for EC2 &amp; RDS and Deploy Docker App</title><link>https://devteds.com/create-aws-cloudformation-stack-for-ec2-rds-and-deploy-docker-app/</link><guid isPermaLink="true">https://devteds.com/create-aws-cloudformation-stack-for-ec2-rds-and-deploy-docker-app/</guid><description>Learn how to deploy a dockerized API application on AWS with CloudFormation YAML template and Docker Compose.</description><pubDate>Sun, 23 Apr 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Learn how to create application stack using AWS CloudFormation and then deploy dockerized API application.&lt;/p&gt;
&lt;p&gt;Stack is defined using YAML template that contains 4 resources to be created on AWS,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;EC2 Instance to run the application on&lt;/li&gt;
&lt;li&gt;EC2 Security Group that defines the possible inbound ports on the EC2 instance&lt;/li&gt;
&lt;li&gt;RDS Instance for MySQL database&lt;/li&gt;
&lt;li&gt;RDS Security Group that defines the possible sources the database can be reached from&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Following are the steps to walk through,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a KeyPair on AWS account to be used for SSH access to EC2&lt;/li&gt;
&lt;li&gt;Generate API keys and configure the AWS CLI on the local machine&lt;/li&gt;
&lt;li&gt;Create a simple stack using CloudFormation and this stack will consist of an EC2 instance and necessary security group for the EC2 instance&lt;/li&gt;
&lt;li&gt;Update the stack to install and configure docker on the EC2 instance&lt;/li&gt;
&lt;li&gt;Update the stack to add RDS instance and necessary security group to the stack&lt;/li&gt;
&lt;li&gt;Create Docker compose file to define the application to be deployed on EC2 instance with its database on RDS instance&lt;/li&gt;
&lt;li&gt;Deploy the application on the EC2 instance (docker host) remotely from local&lt;/li&gt;
&lt;/ol&gt;</content:encoded><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devteds.com/assets/images/post/2017/aws-cloudformation-docker.webp"/><media:content xmlns:media="http://search.yahoo.com/mrss/" medium="image" url="https://devteds.com/assets/images/post/2017/aws-cloudformation-docker.webp"/><category>Docker Tutorial</category><category>Docker on AWS</category><category>Infrastructure as Code</category><category>DevOps</category><author>contact@devteds.com (Chandra Shettigar)</author></item></channel></rss>