▶︎ 3Tier
보통 엔터프라이즈 웹 애플리케이션은 세 가지 종류의 서버로 구성된다.
- 프론트엔드 서비스를 제공하는 웹 서버
 - 비즈니스 로직을 수행하는 웹 애플리케이션(WAS) 서버
 - 데이터를 저장하는 데이터베이스 서버
 
이 세 가지 종류의 서버가 유기적으로 상호작용하며 하나의 애플리케이션으로 구성되는 것을
3Tier 아키텍처라고 부른다.
하지만 위 아키텍처의 경우 웹 애플리케이션 서버가 클라이언트에게 그대로 노출되어 있기에 보안상 위험이 뒤따른다.
그래서 nginx의 프록시 기능을 활용해서 백엔드 애플리케이션으로의 접근을 제한해야 한다.
아래와 같이 변경된 구조가 더 개선된 3Tier 구조라고 볼 수 있겠다.
‣ 구성도
- 프론트엔드 컨테이너는 HostOS의 80 포트로 접속
 - 백엔드 컨테이너는 HostOS의 80 포트의 
/api경로로 접근- Nginx 서버에서 프록시되어 외부에서는 접근 불가
 
 - 데이터베이스 컨테이너는 포트포워딩이 지정되지 않았기 때문에 외부에서는 접근 불가
- 내부 DNS 서버에 등록된 레코드를 통해 백엔드 컨테이너에서 
leafy-postgres로 접속 
 - 내부 DNS 서버에 등록된 레코드를 통해 백엔드 컨테이너에서 
 
‣ 소스코드
프로젝트 clone
git clone https://github.com/JaehyoJJAng/3-tier-image-deploy
cd 3-tier-image-deploy프로젝트 구조
tree -L 2 .‣ 도커 네트워크
frontend, backend 네트워크 생성
docker network create leafy-frontend
docker network create leafy-backend‣ 도커 로그인
${DOCKER_PASSWORD}: DOCKER_PASSWORD 라는 변수에 도커 허브 패스워드 기입.${DOCKER_USER}: DOCKER_USER 라는 변수에 도커 허브 유저명 기입.
# Docker login
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USER}" --password-stdin‣ Docker Run
A. leafy-frontend
Dockerfile 작성
#### Build Stage
# 빌드 이미지로 node:14 지정
FROM node:14 AS build
# 작업 디렉토리 지정
WORKDIR /app
# 라이브러리 설치 파일 복사
COPY package*.json .
# 라이브러리 설치
RUN npm ci
# 소스코드 전체 복사
COPY . .
# 소스코드 빌드
RUN npm run build
#### Production Stage
# 빌드 이미지로 nginx 지정
FROM nginx:1.21.4-alpine
# nginx config 파일 복사
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf.template
# 환경변수 설정
ENV BACKEND_HOST=leafy
ENV BACKEND_PORT=8080
# docker-entrypoint.sh 파일 복사
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# 빌드 이미지에서 생성된 dist 폴더를 nginx 이미지로 복사
COPY  /app/dist /usr/share/nginx/html
# 컨테이너 포트 지정
ARG NGINX_PORT=80
EXPOSE $NGINX_PORT
# ENTRYPOINT 지정
ENTRYPOINT ["docker-entrypoint.sh"]
# COMMAND 지정
CMD ["nginx", "-g", "daemon off;"]1. 도커 이미지 build & push
# build
docker build --tag yshrim12/leafy-frontend:latest .
# push
docker push yshrim12/leafy-frontend:latest2. 컨테이너 실행
docker run -d -it --name leafy-front -p 80:80 yshrim12/leafy-fronend3. leafy-front 컨테이너에 도커 네트워크 연결
docker network connect leafy-frontend leafy-front
docker network connect leafy-backend leafy-front4. leafy-front 컨테이너 재시작
docker restart leafy-front5. 컨테이너 로그 조회
docker logs leafy-frontB. leafy-backend
leafy-backend/Dockerfile
#### Build Stage
# 빌드 이미지로 OpenJDK 11 & Gradle 지정
FROM gradle:7.6.1-jdk11 AS build
# 작업 디렉토리 지정
WORKDIR /app
# 라이브러리 설치 파일 복사
COPY build.gradle settings.gradle ./
# gradle dependencies
RUN gradle dependencies --no-daemon
# 소스코드 전체 복사
COPY ./ ./
# Gradle 빌드 실행하여 JAR 파일 생성
RUN gradle clean build --no-daemon
#### Runtime stage
# 런타임 이미지로 OpenJDK 11-jre-slim 지정
FROM openjdk:11-jre-slim
# 작업 디렉토리 지정
WORKDIR /app
# 빌드 이미지에서 생성된 JAR 파일을 런타임 이미지로 복사
COPY  /app/build/libs/*.jar ./leafy.jar
# 컨테이너 포트 지정
EXPOSE 8080
ENTRYPOINT ["java"]
CMD ["-jar", "leafy.jar"]1. 도커 이미지 build & push
# build
docker build --tag yshrim12/leafy-backend:latest .
# push
docker push yshrim12/leafy-backend:latest2. 컨테이너 실행
docker run -d -it --name leafy -p 8080:8080 \
--network leafy-backend \
-e DB_URL=leafy-postgres
yshrim12/leafy-backend3. 컨테이너 로그 조회
docker logs leafyC. leafy-postgresql
leafy-postgresql/Dockerfile
# PostgreSQL 13 버전을 베이스 이미지로 사용
FROM postgres:13
# init.sql 파일을 /docker-entrypoint-initdb.d/로 복사
# /docker-entrypoint-initdb.d/에 있는 sql문은 컨테이너 처음 실행 시 자동 실행됨.
COPY ./init/init.sql /docker-entrypoint-initdb.d/
# config/postgresql.conf 파일을 /etc/postgresql/postgresql.conf로 복사
# 기본 설정 파일을 덮어쓰기하여 새로운 설정 적용
COPY ./config/postgresql.conf /etc/postgresql/custom.conf
# 계정 정보 설정
ENV POSTGRES_USER=myuser
ENV POSTGRES_PASSWORD=mypassword
ENV POSTGRES_DB=mydb
# 컨테이너 포트 설정
EXPOSE 5432
CMD ["postgres", "-c", "config_file=/etc/postgresql/custom.conf"]1. 도커 이미지 build & push
# build
docker build --tag yshrim12/leafy-postgres:latest .
# push
docker push yshrim12/leafy-postgres:latest2. 컨테이너 실행
docker run -d -it --name leafy-postgres \
--network leafy-backend \
yshrim12/leafy-postgres3. 컨테이너 로그 조회
docker logs leafy-postgre4. 컨테이너 명령어 실행
docker exec -it leafy-postgres su postgres bash -c "psql --username=myuser --dbname=mydb"5. 데이터 조회
$ mydb=# SELECT * FROM users;
$ mydb=# SELECT * FROM plants;
$ mydb=# SELECT * FROM user_plants;
$ mydb=# SELECT * FROM plant_logs;‣ 도커 컴포즈
include옵션을 사용하여 각 티어별 compose.yaml 분리
docker-compose.yaml
version: "3.9"
include:
  - "composes/postgresql.yaml"
  - "composes/backend.yaml"
  - "composes/frontend.yaml"
networks:
  leafy-backend:
    driver: bridge
    external: true
  leafy-frontend:
    driver: bridge
    external: true
volumes:
  mydata: {}composes/postgresql.yaml
services:
  leafy-postgres:
    build: ../leafy-postgresql
    image: leafy-postgres:5.0.0-compose
    volumes:
      - type: volume
        source: "mydata"
        target: "/var/lib/postgresql/data"
    container_name: leafy-postgres
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 256M
    restart: always
    networks:
      - "leafy-backend"composes/backend.yaml
services:
  leafy-backend:
    build: ../leafy-backend
    environment:
      - DB_URL=leafy-postgres
    depends_on:
      - "leafy-postgres"
    container_name: leafy-backend
    deploy:
      resources:
        limits:
          cpus: '1.5'
          memory: 512M
    restart: on-failure
    networks:
      - "leafy-backend"composes/frontend.yaml
services:
  leafy-front:
    build: ../leafy-frontend
    image: leafy-front:5.0.0-compose
    environment:
      - BACKEND_HOST=leafy-backend
    ports:
      - 80:80
    depends_on:
      - "leafy-backend"
    container_name: leafy-frontend
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 64M
    restart: on-failure
    networks:
      - "leafy-frontend"
      - "leafy-backend"▶︎ 파이프라인 구성
‣ 환경 변수 셋업
‣ leafy-frontend
.github/workflows/leafy-frontend.yaml
name: Frontend Build and Push
on:
  push:
    branches:
      # main 브랜치에 push 될 때 워크플로우 실행
      - main
    paths:
      # leafy-frontend 디렉토리에 변경점이 있을 경우에만 워크플로우 동작.
      - 'leafy-frontend/**'
jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: "1. Checkout Repository"
        uses: actions/checkout@v2
        
      - name: "2. Set up Docker buildx"
        uses: docker/setup-buildx-action@v1
      - name: "3. Login to dockerhub"
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
      - name: "4. Build and push"
        uses: docker/build-push-action@v2
        with:
          context: ./leafy-frontend # Dockerfile이 있는 경로 지정
          file: ./leafy-frontend/Dockerfile # Dockerfile 지정
          # 이미지를 레지스트리에 배포
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/leafy-frontend:${{ github.sha }}
          platform: linux/amd64,linux/arm64,windows/amd64배포 시 leafy-frontend 디렉토리에 변경점이 있을 경우 위 워크플로우가 자동으로 실행됨.
‣ leafy-backend
.github/workflows/leafy-backend.yaml
name: Backend Build and Push
on:
  push:
    branches:
      # main 브랜치에 push 될 때 워크플로우 실행
      - main
    paths:
      # leafy-backend 디렉토리에 변경점이 있을 경우에만 워크플로우 동작.
      - 'leafy-backend/**'
jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: "1. Checkout Repository"
        uses: actions/checkout@v2
      - name: "2. Set up Docker buildx"
        uses: docker/setup-buildx-action@v1
      - name: "3. Login to dockerhub"
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
      - name: "4. Build and push"
        uses: docker/build-push-action@v2
        with:
          context: ./leafy-backend # Dockerfile이 있는 경로 지정
          file: ./leafy-backend/Dockerfile # Dockerfile 지정
          # 이미지를 레지스트리에 배포
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/leafy-backend:${{ github.sha }}
          platform: linux/amd64,linux/arm64,windows/amd64배포 시 leafy-backend 디렉토리에 변경점이 있을 경우 위 워크플로우가 자동으로 실행됨.
‣ leafy-postgres
.github/workflows/leafy-postgres.yaml
name: DB Build and Push
on:
  push:
    branches:
      # main 브랜치에 push 될 때 워크플로우 실행
      - main
    paths:
      # leafy-postgresql 디렉토리에 변경점이 있을 경우에만 워크플로우 동작.
      - 'leafy-postgresql/**'
jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: "1. Checkout Repository"
        uses: actions/checkout@v2
      - name: "2. Set up Docker buildx"
        uses: docker/setup-buildx-action@v1
      - name: "3. Login to dockerhub"
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
      - name: "4. Build and push"
        uses: docker/build-push-action@v2
        with:
          context: ./leafy-postgresql # Dockerfile이 있는 경로 지정
          file: ./leafy-postgresql/Dockerfile # Dockerfile 지정
          # 이미지를 레지스트리에 배포
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/leafy-postgresql:${{ github.sha }}
          platform: linux/amd64,linux/arm64,windows/amd64배포 시 leafy-postgresql 디렉토리에 변경점이 있을 경우 위 워크플로우가 자동으로 실행됨.
