<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kat Cosgrove</title>
    <description>The latest articles on DEV Community by Kat Cosgrove (@katcosgrove).</description>
    <link>https://dev.to/katcosgrove</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F294291%2Ff139e61e-c871-4168-8366-83bf664573b2.jpg</url>
      <title>DEV Community: Kat Cosgrove</title>
      <link>https://dev.to/katcosgrove</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/katcosgrove"/>
    <language>en</language>
    <item>
      <title>When Projects Fail: Why Companies Should Treat Open Source as Infrastructure</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Wed, 18 Mar 2026 14:39:24 +0000</pubDate>
      <link>https://dev.to/katcosgrove/when-projects-fail-why-companies-should-treat-open-source-as-infrastructure-32c0</link>
      <guid>https://dev.to/katcosgrove/when-projects-fail-why-companies-should-treat-open-source-as-infrastructure-32c0</guid>
      <description>&lt;p&gt;Maintaining an open source project is hard. It requires managing a group of people who are largely working for free to build something that other people profit off of, usually distributed across the globe, with limited resources. &lt;/p&gt;

&lt;p&gt;The whole time you’re doing this, you’re receiving demands from users and businesses alike for features or bug fixes on a timeline that works for them, not you and your (possibly very limited) group of contributors that you can’t exactly order around, since they aren’t being paid. It’s stressful, and it can be overwhelming. &lt;/p&gt;

&lt;p&gt;When one of these projects is the victim of an attack that takes advantage of the fact that there are only one or two maintainers, or eventually has to shut down due to rising technical debt and falling contributor numbers, the public blame falls on us, not on the businesses that didn’t offer contributors in time. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Open Source Dependency Problem
&lt;/h2&gt;

&lt;p&gt;According to a 2022 Linux Foundation study, open source software makes up 70-90% of any given modern software solution. So, if that much of the world’s digital infrastructure is reliant on open source software, why do so many projects both large and small struggle to attract and maintain contributors? &lt;/p&gt;

&lt;p&gt;We’ve all heard the morality and community arguments for contributing back to open source projects, but we’re still seeing critical projects fall victim to attacks that take advantage of a small, overworked team of maintainers or shut down entirely because they didn’t get help until it was too late. So maybe it’s time we look at the business value of open source contribution from another angle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not contributing to open source is a security problem for your business.
&lt;/h2&gt;

&lt;p&gt;The success of your business and the safety of your users’ data relies heavily on the stability of the open source software you build your product on top of. So, you should be motivated to ensure those open source projects remain healthy and secure, right? However, historically, only the very largest of projects has received this treatment. &lt;/p&gt;

&lt;p&gt;We’ve seen it time and time again. A small but widely-used project has only one or two maintainers. Because they’re overworked, unpaid, and desperate for help, they’re easy to take advantage of. They may not review a PR as thoroughly as they should, and it may be easier for a bad actor to slip into a trusted position over time. Technical debt piles up, and with it, unresolved CVEs. Sometimes this is a one-off issue and a wake-up call for an industry to begin contributing to that specific project, or at least begin doing more investigation into the health of a project before relying on it. Sometimes, that offer of help comes too late and the project is unrecoverable. &lt;/p&gt;

&lt;h2&gt;
  
  
  A Real-World Example: Ingress NGINX
&lt;/h2&gt;

&lt;p&gt;Ingress NGINX is an example of this. Roughly 50% of cloud native environments rely on this small but mighty utility for managing Kubernetes traffic. Despite its popularity and importance to the ecosystem, it has been steadfastly maintained by only two people for years. Calls for help have long been ignored, even in the face of a vulnerability with a 9.8 CVSS score. There is now no choice but to archive the project. &lt;/p&gt;

&lt;p&gt;The fundamental architecture of Ingress NGINX is, to quote Kubernetes Security Response Committee member Tabitha Sable, “a never-ending CVE piñata.” No number of maintainers could reasonably be expected to keep it in a secure state, and it is no longer possible to get it to a point where it would be easier to maintain. If the project received a donation of an entire engineering team today, that engineering team would still just be used to ensure a more graceful shutdown. &lt;/p&gt;

&lt;p&gt;This wasn’t always the case. There was once active  development on a more modern implementation that would have resolved a lot of Ingress NGINX’s design issues, but the project maintainers were never able to attract more contributors and development stalled. &lt;/p&gt;

&lt;p&gt;Had the businesses who are operating large cloud native environments reliant on Ingress NGINX begun contributing back to the project two years ago when asked, we might not be where we are today. Instead, the only responsible option left is to archive the project. Help came too late, and now users are faced with the choice between running an outdated Ingress NGINX deployment (and accepting the attack risk that comes with it) or spending the time and money necessary to migrate to another solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  If You Rely on Open Source, Help Maintain It
&lt;/h2&gt;

&lt;p&gt;Contributing upstream isn’t just for feel-good community points. It isn’t charity. It isn’t for clout. It’s a necessary part of maintaining a secure and reliable software supply chain. Businesses that rely on open source should treat upstream projects as part of their infrastructure. That means dedicating engineering time to contribute fixes and improvements, funding maintainers or foundations that support critical projects, prioritizing contributions to the dependencies their products rely on most, or hiring engineers or DevRel practitioners whose role includes contributing upstream. &lt;/p&gt;

&lt;p&gt;Individual engineers can help too. Contributing documentation, reviewing pull requests, reporting bugs, improving tests, or helping with issue triage are all meaningful ways to reduce the burden on small maintainer teams. Engineers can also advocate within their organizations and push the companies they work for to support the projects their products depend on.&lt;/p&gt;

&lt;p&gt;Open source projects rarely fail overnight. They usually show warning signs long before that point: maintainer burnout, unanswered issues, stalled development, and growing technical debt. Contributing upstream helps address those problems before they turn into security incidents, project shutdowns, or expensive migrations.&lt;/p&gt;

&lt;p&gt;Because when a critical open source project fails, the impact doesn’t stay upstream. It spreads to every company, product, and user that depends on it.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>Cloud Systems Part Three: Deploying to Amazon ECS</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Mon, 31 Jan 2022 15:25:41 +0000</pubDate>
      <link>https://dev.to/pulumi/cloud-systems-part-three-deploying-to-amazon-ecs-1954</link>
      <guid>https://dev.to/pulumi/cloud-systems-part-three-deploying-to-amazon-ecs-1954</guid>
      <description>&lt;p&gt;Cloud engineering is taking over software development. In a lot of ways, this is great; it allows us to build and deploy more complicated applications with less difficulty, and maintaining those applications becomes less troublesome too. We can release smaller updates more quickly than ever, ensuring that we can stay on top of feature requests and security issues. That said, the rise of cloud engineering has also introduced a lot of complexity in the form of dozens of services even within just one cloud provider. Figuring out where to start can be tough, so let’s take a practical tour! In this series, I’ll walk you through building a personal website and deploying it using modern cloud engineering practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elastic Container Service
&lt;/h2&gt;

&lt;p&gt;In the previous tutorial, we extended our personal website to use the Flask web framework, add server-side routing, and package everything up into a Docker container. It's still only running locally, though, and we want to deploy it. To do that, today we'll be learning to use Pulumi to deploy to Amazon's Elastic Container Service. If you completed part two of this series, we'll be picking up right where we left off. If you're just now joining me, you can get the completed code by forking and cloning &lt;a href="https://github.com/katcosgrove/cloud-systems-101" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An AWS Account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://app.pulumi.com" rel="noopener noreferrer"&gt;Pulumi account&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.pulumi.com/docs/get-started/aws/begin/" rel="noopener noreferrer"&gt;Pulumi installed and configured for AWS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python3&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring Amazon ECS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html" rel="noopener noreferrer"&gt;Amazon’s ECS (Elastic Container Service)&lt;/a&gt; is a tool used for container orchestration. It allows you to deploy containerized applications into a cluster, defined as tasks. We’re going to use Pulumi to define all of the necessary rules and settings to do this in a secure manner, then create our infrastructure and deploy the containerized website.&lt;/p&gt;

&lt;p&gt;First, run &lt;code&gt;pulumi new python -y&lt;/code&gt; to start a new Pulumi project if you don't already have one for this tutorial. From now on, we'll be working within &lt;code&gt;__main__.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pulumi_aws&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pulumi_docker&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we have our imports. We’re working with both AWS and Docker, so we need both of those Pulumi providers. Launch the virtual environment set up for you by Pulumi with &lt;code&gt;source venv/bin/activate&lt;/code&gt; and run &lt;code&gt;pip3 install pulumi-aws pulumi-docker&lt;/code&gt; to get both. If you want to make sure your dependencies are saved for later, you can run &lt;code&gt;pip3 freeze &amp;gt; requirements.txt&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app_cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-cluster&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app_vpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Vpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-vpc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cidr_block&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;172.31.0.0/16&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;enable_dns_hostnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app_vpc_subnet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Subnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-vpc-subnet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cidr_block&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;172.31.32.0/20&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this codeblock, we're first creating our ECS cluster and naming it &lt;code&gt;app-cluster&lt;/code&gt;. Then create a VPC (Virtual Private Cloud), and a subnet for it. VPCs are like miniature local networks, with subnets providing the addressing within that mini network. Using a VPC means you can wire up your system as you like it without worrying about how that network setup might collide with other AWS resources, since you define how that network can connect outward. If you've ever used Docker networking, you'll be familiar with the idea that an internal network is different than the external interface.&lt;/p&gt;

&lt;p&gt;It is possible to use the default VPC here rather than defining one, but AWS networking is complex, so it's worth knowing that it is possible to define a custom VPC.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app_gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;InternetGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-gateway&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app_routetable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RouteTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-routetable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RouteTableRouteArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cidr_block&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;gateway_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app_routetable_association&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MainRouteTableAssociation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app_routetable_association&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;route_table_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_routetable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need a gateway and a route table. The gateway's job is to provide a target in that route table we just created and associated with our VPC. This allows our VPC to communicate with the public internet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app_security_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecurityGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;security-group&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enables HTTP access&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ingress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecurityGroupIngressArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tcp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;from_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;to_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;cidr_blocks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="n"&gt;egress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecurityGroupEgressArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;from_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;to_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;cidr_blocks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just about everything you do in AWS requires a security group. The purpose of a security group is to control traffic, both inbound and outbound. This one enables HTTP access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Creating an IAM role used by Fargate to execute all our services
&lt;/span&gt;&lt;span class="n"&gt;app_exec_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-exec-role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;assume_role_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;{
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: [
        {
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Principal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: {
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ecs-tasks.amazonaws.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
            },
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="s"&gt;
        }]
    }&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Attaching execution permissions to the exec role
&lt;/span&gt;&lt;span class="n"&gt;exec_policy_attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RolePolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-exec-policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_exec_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Creating an IAM role used by Fargate to manage tasks
&lt;/span&gt;&lt;span class="n"&gt;app_task_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-task-role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;assume_role_policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;{
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: [
        {
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sts:AssumeRole&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Principal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: {
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ecs-tasks.amazonaws.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
            },
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
            &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="s"&gt;
        }]
    }&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Attaching execution permissions to the task role
&lt;/span&gt;&lt;span class="n"&gt;task_policy_attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RolePolicyAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-access-policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_task_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AMAZON_ECS_FULL_ACCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're going to be using &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" rel="noopener noreferrer"&gt;AWS Fargate&lt;/a&gt; to actually run our container. Fargate is a serverless compute platform designed for running containers without requiring you to provision and manage clusters of EC2 (Elastic Compute Cloud) instances. Since our website is containerized and very small, it's perfect for us. Services also require IAM roles, and in this case, we need to allow Fargate to execute our services and manage our tasks, so we create new roles for both and apply execution permissions to them.&lt;/p&gt;

&lt;p&gt;In the context of Fargate, a task is a collection of containers, and a task definition tells AWS what needs to be running all at once in order to make an application run. The task definitions will be written later onm but first we need to define policies. It's important that these policies be created first, or the task definitions won't create correctly later on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Creating storage space to upload a docker image of our app to
&lt;/span&gt;&lt;span class="n"&gt;app_ecr_repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-ecr-repo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;image_tag_mutability&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MUTABLE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Attaching an application life cycle policy to the storage
&lt;/span&gt;&lt;span class="n"&gt;app_lifecycle_policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LifecyclePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-lifecycle-policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_ecr_repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;{
        &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rules&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: [
            {
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rulePriority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 10,
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Remove untagged images&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;selection&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: {
                    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tagStatus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untagged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
                    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;countType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;imageCountMoreThan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,
                    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;countNumber&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 1
                },
                &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: {
                    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expire&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
                }
            }
        ]
    }&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing we need to do for our infrastructure before we can start deploying to it is create a repository on &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon ECR (Elastic Container Registry)&lt;/a&gt; where our Docker image will live, then attach an application lifecycle policy to that repository. This makes sure that we expire and remove any untagged images in the repository, keeping things from geting clogged up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a Dockerized Flask Application to ECS
&lt;/h2&gt;

&lt;p&gt;We're now ready to deploy our website and wire all of this up!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;flask_targetgroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TargetGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-targetgroup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TCP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;flask_balancer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadBalancer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-balancer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_balancer_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;internal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;security_groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="n"&gt;subnets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;app_vpc_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;flask_listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-listener&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_balancer_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;flask_balancer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TCP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;default_actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ListenerDefaultActionArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forward&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_group_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;flask_targetgroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;
    &lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we need to make it possible for our Flask application to communicate with the internet. That requires three pieces of configuration: a target group for port 80, a load balancer to spread out incoming requests and make sure our website doesn't get overwhelmed as easily, and a listener to forward public traffic to the defined target group. The target group is directed at the endpoint for the VPC we created earlier, defining the port as it is unknown until runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_registry_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rid&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_credentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authorization_token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid credentials&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ImageRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxy_endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;app_registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_ecr_repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_registry_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;flask_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-dockerimage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;image_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_ecr_repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./website&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;skip_push&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_registry&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A small helper function is required here. It's grabbing some of our AWS credentials, specificaly an authorization token, so that we can talk to the registry. Next, we build the Docker image for our website and push it to the repository we created in Amazon ECR earlier. Remember, the Dockerfile is in &lt;code&gt;./website&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;flask_task_definition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TaskDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-task-definition&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frontend-task-definition-family&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;network_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;awsvpc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;requires_compatibilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FARGATE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;execution_role_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_exec_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_role_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_task_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;container_definitions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flask_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-container&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;essential&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;portMappings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;containerPort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hostPort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;protocol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tcp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;}])))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need a task definition for the Flask instance. AWS Fargate will be managing this for us, using the roles we defined earlier. We're also handing it a container definition, including the image name and a little bit of information about it, like the container and host ports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;flask_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;app_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;desired_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;launch_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FARGATE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_definition&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;flask_task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;wait_for_steady_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;network_configuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServiceNetworkConfigurationArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;assign_public_ip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subnets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;app_vpc_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;security_groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;app_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;load_balancers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ServiceLoadBalancerArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;target_group_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;flask_targetgroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;container_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flask-container&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;container_port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResourceOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;depends_on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;flask_listener&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we actually launch our website. We need to create a new service definition and hand it all of the resources we created earlier, beginning with the ECS cluster itself. It has the task definition, our network configurations, and our load balancers, and we make sure that this particular piece of code isn't executed until the listener we created to watch for traffic is online.&lt;/p&gt;

&lt;p&gt;As a shortcut to finding our website, export the DNS name of our load balancer as an output from Pulumi as the last part of the program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app-url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flask_balancer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dns_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're ready to go! Set your AWS region in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pulumi config &lt;span class="nb"&gt;set &lt;/span&gt;aws:region us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;pulumi up&lt;/code&gt; to watch it go! This time, we're going to see &lt;em&gt;way&lt;/em&gt; more services created than before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;     Type                                  Name                        Status      Info
 +   pulumi:pulumi:Stack                   part-three-dev              created
 +   ├─ docker:image:Image                 flask-dockerimage           created
 +   ├─ aws:ec2:Vpc                        app-vpc                     created
 +   ├─ aws:ecs:Cluster                    app-cluster                 created
 +   ├─ aws:iam:Role                       app-exec-role               created
 +   ├─ aws:iam:Role                       app-task-role               created
 +   ├─ aws:ecr:Repository                 app-ecr-repo                created
 +   ├─ aws:iam:RolePolicyAttachment       app-exec-policy             created
 +   ├─ aws:iam:RolePolicyAttachment       app-access-policy           created
 +   ├─ aws:ecr:LifecyclePolicy            app-lifecycle-policy        created
 +   ├─ aws:ec2:Subnet                     app-vpc-subnet              created
 +   ├─ aws:ec2:InternetGateway            app-gateway                 created
 +   ├─ aws:ec2:SecurityGroup              security-group              created
 +   ├─ aws:lb:TargetGroup                 flask-targetgroup           created
 +   ├─ aws:ecs:TaskDefinition             flask-task-definition       created
 +   ├─ aws:lb:LoadBalancer                flask-balancer              created
 +   ├─ aws:ec2:RouteTable                 app-routetable              created
 +   ├─ aws:ec2:MainRouteTableAssociation  app_routetable_association  created
 +   ├─ aws:lb:Listener                    flask-listener              created
 +   └─ aws:ecs:Service                    flask-service               created

Outputs:
    app-url: &lt;span class="s2"&gt;"flask-balancer-a90d260-057e5a40e0267b63.elb.us-west-2.amazonaws.com"&lt;/span&gt;

Resources:
    + 20 created

Duration: 3m36s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pulumi has created 20 resources for you, and helpfully returned the DNS address of your load balancer. I've snipped out the &lt;code&gt;diagnostics&lt;/code&gt; section of this output for the sake of keeping things brief, but you will also see the step-by-step process Docker goes through building and running your image. Go to that URL after a couple of minutes, and your website is online!&lt;/p&gt;

&lt;p&gt;We now have a functional, multi-page website with server-side routing, packaged up into a Docker container and deployed to a fully-configured AWS ECS cluster in just a couple hundred lines of Python, all without leaving the same repository our website is stored in. What if we want to be able to scale really large, though? Some resiliency would be nice. Stay tuned for the next blog for an introduction to Kubernetes, where we'll learn to deploy our website to Amazon's Elastic Kubermetes Service!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>containers</category>
      <category>iac</category>
    </item>
    <item>
      <title>Cloud Systems Part Two: Containerizing a Website</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Wed, 26 Jan 2022 20:43:54 +0000</pubDate>
      <link>https://dev.to/pulumi/cloud-systems-part-two-containerizing-a-website-56c9</link>
      <guid>https://dev.to/pulumi/cloud-systems-part-two-containerizing-a-website-56c9</guid>
      <description>&lt;p&gt;Cloud engineering is taking over software development. In a lot of ways, this is great; it allows us to build and deploy more complicated applications with less difficulty, and maintaining those applications becomes less troublesome too. We can release smaller updates more quickly than ever, ensuring that we can stay on top of feature requests and security issues. That said, the rise of cloud engineering has also introduced a lot of complexity in the form of dozens of services even within just one cloud provider. Figuring out where to start can be tough, so let’s take a practical tour! In this series, I’ll walk you through building a personal website and deploying it using modern cloud engineering practices.&lt;/p&gt;

&lt;p&gt;In part one of this series, we built a personal website and deployed it to AWS S3. That works perfectly well for a static, single-page application with minimal interactivity, but if you want server-side routing or database interactivity, things have to get a little bit more complicated. In part two of this series, we’ll be adding a couple more pages to our personal website, adding server-side routing, and containerizing it with Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers
&lt;/h2&gt;

&lt;p&gt;So, what’s a container and why would you want to use one? You can think of a container here the exact same way you think of containers on a shipping barge. On that barge is a bunch of shipping containers, and inside each shipping container is a bunch of packages. The barge itself is your computer (or your cloud environment), and the shipping containers house your applications.&lt;/p&gt;

&lt;p&gt;It may also help to think of them as smaller, more lightweight incarnations of virtual machines. While a virtual machine virtualizes the machine’s physical hardware through the use of a hypervisor, a container virtualizes only the operating system. A virtual machine is usually several gigabytes in size, whereas a container is usually less than a gigabyte. The small size and speed of deployment as compared to a traditional virtual machine has given rise to the popularity of designing applications as collections of microservices (several single-purpose services running in containers that, when working together, make up your complete application) instead of monoliths (all of your application code and services running as a single unit, often within a single file).&lt;/p&gt;

&lt;p&gt;Deploying your application using containers allows you to package your application code alongside everything required to run it, including its dependencies and an operating system. You know how sometimes you hand code off to someone else, but it doesn’t run for them, so you go “Well, it works on &lt;em&gt;my&lt;/em&gt; machine,” and just shrug? Using a container is pretty similar to just deploying your machine. The most popular container engine today is Docker, so that’s what we’ll be using today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python3&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we go into containerizing and deploying our website, let’s make it a little bit more useful. For this, we’re going to be using Flask, a lightweight web framework for Python. Fork and clone &lt;a href="https://github.com/katcosgrove/cloud-systems-101" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt; to get the full sample code for part two of this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expanding the Website
&lt;/h2&gt;

&lt;p&gt;Our file structure gets more complicated now that we are building a more dynamic website with server-side routing and throwing it into a Docker container. You will have something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;container-tutorial
├── Pulumi.yaml
├── requirements.txt
├── __main__.py
└── website
    ├── Dockerfile
    ├── requirements.txt
    └── app
        ├── server.py
        ├── static
        │   ├── normalize.css
        │   ├── style.css
        │   └── background.jpg
        └── templates
            ├── base.html
            ├── index.html
            ├── portfolio.html
            └── about.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything contained within the &lt;code&gt;app&lt;/code&gt; directory is the website itself. We now have server-side routing, and a few different pages. To run it locally and see what the new website looks like, &lt;code&gt;pip3 install flask&lt;/code&gt; to install the Flask web framework and then run &lt;code&gt;python3 server.py&lt;/code&gt; from the &lt;code&gt;app&lt;/code&gt; directory. Flask will return an IP address where your website is running; navigate there in your browser and click around a bit!&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/about&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;about&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;about.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/portfolio&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;portfolio&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;portfolio.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where before our entire website was made up of a single HTML file, we now have several, and we're using Flask to handle the routing between them. In this server file, three routs are defined: home, about, and portfolio. Flask handles serving these up for us, and associating a route with a particular HTML file. This way, we get a nice &lt;code&gt;www.mywebsite.com/about&lt;/code&gt; URL instead of something like &lt;code&gt;www.mywebsite.com/about.html&lt;/code&gt;. Building a website this way also means we have the ability to apply some logic to each of these routes, such as adding database interaction, user login, and passing conditional variables from the server to the templates that will be rendering each page. We aren't doing any of that yet, but we will!&lt;/p&gt;

&lt;p&gt;At the bottom, we're binding the Flask application to &lt;code&gt;0.0.0.0:80&lt;/code&gt;. You can change that to any other unoccupied local IP address and port you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Templating
&lt;/h2&gt;

&lt;p&gt;Flask can make use of a templating engine called Jinja2 to make adding pages to your website a bit less repetitive. You start with one template, in this case called &lt;code&gt;base.html&lt;/code&gt;, that contains any HTML you want to exist on every single page.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;base.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;url_for&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;static&lt;/span&gt;&lt;span class="err"&gt;',&lt;/span&gt; &lt;span class="na"&gt;filename=&lt;/span&gt;&lt;span class="s"&gt;'style.css'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;url_for&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;static&lt;/span&gt;&lt;span class="err"&gt;',&lt;/span&gt; &lt;span class="na"&gt;filename=&lt;/span&gt;&lt;span class="s"&gt;'normalize.css'&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-cat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;request.path =&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt; &lt;span class="na"&gt;url_for&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="err"&gt;')&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt; &lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;endif&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ url_for('home') }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;request.path =&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt; &lt;span class="na"&gt;url_for&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;about&lt;/span&gt;&lt;span class="err"&gt;')&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt; &lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;endif&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ url_for('about') }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="na"&gt;request.path =&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt; &lt;span class="na"&gt;url_for&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;portfolio&lt;/span&gt;&lt;span class="err"&gt;')&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt; &lt;span class="err"&gt;{%&lt;/span&gt; &lt;span class="na"&gt;endif&lt;/span&gt; &lt;span class="err"&gt;%}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ url_for('portfolio') }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Portfolio&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"social"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://github.com/katcosgrove"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fab fa-github-alt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://twitter.com/Dixie3Flatline"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fab fa-twitter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://linkedin.com/in/katcosgrove"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fab fa-linkedin-in"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
{% block content %}{% endblock %}
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://kit.fontawesome.com/b4747495ea.js"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file includes some metadata, stylesheets, the header, navigation, and any Javascript we want to link in. All of the content in this file will exist on every page. Below the header, in the body, you'll see &lt;code&gt;{% block content %} {% endblock %}&lt;/code&gt;. That's where additional, page-specific content will be added.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% extends 'base.html' %}

{% block content %}

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Name&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Job Title at Company&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is all that exists in our &lt;code&gt;index.html&lt;/code&gt; now. The first directive tells Jinja2 that this bit of HTML goes in the &lt;code&gt;base.html&lt;/code&gt; template, and the remaining two wrap the content we want to insert. The same thing is happening in &lt;code&gt;about.html&lt;/code&gt; and &lt;code&gt;portfolio.html&lt;/code&gt;. Neat, right? Jinja2 is not unique to Flask, so you can use this templating engine with another web framework if you prefer.&lt;/p&gt;

&lt;p&gt;While we could deploy this as-is, it's a bit unwieldy. Let's wrap it up into a container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerizing our Website
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;website&lt;/code&gt; directory, we have something called a Dockerfile. Dockerfiles tell the Docker engine what to do with your application, how your container should behave with respect to the wider internet or other containers, and what needs to be done inside of the container for your application to run. Ours looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FROM ubuntu:20.04

COPY app /app

WORKDIR /app

EXPOSE 80

RUN apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gcc python3-dev python3-pip python-markupsafe

COPY requirements.txt /app

RUN pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"python3"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;

CMD &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"server.py"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile says that our container should be running ubuntu:20.04 as its operating system. It will pull the Ubuntu image from Docker Hub, their public image registry, though you can pull from another registry here as well. The &lt;code&gt;COPY&lt;/code&gt; directive moves our website’s files (the &lt;code&gt;app&lt;/code&gt; directory) into the container. We then set our working directory for all future commands run inside of the container to &lt;code&gt;/app&lt;/code&gt; to reduce the amount of typing we have to do later on, update Ubuntu, and install some dependencies using both apt and from the requirements.txt file we also copy over. Lastly, an entrypoint and command to actually start our application are defined. There is a slightly less verbose way to start the application, but this way of defining an &lt;code&gt;ENTRYPOINT&lt;/code&gt; and &lt;code&gt;CMD&lt;/code&gt; is fairly common, so it’s worth seeing here.&lt;/p&gt;

&lt;p&gt;To see this run locally, we first need to build and tag the image. From the same directory as your Dockerfile, run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker build --tag container-tutorial:latest .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We are calling the docker CLI, telling it we want to build a container, and indicating that we want to tag it at the same time. The tag isn't strictly necessary for the container to run, but it does make the container easier to identify and interact with. We're also assigning it a version of &lt;code&gt;latest&lt;/code&gt;. Note the trailing dot at the end of the command, which is the part of the command that indicates the location of the Dockerfile we want to build.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;docker images&lt;/code&gt; to see a list of all images you have built. Something like this should be in the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloud-systems                                                       latest                                                             0ce569ac0360   10 days ago    408MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the container is built, we need to run it and forward the container’s port to a local port so we can see our site.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker run -d -p 80:80 container-tutorial&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There's a lot going on in that command, so let's take a closer look. the &lt;code&gt;-d&lt;/code&gt; flag is short for &lt;code&gt;--detach&lt;/code&gt;, and it tells the Docker engine that we want to print the container's ID and run it in the background. The &lt;code&gt;-p&lt;/code&gt; flag is short for &lt;code&gt;--publish&lt;/code&gt;, and it tells the Docker engine to publish the container's port to the host. In this case, port 80.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;docker ps&lt;/code&gt; to get a list of all running containers, and you'll see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CONTAINER ID   IMAGE                COMMAND               CREATED       STATUS       PORTS                                       NAMES
f95362faf3cd   container-tutorial   &lt;span class="s2"&gt;"python3 server.py"&lt;/span&gt;   7 days ago    Up 7 days    0.0.0.0:80-&amp;gt;80/tcp, :::80-&amp;gt;80/tcp           affectionate_jackson

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

&lt;/div&gt;



&lt;p&gt;That's our website! You can see the container ID, the image tag, the commands that ran for it to start the application, its host, the container port, and the port it forwarded to. Go to &lt;code&gt;localhost&lt;/code&gt; in your browser, and the website is up!&lt;/p&gt;

&lt;p&gt;We're hosting this locally, though. It's not accessible by the wider internet, and even if it was, our machines would be dealing with all of the traffic. There's nothing in place to distribute traffic or restrict access. Worry not, cloud services exist for that, too. In the next in this series, we'll take our containerized website and deploy it to AWS Elastic Container Service, complete with AWS networking configuration, IAM roles, and AWS Fargate!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>iac</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Cloud Systems Part One: Static Sites and AWS S3</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Tue, 25 Jan 2022 18:48:50 +0000</pubDate>
      <link>https://dev.to/pulumi/cloud-systems-part-one-static-sites-and-aws-s3-30hc</link>
      <guid>https://dev.to/pulumi/cloud-systems-part-one-static-sites-and-aws-s3-30hc</guid>
      <description>&lt;p&gt;Cloud engineering is taking over software development. In a lot of ways, this is great; it allows us to build and deploy more complicated applications with less difficulty, and maintaining those applications becomes less troublesome too. We can release smaller updates more quickly than ever, ensuring that we can stay on top of feature requests and security issues. That said, the rise of cloud engineering has also introduced a lot of complexity in the form of dozens of services even within just one cloud provider. Figuring out where to start can be tough, so let’s take a practical tour! In this series, I’ll walk you through building a personal website and deploying it using modern cloud engineering practices.&lt;/p&gt;

&lt;p&gt;The simplest website you can build is a static site. That means no web server, just HTML, CSS, and maybe some front-end JavaScript. Static sites can still be beautiful and interactive though, and for many people, they’re more than enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; (Simple Storage Service) is an AWS service that provides cloud storage for files, stored as objects in buckets. You can think of it like an external harddrive: plug it into your computer, and you have more storage space for your files, photos, and movies that you can unplug at will and move to another computer. An S3 bucket behaves very similarly in that you can throw just about anything in it, from data lakes to images to logs, and connect it to an application. This also means that you can use it to host a static website! Let’s look at how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An AWS account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://app.pulumi.com" rel="noopener noreferrer"&gt;Pulumi account&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.pulumi.com/docs/get-started/aws/begin/" rel="noopener noreferrer"&gt;Pulumi installed and configured for AWS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python3&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NodeJS (optional if you want to preview the site locally)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a lot of ways to provision an S3 bucket and set its permissions, but for this I’m going to use Pulumi, an Infrastructure as Code tool that allows you to provision, configure, and deploy a variety of cloud services and tools programmatically, without learning a new language. That way, we can stay in our code, and the syntax will be more familiar for developers. It also saves us the headache of going through the AWS Console. All of the code for this, both the website and the infrastructure code, can be found in the &lt;a href="https://github.com/katcosgrove/cloud-systems-101" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for the series, although I'll be walking you through everything here too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Static Site
&lt;/h2&gt;

&lt;p&gt;Create a new directory called &lt;code&gt;s3-tutorial&lt;/code&gt; and navigate into it. From there, &lt;code&gt;run pulumi new python -y&lt;/code&gt; to create a new Pulumi project using Python. You’ll see Pulumi create a few files for you, but the one we’ll be touching is &lt;code&gt;__main__.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before we move on, we need to build out our website. Create a subdirectory and call it &lt;code&gt;website&lt;/code&gt;. In that directory, add &lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;, and &lt;code&gt;normalize.css&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This one is built using only HTML and CSS, plus one background image file. Replace the name and bio with your own, as well as the social links in the header.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"normalize.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fas fa-cat"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"social"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://github.com/katcosgrove"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fab fa-github-alt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://twitter.com/Dixie3Flatline"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fab fa-twitter"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://linkedin.com/in/katcosgrove"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fab fa-linkedin-in"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"banner"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your Name&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Job Title at Company&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit tristique arcu, sed commodo ligula. Ut ullamcorper velit non luctus mollis. Sed placerat quam sed tellus aliquam, ac eleifend quam lacinia. Praesent bibendum velit at metus auctor, vitae feugiat quam luctus. Morbi eu imperdiet metus, et tincidunt tellus. Aliquam non ullamcorper justo. Cras ornare nulla vel pellentesque vulputate. Mauris in elit neque. Aliquam tempor aliquam libero, elementum luctus nisl imperdiet sit amet. Praesent urna ante, scelerisque non tellus mollis, laoreet sodales felis.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://kit.fontawesome.com/b4747495ea.js"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;style.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url('https://fonts.googleapis.com/css?family=News+Cycle|Teko&amp;amp;display=swap')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;list-style-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;-webkit-transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="m"&gt;.5s&lt;/span&gt; &lt;span class="n"&gt;ease-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="m"&gt;.5s&lt;/span&gt; &lt;span class="n"&gt;ease-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="nd"&gt;:active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;157&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;112&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.active&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;157&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;112&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.social&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.logo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('./background.jpg')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.banner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Teko&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.banner&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;News&lt;/span&gt; &lt;span class="n"&gt;Cycle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;normalize.css&lt;/code&gt; can be copied from &lt;a href="//github.com/necolas/normalize.css"&gt;this respository&lt;/a&gt;, and the expected &lt;code&gt;background.jpg&lt;/code&gt; can be downloaded from &lt;a href="https://github.com/katcosgrove/cloud-systems-101/blob/main/part-one/website/background.jpg" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To see what this looks like before we deploy it, we're going to use a tool called &lt;code&gt;live-server&lt;/code&gt;. This isn't a real, functional webserver, but it's a useful tool during development for static websites. It will reload automatically when a change is detected, so you don't have to wait for deploys.&lt;/p&gt;

&lt;p&gt;Install it with this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install -g live-server&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And run it from the same directory as your &lt;code&gt;index.html&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;live-server&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Your browser should automatically open to localhost:8080, but if it doesn’t, navigate there yourself and you’ll see your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring an S3 Bucket
&lt;/h2&gt;

&lt;p&gt;It’s time to deploy this thing to S3. From your project’s root, run &lt;code&gt;source venv/bin/activate&lt;/code&gt; to make sure we’re in a virtual environment, and run &lt;code&gt;pip3 install pulumi-aws&lt;/code&gt;. From now on, we’ll be working in &lt;code&gt;__main__.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For imports, we need the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAsset&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pulumi_aws&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re going to be reading files from the operating system and working with JSON, so we need the first three packages from the standard Python library. We’re also going to be using Pulumi. You will already have the &lt;code&gt;pulumi&lt;/code&gt; package installed, but we also need the AWS provider, which we just installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;web_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3-website-bucket&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;website&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketWebsiteArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;index_document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are instantiating an s3 Bucket object and assigning it to the &lt;code&gt;web_bucket&lt;/code&gt; variable. S3 buckets are capable of hosting websites, but we have to tell the bucket that’s what it’s going to be doing and what it should expect the index document to be called. Ours is just called &lt;code&gt;index.html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;content_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;website&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;filepath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mimetypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;guess_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;web_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;FileAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This codeblock defines our content directory (the &lt;code&gt;website&lt;/code&gt; folder containing your site files, from earlier) and reads each of them in, before creating using the Pulumi AWS module to create an S3 bucket object for that file. We are also including a little bit of metadata for each file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;public_read_policy_for_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2012-10-17&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Statement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Principal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function sets some permissions for our bucket. Since this should be publicly-accessible, we need to allow anyone the ability to read the files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="n"&gt;bucket_policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucket-policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;public_read_policy_for_bucket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we assign the ID of our bucket as the bucket's name, then create a policy for it that includes its name and the policy we defined in the previous step. We're almost done!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bucket_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;web_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;website_url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;web_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;website_endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These last two lines are using Pulumi to export some data for us to interact with. The first is the ID of the bucket so we know which one we're working with, and the second is the website endpoint created by S3 once the bucket is online, so we know where to go to see our site!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying
&lt;/h2&gt;

&lt;p&gt;All that's left to do is deploy! From your project's root directory, define the AWS region you want to work in by running &lt;code&gt;pulumi config set aws:region us-west-2&lt;/code&gt;, then run &lt;code&gt;pulumi up&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Pulumi will first give you an overview of the expected changes and ask if you want to perform the update. In my case, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;     Type                    Name                Plan
 +   pulumi:pulumi:Stack     static-site-s3-dev  create
 +   ├─ aws:s3:Bucket        s3-website-bucket   create
 +   ├─ aws:s3:BucketObject  index.html          create
 +   ├─ aws:s3:BucketObject  style.css           create
 +   ├─ aws:s3:BucketObject  normalize.css       create
 +   ├─ aws:s3:BucketObject  background.jpg      create
 +   └─ aws:s3:BucketPolicy  bucket-policy       create
Resources:
    + 7 to create
Do you want to perform this update?  &lt;span class="o"&gt;[&lt;/span&gt;Use arrows to move, enter to &lt;span class="k"&gt;select&lt;/span&gt;, &lt;span class="nb"&gt;type &lt;/span&gt;to filter]
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;yes
  &lt;/span&gt;no
  details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes smoothly, you'll see more feedback from Pulumi showing each resource that was created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;     Type                    Name                Status
 +   pulumi:pulumi:Stack     static-site-s3-dev  created
 +   ├─ aws:s3:Bucket        s3-website-bucket   created
 +   ├─ aws:s3:BucketObject  index.html          created
 +   ├─ aws:s3:BucketObject  style.css           created
 +   ├─ aws:s3:BucketObject  normalize.css       created
 +   ├─ aws:s3:BucketObject  background.jpg      created
 +   └─ aws:s3:BucketPolicy  bucket-policy       created
Outputs:
    bucket_name: &lt;span class="s2"&gt;"s3-website-bucket-89f0341"&lt;/span&gt;
    website_url: &lt;span class="s2"&gt;"s3-website-bucket-89f0341.s3-website-us-west-2.amazonaws.com"&lt;/span&gt;
Resources:
    + 7 created
Duration: 6s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;Outputs&lt;/code&gt; section there. Those are the two things we exprted at the very end of our &lt;code&gt;__main__.py&lt;/code&gt;: the ID of the bucket, and the website URL. Click on the website URL, and you'll see your personal site, deployed to AWS S3 and publicly accessible by anyone! If you don't want to keep this up, run &lt;code&gt;pulumi destroy&lt;/code&gt; to tear down all of these resources, and you're done!&lt;/p&gt;

&lt;p&gt;We're not quite done yet, though—this site is a little slow, and it's not very interactive. Maybe we want some kind of database associated with it, or the convenience of containers, or the scalability of Kubernetes. Stay tuned for the next tutorial in this series, where we'll keep building on this website to introduce more modern cloud engineering tools until it looks like something you'd happily call production!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>cloud</category>
      <category>iac</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Dodging Docker Hub Limits with Artifactory</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Wed, 03 Feb 2021 21:16:54 +0000</pubDate>
      <link>https://dev.to/katcosgrove/dodging-docker-hub-limits-with-artifactory-1j36</link>
      <guid>https://dev.to/katcosgrove/dodging-docker-hub-limits-with-artifactory-1j36</guid>
      <description>&lt;p&gt;There’s been some chatter about the partnership between &lt;a href="https://jfrog.com/blog/jfrog-docker-partnership-for-dockerhub/" rel="noopener noreferrer"&gt;JFrog and Docker&lt;/a&gt;, most of it around this bit here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;JFrog Artifactory as a pull-through cache for Docker Hub&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;By taking advantage of JFrog Artifactory as a local container cache, coupled with limitless Docker Hub access, enterprise developers will reap a variety of benefits, including:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;A boost to developer productivity. Enterprise developers will get faster and more responsive access to containers by standing up JFrog Artifactory as their local container cache with no Docker Hub limitations.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Optimized usage of IT resources. By caching Docker images locally on JFrog Artifactory, external traffic on the developers’ networks is reduced, lowering their companies’ bandwidth consumption. In addition, it lessens the load on Docker Hub’s infrastructure, which benefits the overall DevOps community.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounds cool. Useful. Punchy. Everyone loves words like “productivity,” “faster,” and “optimized.” If you like the sound of all of this but aren’t super sure what it actually means or how to implement it, I’m here to help!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s a Docker registry pull-through cache?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the context of Docker, a pull-through cache is a registry that acts as an intermediary between you and Docker Hub. A pull-through cache is self-populating; that is, if you attempt to pull an image from the cache that it doesn’t already have, it will go get it from Docker Hub for you, cache a copy, and then hand it over to you. From then on, all requests for that image come from your cache first, rather than straight from Docker Hub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neat. Why would I want to do that though?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Speed and resource management. If you have a bunch of different things pulling that image, going out to Docker Hub every time is a lot of internet traffic. With a local pull-through cache, the request doesn’t have to go out to Docker Hub unless you try to pull an image tag that your cache doesn’t have. There’s also a security aspect to this, since you’re now in control of your dependencies.&lt;/p&gt;

&lt;p&gt;Since you are now only pulling an image from Docker Hub when you request something that doesn’t exist in your pull-through cache, it will take much longer for you to hit Docker Hub’s rate limits, whether you’re pulling anonymously or have your pull-through cache configured to authenticate with Docker Hub. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does JFrog Artifactory have to do with this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simply using a pull-through cache, regardless of the registry you use to do it, means you get more mileage out of Docker Hub’s rate limit on image pulls. However, because of our fancy new partnership with Docker, using &lt;a href="https://jfrog.com/artifactory/" rel="noopener noreferrer"&gt;Artifactory&lt;/a&gt; as your pull-through cache means you’re completely exempt from Docker Hub’s rate limiting. Pull to your heart’s content. Or your CI’s content, whatever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rad. How do I set that up?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Artifactory, you’ll need &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Docker+Registry" rel="noopener noreferrer"&gt;Docker repositories&lt;/a&gt;. The Quick Setup wizard will automatically create three of them for you: &lt;em&gt;docker-local&lt;/em&gt; (for your images), &lt;em&gt;docker-remote&lt;/em&gt; (the pull-through cache), and &lt;em&gt;docker&lt;/em&gt; (a virtual repository that kind of acts as an envelope around your local and remote repositories).&lt;/p&gt;

&lt;p&gt;To treat Artifactory as a pull-through cache, from here, make your requests against the virtual repository. For example, if I wanted to pull a specific version of Ubuntu, I would do this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker pull katc.jfrog.io/docker/ubuntu:16:04&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To break that down a bit, &lt;code&gt;katc.jfrog.io&lt;/code&gt; is the address of my JFrog platform, &lt;code&gt;docker&lt;/code&gt; is the name of my virtual repository, &lt;code&gt;ubuntu&lt;/code&gt; is the image I want, and &lt;code&gt;16.04&lt;/code&gt; is the specific tag. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8qgckq1anqzqm1x3s38u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8qgckq1anqzqm1x3s38u.png" alt="An image of the command  raw `docker pull katc.jfrog.io/docker/ubuntu:16.04` endraw  with red curly braces separating out the sections of the command and describing them, as in the paragraph above this image." width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When that command runs, it will first check my Docker repositories in Artifactory for a matching image and tag. If it isn’t there, Artifactory will go out and pull it from Docker Hub for me, cache it in the docker-remote repository, then serve it to me. No additional configuration is required for this behavior, and because we’ve partnered with Docker, the request won’t count against your rate limit whether your remote repository is authenticated with Docker Hub or not. Cool, right? This partnership &lt;em&gt;does&lt;/em&gt; also apply to the cloud free tier of the JFrog Platform -- &lt;a href="http://bit.ly/KatArticle" rel="noopener noreferrer"&gt;sign up here&lt;/a&gt; to see for yourself!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>artifactory</category>
      <category>devops</category>
    </item>
    <item>
      <title>DevOps 101: Package Management</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Wed, 25 Nov 2020 16:14:39 +0000</pubDate>
      <link>https://dev.to/jfrog/devops-101-package-management-1i14</link>
      <guid>https://dev.to/jfrog/devops-101-package-management-1i14</guid>
      <description>&lt;p&gt;When you’re new to an industry, you encounter a lot of new concepts. This can make it really difficult to get your feet underneath you on an unfamiliar landscape, especially for junior engineers. What’s all this jargon? What does DevOps really mean? What’s all this software? Is DevOps a methodology, or a toolset? Is any of this actually going to make my life easier, or is it just a bunch of industry buzzwords? A lot of the documentation out there assumes you already have additional context and experience, or are proficient in some related tooling, and that doesn’t exactly make it easy to learn. DevOps has a ton of jargon in it, though. We're absolutely swimming in abbreviations and abstractions, and sometimes it's difficult to define a term satisfactorily without needing to define three more for context. It’s like running into a brick wall. &lt;/p&gt;

&lt;p&gt;Here, I'll explain package managers.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What even is that?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;From Wikipedia, a package manager or package-management system is a collection of software tools that automates the process of installing, upgrading, configuring, and removing computer programs for a computer's operating system in a consistent manner.&lt;/p&gt;

&lt;p&gt;If you’re a developer, you have probably already used one of these. For instance, if you’re writing Node.js, you’re probably using NPM. That’s a package manager. If you’re using Linux and you’ve ever installed something with apt-get, APT is a package manager. In plain English, a package manager is what handles the heavy lifting involved in installing something (including that thing’s dependencies), validating that it is what it says it is (to an extent, by comparing checksums), keeping track of versions, upgrades, and uninstalling. This is accomplished in part through the use of a large amount of metadata, defining characteristics about a particular package, alongside the actual application binary. Modern package managers are why you can reliably run &lt;code&gt;pip install Django=-1.3.3&lt;/code&gt; or whatever and be pretty sure you are getting version 1.3.3 of Django, or just &lt;code&gt;pip install Django&lt;/code&gt; and take whatever is the most recent version.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Before Times&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We didn’t always have this, though. The earliest versions of what you could call a package manager are from the mid to late 90s, with Debian introducing dpkg in 1994 and RedHat’s RPM in 1997. Before then, and until package management really took off, we had to do a lot of things manually and we had way less information about the things we were installing.&lt;/p&gt;

&lt;p&gt;You would get a compressed directory, usually in the form of a .tar file. Decompress that, and you would find a readme file with instructions to follow. Some kind of config script would be there which, when run, would tell your C compiler what to do, where new binaries should go, what application dependencies to look for and where, etc. If anything went wrong, it would exit and you would have to go install some more dependencies. If it found everything it needed and the config script completed, it would spit out a Makefile. Run the &lt;code&gt;make&lt;/code&gt; command and, if everything compiles correctly, run &lt;code&gt;make install&lt;/code&gt; to finally install the application. Updates were just as involved, if not more so. Obviously, this is a time-consuming process. Imagine having to do that for every piece of software on your computer, and every piece of software required to run all of it. A lot of things could go wrong on your journey from downloading a .tar file to actually getting the thing installed.&lt;/p&gt;

&lt;p&gt;Understandably, everyone thought this was exhausting and the release of the first package managers for Linux was A Big Deal. It immediately changed computing and application development for the better, forever. Those early package managers pretty much just gave you install, update, and uninstall, but over time, package managers have bundled all of that work and more into simpler commands and encoded extra information alongside the application binary and its dependencies, like version numbers, checksums, dependency graphs, and more. As the popularity of package managers for Linux grew, so did the demand for something similar for other languages. Thus, a whole series of package managers for other languages were born, from CPAN for Perl to PyPi for Python to Cargo for Rust, all with the goal of making it easier to distribute and use software.&lt;/p&gt;

&lt;p&gt;This introduced a whole new problem, though. Applications are easier to install, update, manage, and remove, so we start building more complex applications, and with Continuous Integration becoming popular in the 90s, we start releasing more often, too. One organization might be using multiple languages, and we also start caring about security. This is where binary repository managers enter the playing field.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What’s a binary repository manager?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A binary repository manager is there to manage all of those binaries we now have into a system of repositories. Some of them support just one or two package types for very specialized applications, but the one I’ll talk about supports more. This isn’t the same thing as source control, which is where your code lives, but more of an extension of it. While your code might live in a repository on GitHub or BitBucket or whatever, the result of that code being built or compiled -- your artifacts -- live in a binary repository manager like &lt;a href="https://jfrog.com/artifactory/" rel="noopener noreferrer"&gt;JFrog Artifactory&lt;/a&gt;. For DevOps to really work, this is an important part of the equation. We’re delivering updates far more often now, and we need a way to organize our build artifacts in a sensible way so they’re easier for our other tools, like our CI/CD system, to interact with them and ultimately deploy the updates to the user. Without one, it’s much more difficult to track version numbers, control access, promote builds from testing to production, collect metadata, or detect security problems.  &lt;/p&gt;

&lt;p&gt;Try to imagine writing code and keeping it organized without tools like Git and GitHub. That sounds miserable, right? It’s the same for binaries and a binary repository manager, especially on teams that have multiple languages in one house. If you use a binary repository manager like Artifactory, you can store your Go, JavaScript, Docker images, Python, and Java binaries all in one tool, plus 22 other package types.&lt;/p&gt;

&lt;p&gt;Artifactory breaks up the repositories for each package type into three different classes: Local, remote, and virtual. &lt;strong&gt;Local&lt;/strong&gt; repositories are what they sound like: repositories for your build artifacts resulting from local code that exists on your machine. &lt;strong&gt;Remote&lt;/strong&gt; repositories are also fairly self-explanatory; they contain remote build artifacts, like your project's dependencies. This functions sort of like a cache, so that after the first download, your project pulls its dependencies from the associated remote repository rather than from NPM or PyPi or whatever. If you are using Docker, this is particularly helpful, since it limits the number of pulls you need to make against Docker Hub and you won't hit their new limit for pulls from anonymous users as quickly. &lt;strong&gt;Virtual&lt;/strong&gt; repositories are a little weirder -- they create a kind of envelope around the local and remote repositories for your project, and this is what you'll be interacting with most frequently.&lt;/p&gt;

&lt;p&gt;A lot of headaches are saved here, from an organizational standpoint. Things get released faster because things are more organized and easier to integrate with a CI/CD solution, and there’s no jumping around between a dozen tools that all do the same thing but for different package types. This improvement alone decreases the likelihood of a bad build making it out into the wild, because we humans are really bad at repetitive tasks, and this takes over a lot of repetition for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cool cool cool, how do I try using one of these things? Sounds enterprisey.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It's definitely a thing that's more beneficial to whole companies or dev teams than for an individual person on a side project, but it's still good to learn. There are a handful available, but Artifactory is free up to a certain amount of storage and data transfer. &lt;a href="https://jfrog.com/artifactory/start-free/#saas" rel="noopener noreferrer"&gt;Try it here&lt;/a&gt; on the cloud provider of your choice. It also comes with Xray, a vulnerability detection tool, so throw in a CI/CD system and you're getting pretty close to an end-to-end DevOps solution to play with and learn on. If you don't quite understand CI/CD (or you just need some recommendations for where to start!), check out the previous article in this series: &lt;a href="https://dev.to/jfrog/devops-101-ci-cd-49il"&gt;DevOps 101: CI/CD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summarize this for me, high school essay style.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In conclusion, package managers are a set of tools that make it easier for you to install, use, update, and remove applications. They go further than just automating the steps we used to have to take manually, with config scripts and Makefiles, by also installing your dependencies and managing a bunch of extra metadata we didn’t have clear access to before. The next leap from there is the use of a binary repository as an extension of our source code repositories, to manage all of these binaries and build artifacts produced by our package managers. Doing so gives us more insight into what’s going on with our builds, a simpler way to control who has access to what, and a place to keep all of our build artifacts regardless of the languages or tools involved in our applications. This is a boon to your developers from a sanity and organization standpoint, to your users in the form of faster updates, and to your legal team in the form of faster detection of critical vulnerabilities. The invention of the package manager is possibly one of the most important innovations in computing in decades, and a universal binary repository manager is one of the most important parts of a functional DevOps pipeline.&lt;/p&gt;

&lt;p&gt;I hope I’ve helped you understand what package management is and what it does for you. If you’re still confused, that’s okay too -- there's a lot going on in this space. Stay tuned for more!&lt;/p&gt;

&lt;p&gt;Oh, and if you have specific requests, reach out to me on &lt;a href="https://twitter.com/Dixie3Flatline" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;! My DMs are open.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>packagemanagent</category>
    </item>
    <item>
      <title>AWS CodeArtifact: Lol, who needs build integrations anyway</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Fri, 19 Jun 2020 21:41:01 +0000</pubDate>
      <link>https://dev.to/jfrog/aws-codeartifact-lol-who-needs-build-integrations-anyway-4pn2</link>
      <guid>https://dev.to/jfrog/aws-codeartifact-lol-who-needs-build-integrations-anyway-4pn2</guid>
      <description>&lt;p&gt;This article is part of a series written by JFrog's Developer Advocates. The index can be found here:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jfrog" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1129%2Fa2621fb0-c128-4891-9807-759bd3dd2955.png" alt="JFrog" width="709" height="709"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F218863%2F3dcd84ba-80bd-48e1-8804-7e3f6d7815a8.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/jfrog/jfrog-artifactory-vs-aws-codeartifact-comparison-in-10-ish-parts-521n" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;JFrog Artifactory vs AWS CodeArtifact: Comparison in 10-ish parts&lt;/h2&gt;
      &lt;h3&gt;JBaruch 🎩 for JFrog ・ Jun 19 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#jfrog&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#artifactory&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#codeartifact&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;AWS has released &lt;a href="https://aws.amazon.com/about-aws/whats-new/2020/06/introducing-aws-codeartifact-a-fully-managed-software-artifact-repository-service/" rel="noopener noreferrer"&gt;CodeArtifact&lt;/a&gt;, an artifact repository manager, and as an employee of &lt;a href="https://jfrog.com/artifactory/" rel="noopener noreferrer"&gt;JFrog&lt;/a&gt; I’ve obviously got some opinions. My boss said I didn’t have to censor snark for this, so before I get to the point, I just want to say this: &lt;/p&gt;

&lt;p&gt;I know naming things is hard. There are a million jokes about the difficulties of naming things in computing. AWS already has CodeCommit, CodeBuild, CodeDeploy, CodePipeline, and CodeStar, so maybe they felt backed into a corner because a convention had been established. But, y’all… there is already a completely unrelated product called &lt;a href="https://aws.amazon.com/artifact/" rel="noopener noreferrer"&gt;AWS Artifact&lt;/a&gt;. Obviously this doesn’t impact the quality or usability of the tool and it’s just another example of Amazon’s tendency to forget about the existence of its own tools (understandable, there are SO MANY of them), but it’s confusing and made me chuckle so I had to bring it up.&lt;/p&gt;

&lt;p&gt;Anyway, onwards to the point.&lt;/p&gt;

&lt;p&gt;Do you like vendor lock-in? So does Amazon. As far as I can tell, CodeArtifact didn’t ship with any integrations for the CI/CD tools you’re probably already using and there’s no word on whether or not integrations will eventually be added. If you want to use CodeArtifact, but you’re already committed to Jenkins and moving everything over would be a bear, you’re in an awkward spot. Yes, there’s a CLI. You can still kludge something together. That’s extra work for something that should be easy, though. Your alternative is to migrate everything over to their ecosystem so you can use AWS CodePipeline, the literal only thing it integrates directly with. Either way, it’s inconvenient at best and a huge pain in the ass at worst. Maybe it’s fine if you’re starting a new project from scratch and are okay with living in Jeff’s house forever, but it’s kind of… short-sighted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftr3nk7dk5o5vwfqzgoq9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftr3nk7dk5o5vwfqzgoq9.jpg" alt="A diagram displaying all of the integrations JFrog supports." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, JFrog has its own CI/CD tool. It’s called &lt;a href="https://jfrog.com/pipelines/" rel="noopener noreferrer"&gt;Pipelines&lt;/a&gt;, and its integration with Artifactory is really, really smooth. We’ve got a &lt;a href="https://www.jfrog.com/confluence/display/CLI/CLI+for+JFrog+Artifactory" rel="noopener noreferrer"&gt;CLI&lt;/a&gt; and a &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Artifactory+REST+API" rel="noopener noreferrer"&gt;REST API&lt;/a&gt;, too. However, we also have &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Build+Integration" rel="noopener noreferrer"&gt;direct integrations&lt;/a&gt; for the tools you might already be using -- Jenkins, TeamCity, Bamboo, Azure DevOps, GitHub Actions, and BitBucket Pipelines -- because we want you to be able to use Artifactory and everything it offers regardless of whatever else is going on in your ecosystem. Like, it’d be cool if you used Pipelines instead, but we’re not going to make it difficult to keep using the tool you already like if you don’t want to switch, and we’re not going to cut you off from some of the best features of Artifactory as some kind of punishment for choosing to stick with Jenkins.&lt;/p&gt;

&lt;p&gt;What’s that feature I’m talking about?  Build-info.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jfrog.com/confluence/display/JFROG/BuildInfo" rel="noopener noreferrer"&gt;Build-info&lt;/a&gt; is all the information collected by the build agent, which includes details about the build. The build-info includes a list of project modules, artifacts, dependencies, environment variables and more. When using one of the JFrog clients to build the code, the client can collect the build-info and publish it to Artifactory. When the build-info is published to Artifactory, all the published details become visible in the Artifactory UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5dl8t37774t3sk30uuzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5dl8t37774t3sk30uuzr.png" alt="Alt Text" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You get access to all of that data right in Artifactory as long as you’re using JFrog Pipelines, the JFrog CLI, or one of the six other CI/CD tools we provide integrations for. Which is six more options than AWS is giving you with CodeArtifact, and a whole lot more context for what’s going on with your builds.&lt;/p&gt;

&lt;p&gt;Return to the index:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/jfrog" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1129%2Fa2621fb0-c128-4891-9807-759bd3dd2955.png" alt="JFrog" width="709" height="709"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F218863%2F3dcd84ba-80bd-48e1-8804-7e3f6d7815a8.jpg" alt="" width="400" height="400"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/jfrog/jfrog-artifactory-vs-aws-codeartifact-comparison-in-10-ish-parts-521n" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;JFrog Artifactory vs AWS CodeArtifact: Comparison in 10-ish parts&lt;/h2&gt;
      &lt;h3&gt;JBaruch 🎩 for JFrog ・ Jun 19 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#jfrog&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#artifactory&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#codeartifact&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>aws</category>
      <category>jfrog</category>
      <category>artifactory</category>
      <category>codeartifact</category>
    </item>
    <item>
      <title>DevOps 101: CI/CD</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Thu, 16 Apr 2020 21:55:01 +0000</pubDate>
      <link>https://dev.to/jfrog/devops-101-ci-cd-49il</link>
      <guid>https://dev.to/jfrog/devops-101-ci-cd-49il</guid>
      <description>&lt;p&gt;When you’re new to an industry, you encounter a lot of new concepts. This can make it really difficult to get your feet underneath you on an unfamiliar landscape, especially for junior engineers. In this series, I’ll cover &lt;a href="https://jfrog.com/devops-tools/" rel="noopener noreferrer"&gt;tools&lt;/a&gt; and terminology common to the DevOps space, plus the occasional newbie-friendly tutorial for emerging or established technologies. If you have a request or suggestion, let me know!&lt;/p&gt;

&lt;p&gt;Today, I’ll break down CI/CD.&lt;/p&gt;

&lt;h4&gt;
  
  
  What does that even stand for?
&lt;/h4&gt;

&lt;p&gt;The CI stands for &lt;strong&gt;Continuous Integration&lt;/strong&gt;, and the CD can stand for either &lt;strong&gt;Continuous Delivery&lt;/strong&gt; or &lt;strong&gt;Continuous Deployment&lt;/strong&gt;. Yes, they do mean different things. I know, I know, this sounds like it’s already getting complicated, but I promise it’s not so bad.&lt;/p&gt;

&lt;h4&gt;
  
  
  What does Continuous Integration mean?
&lt;/h4&gt;

&lt;p&gt;Practicing Continuous Integration means merging all developers’ working codebase with the source, multiple times a day. Doing this requires a series of automated build and unit tests to ensure none of the proposed changes cause problems, but the result is that bugs and integration issues are discovered much earlier in the development process. It also forces engineers to write code that’s more modular, making it easier to support later on.&lt;/p&gt;

&lt;p&gt;Continuous Integration has been a thing since the early 90s. Though it hasn’t always been called that, and some of the implementation has changed, the spirit remains the same: merge changes into source in smaller but more frequent increments, test that the project still builds and runs with those changes, and make sure your engineers are all working on the most recent version of the source. Do this, and you won’t end up with a ton of merge conflicts or surprise problems when it comes time to build. &lt;/p&gt;

&lt;h4&gt;
  
  
  Okay, what’s the difference between Continuous Delivery and Continuous Deployment?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Continuous Delivery&lt;/strong&gt; means what it says on the box: your software updates are continuously delivered. In concert with Continuous Integration, this means you should have the ability to deploy a new build very rapidly because you’ve automated some quality gates that would otherwise need to be performed manually, like building and testing. That reduction in manual labor means you get to release a bunch of small changes, rather than one huge update every couple of months. Since you’re now making smaller, incremental changes, you can also be more confident that your release isn’t going to break when you deploy to your users. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continuous Deployment&lt;/strong&gt; is similar, but it goes one step further -- deployment is automated, too. In Continuous Delivery, there is still a manual quality gate involved before an update is out in the wild. This is a controversial step for some, and requires a lot of trust in your system, but I’m personally a huge fan of it. For a modern &lt;a href="https://jfrog.com/pipelines/" rel="noopener noreferrer"&gt;DevOps pipeline&lt;/a&gt; (and thus, you) to be as efficient as possible, human involvement has to be removed wherever possible. I say this a lot, but we are really, really bad at repetitive tasks -- we get bored, we get distracted, and we’re SLOW. Write good, comprehensive tests and automate everything you can, then accept that you absolutely are going to deploy a bad update eventually, whether a human is involved in clicking the Big Green Button or not. &lt;/p&gt;

&lt;h4&gt;
  
  
  Wait, so what’s CI/CD? What does it get me? Go high-level.
&lt;/h4&gt;

&lt;p&gt;CI/CD is just the term for the marriage of these concepts. It’s an important part of DevOps, since automation and efficiency is what we’re all about, and one doesn’t really work in the context of DevOps without the other.&lt;/p&gt;

&lt;p&gt;Implementing CI/CD practices gets you a faster, more reliable release cycle. You can add new features or bug fixes way, way faster, since you know your engineers are all working from the most recent source, you know there are unit and integration tests and you know it builds. There aren’t a bunch of manual steps involved for the engineers or QA or whoever else you might have managing quality gates; instead, somebody pushes code or opens a pull request and those steps are taken care of by your CI/CD tooling.&lt;/p&gt;

&lt;h4&gt;
  
  
  How does this actually work?
&lt;/h4&gt;

&lt;p&gt;To start, you need a CI/CD tool. This is what’s going to automate a bunch of manual processes for you. It does take some time to set up at the start of a new project, but personally, I’m the flavor of lazy where I’m willing to spend extra time at the beginning to make sure I don’t have to do a bunch of repetitive, manual tasks every single time I push code later on. The specifics of configuring any of these tools varies, so check the documentation for your chosen tool, but broadly they all work the same way:&lt;/p&gt;

&lt;p&gt;You set something as a trigger, like telling it to watch your source repository for a commit or a merge. You then configure a series of steps, each with pass/fail conditions, like telling it how to run your unit tests, build, &lt;a href="https://www.jfrog.com/confluence/display/JFROG/CI-CD+Integration+with+Xray" rel="noopener noreferrer"&gt;scan for vulnerabilities&lt;/a&gt;, or deploy your application. With a sufficiently detailed CI/CD pipeline, you don’t have to do anything but write code and push it -- the system handles everything else for you. It’s great.&lt;/p&gt;

&lt;h4&gt;
  
  
  Word, sounds fantastic, sign me up.
&lt;/h4&gt;

&lt;p&gt;If you’re already using some JFrog tools like Artifactory or Xray, it makes sense to stay in the same ecosystem and give Pipelines a try. That way, everything is accessible from one UI. Pipelines integrates with most other DevOps tools and handles a ton of actions natively, so it minimizes the possibility of ending up with the infrastructure version of Frankenstein’s Monster. Configuration is just YAML. If you want to try Pipelines, along with Artifactory and XRay, there’s a cloud-hosted &lt;a href="https://jfrog.com/artifactory/free-trial/#saas" rel="noopener noreferrer"&gt;trial here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For freemium solutions (free usage is limited, but generally fine for personal projects or small stuff), I really like CircleCI and TravisCI. Both are cloud solutions with a good number of built-in integrations, both are pretty easy to configure, and both have support for a wide range of popular programming languages. There are differing limitations in what you can use as your version control and build environments.&lt;/p&gt;

&lt;h4&gt;
  
  
  Summarize this like you’re writing an essay in high school.
&lt;/h4&gt;

&lt;p&gt;In conclusion, CI/CD is a combination of methodology and tooling with the goal of increasing your speed and efficiency as a developer by automating tasks like building, testing, and deploying so that you can do all of those things more often. The benefit to you is more frequent software releases, earlier detection of bugs, and bad releases making it to production less often. There are several tools out there that help you accomplish this goal, from freemium tools like TravisCI or CircleCI to enterprise-scale tools with additional features like JFrog Pipelines.&lt;/p&gt;

&lt;p&gt;I hope I’ve helped you understand what CI/CD is and what it does for you. If you’re still confused, that’s okay too -- this can be kind of a “big” problem and some of it is still in flux. Feel free to get in touch through the comments or on Twitter at &lt;a href="https://twitter.com/Dixie3Flatline" rel="noopener noreferrer"&gt;@Dixie3Flatline&lt;/a&gt; if you have more questions. Stay tuned for the next article in this series, and let me know if you have a request!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cicd</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Easy Automatic Vulnerability Detection in the JFrog Platform</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Wed, 04 Mar 2020 02:38:04 +0000</pubDate>
      <link>https://dev.to/jfrog/easy-automatic-vulnerability-detection-in-the-jfrog-platform-5928</link>
      <guid>https://dev.to/jfrog/easy-automatic-vulnerability-detection-in-the-jfrog-platform-5928</guid>
      <description>&lt;p&gt;JFrog has released a Unified &lt;a href="https://jfrog.com/platform/" rel="noopener noreferrer"&gt;DevOps Platform&lt;/a&gt;, putting the entire product suite under one roof. You’ll have a comprehensive view of what’s going on with your artifacts, more clear permissions control, quicker access to your build pipelines, and my personal favorite: vulnerability scanning baked right in. Let me show you around!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flwr3agme9jmj2sth4ua1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flwr3agme9jmj2sth4ua1.png" alt="A picture of the main dashboard with overview of services available." width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks pretty clean, right? One login gets you access to the entire platform. Your dashboard provides some high-level information on the status of your services, plus usage trends across package types. On the left, you have direct access to your JFrog services: Artifactory, Distribution, Pipelines, and the one I’ll be talking about: Xray. &lt;/p&gt;

&lt;p&gt;JFrog Xray is a universal component DevSecOps tool that protects you from &lt;a href="https://jfrog.com/xray/" rel="noopener noreferrer"&gt;security vulnerabilities&lt;/a&gt; and license compliance issues that may be hidden in your software. Let’s take a look at the Security and Compliance tab to see how we can automatically trigger warnings, fail a build, or even block downloads based on a threshold you set.&lt;/p&gt;

&lt;p&gt;JFrog Xray functions on a system of &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Xray+Security+and+Compliance" rel="noopener noreferrer"&gt;Policies and Watches&lt;/a&gt;. Policies allow us to define security and license compliance behaviors specific to your organization. Once they are defined, they are enforced by applying them to Watches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ovlk53t10ad5nxpahos.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ovlk53t10ad5nxpahos.png" alt="A picture of a n Xray Policy, set to trigger when a High vulnerability is found. It sends an email and fails the build." width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, the Policy is triggered when a vulnerability categorized as “high” is found. Xray’s scan is recursive, so it looks at the smallest components affecting your packages. Its vulnerability database is multi-faceted, using public information, its own private database, and Risk Based Security’s VulnDB. If the vulnerability is known, it’ll find it. This information is presented to you in a couple of places, but the easiest to digest is in the form of an Impact Analysis Graph, which I’ll show you later. &lt;/p&gt;

&lt;p&gt;Once a Policy has been defined, you must assign it to a Watch. A Watch is a collection of repositories, builds, and release bundles that Xray should be scanning. I like to organize these by project, but it’s up to you how to handle that. Policies can be assigned to multiple Watches, so there’s no need to define a ton of them.&lt;/p&gt;

&lt;p&gt;Now that you have a Policy and it’s assigned to a Watch, you’ll start to see Xray data in a few different places elsewhere within the Platform. The fact that everything is now organized within a single pane of glass makes staying on top of any potential security or license issues really, really easy. Let’s hop over to the Artifactory tab and take a look at what’s going on with some of our builds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdninr4huywodp92hgcws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdninr4huywodp92hgcws.png" alt="A picture of all of the builds for a specific project." width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can already see from here that there are some problems. Each of these builds has an Xray status of “high” -- something risky is happening with some component here. We need more information, though. What’s the actual vulnerability? Is this something we really need to act on? What specific component is causing the problem, and what version do we need to update it to if we want to resolve the vulnerability? Click on the build and find out. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fumhjknyv7ec5lb0pb74y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fumhjknyv7ec5lb0pb74y.png" alt="An list of all the vulnerabilities found in a particular build." width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under the Xray Data tab for the most recent build, we get a TON of information. Everything that violates our set Security and License Policies is here. We get some high-level information immediately: a summary of the vulnerability, the severity, which Policy was triggered, the component in question, the affected versions, and the fix versions, if any. Click on one of them, and you get even more information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7vekug8f1cby50la9lq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7vekug8f1cby50la9lq2.png" alt="An impact analysis graph showing specifically where the vulnerability is." width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the right is the Impact Analysis Graph I mentioned earlier. Xray has peeled open our package and drilled down through the layers to find an issue with Jackson. To assist in resolving the vulnerability, we also get a description of the problem, references, CVEs, and more. You know exactly which component is causing a problem, and very clearly what the problem is. There’s no guesswork involved in fixing it, and this information is readily available to you no matter where you are in the Platform.&lt;/p&gt;

&lt;p&gt;Collecting all of JFrog’s products into one platform gives your team a clear view into every aspect of your artifact’s lifecycle. Everything you want to know is easily within reach, in just a click or two. If you really care about automating away the trouble of dealing with security vulnerabilities and license violations, Xray is pretty compelling. Stay tuned for walkthroughs of the rest of the new JFrog Platform, and don’t hesitate to reach out to me if you have questions! &lt;/p&gt;

</description>
      <category>devops</category>
      <category>devsecops</category>
      <category>security</category>
      <category>jfrog</category>
    </item>
    <item>
      <title>DevOps 101: Container Registries</title>
      <dc:creator>Kat Cosgrove</dc:creator>
      <pubDate>Mon, 16 Dec 2019 20:42:45 +0000</pubDate>
      <link>https://dev.to/jfrog/devops-101-container-registries-1lil</link>
      <guid>https://dev.to/jfrog/devops-101-container-registries-1lil</guid>
      <description>&lt;p&gt;When you’re new to an industry, you encounter a lot of new concepts. This can make it really difficult to get your feet underneath you on an unfamiliar landscape, especially for junior engineers. In this series, I’ll cover tools and terminology common to the DevOps space, plus the occasional newbie-friendly tutorial for emerging or established technologies. If you have a request or suggestion, let me know! &lt;/p&gt;

&lt;p&gt;Today, I’ll break down container registries. &lt;/p&gt;

&lt;h4&gt;
  
  
  What’s a container registry?
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;container registry&lt;/strong&gt; is a tool for hosting, versioning, and distributing container images within repositories. They can be public (like Docker Hub) or private (like &lt;a href="https://jfrog.com/container-registry/" rel="noopener noreferrer"&gt;JFrog Container Registry&lt;/a&gt;). If you’ve used Docker, you’ve pulled an image from a container registry before. There are several out there, both public and private, free and paid, but I’ll mostly be focusing on JFrog Container Registry since it’s free, can run locally or on a cloud provider, and provides a few other nice-to-have features that other services don’t, like a Helm repository and advanced metadata.&lt;/p&gt;

&lt;h4&gt;
  
  
  What’s the difference between public and private container registries?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Public registries&lt;/strong&gt; allow anyone to create a repository and host container images there for other people to use. Getting started with a public registry is fast, since you don’t have to deal with any of the infrastructure. However, public registries do sacrifice security in exchange for that ease of use. If using standard images works for your project, and you aren’t concerned about security, a public registry might be the right choice for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private registries&lt;/strong&gt; are hosted by the organization that will be using them. JFrog Container Registry can be hosted either locally on your hardware (on-prem) or with a cloud provider like Google, Amazon, or Microsoft. Hosting your own container registry gives you fine-grained access control and the ability to define your own security policies. Since the organization is in complete control of the images, repositories, and access control for those things, they can be certain that container images are what they say they are and that no one has access to things they shouldn’t. In the case of JFrog Container Registry, you also have the option to organize your images into various local, remote, generic, and virtual repositories, depending on your needs. For larger organizations, this additional reliability and security is worth the effort involved in setting up the infrastructure itself.&lt;/p&gt;

&lt;h4&gt;
  
  
  What are all these different types of repositories?
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Local repositories&lt;/strong&gt; are exactly what they sound like -- locally-managed repositories into which you deploy artifacts. Pretty simple.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote repositories&lt;/strong&gt; are used as caching proxies for remotely-managed repositories. These are a little more complicated, as you must define the behavior of the caching and proxying. It is not the same as a mirror; an artifact only exists in a remote repository once it is requested by something else, according to the configuration you define.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generic repositories&lt;/strong&gt; have no specific package type associated with them. Any time you create a repository, you must define a package type. JFrog Container Registry uses this to do some behind-the-scenes work for you, indexing the artifacts and calculating metadata for your packages to optimize performance. Generic repositories have no package type, so you can put pretty much whatever you want in here, but you do lose out on some features you would see otherwise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual repositories&lt;/strong&gt; bundle up a collection of various remote and local repositories with the same package type into a single URL.&lt;/p&gt;

&lt;h4&gt;
  
  
  Neat. How do I try one of these things?
&lt;/h4&gt;

&lt;p&gt;The quickest and easiest way is with the &lt;a href="https://jfrog.com/container-registry/cloud-registration/" rel="noopener noreferrer"&gt;cloud hosted&lt;/a&gt; version of JFrog Container Registry, since it’s still free-ish (you pay for usage on the cloud provider over a certain amount), mostly configures itself, and doesn’t require you to set up a reverse proxy. Sign up here with your preferred flavor of cloud provider and follow along with the automated setup wizard to configure some initial options and create your default repositories.&lt;/p&gt;

&lt;p&gt;Once that’s done, it’s really easy to interact with. You can use the native Docker client, just like you would with Docker Hub. Just log in:&lt;/p&gt;

&lt;p&gt;docker login ${server-name}-{repo-name}.jfrog.io&lt;/p&gt;

&lt;p&gt;To push an image, give it a tag and push:&lt;/p&gt;

&lt;p&gt;docker tag  ${server-name}-{repo-name}.jfrog.io/&lt;br&gt;
docker push ${server-name}-{repo-name}.jfrog.io/&lt;/p&gt;

&lt;p&gt;Pulling an image is easy, too:&lt;/p&gt;

&lt;p&gt;docker pull ${server-name}-{repo-name}.jfrog.io/&lt;/p&gt;

&lt;p&gt;Setup and usage is a bit more complicated with the on-prem version, but fortunately, the &lt;a href="https://www.jfrog.com/confluence/display/JCR/Getting+Started+with+JFrog+Container+Registry+as+a+Docker+Registry" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; is pretty explicit. &lt;/p&gt;

&lt;h4&gt;
  
  
  Summarize this for me.
&lt;/h4&gt;

&lt;p&gt;If your organization or team is getting serious about cloud native development, a private container registry is probably something you’re going to work with at some point. The goal of a private registry is to provide you with a place to organize, version, and deploy your container images with increased security options and better integration into your existing CI/CD workflows when compared to a public registry, whether it’s hosted on a local server or out on the cloud. &lt;a href="https://jfrog.com/container-registry/" rel="noopener noreferrer"&gt;JFrog Container Registry&lt;/a&gt; is free on-prem or freemium on any of the three major cloud providers and gives you everything the other services on the market do, plus some extra goodies like a Helm repository and rich metadata.&lt;/p&gt;

&lt;p&gt;I hope I’ve helped you understand what this tool does for you and given you a reasonable pushing-off point into using one. If you’re still confused, that’s okay too -- feel free to get in touch if you have more questions. Stay tuned for the next article in this series, and let me know if you have a request for deep dives or demos!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
