{"id":1066,"date":"2022-11-24T13:25:13","date_gmt":"2022-11-24T13:25:13","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=1066"},"modified":"2022-11-24T13:25:13","modified_gmt":"2022-11-24T13:25:13","slug":"build-aws-java-application-ami-packer","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=1066","title":{"rendered":"How to Build Java Application AMI on AWS Using Packer"},"content":{"rendered":"<p>In this tutorial, you will learn to build Java application AMI using Packer. We will also validate the application by deploying a ec2 server using created AMI.<\/p>\n<p>Here is what we will do in this tutorial.<\/p>\n<ol>\n<li>Create a Packer configuration file for Java<\/li>\n<li>Create the Java AMI Image With the application jar.<\/li>\n<li>Deploy ec2 with created AMI and validate the AMI by starting the application jar using the user data shell script.<\/li>\n<\/ol>\n<p>If you are new to packer, I would recommend reading the <a href=\"https:\/\/devopscube.com\/packer-tutorial-for-beginners\/\">packer tutorial<\/a> first.<\/p>\n<h2 id=\"prerequisites\">Prerequisites<\/h2>\n<ol>\n<li><strong>Java application:<\/strong> jar artifact of java application that you want to bake with the AMI. You can ignore this you just want java JDK\/JRE in the AMI. Or you can use the <a href=\"https:\/\/devopscube.com\/build-java-application-using-maven\/\">java application build using maven<\/a> guide to create a jar file.<\/li>\n<li>The Latest Packer binary installed<\/li>\n<li>AWS CLI configured with user permission to create an ec2 server.<\/li>\n<\/ol>\n<h2 id=\"packer-java-ami-github-repo\">Packer Java AMI GitHub Repo<\/h2>\n<p>The packer configuration and scripts used in this guide are hosted on the <a href=\"https:\/\/github.com\/techiescamp\/packer-templates?ref=devopscube.com\" rel=\"noreferrer noopener\">Packer Templates GitHub Repository<\/a> under <code><strong>\/aws\/java<\/strong><\/code> directory. You can clone the repo to follow this guide.<\/p>\n<pre><code>git clone https:\/\/github.com\/techiescamp\/packer-templates<\/code><\/pre>\n<h2 id=\"create-java-packer-configuration-files\">Create Java Packer Configuration Files<\/h2>\n<p>Here is our folder structure to build Java application AMI.<\/p>\n<pre><code>java\n    \u251c\u2500\u2500 java.pkr.hcl\n    \u2514\u2500\u2500 scripts\n        \u2514\u2500\u2500 java.sh\n        \u2514\u2500\u2500 user-data.sh<\/code><\/pre>\n<ol>\n<li><strong><code>java.pkr.hcl<\/code><\/strong> is the Packer configuration file (HCL format).<\/li>\n<li><strong><code>java.sh<\/code><\/strong> shell script in the scripts folder has the commands to install the latest JRE.<\/li>\n<li><strong><code>user-data.sh<\/code><\/strong> shell script contains the java application startup command that will be used as an ec2 user data script when deploying the ec2 instance.<\/li>\n<\/ol>\n<p><strong>Step 1:<\/strong> Create a folder named scripts and create file named<strong> java.sh<\/strong> inside it with the following contents. It is debian based script. If you are using Amazon Linux or Redhat, update the commands accordingly.<\/p>\n<p>Here are installing JRE and creating <strong><code>\/opt\/deployment<\/code><\/strong> \u00a0folder for jar file and <strong><code>\/var\/log\/apps<\/code><\/strong> folder to write the application log file.<\/p>\n<pre><code>#!\/bin\/bash\n\nset -e, -u, -x, -o pipefail\n\nsudo apt update -y\n\nsudo apt install openjdk-17-jre-headless -y\n\nsudo mkdir \/opt\/deployment\nsudo mkdir \/var\/log\/apps\n\nsudo chown -R $USER:$USER \/opt\/deployment\nsudo chown -R $USER:$USER \/var\/log\/apps<\/code><\/pre>\n<p><strong>Step 2: <\/strong>Create a file named <code><strong>java.pkr.hcl<\/strong><\/code> and copy the following contents.<\/p>\n<pre><code>variable \"ami_id\" {\n  type    = string\n  default = \"ami-017fecd1353bcc96e\"\n}\n\nlocals {\n  app_name = \"pet-clinic-java\"\n}\n\nsource \"amazon-ebs\" \"java\" {\n  ami_name      = \"packer-${local.app_name}\"\n  instance_type = \"t2.micro\"\n  region        = \"us-west-2\"\n  source_ami    = \"${var.ami_id}\"\n  ssh_username  = \"ubuntu\"\n  tags = {\n    Env  = \"dev\"\n    Name = \"packer-${local.app_name}\"\n  }\n}\n\nbuild {\n\n  sources = [\"source.amazon-ebs.java\"]\n\n  provisioner \"shell\" {\n    script = \"scripts\/java.sh\"\n  }\n\n  provisioner \"file\" {\n    source      = \"\/path\/to\/app.jar\"\n    destination = \"\/opt\/deployment\/app.jar\"\n  }\n\n  post-processor \"manifest\" {\n    output     = \"manifest.json\"\n    strip_path = true\n  }\n}\n<\/code><\/pre>\n<p>Here is the script explanation.<\/p>\n<ol>\n<li>Here we are using a ubuntu base image with the official Ubuntu AMI id <strong><code>ami-017fecd1353bcc96e<\/code><\/strong>. Replace it with the required base image AMI ID.<\/li>\n<li>Replace <code>pet-clinic-java <\/code>with your app&#8217;s name.<\/li>\n<li>If you are using amazon Linux or Redhat AMI, replace <strong><code>ssh_username \u00a0= \"ubuntu\"<\/code><\/strong> with <strong><code>ssh_username \u00a0= \"ec2-user\"<\/code><\/strong><\/li>\n<li>In the file provisioner block, we are copying the local jar file to the <strong><code><strong>\/opt\/deployment\/app.jar<\/strong><\/code><\/strong> location. Replace <code><strong>\/path\/to\/app.jar<\/strong><\/code> with the absolute path of the jar location.<\/li>\n<li>The shell provisioner block executes the<code><strong> java.sh<\/strong><\/code> shell script from the scripts folder.<\/li>\n<\/ol>\n<h2 id=\"create-java-application-ami-using-packer\">Create Java Application AMI Using Packer<\/h2>\n<p>Now that we have all the Packer configurations ready, we can create the custom Java AMI using Packer.<\/p>\n<p>Ensure you are in the directory where you have the <strong><code>java.pkr.hcl<\/code><\/strong> file.<\/p>\n<p>First, let&#8217;s format and validate the packer template. So if there are any configuration issues, it will throw an error.<\/p>\n<pre><code>packer fmt java.pkr.hcl\npacker validate java.pkr.hcl<\/code><\/pre>\n<p>Now that we have a valid packer configuration file, let&#8217;s execute the following packer command to build the AMI.<\/p>\n<pre><code>packer build java.pkr.hcl<\/code><\/pre>\n<p>On successful execution, you will see the created AMI Id output as shown below.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/image-10-47.png\" class=\"kg-image\" alt=\"java application AMI Id from packer output.\" loading=\"lazy\" width=\"577\" height=\"272\"><\/figure>\n<p>Also, the AMI Id gets stored in the <strong><code>manifest.json<\/code><\/strong> file in the same directory. It gets created as part of the post-processor block. This is useful in CI\/CD pipelines where you want to parse the output AMI id to be used in the next pipeline state.<\/p>\n<pre><code>{\n  \"builds\": [\n    {\n      \"name\": \"java\",\n      \"builder_type\": \"amazon-ebs\",\n      \"build_time\": 1669270824,\n      \"files\": null,\n      \"artifact_id\": \"us-west-2:ami-0cf25bbd911ec1d14\",\n      \"packer_run_uuid\": \"2ed4531c-c0c6-2821-6448-e1e5791a238a\",\n      \"custom_data\": null\n    }\n  ],\n  \"last_run_uuid\": \"2ed4531c-c0c6-2821-6448-e1e5791a238a\"<\/code><\/pre>\n<h2 id=\"test-validate-java-ami\">Test &amp; Validate Java AMI<\/h2>\n<p>Now that we have created the Java AMI, we can validate it by deploying a ec2 server with the newly created AMI.<\/p>\n<p>I will pass a user data shell script to start the application jar that is backed into the AMI. Here is the minimal java startup command. Save it in a file namesd <strong><code>user-data.sh<\/code><\/strong><\/p>\n<pre><code>#!\/bin\/bash\nnohup java -jar \/opt\/deployment\/app.jar &gt; \/var\/log\/apps\/app.log 2&gt;&amp;1 &amp;<\/code><\/pre>\n<p>Following is the <a href=\"https:\/\/devopscube.com\/use-aws-cli-create-ec2-instance\/\">AWS Cli command to create ec2 server.<\/a> You need to replace the parameters highlighted in bold. <strong>scripts\/user-data.sh<\/strong> is the path of the user data script. Replace it with the path of your script file location.<\/p>\n<pre><code>aws ec2 run-instances \\\n    --image-id ami-0daa89240da066fe5 \\\n    --count 1 \\\n    --instance-type t2.micro \\\n    --key-name techiescamp \\\n    --security-group-ids sg-023b5f8e016ce08e1 \\\n    --subnet-id subnet-0782c73ff7b32d7c2 \\\n    --block-device-mappings \"[{\\\"DeviceName\\\":\\\"\/dev\/sdf\\\",\\\"Ebs\\\":{\\\"VolumeSize\\\":30,\\\"DeleteOnTermination\\\":false}}]\" \\\n    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=java-app-server}]' 'ResourceType=volume,Tags=[{Key=Name,Value=java-app-server-disk}]' \\\n    --user-data file:\/\/scripts\/user-data.sh<\/code><\/pre>\n<p>Once the instance is launched, Java applications will take 2-3 minutes for startup. You can verify the application by visiting to <code><strong>http:\/\/IP-ADDRESS:8080<\/strong><\/code>.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>With Packer you can follow <a href=\"https:\/\/devopscube.com\/immutable-infrastructure\/\">Immutable infrastructure<\/a> model for applciation deployments. By building Java images backed with application jar file, you can easily deploy Java application son AWS ennvironents with the flexibility to change the Java startup parameters using ec2 user data.<\/p>\n<p>If you are using Java applciation on AWS autoscalaing groups, packer is a great tool tp package the application jar in to AMI artifact in the CI\/CD process.<\/p>\n<p>Also, check out the <a href=\"https:\/\/devopscube.com\/become-devops-engineer\/\">devops engineer guide<\/a> if you are starting your DevOps journey.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/build-aws-java-application-ami-packer\/\" target=\"_blank\" rel=\"noopener noreferrer\">How to Build Java Application AMI on AWS Using Packer \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/build-aws-java-application-ami-packer\/<\/p>\n","protected":false},"author":1,"featured_media":1067,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1066","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/1066","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1066"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/1066\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/1067"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1066"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1066"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1066"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}