2020년 11월 25일 수요일

[AWS] Php를 이용한 S3 이미지 업로드와 CloudFront에 추가

[ 순서 ]

Preparation : php설치, AWS CloudFront 사용 중

  • S3 사용을 위해 AWS-SDK-PHP 설치

  • 보안자격증명 key, secret 얻기

  • S3이미지 업로드 Php코드작성

  • S3버킷만들기

  • CloudFront에 S3 연동




[ AWS-SDK-PHP 설치 ]

1 2 3 4 5 6 7 8 9 10 11 php composer 설치 #php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" #php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" #php composer-setup.php #php -r "unlink('composer-setup.php');" composer 전역설정 # sudo cp composer.phar /usr/bin/composer aws-sdk-php설치 #sudo composer require aws/aws-sdk-php

 


[ 보안자격증명 key, secret 얻기 ]

우측상단 계정클릭 → 내보안자격증명 → 액세스키 생성하여 key, secret 포함된 CSV파일 다운












[ S3버킷 설정 ]

1. AWS S3탭에 들어가 버킷생성

2.버킷을 퍼블릭으로 설정

3.해당버킷대쉬보드 → 권한 → 버킷정책

{
"Version": "2020-12-04",
"Statement": [
{
"Sid": "AddPerm",
"Effect": "Allow",
"Principal": "",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::yourBucketName/"
}
]
}


[ S3이미지 업로드 PHP code ]

Post로 File업로드시 필요한 파람터 값을 받는다. S3에 대한 리전, key, secret, 버킷이름 등의 정보를 입력하고, 앞서 다운받았던 aws-sdk-php API를 이용해 업로드 코드를 작성한다.

예제 )

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #Php to aws S3 require '/PATHtoAWSSDK/aws-sdk/vendor/autoload.php'; use Aws\S3\S3Client; use Aws\Exception\AwsException; use Aws\Credentials\CredentialProvider; #Bucket 연결 객체 $s3Client = new S3Client([ 'region' => 'ap-northeast-1', 'version' => 'latest', # 자격증명 'credentials' => [ 'key' => 'your_key', 'secret' => 'your_secret', ], ]); #File Upload to S3 $fp = fopen($upload_file, r); $result = $s3Client->putObject([ 'Bucket'=>'upload-image-file', 'Key' => $_FILES['file1']['name'], 'Body' => $fp ]); fclose($fp);

참고 : https://docs.aws.amazon.com/ko_kr/sdk-for-php/v3/developer-guide/getting-started_basic-usage.html



[ CloudFront에 S3추가 ]

AWS CloudFront → Create Distribution → Web섹션 Get Started → Origin Domain Name에 만든 S3 Name으로 입력한다.














나머지 설정은 읽어보면서 입력하고 Distribution Settings에 Price Class입력란을 보면 Class 마다 제공되는 Location과 가격이 다르다.














예를 들어 내 서비스가 아프리카국가의 유저들에게는 지원을 하지 않는다면 요금계층200을 선택해 요금을 더 지불하지 않도록 한다.


또한 모든 Location에 CloudFront를 지원하지 않기에 아래 그림을 참조하여 가장 가까운 Edge Location을 찾아 시스템라인을 구성해야 할 것이다.

예를 들어 중국하얼빈 유저들에게 내 웹/앱 서비스를 할 경우 CloudFront Location은 거리상으로 베이징보다 서울이 가깝다고 해보자. 그럼 중국이 포함된 Price Class를 사용하는게 아닌 한국이 포함된 Class를 써야 가장 최적의 네트워크 속도를 가질 것이다.



마지막으로 SSL Certificate된 사용할 도메인을 입력한다.



[ CloudFront Behaviors ]

cloudfront → 변경할 Distributions → Behaviors 탭 → Create Behavior 버튼 → 생성후 저장 Pattern은 * 로 작성




CloudFront 참조 : https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/Introduction.html

https://www.slideshare.net/lacryma1/53-aws-summit-seoul-2015

https://docs.aws.amazon.com/ko_kr/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesPathPattern



RedHat7&Centos7 APM(apache/Php/mysql) 설치

[ 버전정보 ]

  • Httpd 2.4
  • Php 7.3
  • mariaDB 5.5


[ EC2에 APM설치 ]

  • httpd2.4 설치

#sudo yum install httpd

#httpd -v
Server version: Apache/2.4.6 (Red Hat Enterprise Linux)

 

  • Apache httpd vhost 설정

#sudo vi /etc/httpd/conf/httpd.conf

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Listen 8080 (추가) ..... (VirtualHost 추가) <VirtualHost *:80> ServerName servername:80 ServerAdmin root@example.com DocumentRoot "/var/www/html/server1" CustomLog "logs/access_log" combined ErrorLog "logs/error_log" <Directory "/var/www/html/server1"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> </VirtualHost> <VirtualHost *:8080> ServerName servername:8080 ServerAdmin root@example.com DocumentRoot "/var/www/html/server2" CustomLog "logs/access_log" combined ErrorLog "logs/error_log" <Directory "/var/www/html/server2"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> </VirtualHost> .....

#sudo systemctl restart httpd

 

  • php73 설치

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 remi repository를 yum 에 추가 한다. # wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm # rpm -Uvh epel-release-latest-7.noarch.rpm # wget http://rpms.remirepo.net/enterprise/remi-release-7.rpm # rpm -Uvh remi-release-7.rpm # yum install -y yum-utils # yum-config-manager --enable remi-php73 기존의 설치된 PHP 패키지를 확인하여 잘못된 패키지가 삭제되지 않도록 한다. # yum list installed | cut -d " " -f 1 | grep php 기존 설치된 PHP를 제거. (php5) #yum remove -y `yum list installed | cut -d " " -f 1 | grep php` php 패키지 설치. php-common 외의 패키지는 자신의 상황에 맞게 조정해서 설치한다. # yum install -y php-common php-fpm php-cli \ php-process \ php-opcache php-pecl-apcu \ php-mysqlnd php-pdo \ php-gd \ php-mbstring php-xml \ php-pecl-zip \ php-bcmath #php -v PHP 7.3.34

 

  • mariadb 설치

#yum install mariadb mariadb-server 

#mysql --version
mysql Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1

 

  • APM설정파일 경로

/etc/php.ini

/var/lib/php/

/etc/httpd/conf.d/php.conf

/etc/httpd/conf/httpd.conf

 

  • 명령어

-실행

sudo systemctl start httpd

sudo systemctl start mariadb

-확인

sudo systemctl status httpd

sudo systemctl status mariadb



[Algorithm] 힙 우선순위큐 [Heap Priority Queue] (더 맵게)


문제 ]

매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다. 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은 두 개의 음식을 아래와 같이 특별한 방법으로 섞어 새로운 음식을 만듭니다.

섞은 음식의 스코빌 지수 =  가장 맵지 않은 음식의 스코빌 지수 + (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)

Leo가 가진 음식의 스코빌 지수를 담은 배열 scoville과 원하는 스코빌 지수 K가 주어질 때, 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 섞어야 하는 최소 횟수를 return 하도록 solution 함수를 작성해주세요.
제한 사항

  • scoville의 길이는 2 이상 1,000,000 이하입니다.
  • K는 0 이상 1,000,000,000 이하입니다.
  • scoville의 원소는 각각 0 이상 1,000,000 이하입니다.
  • 모든 음식의 스코빌 지수를 K 이상으로 만들 수 없는 경우에는 -1을 return 합니다.


입력

scovile = [1,2,3,9,10,12]
K = 7
출력
return 2

입출력 예 설명
  1. 스코빌 지수가 1인 음식과 2인 음식을 섞으면 음식의 스코빌 지수가 아래와 같이 됩니다. 새로운 음식의 스코빌 지수 = 1 + (2 * 2) = 5
    가진 음식의 스코빌 지수 = [5, 3, 9, 10, 12]

  2. 스코빌 지수가 3인 음식과 5인 음식을 섞으면 음식의 스코빌 지수가 아래와 같이 됩니다. 새로운 음식의 스코빌 지수 = 3 + (5 * 2) = 13
    가진 음식의 스코빌 지수 = [13, 9, 10, 12]

모든 음식의 스코빌 지수가 7 이상이 되었고 이때 섞은 횟수는 2회입니다. Leo는 모든 음식의 스코빌 지수가 K 이상이 될 때까지 반복하여 섞습니다.



제출 1 ] 

class Solution {
    public int solution(int[] scoville, int K) {
        int answer = 0;
        int result = 0;
        if(K==0) return 0;
        Arrays.sort(scoville);
        for(int i=0;i<scoville.length;i++){
            System.out.println(Arrays.toString(scoville));
            if(scoville[i]==0) return -1;
            if(scoville[i]<K){
                result = scoville[i]+(scoville[i+1]*2);
                if(result > scoville[i+2]) {
                    scoville[i+1]=scoville[i+2];
                    scoville[i+2]=result;    
                }
                answer++;
            } else {
                break;
            }
        }
        System.out.println(answer);
        return answer;
    }
}
몇 몇 테스트에서 타임아웃과 효율성테스트 실패.
결국 힙을 써야될 것같은데 자바의 Priority queue자료형쓰라는 문제였다.


[ Priority queue ]
큐의 구조를 가지면서 우선순위가 높은 엘리먼트가 먼저 poll되는 자료구조이다. 우선순위는 최대힙(내림차순)과 최소힙(오름차순)이 있으며 힙영역 메모리를 사용한다.












이진트리로 되어있으면 최대힙이면 root노드에 가장 큰 값, 
최소힙이면 root노드에 가장 작은 값이 들어간다.

기본적으로 import java.util.PriorityQueue; 를 임포트하고
PriorityQueue<제네릭> pq = new PriorityQueue<>(); 변수를 선언하여 사용한다. 

큐에 값을 추가하는 함수는
  • pq.add() / pq.offer()
큐에 값을 삭제하는 함수
  • pq.poll() // 첫번째 값(루트노드)을 반환하고 제거 비어있다면 null
  • pq.remove() // 첫번째 값 제거
  • pq.clear() // 우선순위 큐 초기화
출력 함수
  • pq.peek() // 우선순위 큐 첫번째 값 출력
  • pq.size() // number of elements present in the PriorityQueue


제출 2 ] 
import java.util.PriorityQueue;
class Solution {
public int solution(int[] scoville, int K) {
            PriorityQueue<Integer> pq = new PriorityQueue<>();
    int answer = 0;
        
            for (int i=0; i < scoville.length; i++){
                pq.add(scoville[i]);
            }

            while(pq.peek() < K){
                if (pq.size() < 2) return -1;
                int a = pq.poll();
        int b = pq.poll();
        pq.add(a + 2 * b);
        answer++;
            }
        return answer;
}
}



우선순위 큐 참조 : 
https://coding-factory.tistory.com/603



2020년 11월 22일 일요일

[Algorithm] 스택&큐 ( 주식가격 )


문제 ]

초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때,

가격이 떨어지지 않은 기간은 몇 초인지를 return하시오.


- 입출력 예

prices : [ 1, 2, 3, 2, 3]

return : [ 4, 3, 1, 1, 0]




제출 ]

선택정렬 비스무리한 느낌이나서 이중 for문으로 해결하면 되지 않을까?

하는 생각으로 stack을 사용하지 않고 제출


class Solution {

    public int[] solution(int[] prices) {

        int len = prices.length;

        int[] answer = new int[len];


        for(int i=0; i<len; i++){

            for(int j=i+1; j<len; j++){

                answer[i]++;

                if (prices[i] > prices[j]){

                    break;

                }

            }

        }

        return answer;

    }

}



제출 2 ]

Stack을 이용한 다른분의 풀이


import java.util.Stack;

class Solution {

    public int[] solution(int[] prices) {

        Stack<Integer> beginIdxs = new Stack<>();

        int i=0;

        int[] terms = new int[prices.length];


        beginIdxs.push(i);

        for (i=1; i<prices.length; i++) {

            while (!beginIdxs.empty() && prices[i] < prices[beginIdxs.peek()]) {

                int beginIdx = beginIdxs.pop();

                terms[beginIdx] = i - beginIdx;

            }

            beginIdxs.push(i);

        }

        while (!beginIdxs.empty()) {

            int beginIdx = beginIdxs.pop();

            terms[beginIdx] = i - beginIdx - 1;

        }

        return terms;

    }

}


- Int형 제네릭 스택 선언, 스택함수로는

stack.push(obj) : 가장 탑에 데이터 추가

stack.pop() : 최근에 추가된 데이터 삭제

stack.peek() : 최근에 추가된 데이터 조회

stack.empty() : stack값이 비었는지 확인, 비면 True 아니면 False


 

2020년 11월 16일 월요일

Apache Kafka - 재시작시 meta.propertie 에러


에러메세지 ]

[2020-11-16 06:12:51,050] ERROR Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)

kafka.common.InconsistentClusterIdException: The Cluster ID lTeGi4hnRaKFOHhVMQnfEg doesn't match stored clusterId Some(eaVbu41vRfSi-XzD7DxMog) in meta.properties. The broker is trying to join the wrong cluster. Configured zookeeper.connect may be wrong.

        at kafka.server.KafkaServer.startup(KafkaServer.scala:223)

        at kafka.server.KafkaServerStartable.startup(KafkaServerStartable.scala:44)

        at kafka.Kafka$.main(Kafka.scala:82)

        at kafka.Kafka.main(Kafka.scala)



원인 ]

해당파일을 찾아 열어보면

cat /mnt/data/kafka-logs/meta.propertie

    cluster.id=lTeGi4hnOodaKFOHhVMQnfEg

    version=0

    broker.id=1

와 같이 되어있는데,

정상적이지 않은 종료, 재시작으로 broker.id에 해당하는 cluster.id가 맞지 않아 생긴다.



해결 ]

해당 파일을 지우고 카프카 재시작한다.

sudo rm /mnt/data/kafka-logs/meta.propertie



[Algorithm] 해시맵 ( 완주하지 못한 선수 )

문제 ]

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.


제한사항

  • 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
  • completion의 길이는 participant의 길이보다 1 작습니다.
  • 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
  • 참가자 중에는 동명이인이 있을 수 있습니다.



제출 ]

import java.util.Arrays;

class Solution {

    public String solution(String[] participant, String[] completion) {

        String answer = "";

        Arrays.sort(participant);

        Arrays.sort(completion);

        int i;

        for(i=0; i < completion.length; i++){

            if(!participant[i].equals(completion[i])){

                return participant[i];

            }

        }

        return participant[i];

    }

}



풀이 ]











사진출처 :https://medium.com/@nsh235482


Arrays.sort함수를 이용하여 두배열을 정렬하면 두가지의 경우가 생긴다.

1. 위 그림처럼 맨뒤에 완주하지 못한 사람이 남는 경우

2. 배열 처음~중간에 두 배열 값이 일치하지 않는 경우


(2)의 경우 for문을 통해 처리했고 (1)의 경우에는 for문이 끝난 후 i 값이 맨 마지막 배열의 인자를 가리키게되므로 마지막 return코드로 처리한다.


처음에는 int i를 for문에 (i=0; i < completion.length; i++) 처럼 선언하고 

마지막 리턴문을 retrun participant[participant.length -1] 로 작성했는데 위의 제출코드처럼 수정한 것이 더 깔끔해보인다.

문제를 처음 보고 해시맵문제인데 해시를 안써도 되겠단 생각이 들었지만, 문제 제출 후 다른 분들이 해시를 이용한 코드를 참고할 수 밖에 없었다.



제출 2 ]

import java.util.HashMap;

class Solution{

    public String solution(String[] participant, String[] completion) {

        String answer = "";

        HashMap<String, Integer> hm = new HashMap<>();

        for (String player : participant) hm.put(player, hm.getOrDefault(player, 0) +1);

        for (String player : completion) hm.put(player, hm.get(player) -1);

        

        for (String key : hm.keySet(0) {

           if (hm.get(key) != 0){

               answer = key;

            }

        }

        return answer;

    }

}



풀이 2 ]

participant배열 값들은 모두 1의 값을 가지는 맵을 정의한다.

ex ) 존 : 1, 잭: 1, 에밀리 : 1......

동명이인일 때는 +1을 한번더 하여 "존 : 2" 가 되도록한다. 


그 뒤 completion배열과 map의 키값을 비교하여 각 value -1을 한다.

ex ) 존 : 1, 잭 0, 에밀리 : 0 ....

결국 1이 남는 key값이 완주하지 못한 선수이다.


* for ( key value : map.keySet() ) : map의 iterator기능을 하는 for 문

* map.getOrDefault ( key, default value) : 찾는 키가 존재한다면 그 키값을 반환하고 없다면 기본값을 반환한다.

* HashMap 함수

  • put()
  • putAll()
  • get()
  • remove()
  • clear()
  • isEmpty()
  • keySet()
  • values()
  • containsKey()
  • containsValue()
  • replace()