<?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: Eric Yi</title>
    <description>The latest articles on DEV Community by Eric Yi (@ericyi).</description>
    <link>https://dev.to/ericyi</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%2F617818%2Ffdd6efe7-0c42-4cd3-ba43-cf8eda426e9e.jpeg</url>
      <title>DEV Community: Eric Yi</title>
      <link>https://dev.to/ericyi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ericyi"/>
    <language>en</language>
    <item>
      <title>Building the Docker Image: Expert Tips and Tricks</title>
      <dc:creator>Eric Yi</dc:creator>
      <pubDate>Sun, 25 Dec 2022 23:35:48 +0000</pubDate>
      <link>https://dev.to/ericyi/building-the-docker-image-expert-tips-and-tricks-29d0</link>
      <guid>https://dev.to/ericyi/building-the-docker-image-expert-tips-and-tricks-29d0</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Docker has revolutionised the way we deploy and run applications, by providing a lightweight and portable way to package and distribute software. Building a high-quality Docker image is essential for ensuring that your application runs smoothly and efficiently in production. In this blog post, we will discuss some key considerations and best practices for building a high-quality Docker image from my own experience. &lt;/p&gt;

&lt;h2&gt;
  
  
  How Container Works
&lt;/h2&gt;

&lt;p&gt;To understand how container works, we need to understand how Linux overlay work. &lt;/p&gt;

&lt;p&gt;Linux overlay is a filesystem that allows the user to stack multiple filesystems on top of one another. Each filesystem has its own set of files and directories that are independent from the underlying filesystem. This allows to merge two directories together and present them as a single unified filesystem. One of these directories, known as the "lower" directory, is read-only, while the other directory, known as the "upper" directory, can be both read from and written to. When a process reads a file from the overlay filesystem, the overlays driver first checks for the file in the upper directory. If it is not found there, it falls back to the lower directory. When a process writes a file, it is always written to the upper directory. This allows you to modify or add new files in the upper directory without changing the original files in the lower directory. &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%2Fuploads%2Farticles%2F9tpvhbueucstfxsdsfhw.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%2Fuploads%2Farticles%2F9tpvhbueucstfxsdsfhw.png" alt="Freeform" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us apply this knowledge to Docker images. When pulling a new image, if layer 2 already exists, it will not pull any layers below layer 2.&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%2Fuploads%2Farticles%2Fmrzywodgifp1epje9jvv.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%2Fuploads%2Farticles%2Fmrzywodgifp1epje9jvv.png" alt="Freeform2" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Multi-Stage Builds Orders
&lt;/h3&gt;

&lt;p&gt;Using what we have learned earlier, to optimize the efficiency of your Docker builds, you should order the layers in your build from the least frequently changed to the most frequently changed. This will allow the build cache to be reusable and improve build times.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;3.7&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;buster&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;requirements&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; 
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="nx"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;working&lt;/span&gt; &lt;span class="nx"&gt;directory&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="nx"&gt;WORKDIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;system&lt;/span&gt; &lt;span class="nx"&gt;dependencies&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;essential&lt;/span&gt; &lt;span class="nx"&gt;libpq&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;Python&lt;/span&gt; &lt;span class="nx"&gt;dependencies&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;pip&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="nx"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;expose&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;
&lt;span class="nx"&gt;EXPOSE&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="nx"&gt;CMD&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;python&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app.py&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is this a good example? The answer is no. First, we should install system dependencies, then copy the requirements file only, and install Python dependencies. Always remember to order them from the less frequently changed  to the more frequently changed.&lt;/p&gt;

&lt;p&gt;Here's how we can fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;3.7&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;buster&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;system&lt;/span&gt; &lt;span class="nx"&gt;dependencies&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;essential&lt;/span&gt; &lt;span class="nx"&gt;libpq&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;requirements&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;Python&lt;/span&gt; &lt;span class="nx"&gt;dependencies&lt;/span&gt;
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="nx"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;pip&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="nx"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;working&lt;/span&gt; &lt;span class="nx"&gt;directory&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="nx"&gt;WORKDIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;expose&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;
&lt;span class="nx"&gt;EXPOSE&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="nx"&gt;CMD&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;python&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app.py&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2 - Build Environment VS Runtime Environment
&lt;/h3&gt;

&lt;p&gt;Use a multi-stage build to separate the build environment from the runtime environment. This will enable you to use a larger base image for the build stage, and then only copy the necessary files to the smaller runtime image. &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%2Fuploads%2Farticles%2Flxcwj6rfw72ml6h7wxxt.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%2Fuploads%2Farticles%2Flxcwj6rfw72ml6h7wxxt.png" alt="Freeform3" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;maven&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;3.6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;jdk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="nx"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;mvn&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;pom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt; &lt;span class="nx"&gt;clean&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt;

&lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;openjdk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;jre&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;slim&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;
&lt;span class="nx"&gt;COPY&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="cm"&gt;/*.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile uses the &lt;code&gt;maven:3.6.3-jdk-11&lt;/code&gt; base image for the build stage and the &lt;code&gt;openjdk:11-jre-slim&lt;/code&gt; base image for the runtime stage. The build stage compiles the source code and creates a JAR file, which is then copied to the runtime image. &lt;/p&gt;

&lt;p&gt;This can be especially useful if the build stage requires a lot of dependencies or tools that are not needed at runtime. For example, if the build stage uses a base image that is 1 GB in size and the runtime stage uses a base image that is 100 MB in size, using this method can potentially save 900 MB in image size.&lt;/p&gt;

&lt;h3&gt;
  
  
  3 - The Package Cache
&lt;/h3&gt;

&lt;p&gt;The package cache is a local repository of package files that are downloaded from the Internet or other sources. When you use &lt;strong&gt;&lt;code&gt;apt-get&lt;/code&gt;&lt;/strong&gt; to install a package, the package files are downloaded to the package cache so that they can be easily accessed and used to install the package. However, as you install more and more packages, the package cache can grow quite large.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;FROM&lt;/span&gt; &lt;span class="nx"&gt;ubuntu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;18.04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;slim&lt;/span&gt;
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; 
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="nx"&gt;curl&lt;/span&gt; 
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt; 
&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;clean&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is the package cache cleaned? The answer is no. As previously discussed, each Docker layer has its own set of files and directories that are separate from the underlying filesystem.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;apt-get clean&lt;/code&gt; only clears the package cache on layer 4.&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%2Fuploads%2Farticles%2Fu6belx2s415hy9ndqe0s.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%2Fuploads%2Farticles%2Fu6belx2s415hy9ndqe0s.png" alt="Freeform4" width="761" height="810"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how we can fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;RUN&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
    &lt;span class="nx"&gt;curl&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
    &lt;span class="nx"&gt;dpkg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;\&lt;/span&gt;
 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Evans, Julia. (2019). "How containers work: overlayfs." Retrieved from &lt;strong&gt;&lt;a href="https://jvns.ca/blog/2019/11/18/how-containers-work--overlayfs/" rel="noopener noreferrer"&gt;https://jvns.ca/blog/2019/11/18/how-containers-work--overlayfs/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Azure Runbook Asyncio Error</title>
      <dc:creator>Eric Yi</dc:creator>
      <pubDate>Thu, 31 Mar 2022 12:13:10 +0000</pubDate>
      <link>https://dev.to/ericyi/azure-runbook-asyncio-error-3pj7</link>
      <guid>https://dev.to/ericyi/azure-runbook-asyncio-error-3pj7</guid>
      <description>&lt;p&gt;We recently had a project that required the use of Azure runbook. I found my Python code was not working as expected. It works locally but not in Azure runbook.&lt;/p&gt;

&lt;p&gt;After debugging, I found the issue is related to Asyncio. Even if I ran the following sample code, I still got an error.&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="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message:&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\Temp&lt;/span&gt;&lt;span class="se"&gt;\v&lt;/span&gt;&lt;span class="s"&gt;ls2h0kl.4gk&lt;/span&gt;&lt;span class="se"&gt;\04&lt;/span&gt;&lt;span class="s"&gt;a596c9-5b8c-4a2d-828b-2bb185668277"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProactorEventLoop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s"&gt;syncio\windows_events.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;310&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;    &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proactor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s"&gt;syncio\proactor_events.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;629&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_make_self_pipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s"&gt;syncio\proactor_events.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;760&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_make_self_pipe&lt;/span&gt;    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ssock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_csock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socketpair&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib\socket.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;597&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;socketpair&lt;/span&gt;    &lt;span class="n"&gt;lsock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nb"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;WinError&lt;/span&gt; &lt;span class="mi"&gt;10050&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="n"&gt;operation&lt;/span&gt; &lt;span class="n"&gt;encountered&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;dead&lt;/span&gt; &lt;span class="n"&gt;networkException&lt;/span&gt; &lt;span class="n"&gt;ignored&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;BaseEventLoop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__del__&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x0000000784F8C310&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s"&gt;syncio&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="s"&gt;ase_events.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;648&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;__del__&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s"&gt;syncio\proactor_events.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;684&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;close&lt;/span&gt;  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s"&gt;"C:\WPy64-3800\python-3.8.0.amd64\lib&lt;/span&gt;&lt;span class="se"&gt;\a&lt;/span&gt;&lt;span class="s"&gt;syncio\proactor_events.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;752&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_close_self_pipeAttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'ProactorEventLoop'&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="s"&gt;'_ssock'&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I raised a ticket to Azure Team. I was told it is because Azure runbook only allows on 31 kernel calls and this module seems to be using something out of this range hence getting this error. &lt;/p&gt;

&lt;p&gt;Their product team is working on enabling full stack of OS calls from Azure runbook but it will be delivered for Python by end of 2022. &lt;/p&gt;

&lt;p&gt;So, the temporary workaround is customer can use hybrid runbook worker as of now. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/automation/automation-hybrid-runbook-worker"&gt;Azure Automation Hybrid Runbook Worker overview&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>python</category>
      <category>devops</category>
    </item>
    <item>
      <title>Use Prometheus and Grafana to Visualize ISP Packet Loss</title>
      <dc:creator>Eric Yi</dc:creator>
      <pubDate>Thu, 16 Sep 2021 07:28:06 +0000</pubDate>
      <link>https://dev.to/ericyi/use-prometheus-and-grafana-to-visualize-isp-packet-loss-2eh</link>
      <guid>https://dev.to/ericyi/use-prometheus-and-grafana-to-visualize-isp-packet-loss-2eh</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Since people started working from home, poor video/voice call connections have become a very annoying problem. &lt;/p&gt;

&lt;p&gt;However, it's hard for people to diagnose what is the root cause. People start blaming their ISPs, and I feel bad for them. In fact, there are a number of reasons that can cause poor video call connections, but if you can tell us with great confidence that my problem is not from your end.&lt;/p&gt;

&lt;p&gt;You should probably start monitoring your ISP  packet loss.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Grafana(You can register a free &lt;a href="https://grafana.com/auth/sign-in?plcmt=top-nav&amp;amp;cta=myaccount" rel="noopener noreferrer"&gt;Grafana Cloud account&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Prometheus (You can &lt;a href="https://prometheus.io/docs/prometheus/latest/installation/" rel="noopener noreferrer"&gt;install it&lt;/a&gt; on your Raspberry Pi or NAS)&lt;/li&gt;
&lt;li&gt;Open Source Router OS (OpenWRT, ClearOS, Asuswrt-Merlin etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up ping_exporter
&lt;/h3&gt;

&lt;p&gt;SSH in to your router &amp;gt; Download ping_exporter&lt;/p&gt;

&lt;p&gt;You can check which version you need &lt;a href="https://github.com/czerwonk/ping_exporter/releases" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/czerwonk/ping_exporter/releases#:~:text&lt;span class="o"&gt;=&lt;/span&gt;ping_exporter_0.4.7_linux_arm64.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; ping_exporter_&lt;span class="k"&gt;*&lt;/span&gt;.tar.gz ping_exporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move to &lt;code&gt;/usr/local/bin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;ping_exporter /usr/local/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose the way you like to get the ISP's DNS servers on your router.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;ping_exporter.yaml&lt;/code&gt; file under &lt;code&gt;/etc/ping_exporter/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;targets:
  - &amp;lt;Your ISP&lt;span class="s1"&gt;'s DNS servers&amp;gt;

ping:
  interval: 2s
  timeout: 3s
  history-size: 42
  payload-size: 120
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;ping_exporter.service&lt;/code&gt; file under &lt;code&gt;/etc/systemd/system/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;Unit]
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ping_exporter

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/ping_exporter &lt;span class="nt"&gt;--config&lt;/span&gt;.path /etc/ping_exporter/ping_exporter.yaml
&lt;span class="nv"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always

&lt;span class="o"&gt;[&lt;/span&gt;Install]
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload the service files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start your &lt;code&gt;ping_exporter.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start ping_exporter.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the status of your &lt;code&gt;ping_exporter.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status ping_exporter.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable your &lt;code&gt;ping_exporter.service&lt;/code&gt; on every reboot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;ping_exporter.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get the results for testing via cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:9427/metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a target to Prometheus
&lt;/h3&gt;

&lt;p&gt;In your Prometheus configuration file, add the target in the &lt;code&gt;static_config&lt;/code&gt; section :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;- targets: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Your router's ip/dns address&amp;gt;:9427"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reload your Prometheus’ configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Grafana Panel
&lt;/h3&gt;

&lt;p&gt;Add a new Panel in your home network's dashboard, we are going to use the &lt;code&gt;ping_loss_percent&lt;/code&gt; metric.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping_loss_percent&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Your router's ip/dns address&amp;gt;:9427"&lt;/span&gt;,target&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Your ISP's DNS servers&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y7h6c4o2lgt3mjmkzwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y7h6c4o2lgt3mjmkzwd.png" alt="Packet Loss Pannel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠️  Please note for voice and video calls, any packet loss below 0.05 could be considered acceptable.&lt;/p&gt;

</description>
      <category>opensource</category>
    </item>
    <item>
      <title>Triggering Jenkins Parameterized Builds Behind A Firewall</title>
      <dc:creator>Eric Yi</dc:creator>
      <pubDate>Thu, 05 Aug 2021 01:24:36 +0000</pubDate>
      <link>https://dev.to/ericyi/triggering-jenkins-parameterized-builds-behind-a-firewall-367j</link>
      <guid>https://dev.to/ericyi/triggering-jenkins-parameterized-builds-behind-a-firewall-367j</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In this post I would like to share with you the process of how to trigger Jenkins parameterized builds behind a firewall. &lt;/p&gt;

&lt;p&gt;Imagine your Jenkins is behind a corporate firewall but still able to receive webhooks from Github, DockerHub and Microsoft Automate. &lt;/p&gt;

&lt;p&gt;I have seen many posts discussing related topics, but most webhook forwarding services cannot forward query parameters, which means they do not have the ability to trigger Jenkins builds with parameters.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Webhook Relay account - register &lt;a href="https://my.webhookrelay.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Jenkins server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuring Webhook Relay
&lt;/h3&gt;

&lt;p&gt;Create a New Bucket &lt;a href="https://my.webhookrelay.com/buckets" rel="noopener noreferrer"&gt;here&lt;/a&gt; . Buckets allow you to group inputs and outputs.&lt;/p&gt;

&lt;p&gt;A newly created bucket will have a default input and you will need to create an output destination where webhooks will be sent.&lt;/p&gt;

&lt;p&gt;Click on &lt;code&gt;OUTPUT DESTINATION&lt;/code&gt;&amp;gt; Fill in your custom &lt;code&gt;Output name&lt;/code&gt;  &amp;gt; Fill in your Jenkins server address  in &lt;code&gt;Output destination&lt;/code&gt;  &amp;gt; click &lt;code&gt;create output&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Output Destination Example&lt;/span&gt;
&lt;span class="c1"&gt;# If your Jenkins allows anonymous users can have build access.&lt;/span&gt;
&lt;span class="s"&gt;http://192.168.0.200:8080&lt;/span&gt;
&lt;span class="c1"&gt;# If only authenticated users can have build access.&lt;/span&gt;
&lt;span class="s"&gt;http://username:token@192.168.0.200:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;code&gt;CONFIGURE&lt;/code&gt; under your Output Destinations &amp;gt; Switch off &lt;code&gt;Lock destination path&lt;/code&gt; &amp;gt; Click on &lt;code&gt;SAVE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It will allow us to send any extra path.&lt;br&gt;
(&lt;a href="http://xyz.hooks.webhookrelay.com/foo/bar" rel="noopener noreferrer"&gt;xyz.hooks.webhookrelay.com/foo/bar&lt;/a&gt; -&amp;gt; 192.168.0.200:8080/foo/bar)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dizae08pl36gi5urnsu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dizae08pl36gi5urnsu.png" alt="webhooksettings"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuring Webhookrelayd agent
&lt;/h3&gt;

&lt;p&gt;Create an access token &lt;a href="https://my.webhookrelay.com/tokens" rel="noopener noreferrer"&gt;here.&lt;/a&gt;  Access tokens are used to authenticate Webhook Relay agents.&lt;/p&gt;

&lt;p&gt;On the Jenkins VM or  a VM that can talk on the Jenkins server, start a webhookrelayd agent.&lt;/p&gt;

&lt;p&gt;Specify configuration through environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your token key&amp;gt;
&lt;span class="nv"&gt;SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your token secret&amp;gt;
&lt;span class="nv"&gt;BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;bucket name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the webhookrelayd container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--name&lt;/span&gt; webhookrelayd &lt;span class="se"&gt;\&lt;/span&gt;
           &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
           webhookrelay/webhookrelayd 

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

&lt;/div&gt;



&lt;p&gt;Now go back to your Bucket page, you should see the status has changed to &lt;code&gt;Client Connected&lt;/code&gt;. If there are any problems, you should check your docker logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7p7uslz04g3ngxwlcoh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7p7uslz04g3ngxwlcoh.png" alt="clientstatus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Jenkins Builds
&lt;/h2&gt;

&lt;p&gt;Open your Terminal, let us test your Jenkins build.&lt;/p&gt;

&lt;p&gt;Here is an example when trigger the Jenkins build normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://192.168.0.200:8080/job/demo/buildWithParameters?token&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;buildToken&amp;gt;&amp;amp;env&lt;span class="o"&gt;=&lt;/span&gt;dev&amp;amp;version&lt;span class="o"&gt;=&lt;/span&gt;20210804&amp;amp;user&lt;span class="o"&gt;=&lt;/span&gt;john.doe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using Webhook Relay, it will be converted to the following url:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://&amp;lt;unique domain&amp;gt;.hooks.webhookrelay.com/job/demo/buildWithParameters?token&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;buildToken&amp;gt;&amp;amp;env&lt;span class="o"&gt;=&lt;/span&gt;dev&amp;amp;version&lt;span class="o"&gt;=&lt;/span&gt;20210804&amp;amp;user&lt;span class="o"&gt;=&lt;/span&gt;john.doe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the Relay Logs &lt;a href="https://my.webhookrelay.com/logs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>sre</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Using GitHub Actions to Deploy CloudFormation Stacks</title>
      <dc:creator>Eric Yi</dc:creator>
      <pubDate>Tue, 20 Apr 2021 11:40:03 +0000</pubDate>
      <link>https://dev.to/ericyi/using-github-actions-to-deploy-cloudformation-stacks-3c4g</link>
      <guid>https://dev.to/ericyi/using-github-actions-to-deploy-cloudformation-stacks-3c4g</guid>
      <description>&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;GitHub Actions is generally available at the end of 2019. Thanks to the open marketplace of GitHub Actions, more and more projects are using it. &lt;/p&gt;

&lt;p&gt;Whether you are in the Devs or Ops team, you must be familiar with infrastructure as code. However, if you don't have a good pipeline, the deployment process can be a headache.&lt;/p&gt;

&lt;p&gt;In this article, you will learn how to use GitHub Actions to deploy CloudFormation stacks.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub Account (Click &lt;a href="https://docs.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions" rel="noopener noreferrer"&gt;here&lt;/a&gt; to see free tier information)&lt;/li&gt;
&lt;li&gt;A Programmatic Access IAM user in AWS&lt;/li&gt;
&lt;li&gt;CloudFormation &amp;amp; S3 permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Process
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Create CFN Templates
&lt;/h2&gt;

&lt;p&gt;Let us create a simple CloudFormation template for creating an S3 bucket.&lt;/p&gt;

&lt;p&gt;The filename is &lt;code&gt;s3.yml&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class="s"&gt;Properties&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AccessControl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Private&lt;/span&gt;
      &lt;span class="na"&gt;BucketEncryption&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ServerSideEncryptionConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ServerSideEncryptionByDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;SSEAlgorithm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AES256&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then we would like to name our own S3 bucket, and for our template to be reusable, we want to pass parameters to CloudFormation stacks.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Name your Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;S3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AccessControl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Private&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;BucketName&lt;/span&gt;
      &lt;span class="na"&gt;BucketEncryption&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ServerSideEncryptionConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ServerSideEncryptionByDefault&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;SSEAlgorithm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AES256&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Finally, we can add descriptions and format version and checkin the template in our repository.  Click &lt;a href="https://github.com/yunpengeric/Cloudformation-Github-Actions-Demo/blob/main/s3.yml" rel="noopener noreferrer"&gt;here&lt;/a&gt; to view the full template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create GitHub Workflows
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/main.yml&lt;/code&gt; under our git root directory.  Let us write our first worflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure AWS Credentials
&lt;/h3&gt;

&lt;p&gt;We are using this action to configure AWS credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions" rel="noopener noreferrer"&gt;"Configure AWS Credentials" Action For GitHub Actions - GitHub Marketplace&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can write the step according to the README.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS Credentials&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;creds&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;aws-access-key-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
     &lt;span class="na"&gt;aws-secret-access-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
     &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ap-southeast-2"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In AWS IAM console, get your access key ID and secret access key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/powershell/latest/userguide/pstools-appendix-sign-up.html" rel="noopener noreferrer"&gt;AWS Account and Access Keys&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then navigate to the main page of  GitHub repository &amp;gt; Click &lt;code&gt;Setting&lt;/code&gt; &amp;gt; In the left sidebar, click &lt;code&gt;Secrets&lt;/code&gt; &amp;gt; Click &lt;code&gt;New repository secret&lt;/code&gt; &amp;gt;  Create &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh74157kvi8e4k7bh6lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbh74157kvi8e4k7bh6lc.png" alt="AccessKeyImage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Deployment Actions
&lt;/h3&gt;

&lt;p&gt;We are using this action to deploy CFN stacks. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace/actions/aws-cloudformation-deploy-cloudformation-stack-action-for-github-actions" rel="noopener noreferrer"&gt;AWS CloudFormation "Deploy CloudFormation Stack" Action for GitHub Actions - GitHub Marketplace&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can write the step according to the README.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As I am writing this article, the latest version is &lt;code&gt;v1.0.3&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to AWS CloudFormation&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/aws-cloudformation-github-deploy@v1.0.3&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3Bucket&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3.yml&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We also need to pass the input parameter &lt;code&gt;BucketName&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# Controls when the action will run.&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Allows you to run this workflow manually from the Actions tab&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bucket"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo-bucket"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then we add &lt;code&gt;parameter-overrides&lt;/code&gt; in the &lt;code&gt;Deploy to AWS CloudFormation&lt;/code&gt; step.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy S3 Buckets CloudFormation Stacks&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/aws-cloudformation-github-deploy@v1.0.3&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3-buckets&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3.yml&lt;/span&gt;
        &lt;span class="s"&gt;parameter-overrides&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
        &lt;span class="s"&gt;BucketName=${{ github.event.inputs.bucketName }}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let's finally organise our action file &lt;code&gt;main.yml&lt;/code&gt;.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy CloudFormation Stacks&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;# Controls when the action will run.&lt;/span&gt;&lt;br&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
  &lt;span class="c1"&gt;# Allows you to run this workflow manually from the Actions tab&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Region"&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ap-southeast-2"&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;bucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bucket&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Name"&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="c1"&gt;# A workflow run is made up of one or more jobs that can run sequentially or in parallel&lt;/span&gt;&lt;br&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;cfn-deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;span class="pi"&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span class="na"&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;Configure AWS credentials&amp;lt;/span&amp;gt;
    &amp;lt;span class="na"&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;creds&amp;lt;/span&amp;gt;
    &amp;lt;span class="na"&amp;gt;uses&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;aws-actions/configure-aws-credentials@v1&amp;lt;/span&amp;gt;
    &amp;lt;span class="na"&amp;gt;with&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;aws-access-key-id&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;${{ secrets.AWS_ACCESS_KEY_ID }}&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;aws-secret-access-key&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;aws-region&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;${{ github.event.inputs.region }}&amp;lt;/span&amp;gt; 

  &amp;lt;span class="pi"&amp;gt;-&amp;lt;/span&amp;gt; &amp;lt;span class="na"&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;Deploy S3 Buckets CloudFormation Stacks&amp;lt;/span&amp;gt;
    &amp;lt;span class="na"&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;s3-buckets&amp;lt;/span&amp;gt;
    &amp;lt;span class="na"&amp;gt;uses&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;aws-actions/aws-cloudformation-github-deploy@v1.0.3&amp;lt;/span&amp;gt;
    &amp;lt;span class="na"&amp;gt;with&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt;  &amp;lt;span class="s"&amp;gt;s3-buckets&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;template&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="s"&amp;gt;s3.yml&amp;lt;/span&amp;gt;
      &amp;lt;span class="na"&amp;gt;parameter-overrides&amp;lt;/span&amp;gt;&amp;lt;span class="pi"&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class="pi"&amp;gt;&amp;amp;gt;-&amp;lt;/span&amp;gt;
        &amp;lt;span class="s"&amp;gt;BucketName=${{ github.event.inputs.bucketName }}&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Use GitHub Workflows&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Make sure you have pushed all the files. Just like the following repo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yunpengeric/Cloudformation-Github-Actions-Demo" rel="noopener noreferrer"&gt;yunpengeric/Cloudformation-Github-Actions-Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then navigate to the main page of  GitHub repository &amp;gt; Click &lt;code&gt;Actions&lt;/code&gt; &amp;gt; In the left sidebar, click &lt;code&gt;Deploy CloudFormation Stacks&lt;/code&gt; &amp;gt;  Click &lt;code&gt;Run Workflow&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;You should see the following pre-filled data &amp;gt; Enter your S3 bucket name (Please note an S3 bucket name is globally unique) &amp;gt; Run Workflow&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wj9nby1ca23s3ucdohn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wj9nby1ca23s3ucdohn.png" alt="actionsimage"&gt;&lt;/a&gt;&lt;br&gt;
After a minute or so, you will see that our S3 bucket has been successfully deployed. If you see any errors, please leverage GitHub logs and CloudFormation console to troubleshoot. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We are still far from it if our goal is to create a good pipeline in production environment. You should take these things into consideration as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How you can rotate keys to ensure security.&lt;/li&gt;
&lt;li&gt;How to modify your CFN templates so that when you deploy a second bucket, you don't delete the first one.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
