<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Docker on Jonathan Franklin</title><link>https://jonnie.github.io/tags/docker/</link><description>Recent content in Docker on Jonathan Franklin</description><generator>Hugo</generator><language>en</language><lastBuildDate>Tue, 17 May 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://jonnie.github.io/tags/docker/index.xml" rel="self" type="application/rss+xml"/><item><title>Data Orchestration &amp; Platform Operations</title><link>https://jonnie.github.io/work/data-orchestration/</link><pubDate>Tue, 17 May 2022 00:00:00 +0000</pubDate><guid>https://jonnie.github.io/work/data-orchestration/</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&#10;&lt;p&gt;Pricesearcher&amp;rsquo;s feed ingestion, product building, indexing and warehouse jobs depended on scheduled workflows. That orchestration layer needed to move from Airflow 1 to Airflow 2 while continuing to support existing jobs. Monitoring also needed to reflect changing feed schedules: a weekly feed should not trigger the same freshness warnings as a daily one.&lt;/p&gt;&#10;&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;&#10;&lt;p&gt;Airflow scheduled the data workflows, including validation tasks around the pipelines. SNS and CloudWatch events reached Slack through Lambda functions. Supporting services included query expansion, merchant and brand enrichment, Spark warehouse uploads and Terraform-managed infrastructure.&lt;/p&gt;</description></item><item><title>Elasticsearch Product Search Index</title><link>https://jonnie.github.io/work/search-index-pipeline/</link><pubDate>Sat, 11 Sep 2021 00:00:00 +0000</pubDate><guid>https://jonnie.github.io/work/search-index-pipeline/</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&#10;&lt;p&gt;Pricesearcher&amp;rsquo;s search service used separate Elasticsearch product indexes for different countries. Index builds needed to carry product identity and cross-merchant links into searchable records, while accommodating market-specific merchant rules and schedules. The team also used Google Sheets to maintain smaller datasets that needed to be searchable.&lt;/p&gt;&#10;&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;&#10;&lt;p&gt;A Makefile-driven pipeline generated Avro source data through Athena, downloaded it from S3 and used a PyPy builder to populate Elasticsearch. It supported the site&amp;rsquo;s &lt;code&gt;nitor&lt;/code&gt; index and a separate &lt;code&gt;nested&lt;/code&gt; index type. Separate configurable builders published selected Google Sheet tabs as Elasticsearch indexes.&lt;/p&gt;</description></item><item><title>Multi-Source Product Search API</title><link>https://jonnie.github.io/work/product-search-api/</link><pubDate>Fri, 16 Jul 2021 00:00:00 +0000</pubDate><guid>https://jonnie.github.io/work/product-search-api/</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&#10;&lt;p&gt;Pricesearcher&amp;rsquo;s website, browser extension and external integrations needed product data from both the company&amp;rsquo;s Elasticsearch index and third-party providers. Combining those sources meant dealing with different response formats, provider latency and client ranking requirements. The API also needed clearer boundaries between the interfaces used by different clients.&lt;/p&gt;&#10;&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;&#10;&lt;p&gt;The Python/Flask API combined the internal Elasticsearch index with Amazon, Yahoo and Kelkoo. Third-party requests ran asynchronously and provider failures were handled separately. Redis with RediSearch supported filtering, sorting and facets over cached results. Web, browser-extension, external-client and core APIs ran as separate deployments with their own OpenAPI specifications.&lt;/p&gt;</description></item><item><title>Product Web Crawler</title><link>https://jonnie.github.io/work/product-web-crawler/</link><pubDate>Tue, 08 Jun 2021 00:00:00 +0000</pubDate><guid>https://jonnie.github.io/work/product-web-crawler/</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&#10;&lt;p&gt;Pricesearcher&amp;rsquo;s price comparison service used merchant websites as a source of product data. The crawl pipeline read sitemaps, found product pages and extracted structured data, with separate deployments for environments and AWS regions. Maintaining it meant handling differences between merchant sites and balancing crawl frequency against the cost of repeated requests.&lt;/p&gt;&#10;&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;&#10;&lt;p&gt;The Python crawler ran on AWS and tracked crawl state and cycles in DynamoDB. Sitemaps stored in S3 fed product-page crawling, while a management API behind Cognito and API Gateway managed merchants and ingestion sources. A companion crawler collected website metadata for the data platform.&lt;/p&gt;</description></item><item><title>Merchant Image Proxy</title><link>https://jonnie.github.io/work/merchant-image-proxy/</link><pubDate>Fri, 19 Feb 2021 00:00:00 +0000</pubDate><guid>https://jonnie.github.io/work/merchant-image-proxy/</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&#10;&lt;p&gt;Pricesearcher displayed product images hosted by merchants, with differing URL formats and image-serving behaviour. The site needed a common endpoint for image requests, resizing and caching.&lt;/p&gt;&#10;&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;&#10;&lt;p&gt;The service was the company&amp;rsquo;s fork of &lt;a href="https://github.com/willnorris/imageproxy"&gt;willnorris/imageproxy&lt;/a&gt;, exposed as a signed company-domain endpoint and deployed on AWS Lambda behind API Gateway. Terraform managed separate staging and production environments.&lt;/p&gt;&#10;&lt;h2 id="what-i-built"&gt;What I built&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Maintained and customised the existing Go fork.&lt;/li&gt;&#10;&lt;li&gt;Added options to pass through the original image or return no image when processing failed, instead of always using the default placeholder.&lt;/li&gt;&#10;&lt;li&gt;Fixed decoding and encoding behaviour that corrupted proxied URLs containing special characters.&lt;/li&gt;&#10;&lt;li&gt;Aligned the Terraform version with the deployments&amp;rsquo; remote state.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="engineering-decisions"&gt;Engineering decisions&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The implementation extended the upstream proxy rather than replacing its existing resizing and caching behaviour.&lt;/li&gt;&#10;&lt;li&gt;Fallback behaviour was configurable between the original image, no image and the upstream default placeholder.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="technologies"&gt;Technologies&lt;/h2&gt;&#10;&lt;p&gt;Go, AWS Lambda, API Gateway, Terraform, Docker, Jenkins.&lt;/p&gt;</description></item><item><title>Product Catalog Pipeline: Feeds to Products</title><link>https://jonnie.github.io/work/product-catalog-pipeline/</link><pubDate>Thu, 10 Dec 2020 00:00:00 +0000</pubDate><guid>https://jonnie.github.io/work/product-catalog-pipeline/</guid><description>&lt;h2 id="problem"&gt;Problem&lt;/h2&gt;&#10;&lt;p&gt;Pricesearcher needed to turn merchant feeds into a catalogue suitable for price comparison. Downloaded listings had to be normalised, assigned to the correct market and currency, and combined with reference data before indexing. Expansion into additional countries also required changes to feed processing, catalogue seeding and scheduling.&lt;/p&gt;&#10;&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;&#10;&lt;p&gt;The ingestion stage processed merchant feeds into Parquet. A Spark product builder joined them with warehouse price history, categories, translated brands, linked product IDs and image-quality data, then supplied the &lt;a href="https://jonnie.github.io/work/search-index-pipeline/"&gt;search index pipeline&lt;/a&gt;. Country-specific processing and schedules ran through the same flow.&lt;/p&gt;</description></item></channel></rss>