<?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: hung5s</title>
    <description>The latest articles on DEV Community by hung5s (@hung5s).</description>
    <link>https://dev.to/hung5s</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%2F420279%2F5646bdca-7f8c-49b0-8090-27d0e3b68476.jpeg</url>
      <title>DEV Community: hung5s</title>
      <link>https://dev.to/hung5s</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hung5s"/>
    <language>en</language>
    <item>
      <title>An easy way to push Docker image from Jenkins to ECR</title>
      <dc:creator>hung5s</dc:creator>
      <pubDate>Thu, 03 Dec 2020 12:15:23 +0000</pubDate>
      <link>https://dev.to/hung5s/an-easy-way-to-push-docker-image-from-jenkins-to-ecr-4lpi</link>
      <guid>https://dev.to/hung5s/an-easy-way-to-push-docker-image-from-jenkins-to-ecr-4lpi</guid>
      <description>&lt;p&gt;This is kind of my personal note so there is no introduction et'al. Long story short, you pull code from GitHub and build a Docker image. You want to push the image to ECR in order to deploy a ECS service. You want to make it quick and simple with just shell commands. How would you make it work?&lt;/p&gt;

&lt;h1&gt;
  
  
  Make Jenkins work with Dockers
&lt;/h1&gt;

&lt;p&gt;Here assume that Jenkins is installed on an EC2 which is launched from the Amazon Linux 2 image. You will need to add the &lt;code&gt;jenkins&lt;/code&gt; user into &lt;code&gt;docker&lt;/code&gt; group:&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;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it's done, you can build the image and tag it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;img name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt; &lt;span class="nt"&gt;--pull&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; /var/lib/jenkins/workspace/&amp;lt;jenkins job name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker tag &amp;lt;img name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt; &amp;lt;AWS user ID&amp;gt;.dkr.ecr.&amp;lt;region&amp;gt;.amazonaws.com/&amp;lt;ECR repo name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now comes the headache. If you try to push the image to ECR using &lt;code&gt;docker push&lt;/code&gt; command, it will fail because there is no authentication token for &lt;code&gt;jenkins&lt;/code&gt; to connect with ECR.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting the token and login
&lt;/h1&gt;

&lt;p&gt;In order to get the token, we will need to run the &lt;code&gt;aws ecr get-login-password&lt;/code&gt; (AWS CLI v2, if v1 the command is &lt;code&gt;get-login&lt;/code&gt;). However, this only work if the AWS CLI has a credential profile for &lt;code&gt;jenkins&lt;/code&gt;. Without it, you will get the error: &lt;code&gt;Unable to locate credentials. You can configure credentials by running "aws configure".&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The thing is that you don't want to run &lt;code&gt;aws configure&lt;/code&gt; within Jenkins's build all the time. It's unnecessary and it will expose your IAM user's secrets.&lt;/p&gt;

&lt;p&gt;What I did is SSHing to the EC2 where Jenkins is installed and run the &lt;code&gt;aws configure&lt;/code&gt; as &lt;code&gt;jenkins&lt;/code&gt; user:&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; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; jenkins aws configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give AWS all the key and secret of the IAM user I've created for ECS deployment, I was able to generate the credential profile. This profile is named &lt;code&gt;default&lt;/code&gt; and stored for &lt;code&gt;jenkins&lt;/code&gt; user.&lt;/p&gt;

&lt;p&gt;Given the profile, back to Jenkins job, it's ok to get the auth token and login to docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="nt"&gt;--profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &amp;lt;AWS user ID&amp;gt;.dkr.ecr.&amp;lt;region&amp;gt;.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and push the image to ECR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push &amp;lt;AWS user ID&amp;gt;.dkr.ecr.&amp;lt;region&amp;gt;.amazonaws.com/&amp;lt;ECR repo name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting them all together we have one Jenkins build shell command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;img name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt; &lt;span class="nt"&gt;--pull&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; /var/lib/jenkins/workspace/&amp;lt;jenkins job name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker tag &amp;lt;img name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt; &amp;lt;AWS user ID&amp;gt;.dkr.ecr.&amp;lt;region&amp;gt;.amazonaws.com/&amp;lt;ECR repo name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="nt"&gt;--profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &amp;lt;AWS user ID&amp;gt;.dkr.ecr.&amp;lt;region&amp;gt;.amazonaws.com &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker push &amp;lt;AWS user ID&amp;gt;.dkr.ecr.&amp;lt;region&amp;gt;.amazonaws.com/&amp;lt;ECR repo name&amp;gt;:v_&lt;span class="nv"&gt;$BUILD_NUMBER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this shell command, Jenkins should be able to push the latest Docker image to ECR. The second shell command will deploy the code:&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="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;region&amp;gt;
&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;service name&amp;gt;
&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;cluster name&amp;gt;
&lt;span class="nv"&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"v_"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BUILD_NUMBER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;task name&amp;gt;

&lt;span class="c"&gt;# Create a new task definition for this build&lt;/span&gt;

&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s;%BUILD_NUMBER%;&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BUILD_NUMBER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;g"&lt;/span&gt; ./task-def.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-v_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BUILD_NUMBER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.json

aws ecs register-task-definition &lt;span class="nt"&gt;--family&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--cli-input-json&lt;/span&gt; file://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-v_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BUILD_NUMBER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.json

&lt;span class="c"&gt;# Update the service with the new task definition and desired count&lt;/span&gt;
&lt;span class="nv"&gt;REVISION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;aws ecs describe-task-definition &lt;span class="nt"&gt;--task-definition&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | egrep &lt;span class="s2"&gt;"revision"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt; &lt;span class="s2"&gt;" "&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/"$//'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;SERVICES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;aws ecs describe-services &lt;span class="nt"&gt;--services&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | jq .failures[]&lt;span class="sb"&gt;`&lt;/span&gt;


&lt;span class="c"&gt;#Create or update service&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"entered existing service"&lt;/span&gt;
  &lt;span class="nv"&gt;DESIRED_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;aws ecs describe-services &lt;span class="nt"&gt;--services&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | jq .services[].desiredCount&lt;span class="sb"&gt;`&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DESIRED_COUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;DESIRED_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;
  &lt;span class="k"&gt;fi
  &lt;/span&gt;aws ecs update-service &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--service&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--task-definition&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REVISION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--desired-count&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DESIRED_COUNT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--deployment-configuration&lt;/span&gt; &lt;span class="nv"&gt;maximumPercent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100,minimumHealthyPercent&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"entered new service"&lt;/span&gt;
  aws ecs create-service &lt;span class="nt"&gt;--service-name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--desired-count&lt;/span&gt; 1 &lt;span class="nt"&gt;--task-definition&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_FAMILY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--cluster&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CLUSTER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Pre-requirements
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;An EC2 instance is launched using Amazon Linux 2 image&lt;/li&gt;
&lt;li&gt;Jeknins is installed on the instance&lt;/li&gt;
&lt;li&gt;An IAM user is created with EC2ContainerRegister policy attached&lt;/li&gt;
&lt;li&gt;AWS CLI on the instance is upgraded to version 2&lt;/li&gt;
&lt;li&gt;Your source code has the file structure similar to the below:&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\root
   + src  &amp;lt;&amp;lt; source code folder
     - source files
   - Dockerfile
   - task-def.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>jenkins</category>
      <category>ecr</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
