{"id":1359,"date":"2018-10-17T01:18:44","date_gmt":"2018-10-17T00:18:44","guid":{"rendered":"https:\/\/rosetta.vn\/short\/?p=1359"},"modified":"2018-10-25T15:44:31","modified_gmt":"2018-10-25T14:44:31","slug":"deploying-frontend-applications%e2%80%8a-%e2%80%8athe-fun-way-hacker-noon","status":"publish","type":"post","link":"https:\/\/rosetta.vn\/short\/2018\/10\/17\/deploying-frontend-applications%e2%80%8a-%e2%80%8athe-fun-way-hacker-noon\/","title":{"rendered":"Deploying frontend applications\u200a\u2014\u200athe fun way \u2013 Hacker Noon"},"content":{"rendered":"<blockquote>\n<section class=\"section section--body section--first\">\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<p id=\"da7f\" class=\"graf graf--p graf-after--figure\">In this post I will tell you how I deploy my frontend applications using GitHub, Jenkins, Docker and Digital Ocean.\u00a0<a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/stackoverflow.com\/questions\/49472695\/how-to-run-container-in-a-remote-docker-host-with-jenkins\" target=\"_blank\" rel=\"noopener\">I had some doubts<\/a>\u00a0through my learning journey so I decided to put up what I learned on this post as a self note.<\/p>\n<p id=\"4e8e\" class=\"graf graf--p graf-after--p graf--trailing\"><strong class=\"markup--strong markup--p-strong\">Note:<\/strong>\u00a0This post is not intended for you my friend, expert and consultant guru 15+ years exp DevOps architect; this is for those of us who are just starting to discover the beauty of CI\/CD tech. This post may contain some bad practices, please take it with a grain of salt.<\/p>\n<\/div>\n<\/div>\n<\/section>\n<section class=\"section section--body\">\n<div class=\"section-divider\">\n<hr class=\"section-divider\" \/>\n<\/div>\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<h3 id=\"c2b6\" class=\"graf graf--h3 graf--leading\">Cut the crap, shoot me the\u00a0TL;DR<\/h3>\n<p id=\"a278\" class=\"graf graf--p graf-after--h3\">You\u2019re like me, you skim around and grab the bullets, you leave. Here\u2019s the TL;DR for you:<\/p>\n<blockquote id=\"5fe2\" class=\"graf graf--blockquote graf-after--p\"><p>1. Spin up 2 servers with Docker installed: build and production servers<\/p><\/blockquote>\n<blockquote id=\"fd91\" class=\"graf graf--blockquote graf-after--blockquote\"><p>2. Install Jenkins (or any other CI) in your build server<\/p><\/blockquote>\n<blockquote id=\"d526\" class=\"graf graf--blockquote graf-after--blockquote\"><p>3. Create a job in your CI that clones, installs and builds your project\u00a0<strong class=\"markup--strong markup--blockquote-strong\">in a docker image<\/strong><\/p><\/blockquote>\n<blockquote id=\"c436\" class=\"graf graf--blockquote graf-after--blockquote\"><p>4. Set up webhooks in GitHub to fire the job on every push<\/p><\/blockquote>\n<blockquote id=\"a2f6\" class=\"graf graf--blockquote graf-after--blockquote\"><p>5. Push the docker image to your personal Docker Hub<\/p><\/blockquote>\n<blockquote id=\"d738\" class=\"graf graf--blockquote graf-after--blockquote\"><p>6. SSH into your production server<\/p><\/blockquote>\n<blockquote id=\"4bbb\" class=\"graf graf--blockquote graf-after--blockquote\"><p>7. Pull the image from Docker Hub and run the container<\/p><\/blockquote>\n<blockquote id=\"0aad\" class=\"graf graf--blockquote graf-after--blockquote\"><p>8. Profit<\/p><\/blockquote>\n<figure id=\"11c1\" class=\"graf graf--figure graf-after--blockquote\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1600\/1*T6i6rIASG-lujJuC_Ivu2w.jpeg?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Docker based continuous deployment workflow<\/figcaption><\/figure>\n<p id=\"cb6a\" class=\"graf graf--p graf-after--figure graf--trailing\">Thanks for reading.<\/p>\n<\/div>\n<\/div>\n<\/section>\n<section class=\"section section--body\">\n<div class=\"section-divider\">\n<hr class=\"section-divider\" \/>\n<\/div>\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<p id=\"37d9\" class=\"graf graf--p graf--leading\">Hopefully I caught your attention and you want more details now. In the following paragraphs I will outline my thought process and explain how my deployment pipeline works. I won\u2019t go deep into technical details but will instead put up some useful links for your further research.<\/p>\n<h3 id=\"4e3f\" class=\"graf graf--h3 graf-after--p\">The problem<\/h3>\n<p id=\"9494\" class=\"graf graf--p graf-after--h3\">Simple: I developed a\u00a0<a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/github.com\/caroso1222\/ast-viewer\" target=\"_blank\" rel=\"noopener\">TypeScript AST interactive viewer<\/a>\u00a0in Angular and I wanted to deploy it.<\/p>\n<\/div>\n<div class=\"section-inner sectionLayout--fullWidth\">\n<figure id=\"31d2\" class=\"graf graf--figure graf--layoutFillWidth graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/2000\/1*pVi41c1uXwh4PYd3S0YC3g.gif?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\"><a class=\"markup--anchor markup--figure-anchor\" href=\"https:\/\/ast.carlosroso.com\/\" target=\"_blank\" rel=\"noopener\">TypeScript AST Interactive Viewer<\/a><\/figcaption><\/figure>\n<\/div>\n<div class=\"section-inner sectionLayout--insetColumn\">\n<p id=\"5346\" class=\"graf graf--p graf-after--figure\"><em class=\"markup--em markup--p-em\">Bu\u2026 but, why not use Firebase, Google Engine, vanilla FTP, GH pages? you know, like \u201cone-click deploys\u201d duh\u200a\u2014\u200a<\/em>you may ask. All those tools work great, but most of them are either too expensive or just felt too magical (nothing tops \u201cgcloud app deploy\u201d) and\u00a0<strong class=\"markup--strong markup--p-strong\">are not that fun to learn<\/strong>! I wanted to understand, automate and have full control of the whole deployment process. I wanted a challenge, I wanted to learn, I wanted to have fun (hell I\u2019m inspired).<\/p>\n<h4 id=\"2858\" class=\"graf graf--h4 graf-after--p\">What it all comes down\u00a0to<\/h4>\n<p id=\"1d55\" class=\"graf graf--p graf-after--h4\">I set up my own challenge which looked like this:<\/p>\n<ol class=\"postList\">\n<li id=\"afe7\" class=\"graf graf--li graf-after--p\">I want to push code and have it deployed automatically\u200a\u2014\u200aaka Continuous Delivery.<\/li>\n<li id=\"62c5\" class=\"graf graf--li graf-after--li\">I want to pay a cheap hosting service<\/li>\n<\/ol>\n<p id=\"1edc\" class=\"graf graf--p graf-after--li\">Let\u2019s draw these non-functional requirements in a black box diagram\u00a0:<\/p>\n<figure id=\"2784\" class=\"graf graf--figure graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1600\/1*imyfQi6O0bSnkBWmBMi6Iw.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Problem illustration. From GitHub to Live App. Icon: magic by \u2726 Shmidt Sergey \u2726 from the Noun\u00a0Project<\/figcaption><\/figure>\n<h3 id=\"0c4b\" class=\"graf graf--h3 graf-after--figure\">Unraveling the Magic\u00a0box<\/h3>\n<p id=\"fa63\" class=\"graf graf--p graf-after--h3\">I\u2019ll divide this chapter into three parts. Each part will reveal one small portion of my Magic box:<\/p>\n<ol class=\"postList\">\n<li id=\"426f\" class=\"graf graf--li graf-after--p\">From GitHub to Magic<\/li>\n<li id=\"1533\" class=\"graf graf--li graf-after--li\">Developing the Magic<\/li>\n<li id=\"50e9\" class=\"graf graf--li graf-after--li graf--trailing\">From Magic to Live App<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/section>\n<section class=\"section section--body\">\n<div class=\"section-divider\">\n<hr class=\"section-divider\" \/>\n<\/div>\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<h4 id=\"62aa\" class=\"graf graf--h4 graf--leading\">1. From GitHub to\u00a0Magic<\/h4>\n<p id=\"5aef\" class=\"graf graf--p graf-after--h4\">Push code, sit and relax, refresh live app, profit. That\u2019s all I wanted.<\/p>\n<p id=\"89bc\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Choosing my VPS (Virtual Private Server)<\/em><\/strong><\/p>\n<p id=\"9ce7\" class=\"graf graf--p graf-after--p\">The first step was then to set up a server which could grab my code from GitHub and execute a series of commands (<em class=\"markup--em markup--p-em\">npm i &amp;&amp; npm run build<\/em>). Not quite sure what to do next, but I knew I had to start from there.<\/p>\n<p id=\"2006\" class=\"graf graf--p graf-after--p\">After reading about the best VPS providers out there, I finally went with\u00a0<a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/www.digitalocean.com\/\" target=\"_blank\" rel=\"noopener\">Digital Ocean<\/a>.\u00a0<strong class=\"markup--strong markup--p-strong\">Great tutorials<\/strong>, easy for new comers, decent DNS management and good pricing\u200a\u2014\u200awas a no-brainer.<\/p>\n<p id=\"5d0e\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">CI tools everywhere<\/em><\/strong><\/p>\n<p id=\"8d1c\" class=\"graf graf--p graf-after--p\">After signing up, I knew I needed some sort of tool that let me grab the code and automate the build process\u200a\u2014\u200athe CI tool\u00a0<em class=\"markup--em markup--p-em\">(Continuous Integration)<\/em>. There are several of them out there, some of them targeted for open source projects, some primarily designed to be self hosted, some premium, some free. I chose Jenkins mainly for its concept of Pipelines and because I had some familiarity with the tool.<\/p>\n<p id=\"0fd2\" class=\"graf graf--p graf-after--p\">Reading through some tutorials, it was relatively easy to get my Jenkins instance up and running. Now I needed to tell Jenkins to grab my code whenever I git pushed. Turns out there are dozens of posts out there on how to set up a GitHub webhook pointing from your repo to your Jenkins Droplet IP. Setting this up was faster than I expected (not quite straightforward for private GitHub repos, though).<\/p>\n<p id=\"81b6\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Let\u2019s recap<\/em><\/strong><\/p>\n<p id=\"8bbb\" class=\"graf graf--p graf-after--p\">At this point I have a DO droplet running Linux with a Jenkins instance pulling code from GitHub on every git push. How cool, huh? Let\u2019s see how our Magic box starts to reveal itself:<\/p>\n<figure id=\"e29a\" class=\"graf graf--figure graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1600\/1*3LI8drY9sq0GHcbKZe8JCg.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Black box diagram with Digital Ocean Droplet and\u00a0Jenkins<\/figcaption><\/figure>\n<p id=\"6137\" class=\"graf graf--p graf-after--figure\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Useful links:<\/em><\/strong><\/p>\n<ol class=\"postList\">\n<li id=\"869a\" class=\"graf graf--li graf-after--p\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/www.digitalocean.com\/docs\/droplets\/how-to\/create\/\" target=\"_blank\" rel=\"noopener\">How to create a Droplet in Digital Ocean<\/a>.<\/li>\n<li id=\"68bc\" class=\"graf graf--li graf-after--li\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-install-jenkins-on-ubuntu-16-04\" target=\"_blank\" rel=\"noopener\">How to install Jenkins in a DO Droplet<\/a>.<\/li>\n<li id=\"1984\" class=\"graf graf--li graf-after--li\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/jenkins.io\/doc\/book\/pipeline\/\" target=\"_blank\" rel=\"noopener\">How to set up and Jenkins pipeline<\/a>.<\/li>\n<li id=\"7cca\" class=\"graf graf--li graf-after--li graf--trailing\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-set-up-continuous-integration-pipelines-in-jenkins-on-ubuntu-16-04\" target=\"_blank\" rel=\"noopener\">How to integrate GitHub with Jenkins<\/a>.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/section>\n<section class=\"section section--body\">\n<div class=\"section-divider\">\n<hr class=\"section-divider\" \/>\n<\/div>\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<h4 id=\"cb74\" class=\"graf graf--h4 graf--leading\">2. Developing the\u00a0magic<\/h4>\n<p id=\"7571\" class=\"graf graf--p graf-after--h4\">In the next few paragraphs, I\u2019ll develop the core of my strategy which leverage Docker and DO droplets. Let\u2019s dive right in.<\/p>\n<p id=\"1065\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Dockerize all the things<\/em><\/strong><\/p>\n<p id=\"b488\" class=\"graf graf--p graf-after--p\">At this point, I have a\u00a0<em class=\"markup--em markup--p-em\">dist<\/em>\u00a0folder with an\u00a0<em class=\"markup--em markup--p-em\">index.html<\/em>\u00a0and its dependencies, ready to go. I know I need to spin up a webserver to serve these files over the web. Let\u2019s then install\u00a0<em class=\"markup--em markup--p-em\">nginx<\/em>\u00a0(or\u00a0<em class=\"markup--em markup--p-em\">Apache<\/em>) in the same server and serve the thing. I also need to install Node and a couple global node packages. Easy peasy, let\u2019s do that\u2026<\/p>\n<blockquote id=\"3b01\" class=\"graf graf--blockquote graf--startsWithDoubleQuote graf-after--p\"><p>\u201cBut wait, Carlos, that hmmm\u2026 that doesn\u2019t feel like the right thing to do\u200a\u2014\u200ayou\u2019d end up with a ton of stuff installed on your server and managing your dependencies will be a hell later on\u2026 just saying, though\u201d<em class=\"markup--em markup--blockquote-em\">\u200a\u2014\u200aInner self<\/em><\/p><\/blockquote>\n<p id=\"66be\" class=\"graf graf--p graf-after--blockquote\">Indeed, I needed a way to encapsulate the dependencies, the\u00a0<em class=\"markup--em markup--p-em\">dist<\/em>\u00a0artifact and even the webserver so that it wouldn\u2019t mess up my server. Let\u2019s use\u00a0<strong class=\"markup--strong markup--p-strong\">Docker<\/strong>. It would let me build an image with my webserver running on a port on my server, and my server filesystem would barely notice what I just did. How cool, huh?<\/p>\n<p id=\"33b7\" class=\"graf graf--p graf-after--p\">I set up a\u00a0<a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/github.com\/caroso1222\/ast-viewer\/blob\/master\/Dockerfile\" target=\"_blank\" rel=\"noopener\">Dockerfile<\/a>\u00a0for my app and edited the job in Jenkins to build a docker image and run the container with the 80 port exposed. It worked like charm. I could access my app live from\u00a0<em class=\"markup--em markup--p-em\">http:\/\/my.server.ip<\/em>.<\/p>\n<p id=\"2667\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">On scaling and stuff<\/em><\/strong><\/p>\n<p id=\"cdf1\" class=\"graf graf--p graf-after--p\">I planned to have many applications deployed with this same schema using the same server. It wouldn\u2019t make sense then to run Jenkins and all my containers in the same server. It just felt like I could do much better\u200a\u2014\u200aI wanted to separate concerns. Sure I can juggle with different users, having Jenkins living in its own user, etc, but something within me really wanted to keep a machine specifically for building my applications.<\/p>\n<p id=\"96c9\" class=\"graf graf--p graf-after--p\">Based on this premise, I created another droplet (<em class=\"markup--em markup--p-em\">aka server<\/em>) with less memory (no heavy lifting, just to serve webapps) and with Docker installed as it should be able to run Docker containers. I would then run all my apps in this server. This would let me scale well because I could easily change the memory allocation on my build server, while leaving my apps untouched.<\/p>\n<p id=\"e4bb\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Let\u2019s recap<\/em><\/strong><\/p>\n<p id=\"2f8e\" class=\"graf graf--p graf-after--p\">We\u2019ve made quite a good progress. We\u2019ve settled for Docker as our core build mechanism and we also decided to separate concerns in favor of maintainability by spinning up a separate production server in another server.<\/p>\n<figure id=\"1e2c\" class=\"graf graf--figure graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1600\/1*_RrbIviKsUwOb10BqXsGyA.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Deployment workflow with Docker and 2 DO\u00a0droplets<\/figcaption><\/figure>\n<p id=\"6449\" class=\"graf graf--p graf-after--figure\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Useful links:<\/em><\/strong><\/p>\n<ol class=\"postList\">\n<li id=\"5afc\" class=\"graf graf--li graf-after--p\">How to Dockerize\u00a0<a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/medium.com\/@tiangolo\/angular-in-docker-with-nginx-supporting-environments-built-with-multi-stage-docker-builds-bb9f1724e984\" target=\"_blank\" rel=\"noopener\">Angular<\/a>,\u00a0<a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/medium.com\/ai2-blog\/dockerizing-a-react-application-3563688a2378\" target=\"_blank\" rel=\"noopener\">React<\/a>\u00a0and\u00a0<a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/vuejs.org\/v2\/cookbook\/dockerize-vuejs-app.html\" target=\"_blank\" rel=\"noopener\">Vue<\/a>\u00a0apps.<\/li>\n<li id=\"1ccb\" class=\"graf graf--li graf-after--li graf--trailing\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/www.digitalocean.com\/community\/tutorials\/how-to-install-and-use-docker-on-ubuntu-18-04\" target=\"_blank\" rel=\"noopener\">How to install Docker in Ubuntu<\/a>.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/section>\n<section class=\"section section--body\">\n<div class=\"section-divider\">\n<hr class=\"section-divider\" \/>\n<\/div>\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<h4 id=\"9c09\" class=\"graf graf--h4 graf--leading\">3. From Magic to Live\u00a0App<\/h4>\n<p id=\"8f07\" class=\"graf graf--p graf-after--h4\">I wasn\u2019t sure how to integrate my servers (build + prod). After\u00a0<a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/stackoverflow.com\/questions\/49472695\/how-to-run-container-in-a-remote-docker-host-with-jenkins\" target=\"_blank\" rel=\"noopener\">asking for help<\/a>and researching, I came to the conclusion that, for my particular use case, I could implement the following workflow:<\/p>\n<ol class=\"postList\">\n<li id=\"a61d\" class=\"graf graf--li graf-after--p\">Build the docker image in the\u00a0<em class=\"markup--em markup--li-em\">Build Server<\/em><\/li>\n<li id=\"8a25\" class=\"graf graf--li graf-after--li\">Push the docker image to my\u00a0<em class=\"markup--em markup--li-em\">Docker Hub<\/em><\/li>\n<li id=\"6a84\" class=\"graf graf--li graf-after--li\">Log in to my prod server via SSH<\/li>\n<li id=\"c6e5\" class=\"graf graf--li graf-after--li\">Pull the image from the\u00a0<em class=\"markup--em markup--li-em\">Docker Hub<\/em>\u00a0and run the container<\/li>\n<\/ol>\n<p id=\"bb58\" class=\"graf graf--p graf-after--li\">It made sense as I didn\u2019t have to resort to using tools like docker-machine or kubernetes to orchestrate my servers.\u00a0<em class=\"markup--em markup--p-em\">It just works,\u00a0<\/em>it\u2019s simple and it looks clean enough for me.<\/p>\n<p id=\"c4d3\" class=\"graf graf--p graf-after--p\"><em class=\"markup--em markup--p-em\">Note on security:<\/em>\u00a0It\u2019s recommended to disable password based authentication on your production server and enable only\u00a0<a class=\"markup--anchor markup--p-anchor\" href=\"https:\/\/hostpresto.com\/community\/tutorials\/how-to-enable-key-based-authentication-for-ssh-on-your-linux-server\/\" target=\"_blank\" rel=\"noopener\">key based authentication from your build server<\/a>. That is, it will be virtually impossible to login to your prod server if not from your build server.<\/p>\n<p id=\"6315\" class=\"graf graf--p graf-after--p\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Show me some code<\/em><\/strong><\/p>\n<p id=\"48f3\" class=\"graf graf--p graf-after--p\">A technical post is not a technical post until you show some code. Let\u2019s see what the Jenkins pipeline looks like in the CI when integrating the whole thing:<\/p>\n<\/div>\n<div class=\"section-inner sectionLayout--outsetColumn\">\n<figure id=\"3f12\" class=\"graf graf--figure graf--layoutOutsetCenter graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/2000\/1*Sxfmc-ggSPTcFf4deOVMDw.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Jenkins Scripted Pipeline. Visit\u00a0<a class=\"markup--anchor markup--figure-anchor\" href=\"https:\/\/gist.github.com\/caroso1222\/e2fcd6fd274d82d91334ebe6e4ce0586\" target=\"_blank\" rel=\"noopener\">gist\u00a0here<\/a>.<\/figcaption><\/figure>\n<\/div>\n<div class=\"section-inner sectionLayout--insetColumn\">\n<p id=\"21fc\" class=\"graf graf--p graf-after--figure\">I\u2019ve labeled some lines in the snippet, let\u2019s go through them real quick:<\/p>\n<ol class=\"postList\">\n<li id=\"c7d9\" class=\"graf graf--li graf-after--p\">Build docker image based on the\u00a0<a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/github.com\/caroso1222\/ast-viewer\/blob\/master\/Dockerfile\" target=\"_blank\" rel=\"noopener\">Dockerfile<\/a>.<\/li>\n<li id=\"be60\" class=\"graf graf--li graf-after--li\">Push the docker image to my\u00a0<em class=\"markup--em markup--li-em\">Docker Hub<\/em>\u00a0account.<\/li>\n<li id=\"fadc\" class=\"graf graf--li graf-after--li\">SSH into the production server<\/li>\n<li id=\"4e99\" class=\"graf graf--li graf-after--li\">Pull the image from my Docker Hub<em class=\"markup--em markup--li-em\">.<\/em><\/li>\n<li id=\"cc73\" class=\"graf graf--li graf-after--li\">Run the container from the image we just pulled. I run the container on port 8080, but that\u2019s because I set up nginx to route the traffic to several containers in the same server via subdomain<em class=\"markup--em markup--li-em\">\u00a0proxy_pass\u00a0<\/em>(e.g. domainA.com serves container A, domainB.com serves container B). I won\u2019t elaborate on this technique as it falls out of the scope of this post.<\/li>\n<\/ol>\n<p id=\"6e72\" class=\"graf graf--p graf-after--li\">As stated at the beginning of this post, this pipeline runs on every\u00a0<em class=\"markup--em markup--p-em\">git push\u00a0<\/em>made in master. The following image shows what happens on Jenkins for every deployment. By the way,\u00a0<strong class=\"markup--strong markup--p-strong\">I love this image as it depicts what the challenge looked like in my head before diving in.<\/strong><\/p>\n<figure id=\"8256\" class=\"graf graf--figure graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1600\/1*LNxe3JwGouNbCpyIItYX4g.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Stage view of the Jenkins pipeline. Each line represents a deployment triggered by a git\u00a0push.<\/figcaption><\/figure>\n<p id=\"0991\" class=\"graf graf--p graf-after--figure\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Let\u2019s wrap up<\/em><\/strong><\/p>\n<p id=\"d6e1\" class=\"graf graf--p graf-after--p\">We finally made it. We have unraveled our black box. Every secret has been revealed and we have been able to cover each one of its components.\u00a0<strong class=\"markup--strong markup--p-strong\">Let\u2019s take a look at what our final deployment workflow looks like now!<\/strong><\/p>\n<figure id=\"8997\" class=\"graf graf--figure graf-after--p\">\n<div class=\"aspectRatioPlaceholder is-locked\">\n<div class=\"aspectRatioPlaceholder-fill\"><\/div>\n<div class=\"progressiveMedia js-progressiveMedia graf-image is-canvasLoaded is-imageLoaded\"><img decoding=\"async\" class=\"progressiveMedia-image js-progressiveMedia-image\" src=\"https:\/\/i0.wp.com\/cdn-images-1.medium.com\/max\/1600\/1*-n1GvBiN7_luWjXLwdVVzQ.png?w=750&#038;ssl=1\" data-recalc-dims=\"1\" \/><\/div>\n<\/div><figcaption class=\"imageCaption\">Diagram of the full deployment strategy covered in this post. A Docker image is built in the build server, goes through the Docker Hub and finally made it to the production server.<\/figcaption><\/figure>\n<p id=\"37c6\" class=\"graf graf--p graf-after--figure\"><strong class=\"markup--strong markup--p-strong\"><em class=\"markup--em markup--p-em\">Useful links<\/em><\/strong><\/p>\n<ol class=\"postList\">\n<li id=\"c5b2\" class=\"graf graf--li graf-after--p\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/hostpresto.com\/community\/tutorials\/how-to-enable-key-based-authentication-for-ssh-on-your-linux-server\/\" target=\"_blank\" rel=\"noopener\">Enable Key based authentication in Ubuntu<\/a>.<\/li>\n<li id=\"636c\" class=\"graf graf--li graf-after--li graf--trailing\"><a class=\"markup--anchor markup--li-anchor\" href=\"https:\/\/medium.freecodecamp.org\/expose-vs-publish-docker-port-commands-explained-simply-434593dbc9a3\" target=\"_blank\" rel=\"noopener\">Run Docker containers and expose ports<\/a>.<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/section>\n<section class=\"section section--body\">\n<div class=\"section-divider\">\n<hr class=\"section-divider\" \/>\n<\/div>\n<div class=\"section-content\">\n<div class=\"section-inner sectionLayout--insetColumn\">\n<h3 id=\"df57\" class=\"graf graf--h3 graf--leading\">Conclusion<\/h3>\n<p id=\"e0b8\" class=\"graf graf--p graf-after--h3\">There are tons of deployment strategies out there, each of them with its pros and cons, and each one relying on a different stack with its own taste. That\u2019s the beauty of it. In this post I covered just one of these strategies that grants me full control over my custom deployment pipeline using two servers, a CI and Docker.<\/p>\n<p id=\"d564\" class=\"graf graf--p graf-after--p\">It all might sound daunting if you\u2019re not very familiar with the concepts we covered. Fear not, I say. It\u2019s a long post mainly because it sums up a learning journey which spanned across almost 3 months, putting 1\u20132 hours each night grinding on these servers and reading stuff I was not very familiar with.<\/p>\n<p id=\"9e13\" class=\"graf graf--p graf-after--p\">Even if you\u2019re a FrontEnd developer focused on browser-land, I still think it\u2019s very valuable that you get familiar with the full stack. It will give you the tools to have a solid conversation and form an opinion when discussing DevOps stuff in your company. It\u2019s better to say\u00a0<em class=\"markup--em markup--p-em\">\u201cyou guys are the experts, but this is my opinion on the matter\u2026\u201d\u00a0<\/em>than saying \u201c<em class=\"markup--em markup--p-em\">well, that\u2019s not my thing, you ops guys figure it out\u201d.<\/em><\/p>\n<p id=\"90e1\" class=\"graf graf--p graf-after--p graf--trailing\">I hope you got some inspiration to go ahead and implement your personal deployment pipeline and get to know all this DevOps tech\u200a\u2014\u200atons of fun ahead!<\/p>\n<\/div>\n<\/div>\n<\/section>\n<\/blockquote>\n<p>Source: <em><a href=\"https:\/\/hackernoon.com\/deploying-frontend-applications-the-fun-way-bc3f69e15331\">Deploying frontend applications\u200a\u2014\u200athe fun way \u2013 Hacker Noon<\/a><\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post I will tell you how I deploy my frontend applications using GitHub, Jenkins, Docker and Digital Ocean.\u00a0I had some doubts\u00a0through my learning journey so I decided to put up what I learned on this post as a<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[30,196,215],"tags":[942,943,941,889,944],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p8jhJx-lV","_links":{"self":[{"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/posts\/1359"}],"collection":[{"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/comments?post=1359"}],"version-history":[{"count":1,"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/posts\/1359\/revisions"}],"predecessor-version":[{"id":1360,"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/posts\/1359\/revisions\/1360"}],"wp:attachment":[{"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/media?parent=1359"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/categories?post=1359"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rosetta.vn\/short\/wp-json\/wp\/v2\/tags?post=1359"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}