<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Brice Fotzo on Medium]]></title>
        <description><![CDATA[Stories by Brice Fotzo on Medium]]></description>
        <link>https://medium.com/@bricefotzo?source=rss-cf7915a0bf5e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*s8YSZOSXQQnx4i5wMpQupQ.jpeg</url>
            <title>Stories by Brice Fotzo on Medium</title>
            <link>https://medium.com/@bricefotzo?source=rss-cf7915a0bf5e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 01:17:44 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@bricefotzo/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[SQL Is All You Need: RAG without a Vector Database in a Dozen Lines of BigQuery]]></title>
            <link>https://medium.com/google-cloud/sql-is-all-you-need-rag-without-a-vector-database-in-a-dozen-lines-of-bigquery-ff08ed5d3ac4?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/ff08ed5d3ac4</guid>
            <category><![CDATA[data]]></category>
            <category><![CDATA[vector-database]]></category>
            <category><![CDATA[rags]]></category>
            <category><![CDATA[bigquery]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Sun, 19 Jul 2026 06:18:15 GMT</pubDate>
            <atom:updated>2026-07-19T06:18:15.294Z</atom:updated>
            <content:encoded><![CDATA[<h3>SQL Is All You Need: A Complete RAG Pipeline in SQL with BigQuery</h3><p><em>Your RAG stack is 90% unnecessary. Here’s the receipts, reproducible on a public dataset.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mCVYes3J9tj4psKERhN0sA.png" /><figcaption>SQL is All You Need</figcaption></figure><p>Vector database. Orchestrator. Embedding service. API keys everywhere. Three containers to answer questions about your own documents.</p><p>That’s the architecture we all normalized for Retrieval-Augmented Generation. And in 2026, for a large class of use cases, it’s obsolete.</p><p>BigQuery’s AI functions turn the LLM into a SQL operators, the same way JOIN or WHERE are operators. Embeddings, vector search, and structured generation all run <strong>where your data already lives</strong>, with your existing IAM, audit logs, and cost controls. And no, it&#39;s not a Gemini-only walled garden: you can bring <strong>Claude</strong>, <strong>Llama</strong>, <strong>Mistral</strong>, or an open-source model from <strong>Hugging Face</strong>.</p><p>In this article, I’ll build the pipeline twice, on a real public dataset you can query right now:</p><ol><li><strong>The one-shot version</strong> (GA): batch-embed a corpus, search it, generate grounded answers</li><li><strong>The living version</strong> (Preview): a corpus that keeps itself embedded as data arrives, because production data doesn’t hold still</li></ol><p>Then, I’ll show you exactly where this approach breaks and when Python remains the right answer.</p><p>Everything below runs on bigquery-public-data.bbc_news.fulltext. Open a <strong>BigQuery tab</strong> or the <a href="https://docs.cloud.google.com/data-cloud-extension?hl=fr"><strong>Google Cloud Data Agent Kit</strong></a> plugin in your IDE and follow along.</p><h3>The stack we all normalized</h3><p>A “standard” RAG architecture in most tutorials looks like this:</p><ol><li><strong>An embedding service</strong>: a hosted API, or a model endpoint you deploy and babysit</li><li><strong>A vector database</strong>: one more SaaS, one more billing account, one more sync pipeline to keep consistent with your source of truth</li><li><strong>An orchestrator</strong>: LangChain, LlamaIndex, or hand-rolled glue code</li><li><strong>An LLM API</strong>: with its rate limits and quota management</li><li><strong>Deployment</strong>: typically two or three containers to build, ship, and patch</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qBj2w7-w7Gg1vX-Y" /><figcaption>RAG — The usual stack VS BigQuery SQL only</figcaption></figure><p>Count the moving parts: five systems, at least three API keys, and (the part nobody talks about) <strong>your data leaves the warehouse</strong>. Every governance rule, every row-level security policy, every audit requirement you carefully built in BigQuery? Gone the moment you export chunks to an external vector store.</p><p>We built this architecture because we had no choice. That premise is no longer true.</p><h3>What changed: the LLM is now a SQL operator</h3><p>The BigQuery AI.* function family reached general availability. Three capabilities matter for RAG:</p><ul><li><strong>AI.GENERATE_EMBEDDING</strong>: batch-generate embeddings from any table</li><li><strong>VECTOR_SEARCH</strong>: a native GoogleSQL function for semantic retrieval, with optional IVF or TreeAH indexes (the latter built on Google&#39;s ScaNN algorithm)</li><li><strong>AI.GENERATE</strong>: a scalar, row-wise generation function with <strong>structured output</strong> via output_schema, usable anywhere a value fits: SELECT, WHERE, even JOIN conditions</li></ul><p>No API key in your code. Authentication goes through a BigQuery <strong>connection</strong>, a managed service account you grant a role once. There’s even a default connection now, which removes the setup ceremony entirely.</p><h3>Not just Gemini</h3><p>A common objection: “so I’m locked into Gemini?” No, and this deserves precision because it’s widely misunderstood.</p><p>BigQuery’s AI functions come in two flavors:</p><ul><li><strong>General-purpose functions</strong> (AI.GENERATE_TEXT, AI.GENERATE_TABLE, AI.GENERATE_EMBEDDING with a MODEL): you have full control over the model choice. Supported remote models include the Gemini family, but also <strong>Anthropic Claude, Meta Llama, and Mistral AI</strong> as managed model-as-a-service endpoints, plus <strong>open-source models</strong> from Hugging Face or the Agent Platform (ex Vertex AI) Model Garden. For open models, the CREATE MODEL statement can even auto-deploy the model for you. Same SQL, your model.</li><li><strong>Managed functions</strong> (AI.IF, AI.CLASSIFY, AI.SCORE, and the scalar AI.GENERATE with an endpoint argument): these are Gemini-powered convenience functions where BigQuery handles model selection and prompt engineering for you.</li></ul><p>Want your embeddings from a self-hosted multilingual E5 or Qwen model for compliance reasons, and your generation from Claude?</p><p>Both start with one CREATE MODEL statement. For embeddings, that&#39;s the whole switch: nothing else in the pipeline moves. For generation, one honest caveat: the typed output_schema used in Step 3 is a feature of the managed AI.GENERATE (Gemini).</p><p>To generate with Claude, Llama, or Mistral, you swap to AI.GENERATE_TEXT over your model (or AI.GENERATE_TABLE when you still want structured columns). Same warehouse, same governance, your model. The generation call is the one line you adapt, not the architecture.</p><p>Let’s build.</p><h3>Path 1: the one-shot pipeline (GA)</h3><p>This is the pattern for batch and analytical workloads: a corpus you embed once (or on a schedule), then query.</p><h3>Step 0. One-time setup</h3><p>Before the model, one prerequisite that most warehouse-native tutorials skip: the <strong>connection</strong>. It’s the managed service account BigQuery uses to call the models, and you set it up once.</p><p>The fast path, if you have the BigQuery Admin role, is the <strong>default connection</strong>: just write WITH CONNECTION DEFAULT (as below) and BigQuery provisions it and grants its service account the right roles on first use. Nothing else to do.</p><p>The explicit path, if you’d rather own your connection (this is the us.my_connection referenced in Path 2), is two commands: create a Cloud resource connection, then grant its service account the Agent Platform User role (roles/aiplatform.user, formerly &quot;Vertex AI User&quot;):</p><pre>PROJECT_ID=my-poject-id<br><br># 1. Create a Cloud resource connection (its location must match your dataset)<br>bq mk --connection --location=US --project_id=$PROJECT_ID \<br>  --connection_type=CLOUD_RESOURCE my_connection<br><br># 2. Grant the connection&#39;s service account the Agent Platform User role<br>gcloud projects add-iam-policy-binding $PROJECT_ID \<br>  --member=&quot;serviceAccount:$(bq show --format=prettyjson \<br>    --connection $PROJECT_ID.US.my_connection | jq -r .cloudResource.serviceAccountId)&quot; \<br>  --role=roles/aiplatform.user</pre><p>Grant the role in the project where you create the model (when the endpoint is a model name) or in the project named in the endpoint URL (when you pass a full URL). That’s the whole auth setup: one role, no keys.</p><p>Now the embedding model, using the default connection:</p><pre>CREATE OR REPLACE MODEL `rag_demo_us.emb_model`<br>  REMOTE WITH CONNECTION DEFAULT<br>  OPTIONS (ENDPOINT = &#39;gemini-embedding-001&#39;);</pre><p>That’s the entire “embedding service deployment”. No container, no endpoint to scale, no key to rotate. (Swap the endpoint for an Agent Platform URL if you deployed an open-source model.)</p><h3>Step 1. Embed the corpus</h3><p>We’ll embed BBC News articles. One statement:</p><pre>CREATE OR REPLACE TABLE `rag_demo_us.news_emb` AS<br>SELECT *<br>FROM AI.GENERATE_EMBEDDING(<br>  MODEL `rag_demo_us.emb_model`,<br>  (<br>    SELECT title, body AS content<br>    FROM `bigquery-public-data.bbc_news.fulltext`<br>    WHERE category = &#39;tech&#39;<br>  ),<br>  STRUCT(&#39;RETRIEVAL_DOCUMENT&#39; AS task_type)<br>);</pre><p>Two details tutorials often miss:</p><ul><li>The input query <strong>must expose a column named </strong><strong>content</strong> (alias it if needed). Including a title column improves embedding quality with RETRIEVAL_DOCUMENT.</li><li>task_type matters: RETRIEVAL_DOCUMENT for the corpus, RETRIEVAL_QUERY for the user&#39;s question. Asymmetric embeddings measurably improve retrieval.</li></ul><p>The output table carries your passthrough columns plus three generated ones: embedding (the vector), statistics (a JSON with token_count and truncated), and status.</p><p><strong>Note the names:</strong> the newer AI.GENERATE_EMBEDDING returns embedding, not the ml_generate_embedding_result you&#39;ll see in older ML.GENERATE_EMBEDDING tutorials. Mixing up the two column layouts is a classic copy-paste failure.</p><p><strong>Pitfall #1: silent partial failures.</strong> A batch embedding job can <em>succeed</em> while individual rows fail (quota pressure, service hiccups). BigQuery reports this per row in the status column; an empty status means success. Always check it (WHERE status = &#39;&#39;). If parallel batches hit RESOURCE EXHAUSTED errors, Google ships remote-inference retry scripts and a Dataform package specifically to iterate until every row is processed. It&#39;s documented, because it happens.</p><p><strong>Pitfall #1bis: silent truncation.</strong> Look at statistics: each row reports its token_count and a truncated flag. A BBC article sits comfortably around a couple hundred tokens, but feed the function documents longer than the model&#39;s input limit and it will embed a <em>truncated</em> version without failing. If your corpus has long documents, monitor LAX_BOOL(statistics.truncated) and chunk upstream.</p><h3>Step 2. (Optional) Index it</h3><p>For small corpora, skip this: VECTOR_SEARCH falls back to brute-force exact search. At scale:</p><pre>CREATE OR REPLACE VECTOR INDEX news_idx<br>ON `rag_demo_us.news_emb`(embedding)<br>OPTIONS (index_type = &#39;IVF&#39;, distance_type = &#39;COSINE&#39;);</pre><p>Know before you’re surprised: index maintenance is fully managed and asynchronous, new rows remain searchable immediately (BigQuery brute-forces the not-yet-indexed tail), tables under 10 MB don’t get a populated index, and indexes aren’t supported on the Standard edition.</p><h3>Step 3. Retrieval + generation, one query</h3><p>Embed the question, retrieve the top 5 articles, ground the prompt, generate a typed answer:</p><pre>SELECT<br>  AI.GENERATE(<br>    (<br>      &#39;Answer using only this context:\n&#39;,<br>      STRING_AGG(base.content, &#39;\n---\n&#39;),<br>      &#39;\nQuestion: what were the major browser security stories?&#39;<br>    ),<br>    endpoint      =&gt; &#39;gemini-2.5-flash&#39;,<br>    output_schema =&gt; &#39;answer STRING, confidence FLOAT64, sources ARRAY&lt;STRING&gt;&#39;<br>  ).*<br>FROM VECTOR_SEARCH(<br>  TABLE `rag_demo_us.news_emb`, &#39;embedding&#39;,<br>  (<br>    SELECT embedding<br>    FROM AI.GENERATE_EMBEDDING(<br>      MODEL `rag_demo_us.emb_model`,<br>      (SELECT &#39;major browser security stories&#39; AS content),<br>      STRUCT(&#39;RETRIEVAL_QUERY&#39; AS task_type)<br>    )<br>  ),<br>  top_k =&gt; 5<br>);</pre><p>Count the core pipeline: a dozen lines. No LangChain. No Pinecone. No deployment. And output_schema returns <strong>typed columns</strong> (answer, confidence, sources), not a JSON blob to parse and pray over.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O1qkGD2sBX65yvQa4HMi6g.png" /><figcaption>Results of the retrieval using AI.GENERATE function</figcaption></figure><p>But here’s the honest limitation of Path 1, and it’s a big one.</p><p>The one-shot pattern has a production problem: <strong>data doesn’t hold still.</strong> New articles arrive, descriptions get edited, tickets get updated. With Path 1, every insert or update means re-running the embedding statement, detecting deltas, handling retries, monitoring staleness.</p><h3>Path 2: the living corpus (Preview)</h3><p>This is exactly what <strong>autonomous embedding generation</strong> solves. You declare the embedding as a <em>generated column</em>, and BigQuery maintains it:</p><pre>CREATE TABLE `rag_demo_us.news_live` (<br>  title STRING,<br>  body  STRING,<br>  body_embedding STRUCT&lt;result ARRAY&lt;FLOAT64&gt;, status STRING&gt;<br>    GENERATED ALWAYS AS (<br>      AI.EMBED(<br>        body,<br>        connection_id =&gt; &#39;us.my_connection&#39;,<br>        endpoint      =&gt; &#39;gemini-embedding-001&#39;)<br>    ) STORED OPTIONS (asynchronous = TRUE)<br>);</pre><p>Now just insert data, from anywhere:</p><pre>INSERT INTO `rag_demo_us.news_live` (title, body)<br>SELECT title, body<br>FROM `bigquery-public-data.bbc_news.fulltext`<br>WHERE category = &#39;tech&#39;;</pre><p>Read that DDL again, because the design is elegant:</p><ul><li>GENERATED ALWAYS AS AI.EMBED(...): the embedding is table metadata, not pipeline code. Insert or update the source column, and BigQuery generates or refreshes the vector in the background.</li><li>asynchronous = TRUE: your INSERTs don&#39;t block on model calls. Embedding jobs run on background slots (you&#39;ll see them prefixed gc_ in your job history).</li><li>The column is a STRUCT&lt;result, status&gt;: failures are visible per row, not swallowed.</li></ul><p>A word on the model choice: I use gemini-embedding-001 here because it&#39;s GA and tops the MTEB multilingual leaderboard. The canonical AI.EMBED doc example uses text-embedding-005, which is also perfectly valid; I&#39;m picking the more recent, higher-quality option. One consequence to plan for: its default output is 3072 dimensions versus 768 for the text-embedding models, which raises storage and index cost. If that matters, request a smaller dimension via AI.EMBED&#39;s model_params (the model is Matryoshka-trained, so it truncates cleanly to 768 or 256).</p><p>And retrieval gets <em>simpler</em>, because the new AI.SEARCH function knows which model the table uses and embeds your query string automatically:</p><pre>SELECT base.title, base.body, distance<br>FROM AI.SEARCH(<br>  TABLE `rag_demo_us.news_live`, &#39;body&#39;,<br>  &#39;major browser security stories&#39;,<br>  top_k =&gt; 5<br>);</pre><p>No manual query embedding. No model bookkeeping. You search <strong>text against a text column</strong>, and the warehouse handles the vector plumbing. Vector indexes integrate too: create one on the source column and index training starts once 80% of rows have embeddings.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h7NJU6EfPPIpC9s1halh5w.png" /><figcaption>Results of the retrieval query with the BigQuery AI.SEARCH function</figcaption></figure><p>This is the version that matters for anything that lives in production. The one-shot pattern is a demo; a corpus that keeps itself embedded is an architecture.</p><p><strong>One label, though: this is Preview</strong> (Pre-GA terms, subject to change). Also worth knowing before you commit: one auto-generated embedding column per table, batch your inserts rather than trickling frequent DML (concurrent DML can delay generation), and copies, clones, and snapshots carry the <em>data</em> but not the generation config. Per the latest release notes, you can enable it on new or existing tables via CREATE TABLE or ALTER TABLE. As always, the <a href="https://cloud.google.com/bigquery/docs/release-notes">BigQuery release notes</a> are the only authoritative source for status; check them before you build.</p><h3>What you inherit for free</h3><p>This is the part that should matter most to data teams, and it’s the least discussed:</p><ul><li><strong>Row-level and column-level security apply to </strong><strong>VECTOR_SEARCH.</strong> If a user can&#39;t see a row, they can&#39;t retrieve it as RAG context. Try implementing <em>that</em> correctly across an external vector DB sync.</li><li><strong>IAM is the only auth model.</strong> No API keys in environment variables, no rotation runbooks for the pipeline itself.</li><li><strong>Audit logs, cost attribution, quotas</strong>: all the boring, load-bearing infrastructure your platform team already operates. You can even trace per-job Agent Platform charges in Cloud Billing via the bigquery_job_id_prefix label.</li><li><strong>No sync pipeline.</strong> The embeddings live next to the source rows. There is no “vector store drifted from the warehouse” incident, because there is no second store. With autonomous embeddings, there isn’t even a refresh job to own.</li></ul><p>The best infrastructure is the one you don’t deploy.</p><h3>Where this breaks</h3><p>I promised receipts, not religion:</p><p><strong>1. Cost is per token, and Gemini 2.5 charges for thinking.</strong> Generation on Gemini 2.5 models bills the thinking tokens on top of output. For classification-style tasks, set the thinking budget to 0 via model_params: lower latency, lower bill. Ignore this and batch jobs get expensive quietly. AI.COUNT_TOKENS exists to estimate before you run.</p><p><strong>2. Row count surprises on complex queries.</strong> With JOINs or ORDER BY ... LIMIT, the number of rows actually sent to the model can differ from what you expect. Google&#39;s own recommendation: materialize your input to a table first, then run inference on it. Do it.</p><p><strong>3. Quotas are real.</strong> Embedding throughput is governed by tokens-per-minute quotas (a few million by default). Fine for most workloads; a “let’s embed the whole data lake tonight” job needs planning (or Provisioned Throughput via request_type =&gt; &#39;dedicated&#39;), not enthusiasm.</p><p><strong>4. </strong><strong>VECTOR_SEARCH bills bytes scanned.</strong> On-demand pricing charges for base table, index, and query scans. Use STORING columns in your index to avoid expensive joins back to the base table, and pre-filter with WHERE clauses on stored columns.</p><p><strong>5. Prompt versioning doesn’t exist.</strong> Your prompt is a string inside a SQL query. No registry, no A/B testing, no evaluation harness. For a production assistant iterating on prompts weekly, this gap is real, and it’s where a thin Python layer (or dbt, with prompts as versioned config) earns its keep.</p><h3>GA or Preview? Read the label</h3><p>As of this writing:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/955/1*mYeLi7oF3Am9Va1h9NHp2A.png" /><figcaption>Status of the capabilities — By the author.</figcaption></figure><p>AI.GENERATE and AI.GENERATE_TABLE reached GA in late January 2026; the partner models (Claude, Llama, Mistral) went GA in the first half of 2025. Statuses change; release notes don&#39;t lie.</p><h3>So… is SQL all you need?</h3><p>Three takeaways:</p><p><strong>1. For batch and analytical RAG: yes, seriously consider it.</strong> Document classification, semantic deduplication, enrichment pipelines, internal Q&amp;A over governed data: the warehouse-native approach wins on governance, simplicity, and total cost of ownership. And you keep your model choice.</p><p><strong>2. For a living corpus, autonomous embeddings change the calculus.</strong> The hard part of production RAG was never the search; it was keeping vectors in sync with reality. Declaring the embedding as a generated column deletes that entire problem class. It’s Preview today, but it’s clearly where the platform is going.</p><p><strong>3. The skill isn’t the syntax; it’s knowing where the line sits.</strong> Sub-second user-facing chat still wants a serving layer. Prompt lifecycle still wants versioning. Cost per token still wants monitoring. These constraints didn’t disappear; they moved.</p><p>I’m putting this to the test on the BBC News corpus: cost per 1,000 documents, end-to-end latency, retrieval recall with and without an index, and how fast autonomous embeddings actually converge after a bulk insert. That’s the follow-up article. If there’s a specific scenario you want benchmarked, tell me.</p><p><em>If you enjoyed this, I write about Data, AI, BigQuery, Cloud Run and a lot of cool stuffs.</em></p><p><strong>References:</strong></p><ul><li><a href="https://cloud.google.com/bigquery/docs/generative-ai-overview">Generative AI overview (model support matrix)</a></li><li><a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-create-remote-model">CREATE MODEL for partner and open LLMs</a></li><li><a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-ai-generate">The AI.GENERATE function</a></li><li><a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-ai-generate-embedding">The AI.GENERATE_EMBEDDING function</a></li><li><a href="https://cloud.google.com/bigquery/docs/autonomous-embedding-generation">Autonomous embedding generation</a></li><li><a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/bigqueryml-syntax-ai-search">The AI.SEARCH function</a></li><li><a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/search_functions">Search functions (VECTOR_SEARCH)</a></li><li><a href="https://cloud.google.com/bigquery/docs/vector-index">Manage vector indexes</a></li><li><a href="https://cloud.google.com/bigquery/docs/release-notes">BigQuery release notes</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ff08ed5d3ac4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-cloud/sql-is-all-you-need-rag-without-a-vector-database-in-a-dozen-lines-of-bigquery-ff08ed5d3ac4">SQL Is All You Need: RAG without a Vector Database in a Dozen Lines of BigQuery</a> was originally published in <a href="https://medium.com/google-cloud">Google Cloud - Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[`gcloud run compose up`: Deploy a Multi-Service GPU Stack to Cloud Run from Docker Compose]]></title>
            <link>https://medium.com/google-cloud/gcloud-run-compose-up-deploy-a-multi-service-gpu-stack-to-cloud-run-from-docker-compose-77d650b39972?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/77d650b39972</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[generative-ai]]></category>
            <category><![CDATA[docker-compose]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[google-cloud-run]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Sat, 16 May 2026 06:29:58 GMT</pubDate>
            <atom:updated>2026-10-14T00:29:09.866Z</atom:updated>
            <content:encoded><![CDATA[<h4>A practical guide to gcloud run compose up, and the pitfalls nobody tells you about</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GYXt44UD-JCx7asS3BACZQ.png" /><figcaption>Docker Compose to Cloud Run(Image by author)</figcaption></figure><p>If you’ve ever wished you could go from docker compose up on your laptop to a fully deployed multi-service application on Google Cloud Run with a single command — this article is for you.</p><p>docker compose up is a wonderful command for local development. But it stops at your machine. Now imagine replacing docker with gcloud run and getting the same stack running in the cloud, production-shaped, with caveats we’ll cover on the section 5. That&#39;s exactly what gcloud run compose up makes possible.</p><p>It also breaks in three specific, undocumented ways, which we’ll cover in 5 so you don’t hit them blind.</p><p>In this article, we’ll deploy a real-world app to Cloud Run using a single compose.yaml — no Kubernetes YAML, no manual GPU configuration. Along the way, we&#39;ll cover the key adaptation points, the gotchas nobody documents, and what actually happens under the hood.</p><h3>1. Prerequisites and Getting Started</h3><p>This article assumes you’re comfortable with Docker Compose and have a basic understanding of Google Cloud. You don’t need to know Cloud Run internals — that’s what we’re here to learn.</p><p><strong>What you need before starting:</strong></p><ul><li><strong>Google Cloud SDK 564.0.0+</strong> — required for gcloud run compose up. This feature’s launch stage is still evolving, so check the official docs before adopting in production.</li><li><strong>A GCP project with billing enabled</strong> and the following APIs activated:</li></ul><pre>gcloud services enable run.googleapis.com \<br>    artifactregistry.googleapis.com \<br>    storage.googleapis.com</pre><ul><li><strong>Docker Desktop 4.40+</strong> with Model Runner enabled, if you want to test locally first:</li></ul><pre>docker desktop enable model-runner --tcp 12434</pre><ul><li><strong>Authenticate locally with </strong>gcloud auth application-default login(ADC)<strong>. The credentials need </strong>BigQuery Data Viewer and BigQuery Job User roles .</li></ul><p><strong>Clone the repo to follow along:</strong></p><pre>git clone https://github.com/bricefotzo/gcp-release-notes.git<br>cd gcp-release-notes</pre><blockquote><em>💡 The patterns in this article apply to any multi-service </em><em>compose.yaml. The GCP Release Notes Navigator is just the vehicle — adapt what you learn here to your own stack.</em></blockquote><h3>2. The Use Case: A Multi-Service Cloud Run Stack with an LLM</h3><p>The app we’re deploying is a multi-service stack with three components:</p><ul><li><strong>A Streamlit frontend: </strong>search and filter interface</li><li><strong>A FastAPI backend: </strong>queries BigQuery and calls the LLM</li><li><strong>Open Weights LLM</strong> (ai/llama3.2 ): served by Docker Model Runner for AI-powered summaries</li></ul><blockquote><em>💡 </em><strong><em>Why Docker Model Runner instead of Ollama?</em></strong></blockquote><blockquote><em>Both expose an OpenAI-compatible API, so your code is identical either way. The difference is at deploy time: Docker Model Runner uses the </em><em>models: top-level key in </em><em>compose.yaml, which </em><em>gcloud run compose translates directly into a GPU-backed Cloud Run service. With Ollama, you&#39;d need to manage the GPU container yourself. Same dev experience, very different deployment story.</em></blockquote><p>The local architecture looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*5IVjaNdeyGlCJ_byxBCRog.png" /><figcaption>Local stack: Streamlit → FastAPI → Docker Model Runner, all via `docker compose up`</figcaption></figure><p>After gcloud run compose up, the Cloud Run deployment looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*4Cxnz-4eHKve7LF1REfZdA.png" /><figcaption>After `gcloud run compose up`: front and back co-located as sidecars; LLM as a separate GPU-backed service.</figcaption></figure><p>The key shift: locally, the backend calls the LLM at http://model-runner.docker.internal:12434. On Cloud Run, the LLM becomes a separate service with its own HTTPS URL - and LLM_URL is automatically rewritten to point to it.</p><p>You don’t need to understand the app’s internals to follow this tutorial. What matters is that it’s a real multi-service stack defined in a single compose.yaml — the patterns here apply to your own projects.</p><h3>3. The compose.yaml for gcloud run compose up, Annotated</h3><p>The entire orchestration lives in a single file. Here it is in full — we’ll break it down right after:</p><pre>services:<br>  # ── Streamlit frontend<br>  front:<br>    build:<br>      context: ./frontend<br>    ports:<br>      - &quot;8090:8080&quot;<br>    environment:<br>      - BACKEND_URL=http://back:8000<br>    depends_on:<br>      back:<br>        condition: service_healthy<br>  # ── FastAPI backend (port 8000)<br>  back:<br>    build:<br>      context: ./backend<br>    ports:<br>      - &quot;8000:8000&quot;<br>    # Docker Model Runner injects LLM_URL and LLM_MODEL automatically<br>    models:<br>      - llm<br>    environment:<br>      - PROJECT_ID=serial-techos<br>      - DATA_PROJECT_ID=bigquery-public-data<br>      - DATASET_ID=google_cloud_release_notes<br>      - TABLE_ID=release_notes<br>    healthcheck:<br>      test: [&quot;CMD&quot;, &quot;python&quot;, &quot;-c&quot;, &quot;import urllib.request; urllib.request.urlopen(&#39;http://localhost:8000/health&#39;)&quot;]<br>      interval: 10s<br>      timeout: 5s<br>      retries: 5<br>      start_period: 30s<br># ── Docker Model Runner ─────────────────────────────────────────<br>models:<br>  llm:<br>    model: ai/llama3.2<br>    context_size: 32768<br>    x-google-cloudrun:<br>      environment:<br>        PORT: 12434<br>        MODEL_RUNNER_PORT: 12434<br>      run-model:<br>        inference-container: docker/model-runner</pre><blockquote><em>💡 Everything prefixed with </em><em>x- is a Compose extension. Docker Compose ignores it locally — it&#39;s metadata for Cloud Run only. This is what makes the same file work in both environments.</em></blockquote><h4>3.1 Frontend — Streamlit</h4><p>The Streamlit frontend is straightforward, it builds from ./frontend, exposes port 8080 and depends on the backend being healthy.</p><pre>environment:<br>  - BACKEND_URL=http://back:8000<br>depends_on:<br>  back:<br>    condition: service_healthy</pre><ul><li>BACKEND_URL=http://back:8000 — On Cloud Run, front and back run as sidecars in the same service, sharing a network namespace. This means http://back:8000 resolves via localhost, exactly as it does locally. No change required.</li><li>depends_on — Translated to a Cloud Run container dependency: {&quot;front&quot;:[&quot;back&quot;]}. Cloud Run won&#39;t route traffic to front until back is healthy.</li></ul><h4>3.2 Backend — FastAPI</h4><pre>models:<br>  - llm<br>environment:<br>      - PROJECT_ID=serial-techos<br>      - DATA_PROJECT_ID=bigquery-public-data<br>      - DATASET_ID=google_cloud_release_notes<br>      - TABLE_ID=release_notes<br>healthcheck:<br>  test: [&quot;CMD&quot;, &quot;python&quot;, &quot;-c&quot;, &quot;import urllib.request; urllib.request.urlopen(&#39;http://localhost:8000/health&#39;)&quot;]<br>  interval: 10s<br>  timeout: 5s<br>  retries: 5<br>  start_period: 30s</pre><ul><li>models: - llm — This is the key that wires back to the LLM service. On Cloud Run, LLM_URL is automatically injected with the HTTPS URL of the deployed llm service. Locally, Docker Model Runner handles this injection directly.</li><li>environment — The variables declared here are injected as Cloud Run environment variables.</li></ul><blockquote>For local development, if you chose to use this use case(github), I use ADC for authentication. For this, I use a compose.override.yaml where I define volume mount for authentication in the local container.</blockquote><pre>services:<br>  back:<br>    volumes:<br>      - ${ADC_PATH:-./adc.json}:/tmp/adc.json:ro<br>    environment:<br>      - GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json</pre><p>This second YAML is taken into account when you run docker compose up so that your back container is authenticated using ADC but the command gcloud run compose up ignores it.</p><h4>3.3 The models: top-level key</h4><pre>models:<br>  llm:<br>    model: ai/llama3.2<br>    x-google-cloudrun:<br>      environment:<br>        PORT: 12434<br>        MODEL_RUNNER_PORT: 12434<br>      run-model:<br>        inference-container: docker/model-runner</pre><p>This is where the local-to-cloud translation happens:</p><ul><li>models: is a top-level Compose key for Docker Model Runner. It declares the LLM as a named dependency, not a traditional service container.</li><li>ai/llama3.2— the model pulled from Docker AI Hub.</li><li>x-google-cloudrun: — the Cloud Run-specific configuration:</li><li>PORT and MODEL_RUNNER_PORT: the port on which the model runner listens</li><li>inference-container: docker/model-runner: the container image used to serve the model on Cloud Run.</li></ul><p>On Cloud Run, this block is translated into a standalone Cloud Run service named llm, running docker/model-runner with an NVIDIA L4 GPU attached. No GPU configuration on your side — one key in the YAML, one service in the cloud.</p><h3>4. Deploying with gcloud run compose up: One Command, Two Services</h3><p>Before running the command, make sure everything is in place:</p><blockquote><em>✅ APIs enabled (Cloud Run, Artifact Registry, Cloud Storage)</em></blockquote><blockquote><em>✅ Docker images build locally (</em><em>docker compose build)</em></blockquote><blockquote><em>✅ Authenticated with </em><em>gcloud auth application-default login</em></blockquote><p>Then:</p><pre>export PROJECT_ID=your-project-id<br>gcloud run compose up \<br>  --project=$PROJECT_ID \<br>  --region=europe-west1</pre><p>That’s it. The command builds your images, pushes them to Artifact Registry, and creates Cloud Run services — all from the compose.yaml.</p><h4>4.1 What gets created</h4><p>This is the most surprising part: you defined three components in your compose file, but Cloud Run creates <strong>two</strong> services, not three.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*li-KnhFSPyKYLSxMsJzwCw.png" /><figcaption>Two Cloud Run services created by gcloud run compose up — one for the web app, one for the LLM</figcaption></figure><p><strong>Service 1: </strong><strong>gcp-release-notes</strong> — front and back are co-located as sidecars in a single Cloud Run service. This is the Cloud Run multi-container pattern. The relevant part of the generated Knative manifest:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E12YVqR7ObYZ-xEm2a_wNw.png" /><figcaption>Knative manifest for the `gcp-release-notes` service — note `LLM_URL` auto-rewritten to the deployed `llm` URL.</figcaption></figure><p>Two things to notice here. front is the ingress container (it has ports.name: http1). And LLM_URL has been automatically rewritten from the local Docker Model Runner address to the HTTPS URL of the deployed llm service.</p><p><strong>Service 2: </strong><strong>llm</strong> — The models: key is translated into a standalone Cloud Run service with a real GPU:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2NDv-tcGN899smPT_YYhzw.png" /><figcaption>Knative manifest for the `llm` service — NVIDIA L4 GPU provisioned from the `models:` block.</figcaption></figure><p>4 vCPUs, 16 GB RAM, one NVIDIA L4 GPU — all provisioned automatically from the models: block in your compose file. CPU throttling is disabled because GPU workloads require consistent CPU availability.</p><blockquote>💡 <strong>About the L4 — and why you don’t get to pick.</strong></blockquote><p>Cloud Run’s models: translation provisions an <strong>NVIDIA L4 (24 GB VRAM)</strong> by default. It’s the minimum-cost inference GPU on Cloud Run, optimized for 7B–14B parameter models at FP16 or INT8/FP8. Plenty for llama3.2 or gemma3:4b. <strong>Not</strong> enough for a 70B model, that needs <strong>~35 GB VRAM</strong> even at 4-bit and exceeds the L4.</p><p>Cloud Run also supports GPUs for bigger models, but gcloud run compose updoesn’t currently expose a GPU-type selector in the models: block. You just get an L4. If you need the Blackwell, you’ll have to fall back to deploying the LLM service manually with gcloud run deploy --gpu-type.</p><blockquote><em>💡 Both services are tagged with </em><em>managed-by: runcompose. Useful if you need to identify compose-managed resources in a project with other Cloud Run services.</em></blockquote><h4>4.2 ⚠️ Models are not pulled at deployment time</h4><p>The docker/model-runner container starts empty — no model is loaded. After deployment, you <strong>must</strong> pull the model manually:</p><pre>curl -X POST https://llm-xxx.europe-west1.run.app/models/create \<br>  -H &quot;Content-Type: application/json&quot; \<br>  -d &#39;{&quot;from&quot;: &quot;ai/llama3.2&quot;}&#39;</pre><blockquote><em>🚨 </em><strong><em>Do this right after deployment.</em></strong><em> Until this call is made, any inference request from the backend will hang and eventually time out.</em></blockquote><h3>5. gcloud run compose up — Real pitfalls</h3><p>gcloud run compose is a powerful abstraction, but it&#39;s still a young tool. Here are the real issues encountered deploying this project.</p><h4>⚠️ Pitfall 1 — Why is LLM_URL injected without the /v1 suffix on Cloud Run?</h4><p>Cloud Run injects LLM_URL pointing to the llm service, but the URL ends at the domain (https://llm-xxx.run.app) without the /v1 path required by the OpenAI-compatible API.</p><p>Locally, Docker Model Runner sets LLM_URL to http://model-runner.docker.internal:12434/engines/v1 — full path included. On Cloud Run, the injected value is just the base URL. Calling it directly returns 404 on every inference endpoint.</p><p><strong>Fix</strong> — detect the environment at runtime:</p><pre># backend/src/ai.py<br>from openai import OpenAI<br><br>LLM_URL = os.environ.get(&quot;LLM_URL&quot;, &quot;&quot;)<br># Cloud Run injects the base URL without path suffix.<br># Locally, Docker Model Runner provides the full path.<br>LLM_ENDPOINT = f&quot;{LLM_URL}/v1&quot; if &quot;run.app&quot; in LLM_URL else LLM_URL<br>client = OpenAI(base_url=LLM_ENDPOINT, api_key=&quot;not-needed&quot;)</pre><h4>⚠️ Pitfall 2— Why doesn’t gcloud run compose up redeploy my llm service?</h4><p>If you change the model name or configuration under the models: key and re-run gcloud run compose up, the llm service is <strong>not updated</strong>. The command reports success, which gives the misleading impression that a new revision was created. In reality, the service is untouched.</p><p>Changing the <strong>context_size</strong> for example, has no workaround at the time of writing. If you’ve found one, please drop it in the comments.</p><h4>⚠️ Pitfall 3 — Why do my pulled models disappear after a Cloud Run restart?</h4><p>Model data is stored in the container’s local filesystem. If the llm container crashes or is evicted — for example due to a timeout on a large inference request — the pulled model is lost and the pull must be repeated.</p><p>A persistent volume would solve this, but since the llm service is generated entirely from the models: key (with no explicit service spec), there is currently no way to attach one via gcloud run compose. This remains an open limitation at the time of writing.</p><blockquote><em>💡 </em><strong><em>In practice:</em></strong><em> Pitfalls 2 and 3 are related. The </em><em>models: key is the most &quot;magic&quot; part of the compose translation — and magic comes with trade-offs. Expect this area to mature in future SDK releases.</em></blockquote><h4>⚠️ Pitfall 4 — Heads up: services default to `max_instances=1`</h4><p>Both Cloud Run services created by gcloud run compose upare deployed with a maximum of one instance. This is a documented default of the Compose deployment path, not a bug — but it’s a silent ceiling. The first deploy works, the demo looks great, then the second concurrent user hits a cold start while the first is mid-request and you start chasing ghost latency.</p><p>This applies to both the gcp-realease-notes web service and the `llm` GPU service. For the LLM, this is doubly painful: with max_instances=1, you also can’t survive an instance eviction (see <strong>Pitfall 3</strong>).</p><p><strong>Fix</strong> — raise it explicitly after deployment:</p><pre># Update the multi-container service<br>gcloud run services update gcp-release-notes \<br> --max-instances=5 \<br> --region=europe-west1<br><br># Update the LLM service<br>gcloud run services update llm \<br> --max-instances=2 \<br> --region=europe-west1</pre><p>Keep llm modest — GPU quotas are tight and each instance is an L4. Two is usually enough for a demo or small internal tool.</p><blockquote>💡 Nothing in your `compose.yaml` controls this today. Until Compose exposes a `max_instances` knob, the post-deploy `<strong>gcloud run services update</strong>` is the only path.</blockquote><h3>Conclusion</h3><p>gcloud run compose up turns a standard compose.yaml into a fully deployed Cloud Run application. Here&#39;s what to remember:</p><p><strong>The compose.yaml is the single source of truth.</strong> The same file runs locally with Docker Desktop and deploys to Cloud Run with one command. No Dockerfile changes, no Kubernetes manifests.</p><p><strong>Two services, not three.</strong> front and back are co-deployed as sidecars in a single multi-container service. The LLM becomes its own GPU-backed service.</p><p><strong>The </strong><strong>models: key is the bridge.</strong> It connects Docker Model Runner locally and triggers automatic GPU provisioning on Cloud Run. This is what makes the deployment seamless — and why Docker Model Runner matters over Ollama for this workflow.</p><p><strong>Service-to-service communication just works.</strong> http://back:8000 resolves via localhost in the sidecar setup. LLM_URL is auto-rewritten to the HTTPS URL of the llm service.</p><p><strong>The tool is still young.</strong> Model pulls, redeployment of the llm service, and ephemeral model storage are real limitations today. Check section 5 before deploying to production.</p><h4>Teardown</h4><blockquote>If a Googler from the Cloud Run team is reading, please implement this command so that we can clean up easily:</blockquote><pre>gcloud run compose down \<br>  --project=$PROJECT_ID \<br>  --region=europe-west1</pre><p>By the way, for now, you can use the label managed-by: runcompose that all the services created by gcloud run compose up hold.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R2Wp59ctvIaSrYBgD6aGjA.png" /></figure><p>Run this command:</p><pre># Delete them all<br>gcloud run services list \<br>  --project=$PROJECT_ID \<br>  --region=europe-west1 \<br>  --filter=&quot;metadata.labels.managed-by=runcompose&quot; \<br>  --format=&quot;value(metadata.name)&quot; | \<br>  xargs -I {} gcloud run services delete {} \<br>    --project=$PROJECT_ID \<br>    --region=europe-west1 \<br>    --quiet</pre><h4>Source code</h4><p>The full source code for the GCP Release Notes Navigator is available on GitHub: <a href="https://github.com/bricefotzo/gcp-release-notes">github.com/bricefotzo/gcp-release-notes</a></p><p><em>If you found this article helpful, follow me on </em><a href="https://medium.com/"><em>Medium</em></a><em> for more content on Google Cloud, data engineering, and AI applications.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=77d650b39972" width="1" height="1" alt=""><hr><p><a href="https://medium.com/google-cloud/gcloud-run-compose-up-deploy-a-multi-service-gpu-stack-to-cloud-run-from-docker-compose-77d650b39972">`gcloud run compose up`: Deploy a Multi-Service GPU Stack to Cloud Run from Docker Compose</a> was originally published in <a href="https://medium.com/google-cloud">Google Cloud - Community</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Started with Google Cloud Platform: Deploy a Web Server — Nginx]]></title>
            <link>https://bricefotzo.medium.com/getting-started-with-google-cloud-platform-deploy-a-web-server-nginx-e98180fa144c?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/e98180fa144c</guid>
            <category><![CDATA[google-compute-engine]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[web3]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Mon, 28 Apr 2025 22:10:25 GMT</pubDate>
            <atom:updated>2026-02-09T07:29:37.877Z</atom:updated>
            <content:encoded><![CDATA[<h3>Getting Started with Google Cloud Platform: Deploy a Web Server — Nginx</h3><p>In my <a href="https://bricefotzo.medium.com/getting-started-with-google-cloud-platform-core-concepts-and-key-services-e0414b3ff2f4">previous article</a>, we explored the fundamental concepts and key services that make up Google Cloud Platform. We covered everything from the core organizational structure to the essential services across compute, storage, database, networking, and AI/ML categories.</p><p>Now it’s time to roll up our sleeves and get our hands dirty with actual implementation.</p><p>I hope you’re full of energy and willing to master cloud computing, because it will be straightforward! Let’s channel that energy into creating your first Google Cloud project!</p><p>We’ll use Nginx, one of the most popular web servers in the world. <br>It’s lightweight, fast, and perfect for serving static content or acting as a reverse proxy. <br>Many major websites (Netflix, Airbnb, etc.) rely on it.</p><h3>Step 1: Create a Google Cloud Account</h3><p>Every journey begins with a single step, and your cloud journey starts with creating an account:</p><p>Visit <a href="https://cloud.google.com/">cloud.google.com</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pQ1kDNdjqihklKMNRU2eHQ.png" /><figcaption>Landing page</figcaption></figure><ol><li>Click “Get started for free”</li><li>Sign in with your existing Google account or create a new one</li><li>Complete the verification process, including payment information</li></ol><p>Don’t worry about the payment information — Google provides $300 in free credits for new users, valid for 90 days. This is perfect for exploring services without immediate financial commitment.</p><blockquote><strong><em>Pro Tip:</em></strong><em> Even though you’re starting with free credits, treat them like real money. This builds good habits that will serve you well when working with actual budgets. Track your spending from day one.</em></blockquote><h3>Step 2: Set Up Your First Project</h3><p>Projects are the fundamental organizational units in Google Cloud. They serve as isolated containers for your resources, APIs, and billing settings.</p><p>Once your account is active:</p><p>Go to the <a href="https://console.cloud.google.com/">Google Cloud Console</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dEZYN7aqSk13TxspsK-Opw.png" /><figcaption>Google Cloud Console — Home first connecion</figcaption></figure><ol><li>Look for the “Select a project” dropdown near the top left</li><li>Click “New Project”</li><li>Give your project a descriptive name (e.g., “learning-gcp-basics” or “mycompany-website-dev”)</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/871/1*2Xh4HiUxJzwcEpyOluOKRQ.png" /><figcaption>Project name creation</figcaption></figure><p>4. Click “Create”</p><p>When naming projects, I’ve found that following a consistent convention helps immensely as your cloud footprint grows. Consider patterns like:</p><ul><li>[purpose]-[environment]-[optional-identifier]</li><li>[department]-[application]-[environment]</li></ul><p>For example: marketing-website-dev or datascience-forecasting-test.</p><h3>Step 3: Enable APIs for Your Services</h3><p>Here’s an aspect of Google Cloud that often surprises newcomers: services don’t work until you explicitly enable their APIs. This design choice is actually a clever security feature — it prevents accidental usage and billing for services you never intended to use.</p><p>To enable an API:</p><ol><li>Navigate to “APIs &amp; Services” &gt; “Library” in the Console</li><li>Search for the API you need (e.g., “Compute Engine API” for VMs)</li><li>Click on the API and then click “Enable”</li></ol><p>For beginners, I recommend starting with:</p><ul><li>Compute Engine API (for virtual machines)</li><li>Cloud Storage API (for object storage)</li><li>Cloud Logging API (for monitoring)</li></ul><p>These form the backbone of many cloud solutions and provide a good starting point for exploration.</p><p>I recall starting GCP implementation following tutorials, this point was so obvious for some that it was not mentioned and I struggled a lot with not working commands just because of services that were not enabled.</p><h3>Step 4: Set Up the Google Cloud CLI</h3><p>While the web console is user-friendly and great for learning, the command line will eventually become your best friend for efficiency and automation. The Google Cloud CLI (gcloud) gives you powerful control over your resources directly from your terminal.</p><ol><li>Download and install the <a href="https://cloud.google.com/sdk/docs/install">Google Cloud SDK</a> for your operating system</li><li>Open your terminal and run the initialization command:</li></ol><pre>gcloud init</pre><p>3. Follow the prompts to authenticate and select your project</p><p>Why using CLI over the console? CLI commands can fit into one script that you launch once to automate all your provisioning, update or delete where for each resource that you create in console it’s a lot of clicks. It’s more user friendly for beginners but way less reproducible and automation ready.</p><h3>Step 5: Set Up IAM and User Access</h3><p>Even if you’re just starting out solo, understanding Identity and Access Management (IAM) is crucial — especially if you plan to collaborate with others or follow security best practices.</p><p>To add a user to your project:</p><ol><li>Navigate to “IAM &amp; Admin” &gt; “IAM” in the Console</li><li>Click “Add”</li><li>Enter the user’s email address(yours)</li><li>Select appropriate roles based on what they need to do</li></ol><blockquote>For the sake of the tutorial, select the Owner role for your account so that you can do a lot of things.</blockquote><p>5. Click “Save”</p><p>Google Cloud follows the principle of least privilege: grant users only the permissions they need to perform their specific tasks — nothing more.</p><p>After an incident with a junior that accidentally deleted our VMs(because of me that provided admin Compute Engine permissions temporarily)I now follow these IAM best practices religiously:</p><ol><li>Always use purpose-specific roles instead of broad admin access</li><li>Set time limits on temporarily permissions using IAM Conditions</li><li>Regularly audit who has what access to your projects</li><li>Use service accounts for programmatic access rather than personal accounts</li></ol><p>These practices have saved me from numerous potential disasters in professional settings.</p><h3>Step 6: Configure Default Settings</h3><p>Spending a few minutes configuring defaults will save you countless hours of repetitive typing and parameter setting:</p><pre># Set default compute region and zone<br>gcloud config set compute/region us-central1<br>gcloud config set compute/zone us-central1-a</pre><blockquote><strong><em>Region Selection Tip:</em></strong><em> Choose a region close to your target users to minimize latency. For learning purposes, any region works, but for production workloads, geography matters significantly.</em></blockquote><p>You can verify your configuration anytime with:</p><pre>gcloud config list</pre><p>I’ve found that taking the time to set up these defaults, along with creating shell aliases for common commands, dramatically increases productivity when working with GCP daily. For example, setting the region prevent you from using the argument — region for the commands that need it if you have the same for all your resources.</p><h3>Step 7: Create Your First Virtual Machine</h3><p>Now for the exciting part — spinning up your first server in the cloud:</p><pre>gcloud compute instances create my-first-instance \<br>  --machine-type=e2-micro \<br>  --image-family=debian-11 \<br>  --image-project=debian-cloud \<br>  --boot-disk-size=20GB</pre><p>Let’s break down the VM creation command parameters:</p><p>- <strong>machine-type=e2-micro</strong>: This is one of Google Cloud’s economical machine types, featuring 2 shared vCPUs and 1 GB of memory. It’s perfect for learning and light workloads.</p><p>- <strong>image-family=debian-11</strong> and <strong>image-project=debian-cloud</strong>: These parameters specify the operating system. We’re using Debian 11 (also known as “Bullseye”), which is a stable, well-supported Linux distribution.</p><p>- <strong>boot-disk-size=20GB</strong>: This sets the size of your main storage disk. You can adjust this based on your needs — remember, you pay for provisioned space regardless of usage.</p><p>Here is the output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t6_hs5oJtpHukm7iXuiMqA.png" /><figcaption>result of the command to provison a VM</figcaption></figure><p>To connect to your newly created VM:</p><pre>gcloud compute ssh my-first-instance</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S7BHSEbLx32R02mVMuiZSw.png" /><figcaption>ssh terminal of the VM</figcaption></figure><p>You’re now connected to a computer that exists only in Google’s data centers! This is cloud computing made tangible.</p><p>Let’s test some basic commands on your new VM:</p><pre># Check the system information<br>uname -a<br># Check available disk space<br>df -h</pre><p>Let’s deploy our first website on internet manually, using a VM:</p><pre># Install a simple web server<br>sudo apt update &amp;&amp; sudo apt install -y nginx<br># Start the web server<br>sudo systemctl start nginx</pre><p>Now your nginx web server is available:</p><pre>curl localhost:80</pre><p>Here is the result:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>&lt;head&gt;<br>&lt;title&gt;Welcome to nginx!&lt;/title&gt;<br>&lt;style&gt;<br>    body {<br>        width: 35em;<br>        margin: 0 auto;<br>        font-family: Tahoma, Verdana, Arial, sans-serif;<br>    }<br>&lt;/style&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>&lt;h1&gt;Welcome to nginx!&lt;/h1&gt;<br>&lt;p&gt;If you see this page, the nginx web server is successfully installed and<br>working. Further configuration is required.&lt;/p&gt;<br><br>&lt;p&gt;For online documentation and support please refer to<br>&lt;a href=&quot;http://nginx.org/&quot;&gt;nginx.org&lt;/a&gt;.&lt;br/&gt;<br>Commercial support is available at<br>&lt;a href=&quot;http://nginx.com/&quot;&gt;nginx.com&lt;/a&gt;.&lt;/p&gt;<br><br>&lt;p&gt;&lt;em&gt;Thank you for using nginx.&lt;/em&gt;&lt;/p&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>You’ve now deployed this web page on your VM. We can access it using curl in the VM but can we access it outside of the VM?</p><h4>Accessing the VM from the console</h4><p>Go back to the Google Cloud Console and navigate to the VM instances tab of the Compute Engine service:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/669/1*29ET9ZkRBJPM2hXn8RpFGQ.png" /><figcaption>Google Cloud Console — Services</figcaption></figure><p>You’ll see the list of your VMs that looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XedFU9vkWWvOIMxjgB5PIg.png" /><figcaption>Google Compute Engine VM</figcaption></figure><p>When your VM is created, it will have both an internal and external IP address:<br>- The internal IP is used for communication within your Google Cloud network<br>- The external IP allows access from the internet (once properly configured).</p><p>Copy the <strong>External IP</strong> of your VM and paste it into a page of your browser:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QQckqnnQDujOX-Km09vvJA.png" /><figcaption>VM URL into the browser</figcaption></figure><p>Unfortunately, our web page isn’t accessible due to a connection timeout.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FRf3HnrmxelxPVUrHIhkiA.png" /><figcaption>Failed connection to the VM</figcaption></figure><h4>But why?</h4><p>Google Cloud Platform implements the principle of least privilege across all its services, including VM instances. By default, new VMs are not accessible from the internet — a security-first approach that protects your resources.</p><h4>Let’s fix it!</h4><p>Now, let’s configure our web server to be accessible from the internet using its IP address and port.</p><p>Let’s make our web server available on the global internet so that anyone can reach it !</p><p>To see your web server in action, you’ll need to allow HTTP traffic:</p><ol><li>Go to the Google Cloud Console</li><li>Navigate to “VPC Network” &gt; “Firewall”</li><li>Click “Create Firewall Rule”</li><li>Name your rule (e.g., “allow-http”)</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/763/1*HxBjlCnh26whtFslA-amYA.png" /><figcaption>Name of the rule</figcaption></figure><p>5. Set direction of traffic to <strong>Ingress </strong>and Action to <strong>Allow</strong></p><p>6. Set target tags to “http-server”</p><p>7. Add source IP range: 0.0.0.0/0</p><blockquote>“0.0.0.0/0” means “allow from any IP address” and that this is not recommended for production environments</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*B0F72gKlTzesyCU-hnuABQ.png" /><figcaption>Setting direction of traffic and action on match</figcaption></figure><p>8. Select “Specified protocols and ports” and enter “tcp:80”</p><p>9. Click “Create”</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/745/1*L7rdm6gi55XBPX8ZPiKdBQ.png" /><figcaption>TCP Configuration</figcaption></figure><p>Then tag your instance:</p><pre>gcloud compute instances add-tags my-first-instance --tags=http-server</pre><p>Now you can access your web server by navigating to your VM’s external IP address in your browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fJ4DK-8A8H9hdWkJ3dlzSw.png" /><figcaption>Our first web server deployed on GCP — Compute Engine</figcaption></figure><h3>Conclusion</h3><p>We just deployed our first web server on Google Cloud Platform after creating a Virtual Machine using the service Compute Engine in a newly created project. We saw that Google implement the least privilege principle everywhere: APIs are disable dy default and VMs are not connected to internet. To enable the internet traffic we created a Firewall very easily with the service VPC Network.</p><p>So we’ve used 3 services : <strong>IAM </strong>to handle our own permissions, <strong>Compute Engine </strong>to create a VM and deploy a server<strong> </strong>and <strong>VPC Network </strong>to control our network traffic<strong>.</strong></p><p>Stay tuned for the next article in this series, where we’ll explore more advanced Google Cloud Platform features and implementations.</p><p>Until then, happy clouding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e98180fa144c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Started with Google Cloud Platform: Core Concepts and Key Services]]></title>
            <link>https://bricefotzo.medium.com/getting-started-with-google-cloud-platform-core-concepts-and-key-services-e0414b3ff2f4?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/e0414b3ff2f4</guid>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[gcp]]></category>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[infrastructure]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Mon, 28 Apr 2025 03:22:35 GMT</pubDate>
            <atom:updated>2025-04-28T03:22:35.576Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*9-N0jPZ-Fn87u8sU.png" /><figcaption>Google Cloud Platfrom Logo</figcaption></figure><h3>Introduction</h3><p>Tech is moving fast, AI is moving even faster! However, most challenges companies face are still fundamental ones like:</p><ul><li>Building and deploying reliable apps</li><li>Collecting, processing and analyzing internal/external data (big or not)</li><li>Taking advantage of data with Machine Learning/advanced analysis</li><li>And so on…</li></ul><p>One skill that companies consistently demand today is cloud computing expertise for building, deploying, and scaling applications. Among the major cloud providers, Google Cloud Platform (GCP) stands out with its robust infrastructure, advanced data analytics capabilities, and cutting-edge machine learning services — all built on the same infrastructure that powers Google’s own products.</p><p>In this comprehensive guide, we’ll explore the fundamental concepts of Google Cloud Platform, its core services, and how to get started with your first GCP project. Whether you’re a developer, architect, or IT professional looking to expand your cloud expertise, this article will provide you with the foundation you need to navigate the Google Cloud ecosystem confidently.</p><h3>What is Google Cloud Platform?</h3><p>Google Cloud Platform is a suite of cloud computing services that runs on the same infrastructure that Google uses internally for its end-user products, such as Google Search, Gmail, and YouTube. GCP offers a wide range of services including computing, storage, networking, big data, machine learning, and the Internet of Things (IoT).</p><h3>Key Benefits of Google Cloud Platform</h3><ul><li><strong>Global Infrastructure</strong>: GCP operates in 35+ regions and 106+ zones worldwide, enabling you to deploy applications close to your users.</li><li><strong>Security by Design</strong>: Security is built into Google’s infrastructure from the ground up, with layered security measures and encryption by default.</li><li><strong>Innovative Technology</strong>: Access to cutting-edge technologies like TensorFlow, BigQuery, and Spanner that were developed at Google.</li><li><strong>Pay-as-you-go Pricing</strong>: Only pay for the resources you use, with per-second billing for many services.</li></ul><h3>Core Google Cloud Concepts</h3><p>Before diving into specific services, let’s understand some fundamental concepts that are central to working with Google Cloud Platform.</p><h3>Projects, Organizations, and Resource Hierarchy</h3><p>In Google Cloud, resources are organized hierarchically:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/586/0*kJI9NhjlqcwzUBZT" /><figcaption>Google Cloud Hierarchy — from Google Cloud Docs</figcaption></figure><ol><li><strong>Organization</strong>: The root node of the hierarchy, typically representing your company.</li><li><strong>Folders</strong>: Optional containers for grouping projects (e.g., departments or teams).</li><li><strong>Projects</strong>: The basic organizational entity in Google Cloud. Each project is a separate compartment with its own APIs, billing, and access controls.</li><li><strong>Resources</strong>: Individual services and components (VMs, databases, etc.) that live within projects.</li></ol><p>This hierarchy allows for logical organization and enables granular access control and policy management.</p><h3>Identity and Access Management (IAM)</h3><p>Google Cloud IAM is the system that controls who can do what on which resources:</p><ul><li><strong>Who</strong>: Google accounts, service accounts, Google groups, or domains</li><li><strong>What</strong>: Permissions defined in roles</li><li><strong>Which</strong>: Google Cloud resources</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*G_6LQHnph0N7vstP" /><figcaption>IAM Policies — from Google Cloud Docs</figcaption></figure><p>IAM uses a policy that binds <strong>members</strong> to <strong>roles</strong> on <strong>resources</strong>. The three types of roles are:</p><ol><li><strong>Predefined roles</strong>: Curated collections of permissions managed by Google</li><li><strong>Basic roles</strong>: Legacy roles (Owner, Editor, Viewer) with broad permissions</li><li><strong>Custom roles</strong>: User-defined sets of permissions tailored to specific needs</li></ol><p>Understanding IAM is crucial for maintaining security in your cloud environment.</p><h3>Regions and Zones</h3><p>Google Cloud resources are hosted in multiple locations worldwide, divided into:</p><ul><li><strong>Regions</strong>: Geographic areas containing zones (e.g., us-central1, europe-west1)</li><li><strong>Zones</strong>: Deployment areas within a region that are independent of other zones</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*px6gde_ImZ2nE2C_15grLg.png" /><figcaption>Regions and Zones Insights — from Google Cloud About</figcaption></figure><p>When deploying resources, consider factors like latency, availability requirements, and data residency regulations in choosing the appropriate region and zone.</p><h3>Virtual Private Cloud (VPC)</h3><p>A Virtual Private Cloud is a virtual version of a physical network that provides:</p><ul><li><strong>Global networking</strong>: Single VPC can span multiple regions</li><li><strong>Software-defined networking</strong>: No physical networking equipment to manage</li><li><strong>Private connectivity</strong>: Resources can communicate without public IP addresses</li><li><strong>Firewall rules</strong>: Control traffic flow to and from your resources</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/957/0*-tj04OUdEuaETUtc" /><figcaption>Load Balancing architecture —from Google Cloud Docs</figcaption></figure><p>VPCs are the foundation of network security and isolation in Google Cloud.</p><h3>Interacting with Google Cloud Platform</h3><p>Google Cloud provides multiple ways to interact with and manage your cloud resources, each with its own advantages depending on your needs and workflow.</p><h3>Google Cloud Console</h3><p>The <a href="http://console.cloud.google.com/">Cloud Console</a> is a web-based graphical user interface for managing your Google Cloud resources:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WLFHwiQtMH6kKqvGqtQpQQ.png" /><figcaption>Google Cloud Console — The Web UI — Screenshot</figcaption></figure><ul><li><strong>User-friendly</strong>: Intuitive interface with search, navigation, and dashboards</li><li><strong>Visual management</strong>: Graphical representation of resources and their relationships</li><li><strong>Integrated tools</strong>: Built-in Cloud Shell, monitoring dashboards, and logging</li><li><strong>No installation required</strong>: Access from any browser</li></ul><p>The Console is ideal for beginners, visualizing complex architectures, and for tasks that benefit from a graphical interface.</p><h3>Google Cloud SDK and Command Line Interface (CLI)</h3><p>The Google Cloud SDK includes command-line tools for managing Google Cloud resources:</p><ul><li><strong>gcloud</strong>: Main CLI tool for most Google Cloud services</li><li><strong>gsutil</strong>: Specialized tool for Cloud Storage</li><li><strong>bq</strong>: Command-line tool for BigQuery</li></ul><p>The CLI offers several advantages:</p><ul><li>Automation and scripting capabilities</li><li>Faster execution of repetitive tasks</li><li>Integration with CI/CD pipelines</li><li>Precise control over resource configurations</li></ul><pre># Example of creating a VM instance using gcloud CLI<br>gcloud compute instances create my-vm \<br>  --machine-type=e2-medium \<br>  --zone=us-central1-a \<br>  --image-family=debian-11 \<br>  --image-project=debian-cloud</pre><h3>Cloud Shell</h3><p>Cloud Shell is an online development and operations environment accessible from your browser:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hwGOXOnGVxtH40OkYRELoQ.png" /><figcaption>Cloudshell opened — Screenshot</figcaption></figure><ul><li>Browser-based command line with pre-installed Google Cloud SDK</li><li>5GB of persistent disk storage</li><li>Pre-authorized access to your Google Cloud resources</li><li>Built-in code editor and web preview functionality</li></ul><p>Cloud Shell is perfect for when you need CLI access without installing anything locally.</p><h3>Google Cloud APIs</h3><p>For programmatic access, Google Cloud offers comprehensive REST APIs:</p><ul><li>Available for all Google Cloud services</li><li>Client libraries in multiple languages (Python, Java, Go, Node.js, etc.)</li><li>Authentication through service accounts or user credentials</li><li>Fine-grained control over resources</li></ul><pre># Example of using the Python client library to list Compute Engine instances<br>from google.cloud import compute_v1<br><br>def list_instances(project_id, zone):<br>    instance_client = compute_v1.InstancesClient()<br>    instances = instance_client.list(project=project_id, zone=zone)<br>    for instance in instances:<br>        print(f&quot;Instance: {instance.name}&quot;)<br>list_instances(&quot;my-project&quot;, &quot;us-central1-a&quot;)</pre><h3>Infrastructure as Code Tools</h3><p>Google Cloud supports several infrastructure as code (IaC) tools:</p><ul><li><strong>Terraform</strong>: Open-source tool with strong Google Cloud support</li><li><strong>Pulumi</strong>: Modern IaC tool using familiar programming languages</li><li><strong>Ansible, Chef, Puppet</strong>: Configuration management tools with Google Cloud support</li></ul><p>These tools allow you to define your infrastructure in code, enabling version control, repeatability, and consistency across environments.</p><pre># Example Terraform code to create a Cloud Storage bucket<br>provider &quot;google&quot; {<br>  project = &quot;my-project&quot;<br>  region  = &quot;us-central1&quot;<br>}<br>resource &quot;google_storage_bucket&quot; &quot;static_website&quot; {<br>  name          = &quot;my-static-website-bucket&quot;<br>  location      = &quot;US&quot;<br>  force_destroy = true<br>  website {<br>    main_page_suffix = &quot;index.html&quot;<br>    not_found_page   = &quot;404.html&quot;<br>  }<br>}</pre><h3>Mobile App</h3><p>The Google Cloud mobile app allows you to monitor and manage your resources on the go:</p><ul><li>View resource status and performance metrics</li><li>Receive alerts and notifications</li><li>Manage incidents and take basic actions</li><li>Monitor billing and budget alerts</li></ul><p>The app is available for both Android and iOS devices.</p><h3>Essential Google Cloud Services</h3><p>Now that we’ve covered the core concepts, let’s explore the essential services offered by Google Cloud Platform. These services are categorized into several main areas:</p><h3>Compute Services</h3><h4>Compute Engine</h4><p>Google’s Infrastructure as a Service (IaaS) offering provides virtual machines (VMs) with complete control over the operating system, configurations, and installed software. Key features include:</p><ul><li>Custom machine types to optimize cost and performance</li><li>Preemptible VMs for cost savings on interruptible workloads</li><li>Live migration during host maintenance events</li><li>Per-second billing with sustained use discounts</li></ul><h4>Google Kubernetes Engine (GKE)</h4><p>GKE is a managed Kubernetes service that simplifies container orchestration:</p><ul><li>Automated cluster management and scaling</li><li>Multi-cluster support and multi-region deployments</li><li>Integration with Google Cloud services</li><li>Auto-upgrade and auto-repair capabilities</li></ul><h4>Cloud Run</h4><p>Cloud Run is a fully managed compute platform for deploying and scaling containerized applications:</p><ul><li>Serverless: only pay for the resources you use</li><li>Built on Knative, an open-source serverless platform</li><li>Supports any language, any library, any binary</li><li>Automatic scaling from zero to handle traffic spikes</li></ul><h4>App Engine</h4><p>App Engine is a Platform as a Service (PaaS) for building scalable web applications and APIs:</p><ul><li>Standard environment with preconfigured runtimes</li><li>Flexible environment for custom runtime support</li><li>Automatic scaling and high availability</li><li>Built-in services like Memcache, Task Queues, and Cron jobs</li></ul><h3>Storage Services</h3><h4>Cloud Storage</h4><p>Object storage for storing and accessing unstructured data:</p><ul><li>Multiple storage classes (Standard, Nearline, Coldline, Archive)</li><li>Global edge caching with Cloud CDN integration</li><li>Strong consistency and durability guarantees</li><li>Lifecycle management policies</li></ul><pre># Create a storage bucket with gcloud<br>gcloud storage buckets create gs://my-unique-bucket-name \<br>  --location=us-central1 \<br>  --default-storage-class=STANDARD</pre><h4>Persistent Disk and Local SSD</h4><p>Block storage volumes for use with Compute Engine and GKE:</p><ul><li>Persistent Disks: durable network storage</li><li>Local SSDs: high-performance, temporary local storage</li><li>Snapshots for point-in-time backups</li><li>Regional persistent disks for high availability</li></ul><h4>Filestore</h4><p>Managed file storage for applications that require a file system interface:</p><ul><li>NFSv3 compatible</li><li>High-performance for file-based workloads</li><li>Fully managed and scalable</li><li>Integrated with GKE and Compute Engine</li></ul><h3>Database Services</h3><h4>Cloud SQL</h4><p>Managed relational database service for MySQL, PostgreSQL, and SQL Server:</p><ul><li>Automated backups, replication, and failover</li><li>Automatic storage increases</li><li>Point-in-time recovery</li><li>Private connectivity through VPC</li></ul><h4>Cloud Spanner</h4><p>Globally distributed, horizontally scalable relational database with strong consistency:</p><ul><li>99.999% availability SLA</li><li>Combines the benefits of relational structure with horizontal scale</li><li>Automatic sharding and replication</li><li>Strong consistency across regions and continents</li></ul><h4>Firestore and Datastore</h4><p>Flexible, scalable NoSQL database for mobile, web, and server development:</p><ul><li>Real-time updates and offline support (Firestore)</li><li>Automatic scaling with no downtime</li><li>ACID transactions</li><li>Powerful querying capabilities</li></ul><h4>Bigtable</h4><p>Petabyte-scale, wide-column NoSQL database service:</p><ul><li>Designed for high throughput and low latency</li><li>Seamless scaling without downtime</li><li>Open-source HBase API compatibility</li><li>Ideal for time-series data, IoT, and analytical workloads</li></ul><h4>Memorystore</h4><p>Fully managed in-memory data store services compatible with Redis and Memcached:</p><ul><li>Sub-millisecond latency</li><li>High availability configuration options</li><li>Automatic failover</li><li>Scalable performance</li></ul><h3>Networking Services</h3><h4>Virtual Private Cloud (VPC)</h4><p>Software-defined networking with global reach:</p><ul><li>Global private network for your GCP resources</li><li>Subnet creation and IP address management</li><li>VPC Network Peering for connectivity between VPCs</li><li>Shared VPC for multi-project networking</li></ul><h4>Cloud Load Balancing</h4><p>Distribute traffic across instances and regions:</p><ul><li>Global and regional load balancing</li><li>HTTP(S), TCP/SSL, and UDP load balancing</li><li>Autoscaling and health checks</li><li>Seamless integration with Cloud CDN</li></ul><h4>Cloud CDN</h4><p>Content delivery network built on Google’s global edge network:</p><ul><li>Accelerate content delivery</li><li>Reduce serving costs</li><li>Offload origin servers</li><li>Support for HTTP/2 and QUIC</li></ul><h4>Cloud DNS</h4><p>Scalable, reliable, and managed authoritative DNS service:</p><ul><li>100% uptime SLA</li><li>Global anycast name servers</li><li>Low-latency DNS lookups</li><li>Integrated with Cloud Console and gcloud CLI</li></ul><h3>Big Data and Analytics</h3><h4>BigQuery</h4><p>Serverless, highly scalable data warehouse with built-in machine learning:</p><ul><li>Petabyte-scale querying</li><li>Separation of storage and compute</li><li>Real-time analytics</li><li>Built-in ML capabilities with BigQuery ML</li></ul><pre>-- Example BigQuery SQL query<br>SELECT <br>  date,<br>  COUNT(DISTINCT user_id) AS daily_active_users<br>FROM <br>  `project_id.dataset.events_table`<br>WHERE <br>  date BETWEEN &#39;2023-01-01&#39; AND &#39;2023-01-31&#39;<br>GROUP BY <br>  date<br>ORDER BY <br>  date</pre><h4>Dataflow</h4><p>Unified stream and batch data processing service:</p><ul><li>Based on Apache Beam</li><li>Horizontal autoscaling</li><li>Built-in integration with other Google Cloud services</li><li>Real-time analytics and ETL</li></ul><h4>Pub/Sub</h4><p>Messaging service for event ingestion and delivery:</p><ul><li>At-least-once message delivery</li><li>Push and pull delivery models</li><li>Global by default</li><li>Supports millions of messages per second</li></ul><h4>Dataproc</h4><p>Managed Hadoop and Spark service:</p><ul><li>Quick cluster creation</li><li>Integration with other Google Cloud services</li><li>Custom machine types</li><li>Per-second billing</li></ul><h3>Artificial Intelligence and Machine Learning</h3><h4>Vertex AI</h4><p>Unified platform for building, deploying, and scaling ML models:</p><ul><li>Agent builder, Model registry, Notebooks, etc…</li><li>Custom training for maximum flexibility</li><li>MLOps tools for deployment and monitoring</li><li>Feature Store for feature management</li></ul><h4>Conclusion</h4><p>In this introductory guide, we’ve covered the fundamental concepts of Google Cloud Platform, from its organizational structure and core services to the various ways you can interact with the platform. These building blocks form the foundation for any cloud journey(Data Engineer, Data Scientist, Developer, etc…), regardless of your specific use case or industry.</p><p>I wrote this article because of the growing demand of cloud computing skills and students/juniors that are asking me a lot where to start their cloud journey to learn efficiently. I hope this can help have a first overview before diving into more specific areas.</p><p>See you for the next ones on Cloud Computing with GCP.</p><p>If you have questions, feel free to reach on <a href="https://www.linkedin.com/in/bricefotzo/">LinkedIn</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e0414b3ff2f4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Testing LLMs and Prompts using Testcontainers and Ollama in Python]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/testing-llms-and-prompts-using-testcontainers-and-ollama-in-python-81e8f7c18be7?source=rss-cf7915a0bf5e------2"><img src="https://cdn-images-1.medium.com/max/1280/1*0V5_y-df0ktk86jmxvHq_Q.png" width="1280"></a></p><p class="medium-feed-snippet">An easy-to-use testing framework for LLMs and prompts using Python</p><p class="medium-feed-link"><a href="https://towardsdev.com/testing-llms-and-prompts-using-testcontainers-and-ollama-in-python-81e8f7c18be7?source=rss-cf7915a0bf5e------2">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/testing-llms-and-prompts-using-testcontainers-and-ollama-in-python-81e8f7c18be7?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/81e8f7c18be7</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Fri, 02 Aug 2024 09:07:45 GMT</pubDate>
            <atom:updated>2024-08-29T12:15:51.591Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[5 minutes to create your own Spark Cluster: A Step-by-Step guide]]></title>
            <link>https://blog.dataengineerthings.org/5-minutes-to-create-your-own-spark-cluster-a-step-by-step-guide-31afe5f84641?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/31afe5f84641</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[spark]]></category>
            <category><![CDATA[data]]></category>
            <category><![CDATA[cluster]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Fri, 05 Jul 2024 06:41:19 GMT</pubDate>
            <atom:updated>2024-07-26T16:42:31.741Z</atom:updated>
            <content:encoded><![CDATA[<h3>How to create a quickly create a Spark Cluster for free?</h3><h4>Set up a local spark cluster in 2 commands using Docker Compose</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5uZN0t8iHSHXmHac6snA7g.png" /><figcaption>Free Spark Cluster with Docker and Docker compose</figcaption></figure><p>Are you a developer, data scientist, or student looking to harness the power of Apache Spark without the complexity of a full-scale cluster?</p><p>This tutorial will guide you through creating a local Spark cluster using Docker, streamlining your development process and reducing overhead.</p><p>Why use Docker for a local Spark Cluster?</p><ul><li><strong>Develop and test locally:</strong> Run Spark jobs on your machine without a full-scale cluster.</li><li><strong>Simulate production environments</strong>: Mirror real-world settings for consistent development and deployment.</li><li><strong>Learn and experiment</strong>: Set up a functional Spark environment to practice big data processing techniques.</li></ul><p>By leveraging Docker volumes, we’ll ensure your data and configurations persist, making your local cluster a robust tool for continuous development and testing.</p><p>Let’s dive in and discover how Docker can make working with Spark easier and more efficient!</p><h3>Prerequisites</h3><p>Before we dive into setting up our local Spark cluster using Docker, ensure you have the following prerequisites :</p><ul><li><strong>Docker</strong>: you need Docker installed and running on your machine. You can download it from Docker’s official site.</li><li><strong>Basic knowledge of Docker and Spark</strong>: Familiarity with Docker commands and a basic understanding of Apache Spark will help you follow this tutorial more effectively, even if running the commands will just work.</li></ul><h3>Why use a non-root container?</h3><p>Non-root container images add an extra layer of security and are generally recommended for production environments. However, because they run as a non-root user, privileged tasks are typically off-limits.</p><h4>Configuration</h4><p><strong>Custom Docker image</strong></p><p>Create a new file named Dockerfile with the following content:</p><pre># Use the bitnami Spark image as it comes pre-configured with necessary Spark components<br>FROM bitnami/spark:latest<br># Add a user to run the application<br>RUN useradd -ms /bin/bash spark<br># Create directories for data and jobs<br>RUN mkdir -p /data/inputs /data/outputs /jobs<br># Set the ownership of the directories to the spark user<br>RUN chown -R spark:spark /opt/bitnami/spark /data /jobs<br># Set the user to run the application<br>USER spark</pre><p>We create and set up a new user named spark to run the application to ensure that the container runs as a non-root user.</p><blockquote><em>It’s a good practice to run containers as </em><strong><em>non-root users</em></strong><em> to enhance security and reduce the risk of privilege escalation attacks. For example, if a container is compromised, the attacker will have limited access to the host system.</em></blockquote><h3>Start the cluster</h3><p>Docker Compose is a tool for defining and running multi-container Docker applications. It uses a YAML file to configure the application’s services, networks, and volumes, making it easier to manage and scale your application.</p><p><strong>Environment variables</strong> Customizable environment variables</p><ul><li><strong>SPARK_MODE</strong>: Spark cluster mode to run (can be master or worker).</li><li><strong>SPARK_MASTER_URL</strong>: Url where the worker can find the master. Only needed when spark mode is worker.</li></ul><p>Create a new file named docker-compose.yml with the following content:</p><pre>services:<br>  spark-master:<br>    build:<br>      context: .<br>    container_name: spark-master<br>    hostname: spark-master<br>    networks:<br>      - spark-network<br>    ports:<br>      - &quot;8080:8080&quot;<br>      - &quot;7077:7077&quot;<br>    volumes:<br>      - spark-inputs:/data/inputs<br>      - ./data/outputs:/data/outputs<br>      - ./jobs:/jobs<br>    environment:<br>      - SPARK_MODE=master<br>  spark-worker:<br>    build:<br>      context: .<br>    hostname: spark-worker<br>    networks:<br>      - spark-network<br>    environment:<br>      - SPARK_MASTER=spark://spark-master:7077<br>      - SPARK_MODE=worker<br>    volumes:<br>      - spark-inputs:/data/inputs<br>      - ./data/outputs:/data/outputs<br>      - ./jobs:/jobs  <br>networks:<br>  spark-network:<br>    driver: bridge<br>volumes:<br>  spark-inputs:</pre><p>This configuration file defines two services: spark-master and spark-worker, each running the custom-spark image. The spark-master service runs in master mode, while the spark-worker service runs in worker mode and connects to the spark-master service.</p><p>To start the Spark cluster using Docker Compose, run the following command:</p><pre>docker-compose up -d</pre><p>This command starts the Spark cluster using the configuration defined in the docker-compose.yml file.</p><p>To start only the master, you can use the command:</p><pre>docker-compose up -d spark-master</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/834/1*ppBBNVb4oV5cwpvw2HfkHw.png" /><figcaption>Spark master started with compose</figcaption></figure><p>To start only the workers, the number of workers you want to start, you can use the command:</p><pre>docker-compose up -d --scale spark-worker=2</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/966/1*QdfcBkeJNbJciyleHM96hA.png" /><figcaption>2 Spark workers started with compose</figcaption></figure><p>This command starts two worker nodes in the Spark cluster.</p><h3>Running a Spark Job</h3><ul><li><strong>Prepare folder structure</strong></li></ul><p>Create the following directories to store the inputs, outputs, and job scripts:</p><pre>mkdir -p ./data/inputs ./data/outputs ./jobs</pre><ul><li><strong>Create the script</strong></li></ul><p>We’ll create a simple PySpark script that reads the Iris dataset, performs a basic data manipulation, and writes the results back to the spark-data volume.</p><p>Create a new file named spark_job.py with the following content:</p><pre>from pyspark.sql import SparkSession  <br># Initialize Spark session <br>spark = SparkSession.builder.appName(&quot;IrisDataProcessing&quot;).getOrCreate()  <br># Read the dataset <br>df = spark.read.csv(&quot;/data/inputs/iris.csv&quot;, inferSchema=True, header=False)  <br># Rename columns <br>columns = [&quot;sepal_length&quot;, &quot;sepal_width&quot;, &quot;petal_length&quot;, &quot;petal_width&quot;, &quot;species&quot;] <br>df = df.toDF(*columns)  <br># Perform a basic data manipulation: calculate average sepal length by species <br>avg_sepal_length = df.groupBy(&quot;species&quot;).avg(&quot;sepal_length&quot;)  <br># Write the results back to the Docker volume <br>avg_sepal_length.write.csv(&quot;/data/outputs/avg_sepal_length_by_species&quot;)  <br># Stop the Spark session <br>spark.stop()</pre><ul><li><strong>Getting the data</strong></li></ul><p>Let’s download the Iris dataset from the UCI Machine Learning Repository or any other source you prefer.</p><pre>wget https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data -O data/inputs/iris.csv</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mgMFcy4kEXD_lEd3vRkR7Q.png" /><figcaption>Iris data downloading</figcaption></figure><ul><li><strong>Copy the data and the script to the cluster</strong></li></ul><p><strong>Input data</strong>: Copy the Iris dataset to the Spark master container.</p><pre>docker cp data/inputs/iris.csv spark-master:/data/inputs/iris.csv</pre><p>That’s the output you should see:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*e9pTa8nXKEiOMt4086W3zw.png" /><figcaption>Result of docker copy</figcaption></figure><p><strong>PySpark script</strong>: we don’t need to copy the script to the master container, we can just move it to the jobs folder that is already mounted to the master container.</p><pre>cp spark_job.py .jobs/spark_job.py</pre><p>Verifying Data Accessibility To verify that the data is accessible, you can start an interactive session within the Spark master container:</p><pre>docker exec -it spark-master ls /data/inputs /jobs</pre><p>Inside the container, list the contents of the /data/inputs and /jobs directory to ensure the dataset is present.</p><p>You should see this output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t4OEJopq732uxMMgLCUalw.png" /><figcaption>Listing of files on the container</figcaption></figure><h3>Job Execution</h3><p>Execute the script using the Spark master container:</p><pre>docker exec -it spark-master spark-submit \<br>  --master spark://spark-master:7077 \<br>  --deploy-mode client /jobs/spark_job.py</pre><p>This command runs the PySpark script on the Spark cluster, reading the dataset from the volume, processing it, and writing the results back to the volume.</p><p>The output should look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*baScwKO71rqZKTbrJ4HbBQ.png" /><figcaption>Spark job results</figcaption></figure><p>Once the job is finished, you can check the results:</p><pre>docker exec -it spark-master ls /data/outputs/avg_sepal_length_by_species</pre><p>You should see the output files in the avg_sepal_length_by_species directory.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T6iC1FegoeEI7SCR43P2oQ.png" /><figcaption>Files generated by the spark job</figcaption></figure><p>The output files contain the results of the Spark job, showing the average sepal length by species. You can display the contents of the output files to view the results:</p><pre>docker exec -it spark-master cat /data/outputs/avg_sepal_length_by_species/part-00000-9b270837-872e-4a34-bb5b-4cb6d52044be-c000.csv</pre><blockquote><em>Warning: The output file name may vary depending on the Spark job execution. Replace part-00000–9b270837–872e-4a34-bb5b-4cb6d52044be-c000.csv with the actual file name in your output directory.</em></blockquote><h3>Tips and Tricks</h3><ul><li>Alias the spark-submit command: To simplify running Spark jobs, you can create an alias for the spark-submit command. For example, you can add the following line to your .bashrc or .bash_profile file:</li></ul><pre>alias spark-submit=&#39;docker exec -it spark-master spark-submit --master spark://spark-master:7077 --deploy-mode client&#39;</pre><p>With your cluster up and running, if you run the command:</p><pre>spark-submit /jobs/spark_job.py</pre><p>It will execute the Spark job on the cluster without needing to specify the master URL each time.</p><h3>Stopping the Cluster</h3><p>To stop the Spark cluster, you can use the following commands:</p><pre>docker-compose down</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/759/1*h7dajoFUD3LrOeD1k6ttCA.png" /><figcaption>Network and containers stopped and removed</figcaption></figure><p>Setting up a local Spark cluster using <strong>Docker</strong> can streamline your development process, enabling you to test Spark jobs, simulate production environments, and experiment with Spark features in a controlled environment.</p><p>By using <strong>Docker volumes</strong>, you can ensure that your data and configurations persist across restarts, making your local cluster a robust tool for continuous development and testing.</p><p>This approach is beneficial not only for <strong>Data Engineers</strong> but also for <strong>Data Scientists</strong> who work with Scala or PySpark. It allows for incremental development without heavily utilizing resources from a real cluster on any cloud platform, as it was the case in my previous job.</p><p>You can find the entire code <a href="https://github.com/bricefotzo/tutos-docker/blob/main/spark-tuto">there</a>.</p><p><a href="https://www.linkedin.com/in/bricefotzo">Let’s connect!</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=31afe5f84641" width="1" height="1" alt=""><hr><p><a href="https://blog.dataengineerthings.org/5-minutes-to-create-your-own-spark-cluster-a-step-by-step-guide-31afe5f84641">5 minutes to create your own Spark Cluster: A Step-by-Step guide</a> was originally published in <a href="https://blog.dataengineerthings.org/">Data Engineer Things</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[reqs-check: The Python package I wish I had known about earlier]]></title>
            <link>https://towardsdev.com/the-python-package-i-wish-i-had-known-about-earlier-reqs-check-e3d2e961c2ac?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/e3d2e961c2ac</guid>
            <category><![CDATA[requirements]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[pip]]></category>
            <category><![CDATA[dependencies]]></category>
            <category><![CDATA[development]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Sun, 30 Jun 2024 23:22:49 GMT</pubDate>
            <atom:updated>2024-07-01T16:49:03.654Z</atom:updated>
            <content:encoded><![CDATA[<p>Check your requirements.txt files. Compare them and find duplicate packages.</p><p>Ensuring your Python project works smoothly, especially the complex ones, means making sure all dependencies are present with the correct versions. Most projects use a requirements.txt file for this.</p><p>In large or complex projects, I’ve often faced issues with this file — especially when updating from another file, dealing with multi-projects in a single repo, or managing conflicts.</p><p>In this article, I’ll introduce you to reqs-check, a tool designed to help simplify dependency management. We’ll explore its benefits, how to use it, and how to integrate it into your development workflow, including pre-commit hooks and CI/CD pipelines.</p><h3>Why use reqs-check?</h3><p>In complex projects, dependency issues can arise from multiple sources:</p><ul><li>Inconsistency between requirements files of different branches/workspaces of the same project</li><li>Duplicate packages with different versions</li><li>Missing packages across different environments</li><li>Inconsistent package versions causing unexpected behaviors</li></ul><p>reqs-check helps you identify and resolve these issues efficiently, ensuring smoother development and deployment processes.</p><h3>Advantages of reqs-check</h3><ol><li><strong>Easy comparison</strong>: Quickly compare multiple dependencies files to spot differences and inconsistencies.</li><li><strong>Duplicate detection</strong>: Identify and resolve duplicate packages within your dependencies files.</li><li><strong>Automation ready</strong>: Easily integrate with pre-commit hooks and CI/CD pipelines for automated checks.</li><li><strong>Extensible</strong>: Future plans include support for additional file formats, security checks, and detailed reporting.</li></ol><h3>Installing reqs-check</h3><p>Getting started with reqs-check is simple. You can install it using pip:</p><pre>pip install reqs-check</pre><h3>How to use reqs-check?</h3><h3>Basic usage</h3><p>reqs-check provides a command-line interface for checking and comparing dependencies files. Here&#39;s a breakdown of its usage:</p><pre>usage: reqs-check [-h] [--only-diff] [--find-duplicates] F [F ...]</pre><h3>Positional arguments:</h3><ul><li>F: Paths to the dependencies files</li></ul><h3>Options:</h3><ul><li>-h, --help: Show this help message and exit</li><li>--only-diff: <strong>Only show packages</strong> with differing versions</li><li>--find-duplicates: <strong>Find duplicate packages</strong> in the dependencies files</li></ul><h3>Basic Commands</h3><p>reqs-check provides several useful commands to help manage your dependencies:</p><ol><li><strong>Compare dependencies files</strong>: Compare multiple dependencies files to find differences.</li></ol><pre>reqs-check requirements1.txt requirements2.txt</pre><p><strong>2. Show only differing packages</strong>: Display only the packages with differing versions between files.</p><pre>reqs-check --only-diff requirements1.txt requirements2.txt</pre><p><strong>3. Find duplicate packages</strong>: Identify duplicate packages within your dependencies files.</p><pre>reqs-check --find-duplicates requirements.txt </pre><h3>Examples</h3><p>Let’s dive into a practical example. Consider two files:</p><p><strong>requirements1.txt</strong></p><pre>pandas==1.1.5<br>numpy==1.19.5<br>scipy~=1.5.4<br>matplotlib==3.3.4</pre><p><strong>requirements2.txt</strong></p><pre>pandas==1.1.5<br>numpy==1.19.3<br>matplotlib==3.2.1<br>requests&gt;=2.24.0<br>scipy==1.5.4<br>numpy==1.19<br>requests</pre><p>To compare these files, you would run:</p><pre>reqs-check requirements1.txt requirements2.txt</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pq0otFxHDJtxw9N7sYvmLg.png" /><figcaption>Comparison of the packages</figcaption></figure><p>To focus only on the packages with differing versions, use:</p><pre>reqs-check --only-diff requirements1.txt requirements2.txt</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mu8i9OgxIiQGcgablwI2SA.png" /><figcaption>Only the packages with differences are displayed</figcaption></figure><p>And to find duplicates within the files:</p><pre>reqs-check --find-duplicates requirements1.txt requirements2.txt</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NU6gXWw3sXmu-7AkSoDEAA.png" /><figcaption>Packages numpy and requests are dupliacted</figcaption></figure><blockquote>As you can see, we can look for duplicates in many files at time.</blockquote><h3>Integrating reqs-check into your workflow</h3><h3>Pre-commit Hooks</h3><p>Pre-commit hooks can catch issues before they make it into your version control system. Here’s how you can integrate reqs-check into your pre-commit workflow.</p><p>Let’s take this requirements.txt file as an example:</p><pre>pandas==1.1.5<br>numpy==1.19.3<br>matplotlib==3.2.1<br>scipy==1.5.4<br>numpy<br>requests&gt;=2.24.0</pre><blockquote>The package numpy is listed twice there and we’ll try to commit it into our repository.</blockquote><p>Let’s move in a git repository.</p><ol><li>Create the file dev/requirements.txt and add the content of the example</li><li>Create a .pre-commit-config.yaml file in your project root and add this:</li></ol><pre>repos:<br>-   repo: local<br>    hooks:<br>    -   id: reqs-check<br>        name: reqs-check<br>        entry: reqs-check --find-duplicates<br>        language: system<br>        files: ^.*requirements.*\.txt$</pre><p>3. Install pre-commit and set up the hook:</p><pre>pip install pre-commit</pre><p>4. Add the changes to git:</p><pre>git add .pre-commit-config.yaml dev/requirements.txt</pre><p>5. Commit the changes</p><pre>git commit -m &quot;Add the dependencies&quot;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IQoxjemq8XsWUym-PQXNDA.png" /><figcaption>Failed commit of the problematic requirements.txt</figcaption></figure><p>7. Fix and resolve</p><p>Let’s fix it, removing the non versioned occurrence of numpy at the line 5</p><pre>pandas==1.1.5<br>numpy==1.19.3<br>matplotlib==3.2.1<br>scipy==1.5.4<br>requests&gt;=2.24.0</pre><p>Add the changes and commit again:</p><pre>git add dev/requirements.txt<br>git commit -m &quot;Add the dependencies&quot;</pre><p>There is the result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mQA7WFQg6c5d2dqmhN1drw.png" /><figcaption>Commit the fixed requirements.txt</figcaption></figure><h3>CI/CD Integration</h3><p>Automating dependency checks in your CI/CD pipeline ensures consistent and reliable deployments. Here’s an example using GitHub Actions:</p><ol><li>Create a workflow file .github/workflows/dependency-check.yml:</li></ol><pre>name: Dependency Check<br><br>on: [push, pull_request]<br>jobs:<br>  dependency-check:<br>    runs-on: ubuntu-latest<br>    steps:<br>    - name: Checkout code<br>      uses: actions/checkout@v2<br>    - name: Set up Python<br>      uses: actions/setup-python@v2<br>      with:<br>        python-version: &#39;3.x&#39;<br>    - name: Install dependencies<br>      run: |<br>        python -m pip install --upgrade pip<br>        pip install reqs-check<br>    - name: Run reqs-check<br>      run: reqs-check --find-duplicates requirements.txt</pre><p>This workflow will run reqs-check on every push or pull request, ensuring that any issues with dependencies are caught early.</p><h3>Future Developments</h3><p>The current version of reqs-check focuses on comparing and cleaning up dependencies files, but we have exciting plans for the future:</p><ul><li><strong>Linting for best practices</strong>: Ensure your dependencies files follow best practices.</li><li><strong>Support for additional formats</strong>: Including Pipfile and pyproject.toml.</li><li><strong>Security checks</strong>: Identify and address vulnerabilities in your dependencies.</li><li><strong>Detailed reports</strong>: Generate comprehensive reports in formats like JSON and HTML.</li><li><strong>CI/CD Integration</strong>: Enhanced support for various CI/CD systems.</li></ul><h3>Conclusion</h3><p>Managing dependencies is a critical part of any Python project, and reqs-check provides a quite great toolset to make this task easier and more efficient. By integrating reqs-check into your workflow, you can ensure consistency, avoid conflicts, and maintain a healthier codebase.</p><p>Try out reqs-check today and see how it can simplify your dependency management process!</p><p>Don’t hesitate to provide some feedbacks on this package and how helpful it can be!</p><h3>About me</h3><p>You can follow me, contact me, ask questions or whatever I can help you for:</p><ul><li><a href="https://github.com/bricefotzo">GitHub</a></li><li><a href="https://www.linkedin.com/in/bricefotzo">Linkedin</a></li><li><a href="https://bricefotzo.medium.com/">Medium</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e3d2e961c2ac" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/the-python-package-i-wish-i-had-known-about-earlier-reqs-check-e3d2e961c2ac">reqs-check: The Python package I wish I had known about earlier</a> was originally published in <a href="https://towardsdev.com/">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to copy files into a Docker container without using the COPY command]]></title>
            <link>https://towardsdev.com/how-to-copy-files-into-a-docker-container-without-using-the-copy-command-04dacd58c158?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/04dacd58c158</guid>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[data]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Wed, 12 Jun 2024 19:50:24 GMT</pubDate>
            <atom:updated>2024-07-08T18:22:45.896Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’ve ever written a Dockerfile, you’ve likely used the <strong>COPY</strong> command to transfer files and folders into your container.</p><p>While useful, there are times when you need to add files to a running container without stopping it or rebuilding the image. Let’s explore alternative methods for transferring files into your Docker container.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DcLwFNoISGlOO4pAW5lhag.png" /><figcaption>Copy of files from host to container</figcaption></figure><blockquote>This guide is ideal for any <strong>Data Guy </strong>or <strong>Software Engineers</strong> that want to develop in a container or try and update code with various data without stoping their container.</blockquote><p>Let’s go!</p><h3>Prerequisites</h3><p>Before starting, make sure you have:</p><ul><li>Basic understanding of Docker (this <a href="https://docker-curriculum.com/">tutorial</a> can help)</li><li><a href="https://docs.docker.com/get-docker/">Docker installed</a> on your machine</li></ul><h3>Getting Started</h3><p>Here are <strong>3 methods</strong> to copy files into a running container without using the COPY command in your Dockerfile:</p><ol><li>Using docker cpcommand</li><li>Using bind mounts</li><li>Using wget or curl</li></ol><h4>Preparations</h4><p>Before diving into the methods, let’s do some preparation:</p><ol><li>Download this csv <a href="https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data">data</a> that we’ll use and save it to your workspace</li><li>Run a <strong>FastAPI</strong> container where we’ll push our data and check their presence using a specific endpoint.</li></ol><p><strong>Pull the Docker image for tutorial</strong></p><pre>docker pull ghcr.io/bricefotzo/docker-init-example:main</pre><p><strong>Run the container</strong></p><pre>docker run -d --name my-api -p 8000:8000 ghcr.io/bricefotzo/docker-init-example:main</pre><p>Navigating to the URL localhost:8000 in your browser, you’ll see this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uwfC5cRDD1k1vRLvzKWt6A.png" /><figcaption>The 3 endpoints exposed by the image docker-init-example:main</figcaption></figure><p>The one we’ll use to check data is the second(/tree).</p><p>You can try this endpoint to check what is inside the folder of the app: app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0COo83zshjMCSypSraSImg.png" /><figcaption>Listing of files and folders present in the folder <strong>/app</strong></figcaption></figure><p>As you can see, listing the directory of the project(/app), we have <strong>4 files</strong> and <strong>1 folder</strong>. In the following methods, we’ll use different methods to check data availability.</p><p>Let’s start transferring files !</p><h3>Method 1: Using docker cp Command</h3><p>With my-apicontainer running, we’ll add data in it without stopping it or rebuilding the image.</p><p>The docker cp command allows you to copy files or directories between the host and a container&#39;s filesystem, even after the container is running.</p><h4><strong>How to use docker cp:</strong></h4><blockquote><strong>Usage:</strong></blockquote><blockquote>docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-</blockquote><blockquote>docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH</blockquote><p>Copy a file from host to container:</p><pre>docker cp ./iris.csv my-api:/app<br>#Successfully copied 6.66kB to my-api:/app</pre><p>Copy a directory from host to container:</p><pre>docker cp ./data my-api:/app <br>#Successfully copied 12.8kB to my-api:/app</pre><p>As you can see, 2 items were added: iris.csv and data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w5KeL2gZsEISyxUUVA03pw.png" /><figcaption>Files and directories after copying iris.csv and data</figcaption></figure><p>Checking in /app/data, we have 2 files: iris.csv and iris2.csv.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cioFIsLfy7RAWO4dpjnWEw.png" /><figcaption>Files under the directory <strong>/app/data</strong></figcaption></figure><p>Copy a file from container to host:</p><p>Let’s copy the file main.py into our host and let’s check what is inside.</p><pre>docker cp my-api:/app/main.py ./<br>#Successfully copied 2.56kB to /Users/&lt;username&gt;/workspace/docker-init-example/data/./</pre><h4><strong>When to use docker cp :</strong></h4><ul><li>When you need to update files in a running container without rebuilding the image.</li><li>For temporary changes or debugging purposes.</li></ul><h3>Method 2: Using Bind Mounts</h3><p>Bind mounts allow you to mount a directory from the host filesystem into the container. This method provides a direct mapping between the host and container, so changes in the host directory are immediately reflected in the container and vice versa.</p><h4><strong>How to use bind mounts:</strong></h4><p>Create a workspace on our host and add some files:</p><pre>mkdir api-data<br>touch api-data/data1.csv<br>touch api-data/data2.csv</pre><p>Start another API container with volume mounts:</p><pre>docker run -d --name my-api2 -v ./api-data:/app/data/api -p 8001:8000 ghcr.io/bricefotzo/docker-init-example:main</pre><p>The container is named my-api2 and the volume bind is done between the local folder api-data that we just created and the container folder /app/data/api.</p><blockquote><strong>Note:</strong> The port binding is changed to avoid conflict with the previous container. You can clean up the previous one and continue using the same container name and port if you want.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iRBAfHHUk3FPUiwa_2Khog.png" /></figure><p>Let’s just add another file into the local directory api-data and check the content of /app/data/api.</p><pre>touch api-data/data3.csv</pre><p>Let’s check:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DVaDBz7honRT4QxSUJHuNA.png" /></figure><h4><strong>When to use bind mounts:</strong></h4><ul><li>For development environments where you want to reflect code changes in real-time without rebuilding the image.</li><li>When you need persistent storage that directly maps to the host filesystem.</li></ul><h3>Method 3: Using wget or curl</h3><p>Sometimes, it’s more efficient to download files directly from the internet into your container using tools like wgetor curl.</p><h4><strong>How to use wget or curl:</strong></h4><p>Download a file using <strong>docker exec</strong> and <strong>wget</strong>:</p><pre>docker exec -it my-api2 /bin/sh -c &quot;wget https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data -O /app/data/api/iris.csv&quot;</pre><p><strong>Output:</strong></p><pre>--2024-06-12 18:29:52--  https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data<br>Resolving archive.ics.uci.edu (archive.ics.uci.edu)... 128.195.10.252<br>Connecting to archive.ics.uci.edu (archive.ics.uci.edu)|128.195.10.252|:443... connected.<br>HTTP request sent, awaiting response... 200 OK<br>Length: unspecified<br>Saving to: &#39;/app/data/api/iris.csv&#39;<br><br>/app/data/api/iris.csv                         [ &lt;=&gt;                                                                                    ]   4.44K  --.-KB/s    in 0.001s  <br><br>2024-06-12 18:29:54 (3.36 MB/s) - &#39;/app/data/api/iris.csv&#39; saved [4551]</pre><p>Download a file using <strong>docker exec</strong> and <strong>curl</strong>:</p><pre>docker exec -it my-api2 /bin/sh -c &quot;curl -o /app/data/api/iris2.csv https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data&quot;</pre><p><strong>Output:</strong></p><pre>% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current<br>                                 Dload  Upload   Total   Spent    Left  Speed<br>100  4551    0  4551    0     0   8538      0 --:--:-- --:--:-- --:--:--  8586</pre><p>Then let’s check if the data have been downloaded in the target folder.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hkrl1HO0OHl3_2CtyEShUA.png" /><figcaption>Both of <strong>iris.csv</strong> and <strong>iris2.csv</strong> are present in the folder</figcaption></figure><h4><strong>When to use wget or curl:</strong></h4><ul><li>When you need to download files from a remote server directly into the container.</li><li>For small files or configuration scripts needed at runtime.</li></ul><h4>Conclusion</h4><p>While the <strong>COPY</strong> command in a Dockerfile is useful for static file transfers during image build time, these alternative methods offer greater flexibility for various scenarios:</p><ul><li>docker cp for on-the-fly file transfers.</li><li>Bind mounts for real-time development and persistent storage.</li><li>wget or curl for downloading files directly into the container.</li></ul><p>By utilizing these methods, you can manage file transfers more effectively in your Docker workflows, making your tasks as a data engineer more efficient and streamlined.</p><p><a href="https://www.linkedin.com/in/bricefotzo">Let’s connect</a> to continue the discussion!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=04dacd58c158" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/how-to-copy-files-into-a-docker-container-without-using-the-copy-command-04dacd58c158">How to copy files into a Docker container without using the COPY command</a> was originally published in <a href="https://towardsdev.com/">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mastering Docker Volumes: A practical guide for Beginners]]></title>
            <link>https://bricefotzo.medium.com/mastering-docker-volumes-a-practical-guide-for-beginners-2c9d4d969a8c?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/2c9d4d969a8c</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Wed, 05 Jun 2024 21:02:03 GMT</pubDate>
            <atom:updated>2024-07-08T18:23:56.375Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/664/0*dOB1ac47iTH7E0zu" /><figcaption>Docker volume from <a href="https://docs.docker.com/storage/volumes/">https://docs.docker.com/storage/volumes/</a></figcaption></figure><p>Imagine having docker containers running, and you want to update or restart it without losing the data you have inside. How would you manage that? This is a common challenge many faces when first diving into Docker. However, Docker volumes offer an elegant solution to this problem by ensuring your data persists beyond the lifecycle of a container.</p><p>In this article, we’ll explore what Docker volumes are, why they are indispensable, and how you can use them effectively to manage your data. Whether you’re new to Docker or looking to deepen your understanding, this guide will provide you with practical insights and examples.</p><h3>Table of content</h3><ul><li><a href="#why-use-docker-volumes">Why using Docker volumes?</a></li><li><a href="#creating-and-managing-volumes">Creating and managing volumes</a></li><li><a href="#using-volumes-with-containers">Using volumes with containers</a></li><li><a href="#cleaning-up-volumes">Cleaning up volumes</a></li></ul><h3>What makes Docker volumes essential?</h3><p>Docker volumes are designed to persist data created by and used within Docker containers. Unlike a container’s writable layer, data in volumes exists outside the container lifecycle, ensuring it remains intact even if the container is deleted.</p><h3>Why use Docker volumes?</h3><ol><li><strong>Data persistence</strong>: Volumes ensure your data is not lost when containers are removed. This persistence is critical for applications that need to retain state or logs across container restarts.</li><li><strong>Multi-Container sharing</strong>: Volumes can be shared among multiple containers, making them ideal for scenarios where data needs to be accessed by more than one container.</li><li><strong>Space efficiency</strong>: Since volumes exist outside the container’s writable layer, they do not inflate the size of your Docker images, leading to more efficient storage management.</li><li><strong>Lifecycle independence</strong>: Volumes have their own lifecycle, separate from the containers that use them, which adds flexibility in managing your data.</li></ol><h3>Creating and managing volumes</h3><h4>Creating a volume</h4><p>To create a new volume, you use the docker volume create command:</p><pre>docker volume create my_volume</pre><p>This command creates a volume named my_volume.</p><h4>Listing volumes</h4><p>To see all available volumes, you can list them with:</p><pre>docker volume ls</pre><p>The result looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1018/1*LBW7HZjWbanouVCXAI3ncw.png" /></figure><h4>Inspect volumes</h4><p>Let’s inspect our created volume named my_volume . The command to run is:</p><pre>docker volume inspect my_volume</pre><p>We have various information about the volume, especially the path where to find the volume data on the local machine.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2sPOhd2E4XR9GQSFpWBSyA.png" /></figure><h3>Using volumes with containers</h3><h4>Running a container with a volume</h4><p>When running a container, you can mount a volume to a specific directory within the container using the -v flag:</p><pre>docker run -d --name my_container -v my_volume:/data nginx</pre><p>This example runs a Nginx container and mounts my_volume to the /data directory inside the container.</p><p>You can check if the container created has the volume my_container with:</p><pre>docker inspect -f &quot;{{ .Mounts }}&quot; my_container</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E7btfNtpqSOmz-sZOcwo7g.png" /></figure><h4>Sharing volumes between containers</h4><p>You can share volumes between containers, which is useful for applications requiring shared storage:</p><pre>docker run -d --name my_container_2 --volumes-from my_container nginx</pre><p>Here, a new nginx container my_container_2 is run, attaching the volumes from an existing container named my_container. Let’s check it with:</p><pre>docker inspect -f &quot;{{ .Mounts }}&quot; my_container_2</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*msNO-UPqkQ3hcDmA9zQCQQ.png" /></figure><h4>Mounting many volumes in a container</h4><p>You can mount many volumes to a single container like this:</p><pre>docker run -d --name my_container_3 -v volume1:/data1 -v volume2:/data2 nginx</pre><p>Let’s check:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b5YR7rYK_eVNT4D2MBKb0Q.png" /></figure><p>We have in fact 2 volumes for the container my_container_3 .</p><h4>Mounting a volume as Read-Only</h4><p>For scenarios where you want to prevent modifications to the data, you can mount a volume with read-only access:</p><pre>docker run -d -v my_volume:/data:ro nginx</pre><p>This mounts my_volume in read-only mode at /data using the keyword ro. By default, the keyword rw for read-write is the one used.</p><h3>Cleaning up volumes</h3><h4>Deleting a specific volume</h4><p>To remove a volume, you use:</p><pre>docker volume rm my_volume</pre><h4>Running a container with temporary volumes</h4><p>In some cases, you might want to use volumes temporarily and delete them once the container stops:</p><pre>docker run --rm -v temp_volume1:/data1 -v temp_volume2:/data2 nginx</pre><p>The --rm flag ensures that temp_volume1 and temp_volume2 are removed after the container exits.</p><h4>Pruning unused volumes</h4><p>To clean up all unused volumes and free up space:</p><pre>docker volume prune</pre><p>This command removes all volumes that are not currently in use by any container.</p><h3>Conclusion</h3><p>Docker volumes are an essential feature for any Docker user, providing a robust solution for data persistence, sharing, and efficient storage management. By mastering the use of Docker volumes, you can ensure your data is safely managed and accessible, even as your containers come and go. With the practical examples and commands provided, you’re now equipped to leverage Docker volumes to their full potential in your projects.</p><p><a href="https://www.linkedin.com/in/bricefotzo">Let’s connect</a> to continue the discussion! 😉</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2c9d4d969a8c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging RSS Extraction with Langchain and Newspaper3k]]></title>
            <link>https://bricefotzo.medium.com/debugging-rss-extraction-with-langchain-and-newspaper3k-fb413cd6f025?source=rss-cf7915a0bf5e------2</link>
            <guid isPermaLink="false">https://medium.com/p/fb413cd6f025</guid>
            <category><![CDATA[rss]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[langchain]]></category>
            <dc:creator><![CDATA[Brice Fotzo]]></dc:creator>
            <pubDate>Wed, 05 Jun 2024 19:39:12 GMT</pubDate>
            <atom:updated>2024-06-05T19:39:12.589Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uqAsUeEj294LxEoQmAX9fw.png" /><figcaption>Langchain and RSS extraction</figcaption></figure><h3>Introduction</h3><p>Working with RSS feeds can be challenging, especially when integrating different libraries and tools. Recently, I encountered issues while trying to run RSS extraction code using Langchain and Newspaper3k. This article walks through the debugging process and how I resolved the errors.</p><h3>Initial Setup and Encountered Errors</h3><p>I started by setting up my environment and writing a simple script to extract RSS feeds. Despite installing newspaper3k, I encountered the following error:</p><pre>Error processing entry https://github.com/google-deepmind/penzai, exception: newspaper package not found, please install it with `pip install newspaper3k`</pre><h3>Using Newspaper3k</h3><p>I decided to test Newspaper3k independently with a simple script:</p><pre>import newspaper<br><br>cnn_paper = newspaper.build(&#39;http://cnn.com&#39;)<br><br>for article in cnn_paper.articles:<br>    print(article.url)</pre><p>This led to another error:</p><pre>Traceback (most recent call last):<br>  File &quot;D:\workspace\rss\try.py&quot;, line 1, in &lt;module&gt;<br>    import newspaper<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\newspaper\__init__.py&quot;, line 10, in &lt;module&gt;<br>    from .api import (build, build_article, fulltext, hot, languages,<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\newspaper\api.py&quot;, line 14, in &lt;module&gt;<br>    from .article import Article<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\newspaper\article.py&quot;, line 15, in &lt;module&gt;<br>    from . import network<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\newspaper\network.py&quot;, line 14, in &lt;module&gt;<br>    from .configuration import Configuration<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\newspaper\configuration.py&quot;, line 15, in &lt;module&gt;<br>    from .parsers import Parser<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\newspaper\parsers.py&quot;, line 12, in &lt;module&gt;<br>    import lxml.html.clean<br>  File &quot;D:\workspace\rss\.venv\lib\site-packages\lxml\html\clean.py&quot;, line 18, in &lt;module&gt;<br>    raise ImportError(<br>ImportError: lxml.html.clean module is now a separate project lxml_html_clean.<br>Install lxml[html_clean] or lxml_html_clean directly.</pre><h3>Resolving the errors</h3><h4>Step 1: Install Missing Dependencies</h4><p>The error suggested installing lxml_html_clean, which I did using the following command:</p><pre>pip install lxml_html_clean</pre><h4>Step 2: Verify Installation</h4><p>After installation, I reran the Newspaper3k script, which worked without issues:</p><pre>import newspaper<br><br>cnn_paper = newspaper.build(&#39;http://cnn.com&#39;)<br>for article in cnn_paper.articles:<br>    print(article.url)</pre><h4>Step 3: Retry Langchain Code</h4><p>With Newspaper3k working, I retried the original Langchain code. This time, it executed successfully without any errors.</p><h3>Conclusion</h3><p>Debugging integration issues can be time-consuming, but systematically isolating and testing components helps identify and resolve problems effectively. Ensuring all dependencies are correctly installed and up-to-date is crucial for smooth execution. I hope this walkthrough helps others facing similar issues with RSS extraction and library integrations.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fb413cd6f025" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>