CI CD (aws terraform) - prgrms-web-devcourse-final-project/WEB4_5_ServerSOS_BE GitHub Wiki

CI/CD ๊ตฌ์ถ•

ํ”„๋ก ํŠธ์— ๊ณ ๋ คํ•  ์‚ฌํ•ญ

  • cors โ†’ cors ์— ํ”„๋ก ํŠธurl์ถ”๊ฐ€
  • cookie โ†’ samesite strict x
  • redirectUrl (์†Œ์…œ๋กœ๊ทธ์ธ)

ํฐ ํ๋ฆ„

  1. aws ์„œ๋ฒ„ ๊ตฌ์ถ•(ํ…Œ๋ผํผ)
  2. docker๋ฅผ ํ†ตํ•ด ์ปจํ…Œ์ด๋„ˆํ™”

AWS ๊ตฌ์กฐ์™€ ํ…Œ๋ผํผ ์ดํ•ดํ•˜๊ธฐ

๊ตฌ์กฐ

์ „์ฒด์ ์ธ ๊ตฌ์กฐ : ๋ฆฌ์ „ > ๊ฐ€์šฉ ์˜์–ต(AZ) > VPC > ์„œ๋ธŒ๋„ท > EC2

๋ฆฌ์ „ : ์ „ ์„ธ๊ณ„์˜ ๋ถ„์‚ฐ๋œ AWS ์„ผํ„ฐ ๋ฌถ์Œ

๊ฐ€์šฉ ์˜์—ญ : ๋ฆฌ์ „ ์•ˆ์˜ ๋ฐ์ดํ„ฐ ์„ผํ„ฐ๋“ค

VPC : ๋ฆฌ์ „ ์•ˆ์—์„œ ๋งŒ๋“œ๋Š” ๋„คํŠธ์›Œํฌ ๊ณต๊ฐ„

ํ…Œ๋ผํผ ์˜ˆ์ œ์™€ ๋น„์œ ๋กœ ์ดํ•ดํ•˜๊ธฐ

  • ๋น„์œ 

    • VPC : ํ•˜๋‚˜์˜ ๋งˆ์„
    • ์„œ๋ธŒ๋„ท : ๋งˆ์„ ์•ˆ์˜ ํ•˜๋‚˜์˜ ๊ตฌ์—ญ
    • IGW(์ธํ„ฐ๋„ท ๊ฒŒ์ดํŠธ์›จ์ด) : ์™ธ๋ถ€์™€ ๋งˆ์„์„ ์—ฐ๊ฒฐ ํ•ด์ฃผ๋Š” ์ž…์ถœ๊ตฌ
    • ๋ผ์šฐํŒ… ํ…Œ์ด๋ธ”(rt) : ๊ธธ ์•ˆ๋‚ด ํ‘œ์ง€ํŒ
    • ์„ธํ๋ฆฌํ‹ฐ ๊ทธ๋ฃน(sg) : ๋ˆ„๊ตฌ๋ฅผ ๋“ค์–ด๊ณ ๊ฒŒ ํ• ์ง€ ์ •ํ•˜๋Š” ๋ณด์•ˆ๋ฌธ
    • EC2 : ์ง‘ (ํ•˜๋‚˜์˜ ์„œ๋ธŒ๋„ท์— ์†ํ•œ๋‹ค) - ์„œ๋ฒ„
    • iam role : ๊ถŒํ•œ ์ฆ๋ช…์„œ
    • user_data : ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์„ธํŒ…
  • ์ฝ”๋“œ ํ๋ฆ„ ์„ค๋ช…

    • ๊ธฐ์ดˆ ๊ณต์‚ฌ
      • VPC
        • IP์˜ ๋ฒ”์œ„๋ฅผ 10.0.0.0/16 ์œผ๋กœ ์ •ํ•จ
      • Subnet
        • VPC IP์˜ ๋ฒ”์œ„ ์•ˆ์— IP ์„ค์ •
        • ๊ฐ€์šฉ ์˜์—ญ ์„ค์ •
        • ํ˜„์žฌ 4๊ฐœ์˜ ๊ตฌ์—ญ์„ ๋งŒ๋“ค์—ˆ์Œ
      • IG
        • ์™ธ๋ถ€๋กœ ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋Š” ํ†ต๋กœ ๋งŒ๋“ฌ
      • ๋ผ์šฐํŒ… ํ…Œ์ด๋ธ”
        • 0.0.0.0/0(๋ชจ๋“  IP) ๋Š” IG๋กœ ๋‚˜๊ฐ€๊ฒŒํ•จ
        • assosiation ์„ ํ†ตํ•ด 4๊ฐœ์˜ ์„œ๋ธŒ๋„ท์— ์„ค์ •ํ•ด์คŒ
      • ์‹œํ๋ฆฌํ‹ฐ ๊ทธ๋ฃน
        • ingress : ๋ชจ๋“  ๋ฐฉ๋ฌธ์— ๋Œ€ํ•ด์„œ ์—ด์–ด๋‘ 
        • egress : ๋ชจ๋“  ์™ธ์ถœ์— ๋Œ€ํ•ด์„œ ์—ด์–ด๋‘ 
    • ์ง‘ ์ง“๊ธฐ
      • EC2
        • IAM ์—ญํ•  ๋งŒ๋“ค๊ธฐ โ†’ ๊ถŒํ•œ ๋ถ€์—ฌ
        • ํ”„๋กœํ•„ ๋งŒ๋“ค๊ธฐ(EC2๊ฐ€ ๋ถ€์ฐฉํ•จ)
        • ์„ค์ •
          • ami : ์šด์˜์ฒด์ œ
          • instance_type : ์„œ๋ฒ„ ํฌ๊ธฐ
          • ์œ„์— ์„ค์ •ํ•œ ๊ธฐ์ดˆ ๊ณต์‚ฌ๋ฅผ ์„ค์ •ํ•ด์คŒ
          • user_data : ๊ธฐ์ดˆ ๋ฐ์ดํ„ฐ ๋„ฃ์–ด์คŒ
terraform {
  // aws ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ด
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}

# AWS ์„ค์ • ์‹œ์ž‘
provider "aws" {
  region = var.region
}
# AWS ์„ค์ • ๋

# VPC ์„ค์ • ์‹œ์ž‘
resource "aws_vpc" "vpc_1" {
  cidr_block = "10.0.0.0/16"

  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.prefix}-vpc-1"
  }
}

# Subnet ์„ค์ • ์‹œ์ž‘
resource "aws_subnet" "subnet_1" {
  vpc_id                  = aws_vpc.vpc_1.id
  cidr_block              = "10.0.1.0/24"
  availability_zone       = "${var.region}a"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.prefix}-subnet-1"
  }
}

# Subnet ์„ค์ • ์‹œ์ž‘
resource "aws_subnet" "subnet_2" {
  vpc_id                  = aws_vpc.vpc_1.id
  cidr_block              = "10.0.2.0/24"
  availability_zone       = "${var.region}b"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.prefix}-subnet-2"
  }
}

# Subnet ์„ค์ • ์‹œ์ž‘
resource "aws_subnet" "subnet_3" {
  vpc_id                  = aws_vpc.vpc_1.id
  cidr_block              = "10.0.3.0/24"
  availability_zone       = "${var.region}c"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.prefix}-subnet-3"
  }
}

# Subnet ์„ค์ • ์‹œ์ž‘
resource "aws_subnet" "subnet_4" {
  vpc_id                  = aws_vpc.vpc_1.id
  cidr_block              = "10.0.4.0/24"
  availability_zone       = "${var.region}d"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.prefix}-subnet-4"
  }
}

resource "aws_internet_gateway" "igw_1" {
  vpc_id = aws_vpc.vpc_1.id

  tags = {
    Name = "${var.prefix}-igw-1"
  }
}

resource "aws_route_table" "rt_1" {
  vpc_id = aws_vpc.vpc_1.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw_1.id
  }

  tags = {
    Name = "${var.prefix}-rt-1"
  }
}

resource "aws_route_table_association" "association_1" {
  subnet_id      = aws_subnet.subnet_1.id
  route_table_id = aws_route_table.rt_1.id
}

resource "aws_route_table_association" "association_2" {
  subnet_id      = aws_subnet.subnet_2.id
  route_table_id = aws_route_table.rt_1.id
}

resource "aws_route_table_association" "association_3" {
  subnet_id      = aws_subnet.subnet_3.id
  route_table_id = aws_route_table.rt_1.id
}

resource "aws_route_table_association" "association_4" {
  subnet_id      = aws_subnet.subnet_4.id
  route_table_id = aws_route_table.rt_1.id
}

resource "aws_security_group" "sg_1" {
  name = "${var.prefix}-sg-1"

  ingress {
    from_port = 0
    to_port   = 0
    protocol  = "all"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port = 0
    to_port   = 0
    protocol  = "all"
    cidr_blocks = ["0.0.0.0/0"]
  }

  vpc_id = aws_vpc.vpc_1.id

  tags = {
    Name = "${var.prefix}-sg-1"
  }
}

# EC2 ์—ญํ•  ์ƒ์„ฑ
resource "aws_iam_role" "ec2_role_1" {
  name = "${var.prefix}-ec2-role-1"

  # ์ด ์—ญํ• ์— ๋Œ€ํ•œ ์‹ ๋ขฐ ์ •์ฑ… ์„ค์ •. EC2 ์„œ๋น„์Šค๊ฐ€ ์ด ์—ญํ• ์„ ๊ฐ€์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •
  assume_role_policy = <<EOF
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "",
        "Action": "sts:AssumeRole",
        "Principal": {
            "Service": "ec2.amazonaws.com"
        },
        "Effect": "Allow"
      }
    ]
  }
  EOF
}

# EC2 ์—ญํ• ์— AmazonS3FullAccess ์ •์ฑ…์„ ๋ถ€์ฐฉ
resource "aws_iam_role_policy_attachment" "s3_full_access" {
  role       = aws_iam_role.ec2_role_1.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}

# EC2 ์—ญํ• ์— AmazonEC2RoleforSSM ์ •์ฑ…์„ ๋ถ€์ฐฉ
resource "aws_iam_role_policy_attachment" "ec2_ssm" {
  role       = aws_iam_role.ec2_role_1.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
}

# IAM ์ธ์Šคํ„ด์Šค ํ”„๋กœํŒŒ์ผ ์ƒ์„ฑ
resource "aws_iam_instance_profile" "instance_profile_1" {
  name = "${var.prefix}-instance-profile-1"
  role = aws_iam_role.ec2_role_1.name
}

locals {
  ec2_user_data_base = <<-END_OF_FILE
#!/bin/bash
# ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ 4GB ์„ค์ •
sudo dd if=/dev/zero of=/swapfile bs=128M count=32
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo sh -c 'echo "/swapfile swap swap defaults 0 0" >> /etc/fstab'

# ๋„์ปค ์„ค์น˜ ๋ฐ ์‹คํ–‰/ํ™œ์„ฑํ™”
yum install docker -y
systemctl enable docker
systemctl start docker

# ๋„์ปค ๋„คํŠธ์›Œํฌ ์ƒ์„ฑ
docker network create common

# nginx ์„ค์น˜
docker run -d \
  --name npm_1 \
  --restart unless-stopped \
  --network common \
  -p 80:80 \
  -p 443:443 \
  -p 81:81 \
  -e TZ=Asia/Seoul \
  -v /dockerProjects/npm_1/volumes/data:/data \
  -v /dockerProjects/npm_1/volumes/etc/letsencrypt:/etc/letsencrypt \
  jc21/nginx-proxy-manager:latest

# redis ์„ค์น˜
docker run -d \
  --name=redis_1 \
  --restart unless-stopped \
  --network common \
  -p 6379:6379 \
  -e TZ=Asia/Seoul \
  redis --requirepass ${var.password_1}

# mysql ์„ค์น˜
docker run -d \
  --name mysql_1 \
  --restart unless-stopped \
  -v /dockerProjects/mysql_1/volumes/var/lib/mysql:/var/lib/mysql \
  -v /dockerProjects/mysql_1/volumes/etc/mysql/conf.d:/etc/mysql/conf.d \
  --network common \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=${var.password_1} \
  -e TZ=Asia/Seoul \
  mysql:latest

# MySQL ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
echo "MySQL์ด ๊ธฐ๋™๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ ์ค‘..."
until docker exec mysql_1 mysql -uroot -p${var.password_1} -e "SELECT 1" &> /dev/null; do
  echo "MySQL์ด ์•„์ง ์ค€๋น„๋˜์ง€ ์•Š์Œ. 5์ดˆ ํ›„ ์žฌ์‹œ๋„..."
  sleep 5
done
echo "MySQL์ด ์ค€๋น„๋จ. ์ดˆ๊ธฐํ™” ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ์ค‘..."

docker exec mysql_1 mysql -uroot -p${var.password_1} -e "
CREATE USER 'lldjlocal'@'127.0.0.1' IDENTIFIED WITH caching_sha2_password BY '1234';
CREATE USER 'lldjlocal'@'172.18.%.%' IDENTIFIED WITH caching_sha2_password BY '1234';
CREATE USER 'lldj'@'%' IDENTIFIED WITH caching_sha2_password BY '${var.password_1}';

GRANT ALL PRIVILEGES ON *.* TO 'lldjlocal'@'127.0.0.1';
GRANT ALL PRIVILEGES ON *.* TO 'lldjlocal'@'172.18.%.%';
GRANT ALL PRIVILEGES ON *.* TO 'lldj'@'%';

CREATE DATABASE glog_prod;

FLUSH PRIVILEGES;
"

echo "${var.github_access_token_1}" | docker login ghcr.io -u ${var.github_access_token_1_owner} --password-stdin

END_OF_FILE
}

data "aws_ami" "latest_amazon_linux" {
  most_recent = true
  owners = ["amazon"]

  filter {
    name = "name"
    values = ["al2023-ami-2023.*-x86_64"]
  }

  filter {
    name = "architecture"
    values = ["x86_64"]
  }

  filter {
    name = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name = "root-device-type"
    values = ["ebs"]
  }
}

# EC2 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
resource "aws_instance" "ec2_1" {
  # ์‚ฌ์šฉํ•  AMI ID
  ami = data.aws_ami.latest_amazon_linux.id
  # EC2 ์ธ์Šคํ„ด์Šค ์œ ํ˜•
  instance_type = "t3.micro"
  # ์‚ฌ์šฉํ•  ์„œ๋ธŒ๋„ท ID
  subnet_id = aws_subnet.subnet_2.id
  # ์ ์šฉํ•  ๋ณด์•ˆ ๊ทธ๋ฃน ID
  vpc_security_group_ids = [aws_security_group.sg_1.id]
  # ํผ๋ธ”๋ฆญ IP ์—ฐ๊ฒฐ ์„ค์ •
  associate_public_ip_address = true

  # ์ธ์Šคํ„ด์Šค์— IAM ์—ญํ•  ์—ฐ๊ฒฐ
  iam_instance_profile = aws_iam_instance_profile.instance_profile_1.name

  # ์ธ์Šคํ„ด์Šค์— ํƒœ๊ทธ ์„ค์ •
  tags = {
    Name = "${var.prefix}-ec2-1"
  }

  # ๋ฃจํŠธ ๋ณผ๋ฅจ ์„ค์ •
  root_block_device {
    volume_type = "gp3"
    volume_size = 12 # ๋ณผ๋ฅจ ํฌ๊ธฐ๋ฅผ 12GB๋กœ ์„ค์ •
  }

  user_data = <<-EOF
${local.ec2_user_data_base}
EOF
}