{"id":1292,"date":"2019-05-20T15:15:45","date_gmt":"2019-05-20T15:15:45","guid":{"rendered":"http:\/\/bitcows.com\/?p=1292"},"modified":"2024-05-10T18:02:01","modified_gmt":"2024-05-10T18:02:01","slug":"react-production-builds-using-docker-compose","status":"publish","type":"post","link":"https:\/\/bitcows.com\/?p=1292","title":{"rendered":"React Production Builds using Docker Compose"},"content":{"rendered":"<p>If you have ever needed to create a docker container that runs a React app but you need to use a docker-compose file to add environmental variables to it but need it to be flexible enough to use any compose file on any given server (stage, prod, test) then this post might be for you.<\/p>\n<h3>The Scenario<\/h3>\n<p>You have a React app, and you are trying to create a single docker container to deploy that once started will consume a given docker-compose files environmental variables, but you don&#8217;t want to store a build in the container with predefined environment variables.<\/p>\n<h3>Prerequisites<\/h3>\n<p>To understand this you have to have a basic knowledge of React and Docker. This article assumes that you have a React project and know how to create a React project. The purpose is more of an in-depth understanding of how to use a Docker Compose file to inject environmental variables into your production build at the start of your container. If you don&#8217;t understand the basics of React please <a href=\"https:\/\/reactjs.org\/docs\/create-a-new-react-app.html\" rel=\"noopener\" target=\"_blank\">check out this first.<\/a><\/p>\n<h3>What you will need<\/h3>\n<p>0. React project and a base understanding of what a <a href=\"https:\/\/facebook.github.io\/create-react-app\/docs\/production-build\" target=\"_blank\" rel=\"noopener\">build<\/a> is for it.<br \/>\n1. Dockerfile<br \/>\n2. package.json<br \/>\n3. docker-compose.yml<br \/>\n4. strong stomach for debugging<\/p>\n<p><strong>Dockerfile<\/strong><br \/>\nFirst start by creating your Dockerfile. Here we create a working directory, and for security best practice we add a non-root user. Then we add the node_modules to the path, install all our node_modules, the react-script install and finally the serve. Lastly we call start to kick off the process when the container starts up. The CMD at the end calls the package.json start script to kick off your build and start the server.<\/p>\n<pre class=\"prettyprint\">FROM node:10.15.0\n\n# set working directory\nRUN mkdir \/usr\/src\/reactapp\nWORKDIR \/usr\/src\/reactapp\n\n# add `\/usr\/src\/app\/node_modules\/.bin` to $PATH\nENV PATH \/usr\/src\/reactapp\/node_modules\/.bin:$PATH\n\n# TODO: for security purposes, you should update this Dockerfile to specify your own target user\/group\n# -R changes the ownership rights of a file recursively\nRUN addgroup --system company-group &amp;&amp; adduser --system company-user --ingroup company-group &amp;&amp; \\\n\tchown -R company-usercompany-group \/usr\/src\/reactapp\n\n# Denotes to copy all files in the service to '\/usr\/src\/reactapp' folder in the container\n# install and cache app dependencies\nCOPY --chown=company-user:company-group . \/usr\/src\/reactapp\n\nRUN npm install --silent \n\nUSER root\nRUN npm install react-scripts@3.0.1 -g --silent \nRUN npm install -g serve --save\n\n# start app\nCMD [\"npm\", \"start\"]\n<\/pre>\n<p><strong>NOTE:<\/strong> Silencing the NPM output, via &#8211;silent, is a personal choice, it can swallow errors. If you find you have errors change &#8211;silent to &#8211;verbose<\/p>\n<p><strong>package.json<\/strong><br \/>\nNext we see a portion of the package.json file. This is the only part you need to focus on, as I assume your packages would be specific for the project you are working on.<\/p>\n<p>The most important part to see on this is the <strong>start<\/strong> script. This does the build of your project, then it starts the build with the serve command using port 8080 (the port is what ever you want it to be. In my case i was using 8080)<\/p>\n<p>The debugging script shown here pulls all my environmental variables from a .env file so that i can dynamically run my project with out at docker images (but thats out of scope for this)<\/p>\n<pre class=\"prettyprint\">. . .\n  \"scripts\": {\n    \"start\": \"GENERATE_SOURCEMAP=false react-scripts build --production &amp;&amp; serve -s build -l 8080\",\n    \"debugging\": \"env-cmd .env.development react-scripts start\",\n    \"build\": \"GENERATE_SOURCEMAP=false react-scripts build --production\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  },\n. . .\n<\/pre>\n<p>Once this is set up you can do your build. <code>docker build -t my.docker.image:latest<\/code><\/p>\n<p><strong>docker-compose.yml<\/strong><br \/>\nThis is a basic version of a docker compose file. Here we are are using the image we created with our docker build, once it starts it uses our environment variables as seen here.<\/p>\n<p>You will notice that some of these start with <strong>REACT_<\/strong> this is important as the React project will not pick up any custom variables that don&#8217;t start with <strong>REACT_<\/strong>.<\/p>\n<p>In this example all I want to pass in is a logo and company name for my site to display.<\/p>\n<pre class=\"prettyprint\">\nversion: \"3.2\"\nservices:\n  web:\n    image: my.docker.image:latest\n    ports:\n    - 8075:8080\n    expose:\n    - \"8080\"\n    environment:\n        NODE_ENV: production\n        REACT_APP_LOGO: cust_images\/acme.png\n        REACT_APP_COMPANYNM: Acme\n        PORT: 8080 \n<\/pre>\n<h3>Code Example<\/h3>\n<p>Here we see a very basic use case on how to get your environment variables to use. Plucking the variables out is pretty easy <code>{process.env.REACT_APP_LOGO}<\/code>.<\/p>\n<pre class=\"prettyprint\">\nimport React, { Component } from 'react';\nimport { BrowserRouter } from 'react-router-dom';\n\nclass Thanks extends Component {\n\n    render() {\n        return ( \n            &lt;BrowserRouter&gt; \n            &lt;&gt;\n            &lt;header&gt;\n                &lt;img className=\"venderLogo-thanks\" src={process.env.REACT_APP_LOGO} alt=\"Vender Logo\"\/&gt;\n            &lt;\/header&gt;\n            &lt;div id=\"formWrapper\" className=\"thanks-wrapper\"&gt;\n                &lt;h1&gt;Submission Success&lt;\/h1&gt;\n                &lt;p&gt;Thank you for submitting your connection information.&lt;\/p&gt;\n                {process.env.REACT_APP_COMPANYNM}\n            &lt;\/div&gt;\n            &lt;\/&gt;\n            &lt;\/BrowserRouter&gt; \n            );\n    }\n}\n  \nexport default Thanks;\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>If you have ever needed to create a docker container that runs a React app but you need to use a docker-compose file to add environmental variables to it but need it to be flexible enough to use any compose file on any given server (stage, prod, test) then this post might be for you.&hellip;<\/p>\n<p class=\"more-link\"><a href=\"https:\/\/bitcows.com\/?p=1292\" class=\"themebutton\">Read More<\/a><\/p>\n","protected":false},"author":1,"featured_media":2286,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,11,20,25],"tags":[41,42,60],"class_list":["post-1292","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coding","category-docker","category-javascript","category-react","tag-docker","tag-docker-compose","tag-react"],"_links":{"self":[{"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/posts\/1292","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bitcows.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1292"}],"version-history":[{"count":1,"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/posts\/1292\/revisions"}],"predecessor-version":[{"id":2291,"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/posts\/1292\/revisions\/2291"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bitcows.com\/index.php?rest_route=\/wp\/v2\/media\/2286"}],"wp:attachment":[{"href":"https:\/\/bitcows.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bitcows.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bitcows.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}