Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

image resizer #23

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions aws/image-resizer/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions aws/image-resizer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Image resizer for S3

- Lambda Edge 기반의 이미지 리사이저

## 리소스 구성

1. Lambda
2. Cloudfront
3. CodePipeline(CodeBuild)
4. Lambda Edge
5. S3 (for CodeBuild)

## 프로젝트 템플릿

- 현재는 Node.js 서버만 고려한 상태입니다.

### Node.js

- [템플릿](https://github.com/myyrakle/image_resizer_template) 프로젝트를 clone하거나 fork해서 사용합니다.

## 준비물

- 다음 명령을 사용해서 사용할 codestar 정보를 조회합니다.
`aws codestar-connections list-connections`
- github에 템플릿을 참고해서 프로젝트를 생성합니다.

---

## parameter 설정

- 자세한 것은 [](./variables.tf)에서 확인하거나 수정할 수 있습니다.

### required parameter

1. region: 리전 정보입니다. 서울이라면 ap-northeast-2 값을 넘겨줍니다.
2. environment: 환경 정보입니다. server_name과 조합되어 고유의 리소스 이름을 형성합니다. prod, stage, dev 등의 값을 설정하면 됩니다.
3. system_name: 시스템명입니다. environment와 조합해서 고유의 리소스 이름을 형성합니다.
4. github_user: github username or organization name입니다.
5. github_repository: 레포지토리명입니다.
6. github_branch: 트리거할 브랜치입니다.
7. codestar_arn: codestart connection ARN입니다.

### optional parameter

1. buildspec_path: 빌드에 사용할 buildspec.yml 위치입니다.
2. codebuild_compute_type: code build 컴퓨팅 타입입니다.
3. lambda_runtime: 람다 런타임. 현재는 node.js만 고려해둔 상태입니다.
4. lambda_layers: 레이어 목록입니다.
113 changes: 113 additions & 0 deletions aws/image-resizer/cicd.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// 아티팩트 버킷
resource "aws_s3_bucket" "artifact_bucket" {
bucket = "${local.resource_id}-artifact-bucket"

tags = local.tags
}

// 빌드 캐시용 버킷
resource "aws_s3_bucket" "cache_bucket" {
bucket = "${local.resource_id}-cache-bucket"

tags = local.tags
}

// code build
resource "aws_codebuild_project" "codebuild" {
name = local.resource_id
description = "code build"
build_timeout = "30"
service_role = aws_iam_role.codebuild_role.arn

artifacts {
type = "S3"
location = aws_s3_bucket.artifact_bucket.bucket
}

cache {
type = "S3"
location = aws_s3_bucket.cache_bucket.bucket
}

environment {
compute_type = var.codebuild_compute_type
image = "aws/codebuild/amazonlinux2-x86_64-standard:4.0"
type = "LINUX_CONTAINER"
image_pull_credentials_type = "CODEBUILD"
privileged_mode = true

environment_variable {
name = "AWS_ACCOUNT_ID"
value = local.account_id
}

environment_variable {
name = "AWS_DEFAULT_REGION"
value = local.region
}

environment_variable {
name = "EnvironmentName"
value = local.resource_id
}
}

source {
type = "S3"
location = "${aws_s3_bucket.artifact_bucket.arn}/source.zip"
buildspec = var.buildspec_path
}

tags = local.tags
}

// code pipeline
resource "aws_codepipeline" "codepipeline" {
name = local.resource_id
role_arn = aws_iam_role.codepipeline_role.arn

artifact_store {
location = aws_s3_bucket.artifact_bucket.bucket
type = "S3"
}

stage {
name = "Source"

action {
configuration = {
ConnectionArn : var.codestar_arn
FullRepositoryId : join("/", [var.github_user, var.github_repository])
BranchName : var.github_branch
}

name = "Source"
category = "Source"
owner = "AWS"
provider = "CodeStarSourceConnection"
version = "1"

output_artifacts = ["Source"]
}
}

stage {
name = "Build"

action {
name = "Build"
category = "Build"
owner = "AWS"
provider = "CodeBuild"
input_artifacts = ["Source"]
output_artifacts = ["Build"]
version = "1"

configuration = {
ProjectName = aws_codebuild_project.codebuild.name
}
}
}

tags = local.tags
}
91 changes: 91 additions & 0 deletions aws/image-resizer/codes/origin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const http = require("http");
const https = require("https");
const querystring = require("querystring");
const sharp = require("sharp");
const urlencode = require("urlencode");

const aws = require("aws-sdk");
const S3 = new aws.S3();

// 이미지 사이즈로 축소
async function toSmallSize(image) {
const resized = image.resize({ width: 140, height: 140 });

return resized;
}

const BUCKET_NAME = "metaverse2-community-image";

exports.handler = async (event, context, callback) => {
const response = event.Records[0].cf.response;

try {
console.log("Response status code :%s", response.status);

// 이미 리사이징된 파일이 저장되어있지 않을 경우
if (response.status == 404 || response.status == 403) {
const request = event.Records[0].cf.request;

const requestUri = urlencode.decode(request.uri).replace("/", ""); // 첫번째 슬래시 제거
const uri = requestUri.replace("resize/", ""); // resize prefix 제거

console.log("requestUri: ", requestUri);

const resizeType = uri.split("/")[0];
const originUri = uri.replace(resizeType + "/", "");

console.log("resizeType: ", resizeType);
console.log("originUri: ", originUri);

switch (resizeType) {
// /resize/small/... 케이스 처리
case "small": {
const data = await S3.getObject({
Bucket: BUCKET_NAME,
Key: originUri,
}).promise();

let image = sharp(data.Body);
const metadata = await image.metadata();
const format = metadata.format;

const mimeType = "image/" + format;

image = await toSmallSize(image);

const buffer = await image.toBuffer();

// 백업이 필요하다면 resize 경로에 저장. 하지 않더라도 캐싱 자체는 됨. 선택사항
// await S3.upload({
// Body: buffer,
// Bucket: '...',
// Key: requestUri,
// ContentType: mimeType,
// ACL: 'public-read',
// }).promise();

// generate a binary response with resized image
response.status = 200;
response.body = buffer.toString("base64");
response.bodyEncoding = "base64";
response.headers["content-type"] = [
{ key: "Content-Type", value: mimeType },
];
callback(null, response);

break;
}
default: {
callback(null, response);
break;
}
}
} else {
callback(null, response);
}
} catch (error) {
console.log("!! ERROR");
console.error(error);
callback(null, response);
}
};
14 changes: 14 additions & 0 deletions aws/image-resizer/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
data "aws_caller_identity" "current" {}

locals {
region = "us-east-1"

tags = {
Environment = var.environment
Application = var.server_name
}

resource_id = join("-", [var.system_name, var.environment])

account_id = data.aws_caller_identity.current.account_id
}
35 changes: 35 additions & 0 deletions aws/image-resizer/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
terraform {
required_providers {
# 일종의 라이브러리 로드
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}

required_version = ">= 1.2.0"
}

provider "aws" {
region = local.region
}

resource "aws_lambda_function" "lambda" {
description = "A lambda connect function for ${local.resource_id}"
function_name = local.resource_id
role = aws_iam_role.lambda_role.arn
layers = var.lambda_layers
runtime = var.lambda_runtime
handler = "index.handler"
filename = "codes/zip/connect.zip"

environment {
variables = {
ServerName = var.server_name
ENVIRONMENT = var.environment
ConnectionTableName = "${local.resource_id}_connection"
}
}

tags = local.tags
}
1 change: 1 addition & 0 deletions aws/image-resizer/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading