Skip to content

Commit

Permalink
clean up and embed iframe direct in doc
Browse files Browse the repository at this point in the history
  • Loading branch information
NewGraphEnvironment committed Jan 19, 2025
1 parent 44bd451 commit 4cdad63
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"hash": "3bb2524213ffd0f244e989545a43ae4d",
"hash": "42f8a045bfe62c83d2391b614168b8bf",
"result": {
"engine": "knitr",
"markdown": "---\ntitle: \"Setting up TiTiler to serve COGs of UAV imagery on AWS with leaflet and Elastic Beanstalk\"\nauthor: \"al\"\ndate: \"2025-01-17\"\ndate-modified: \"2025-01-17\"\ncategories: [aws, s3, r, paws, s3sf, leaflet, COG]\nimage: \"image.jpg\"\nparams:\n repo_owner: \"NewGraphEnvironment\"\n repo_name: \"new_graphiti\"\nformat: \n html:\n code-fold: true\n---\n\n\nWhoa Bobby-Joe. \n\nImage by ChatGPT.\n\nCrazy all day journey here to set up a [TiTiler](https://github.com/developmentseed/titiler) on a remote server. Thanks \nto ChatGPT for the help.\n\nThis is a continuation of a past post that you can find [here](https://www.newgraphenvironment.com/new_graphiti/posts/2024-09-21-aws-cors-cog-leaflet/).\n\nWe already have a bucket set up to serve our COG imagery files and we have crafted a simple index.HTML file to point to \nthose COG files and serve them out in a basic leaflet map. A little bit of complexity arrives because we need a tile service \nin order for these images to be able to be rendered in the browser. For that we need something like `titiler` running \non a cloud instance. So we're gonna document that set up on AWS here so we can find it again.\n\nWe are gonna use something called elastic beanstalk (eb), which is a AWS service to simplify things for us so the first thing \nwe need to do was\n\n brew install a WSEBCLI. \n \n \nAfter that's done - because we are already set up with things like environmental variables for configuration of AWS it is \ngoing to link to those credentials automatically on initialization. \n\nSo next we need to identify a launch template for the `eb` environment as per these [docs](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-autoscaling-launch-templates.html)\n\nFirst thing is to find the latest Amazon Linux 2 AMI ID:\n\n aws ssm get-parameters --names \"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64\" --region us-west-2\n \nwhich gives us\n\n```\n{\n \"Parameters\": [\n {\n \"Name\": \"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64\",\n \"Type\": \"String\",\n \"Value\": \"ami-093a4ad9a8cc370f4\",\n \"Version\": 105,\n \"LastModifiedDate\": \"2025-01-16T16:44:38.939000-08:00\",\n \"ARN\": \"arn:aws:ssm:us-west-2::parameter/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64\",\n \"DataType\": \"text\"\n }\n ],\n \"InvalidParameters\": []\n}\n```\n\nSo then we create a launch template with the following cmd:\n\n aws ec2 create-launch-template --launch-template-name TitilerTemplate \\\n --launch-template-data '{\n \"ImageId\": \"ami-093a4ad9a8cc370f4\",\n \"InstanceType\": \"t3.micro\"\n }'\n \nThis gives us back this which we use to get our LaunchTemplateId:\n\n```\n{\n \"LaunchTemplate\": {\n \"LaunchTemplateId\": \"lt-049eff4ed7a9490f8\",\n \"LaunchTemplateName\": \"TitilerTemplate\",\n \"CreateTime\": \"2025-01-17T23:37:06+00:00\",\n \"CreatedBy\": \"arn:aws:iam::{my-secret-account-id}:user/{my-secet-username}\",\n \"DefaultVersionNumber\": 1,\n \"LatestVersionNumber\": 1\n }\n}\n```\n\n\nThe default security group is likely not appropriate for a public-facing tile server because it might:\n\nAllow broad internal access within your AWS account, which is unnecessary.\nRestrict external traffic, preventing public access to your tiles.\n\nFor a public-facing tile server like Titiler, the security group should:\n\nAllow Inbound HTTP/HTTPS Traffic:\nOpen port 80 (HTTP) and port 443 (HTTPS) to the world (0.0.0.0/0).\nRestrict Unnecessary Access:\nLimit other inbound traffic (e.g., SSH or internal AWS traffic) unless explicitly needed.\nCreate a Custom Security Group\n\nHere’s how to set up a security group specifically for your tile server:\n\nCreate the Security Group:\n\n\n aws ec2 create-security-group --group-name titilersecuritygroup \\\n --description \"Security group for Titiler tile server\"\n \nAllow Public HTTP/HTTPS Access:\n\n\n aws ec2 authorize-security-group-ingress --group-name titilersecuritygroup \\\n --protocol tcp --port 80 --cidr 0.0.0.0/0\n aws ec2 authorize-security-group-ingress --group-name titilersecuritygroup \\\n --protocol tcp --port 443 --cidr 0.0.0.0/0\n\nGet the Security Group ID:\n\n\n aws ec2 describe-security-groups --group-names titilersecuritygroup --query \"SecurityGroups[0].GroupId\" --output text\n\n\nUpdate the Launch Template: Add the Security Group ID to the Launch Template using its LaunchTemplateId:\n\n\n\n```\noption_settings:\n aws:autoscaling:launchtemplate:\n LaunchTemplateId: lt-049eff4ed7a9490f8\n InstanceType: t3.micro\n SecurityGroups: sg-xxxxxxxxxxxxxxxxxx\n```\n\nThen we make a litle `launchtemplate.config` file and put it in our main project directory `elastic-beanstock` in a \n`.ebextensions` directory. It looks like this:\n\n```\noption_settings:\n aws:autoscaling:launchconfiguration:\n SecurityGroups: sg-xxxxxxxxxxxxxxxxxx\n InstanceType: t3.micro\n RootVolumeType: gp3\n MonitoringInterval: \"1 minute\"\n DisableIMDSv1: true\n IamInstanceProfile: \"aws-elasticbeanstalk-ec2-role\"\n```\n\nThen we make a `Dockerrun.aws.json` file to go in our main project directory `elastic-beanstock`. It looks like this:\n\n```\n{\n \"AWSEBDockerrunVersion\": \"1\",\n \"Image\": {\n \"Name\": \"developmentseed/titiler\",\n \"Update\": \"true\"\n },\n \"Ports\": [\n {\n \"ContainerPort\": 80\n }\n ]\n}\n```\n\nThen we create a `trust-policy.json` in our main `elastic-beanstock` directory to allow `eb` to:\n\n - Launch and terminate EC2 instances.\n - Create and manage security groups.\n - Configure Auto Scaling.\n - Set up Elastic Load Balancers.\n - Access S3 buckets for deployments.\n \n It looks like this:\n \n ```\n {\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"elasticbeanstalk.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n```\n\nAttach the policy \n\n aws iam attach-role-policy --role-name aws-elasticbeanstalk-service-role \\\n --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess\n\nVerify the policy is attached:\n\n aws iam list-attached-role-policies --role-name aws-elasticbeanstalk-service-role\n\nYou should see AmazonEC2FullAccess in the output.\n\nVerify the VPC\n\n aws ec2 describe-vpcs --query \"Vpcs[?IsDefault].VpcId\" --region us-west-2 --output text\n \nUpdate .ebextensions/launchtemplate.config. Now it looks like this:\n\n```\noption_settings:\n aws:autoscaling:launchconfiguration:\n SecurityGroups: sg-xxxxxxxxxxxxxx\n InstanceType: t3.micro\n RootVolumeType: gp3\n MonitoringInterval: \"1 minute\"\n DisableIMDSv1: true\n IamInstanceProfile: \"aws-elasticbeanstalk-ec2-role\"\n aws:ec2:vpc:\n VPCId: vpc-xxxxxxxxxxxxx\n ```\n\nNext it gets weird - Find the Default Route Table:\n\n aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-0232b379f43b2237f --region us-west-2\n\n\nTHEN:\n\n aws ec2 associate-route-table --route-table-id rtb-0ae8d407cc5bb8d24 --subnet-id subnet-xxx\n aws ec2 associate-route-table --route-table-id rtb-xx --subnet-id subnet-xx\n aws ec2 associate-route-table --route-table-id rtb-x --subnet-id subnet-x\n aws ec2 associate-route-table --route-table-id rtb-xx --subnet-id subnet-xx\n\nUpdate .ebextensions/launchtemplate.config: Ensure your configuration file includes the associated subnets:\n\n```\noption_settings:\n aws:autoscaling:launchconfiguration:\n SecurityGroups: sg-xxxxx\n InstanceType: t3.micro\n RootVolumeType: gp3\n MonitoringInterval: \"1 minute\"\n DisableIMDSv1: true\n IamInstanceProfile: \"aws-elasticbeanstalk-ec2-role\"\n aws:ec2:vpc:\n VPCId: vpc-xxx\n Subnets: subnet-0af968a6248b166ec,subnet-0f1ae62f1124d33b9,subnet-042e5c84263f93d15,subnet-05513f7bc88739e10\n\n```\n\n\nNow we create the env:\n\n eb create titiler-env \n\n \nOnce that is completed we can find our Elastic Beanstalk environment's `CNAME` with:\n\n eb status\n\n\n\nIts `CNAME: titiler-env.eba-s4jhubvr.us-west-2.elasticbeanstalk.com`\n\n \nHere is what our setup file structure looks like.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nfs::dir_tree(\"/Users/airvine/Projects/repo/elastic-beanstalk\", recurse = TRUE, all = TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n/Users/airvine/Projects/repo/elastic-beanstalk\n├── .ebextensions\n│ └── launchtemplate.config\n├── .elasticbeanstalk\n│ └── config.yml\n├── .gitignore\n├── Dockerrun.aws.json\n└── trust-policy.json\n```\n\n\n:::\n:::\n\n\n\nSo we are going to add this to the index file that we serve in another AWS bucket to serve out our leaflet map\nwith the COG.\n\n 'http://titiler-env.eba-s4jhubvr.us-west-2.elasticbeanstalk.com/cog/tiles/{z}/{x}/{y}.png?url=https://23cog.s3.amazonaws.com/20210906lampreymoricetribv220230317.tif'\n \n \nGeez... Here it is and can be seen in alll its fullscreen glory [here](http://23cog.s3-website-us-west-2.amazonaws.com/test-viewer/)!!!\n\n\n::: {.cell}\n\n```{.r .cell-code}\nmap_url <- '<iframe src=\"http://23cog.s3-website-us-west-2.amazonaws.com/test-viewer/\" width=\"100%\" height=\"600\" frameborder=\"0\"></iframe>'\n\nknitr::asis_output(map_url)\n```\n\n::: {.cell-output-display}\n`````{=html}\n<iframe src=\"http://23cog.s3-website-us-west-2.amazonaws.com/test-viewer/\" width=\"100%\" height=\"600\" frameborder=\"0\"></iframe>\n`````\n:::\n:::\n",
"markdown": "---\ntitle: \"Setting up TiTiler to serve COGs of UAV imagery on AWS with leaflet and Elastic Beanstalk\"\nauthor: \"al\"\ndate: \"2025-01-17\"\ndate-modified: \"2025-01-17\"\ncategories: [aws, s3, r, paws, s3sf, leaflet, COG]\nimage: \"image.jpg\"\nparams:\n repo_owner: \"NewGraphEnvironment\"\n repo_name: \"new_graphiti\"\nformat: \n html:\n code-fold: true\n---\n\n\nWhoa Bobby-Joe. \n\nJourney here to set up a [TiTiler](https://github.com/developmentseed/titiler) on a remote server. \n\nThis is a continuation of a past post that you can find [here](https://www.newgraphenvironment.com/new_graphiti/posts/2024-09-21-aws-cors-cog-leaflet/).\nThanks to ChatGPT for the help. Image by ChatGPT.\n\nWe already have a bucket set up to serve our COG imagery files and we have crafted a simple `index.HTML` file on `AWS` to point to \na COG file and serve it out in a basic leaflet map. A little bit of complexity arrives because we want a tile service \nin order for these images to be able to be rendered in the browser using server side rendering. For that we need something like `TiTiler` running \non a cloud instance. So we're gonna document that set up on AWS here so we can find it again.\n\nTo enable scalability and simplify deployment we will use [`AWS Elastic Beanstalk`](https://aws.amazon.com/elasticbeanstalk/?gclid=Cj0KCQiA4rK8BhD7ARIsAFe5LXIss1fnBHnOiluC0QJbB7A6W4AHZ-4hnYtJsmdcfZcLwHWWCjjLdC0aAheVEALw_wcB&trk=420d780f-52e0-462f-8a22-617780037847&sc_channel=ps&ef_id=Cj0KCQiA4rK8BhD7ARIsAFe5LXIss1fnBHnOiluC0QJbB7A6W4AHZ-4hnYtJsmdcfZcLwHWWCjjLdC0aAheVEALw_wcB:G:s&s_kwcid=AL!4422!3!651612435557!e!!g!!elastic%20beanstalk!19836375244!150076953787) (`eb`). We are on a mac so first thing we do is:\n\n brew install a WSEBCLI. \n \n \nBecause we are already set up with credentials through environmental variables back when we set up [`awscli`](https://formulae.brew.sh/formula/awscli) `eb`\nwill link to those credentials automatically on initialization. \n\nSo next we need to identify a launch template for the `eb` environment as per these [docs](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-autoscaling-launch-templates.html)\n\nFirst thing is to find the latest Amazon Linux 2 AMI ID:\n\n aws ssm get-parameters --names \"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64\" --region us-west-2\n \nwhich gives us\n\n```\n{\n \"Parameters\": [\n {\n \"Name\": \"/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64\",\n \"Type\": \"String\",\n \"Value\": \"ami-093a4ad9a8cc370f4\",\n \"Version\": 105,\n \"LastModifiedDate\": \"2025-01-16T16:44:38.939000-08:00\",\n \"ARN\": \"arn:aws:ssm:us-west-2::parameter/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64\",\n \"DataType\": \"text\"\n }\n ],\n \"InvalidParameters\": []\n}\n```\n\nSo then we do the long way and first create a launch template with the following cmd:\n\n aws ec2 create-launch-template --launch-template-name TitilerTemplate \\\n --launch-template-data '{\n \"ImageId\": \"ami-093a4ad9a8cc370f4\",\n \"InstanceType\": \"t3.micro\"\n }'\n \nThis gives us back this which we use to get our LaunchTemplateId:\n\n```\n{\n \"LaunchTemplate\": {\n \"LaunchTemplateId\": \"lt-049eff4ed7a9490f8\",\n \"LaunchTemplateName\": \"TitilerTemplate\",\n \"CreateTime\": \"2025-01-17T23:37:06+00:00\",\n \"CreatedBy\": \"arn:aws:iam::{my-secret-account-id}:user/{my-secet-username}\",\n \"DefaultVersionNumber\": 1,\n \"LatestVersionNumber\": 1\n }\n}\n```\n\n\nThe default security group is likely not appropriate for a public-facing tile server because it might:\n\n - Allow broad internal access within your AWS account, which is unnecessary.\n - Restrict external traffic, preventing public access to your tiles.\n\nFor a public-facing tile server like Titiler, the security group should:\n\n - Allow Inbound HTTP/HTTPS Traffic:\n - Open port 80 (HTTP) and port 443 (HTTPS) to the world (0.0.0.0/0).\n - Restrict Unnecessary Access:\n - Limit other inbound traffic (e.g., SSH or internal AWS traffic) unless explicitly needed.\n - Create a Custom Security Group\n\nHere’s how to set up a security group specifically for your tile server:\n\nCreate the Security Group:\n\n\n aws ec2 create-security-group --group-name titilersecuritygroup \\\n --description \"Security group for Titiler tile server\"\n \nAllow Public HTTP/HTTPS Access:\n\n\n aws ec2 authorize-security-group-ingress --group-name titilersecuritygroup \\\n --protocol tcp --port 80 --cidr 0.0.0.0/0\n aws ec2 authorize-security-group-ingress --group-name titilersecuritygroup \\\n --protocol tcp --port 443 --cidr 0.0.0.0/0\n\nGet the Security Group ID:\n\n\n aws ec2 describe-security-groups --group-names titilersecuritygroup --query \"SecurityGroups[0].GroupId\" --output text\n\n\nUpdate the Launch Template: Add the Security Group ID to the Launch Template using its LaunchTemplateId:\n\n\nThen we make a litle `launchtemplate.config` file and put it in our main project directory `elastic-beanstock` in a \n`.ebextensions` directory. It looks like this with our `SecurityGroups` id added as per our last query:\n\n```\noption_settings:\n aws:autoscaling:launchconfiguration:\n SecurityGroups: sg-xxxxxxxxxxxxxxxxxx\n InstanceType: t3.micro\n RootVolumeType: gp3\n MonitoringInterval: \"1 minute\"\n DisableIMDSv1: true\n IamInstanceProfile: \"aws-elasticbeanstalk-ec2-role\"\n```\n\nIn order to have an easy launch of `Titiler` we make a `Dockerrun.aws.json` file to go in our main `elastic-beanstock` roject \ndirectory we have created to do this work. The `Dockerrun.aws.json` file looks like this:\n\n```\n{\n \"AWSEBDockerrunVersion\": \"1\",\n \"Image\": {\n \"Name\": \"developmentseed/titiler\",\n \"Update\": \"true\"\n },\n \"Ports\": [\n {\n \"ContainerPort\": 80\n }\n ]\n}\n```\n\nThen we create a `trust-policy.json` in our main `elastic-beanstock` directory to allow `eb` to:\n\n - Launch and terminate EC2 instances.\n - Create and manage security groups.\n - Configure Auto Scaling.\n - Set up Elastic Load Balancers.\n - Access S3 buckets for deployments.\n \n It looks like this:\n \n ```\n {\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"elasticbeanstalk.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n```\n\nNow we attach the policy \n\n aws iam attach-role-policy --role-name aws-elasticbeanstalk-service-role \\\n --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess\n\nTo be sure - we verify the policy is attached:\n\n aws iam list-attached-role-policies --role-name aws-elasticbeanstalk-service-role\n\nYou should see AmazonEC2FullAccess in the output.\n\nVerify the VPC. A VPC (Virtual Private Cloud) is a private, isolated network within AWS where you can launch and manage \nAWS resources like EC2 instances, databases, and load balancers. Run this command to see the route tables for each subnet \nand determine if they are public:\n\n aws ec2 describe-vpcs --query \"Vpcs[?IsDefault].VpcId\" --region us-west-2 --output text\n \n\nNext it gets weird - Find the Default Route Table with a query that includes our uniqye `VpcId` which we recieved from\nour last query:\n\n aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-XXXXXXXXXXXXX --region us-west-2\n\n\nBecause the default route table is connected to an Internet Gateway - subnets need to be explicitly associated with this route table.\nLook for entries with \"DestinationCidrBlock\": \"0.0.0.0/0\" and \"GatewayId\": \"igw-xxxxxxxx\" in the output. \nThese indicate that the subnet is public.; those without are private:\n\n aws ec2 associate-route-table --route-table-id rtb-xx --subnet-id subnet-xxx\n aws ec2 associate-route-table --route-table-id rtb-xx --subnet-id subnet-xx\n aws ec2 associate-route-table --route-table-id rtb-x --subnet-id subnet-x\n aws ec2 associate-route-table --route-table-id rtb-xx --subnet-id subnet-xx\n\nUpdate your `VPCId` in your `.ebextensions/launchtemplate.config`.\nAlso Ensure your configuration file includes the associated subnets:\n\n```\noption_settings:\n aws:autoscaling:launchconfiguration:\n SecurityGroups: sg-xxxxx\n InstanceType: t3.micro\n RootVolumeType: gp3\n MonitoringInterval: \"1 minute\"\n DisableIMDSv1: true\n IamInstanceProfile: \"aws-elasticbeanstalk-ec2-role\"\n aws:ec2:vpc:\n VPCId: vpc-xxx\n Subnets: subnet-xx,subnet-xx,subnet-xx,subnet-xx\n\n```\n\n\nNow we create the env:\n\n eb create titiler-env \n\n \nOnce that is completed we can find our Elastic Beanstalk environment's `CNAME` with:\n\n eb status\n\n\n\nIts `CNAME: titiler-env.eba-s4jhubvr.us-west-2.elasticbeanstalk.com`\n\n \nHere is what our setup file structure looks like.\n\n::: {.cell}\n\n```{.r .cell-code}\nfs::dir_tree(\"/Users/airvine/Projects/repo/elastic-beanstalk\", recurse = TRUE, all = TRUE)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n/Users/airvine/Projects/repo/elastic-beanstalk\n├── .ebextensions\n│ └── launchtemplate.config\n├── .elasticbeanstalk\n│ └── config.yml\n├── .gitignore\n├── Dockerrun.aws.json\n└── trust-policy.json\n```\n\n\n:::\n:::\n\n\nSo we are going to add this to the index file that we serve in another AWS bucket to serve out our leaflet map\nwith the COG.\n\n 'http://titiler-env.eba-s4jhubvr.us-west-2.elasticbeanstalk.com/cog/tiles/{z}/{x}/{y}.png?url=https://23cog.s3.amazonaws.com/20210906lampreymoricetribv220230317.tif'\n \n \nGeez... Here it is and can be seen in alll its fullscreen glory [here](http://23cog.s3-website-us-west-2.amazonaws.com/test-viewer/)!!!\n\n<iframe src=\"http://23cog.s3-website-us-west-2.amazonaws.com/test-viewer/\" \n style=\"border: none;\" \n width=\"100%\" \n height=\"600\">\n</iframe>\n",
"supporting": [],
"filters": [
"rmarkdown/pagebreak.lua"
Expand Down
Loading

0 comments on commit 4cdad63

Please sign in to comment.