Skip to content
๊ถŒ๋‚˜์˜ edited this page Aug 31, 2022 · 18 revisions

์ฒซ ๋ฒˆ์งธ ๋„์ „ - ๊ธฐ๋ณธ์ ์ธ CI/CD ๊ตฌ์ถ• ๋ฐ 1๊ฐœ์˜ EC2

์ดˆ๊ธฐ์—๋Š” ์ปจํ…Œ์ด๋„ˆ์— ๋Œ€ํ•œ ๊ฐœ๋…๋„ ๋ถ€์กฑํ–ˆ๊ณ , AWS๋ฅผ ํ™œ์šฉํ•˜์—ฌ flask ์„œ๋ฒ„๋ฅผ ๋Œ๋ ค๋ณธ ๊ฒŒ ์ „๋ถ€์˜€์œผ๋ฉฐ, CI/CD ๊ตฌ์ถ• ์ž์ฒด์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด ์—†์–ด์„œ CI/CD ๊ตฌ์ถ• ์ดํ›„ AWS์— ์•ˆ์ •์ ์œผ๋กœ ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ๊ฐ€ ๋˜์—ˆ๋‹ค.

  • vpc, ์„œ๋ธŒ๋„ท ์„ค์ • - default vpc ๋”ฐ๋ผ๊ฐ
  • github actions๋กœ front build
  • github actions๋กœ back build
  • ์ดˆ๊ธฐ public subnet์˜ RDS private subnet์œผ๋กœ ์ด๋™ ํ•„์š”
  • ELK ์Šคํƒ์šฉ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ

์›น ์„œ๋ฒ„

  • HTTP ์š”์ฒญ์„ ์ฝ์–ด์„œ ์‘๋‹ต์„ ํ•ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋žจ
  • ์ƒ์šฉ์œผ๋กœ ๋งŽ์ด ์“ฐ์ด๋Š” ์›น ์„œ๋ฒ„ ํ”„๋กœ๊ทธ๋žจ์€ ํฌ๊ฒŒ apache์™€ nginx ์žˆ์Œ.

Apache vs nginx

1. Apache

  • ๊ธฐ๋ณธ์ ์œผ๋กœ 2๊ฐ€์ง€ ๋ฐฉ์‹์ด ์žˆ์Œ.

    • Prefork MPM(Multi Processing Module)
      • HTTP ์š”์ฒญ์ด ์˜ฌ ๋•Œ๋งˆ๋‹ค ํ”„๋กœ์„ธ์Šค ๋ณต์ œํ•ด์„œ ๊ฐ๊ฐ ๋ณ„๋„ ํ”„๋กœ์„ธ์Šค์—์„œ ํ•ด๋‹น HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ
    • Worker MPM
      • ํ•˜๋‚˜์˜ HTTP ์—ฐ๊ฒฐ ํ›„, ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋ณต์ œ๋œ ํ”„๋กœ์„ธ์Šค ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•ด์„œ ์—ฌ๋Ÿฌ HTTP ์š”์ฒญ
  • ์•„๋ฌด๋ž˜๋„ ์ด๋Ÿฐ ๋ฐฉ์‹์€ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•˜๋“  ์“ฐ๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋“  ์š”์ฒญ์— ๋”ฐ๋ผ ๋ญ”๊ฐ€๋ฅผ ์ƒ์„ฑํ•ด์•ผํ•œ๋‹ค. ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ?

2. Nginx

  • Event Driven ๋ฐฉ์‹์œผ๋กœ ๊ตฌ๋™
    • ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค๋กœ ๋™์ž‘ํ•˜๋ฉฐ, HTTP ์š”์ฒญ์„ event๋กœ ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•จ.
      • ๋Œ€๋ถ€๋ถ„์˜ HTTP ์‘๋‹ต์€ ๊ฒฐ๊ตญ html ํŒŒ์ผ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, IO ์ž‘์—…. IO ์ž‘์—…์œผ๋กœ event๋ฅผ ํฌ์›Œ๋”ฉํ•˜๊ณ , ์š”์ฒญ ์ˆœ์ด ์•„๋‹Œ, ์š”์ฒญ์ด ๋๋‚œ ์ˆœ์œผ๋กœ ์ฒ˜๋ฆฌํ•จ.
    • HTTP ์š”์ฒญ๋งˆ๋‹ค ํ”„๋กœ์„ธ์Šค๋“  ์“ฐ๋ ˆ๋“œ๋“ฑ ์ƒ์„ฑ์ด ํ•„์š”์—†์œผ๋ฏ€๋กœ, ์‹œ์Šคํ…œ ์ž์› ๊ด€๋ฆฌ์— ์žฅ์ ์ด ์žˆ์Œ.

๊ฒฐ๋ก 

Apache์™€ nginx ์ค‘ HTML ํŒŒ์ผ ์‚ฌ์ด์ฆˆ, ์–ด๋–ค ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์“ฐ๋Š๋ƒ ๋“ฑ ๋‹ค์–‘ํ•œ ์กฐ๊ฑด ๋•Œ๋ฌธ์— ๋ฌด์—‡์ด ๋” ๋ฌด์กฐ๊ฑด ์„ฑ๋Šฅ์ด ์ข‹๋‹ค๊ณ ๋Š” ์ด์•ผ๊ธฐ ํ•  ์ˆ˜ ์—†์Œ. ํ•˜์ง€๋งŒ ๋งŽ์€ ์ ‘์†์ž๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์‹œ์Šคํ…œ ์ž์› ๊ด€๋ฆฌ ํšจ์œจ์„ฑ ๋•Œ๋ฌธ์— Nginx๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์„ฑ๋Šฅ์ด ๋” ์ข‹์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Nginx๋ฅผ ์‚ฌ์šฉํ•จ.

๋ฐฐํฌ ์ „๋žต

๋‘ ๋ฒˆ์งธ ๋„์ „ - EC2์™€ ECS ๊ตฌ์ถ•


์ฐธ๊ณ 

์žฌ์ค€๋‹˜์ด ์•Œ๋ ค์ฃผ์‹  - ์ข‹์€ ๊ณต์‹๋ฌธ์„œ


์–ด๋Š ์ •๋„ aws ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์ถ•์— ๋Œ€ํ•œ ๊ธฐ๋ณธ๊ธฐ๊ฐ€ ๋‹ค์ง€๊ณ , ์ปจํ…Œ์ด๋„ˆ ๊ฐœ๋…์„ ํ•™์Šตํ•œ ์ดํ›„, ํ•ด๋‹น ๊ธฐ๋ณธ๊ธฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์ถ•์— ๋„์ „ํ•˜์˜€๋‹ค.

์ˆœ์„œ

  • (front) ๋„์ปคํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

  • (front) ECR์— ํ‘ธ์‹œ

  • (front) ECR ์ด๋ฏธ์ง€ ํ’€ ๋ฐ›์•„์„œ ์ž˜ ๋™์ž‘ํ•˜๋Š” ์ง€ ํ™•์ธ

  • Fargate ์ž‘์—… ์ •์˜ ์ƒ์„ฑ

  • Fargate ALB ์ƒ์„ฑ

  • Fargate ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

  • Fargate ์„œ๋น„์Šค ์ƒ์„ฑ ๋ฐ ์ž‘์—…์ •์˜ ์—ฐ๊ฒฐ

  • EC2 ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

  • Fargate ์„œ๋น„์Šค ์ƒ์„ฑ ๋ฐ ์ž‘์—…์ •์˜ ์—ฐ๊ฒฐ

  • (back) ๋„์ปคํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

  • (back) ECR์— ํ‘ธ์‹œ

  • (back) ECR์— ํ‘ธ์‹œ

  • ECSํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ

  • (back) private RDS ์—ฐ๊ฒฐ (SSH ํ„ฐ๋„๋ง ํ•„์š”)

  • [x]

๋„์ปค

๋„์ปค๋ž€?

๋‹ค์–‘ํ•œ ์šด์˜์ฒด์ œ์™€ ์‹œ์Šคํ…œ ํ™˜๊ฒฝ์—์„œ ์„œ๋ฒ„ ์…‹์—…์„ ์œ„ํ•œ ์ž‘์—…์ด ๋ชจ๋‘ ๋‹ค๋ฅด๊ณ  ๋ณต์žก.

๋„์ปค๋Š” ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜์˜ ๊ฐ€์ƒํ™” ํ”Œ๋žซํผ์ด๋ผ์„œ, ์ปจํ…Œ์ด๋„ˆ ์ƒ์— ์„œ๋ฒ„๋ฅผ ์…‹์—… ํ•ด๋†“์„ ์ˆ˜ ์žˆ์Œ.

๊ทธ๋ž˜์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰๋งŒ ํ•˜๋ฉด, ์„œ๋ฒ„ ์ด์ „์ด๋‚˜ ์„œ๋ฒ„ ํŒจํ‚ค์ง€ ๋ฒ„์ „ ๋“ฑ๋“ฑ์œผ๋กœ ์ผ์ผ์ด ์„œ๋ฒ„ ์„ค์ •ํ•  ํ•„์š”์—†์ด, ๋™์ผํ•œ ์„œ๋ฒ„ ์…‹์—… ๊ฐ€๋Šฅ.

๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค์™€ DevOps, ๋„์ปค

  • ๊ฐ ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋ฅผ ๋„์ปค๋กœ ๊ฐœ๋ฐœ
  • ์ดˆ๋Œ€์šฉ๋Ÿ‰ ์„œ๋น„์Šค ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์œ„ํ•œ ์„œ๋ฒ„ ํ•ธ๋“ค๋ง (ex. ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ ๊ด€๋ฆฌ) => ์ฟ ๋ฒ„๋„คํ‹ฐ์Šค

docker image

  • docker ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ช…๋ น๋“ค์„ ๊ฐ€์ง„ ํ…œํ”Œ๋ฆฟ

docker container

  • docker ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋ฆฌ๋ˆ…์Šค ์ปจํ…Œ์ด๋„ˆ ํ˜•ํƒœ๋กœ ์‹คํ–‰ํ•œ ์ƒํƒœ (instance)

EC2 ๋„์ปค ์‹คํ–‰ ํ™˜๊ฒฝ ๊ตฌ์ถ•

sudo yum update -y

sudo yum install docker -y

sudo service docker start

sudo usermod -aG docker ec2-user

aws-cli ์„ค์น˜

์ฒ˜์Œ์—๋Š” ๊ณต์‹ ๋ฌธ์„œ๋งŒ์„ ์ฐธ๊ณ ํ–ˆ๋Š”๋ฐ, ์ด ๋ฐฉ์‹์œผ๋กœ ํ•˜๋ฉด ๊ณ„์† --update๋ฅผ ๋ถ™์—ฌ๋„ 1๋ฒ„์ „๋Œ€์—์„œ ๋ฉˆ์ถฐ์žˆ์—ˆ๋‹ค. ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ๊ธฐ์กด aws-cli๋ฅผ ์‚ญ์ œํ•˜๊ณ  ์„ค์น˜ํ•œ ์ดํ›„, ์ธ์Šคํ„ด์Šค๋ฅผ ๋‚˜๊ฐ”๋‹ค๊ฐ€ ๋‹ค์‹œ ๋“ค์–ด์™€๋ณด๋‹ˆ ์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ๋‹ค.


์ฐธ๊ณ 

aws-cli ์„ค์น˜ ๊ณต์‹๋ฌธ์„œ

# AWS CLI v1 ์—์„œ v2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•ด๋ด…์‹œ๋‹ค


# aws ์˜ ์œ„์น˜๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
$ which aws 
/usr/bin/aws

# `ls` ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ symlink๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒ€์ƒ‰
$ ls -l /usr/bin/aws 
-rwxr-xr-x 1 root root 818  9์›” 28  2020 /usr/bin/aws

# aws ์˜ ์‚ญ์ œ๋ฅผ ์œ„ํ•ด ํŒŒ์ผ์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
$ sudo rm /usr/bin/aws 
$ sudo rm /usr/bin/aws_completer 
$ sudo rm -rf /usr/local/aws-cli

# AWS CLI v2 ์„ค์น˜ ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œ
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 44.8M  100 44.8M    0     0  59.8M      0 --:--:-- --:--:-- --:--:-- 59.8M

# ํŒจํ‚ค์ง€์˜ ์••์ถ•์„ ํ’€๊ณ  ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ ์•„๋ž˜์— `aws`๋ผ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ
$ unzip awscliv2.zip
inflating: aws/dist/awscli/examples/ec2/delete-key-pair.rst
inflating: aws/dist/awscli/examples/ec2/authorize-client-vpn-ingress.rst
inflating: aws/dist/awscli/examples/ec2/delete-network-interface.rst
...

# ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰
$ sudo ./aws/install
You can now run: /usr/local/bin/aws --version

# ์„ค์น˜ ์œ„์น˜๋ฅผ ์ง€์ •
$ ./aws/install -i /usr/local/aws-cli -b /usr/local/bin
Found preexisting AWS CLI installation: /usr/local/aws-cli/v2/current. Please rerun install script with --update flag.

Dockerize

[Front] React Dockerize

[์˜ค๋ฅ˜] npm Err

[์›์ธ]

์ง€์†์ ์œผ๋กœ yarn install/build ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์—์„œ npm Err๊ฐ€ ๋‚ฌ๋‹ค. ์ด์œ ๋Š” node alpine ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์˜€๋Š”๋ฐ, alpine์€ ๊ฒฝ๋Ÿ‰ํ™” ๋œ ์ด๋ฏธ์ง€๋ผ์„œ python ๋“ฑ์ด ์„ค์น˜๋˜์–ด ์žˆ์ง€ ์•Š๊ณ , ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“ˆ์˜ ์„ค์น˜๊ฐ€ ์‹คํŒจ๋˜์—ˆ๋‹ค.

[ํ•ด๊ฒฐ]

์šฐ์„  FROM node:16 ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฏธ์ง€๊ฐ€ ๊ฑฐ๋Œ€ํ•ด์ ธ์„œ ์ข‹์€ ํ•ด๊ฒฐ๋ฐฉ์‹์€ ์•„๋‹Œ ๊ฒƒ ๊ฐ™๋‹ค. ์ถ”ํ›„์— ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„ ๋ณผ ์˜ˆ์ •์ด๋‹ค.


์ฐธ๊ณ  # [CICD / ECS] CodePipeline์œผ๋กœ ECS Fargate ๋ฐฐํฌ ์ž๋™ํ™” #1 - ๊ฐœ์š”, ์•„ํ‚คํ…์ฒ˜

# ใ€Œ์ดˆ์‹ฌ์žใ€๋ฌด์ž‘์ • ํ•ด๋ณด๋Š” ECS ์ปจํ…Œ์ด๋„ˆ ์„œ๋น„์Šค ๊ตฌ์ถ• โ€“ 1 (ECR์— ์ด๋ฏธ์ง€ ํ‘ธ์‹œ)

p.s ์ถ”ํ›„์— ์ด ๋ธ”๋กœ๊ทธ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ vpc/subnet ๋‚ด์šฉ ์ •๋ฆฌํ•˜๊ธฐ

https://www.44bits.io/ko/post/getting-started-with-ecs-fargate

AWS ECS fargate๋ฅผ ์‚ฌ์šฉํ•œ Appilication ๋ฐฐํฌ - 1


ECR


์ฐธ๊ณ 

AWS-ECR ์‚ฌ์šฉํ•˜๊ธฐ


  • ECR ๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ƒ์„ฑ ECR ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ privateํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

[์˜ค๋ฅ˜] github actions -> push -> EOF ์—๋Ÿฌ

[์›์ธ]

github actions๋ฅผ ํ†ตํ•ด ECR์— ๋กœ๊ทธ์ธํ•˜์—ฌ ECR๋กœ ํ‘ธ์‹œํ•˜๋Š” yaml ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜์˜€๋Š”๋ฐ, ๊ณ„์† ์›์ธ๋ชจ๋ฅผ EOF ์—๋Ÿฌ๊ฐ€ ๋‚ฌ๋‹ค. ์›์ธ์€ permissions๋ฅผ ์„ค์ •ํ•ด์ฃผ์ง€ ์•Š์•„์„œ ์˜€๋‹ค.

[ํ•ด๊ฒฐ]

๋ ˆํฌ์ง€ํ† ๋ฆฌ์˜ permissions์— ๋“ค์–ด๊ฐ€์„œ ์ •์ฑ…์„ ํŽธ์ง‘ํ•˜์—ฌ, deployํ•ด์•ผํ•˜๋Š” IAM ๊ณ„์ •์— push ๊ถŒํ•œ์„ ๋ถ€์—ฌํ–ˆ๋‹ค.

์ •์ฑ…
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPushAndPull",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::[์ˆซ์ž]:user/[IAM User๋ช…]"
        ]
      },
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchGetImage",
        "ecr:CompleteLayerUpload",
        "ecr:GetDownloadUrlForLayer",
        "ecr:InitiateLayerUpload",
        "ecr:PutImage",
        "ecr:UploadLayerPart"
      ]
    }
  ]
}

  • ์ด๋ฏธ์ง€๋ฅผ pull ๋ฐ›๊ธฐ ์œ„ํ•ด AmazonEC2ContainerRegistryFullAccess ๊ถŒํ•œ ๊ฐ€์ง€๋Š” IAM ์‚ฌ์šฉ์ž ์ƒ์„ฑ

  • aws configure ํ•ด์„œ ํ•ด๋‹น ์•ก์„ธ์Šคํ‚ค, ๋น„๋ฐ€๋ฒˆํ˜ธ, ๋ฆฌ์ „ ์ž…๋ ฅ

  • aws ecr get-login --no-include-email --region ap-northeast-2 ํ•˜๋ฉด ๋กœ๊ทธ์ธ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช…๋ น์–ด๋“ค ์ถœ๋ ฅ ์ด๊ฑธ ๋ณต์‚ฌํ•ด์„œ ๋กœ๊ทธ์ธ ํ•˜๋ฉด ๋œ๋‹ค.

๊ฒฝ๊ฑดํ•˜๊ฒŒ ๋ฐฑ์—”๋“œ ๋„์ปคํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

์ž๊พธ ๋„์ปคํŒŒ์ผ ์ƒ์„ฑ์—์„œ hibernate ์—๋Ÿฌ๋‚˜์„œ ์›์ธ ์ฐพ์œผ๋ ค ํ•˜๋‚˜ํ•˜๋‚˜ ๊ณผ์ •์„ ์ ์–ด๋ณด๋ ค ํ•œ๋‹ค.

  • ์šฐ๋ถ„ํˆฌ jdk8 ์ด๋ฏธ์ง€ ๋ฐ›์•„์˜ค๊ณ  ์ปจํ…Œ์ด๋„ˆํ™”

$ docker pull rtfpessoa/ubuntu-jdk8

$ docker run -t -d --name my_temp rtfpessoa/ubuntu-jdk8:latest


[์˜ค๋ฅ˜] ERROR: JAVA_HOME is set to an invalid directory

[์›์ธ]

๋„์ปคํŒŒ์ผ ์ƒ์„ฑ์„ ์œ„ํ•ด ์šฐ๋ถ„ํˆฌ์—์„œ ์ง์ ‘ ๋นŒ๋“œํ•ด๋ณด๋ ค๋Š” ์™€์ค‘์— ๋‚จ.

[ํ•ด๊ฒฐ]

$ readlink -f $(which java)

๊ฒฝ๋กœ

$ export JAVA_HOME="์œ„ ๊ฒฝ๋กœ์—์„œ bin/java ๋บ€ ๊ฒฝ๋กœ"

  • ํ™•์ธ

$JAVA_HOME/bin/java -version

ํ–ˆ์„ ๋•Œ ์ž˜ ๋‚˜์˜ค๋ฉด ๋จ.


  • appication.yaml์— DB ์ฃผ์†Œ ๋„ฃ๊ณ 

  • ๊ทธ๋ž˜๋“ค ๋นŒ๋“œ

$ chmod +x gradlew

$ ./gradlew build

[์˜ค๋ฅ˜] test FAILED

[์›์ธ]

> Task :test FAILED                                                                                                                                                                                                                                               FAILURE: Build failed with an exception.                                                                                                                                                                                                                          * What went wrong:                                                                                                               Execution failed for task ':test'.                                                                                               > There were failing tests. See the report at: file:///home/izero/uplus.com/backend/project/build/reports/tests/test/index.html                                                                                                                                   * Try:                                                                                                                           > Run with --stacktrace option to get the stack trace.                                                                           > Run with --info or --debug option to get more log output.                                                                      > Run with --scan to get full insights.                                                                                                                                                                                                                           * Get more help at https://help.gradle.org                                                                                                                                                                                                                        BUILD FAILED in 15m 5s 

test์—์„œ ๊ณ„์† ์—๋Ÿฌ๊ฐ€๋‚ฌ๋‹ค.

[ํ•ด๊ฒฐ] ํ…Œ์ŠคํŠธ ์ œ์™ธํ•˜๊ณ  ๋นŒ๋“œ

๊ณ„์† ๋‚˜๋ฅผ ๊ดด๋กญํ˜”๋˜ ์›์ธ..!

ํ…Œ์ŠคํŠธ ์ œ์™ธํ•˜๊ณ  ๋นŒ๋“œํ•˜๋ฉด ๋œ๋‹ค!

$ ./gradlew build -x test


  • ls๋กœ build ์† jar ํŒŒ์ผ์ด๋ฆ„ ํ™•์ธ

$ /build/libs# ls

project-0.0.1-SNAPSHOT.jar

$ java -jar build/libs/project-0.0.1-SNAPSHOT.jar

. ____ _ __ _ _ /\ / ' __ _ () __ __ _ \ \ \ \ ( ( )__ | '_ | '| | ' / ` | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ' || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.7.3) 2022-08-30 11:31:43.102 INFO 23088 --- [ main] com.lguplus.project.ProjectApplication : Starting ProjectApplication using Java 1.8.0_342 on ip-172-31-34-43 with PID 23088 (/home/ubuntu/uplus.com/backend/project/build/libs/project-0.0.1-SNAPSHOT.jar started by root in /home/ubuntu/uplus.com/backend/project) 2022-08-30 11:31:43.108 INFO 23088 --- [ main] com.lguplus.project.ProjectApplication : No active profile set, falling back to 1 default profile: "default"

๊ทผ๋ฐ ์ค‘๋‹จ์ด ๋œ๋‹ค...

  • jar ํŒŒ์ผ my_temp ์ปจํ…Œ์ด๋„ˆ์˜ home ๊ฒฝ๋กœ์— ๋ณต์‚ฌ

$ docker cp *.jar my_temp:home/

  • my_temp bash๋กœ ๋“ค์–ด๊ฐ€์„œ ๊ฒฝ๋กœ ํ™•์ธ

$ docker exec -it my_temp bash

$ /home# ls

project-0.0.1-SNAPSHOT.jar

$ /home# java -jar *.jar

๊ทธ๋ž˜๋„ ๊ณ„์† ๋‚˜๋Š” ์—๋Ÿฌ...

#11 75.45 Starting a Gradle Daemon (subsequent builds will be faster)                                                                            #11 171.6                                                                                                                                        #11 171.8 FAILURE: Build failed with an exception.                                                                                               #11 171.9                                                                                                                                        #11 171.9 * What went wrong:                                                                                                                     #11 171.9 Task 'test' not found in root project 'project'.                                                                                       #11 171.9                                                                                                                                        #11 171.9 * Try:                                                                                                                                 #11 171.9 > Run gradlew tasks to get a list of available tasks. 

  • ๋„์ปคํŒŒ์ผ ๋งŒ๋“ค์–ด๋ด„
FROM adoptopenjdk/openjdk8:ubi AS builder
WORKDIR /home/ec2-user/app
COPY gradlew .                                                                                    =
COPY gradle gradle                                                               
COPY settings.gradle .                                  
COPY src src                     
RUN chmod +x ./gradlew                           
RUN ./gradlew build bootJar             

FROM adoptopenjdk/openjdk8:ubi                                              
COPY --from=builder . .
#EXPOSE 8080
#ENTRYPOINT ["java", "-jar", "app.jar"]     

FROM openjdk:8-jdk-alpine AS builder
WORKDIR /home/ec2-user/app
COPY gradlew .
COPY gradle gradle                                                               
COPY settings.gradle .                                  
COPY src src                     
RUN chmod +x ./gradlew                           
RUN ./gradlew build bootJar             

FROM openjdk:8-jdk-alpine                                             
COPY --from=builder build/libs /builds
#EXPOSE 8080
#ENTRYPOINT ["java", "-jar", "app.jar"]     

์—ฌ๊ธฐ๊นŒ์ง€ ํ–ˆ์„ ๋•Œ ๊ฒฝ๋กœ๊ฐ€ /home/ec2-user/app/build/libs # ls project-0.0.1-SNAPSHOT.jar

  • ์ˆ˜์ •ํ•ด์„œ ์„ฑ๊ณตํ•œ ๋„์ปคํŒŒ์ผ
FROM openjdk:8-jdk-alpine AS builder
WORKDIR /home/ec2-user/app
COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
RUN chmod +x ./gradlew
RUN ./gradlew build -x test

FROM openjdk:8-jdk-alpine
COPY --from=builder /home/ec2-user/app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]     

swagger ์ž˜ ๋‚˜์˜ด.

Nginx+React+Spring Docker fargate์— ๋ฐฐํฌ


์ฐธ๊ณ 

https://velog.io/@nayoon-kim/Spring-boot-nginx-docker-ec2-%EC%9E%90%EB%B0%94%EC%84%A4%EC%B9%98%EB%B6%80%ED%84%B0

[AWS] Spring Boot, Nginx, Docker ๋กœ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌํ•˜๊ธฐ - 1ํƒ„

[AWS ECS] ECR ์ด๋ฏธ์ง€๋ฅผ ECS๋กœ ์‹คํ–‰ (์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ๋ฐ ๊ด€๋ฆฌ)


  • ์ž‘์—… ์ •์˜ ์ƒ์„ฑ

  • BE์šฉ ALB ์ƒ์„ฑ

Fargate vs EC2

๊ฐœ์š”

- ECS with EC2
AWS ์ž์ฒด ๊ฐœ๋ฐœํ•˜์—ฌ ์ œ๊ณตํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ ์„œ๋น„์Šค
    - ์žฅ์ 
    EC2 ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ full ์ปจํŠธ๋กค, GPU ์ธ์Šคํ„ด์Šค ํ™œ์šฉ ์šฉ์ด
    spot ์ธ์Šคํ„ด์Šค ํ™œ์šฉ ๊ฐ€๋Šฅํ•˜์—ฌ ์ตœ๋Œ€ 90% ๋น„์šฉ ์ ˆ๊ฐ
    - ๋‹จ์ 
    EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผํ•จ, ์˜ˆ: ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ ํŒจ์น˜ ๋“ฑ์„ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ด์•ผํ•จ

- ECS with Fargate
AWS ๋žŒ๋‹ค์™€ EC2 ๊ธฐ๋ฐ˜์˜ ECS ์ปจํ…Œ์ด๋„ˆ์˜ ์ค‘๊ฐ„ ์ฏค์— ์œ„์น˜ํ•œ ์„œ๋น„์Šค, EC2 ์ธ์Šคํ„ด์Šค ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ๊ฑฑ์ • ์•ˆํ•ด๋„ ๋จ
    - ์žฅ์ 
    ๊ด€๋ฆฌํ•  ์„œ๋ฒ„๊ฐ€ ์—†์Œ
    CPU/Memory ์„ ํƒ์ด ์ƒ๋Œ€์ ์œผ๋กœ ์ข€ ๋” ์šฉ์ดํ•จ
    Fargate Spot์„ ์‚ฌ์šฉํ•˜์—ฌ 70% ์ ˆ๊ฐ๋œ ๊ธˆ์•ก์œผ๋กœ ์ด์šฉํ•  ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ
    - ๋‹จ์ 
    awsvpc๋กœ ๋„คํŠธ์›Œํฌ ์˜ต์…˜์ด ์ œํ•œ๋จ
  • ๋น„์šฉ ์ธก๋ฉด

    ec2๋Š” t3.micro ์‹œ๊ฐ„๋‹น 0.013 USD 2vcpu 1GiBt2.micro ์‹œ๊ฐ„๋‹น 0.0144 USD 1vcpu 1GiB

    fargate๋Š” vcpu๋‹น 0.04656 USDGB๋‹น 0.00511 USD

    fargate๋ฅผ 1vcpu 1GB ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ทธ ํ•ฉ์ด 0.05167 USD ์ •๋„๊ฐ€ ๋˜๋Š”๋ฐ

    ๋™๊ธ‰์˜ ec2์— ๋น„ํ•ด์„œ ๋งŽ๊ฒŒ๋Š” 3๋ฐฐ๊นŒ์ง€ ๋น„์šฉ์ด ๋น„์‹ธ๋‹ค.

๊ฒฐ๋ก 

๋น„๋ก ๋น„์šฉ์  ์ธก๋ฉด์—์„œ ํฐ ๋‹จ์ ์ด ์žˆ์ง€๋งŒ, ์ด์ „์— EC2๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๊ตฌ์ถ•ํ•ด๋ดค๊ธฐ์— ์ง์ ‘ ๊ด€๋ฆฌํ•  ๋ถ€๋ถ„์ด ์ ๋‹ค๋Š” ์žฅ์ ์„ ๊ฐ€์ง„ fargate๋ฅผ ๊ฒฝํ—˜ ๋ฐ ํ•™์Šต์  ์ธก๋ฉด์—์„œ ์„ ํƒํ•ด ๋ดค๋‹ค. fargate๋ฅผ ์„ ํƒํ•˜๋ ค๊ณ  ํ–ˆ์œผ๋‚˜ statelessํ•œ fargate์—์„œ๋Š” ์นดํ”„์นด ๊ตฌ์„ฑ์ด ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค๊ณ  ํ•˜์—ฌ์„œ, ๋น„์šฉ์ธก๋ฉด์—์„œ ์ €๋ ดํ•œ EC2๋ฅผ ์„ ํƒํ–ˆ๋‹ค.

๋ฐฐํฌ ์ „๋žต ์„ ํƒ

ECS์—์„œ ์ž์ฒด์ ์œผ๋กœ 2๊ฐ€์ง€์˜ ๋ฐฐํฌ์ „๋žต์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

1. ๋กค๋ง ( Rolling )

  • ์ธ์Šคํ„ด์Šค๋ฅผ ์ •ํ•ด๋†“์€ ๋‹จ์œ„๋กœ ๊ต์ฒดํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์ด ์„œ๋ฒ„์˜ ๋Œ“์ˆ˜๊ฐ€ 10๋Œ€๋ผ๋ฉด 2๋Œ€์”ฉ ๋กค๋ง ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ฒ ๋‹ค๊ณ  ํ•œ๋‹ค๋ฉด, ์ƒˆ๋กœ์šด ๋ฒ„์ „ 2๋Œ€๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ์ •์ƒ์ ์œผ๋กœ ์„œ๋น„์Šค๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์ผ๋•Œ ํŠธ๋ž˜ํ”ฝ์ด 2๋Œ€๋กœ ์ „ํ™˜๋˜๊ณ , ๊ตฌ๋ฒ„์ „ 2๋Œ€๋Š” ์„œ๋น„์Šค์—์„œ ์ œ์™ธํ•œ๋‹ค. ( draining ) ์ด ๋ฐฉ์‹์œผ๋กœ 2๋Œ€์”ฉ ์ˆœ์ฐจ์ ์œผ๋กœ ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ ๊ต์ฒด๋˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ์ด ๋ฐฉ์‹์€ ๊ฐ€์šฉ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ ์€ ์ƒํƒœ์—์„œ๋„ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ์—…๋ฐ์ดํŠธ ํ”„๋กœ์„ธ์Šค ๋™์•ˆ ๋‘ ๊ฐ€์ง€ ๋ฒ„์ „์˜ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ„์ „ ํ˜ธํ™˜์„ฑ์˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

2. ๋ธ”๋ฃจ ๊ทธ๋ฆฐ ( Blue/Green )

  • ๊ตฌ ๋ฒ„์ „์„ ๋ธ”๋ฃจ ์‹  ๋ฒ„์ „์„ ๊ทธ๋ฆฐ์ด๋ผ๊ณ  ํ•ด์„œ ๋ถ™์—ฌ์ง„ ์ด๋ฆ„์œผ๋กœ, ๋™์ผํ•œ ์„œ๋ฒ„๋ฅผ ๋ฏธ๋ฆฌ ๊ตฌ์ถ•ํ•œ๋’ค์— ๋ผ์šฐํŒ…์„ ์ˆœ๊ฐ„์ ์œผ๋กœ ์ „ํ™˜ํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๋น ๋ฅธ ๋กค๋ฐฑ์ด ๊ฐ€๋Šฅํ•œ ์žฅ์ ์ด ์žˆ๊ณ , ์šด์˜ ํ™˜๊ฒฝ์„ ์œ ์ง€ํ•œ์ฑ„๋กœ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌ๋  ๋ฒ„์ „์˜ ํ…Œ์ŠคํŠธ๋„ ๊ฐ€๋Šฅํ•œ ๊ตฌ์„ฑ์ด ๋˜๋‚˜, ์ž์›์ด ๋‘๋ฐฐ๋กœ ํ•„์š”ํ•˜๊ฒŒ ๋˜์–ด ๋น„์šฉ์ด ๋งŽ์ด ๋ฐฉ์ƒํ•˜๋Š” ๋‹จ์  ๋„ ์กด์žฌํ•œ๋‹ค.

Fargate SSH ์ ‘์†


์ฐธ๊ณ 

# 9 steps to SSH into an AWS Fargate managed container


ALB ์ƒ์„ฑ ๋ฐ Fargate๋กœ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ํ”„๋ก ํŠธ์—”๋“œ ํ™•์ธํ•ด๋ณด๊ธฐ


์ฐธ๊ณ 

https://dev.classmethod.jp/articles/lets-make-aws-fargate-first/


Private RDS ํ„ฐ๋„๋ง ๋ฐฉ๋ฒ•


์ฐธ๊ณ 

https://velog.io/@ynsoo1225/AWS-Bastion-Host%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-IntelliJ%EC%97%90%EC%84%9C-Private-Subnet-RDS-%EC%97%B0%EA%B2%B0


ECS ํด๋Ÿฌ์Šคํ„ฐ ์ƒ์„ฑ๊ณผ์ •

๋ฆฌ๋ˆ…์Šค ์œ ์šฉํ•œ ๋ช…๋ น์–ด

- ์ „์ฒด ํด๋”์—์„œ ํด๋” ์ฐพ๊ธฐ
find / -name ํด๋”๋ช… -type d

์š”์•ฝ

๊ณ„์ • ๊ธฐ์ดˆ๋ณด์•ˆ

๋ฃจํŠธ๊ณ„์ •์˜ Access Key๋ฅผ ํƒˆ์ทจํ•˜์—ฌ ๊ณ ๋น„์šฉ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ณ ๋น„์šฉ์˜ ์„œ๋น„์Šค๋ฅผ ์‹ ์ฒญํ•˜์—ฌ ๋‹จ๊ธฐ๊ฐ„์— ์ˆ˜๋งŒ ๋‹ฌ๋Ÿฌ์— ๋‹ฌํ•˜๋Š” ๋น„์šฉ์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ํ”ผํ•ด๊ฐ€ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ, ๊ผญ ์ถ”๊ฐ€ ๋ณด์•ˆ ์„ค์ •์ด ํ•„์š”ํ•จ.

  1. ๋ฃจํŠธ ์‚ฌ์šฉ์ž MFA ์„ค์ •

AWS ์ฝ˜์†”์— ๋กœ๊ทธ์ธํ•  ๋•Œ ์•„์ด๋””/ํŒจ์Šค์›Œ๋“œ ์™ธ ์ถ”๊ฐ€ ์ธ์ฆ์„ ๋”ํ•˜๋Š” ๊ฒƒ

  1. ๋ฃจํŠธ ์‚ฌ์šฉ์ž Access Key ์‚ญ์ œ

์•ก์„ธ์Šค ํ‚ค๋Š” ์•„์ด๋””/ํŒจ์Šค์›Œ๋“œ ์ธ์ฆ๊ณผ ๋™์ผํ•œ ๊ถŒํ•œ์„ ๊ฐ–์ง€๋งŒ AWS CLI, PowerShell์šฉ ๋„๊ตฌ, AWS SDK ๋˜๋Š” ์ง์ ‘ AWS API ํ˜ธ์ถœ์„ ํ†ตํ•ด AWS๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ์กด์žฌ์ด์ง€๋งŒ ์•ก์„ธ์Šค ํ‚ค๊ฐ€ ๋ฃจํŠธ ์‚ฌ์šฉ์ž ๊ถŒํ•œ์œผ๋กœ ์‚ฌ์šฉ๋  ๋•Œ๋Š” ๊ทธ ์šฉ๋„ ๋งŒํผ์ด๋‚˜ ๋งค์šฐ ๊ฐ•๋ ฅํ•ด์„œ ํƒˆ์ทจ๋˜์–ด ์•…์šฉํ•˜๋ฉด ๊ด‘๋ฒ”์œ„ํ•œ ์˜์—ญ์—์„œ ํ”ผํ•ด๋ฅผ ์ค„ ์ˆ˜ ์žˆ์Œ. ๋”ฐ๋ผ์„œ, ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์—†๋‹ค๋ฉด ๋ฃจํŠธ ์‚ฌ์šฉ์ž ๊ณ„์ •์—๋Š” ๋ฐ˜๋“œ์‹œ ์•ก์„ธ์Šค ํ‚ค๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์–ด์•ผ ํ•จ.

  1. IAM ์‚ฌ์šฉ์ž ์„ค์ •

admin ์‚ฌ์šฉ์ž๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด AdministratorAccess ๊ถŒํ•œ๋งŒ ๋ถ€์—ฌ. ์ด๋ ‡๊ฒŒ ๋นŒ๋ง ๊ถŒํ•œ์„ ๊ฐ€์ง€์ง€ ์•Š์€ ์‚ฌ์šฉ์ž๋ฅผ ์ด์šฉํ•˜์—ฌ, AWS ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์•ก์„ธ์Šค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ œ์–ด.

์›น ์„œ๋ฒ„ - Nginx

Apache์™€ nginx ์ค‘ HTML ํŒŒ์ผ ์‚ฌ์ด์ฆˆ, ์–ด๋–ค ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ์“ฐ๋Š๋ƒ ๋“ฑ ๋‹ค์–‘ํ•œ ์กฐ๊ฑด ๋•Œ๋ฌธ์— ๋ฌด์—‡์ด ๋” ๋ฌด์กฐ๊ฑด ์„ฑ๋Šฅ์ด ์ข‹๋‹ค๊ณ ๋Š” ์ด์•ผ๊ธฐ ํ•  ์ˆ˜ ์—†์Œ. ํ•˜์ง€๋งŒ ๋งŽ์€ ์ ‘์†์ž๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ์‹œ์Šคํ…œ ์ž์› ๊ด€๋ฆฌ ํšจ์œจ์„ฑ ๋•Œ๋ฌธ์— Nginx๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์„ฑ๋Šฅ์ด ๋” ์ข‹์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Nginx๋ฅผ ์‚ฌ์šฉํ•จ.

๋ฐฐํฌ ์ „๋žต

๋„์ปค

์ถ”ํ›„ ์„œ๋ฒ„ ๊ด€๋ฆฌ์˜ ํŽธ์˜๋ฅผ ์œ„ํ•ด ๋„์ปค๋ผ์ด์ง• ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•จ.

Clone this wiki locally