AWS - S3, 클라우드 프론트, Jenkins, ReverseProxy

Tadap·2023년 7월 30일
0

AWS 학교

목록 보기
5/5
post-thumbnail

S3

S3 버킷이라고 하는데 데이터 저장소이다.

resource "aws_s3_bucket" "bucket_1" {
  bucket = "${var.prefix}-bucket-${var.nickname}-1"

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

data "aws_iam_policy_document" "bucket_1_policy_1_statement" {
  statement {
    sid    = "PublicReadGetObject"
    effect = "Allow"

    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.bucket_1.arn}/*"]
  }
}

resource "aws_s3_bucket_policy" "bucket_1_policy_1" {
  bucket = aws_s3_bucket.bucket_1.id

  policy = data.aws_iam_policy_document.bucket_1_policy_1_statement.json

  depends_on = [aws_s3_bucket_public_access_block.bucket_1_public_access_block_1]
}

resource "aws_s3_bucket_public_access_block" "bucket_1_public_access_block_1" {
  bucket = aws_s3_bucket.bucket_1.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket" "bucket_2" {
  bucket = "${var.prefix}-bucket-${var.nickname}-2"

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

버킷을 만들고, 이후에 IAM정책과 버킷에 대한 정책을 만든다.

클라우드 프론트

클라우드 프론트란 정적인 파일(정적 웹 페이지, 이미지 등등)은 바뀌지 않는다.
이때, 내가 글로벌 서비스를 하고있다면.한국에서 모든 이미지같은 정적 데이터를 가져오는것 보다 미국은 미국에서, 유럽은 유럽에서 이미지를 가져오는게 더 효율적일 것이다.
AWS는 리전마다 클라우드 프론트가 있고 내가 적용을 하면 전세계 저장소로 이미지가 퍼진다. 이를 이용하여 이미지를 가져올 수 있다.

도메인 구매

도메인과 ROUTE53을 이용하여 서비스 실행도 해 보았다.
강사님은 iwantmynames를 이용하였다.
나는 GoogleDomain을 이용하여 구매하였다.
이후 맞춤 네임서버에서 AWS로 넘기는 작업을 통해 아마존에서 관리 가능하게 했다.
그러면 이제 해당 도메인은 AWS가 사용하고 이를 AWS에 올린 서비스와 연결이 가능해진다.
이렇게 구매하고나면(구매한 도메인이 a.com 일때, test.a.com 등 하위 주소는 다 사용가능하다.

이후 이를 이용하여 퍼블릭 IP 를 연결하였다

퍼블릭 IP 연결

# ec2-1 에 public 도메인 연결
resource "aws_route53_record" "domain_1_ec2_1" {
  zone_id = var.domain_1_zone_id
  name    = "ec2-1.${var.domain_1}"
  type    = "A"
  ttl     = "300"
  records = [aws_instance.ec2_1.public_ip]
}

${var.domain_1} 은 내가 구매한 도메인 주소이다. 앞에 ec2-1을 붙여 임시 서비스를 할 것이다.
밑에 records 에 EC2인스턴스의 공공IP를 입력하였다.

생성된 모습이다.

Jenkins

업로드 전에, Jenkins를 이용한다.
이 아저씨는 CI/CD를 해준다.
소스코드를 Main에 PR을 통해 Merge를 하면 GitHub의 WebHook을 이용해서 Jenkins서버에 알려준다. 그러면 Jenkins아저씨가 코드를 가져와 테스트를 하고, 문제가 없으면 빌드한 코드로 컨테이너를 만든다(Docker) 이후 실행중인 컨테이너와 현재 새로 만든 컨테이너를 교체한다.

먼저 EC2에 Git, Docker등 필요한 프로그램들을 설치한다.
주요프그램이 Docker, NginxProxyManager 그리고 Jenkis가 있다.

Jenkins 설치 이후 Pipeline을 만들어 준다

깃헙에서 webhook도 사용할 것이기 때문에 이 트리거 옵션도 켜준다

pipeline {
    agent any
    
    environment {
        timestamp = "${System.currentTimeMillis() / 1000L}"
    }
    
    stages {
        stage('Prepare') {
            steps {
                script {
                    // Get the ID of the dlike:latest image
                    def oldImageId = sh(script: "docker images dlike:latest -q", returnStdout: true).trim()
                    env.oldImageId = oldImageId
                }
            
                git branch: 'main',
                    url: 'https://github.com/tadaHP/JenkinsTest'
            }
            
            post {
                success { 
                    sh 'echo "Successfully Cloned Repository"'
                }
                failure {
                    sh 'echo "Fail Cloned Repository"'
                }
            }  
        }
    
        stage('Build Gradle') {
            
            steps {
                
                dir('.') {
                    sh '''
                    cat <<EOF > src/main/resources/application-secret.yml
spring:
  security:
    oauth2:
      client:
        registration:
          kakao:
            clientId: 카카오_REST_API_KEY
custom:
  site:
    baseUrl: https://여러분의_도메인
EOF
                    '''
                }
            
                dir('.') {
                    sh """
                    chmod +x gradlew
                    """
                }
                
                dir('.') {
                    sh """
                    ./gradlew clean build
                    """
                }
            }
            
            post {
                success { 
                    sh 'echo "Successfully Build Gradle Test"'
                }
                 failure {
                    sh 'echo "Fail Build Gradle Test"'
                }
            }    
        }
        
        stage('Build Docker Image') {
            steps {
                script {
                    sh "docker build -t dlike:${timestamp} ."
                }
            }
        }

        stage('Run Docker Container') {
            steps {
                script {
                    // Check if the container is already running
                    def isRunning = sh(script: "docker ps -q -f name=dlike_1", returnStdout: true).trim()

                    if (isRunning) {
                        sh "docker rm -f dlike_1"
                    }
                    
                    // Run the new container
                    try {
                        sh """
                        docker run \
                          --name=dlike_1 \
                          -p 8080:8080 \
                          -v /docker_projects/dlike_1/volumes/gen:/gen \
                          --restart unless-stopped \
                          -e TZ=Asia/Seoul \
                          -d \
                          dlike:${timestamp}
                        """
                    } catch (Exception e) {
                        // If the container failed to run, remove it and the image
                        isRunning = sh(script: "docker ps -q -f name=dlike_1", returnStdout: true).trim()
                        
                        if (isRunning) {
                            sh "docker rm -f dlike_1"
                        }
                        
                        def imageExists = sh(script: "docker images -q dlike:${timestamp}", returnStdout: true).trim()
                        
                        if (imageExists) {
                            sh "docker rmi dlike:${timestamp}"
                        }
                        
                        error("Failed to run the Docker container.")
                    }

                    // If there's an existing 'latest' image, remove it
                    def latestExists = sh(script: "docker images -q dlike:latest", returnStdout: true).trim()
                    
                    if (latestExists) {
                        sh "docker rmi dlike:latest"
                        
                        if(!oldImageId.isEmpty()) {
                        	sh "docker rmi ${oldImageId}"
                        }
                    }

                    // Tag the new image as 'latest'
                    sh "docker tag dlike:${env.timestamp} dlike:latest"
                }
            }
        }
    }
}

파이프 라인 코드이다. 추후에 Jenkins Script 작성법은 정리해야 겠다.
지금 중요한건 git branch, 그리고 프로그램에 설정파일 형태로 들어가는 카카오 RestApiKey이다.

바탕화면에서 지금 빌드 버튼을 누르면 git에서 파일을 가져와서, 테스트를 돌리고, 이후 jar로 압축한 파일을 도커파일로 만들어서 올린다.
이후에 깃헙에서 아래처럼 웹 훅을 넣어주면 끝이다.

ReverseProxy

여러 기능이 있는데 하나의 서버에 여러가지 서비스를 올릴수 있게한다. 이렇게 하면 여러 도메인으로 요청이 오면 이를 분석하여 특정 서비스로 분산시켜준다(현재 목표) 이 외에도 SSL, 로드 밸런싱 등 여러가지가 가능하다.

여기선 호스트 이름기반 가상 프록싱을 한다.

여러 주소지만 목적지는 하나의 서버에 다른 서비스이다. 이렇게 하면 jenkins, NginxProxyManager, 그리고 웹서비스를 서비스 할 수 있다.
추가적으로 SSL 적용이다. Let'sEncrypt라는 무료 서비스이다

DynamoDB

NoSQL로 아마존에서 제공하는 NoSQL이다. Key-Value, Document, Column Family, Graph 등을 지원하며 고속으로 읽고 쓰기가 가능하나 특정 조인 쿼리의경우에는 오히려 느릴수도 있다.

post 같은 경우는 id로만 되어있다.
chatMessage의 경우는 크게 chatRoomId로 분리 이후 다시 createDate로 정렬되어있다.
이렇게 하면 특정 파티션(여기선 roomId)끼리 모여있어서 찾는데 효율이 더 좋다.

0개의 댓글

관련 채용 정보