<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Herbz</title>
    <description>The latest articles on DEV Community by Herbz (@herbzhao).</description>
    <link>https://dev.to/herbzhao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F364332%2F1b884964-1e5b-4f09-aaa9-2f9b5bff3053.jpeg</url>
      <title>DEV Community: Herbz</title>
      <link>https://dev.to/herbzhao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/herbzhao"/>
    <language>en</language>
    <item>
      <title>Docker + Flask + Vue + Nginx + MongoDB- deployment and development in one package (3) </title>
      <dc:creator>Herbz</dc:creator>
      <pubDate>Sun, 26 Apr 2020 19:40:36 +0000</pubDate>
      <link>https://dev.to/herbzhao/docker-flask-vue-nginx-mongodb-deployment-and-development-in-one-package-3-1l9g</link>
      <guid>https://dev.to/herbzhao/docker-flask-vue-nginx-mongodb-deployment-and-development-in-one-package-3-1l9g</guid>
      <description>&lt;h1&gt;
  
  
  Chapter 7: Initialize MongoDB
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now that we have a running backend and frontend, the next step is to get the database running so we can handle data better.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In our &lt;code&gt;docker-compose.yml&lt;/code&gt; add following:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    mongodb:
        image: mongo:4.2.5-bionic
        container_name:  mongodb
        restart: always
        environment:
            - MONGO_INITDB_ROOT_USERNAME=root
            - MONGO_INITDB_ROOT_PASSWORD=example
        volumes:
            - './database:/data/db'
        ports:
            - "27017:27017"
            - "27018:27018"
            - "27019:27019"
        expose:
            - 27017
            - 27018
            - 27019

    mongo-express:
        image: mongo-express
        restart: always
        environment:
            # mongodb authentication
            - ME_CONFIG_MONGODB_ADMINUSERNAME=root
            - ME_CONFIG_MONGODB_ADMINPASSWORD=example
            # mongodb URI
            - ME_CONFIG_MONGODB_SERVER=mongodb
            - ME_CONFIG_MONGODB_PORT=27017
            # web login username and password
            - ME_CONFIG_BASICAUTH_USERNAME=mongo
            - ME_CONFIG_BASICAUTH_PASSWORD=express
        ports:
            - 8081:8081
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This setup allows us to have mongodb and a nice web UI mongo-express running. For additional security, we have set password for our db, and we have also set a web login &lt;code&gt;BASICAUTH&lt;/code&gt; for our mongo-express service. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spin this up with &lt;code&gt;docker-compose up --build&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the browser, we can access to our database with &lt;code&gt;localhost:8081&lt;/code&gt; via mongo-express.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>mongodb</category>
      <category>flask</category>
    </item>
    <item>
      <title>Build a website monitor with docker + flask + twilio + vue + mongodb (part 1)</title>
      <dc:creator>Herbz</dc:creator>
      <pubDate>Sun, 26 Apr 2020 19:40:14 +0000</pubDate>
      <link>https://dev.to/herbzhao/build-a-website-monitor-with-docker-flask-twilio-vue-mongodb-part-1-33ml</link>
      <guid>https://dev.to/herbzhao/build-a-website-monitor-with-docker-flask-twilio-vue-mongodb-part-1-33ml</guid>
      <description>&lt;h3&gt;
  
  
  I have been eager to learn docker for quite some time and finally got some quality (self-isolation) time to try it out. Turns out it is really as good as the internet says, and let's jump into it!
&lt;/h3&gt;

&lt;h4&gt;
  
  
  I have recently built a website but sometimes it may go offline due to a wrong deployment build or whatever reason. There are some online services that claims to notify you when your site is down, but I don't know how often they check your site etc.. So let's build our own website monitor!
&lt;/h4&gt;







&lt;h3&gt;
  
  
  Step 1: Python whatsapp messaging service by twilio
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The basic idea is simple, we query our website, check the response, if the response is not as expected, we notify ourselves via sms or email (the traditional way) or via whatsapp (the better way).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Twilio offer free whatsapp messaging service, and who still uses SMS in 2020?? Sign up an account here: &lt;a href="https://www.twilio.com/whatsapp"&gt;https://www.twilio.com/whatsapp&lt;/a&gt; and this video explained everything you need &lt;a href="https://www.youtube.com/watch?v=98OewpG8-yw"&gt;https://www.youtube.com/watch?v=98OewpG8-yw&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basically you need 1. twilio credentials, 2. link your whatsapp number  to the twilio sender and 3. install the twilio module for python. &lt;br&gt;
To do it more properly, let's also use virtual environment for python.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;backend
pip &lt;span class="nb"&gt;install &lt;/span&gt;pipenv
pipenv shell
pipenv &lt;span class="nb"&gt;install &lt;/span&gt;twilio
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You also need to set up few environment variables before it can work properly (and this is better for our docker app later than hardcode). Credentials can be found here: &lt;a href="https://www.comm100.com/livechat/knowledgebase/where-do-i-find-the-twilio-account-sid-auth-token-and-phone-number-sid.html"&gt;https://www.comm100.com/livechat/knowledgebase/where-do-i-find-the-twilio-account-sid-auth-token-and-phone-number-sid.html&lt;/a&gt;. ADMIN_PHONE_NUMBER is your phone number.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TWILIO_ACCOUNT_SID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'abcd'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TWILIO_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1234'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ADMIN_PHONE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'+441234'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Alright, the whatsapp messaging service can be configured like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# backend/TwilioWhatsapp.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;twilio.rest&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="c1"&gt;# check document here if needed
# https://www.twilio.com/docs/libraries/reference/twilio-python/index.html
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_whatsapp_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message_body&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# without specifying credentials, it uses environment variables
&lt;/span&gt;
    &lt;span class="n"&gt;from_whatsapp_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"whatsapp:+14155238886"&lt;/span&gt;

    &lt;span class="n"&gt;to_whatsapp_number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"whatsapp:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;to_whatsapp_number&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message_body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;from_whatsapp_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;to_whatsapp_number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'ADMIN_PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We can test it by &lt;code&gt;python TwilioWhatsapp.py&lt;/code&gt;, you should get a whatsapp message if you have set up the whatsapp sandbox properly. &lt;/li&gt;
&lt;/ol&gt;







&lt;h3&gt;
  
  
  Step 2: check whether our site is live using &lt;code&gt;requests + beautifulsoup&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;install beautifulsoup4 and lxml library&lt;br&gt;
&lt;code&gt;pipenv install beautifulsoup4 lmxl&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our python code would be&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# backend/URLChecker.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;

&lt;span class="n"&gt;chrome_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'user-agent'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_text_from_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_to_find&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chrome_headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"lxml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text_to_find&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_text_from_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chrome_headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"lxml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extract_text_from_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://google.com/"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find_text_from_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://google.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Google'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Basically we can use &lt;code&gt;extract_text_from_URL()&lt;/code&gt; to see what your site normally returns and then use &lt;code&gt;find_text_from_URL()&lt;/code&gt; to find whether the text is available. As far as I find, the vue.js rendered website does not return the proper response from &lt;code&gt;requests&lt;/code&gt;, but it is anyway enough to use those texts. &lt;/li&gt;
&lt;/ul&gt;







&lt;h3&gt;
  
  
  Step 3: create a service that checks our website conditions every so often.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Assuming we will have multiple website to monitor in the future, we can create a simple &lt;code&gt;site_to_monitor.json&lt;/code&gt; to store the information we need. When we actually want to allow other users to add new sites, we probably should migrate to a database.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;backend/site_to_monitor.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://google.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text_to_find"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Google"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"phone_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+441234567"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now our monitor_service app
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# backend/MonitorService.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;TwilioWhatsapp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_whatsapp_message&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;URLChecker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;  &lt;span class="n"&gt;find_text_from_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extract_text_from_URL&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monitor_service&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# every one hour check the service itself is working.
&lt;/span&gt;        &lt;span class="n"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'ADMIN_PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"From Downotifier: the monitoring service is fully functioning"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"site_to_monitor.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;site_info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;site_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;site_info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;find_text_from_URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'URL'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'text_to_find'&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                    &lt;span class="n"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'phone_number'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"Your site {} is down, please check it"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'URL'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
                    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'URL'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" is down."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;monitor_service&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;









&lt;h3&gt;
  
  
  Small wrap up: Now the monitoring service is ready, we shall prepare our backend API to allow user adding new website to monitor, see which text is currently returned by our service and also remove the old records if it is no longer necessary.
&lt;/h3&gt;







&lt;p&gt;Part 2 of this article is here: &lt;/p&gt;

</description>
      <category>docker</category>
      <category>mongodb</category>
      <category>flask</category>
      <category>vue</category>
    </item>
    <item>
      <title>Docker + Flask + Vue + Nginx - deployment and development in one package (2)</title>
      <dc:creator>Herbz</dc:creator>
      <pubDate>Sat, 11 Apr 2020 17:34:27 +0000</pubDate>
      <link>https://dev.to/herbzhao/my-docker-learning-journey-2-using-vue-4mlf</link>
      <guid>https://dev.to/herbzhao/my-docker-learning-journey-2-using-vue-4mlf</guid>
      <description>&lt;p&gt;Last time I set up a flask environment with Nginx and it allows hot reload etc. So practically we can test our code in development and directly deploy it without any changes. (other than the Nginx file for a little bit).&lt;/p&gt;

&lt;p&gt;Now we want to incorporate a front end, and I prefer &lt;code&gt;Vue.js&lt;/code&gt; for its simplicity. As it turns out, we don't have to install anything locally on our host machine. Everything can be done in the container.&lt;/p&gt;

&lt;p&gt;The idea is to run &lt;code&gt;vue ui&lt;/code&gt; on our container, which will allow us to serve and build our web app later. And all we need to do is to ports our 8000 (vue ui) and 8080 (app). &lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 4: Vue.js frontend
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;frontend&lt;/code&gt; folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the &lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:lts-alpine

# install project dependencies
RUN npm install -g @vue/cli

# copy project files and folders to the current working directory (i.e. 'app' folder)
ADD . /frontend


# vue ui --headless --port 8000 --host 0.0.0.0
CMD [ "vue", "ui", "--headless" , "--port", "8000",  "--host", "0.0.0.0" ]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  NOTE: I spent some time to figure it out that the vue ui has to be hosted on 0.0.0.0:8000, the localhost of container doesn't work. You can read more here: &lt;a href="https://pythonspeed.com/articles/docker-connection-refused/"&gt;https://pythonspeed.com/articles/docker-connection-refused/&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Then modify the &lt;code&gt;docker-compose.yml&lt;/code&gt; by adding the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    frontend:
        build: ./frontend
        container_name:  frontend
        restart: always
        #  mount the volumes so we can change code and hot reload
        volumes:
            - './frontend:/frontend'

        #  port allows the host machine to link to container
        #  8000 for vue ui, 5000 for our vue app
        ports:
            - "8000:8000"
            - "5000:5000"

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now we can create a project using the vue ui in our &lt;code&gt;frontend&lt;/code&gt; folder. A problem I noticed is that vue seems to have issue overwriting our frontend folder, so instead it is making a &lt;code&gt;frontend/frontend&lt;/code&gt; nested folder. No big harm anyway.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating the project just using the default setting. Then in vue ui go to &lt;code&gt;Tasks&lt;/code&gt; --&amp;gt; &lt;code&gt;serve&lt;/code&gt;, change the parameters to be &lt;code&gt;host: 0.0.0.0, port: 5000&lt;/code&gt;. And then serve.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now go to &lt;code&gt;localhost:5000&lt;/code&gt;, and your vue app should be up and running. Changing the file on your host machine will reflect the changes in real time, so we are good to develop.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  PS: if you are having problem of modifying the frontend folder, use &lt;code&gt;sudo chmod -R 777 frontend/&lt;/code&gt;
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Chapter 5: Review the other code and fix
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now that we have both our frontend and backend connected, we need to make some modification. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In our &lt;code&gt;nginx.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;

    # https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04
    #  listen to our npm server.
    location / {
        proxy_pass http://frontend:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    location /api {
        include uwsgi_params;
        uwsgi_pass backend:8080;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now that we listen to our uWSGI server at localhost:80/api and listen to our npm server at localhost:80/. We need to modify the route in our python &lt;code&gt;app.py&lt;/code&gt;, simply by changing &lt;code&gt;/helloworld/&lt;/code&gt; to &lt;code&gt;/api/helloworld/&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask
app = Flask(__name__)

@app.route('/api/hello')
def hello_world():
    return 'Still hot reloading!'

if __name__ == "__main__":
    app.run(host='0.0.0.0')

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 6: communication between backend and frontend
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now, our frontend can communicate with our backend through &lt;code&gt;localhost/api&lt;/code&gt; or &lt;code&gt;backend:8080&lt;/code&gt;, but the &lt;code&gt;backend&lt;/code&gt; connection is only made available between containers. &lt;strong&gt;When we access our frontend app via browser, &lt;code&gt;backend&lt;/code&gt; will not be accessible!!&lt;/strong&gt; Instead, we have to use the first option. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Luckily this doesn't mean we need to do anything different between development and deployment. All we need to do is to use the relative path such as &lt;code&gt;/api/hello&lt;/code&gt; to access to our backend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To test our connection, we can do a simple axios call from frontend to backend.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;frontend/components/HelloWorld.vue&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class="hello"&amp;gt;
    &amp;lt;h1&amp;gt;{{ msg }}&amp;lt;/h1&amp;gt;
    &amp;lt;button v-on:click="test_axios"&amp;gt;Test access to our backend&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;script&amp;gt;
import axios from 'axios';

export default {
  name: 'HelloWorld',
  data() {
    return {

    }
  },
  props: {
    msg: String
  },
  mounted() {
    this.test_axios()
  },
methods: {
    test_axios: function () {
      const baseURI = '/api/hello'
      // const baseURI = 'http://localhost:80/api/hello'
      axios.get(baseURI)
      .then((result) =&amp;gt; {
        console.log(result)
      }).catch(function (error) {
           console.log(error);
        });
    }
  },
}
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 7: a simple user case for get, post requests.
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;So above we are still using a simple hello_world and it may not be a good enough starting point for anyone who wants a skeleton project to work with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let's modify our frontend a bit to make it more interesting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;frontend/HelloWorld.vue&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class="hello"&amp;gt;
    &amp;lt;button v-on:click="test_get"&amp;gt;Fetch info from our backend&amp;lt;/button&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;input v-model="user_name" placeholder="user_name" /&amp;gt;
    &amp;lt;br /&amp;gt;
    &amp;lt;button v-on:click="test_post"&amp;gt;Send your name to backend&amp;lt;/button&amp;gt;
    &amp;lt;div&amp;gt;{{backend_response}}&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
import axios from "axios";

export default {
  name: "HelloWorld",
  data() {
    return {
      user_name: "",
      backend_response: ""
    };
  },
  props: {
    msg: String
  },
  mounted() {},
  methods: {
    test_get: function() {
      var baseURI = "/api/test_get";
      var vm = this;
      // const baseURI = 'http://localhost:80/api/hello'
      axios
        .get(baseURI)
        .then(response =&amp;gt; {
          console.log(response);
          vm.backend_response = response.data.message;
        })
        .catch(function(error) {
          console.log(error);
        });
    },
    test_post: function() {
      var baseURI = "/api/test_post";
      var vm = this;
      // const baseURI = 'http://localhost:80/api/hello'
      axios
        .post(baseURI, {
          user_name: vm.user_name
        })
        .then(response =&amp;gt; {
          console.log(response.data);
          vm.backend_response = response.data;
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  }
};
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Note here I use &lt;code&gt;var vm = this&lt;/code&gt; for some arrow functions, this is a nice trick that will save you from being insane.. &lt;a href="https://gist.github.com/JacobBennett/7b32b4914311c0ac0f28a1fdc411b9a7"&gt;https://gist.github.com/JacobBennett/7b32b4914311c0ac0f28a1fdc411b9a7&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;And our python backend is also modified to accept get and post&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;backend/app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask, jsonify, request
from flask_cors import CORS, cross_origin
import json

app = Flask(__name__)
CORS(app)


@app.route('/api/test_get', methods=['GET'])
@cross_origin()
def test_get():
    return jsonify({'message': 'Hello, I am your backend'})

@app.route('/api/test_post', methods=['POST'])
@cross_origin()
def test_post():
    if request.method == "POST":
        response = request.get_json()
        result = "Hello " + response['user_name'] 
        return  result

if __name__ == "__main__":
    app.run(host='0.0.0.0')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now you are all good!&lt;/li&gt;
&lt;li&gt;Next chapter we will be connecting to a mongodb and I think we are done!&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Docker + Flask + Vue + Nginx - deployment and development in one package (1)</title>
      <dc:creator>Herbz</dc:creator>
      <pubDate>Fri, 10 Apr 2020 20:17:21 +0000</pubDate>
      <link>https://dev.to/herbzhao/my-docker-learning-journey-edh</link>
      <guid>https://dev.to/herbzhao/my-docker-learning-journey-edh</guid>
      <description>&lt;h3&gt;
  
  
  Building a minimalist Flask + uWSGI + Vue.js + Nginx app
&lt;/h3&gt;

&lt;p&gt;with docker deployment&lt;/p&gt;

&lt;h4&gt;
  
  
  keep it to a good practice as possible.
&lt;/h4&gt;

&lt;h1&gt;
  
  
  Chapter 1. Backend (Flask + uWSGI)
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;First, establish a virtual environment use pipenv.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir backend

pipenv shell`

pipenv install flask, uWSGI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Second, create a bare minimal flask app named as &lt;code&gt;app.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == "__main__":
    app.run(host='0.0.0.0')

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Third, create a &lt;code&gt;wsgi.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from app import app

if __name__ == "__main__":
    app.run()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Lastly, create a &lt;code&gt;app.ini&lt;/code&gt; file for uWSGI configs
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[uwsgi]
# the module itself, by referring to the wsgi.py file minus the extension, and the callable within the file, app:
module = wsgi:app

# Enable hot reload!
py-autoreload = 1

# Nginx to handle actual client connections, which will then pass requests to uWSGI. 
socket = :8080


master = true
processes = 4
threads = 2

#  giving the Nginx group ownership of the uWSGI process later on, 
#  so we need to make sure the group owner of the socket can read information from it and write to it.
chmod-socket = 660

# clean up the socket when the process stops by adding the vacuum option:
vacuum = true
die-on-term = true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;After all of this, our python backend is ready, create a requirements.txt by
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipenv lock -r &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now we just need to create a docker file, which will copy files and then install necessary libraries (as outlined in requirements.txt) and get our uWSGI server up and running.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# use python container image
From python:3.6-stretch

# set the working directory of the image filesystem 
WORKDIR /backend

# copy current directory to the working directory 
ADD . /backend

# Install the python dependencies
RUN pip install -r requirements.txt

# start the uWSGI
CMD ["uwsgi", "app.ini"]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In our backend folder, we should have these files now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.ini
app.py
Dockerfile
Pipfile
requirements.txt
wsgi.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 3. Deployment (nginx)
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;### All we need to do is to re-direct request from outside to our uWSGI server in container. Luckily we can directly access to them using the image name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;for more details: &lt;a href="http://nginx.org/en/docs/beginners_guide.html"&gt;http://nginx.org/en/docs/beginners_guide.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The nginx listen to 80 port and direct calls to different locations such as "/diary" to different internal ports. (flask: 8080 in this case)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {

    listen 80;

    location / {
        include uwsgi_params;
        uwsgi_pass flask:8080;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then, the Dockerfile
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM nginx

# remove the default conf
RUN rm /etc/nginx/conf.d/default.conf

#  copy the nginx.conf in our filesystem into the image filesystem
COPY nginx.conf /etc/nginx/conf.d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Chapter 4: docker-compose to connect all containers
&lt;/h1&gt;

&lt;p&gt;All the containers are in a virtual lan environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;go to the root folder of the project, now you should have following folders
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;backend/
frontend/
database/
nginx/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a new file called &lt;code&gt;docker-compose.yml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# check your version: https://docs.docker.com/compose/compose-file/
version: "3.7"


services:
# first container
    backend:
        # look for dockerfile and build the container
        build: ./backend
        container_name:  backend
        restart: always
        # set enviornemt variables
        environment: 
            - APP_NAME=FlaskApp
        #  mount the volumes so we can change code and hot reload
        volumes:
            - './backend:/backend'
        #  expose port on the container, only to other containers
        expose:
            - 8080

    nginx:
        build: ./nginx
        container_name:  nginx
        restart: always
        # port allows the host machine to link to container
        # pass 80 from host machine to 80 of the image filesystem
        ports:
            - "80:80"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;NOTE: we added a volumes here that allows future hot-reload to happen. (together with the uWSGI's py-autoreload)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NOTE: The difference about ports and expose: &lt;br&gt;
expose allow communication between multiple containers&lt;br&gt;
ports map one of the container port to the host machine port (the real internet)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flask</category>
      <category>docker</category>
      <category>nginx</category>
    </item>
  </channel>
</rss>
