<?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: Ole Kristian Losvik</title>
    <description>The latest articles on DEV Community by Ole Kristian Losvik (@losol).</description>
    <link>https://dev.to/losol</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%2F194002%2F70049a1d-4695-4ac9-8ef0-898ce22df75d.jpeg</url>
      <title>DEV Community: Ole Kristian Losvik</title>
      <link>https://dev.to/losol</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/losol"/>
    <language>en</language>
    <item>
      <title>Run Dash+Poetry+Gunicorn as your own Docker container on Heroku</title>
      <dc:creator>Ole Kristian Losvik</dc:creator>
      <pubDate>Wed, 15 Jun 2022 21:54:25 +0000</pubDate>
      <link>https://dev.to/losol/run-dashpoetrygunicorn-as-your-own-docker-container-on-heroku-190b</link>
      <guid>https://dev.to/losol/run-dashpoetrygunicorn-as-your-own-docker-container-on-heroku-190b</guid>
      <description>&lt;p&gt;I am totally a noob, and this should be seen only as a working example, and never be used for something serious. Feel free to suggest some improvements and comments! &lt;/p&gt;

&lt;h2&gt;
  
  
  Dash
&lt;/h2&gt;

&lt;p&gt;The open source &lt;a href="https://dash.plotly.com/"&gt;Dash&lt;/a&gt; platform is my main reason for my desire  to learn to program. I am starting with simple data analytics and visualization, and hoping to get into AI and more complex matters later. &lt;/p&gt;

&lt;h2&gt;
  
  
  Poetry
&lt;/h2&gt;

&lt;p&gt;Python comes in different versions, and all my packages do as well. &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt; makes life easier for me when learning to program. It both handles dependencies for my project, as well as providing me with a virtual environment with all my packages present! &lt;/p&gt;

&lt;p&gt;This is a example configuration in my &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.poetry]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dashes"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Data analytics"&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GPL3"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.dependencies]&lt;/span&gt;
&lt;span class="py"&gt;python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^3.10"&lt;/span&gt;
&lt;span class="py"&gt;dash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^2.4.1"&lt;/span&gt;
&lt;span class="py"&gt;pandas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.4.2"&lt;/span&gt;
&lt;span class="py"&gt;numpy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.22.4"&lt;/span&gt;
&lt;span class="py"&gt;pyjstat&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^2.2.1"&lt;/span&gt;
&lt;span class="py"&gt;dash-bootstrap-components&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.1.0"&lt;/span&gt;
&lt;span class="py"&gt;dash-labs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.0.8"&lt;/span&gt;
&lt;span class="py"&gt;gunicorn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^20.1.0"&lt;/span&gt;
&lt;span class="py"&gt;whitenoise&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^6.2.0"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.poetry.dev-dependencies]&lt;/span&gt;
&lt;span class="py"&gt;flake8&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^4.0.1"&lt;/span&gt;
&lt;span class="py"&gt;isort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^5.10.1"&lt;/span&gt;
&lt;span class="py"&gt;autopep8&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"^1.6.0"&lt;/span&gt;

&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["poetry-core&amp;gt;=1.0.0"]&lt;/span&gt;
&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"poetry.core.masonry.api"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;What &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; does, is a bit above my imagination. Still: it makes it possible to run pretty much software with a few Google searches. &lt;/p&gt;

&lt;p&gt;There seems to be a trend towards slim Docker images for your containers, so I tried a multi stage build. First we are exporting the dependencies to a &lt;code&gt;requirements.txt&lt;/code&gt;, and then in a new container we install dependencis and run the Gunicorn web server. &lt;/p&gt;

&lt;p&gt;This is my &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;################&lt;/span&gt;
&lt;span class="c"&gt;# Stage: Build #&lt;/span&gt;
&lt;span class="c"&gt;################&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.10-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; POETRY_VERSION=1.1.13&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Export poetry dependencies to file&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"poetry==&lt;/span&gt;&lt;span class="nv"&gt;$POETRY_VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; poetry.lock pyproject.toml ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv /app/venv
&lt;span class="k"&gt;RUN &lt;/span&gt;poetry &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;--without-hashes&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;--output&lt;/span&gt; /app/requirements.txt

&lt;span class="c"&gt;#####################&lt;/span&gt;
&lt;span class="c"&gt;# Stage: Production #&lt;/span&gt;
&lt;span class="c"&gt;#####################&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.10-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;prod&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONPATH=/app&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy requirements from build stage, and install them&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/requirements.txt . &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Create a non-root user to run the web server&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;adduser &lt;span class="nt"&gt;-u&lt;/span&gt; 5678 &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="nt"&gt;--gecos&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; appuser &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; appuser /app
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="c"&gt;# Run server&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; ${PORT:-8000}&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; gunicorn --bind 0.0.0.0:${PORT:-8000} index:server&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Heroku
&lt;/h2&gt;

&lt;p&gt;Heroku is a super solid cloud platform suitable for running your enterprise apps. However, they do also provide a free tier. It takes some time to warm up for some request, howeve super cool to test your apps with. &lt;/p&gt;

&lt;p&gt;You need to tell Heroku that you have your own image to run, just download their CLI first. Use the Heroku CLI, and make sure to set your run stack to container: &lt;code&gt;heroku stack:set container&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Heroku also loves to decide which port the app should be available at, so make sure to get their PORT from environment variables. See above for how I managed to read the port when present, and default to port 8000 if not provided – &lt;code&gt;${PORT:-8000}&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Needed to add a file to my repo, &lt;code&gt;heroku.yml&lt;/code&gt;, as well&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn index:server --bind 0.0.0.0:$PORT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then: finally my little data analytics dashboard is running on Heroku! Again - feel free to suggest improvements in the comments section. &lt;/p&gt;

</description>
      <category>dash</category>
      <category>python</category>
      <category>docker</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
