<?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: Joel Hayhurst</title>
    <description>The latest articles on DEV Community by Joel Hayhurst (@joelmichael).</description>
    <link>https://dev.to/joelmichael</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%2F450251%2Fab1dba0f-c519-4587-8783-4ec1ed48e41d.jpeg</url>
      <title>DEV Community: Joel Hayhurst</title>
      <link>https://dev.to/joelmichael</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joelmichael"/>
    <language>en</language>
    <item>
      <title>WSL for Rails developers</title>
      <dc:creator>Joel Hayhurst</dc:creator>
      <pubDate>Wed, 19 May 2021 17:43:58 +0000</pubDate>
      <link>https://dev.to/hint/wsl-for-rails-developers-2a3g</link>
      <guid>https://dev.to/hint/wsl-for-rails-developers-2a3g</guid>
      <description>&lt;p&gt;It is now possible, thankfully, to develop in Rails on Windows. For many years, it seemed one needed a Mac for this purpose; and I have nothing against Macs, and enjoy the MacBook Pro, which I have used throughout my career. But I also have a Windows machine. I like to play games sometimes, and now that I work from home, along with an increasing number of people, I have access to it. I like the idea that developers should be able to enjoy their preference in environment, where reasonable. Some prefer to boot directly into Linux, which is also a perfectly fine choice. On a whim, I decided to try Rails development on Windows, and it has worked out nicely.&lt;/p&gt;

&lt;p&gt;With WSL, the Windows Subsystem for Linux, we can now run a Linux distribution &lt;em&gt;within&lt;/em&gt; Windows 10. You can use it similarly to how you might use Homebrew on a Mac, and the whole thing is surprisingly seamless, especially after some recent improvements. All of the Linux development and server tools you're used to are present, and work the same as you are familiar with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;It's relatively easy to install; first you have to enable WSL as a "Windows feature", and then you go to the Microsoft Store and install your preferred distro. I know they have Debian and Ubuntu available, as well as some others. I chose Debian.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F01a94ce590d03e7fd06a7d472a7a841f%2F494f9%2Fwsl-features.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F01a94ce590d03e7fd06a7d472a7a841f%2F494f9%2Fwsl-features.png" alt="Enabling the WSL Windows feature"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F2721cc4426397ceddd210396f62a9514%2F1fbe8%2Fwsl-microsoft-store.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F2721cc4426397ceddd210396f62a9514%2F1fbe8%2Fwsl-microsoft-store.png" alt="Debian on the Microsoft Store"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminals
&lt;/h3&gt;

&lt;p&gt;As for terminals, there's a few options. The default terminal that comes with WSL is named after the distribution you installed, so for me it is simply called "Debian". It has the Debian swirl logo, which I like, and the default color scheme and font it comes with I find pleasant. On the downside, I've found it will always scroll down if new content comes up (say you are tailing a test log), so I got in the habit of waiting for the tests to finish before examining logs. It also doesn't have tabbing, but I found I don't need tabbing as much as I thought; I'm not on a laptop screen, so I have enough space to keep separate terminals open. It is possible to use PowerShell to access WSL, by running the &lt;code&gt;wsl&lt;/code&gt; command, but I find the color scheme is a bit off. And there's other options, like the new Windows Terminal, which does have tabbing and lots of customization options, but requires editing a text file to change settings (you can do it, you're a developer!), and still feels kind of in beta. In general though, I'm fine with the default WSL terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fa2569589606893caa4307c1e9b736f0f%2Fd72d4%2Fwsl-terminal3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fa2569589606893caa4307c1e9b736f0f%2Fd72d4%2Fwsl-terminal3.png" alt="Default Debian WSL terminal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VSCode
&lt;/h3&gt;

&lt;p&gt;WSL also integrates perfectly with VSCode, which I use for most of my code editing. You can edit files directly within WSL and have them immediately affect the app. I mention this because there used to be an issue with this in WSL 1, but it has been resolved since WSL 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fb7ec1ff558e9e80d189d5ac23b2587ef%2F3ddad%2Fwsl-vscode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fb7ec1ff558e9e80d189d5ac23b2587ef%2F3ddad%2Fwsl-vscode.png" alt="Debian WSL in VSCode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Docker Desktop also integrates very nicely with WSL since version 2. Before WSL 2, Docker had to use Hyper-V to emulate Linux, but now we don't even need Hyper-V enabled. Docker now uses the WSL 2 backend, and it seems quite a bit faster to start as well, in my experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fc22bef4097273d285ea7dc197d847246%2F22d0a%2Fwsl-docker.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2Fc22bef4097273d285ea7dc197d847246%2F22d0a%2Fwsl-docker.png" alt="Booting Docker Desktop on WSL 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Filesystem
&lt;/h3&gt;

&lt;p&gt;When you use WSL, you will want to store all of your relevant code within the WSL filesystem, not on the Windows filesystem. In WSL 1 there was more crossover, but not in WSL 2; everything goes on the Linux virtual drive. It is still possible to browse these files using Windows Explorer, if you are so inclined, but in general probably best to stick with Linux tools when using this drive. Still I do open one of my WSL directories in Explorer regularly for the purpose of easily opening screenshots produced by Capybara.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F1348a707d563e5d1ee4c5991973d8286%2F928ea%2Fwsl-explorer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F1348a707d563e5d1ee4c5991973d8286%2F928ea%2Fwsl-explorer.png" alt="Viewing WSL files in Explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy-and-paste
&lt;/h3&gt;

&lt;p&gt;One other note about transitioning from Mac, you'll probably want to enable the Ctrl-Shift-C and Ctrl-Shift-V copy-paste functionality in the terminal. On Mac, we have that handy Command key, but on Windows we are stuck with just Ctrl, which is used plenty by Linux itself; so Ctrl-Shift is a necessary compromise. It's not that hard to get accustomed to, and besides that, we can also use right-click to copy and paste text.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;p&gt;I've been developing with this system for over a year now and haven't run into any serious problems. Probably the biggest issue I had is that WSL, combined with the code repositories of some large apps I'm working on, used up a lot of disk space, and my C: drive only has 256 GB. I did however find a way to move WSL to a separate drive, a nice 1 TB SSD, and that solved my space issues. This can be accomplished using the &lt;code&gt;wsl --export&lt;/code&gt; and &lt;code&gt;wsl --import&lt;/code&gt; commands without much fuss.&lt;/p&gt;

&lt;p&gt;Another issue I've faced is that when working on a larger application, a lot of memory is used up by WSL. Running two operating systems simultaneously uses more resources. Even after quitting the applications, and quitting Docker, this memory would still be reserved by WSL. The solution is that when I'm done with using WSL for the day, I run &lt;code&gt;wsl --shutdown&lt;/code&gt; in PowerShell and that frees up all the memory again. I went ahead and upgraded to 32 GB of RAM to avoid these paging annoyances. However, this upgrade is only necessary because of the size of this application. Every other application I've worked in was comfortable with 16 GB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F49110ef872ccda4fd8a33ee8052f9b3d%2F889a4%2Fwsl-powershell.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhint.io%2Fstatic%2F49110ef872ccda4fd8a33ee8052f9b3d%2F889a4%2Fwsl-powershell.png" alt="Shutting down WSL with PowerShell"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;These are all of the thoughts I have on WSL at this time. I hope you find it useful to be able to program in Rails on Windows, if that is your desire. If you give it a shot, you might find it's more comfortable than you expected. Thanks for reading.&lt;/p&gt;

</description>
      <category>wsl</category>
      <category>windows</category>
      <category>linux</category>
    </item>
    <item>
      <title>Ruby on AWS Lambda: Layer Dependencies</title>
      <dc:creator>Joel Hayhurst</dc:creator>
      <pubDate>Tue, 11 Aug 2020 20:00:41 +0000</pubDate>
      <link>https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe</link>
      <guid>https://dev.to/hint/ruby-on-aws-lambda-layer-dependencies-1ooe</guid>
      <description>&lt;p&gt;&lt;em&gt;This article is part of our Ruby on AWS Lambda blog series. A recent project had us migrating an existing PDF document processing system from Rails Sidekiq to AWS Lambda. The processing includes OCR, creating preview images, splicing the PDF, and more. Moving to Lambda reduced processing time by 300 times in some cases.&lt;/em&gt;&lt;br&gt;
​&lt;br&gt;
&lt;em&gt;This series of articles will serve less as a step-by-step process to get OCR serverless infrastructure up and running and more of a highlight reel of our "Aha!" moments. In part one, we talk about creating a AWS Lambda Layer with Docker. Check out the other posts in the series:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-planning-architecting-3foi"&gt;Planning &amp;amp; Architecture&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-aws-lambda-package-ship-it-7ol"&gt;Package &amp;amp; Ship It!&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://dev.to/hint/ruby-on-lambda-integrating-with-activestorage-5b11"&gt;Integrating with Active Storage&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building dependencies for Lambda can be confusing. They need to be compiled, zipped up, and then made into a layer. We also have to keep the file size for the dependencies under Lambda's limit. Here is how we did that using a Dockerfile.&lt;br&gt;
​&lt;br&gt;
You can &lt;a href="https://gist.github.com/joelmichael/efe48be65d789c555459067b5a54e42d"&gt;check out the full Dockerfile here&lt;/a&gt;.&lt;br&gt;
​&lt;/p&gt;
&lt;h2&gt;
  
  
  Dockerfile details
&lt;/h2&gt;

&lt;p&gt;​&lt;br&gt;
One question when building the dependencies was which Docker image to use. We first tried using the &lt;code&gt;amazonlinux&lt;/code&gt; image, but this actually resulted in some build problems with one of our dependencies. We later found the LambCI images and ended up using &lt;code&gt;lambci/lambda:build-ruby2.7&lt;/code&gt;, because we are using Ruby. This worked perfectly for us, and it has the benefit of already having build tools installed, making for faster Docker builds.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use AWS Lambda ruby2.7 build environment&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; lambci/lambda:build-ruby2.7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
AWS Lambda currently has a limit of 250 MB for its dependencies. If you are building significantly larger dependencies, as we were, then you are likely to reach this limit. It is therefore very important to compile your dependencies in a way that reduces file size. By using the &lt;code&gt;-Os&lt;/code&gt; compile time option, we were able to reduce the size of our binaries by over 90%.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Optimize compilation for size to try and stay below Lambda's 250 MB limit&lt;/span&gt;
&lt;span class="c"&gt;# This reduces filesize by over 90% (!) compared to the default -O2&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CFLAGS "-Os"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CXXFLAGS $CFLAGS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
While slightly less performant than the default &lt;code&gt;-O2&lt;/code&gt;, the massively reduced file size is worth it in this situation.&lt;br&gt;
​&lt;br&gt;
Next up is building Leptonica.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Leptonica image-reading dependencies
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libjpeg-devel libpng-devel libtiff-devel
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; http://www.leptonica.org/source/leptonica-1.79.0.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf leptonica-1.79.0.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; leptonica-1.79.0&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
You will need to install the &lt;code&gt;-devel&lt;/code&gt; packages when compiling, but won't need the &lt;code&gt;-devel&lt;/code&gt; variant when providing the dependencies for runtime.&lt;br&gt;
​&lt;br&gt;
Then Tesseract:&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Optional Tesseract foreign language training dependencies
&lt;span class="c"&gt;# libicu-devel on Yum is of insufficient version (50, 52 is required)&lt;/span&gt;
&lt;span class="c"&gt;# These are also not really necessary for our usage.&lt;/span&gt;
&lt;span class="c"&gt;#RUN yum install -y libicu-devel pango-devel cairo-devel&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-Lo&lt;/span&gt; tesseract-4.1.1.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;  https://github.com/tesseract-ocr/tesseract/archive/4.1.1.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf tesseract-4.1.1.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; tesseract-4.1.1&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./autogen.sh &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="c"&gt;# These ENV vars have to be set or it will not build&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LEPTONICA_CFLAGS -I/opt/include/leptonica&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LEPTONICA_LIBS -L/opt/lib -lleptonica&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
​
# English training data
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt/share/tessdata&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://github.com/tesseract-ocr/tessdata_best/raw/master/eng.traineddata
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
GhostScript was technically installable via RPM, but the amount of dependencies was too great. More on that later. We decided to just compile it with a minimal dependency set.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/ghostscript-9.52.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf ghostscript-9.52.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; ghostscript-9.52&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Ironically, we end up installing &lt;code&gt;ghostscript-devel&lt;/code&gt; so that ImageMagick can be built. It might be possible to use the prior GhostScript installation here, but this was simple enough for build purposes.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ghostscript-devel
​
&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-Lo&lt;/span&gt; ImageMagick-7.0.10-6.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;  https://github.com/ImageMagick/ImageMagick/archive/7.0.10-6.tar.gz
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;zxvf ImageMagick-7.0.10-6.tar.gz
​
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; ImageMagick-7.0.10-6&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./configure &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt
&lt;span class="k"&gt;RUN &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
We considered using libvips instead of ImageMagick, but it would have added scope to the project. Nonetheless I have some commented out code in the Dockerfile for building libvips in case we decide to switch to it in the future.&lt;br&gt;
​&lt;br&gt;
For Ruby gems, a Gemfile in the same directory that contains all of the gems is all we need.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s1"&gt;'https://rubygems.org'&lt;/span&gt;
&lt;span class="err"&gt;​&lt;/span&gt;
&lt;span class="c1"&gt;# PDF processing gems&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'simhash2'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'phashion'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rtesseract'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'mini_magick'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'pdf-reader'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'hexapdf'&lt;/span&gt;
&lt;span class="err"&gt;​&lt;/span&gt;
&lt;span class="c1"&gt;# Other gems used in these files&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'procto'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'adamantium'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'concord'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'activesupport'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'~&amp;gt; 6.0.2'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'chronic'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'activestorage'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Note that a little bit of trickery is necessary to modify the gem paths for loading in Lambda.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Phashion dependencies
&lt;span class="c"&gt;# Can skip this step because they are already installed above for Leptonica&lt;/span&gt;
&lt;span class="c"&gt;#RUN yum install -y libjpeg-devel libpng-devel&lt;/span&gt;
​
# Copy Gemfile from host into container's current directory
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Gemfile .&lt;/span&gt;
​
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle config &lt;span class="nb"&gt;set &lt;/span&gt;path vendor/bundle
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle
​
# Modify directory structure for Lambda load path
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; vendor/bundle&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;ruby/gems
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;ruby/2.&lt;span class="k"&gt;*&lt;/span&gt; ruby/gems
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;ruby /opt
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Now the RPM packages. I left in the installation for GhostScript and its dependencies from RPM as a comment, mainly so you can see how many packages I had to specify manually.&lt;br&gt;
​&lt;br&gt;
Note that you will have to specify the &lt;code&gt;x86_64&lt;/code&gt; variants of these packages when using these tools.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root&lt;/span&gt;
​
# Install yumdownloader and rpmdev-extract
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; yum-utils rpmdevtools
​
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;rpms
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; rpms&lt;/span&gt;
​
# Download dependency RPMs
&lt;span class="k"&gt;RUN &lt;/span&gt;yumdownloader libjpeg-turbo.x86_64 libpng.x86_64 libtiff.x86_64 &lt;span class="se"&gt;\
&lt;/span&gt;  libgomp.x86_64 libwebp.x86_64 jbigkit-libs.x86_64
&lt;span class="c"&gt;# GhostScript and dependencies&lt;/span&gt;
&lt;span class="c"&gt;# To reduce dependencies, we are compiling GhostScript from source instead&lt;/span&gt;
&lt;span class="c"&gt;# RUN yumdownloader ghostscript.x86_64 cups-libs.x86_64 fontconfig.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   fontpackages-filesystem freetype.x86_64 ghostscript-fonts jasper-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   lcms2.x86_64 libICE.x86_64 libSM.x86_64 libX11.x86_64 libX11-common \&lt;/span&gt;
&lt;span class="c"&gt;#   libXau.x86_64 libXext.x86_64 libXt.x86_64 libfontenc.x86_64 libxcb.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   poppler-data stix-fonts urw-fonts xorg-x11-font-utils.x86_64 avahi-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   acl.x86_64 audit-libs.x86_64 cracklib.x86_64 cracklib-dicts.x86_64 cryptsetup-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   dbus.x86_64 dbus-libs.x86_64 device-mapper.x86_64 device-mapper-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   elfutils-default-yama-scope elfutils-libs.x86_64 gzip.x86_64 kmod.x86_64 kmod-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   libcap-ng.x86_64 libfdisk.x86_64 libpwquality.x86_64 libsemanage.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   libsmartcols.x86_64 libutempter.x86_64 lz4.x86_64 pam.x86_64 qrencode-libs.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   shadow-utils.x86_64 systemd.x86_64 systemd-libs.x86_64 ustr.x86_64 util-linux.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   expat.x86_64 xz-libs.x86_64 libgcrypt.x86_64 libgpg-error.x86_64 elfutils-libelf.x86_64 \&lt;/span&gt;
&lt;span class="c"&gt;#   bzip2-libs.x86_64&lt;/span&gt;
​
# Extract RPMs
&lt;span class="k"&gt;RUN &lt;/span&gt;rpmdev-extract &lt;span class="k"&gt;*&lt;/span&gt;.rpm
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.rpm
​
# Copy all package files into /opt/rpms
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-vR&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;/usr/&lt;span class="k"&gt;*&lt;/span&gt; /opt
​
# The x86_64 packages extract as lib64, we need to move these files to lib
&lt;span class="k"&gt;RUN &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; rsync
&lt;span class="k"&gt;RUN &lt;/span&gt;rsync &lt;span class="nt"&gt;-av&lt;/span&gt; /opt/lib64/ /opt/lib/
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /opt/lib64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Notice some more path management. We used &lt;code&gt;rsync&lt;/code&gt; for copying because &lt;code&gt;cp&lt;/code&gt; gave us some problems.&lt;br&gt;
​&lt;br&gt;
Now we just need to zip up the dependencies.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;zip &lt;span class="nt"&gt;-r&lt;/span&gt; /root/ProcessDocumentLayer.zip &lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
And lastly, the entrypoint for the Dockerfile, which copies the zip file to an output directory.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/bin/cp", "/root/ProcessDocumentLayer.zip", "/output"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Now we just need the Docker commands to build this. I put them at the very top of the file under a "Usage" section.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Usage:&lt;/span&gt;
&lt;span class="c"&gt;# docker build -t lambda .&lt;/span&gt;
&lt;span class="c"&gt;# docker run -v $(pwd):/output lambda&lt;/span&gt;
&lt;span class="c"&gt;# ./publish_layer.sh.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
The &lt;code&gt;publish_layer.sh&lt;/code&gt; script is a small one we wrote that uses &lt;code&gt;awscli&lt;/code&gt; to upload and publish the layer. You'll have to authenticate with AWS for it to work. I used &lt;code&gt;aws configure&lt;/code&gt; for this purpose but you can &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html"&gt;check out this article&lt;/a&gt; for more info.&lt;br&gt;
​&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;ProcessDocumentLayer.zip s3://process-document-layers
aws lambda publish-layer-version &lt;span class="nt"&gt;--layer-name&lt;/span&gt; ProcessDocumentLayer &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"Process Document dependencies"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--content&lt;/span&gt; &lt;span class="nv"&gt;S3Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;process-document-layers,S3Key&lt;span class="o"&gt;=&lt;/span&gt;ProcessDocumentLayer.zip &lt;span class="nt"&gt;--compatible-runtimes&lt;/span&gt; ruby2.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
And that's it. With this Dockerfile, we are able to easily build and publish a dependency layer for our OCR system on Lambda. We hope this was useful for you!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>aws</category>
      <category>serverless</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
