Skip to content

Commit

Permalink
feat(infra): 5주차 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
dotoleeoak committed Oct 26, 2023
1 parent fe78e1b commit 0e646c3
Show file tree
Hide file tree
Showing 6 changed files with 343 additions and 4 deletions.
2 changes: 1 addition & 1 deletion content/docs/infra/4. AWS: EC2, Network.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ flowchart LR
Nginx -- localhost:3000 --> Node.js
end
end
Client -- http://<domain-name> --> Nginx
Client -- http://[domain-name] --> Nginx
```

- Nginx container에서 listen하는 port를 80번(HTTP)으로 변경해주세요.
Expand Down
136 changes: 133 additions & 3 deletions content/docs/infra/5. AWS: S3, IAM.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,139 @@
title = "5. AWS: S3, IAM"
description = "AWS의 S3와 IAM에 대해 알아봅니다."
icon = "article"
date = "2023-09-11"
lastmod = "2023-10-05"
date = "2023-10-26"
lastmod = "2023-10-26"
weight = 350
+++

{{< alert context="info" text="작성 준비 중입니다! 조금만 기다려주세요. 🥹" />}}
Amazon S3는 AWS에서 제공하는 파일 스토리지 서비스입니다. 작게는 HTML, CSS 같은 웹 사이트 정적 파일을 저장하고, 크게는 수십 테라바이트의 데이터를 저장할 수 있어요.

IAM은 AWS의 Identity and Access Management의 약자로, AWS의 사용자, 그룹, 권한을 관리하는 서비스입니다. 보안 뿐만 아니라 프로그램의 예상치 못한 동작을 방지하기 위해 IAM을 사용해요.

## 공부할 내용 📚

### 1. Amazon S3

Amazon S3의 기본적인 용어들을 알아보고, S3 버킷을 생성하며 관련 설정들을 알아봐요.

- S3의 개념과 특징을 알아봐요.
- S3의 용어들을 알아봐요. (bucket, object, key, ACL)
- S3 버킷을 생성하고, 버킷에 파일을 업로드해봐요.
- S3의 요금을 알아봐요.
- S3의 버저닝 기능을 알아봐요.

#### 참고 자료

- **["AWS Amazon S3 버킷 생성하기" (글)](https://zzang9ha.tistory.com/358)**: S3의 다양한 개념들이 정리되어있습니다.
- **["Amazon S3 요금"](https://aws.amazon.com/ko/s3/pricing/)**: AWS의 S3 요금 페이지입니다. S3의 요금을 알아보세요.
- **["S3 버킷에서 버전 관리를 사용하면 무엇이 달라질까?" (글)](https://dev.classmethod.jp/articles/jw-what-would-it-make-a-difference-to-use-version-management-in-an-s3-bucket/)**: S3의 버전 관리 기능에 대해 알아보세요.

### 2. Amazon CloudFront

Amazon CloudFront는 AWS에서 제공하는 CDN(Content Delivery Network) 서비스입니다. CDN은 전 세계에 분산된 서버를 통해 정적 파일을 빠르게 전송할 수 있도록 도와줍니다.

- CloudFront의 개념과 특징을 알아봐요.
- CloudFront의 용어들을 알아봐요. (distribution, origin, TTL, invalidation)
- CloudFront의 요금을 알아봐요.

#### 참고 자료

- **["CloudFront" (글))](https://velog.io/@combi_areum/AWS-CloudFront)**: Cloudfront 개념과 특징을 정리한 글입니다.
- **["CloudFront 요금"](https://aws.amazon.com/ko/cloudfront/pricing/)**: AWS의 CloudFront 요금 페이지입니다. CloudFront의 요금을 알아보세요.

### 3. AWS IAM

AWS IAM의 용어들과 구성, 문법을 알아봐요.

- IAM의 용어들을 알아봐요. (User, Group, Role, Policy, Permission)
- User를 새로 생성하고, 직접 로그인해봐요.
- IAM Policy의 JSON 문법을 알아봐요. (Statement, Effect, Action, Resource)
- Identity-based Policy와 Resource-based Policy의 차이를 알아봐요.

{{< alert context="warning" text="AWS의 Best Practice 중 하나는 root 계정을 직접 쓰지 않고 IAM User로 접근하는 것입니다. 앞으로 가능하면 IAM User로 연습해보세요!" />}}

#### 참고 자료

- **["IAM이란 (정책, 역할, 권한 경계)" (글)](https://yoonchang.tistory.com/93)**: IAM의 다양한 개념들이 정리되어있습니다.
- **["AWS IAM Identity base 와 resource base policy 의 차이" (글)](https://going-to-end.tistory.com/entry/AWS-IAM-Policy%EC%A0%95%EC%B1%85-Role%EC%97%AD%ED%95%A0-%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90)**

## 프로젝트 실습 🎈

이번 주에는 아래 그림처럼 클라우드 서비스를 구성해봐요. Client의 요청은 CloudFront로 전달되고, `/api` 요청은 EC2의 Node.js로, 그 외 요청은 S3로 전달됩니다. ALB는 AWS의 Load Balancer 서비스로, 스터디에 없었던 내용인 만큼 세팅 방법은 아래를 참고해주세요. 실습 내용이 많은 만큼, 힌트를 많이 드릴게요!

```mermaid
flowchart LR
subgraph AWS
subgraph Docker in EC2
Node.js
end
ALB -- localhost:3000 --> Node.js
CloudFront -- /api/* --> ALB
CloudFront -- /* --> S3
end
Client -- http://[domain-name] --> CloudFront
```

- S3에 HTML, CSS, JS 파일을 업로드하세요. (파일 링크:
<a href="/docs/infra/assets/week5/index.html" download>HTML</a>,
<a href="/docs/infra/assets/week5/style.css" download>CSS</a>,
<a href="/docs/infra/assets/week5/script.js" download>JS</a>)

- S3에서 Static Website Hosting을 설정하고, Permisson을 public으로 바꾼 다음 접속이 잘 되는지 확인하세요. Permission은 아래 순서로 바꿀 수 있어요.

1. Bucket 선택 > Permissions 탭 > Block public access 편집 > ACL 들어간 항목 제외한 두 가지 모두 체크 해제 ('Save changes'가 안된다면, 왼쪽 탭에서 'Block Public Access settings for this account' 클릭 후 마찬가지로 체크 해제)
2. Bucket Policy 편집 > 아래 JSON을 붙여넣기 > `YOUR_BUCKET_NAME`을 자신의 버킷 이름으로 바꾸기

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
}
]
}
```

- CloudFront를 생성하고, S3를 origin으로 설정하세요. 이후 CloudFront의 Domain Name으로 접속이 잘 되는지 확인하세요.

- EC2에 기존의 Node.js Docker Container를 올리세요. Nginx 컨테이너는 사용하지 않습니다. docker-compose.yml 파일에서 Nginx 컨테이너를 제거하고, Node.js 컨테이너의 포트 3000을 열어주세요.

```yml
version: '3'

services:
nodejs:
container_name: nodejs
build:
context: .
dockerfile: Dockerfile
# 또는 `image: <owner>/<image>:<tag>`
ports:
- 3000:3000
```
- EC2의 3000번 포트를 Security Group으로 열어두세요. 직접 URL을 입력해서 접속이 잘 되는지 확인하세요. (예: `http://123.456.789.123:3000/api/os`)

- ALB를 생성하기 전, Target Group을 생성하세요.

1. 왼쪽 탭의 'Target Groups'를 클릭하세요.
2. 'Create target group'을 클릭하세요.
3. Target type은 'Instances'를 선택하고, Target Group Name을 설정하세요. 나머지는 기본값으로 두고, Next를 클릭하세요.
4. 실행 중인 EC2 인스턴스를 선택하고, 포트 번호는 3000으로 설정하세요. 'Include as pending below'를 클릭하고, 'Create target group'을 클릭하세요.

- EC2 화면에서 왼쪽 탭의 'Load Balancers'를 클릭하여 ALB를 생성하세요.

1. 세 가지 옵션 중 'Application Load Balancer'를 선택하세요.
2. Scheme은 'Internet-facing'으로 설정하세요.
3. 'Mapping' 중 'Availability Zones'에서 최소 두 개의 Availability Zone을 선택하세요. **Target Group의 Availability Zone을 포함해야합니다.**
4. 나머지는 그대로 두고, "Listeners and routing"에서 HTTP:80에 target group을 이전 단계에서 생성한 target group을 선택하세요.
5. 'Create load balancer'를 클릭하세요.
6. Load balancer를 선택하고, Security 탭에서 Security Group을 수정하세요. Inbound 규칙에 HTTP:80을 추가하세요.
6. Load balancer의 정보 중 'DNS name'을 찾아 api 요청(예: `/api/os`)이 잘 되는지 확인하세요. 구성되는 데 시간이 걸릴 수 있어요. (**잊지 말고 http:// 붙여서 요청하세요!**)

- CloudFront에서 ALB를 origin으로 설정하세요. `/api/*` 요청은 ALB로, 그 외 요청은 S3로 전달되도록 설정하세요. 이후 CloudFront의 Domain Name으로 접속이 잘 되는지 확인하세요.
43 changes: 43 additions & 0 deletions content/docs/infra/assets/week5/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OS Information</title>
<link rel="stylesheet" href="style.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap"
rel="stylesheet"
/>
<script src="script.js"></script>
</head>
<body>
<div class="login">
<form
action="/login"
method="POST"
onsubmit="formType == 'login' ? login(event) : signup(event)"
>
<input type="text" name="username" placeholder="아이디" />
<input type="password" name="password" placeholder="비밀번호" />
<input type="hidden" name="email" placeholder="이메일" />
<button type="submit">로그인</button>
<a onclick="toggle()">회원가입 하기</a>
</form>
</div>
<div class="wrapper">
<h1 class="title">About OS</h1>
<section class="card">
<h2 class="sub-title">OS Type</h2>
<p class="type">Loading...</p>
<h2 class="sub-title">Name</h2>
<p class="hostname">Loading...</p>
<h2 class="sub-title">CPU Number</h2>
<p class="cpu-num">Loading... Core</p>
<h2 class="sub-title">Total Memory</h2>
<p class="total-mem">Loading...</p>
</section>
</div>
</body>
</html>
84 changes: 84 additions & 0 deletions content/docs/infra/assets/week5/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
let formType = 'login'

const load = async () => {
try {
const data = await fetch('/api/os').then((res) => res.json())
document.querySelector('.type').innerText = data.type
document.querySelector('.hostname').innerText = data.hostname
document.querySelector('.cpu-num').innerText = data.cpu_num
document.querySelector('.total-mem').innerText = data.total_mem
} catch (error) {
console.error(error)
alert('⚠️ 서버에서 데이터를 가져올 수 없어요!\n' + error.message)
}
}

const login = async (e) => {
e.preventDefault()
const username = e.target.username.value
const password = e.target.password.value
if (!username || !password) {
alert('⚠️ 아이디와 비밀번호를 입력해주세요!')
return
}
try {
const data = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
})
if (data.status !== 200) {
alert('⚠️ 로그인에 실패했어요!')
return
}
document.querySelector('.login').classList.add('hide')
await load()
} catch (error) {
console.error(error)
alert('⚠️ 서버에서 데이터를 가져올 수 없어요!\n' + error.message)
}
}

const signup = async (e) => {
e.preventDefault()
const username = e.target.username.value
const password = e.target.password.value
const email = e.target.email.value
if (!username || !password) {
alert('⚠️ 아이디와 비밀번호를 입력해주세요!')
return
}
if (!email) {
alert('⚠️ 이메일을 입력해주세요!')
return
}
try {
const data = await fetch('/api/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password, email })
})
if (data.status !== 201) {
alert('⚠️ 회원가입에 실패했어요!')
return
}
alert('✅ 회원가입에 성공했어요!')
} catch (error) {
console.error(error)
alert('⚠️ 서버에서 데이터를 가져올 수 없어요!\n' + error.message)
}
}

const toggle = () => {
formType = formType === 'login' ? 'signup' : 'login'
document.querySelector('button').innerText =
formType === 'login' ? '로그인' : '회원가입'
document.querySelector('a').innerText =
formType === 'login' ? '회원가입 하기' : '로그인 하기'
document.querySelector('input[name="email"]').type =
formType === 'login' ? 'hidden' : 'email'
}
79 changes: 79 additions & 0 deletions content/docs/infra/assets/week5/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
body {
height: 100%;
margin: 0;
font-family: 'Inter', 'Noto Sans KR', system-ui, -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell,
'Open Sans', 'Helvetica Neue', sans-serif;
}
.login {
position: fixed;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(4px);
transition: all 0.5s ease-in-out;
}
.login > form {
width: 20rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 1rem;
}
.login > form > a {
background-color: #fff;
opacity: 0.75;
padding: 0 0.5rem;
cursor: pointer;
text-decoration: underline;
}
input {
width: 100%;
padding: 0.5rem 1rem;
border-radius: 8px;
border: none;
font-size: 1rem;
}
button {
width: 100%;
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 1rem;
border: none;
background-color: #3581fa;
color: #fff;
cursor: pointer;
}
button:hover {
opacity: 0.8;
}
.hide {
visibility: hidden;
opacity: 0;
}
.wrapper {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.title {
font-size: 2.5rem;
background: -webkit-linear-gradient(315deg, #42d392 25%, #647eff);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.sub-title {
font-size: 1.25rem;
}
.card {
border-radius: 16px;
padding: 1rem 2rem;
background-color: #eee;
}
3 changes: 3 additions & 0 deletions hugo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ url = "/docs/frontend/1.-html,-css"
identifier = "community"
weight = 30

[markup.goldmark.renderer]
unsafe = true # raw HTML support

[deployment]
[[deployment.targets]]
name = 'AWS S3'
Expand Down

0 comments on commit 0e646c3

Please sign in to comment.