1. 동네홍보

내가 팔고자 하는 물건의 컨셉을 명확히 하고 블로그에 적듯이 쉽고 짧게 글을 적어보세요.

쿠폰을 지급하고 유입을 유도하는 용도입니다.

이 때, SEDA 법칙을 지킵니다.

Shortly, 글을 짧게 쓴다.
Easily, 글을 쉽게 쓴다.
Divide, 문단을 나눈다.
Again, 다시 읽어라.

 

2. 가게소식

가게상품에서 팔고자하는 상품으로 후기랑 단골 쌓아야 합니다.

 

3. 광고

당근에서는 현재 광고 말고는 팔 수 있는 방법이 없습니다.

광고 탭을 누르고 - 홈피드 누릅니다!

지역을 선택합니다.

예산을 셋팅하고 기간을 선택해서 광고를 돌립니다.

반응형

https://stackoverflow.com/questions/26929297/html-5-input-type-date-control-max-date-is-not-working-in-iphone-ipad

 

Html 5 [input type=Date] control, MAX date is not working in iPhone/Ipad

Greetings. Working with html date input control. input type="date" max="2014-13-11" In chrome its recognizing 'max'attribute hence restricting and disabling all future date But, the same is not

stackoverflow.com

input type=Date로 최대 일자와 최소 일자를 적용했다.

웹에서는 잘 적용이 되었지만 모바일로 하면 적용이 안되는 문제를 발견했다.

결론적으로 iphone과 ipad에서 지원이 안되는 문제가 있었고, Pickaday 라이브러리를 사용해서 해결했다.

 

html

<!-- Pikaday Library -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/pikaday/css/pikaday.css">
<script src="https://cdn.jsdelivr.net/npm/pikaday/pikaday.js"></script>

<input type="text" id="datepicker" style="float:right; text-align:center; width:120px">

javascript

var today = new Date();
var lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
var picker = new Pikaday({
    field: document.getElementById('datepicker'),
    onSelect: date => {
      const year = date.getFullYear()
           ,month = date.getMonth() + 1
           ,day = date.getDate()
           ,formattedDate = [
              year
             ,month < 10 ? '0' + month : month
             ,day < 10 ? '0' + day : day
            ].join('-')
      document.getElementById('datepicker').value = formattedDate
    },
    minDate: today,
    maxDate: lastDay
});

위의 코드에서 onSelect: date 는 date format을 yyyy-mm-dd로 표현하기 위함이다.

반응형

2023.08.07 - [웹개발] - [docker] fastapi + nginx 블루 그린 배포(무중단배포)[2]

 

[docker] fastapi + nginx 블루 그린 배포(무중단배포)[2]

2023.08.07 - [웹개발] - [docker] fastapi + nginx 블루 그린 배포(무중단배포)[1] 이전편에 이어서 github actions에서 workflow 코드를 공유하겠다. 이전 nginx 설정편을 보고 싶다면 위의 링크를 참고 부탁드린다.

tjjourney7.tistory.com

이전 글에 이어서 deploy관련 sh 스크립트와 docker compose 설정파일에 대해서 설명하겠다.

우선 들어가기 앞서 docker와 docker compose가 설치되어 있지 않다면 배포 대상서버에 설치하길 바란다.

#!/bin/bash
IS_GREEN=$(docker ps | grep green) # 현재 실행중인 App이 blue인지 확인합니다.

if [ -z $IS_GREEN  ];then # blue라면

  echo "### BLUE => GREEN ###"

  echo "1. get green image"
  docker compose pull green # green으로 이미지를 내려받습니다.

  echo "2. green container up"
  docker compose up -d green # green 컨테이너 실행

  while [ 1 = 1 ]; do
    echo "3. green health check..."
    sleep 3

    REQUEST=$(curl http://127.0.0.1:8080) # green으로 request
      if [ -n "$REQUEST" ]; then # 서비스 가능하면 health check 중지
        echo "health check success"
        break ;
      fi
  done;

  echo "4. reload nginx"
  sudo cp -p /etc/nginx/nginx-green.conf /etc/nginx/nginx.conf || exit 1
  sudo sudo systemctl restart nginx || exit 1

  if [ "$(docker ps -q -f name=blue)" ]; then
    echo "5. blue container down"
    docker compose stop blue
  fi
  
else
  echo "### GREEN => BLUE ###"

  echo "1. get blue image"
  docker compose pull blue

  echo "2. blue container up"
  docker compose up -d blue

  while [ 1 = 1 ]; do
    echo "3. blue health check..."
    sleep 3
    REQUEST=$(curl http://127.0.0.1:8081) # blue로 request

    if [ -n "$REQUEST" ]; then # 서비스 가능하면 health check 중지
      echo "health check success"
      break ;
    fi

  done;

  echo "4. reload nginx"
  sudo cp -p /etc/nginx/nginx-blue.conf /etc/nginx/nginx.conf || exit 1
  sudo sudo systemctl restart nginx || exit 1

  if [ "$(docker ps -q -f name=green)" ]; then
    echo "5. green container down"
    docker compose stop green
  fi

fi

위의 로직을 설명하면 이렇다.

1) green 컨테이너가 실행되고 있다면 blue 컨테이너를 실행

2) nginx port를 blue로 바라보게 copy

3) green 컨테이너를 다운시킨다.

4) blue 컨테이너가 실행되고 있다면 위의 로직을 반대로 설정

 

이렇게 하면 사용자는 nginx가 restart되는 아주 찰나의 시점을 제외하고는 무중단으로 홈페이지를 사용할 수 있게 된다.

위의 스크립트에서 nginx 없이 아래와 같이 시도해보았지만 중단 시간이 5초 이상으로 생각보다 길었다.
1) green 컨테이너가 실행되고 있으면 green 컨테이너를 다운

2) blue 컨테이너 실행

3) blue 컨테이너가 실행하고 있으면 blue 컨테이너 다운

4) green 컨테이너 실행

 

따라서 더 짧은 중단시간을 위해 nginx가 필요한 것이다.

이를 위해 docker compose 파일(docker-compose.yml)은 아래와 같다.

version: '3.3'
services:
  green:
    container_name: green
    image: jaypark/image
    ports:
      - "8080:8080"

  blue:
    container_name: blue
    image: jaypark/image
    ports:
      - "8081:8080"

위 코드에서도 짧지만 정말 많은 시행착오를 겪었다.

1. 우선 version: '3.3' 부분은 본인이 설치한 docker compose 버전에 호환되게 넣어줘야 한다. 그렇지 않으면 에러가 발생한다.

2. 줄간격 맞추는 부분도 정확하게 해야지 에러가 발생하지 않는다.

3. image: jaypark/image 부분은 본인의 docker hub repository 명을 넣어야 한다. 그렇지 않으면 에러가 발생한다.

4. ports: 부분은 8081:8080으로 설명하자면 docker container의 외부 port는 8080이며, 내부적으로 사용되는 호스트포트는 8081이란 의미이고, 이 둘을 연결한다고 보면 된다.

지난번 포스트에서 nginx를 80 port로 listen하고 green, blue에 맞게 호스트 port를 각각 8080, 8081로 연결하기 때문에 외부 요청이 블루 그린배포시 다른 포트로 들어올 수 있다. 이 부분이 정말 헷갈렸다...

 

반응형

2023.08.07 - [웹개발] - [docker] fastapi + nginx 블루 그린 배포(무중단배포)[1]

이전편에 이어서 github actions에서 workflow 코드를 공유하겠다.

이전 nginx 설정편을 보고 싶다면 위의 링크를 참고 부탁드린다.

 

github actions workflow를 위한 파일(.yml)

github 화면

위의 github 화면에서 New workflow를 누르면 여러 Template이 나오는데 docker image를 선택했다.

이후 아래처럼 코드를 변경했다.

name: Docker Image CI

on:
  push:
    branches: [ "main" ]

jobs:
  build: 
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v3
    
    # Docker 이미지 Build (1)        
    - name: docker image build
      run:
        docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPONAME }} .
    
    # DockerHub Login (2)
    - name: docker login 
      uses: docker/login-action@v2
      with: 
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    # Docker Hub push (3)
    - name: docker Hub push
      run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPONAME }}
    
  # 배포
  deploy:
    needs: build # build 후에 실행되도록 정의
    name: Deploy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Deliver File
        uses: appleboy/scp-action@v0.1.4
        with:
          host: ${{ secrets.EC2_REMOTE_IP }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          port: 22
          source: "bluegreen/deploy_blue_green.sh,bluegreen/docker-compose.yml"
          target: "/home/ec2-user/"
      # ec2 접속
      - name: Application Run
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_REMOTE_IP }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          port: 22
          script: |
            cd /home/ec2-user/bluegreen
            docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPONAME }}
            chmod 777 ./deploy_blue_green.sh
            chmod 777 ./docker-compose.yml
            ./deploy_blue_green.sh
            docker image prune -f
            docker system prune --volumes -f

정말 위 코드를 삽질의 삽질을 거듭하며 만들었다. 정말 너무 힘들었다....

한 단계 한 단계 설명을 붙이도록 하겠다.

 

github에 코드에 push가 발생하면 아래와 같이 동작한다.

빌드단계

1) docker build를 수행한다.

2) docker hub에 로그인한다.(private저장소라서 로그인을 수행해야 한다. private으로 만들지 않았다면 괜찮다.)

3) docker hub 에 private repository에 push한다.

배포단계

4) 빌드단계가 끝나고(needs: build 코드를 위에서 확인할 수 있다) 배포단계를 수행한다.
    ec2에 scp로 deploy_blue_green.sh, docker-compose.yml파일을 /home/ec2-user/bluegreen 경로에 전송한다.

5)ssh로 ec2에 접속한 후 빌드시 수행했던 방식으로 docker hub에 로그인한다.

6)docker pull을 통해서 빌드시 docker hub에 올려놨던 image를 받는다.

7)미리 전송해놓았던 deploy_blue_green.sh을 실행한다.

8)쓸모없는 용량을 없애기위해 image와 캐시들을 삭제한다.

 

위에서 ${{secrets.블라블라}} 는 보안을 위해 github에서 설정하는 부분이니 구글링에 수많은 자료가 검색되니 참고 부탁드린다.

 

다음시간에는 deploy_blue_green.sh, docker-compose.yml 설정을 다뤄보도록 하겠다.

반응형

웹 시스템을 구축 후 운영하다보니 코드 수정 부분을 자주 운영이관해야 했다.

그러다보니 배포시 무중단에 대한 니즈가 발생했고, 일주일간 꼬박 삽질했다...

이 글을 보는 사람들은 시간을 단축하길 바라며 자료를 남긴다.

 

일단 결론이 된 코드부터 공유하겠다.

1. nginx 설정

nginx가 배포할 대상 서버에 설치되어 있지 않다면 설치해주시길 바란다.

blue 배포시 nginx-blue.conf --> nginx.conf 로 덮어쓰고 nginx 서비스를 재기동하고,

green 배포시 nginx-green.conf --> nginx.conf로 덮어쓰고 nginx서비스를 재기동한다.

서버에 미리 nginx-blue.conf, nginx-green.conf 파일을 만들거나 github actions에서 /etc/nginx/ 경로에 2가지 파일을 배포한다.

/etc/nginx.conf 파일의 모습

아래에서 server localhost:8081 부분만 server localhost:8080로만 스위칭된다고 생각하면 된다.

# /etc/nginx/nginx.conf
# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    upstream serverurl {
        server localhost:8081; # green blue인 경우 8080
    }

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    sendfile            on;
    tcp_nopush          on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;


        location / {
            include /etc/nginx/uwsgi_params;
            proxy_pass http://serverurl;
        }
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }
}

 

2. 블루 그린 배포에서 nginx의 역할

위의 경우 80포트로 들어오는 요청을 blue인 경우 localhost:8081로 보내주고, green인 경우 localhost:8080으로 보내준다.

한 마디로 여기서는 무중단배포를 위한 연결다리 역할로만 사용한다.

 

다음 편에서는 github actions의 workflow 설정을 다뤄보겠다.(아래 링크 참조)

2023.08.07 - [웹개발] - [docker] fastapi + nginx 블루 그린 배포(무중단배포)[2]

 

반응형

<돈 버는 규칙은 이미 바뀌었다>

우선 글을 작성하기 전에 다음 링크를 삽입한다. 필자는 이 채널과 전혀 무관한 평범하기 그지없는 사람이다.

https://youtu.be/uOwNhYCckLU

해당 내용은 고등학생이 웬만한 대기업 직장인의 월급 이상을 번다는 내용이다. 안본 개발자가 있다면 꼭 시청하길 바란다.

이렇게 알 수 있듯이 시대가 이미 변했다. 돈 버는 규칙이 바뀌었고, 이미 직장인, 대기업, 공기업 등 모든 부모님들이 바라는 안정적인 직업의 개념은 사라졌다.

 

<가장 유리한 포지션 : 개발자>

현 시대에서 가장 유리한 사람들이 개발자이다. 대부분의 사람들이 인터넷으로 나름의 플랫폼을 꿈꾼다. 하지만 대부분 실패한다. 그 이유가 플랫폼 개발은 투자를 받아서 어떻게든 개발하는데, 이후 운영하는 노하우가 없기 때문이다. 운영하면서 또 막대한 자본이 들어가고 결국 적자로 해당 사업이 실패한다.

개발자는 어떻게 다를까?

1. 직접 화면과 기능을 설계해서 구현할 수 있다.

2. 인프라를 구축해 해당 웹 소스를 배포할 수 있다.

3. 웹 기능을 운영하며, 중간 중간 필요한 부분을 수정하고, 에러에 대응할 수 있다.

 

<막연한 두려움>

AWS에 소스 올리면 해킹 당하는거 아니야?
AWS에서 해킹당하면 돈 폭탄 나와서 망하는거 아니야?
나 개발은 많이 했는데 클라우드, docker 이런거 하나도 모르는데?

 

실제 필자가 스스로에게 말했던 이야기이다. 이렇게 유리한 포지션임에도 위와 같은 막연한 두려움을 갖고 아이디어는 있지만 실제 행동에 옮기지 못한다. 우리가 10여년간 받은 주입식 교육 때문에 실행에 옮기는 것이 익숙하지 않을수도 있다. 이 두려움만 깨버릴 수 있다면 로우 리스크로 하이 리턴의 비지니스를 직접 운영할 수 있다.

필자의 경우 3대 SI 기업에서 4~5년간 개발을 진행했고, 마지막 1년은 개발과 PM업무를 동시에 진행했다. 이후 막연한 두려움을 깨버리고자 아무것도 모른체 클라우드 쪽으로 부서를 옮겼다. 처음엔 엄청난 도전이었다. 아무것도 모르니 잘할 수 있을까? 지금까지 한게 개발인데 적응할 수 있을까? 등등 또 부정적인 질문들이 온 몸을 감쌌다.

하지만 지금은 도전하지 않았으면 큰일날뻔 했다고 생각한다. 직장에 다니면서도 웹 비지니스를 구축했다. 리스크는 아예 없었다.

현재는 AWS, Docker, fastapi, github, github action 등을 이용하여 웹 비지니스를 운영하고 있다.

개발도 직접하고, 개발 소스를 github action, aws codepipeline을 통해 CI/CD하고, 운영자로서도 역할을 수행하고 있다.

나같은 겁쟁이가 단 한번의 용기로 이를 이루어낸 것이다.

 

<리스크는 없다>

나같은 겁쟁이 비전공자도 해냈다. 아무 리스크없이 월 100~200만원씩 들어오는 비지니스를 만들었다. 유일한 투자는 바로 나의 시간이다. 운영 비용도 1년에 10만원 아래이다.

혹시 이 글을 본 사람들이 있다면 그들을 위해서 개발부터 운영까지 모든 것을 담아서 글만 따라서 하기만 하면 바로 내가 구현한 웹의 전부를 따라할 수 있도록 글을 써보려고 한다. 관심있으신 분들이 있으면 댓글에 메일을 남겨주시길 바란다.

고민만하고 있는 개발자의 비지니스를 위해서!

 

 

반응형

Spring boot project 경로

1. cmd 실행해서 디렉토리 접근한다. (dir 명령어로 어떤 파일이 있는지 확인 가능)

2. gradlew build 입력

3. build 완료 후 build/libs 경로 들어감. 이후 java -jar (build된 .jar 파일명) 입력

4. Spring 프로젝트가 서버에서 실행되는 것을 확인할 수 있다. Spring boot 참 편리하다. ㄷㄷ

반응형

+ Recent posts