# ================================
urlpatterns = [
path("<int:place_id>/comment/", views.PlaceCommentView.as_view(),
name='place_commment_view'),
path("<int:place_id>/comment/<int:place_comment_id>/", views.PlaceCommentDetailView.as_view(),
name='place_commment_detail_view'),
]
# ================================
class PlaceCommentView(APIView):
# (프론트) 해당 게시글 댓글 가져오기
# 중복 없이 가져오기
def get(self, request, place_id): # ✔️
place = get_object_or_404(Place, id=place_id)
query = place.place_comment_place.filter(main_comment=None)
serializer = PlaceCommentSerializer(query, many=True)
return Response(serializer.data)
# ================================
# 댓글 출력을 위한 오버라이딩
class RecursiveSerializer(serializers.Serializer):
def to_representation(self, instance):
serializer = self.parent.parent.__class__(instance, context=self.context)
return serializer.data
# place 댓글 시리얼라이저
class PlaceCommentSerializer(serializers.ModelSerializer):
reply = serializers.SerializerMethodField()
# slug_field : Place 모델의 id
place = serializers.SlugRelatedField(queryset=Place.objects.all(), slug_field='id')
class Meta:
model = PlaceComment
fields = ['id', 'user', 'place', 'content', 'main_comment', 'created_at', 'updated_at', 'reply']
def get_reply(self, instance):
serializer = self.__class__(instance.reply, many=True)
serializer.bind('', self)
return serializer.data
역할 분담
오늘의 커밋
[Feat] : place 대댓글 기능 추가
-views 생성 : 댓글, 대댓글, 좋아요 기능 추가 -views 추가 : 상세보기 댓글 시리얼라이저 추가 -views 수정 : 권한 수정 (is_admin -> is_staff), Response 컨벤션 맞춰 변경 -models 수정 : category, like 테이블 추가, image -> PlaceImage 모델로 분리 -serializers 수정 : 이미지 시리얼라이저, 대댓글 시리얼라이저 추가 및 필드 순서 변경 -urls 제거 : 대댓글 urls 제거
# 전체 삭제
sudo rm -rf ./*
ls -l
sudo docker ps -a
sudo docker rm -f 24 36 #숫자는 컨테이너 id
sudo docker volume prune
# 사용되지 않는 모든 것을 삭제
sudo docker system prune -a
vi docker-compose.yml
version: '3.8'
volumes:
postgres: {} # postgresql에서 사용 할 볼륨 지정
services:
postgres:
container_name: postgres
image: postgres:14.5
volumes:
- postgres:/var/lib/postgresql/data/
environment: # postgresql 컨테이너에서 사용할 환경변수 지정해주기
- POSTGRES_USER=user # 데이터베이스 사용자 지정
- POSTGRES_PASSWORD=P@ssw0rd # 사용자 비밀번호 지정
- POSTGRES_DB=django # 데이터베이스 이름 지정
restart: always
sudo docker compose up -d
gunicorn이란?
django 프로젝트를 실행할 때 사용되는 runserver와 같이, 사용자의 요청을 받아 django에 작성한 코드를 실행시켜 주도록 하는 역할을 해줍니다.
단순하게 생각해서 배포용으로 사용되는 runserver라고 생각해도 무관합니다.
기본적으로 runserver는 배포용이 아닌 개발용으로 사용되는 명령어이며, 공식 문서에서도 runserver로 배포하는 것을 권장하지 않고 있습니다.
runserver는 기본적으로 싱글 스레드에서 동작하지만, gunicorn은 멀티 스레드로 동작하도록 설정할 수 있기 때문에 많은 요청을 더 효율적으로 처리할 수 있습니다.
mkdir backend
cd backend/
git clone https://github.com/sparta-course/drf-project.git ./django
# 경로 변경시 추후에 docker compose 파일에서 볼륨을 잡아줄 때 수정이 필요함
vi django/drf_project/settings.py
# 아래 코드로 변경
ALLOWED_HOSTS = ['*']
STATIC_ROOT = BASE_DIR / "static"
# timezone 변경
sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
date
vi Dockerfile
# python 3.10.8버전 이미지를 사용해 빌드
# 프로젝트와 일치시키는 것을 권장
FROM python:3.10.8
# .pyc 파일을 생성하지 않도록 설정합니다.
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정합니다.
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성합니다.
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정합니다.
WORKDIR /app/
# requirments.txt를 작업 디렉토리(/app/) 경로로 복사합니다.
COPY ./django/requirements.txt .
# 프로젝트 실행에 필요한 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# gunicorn을 사용하기 위한 패키지를 설치합니다.
RUN pip install gunicorn
cd ..
vi docker-compose.yml
version: '3.8'
services:
backend:
container_name: backend
build: ./backend/
# drf_project.wsgi는 프로젝트 경로에 맞게 지정해야 합니다.
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
ports:
- 80:8000
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro # host의 timezone 설정을 컨테이너에 적용합니다.
# ro 은 읽기 전용(read only) 속성으로 볼륨을 설정하는 것을 의미합니다.
restart: always
sudo docker compose up -d
# 장고가 이미 80번 포트를 사용하고 있으므로 에러가 남
sudo docker ps
sudo docker rm -f c8 92 # 숫자는 본인 컨테이너 ID
sudo docker compose up -d
sudo docker compose logs -f
nginx / postgresql / django 연동하기
mkdir nginx
vi nginx/default.conf
server {
listen 80;
server_name _; # 모든 도메인 혹은 ip로 들어오는 요청에 대해 처리해 줍니다.
location / { # nginx로 요청이 들어왔을 때
proxy_pass http://backend:8000/; # backend 컨테이의 8000번 포트로 전달합니다.
}
location /static/ { # 브라우저에서 /static/ 경로로 요청이 들어왔을 때
alias /static/; # /static/ 경로에 있는 파일들을 보여줍니다.
}
location /media/ { # 브라우저에서 /media/ 경로로 요청이 들어왔을 때
alias /media/; # /media/ 경로에 있는 파일들을 보여줍니다.
}
}
vi backend/django/drf_project/settings.py
# 해당부분 수정하기
import os
# 환경변수에 따라 DEBUG모드 여부를 결정합니다.
DEBUG = os.environ.get('DEBUG', '0') == '1'
# 접속을 허용할 host를 설정합니다.
ALLOWED_HOSTS = ['backend', ]
# postgres 환경변수가 존재 할 경우에 postgres db에 연결을 시도합니다.
POSTGRES_DB = os.environ.get('POSTGRES_DB', '')
if POSTGRES_DB:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': POSTGRES_DB,
'USER': os.environ.get('POSTGRES_USER', ''),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', ''),
'HOST': os.environ.get('POSTGRES_HOST', ''),
'PORT': os.environ.get('POSTGRES_PORT', ''),
}
}
# 환경변수가 존재하지 않을 경우 sqlite3을 사용합니다.
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# CORS 허용 목록에 ec2 ip를 추가합니다.
CORS_ORIGIN_WHITELIST = ['http://$ec2_public_ip']
# ex) CORS_ORIGIN_WHITELIST = ['http://43.201.72.190']
# CSRF 허용 목록을 CORS와 동일하게 설정합니다.
CSRF_TRUSTED_ORIGINS = CORS_ORIGIN_WHITELIST
sudo docker compose down
sudo docker ps -a
vi docker-compose.yml
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5
volumes:
- postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=P@ssw0rd
- POSTGRES_DB=django
restart: always
backend:
container_name: backend
build: ./backend/
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro
- django_media:/app/media/ # nginx에서 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/app/static/ # nginx에서 static을 사용할 수 있도록 volume을 지정해줍니다.
environment: # django에서 사용할 설정들을 지정해줍니다.
- DEBUG=1
- POSTGRES_DB=django
- POSTGRES_USER=user
- POSTGRES_PASSWORD=P@ssw0rd
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- django_media:/media/ # django의 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/static/ # django의 static 사용할 수 있도록 volume을 지정해줍니다.
depends_on:
- backend
restart: always
# python 3.10.8-slim버전 이미지를 사용해 빌드
FROM python:3.10.8-slim
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN mkdir /app/
WORKDIR /app/
# slim 이미지에서 postgresql 패키지를 설치하기 위해 필요 명령어 추가
RUN apt update && apt install libpq-dev gcc -y
COPY ./django/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn psycopg2
vi docker-compose.yml
services:
postgres:
image: postgres:14.5-alpine # alpine으로 수정
nginx:
image: nginx:1.23.2-alpine # alpine으로 수정
sudo docker compose up -d --build
sudo docker images
# 필요없는 이미지 삭제
sudo docker rmi aa c7 # 숫자는 none이라고 쓰인 이미지ID
sudo docker image prune -a #컨테이너에서 사용중인 이미지 이외에 모두 삭제
도메인 배포 서버에 연결
AWS, 가비아, freenom 등 마음에 드는 곳에서 구매하면 됩니다.
freenom의 경우 무료이지만 오류가 많습니다.
도메인이 등록되는데 최대 24시간까지 걸리기도 합니다.
ex) 소셜로그인 오류, xxx.tk 등 도메인 전체 주소를 입력해야 하는 오류 등
레코드 2개 생성
www를 강제로 붙여주는 작업
vi nginx/default.conf
server {
listen 80;
server_name www.spartacodingclub.tk; # www.spartacodingclub.tk 도메인으로 들어오는 요청을 처리해줍니다.
location / {
proxy_pass http://backend:8000/;
}
location /static/ {
alias /static/;
}
location /media/ {
alias /media/;
}
}
server {
listen 80;
server_name spartacodingclub.tk; # www가 없는 url로 요청 했을 때
return 301 http://www.spartacodingclub.tk$request_uri; # www를 붙인 url로 redirection 해줍니다.
}
sudo docker compose restart nginx
https 란?
웹 서버에서 데이터를 전송하기 위한 http 프로토콜에 보안을 의미하는 secure를 붙여 http 통신을 더 안전하게 할 수 있도록 해주는 프로토콜입니다.
보안적인 특성 때문에 배포를 하는 서비스에는 https를 필수적으로 적용하게 됩니다.
https를 적용하기 위해서는 SSL 인증서를 발급받고 웹서버에서 인증서를 지정해주는 과정이 필요합니다.
※ '503 Service Temporarily Unavailable' 에러가 발생하는 경우 로드밸런싱 대상 그룹에 Healthy 부분을 잘 체크하자. 인스턴스 가용영역을 서브넷에서 설정하지 않은 경우 unused라고 나타나며 에러가 발생하기도 한다. 이 경우 로드벨런서 서브넷 편집에서 해당 가용영역을 추가하면 해결할 수 있다.
※ 보안 그룹 선택할 경우 생성후 인바운드 규칙을 바로 추가해주거나, 애초에 SSH, HTTPS, HTTP 포트 허용한 걸로 선택하자.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0744 for '/home/geek/.ssh/id_rsa' are too open.
It is recommended that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: /home/geek/.ssh/id_rsa
django, db, web server 등 다양한 어플리케이션을 컨테이너 환경에서 테스트 및 배포할 수 있는 소프트웨어 플랫폼입니다.
컨테이너 환경은 가상 머신(Virtual machine)에서 사용되는 가상 환경과 비슷하지만 다른 구조를 가지고 있습니다.
docker를 사용할 경우 다양한 어플리케이션을 쉽고 빠르게 배포할 수 있습니다.
컨테이너는 각각 독립적인 환경에서 구동되기 때문에 안정적인 운영이 가능합니다.
docker 이미지를 통해 동일한 환경을 가진 컨테이너를 쉽게 생성하고 관리할 수 있습니다.
도커 = 가상 머신 - os
docker 이미지란?
가상 머신에서 윈도우를 설치할 때, 윈도우 ISO를 사용해 설치하는 것처럼 docker 컨테이너는 docker 이미지를 기반으로 생성됩니다.
이미지는 docker 컨테이너가 생성될 때 실행되는 실행파일, 설정 등을 가지고 있습니다. 예를 들어, docker postgresql 이미지는 컨테이너가 실행됐을 때 postgresql을 실행시키기 위한 패키지들이 기본적으로 설치되어 있습니다.
docker 설치하기
docker 패키지 설치
sudo apt install docker.io -y # 설치과정 중 선택지가 나오면 자동 yes
# 만약 아래와 같은 에러가 발생한다면
# E: Package 'docker.io' has no installation candidate
# sudo apt update 명령어 실행 후 docker 패키지를 다시 설치해주세요
docker 설치 확인
sudo docker --version # 도커 버전이 출력되는지 확인합니다.
# Docker version 20.10.12, build 20.10.12-0ubuntu2~20.04.1 - 정상
# command not found: docker 와 같은 문구가 출력될 경우 docker가 설치되었는지 확인해야 합니다.
docker 컨테이너 생성하기
sudo docker run -d -p 80:80 httpd:latest
# run : 이미지를 사용해 컨테이너를 실행시킵니다.
# -d : 컨테이너를 데몬(백그라운드)으로 실행시킵니다.
# 80:80 : 80번 포트로 접속했을 때 컨테이너에 접근할 수 있도록 포트포워딩 설정을 해줍니다.
# httpd:latest : httpd의 가장 최신 이미지를 사용해 컨테이너를 생성합니다.
실행중인 컨테이너 확인하기
sudo docker ps # 실행중인 컨테이너 목록 확인하기
# CONTAINER ID : 컨테이너가 가지고 있는 고유한 id
# IMAGE : 컨테이너가 생성될 때 사용된 이미지
# COMMAND : 컨테이너가 생성될 때 실행되는 명령어
# CREATED : 생성 후 경과 시간
# STATUS : 컨테이너 상태
# PORTS : 사용중인 포트
sudo docker ps -a
# -a : 중지된 컨테이너 목록까지 포함해서 모두 확인하기
다운받은 이미지 확인하기
sudo docker images
# REPOSITORY : 이미지 저장소 이름
# TAG : 이미지 버전
# IMAGE ID : 이미지의 고유한 id
# CREATED : 이미지 생성일(마지막 업데이트 일)
# SIZE : 이미지 용량
컨테이너 내부로 들어가보기
sudo docker exec -it $container_id /bin/bash
# $containser_id : sudo docker ps를 쳤을 때 확인되는 container_id를 입력합니다.
# /bin/bash : 컨테이너에 접속할 때 사용되는 쉘을 입력합니다.
# 이미지에 따라 /bin/bash라는 쉘이 존재하지 않을 수 있는데, 이 경우에는 /bin/sh를 사용해 접속합니다.
# 종료 : exit
docker-compose란?
docker-compose는 docker 2개 이상의 컨테이너를 더욱 간편하게 관리하기 위해 사용되는 툴입니다.
docker-compose를 사용할 때에는 docker-compose.yml (혹은 .yaml)이라는 파일에 컨테이너에서 사용 될 이미지, 옵션 등을 작성한 후 사용하게 됩니다.
이를 통해 docker 명령어만 사용할 때보다 여러 컨테이너를 더 간편하고 직관적으로 컨테이너를 관리할 수 있습니다.
sudo mkdir -p /usr/lib/docker/cli-plugins
# /usr/lib/docker 경로에 cli-plugins라는 디렉토리를 생성합니다.
# -p : 만약 상위 디렉토리가 없다면 함께 생성합니다.
sudo curl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o /usr/lib/docker/cli-plugins/docker-compose
# github에 release 된 docker-compose 파일을 /usr/lib/docker/cli-plugins/ 경로에 다운로드 받습니다.
# v2.11.2는 docker-compose의 버전이며, 최신 버전은 여기서 확인 가능합니다.
sudo chmod +x /usr/lib/docker/cli-plugins/docker-compose
# 다운받은 docker-compose 파일에 실행 권한을 부여해 줍니다.
sudo docker compose version
# docker-compose가 정상적으로 설치되었는지 확인합니다.
# Docker Compose version v2.11.2 정상적으로 설치 된 경우 버전이 출력됩니다.
※ docker-compose.yml 파일이 존재하지 않는 경로에서 docker compose 명령어를 실행시킬 경우 다음과 같은 에러가 발생할 수 있습니다.
docker-compose로 컨테이너 생성하기
💡 docker 명령어가 아닌 docker-compose를 활용해 컨테이너를 생성하는 방법을 알아보겠습니다.
docker 컨테이너 삭제하기
sudo docker ps -a # docker에 존재하는 컨테이너 목록을 확인합니다.
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 54445308314d httpd:latest "httpd-foreground" 23 hours ago Up 23 hours 0.0.0.0:80->80/tcp, :::80->80/tcp sweet_engelbart
sudo docker rm -f $container_id # 컨테이너의 실행중 여부와 관계 없이 강제로 삭제합니다.
docker-compose.yml 작성해보기
vi docker-compose.yml
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의
# 즉 위의 version에 따라 밑의 services의 형식, 문법이 변함
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용
container_name: example # 컨테이너 이름을 지정
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정
restart: always # 컨테이너가 종료됐을 때 다시 실행
컨테이너 실행시켜보기
# docker compose 명령어는 docker-compose.yml 파일이 존재하는 자리에서 실행해야 합니다.
sudo docker compose up -d
# up : docker-compose.yml 파일을 읽어 정의된 서비스들을 실행시킵니다.
# -d : 컨테이너를 데몬(백그라운드)으로 실행시킵니다.
sudo docker ps
컨테이너 중지시키기
1. stop
sudo docker compose stop
sudo docker ps -a # 중지 상태의 컨테이너 확인 가능
# docker ps는 동작 중인 컨테이너만 보여줌
sudo docker compose start # 재시작
2.down
sudo docker compose down
sudo docker ps -a
sudo docker compose up -d # 재생성시 컨테이너 id가 변경됨
※ aws 인스턴스 중지와 종료 차이와 유사
컨테이너 포트 포워딩 설정
포트 포워딩(port forwarding)이란?
포트 포워딩이라는 이름과 같이, 외부에서 서버의 특정 포트에 접근했을 때 지정한 서비스로 전달해 주는 것을 의미합니다.
예를 들어 특정 컨테이너의 포트포워딩 설정을 80:8000과 같이 해줬다면, 외부에서 80 포트로 접속했을 때 해당 컨테이너의 8000번 포트로 접속하겠다는 의미입니다.
※ http 프로토콜은 기본적으로 80 포트를 사용하고 https 프로토콜은 443 포트를 사용하게 되며 이는 웹 브라우저에 주소를 입력할 때 생략됩니다.
docker-compose.yml 수정하기
vi docker-compose.yml
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정합니다.
ports: # 포트포워딩을 설정해줍니다.
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결해줍니다.
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
변경사항 적용하기
sudo docker compose up -d
컨테이너 로그 확인하기
sudo docker compose logs
sudo docker compose logs -f # 실시간 로그 확인
# 종료 : ctrl + c
volume을 사용해 데이터 보존하기
❓volume을 사용하지 않으면?
sudo docker exec -it example /bin/bash
# docker container에 접속할 때 container id 대신 container name을 사용할 수도 있습니다.
echo "test" >> /usr/local/apache2/htdocs/index.html # echo 문자열 : 문자열을 출력해줌
exit
sudo docker compose down
sudo docker compose up -d
docker 컨테이너는 컨테이너가 종료될 때 변경된 데이터는 모두 초기화 된다는 특징을 가지고 있습니다.
volume은 컨테이너에 저장되는 데이터의 일부를 host와 공유해 주는 역할을 합니다.
host에 저장 된 데이터는 사용자가 직접 삭제하지 않는 이상 계속해서 유지되며, 때문에 컨테이너가 종료된다 하더라도 데이터는 유실되지 않습니다.
volume은 컨테이너 내부에서 변경되는 내용들을 유지해야 할 때 주로 사용됩니다.
docker volume 종류
docker volume의 종류는 docker volume, bind mount, tmpfs mount방식이 있으며 docker volume 방식이 주로 사용됩니다.
docker volume
docker 엔진이 관리하는 volume을 생성하는 방식입니다.
docker volume 방식을 사용해 생성된 volume은 host의 /var/lib/docker/volumes/ 경로에 저장됩니다.
docker에서 가장 권장하는 방식입니다.
vi docker-compose.yml
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
volumes:
example_http_code: {} # docker volume을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정합니다.
ports: # 포트포워딩을 설정해줍니다.
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결해줍니다.
volumes: # volume을 성정해줍니다.
- example_http_code:/usr/local/apache2/htdocs/
# 위에서 정의한 볼륨: 컨테이너 폴더를 mount
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
sudo docker compose up -d
sudo docker exec -it example /bin/bash
echo "test" >> /usr/local/apache2/htdocs/index.html
exit
sudo docker compose down
sudo docker compose up -d
# 볼륨 정보 확인
sudo docker volume ls
sudo docker volume inspect ubuntu_example_http_code
bind mount
docker volume 방식과 매우 유사합니다.
docker container를 생성할 때 사용자가 지정한 경로에 데이터가 저장됩니다.
docker 엔진의 관리를 받지 않는 영역이기 때문에 사용자가 직접 파일을 추가/수정/삭제 할 수 있다는 특징이 있습니다. docker 공식 문서에서는 이러한 특징으로 인해 운영에 영향을 미칠 수 있기 때문에 유사한 기능인 docker volume 방식을 사용하는 것을 권장하고 있습니다.
vi docker-compose.yml
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
image: 'httpd:latest' # 컨테이너를 생성할 때 사용될 이미지를 지정합니다.
ports: # 포트포워딩을 설정해줍니다.
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결해줍니다.
volumes: # volume을 성정해줍니다.
- ./example_http_code/:/usr/local/apache2/htdocs/ # 정의한 volume의 mount할 경로를 지정합니다.
# host 경로 : 컨테이너 경로를 볼륨으로 정의
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
cat docker-compose.yml
sudo docker compose up -d
ls -l example_http_code/
# 폴더가 비어있음
sudo vi example_http_code/index.html
bind mount
# 컨테이너 수정
sudo docker exec -it example /bin/bash
echo "test" >> /usr/local/apache2/htdocs/index.html
exit
cat example_http_code/index.html
tmpfs mount
기존의 방식들이 ssd 혹은 hdd와 같은 저장장치에 데이터를 저장한다면, tmpfs mount 방식은 휘발성 메모리인 RAM에 데이터를 저장합니다.
파일로 저장하면 안 되는 민감한 정보를 다룰 때 사용됩니다.
언제 어떤 방식의 volume을 사용해야 할까?
docker volume은 주로 컨테이너간 파일을 공유하거나 중요도가 높은 파일들을 사용자가 쉽게 수정/삭제 하지 못하도록 할 때 주로 사용합니다.
bind mount는 설정파일 혹은 소스코드를 프로젝트와 서버가 공유할 때 주로 사용됩니다.
tmpfs mount는 많은 양의 데이터를 임시로 저장할 때, 혹은 보안적으로 중요한 데이터를 저장할 때 주로 사용됩니다.
docker compose down 혹은 docker rm 명령어로 컨테이너를 삭제해도, docker volume은 삭제되지 않고 남아있게 됩니다.
sudo docker compose down
# 사용하지 않는 볼륨을 일괄적으로 삭제
sudo docker volume prune
sudo docker volume ls
이미지 직접 빌드하기
Dockerfile 이란?
docker 의 이미지를 직접 생성하기 위한 용도로 작성하는 파일입니다.
Dockerfile을 작성할 때는 기본이 되는 이미지를 지정한 후, 특정 패키지를 설치하거나 파일을 추가하는 등의 작업을 통해 사용자가 직접 이미지를 빌드하고 사용할 수 있습니다.
사용자가 개발한 프로젝트 혹은 설정파일 등을 이미지에 포함시키거나 이미지에 기본적으로 특정 패키지를 설치하고싶을 때 등 다양한 용도로 사용됩니다.
Dockerfile 작성하기
vi Dockerfile
# 빌드할 때 사용할 이미지를 지정해줍니다.
FROM httpd:latest
# 현재 경로에 존재하는 index.html 파일을 컨테이너 내부로 복사합니다.
# 컨테이너 내부에 이미 존재하는 경우 덮어쓰기
# 없는 경우 새로 생성
COPY ./index.html /usr/local/apache2/htdocs/index.html
vi docker-compose.yml
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
build: . # 현재 경로에 있는 Dockerfile을 사용해 이미지를 생성합니다.
ports: # 포트포워딩을 설정해줍니다.
- 80:80 # 외부에서 80 포트로 접속했을 때 컨테이너의 80 포트로 연결해줍니다.
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
vi index.html
docker file
sudo docker compose up -d
sudo docker compose down
※ Dockerfile로 빌드된 이미지가 없을 경우, --build 옵션을 추가하지 않더라도 이미지를 빌드 하게 됩니다. 하지만, 기존에 빌드된 이미지가 있을 경우에는 새로 빌드하지 않고 기존에 빌드 된 이미지를 사용합니다. 때문에, Dockerfile을 수정하고 이미지를 새로 빌드해야 할 때는 --build 옵션을 추가해서 사용해야 합니다.
sudo docker compose up --build -d
entrypoint란?
docker 컨테이너가 생성될 때 기본적으로 실행 할 명령어를 지정해 주는 옵션입니다.
데이터베이스를 실행시키기 위해 만든 이미지는, 컨테이너가 생성될 때 데이터베이스 서비스를 실행시켜야 합니다. 이 때 사용되는 옵션이 entrypoint입니다.
entrypoint는 Dockerfile과 docker-compose.yml 모두 작성할 수 있습니다.
만약 Dockerfile, docker-compose.yml 모두 entrypoint가 작성되어 있다면 Dockerfile의 entrypoint는 무시되고 docker-compose.yml의 명령어가 우선적으로 수행됩니다.
vi Dockerfile
FROM python:3.9.15
# .pyc 파일을 생성하지 않도록 설정합니다.
# 파이썬 사용할 때 거의 필수 옵션
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정합니다.
# 파이썬 사용할 때 거의 필수 옵션
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성합니다.
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정합니다.
WORKDIR /app/
# main.py 파일을 /app/ 경로로 복사합니다.
COPY ./main.py /app/
vi docker-compose.yml
version: '3.8' # docker-compose.yml에 사용될 문법 버전을 정의합니다.
services:
example: # 서비스 이름을 지정합니다. 서비스 이름은 컨테이너끼리 통신할 때 사용됩니다.
container_name: example # 컨테이너 이름을 지정합니다.
build: . # 현재 경로에 있는 Dockerfile을 사용해 이미지를 생성합니다.
entrypoint: sh -c "python3 main.py" # 작업 디렉토리에 존재하는 main.py 파일을 실행시킵니다.
# sh -c 뒤에 파일을 실행시킴
restart: always # 컨테이너가 종료됐을 때 다시 실행시켜 줍니다.
vi main.py
from time import sleep
for i in range(100):
print(f"print number : {i}")
sleep(1)
# 도커 파일 수정한 후에도 빌드해야 함
sudo docker compose up --build -d
컨테이너가 1개일 때와 2개 이상일 때 docker-compose.yml을 작성하는 방법은 기존과 크게 다르지 않습니다.
다만, ports 혹은 서비스 이름 등 중복되면 안되는 몇몇 옵션이 존재하기 때문에 중복되는 값이 있는지 확인해야 하며 만약 특정 값이 중복되어 들어간다면 컨테이너가 정상적으로 생성되지 않거나 생성하는 과정에서 에러가 발생할 수 있습니다.
(위의 dockerfile을 그대로 사용)
vi docker-compose.yml
version: '3.8'
services:
example1:
container_name: example1
image: 'httpd:latest'
ports:
- 80:80
restart: always
example2: # 서비스 이름이 동일하면 컨테이너가 정상적으로 생성되지 않을 수 있습니다.
container_name: example2 # 컨테이너 이름이 동일하면 컨테이너 생성 시 에러가 발생합니다.
build: .
entrypoint: sh -c "python3 main.py"
restart: always
sudo docker compose up -d
sudo docker ps
sudo docker rm -f example
docker-compose.yml에 두 개 이상의 컨테이너를 생성하도록 작성한 경우, 기본적으로는 작성 한 순서대로 컨테이너가 실행됩니다.
하지만 경우에 따라 특정 컨테이너가 먼저 실행되어야 하는 경우가 있습니다.
이 때, depends_on 옵션을 사용해 컨테이너간 실행 순서를 컨트롤 할 수 있습니다.
vi docker-compose.yml
version: '3.8'
services:
example1:
container_name: example1
image: 'httpd:latest'
ports:
- 80:80
depends_on:
- example2 # 해당 컨테이너보다 먼저 실행되어야 하는 컨테이너를 지정합니다.
restart: always
example2: # 서비스 이름이 동일하면 컨테이너가 정상적으로 생성되지 않을 수 있습니다.
container_name: example2 # 컨테이너 이름이 동일하면 컨테이너 생성 시 에러가 발생합니다.
build: .
entrypoint: sh -c "python3 main.py"
restart: always
Linux의 명령어는 명령어 -옵션 의 구조로 이루어져 있습니다. ls # 파일 목록을 보여주는 명령어 ls -l # -l : 파일 목록을 더 자세히 보여주는 옵션 ls -a # -a : 숨김 파일까지 보여주는 옵션
2개 이상의 옵션을 한번에 사용할 수도 있습니다.
ls -al # 옵션의 순서는 무관합니다.
일부 명령어는 source와 destination의 구조로 이루어져 있습니다.
cp a.txt a_copy.txt # a.txt를 a_copy.txt로 복사 cp -r folder folder_copy # folder를 복사 할 때는 -r 옵션이 필요합니다.
ssh 연결하기
(cmd)
ssh -i {키페어 경로} ubuntu@{퍼블릭 IPv4}
파일과 디렉토리 다뤄보기
상대경로와 절대경로란?
리눅스에서는 디렉토리를 이동하거나 파일을 지정할 때, 상대경로 혹은 절대경로를 사용하게 됩니다.
절대경로를 사용할 경우 현재 위치와 관계 없이 절대적인 경로를 입력하여 사용합니다.
ex) cd /home/user/example/dir/
상대경로는 내 현재 위치를 기준으로 경로를 지정하게 됩니다.
ex) cd ./dir/ 현재 위치에 존재하는 dir 디렉토리로 이동
ex) cd ../ 현재 위치를 기준으로 상위 디렉토리로 이동
리눅스에서 숨김 파일을 취급하는 방법
리눅스에서는 파일 혹은 디렉토리 이름이 .으로 시작할 경우 숨김 파일로 취급됩니다.
ex) touch test : test라는 이름의 파일 생성 touch .test : .test라는 이름의 숨김 파일 생성
wild card(*)란?
특정 파일이나 디렉토리를 지정할 때, 이름을 지정해 줘야 하는 경우가 있습니다. 이 때 wild card(*)를 사용하면 원하는 파일을 더 빠르게 탐색하거나 실행시킬 수 있습니다.
ls -l ./*.txt # 현재경로에 있는 .txt로 끝나는 모든 파일 목록 출력하기
기본 명령어
# 폴더 생성하기
mkdir ./test # 현재 경로에 test 디렉토리 생성하기
mkdir ./test/test2 # test 디렉토리 안에 test2 디렉토리 생성하기
mkdir /home/ubuntu/test3 # /home/user 경로에 test3 디렉토리 생성하기
# 현재 경로 확인하기
pwd
# 경로 이동하기
cd ./test # 현재 경로의 test 디렉토리로 이동
cd ./test/test2 # test 디렉토리 안에 test2 디렉토리로 이동
cd ../test3 # 상위 디렉토리의 test3 디렉토리로 이동
cd /home/user/test4 # /home/user/test4 디렉토리로 이동
# 파일 목록보기
ls # 현재 위치에 파일 목록 보기
ls ./test # test 경로에 파일 목록 보기
ls -l # 현재 위치에 파일 목록을 더 자세히 보기
ls -a # 현재 위치에 숨겨짐 파일을 포함해서 보기
ls -al # 현재 위치에 숨겨진 파일을 포함해서 목록을 더 자세히 보기
# 이외에도 다양한 옵션들이 존재하며, ls --help 명령어로 확인 가능합니다.
# 파일 생성하기
touch test.txt # test.txt 파일 생성하기
# 파일 복사하기
cp test.txt test_copy.txt # test.txt 파일을 test_copy.txt라는 이름으로 복사하기
cp -r directory directory_copy # 폴더를 복사할 경우 -r 옵션 사용
# 파일 이동하기
mv ./directory1/test.txt ./ # directory1 경로의 test.txt 파일을 현재 경로로 이동
# 파일명 변경
mv test.txt text2.txt # test.txt 파일의 이름을 test2.txt로 변경
# 파일 삭제하기
rm test.txt # test.txt 파일 삭제
rm -r directory # 디렉토리를 삭제하기 위해서는 -r 옵션을 붙여줘야 합니다.
rm -rf ./* : 현재 위치 모든 파일을 에러가 나도 강제로(-f) 삭제
파일 편집
# insert mode
i # 현재 위치에서 insert mode 진입
a # 현재 위치에서 커서를 한 칸 뒤로 이동 후 insert mode 진입
shift + a # 현재 위치에서 가장 마지막 텍스트로 이동 한 후 insert mode 진입
o # 현재 위치에서 한칸 개행 한 후 insert mode 진입
shift + o # 커서 한줄 위에서 insert mode 진입
esc # insert mode 나가기
h,j,k,l # 방향 키
# commend mode
u # undo
ctrl + r # redo
gg # 커서를 가장 처음으로 옮김
shift + g # 커서를 마지막 줄로 옮김
dd # line 잘라내기
[숫자]dd # 커서위치부터 몇줄 삭제 ex. 5dd
yy # line 복사
p # 붙여넣기
:se nu # 라인 줄 표시
:숫자 # 숫자 번째 라인으로 이동
/word # word라는 단어를 검색
/\cwird # 대소문자를 구분하지 않고 word라는 단어를 검색 (ex - /\cword)
n # 다음 단어 검색
shift + n # 이전 단어 검색
cw # 단어 변경
dw # 단어 삭제 ex.test 12 중 test 안에서 커서를 두고 사용시 test 라는 단어 삭제
w # 다음 단어 앞부분으로 이동
:q! # 수정내역이 있어도 저장하지 않고 강제로 나가기
# 파일 내용 확인
# 파일 내용 보기
cat test.txt # test.txt
# 파일의 시작 부분 보기
head test.txt # test.txt 파일의 첫 10줄 내용 보기
head -50 test.txt # text.txt 파일의 첫 50줄 내용 보기
# 파일의 끝 부분 보기
tail text.txt # text.txt 파일의 마지막 10줄 내용 보기
tail -50 text.txt # text.txt 파일의 마지막 50줄 내용 보기
자주 사용되는 명령어
# 특정 문자열이 포함 된 파일 찾기
grep word ./* # 현재 경로의 모든 파일을 대상으로 word라는 단어가 포함된 파일 찾기
grep word ./* -r # -r 옵션을 붙이면 디렉토리 내부까지 모두 탐색합니다.
# 특정 조건의 파일 찾기
find / -name "*txt*" # 최상위 경로에서 test가 포함된 모든 파일
find ./ -type d # 현재 경로에서 모든 디렉토리 검색
find ./ -type f -name "*.txt" # 현재 경로에서 .txt로 끝나는 파일 검색
# 명령어 기록 출력하기
history
# 리소스 사용량 확인하기
htop
# 남은 디스크 용량 확인하기
df -h
# 특정 파일 혹은 폴더가 사용중인 디스크 용량 확인하기
du -sh ./* # 현재 경로의 파일 및 디렉토리가 차지하는 용량 확인하기
# ip 주소를 포함해 다양한 네트워크 정보를 확인
ifconfig
Private Subnet: 비즈니스 로직을 처리하는 레이어입니다. 보통 웹 애플리케이션 서버
Data Subnet: 데이터를 저장하는 레이어입니다. 보통 데이터베이스 서버, 파일 서버 등이 사용
대부분의 웹앱은 같은 아키텍처를 가지고 있습니다.
❓ 그런데 서비스를 만들때마다 같은 인프라를 만들어야 한다면?
EBS는 개발자 관점으로 AWS를 접근합니다.
DB설정, 로드밸런서 자동 설정
자동으로 스케일링(확장성)을 지원
3-tier 아키텍처는 애플리케이션의 확장성과 유연성을 향상시키고, 높은 가용성과 성능을 제공합니다. 각 레이어를 독립적으로 확장할 수 있으며, 부하 분산과 장애 극복 기능을 쉽게 추가할 수 있습니다. 또한, 애플리케이션의 유지보수와 업그레이드가 용이하며, 보안성이 높습니다.
배포 프로세스를 자동으로 처리하며, 필요한 인프라 자원을 프로비저닝하고, 로드 밸런싱과 오토스케일링 등의 기능을 제공합니다.
EBS 개념
Application
애플리케이션코드, 구성 및 관련 파일
Elastic Beanstalk는 다양한프로그래밍 언어와 프레임워크를 지원합니다
Environment
Amazon EC2 인스턴스, 데이터베이스 인스턴스, 로드 밸런서 등인프라
EBS 생성하기
언급되지 않은 것은 기본값으로 세팅
(EC2) 인스턴스, 로드 밸런서, Auto Scaling, S3 등에 자동으로 추가된 것을 확인 할 수 있습니다.
https://db.도메인/ 으로 접속하기리스너와 보안에 HTTPS 설정이 빠졌다면 각각 HTTPS 추가하기
인터넷 스토리지 서비스입니다. 이 서비스는 개발자나 IT 운영자가 웹 규모 컴퓨팅 작업을 수행하는 데 필요한 데이터 저장 공간을 제공합니다. Amazon S3는 웹 사이트 호스팅, 온라인 백업, 데이터 아카이브, 기업 애플리케이션, Big Data 분석 등 다양한 용도로 사용됩니다. 콘솔창에서 region 상관없이 S3를 모두 확인할 수 있습니다.
* 프론트엔드 - 정적 웹사이트 호스팅 / 백엔드 - 동적 웹사이트 호스팅
S3 서비스의 사용 예
웹 사이트 호스팅
멀티미디어 파일 저장 및 스트리밍
애플리케이션 데이터 저장
백업 및 복원
아카이브
S3 서비스의 장점
높은 내구성, 가용성 및 안정성
손쉬운 사용 및 관리
보안성
높은 확장성
EBS는 단일 인스턴스에 대한 고성능 스토리지 서비스가 필요한 경우. Read/ Write가 빈번한 경우 사용한다.
S3는 비교적 저렵한것이 장점. 정적 웹 사이트도 지원하므로 여러개의 정적 HTML 페이지를 호스팅해야 하는 경우 사용. Write once Read many times 파일 수정 불가(덮어쓰기)
EFS
EBS
S3
NFS 파일 시스템
Block Storage
object(객체) Storage
비쌈
평균
쌈
빈번한 Read / Write
빈번한 Read / Write
Write once Read many times
여러 AZ에 중복 저장
단일 AZ에 중복 저장
여러 AZ에 중복 저장
파일 수정 가능
파일 수정 가능
덮어쓰기
대규모 사용 사례에 적합
데이터베이스, ERP 시스템, 메일 서버, 쉐어포인트, 웹 서버, 디렉터리 서버, DNS 서버 또는 미들웨어에 적합
정적 웹사이트(프론트) 호스팅에 적합
Buckets?
버킷이란 데이터를 저장하는 가장 상위 레벨의 폴더 형태의 컨테이너입니다!
S3에 저장되는 파일들을 ‘객체’ 라고 부르고 모든 객체는 ‘키’(디렉토리)로 식별됩니다.
AWS에서 제공하는 클라우드 컴퓨팅 리소스를 사용할 수 있는 가상의 사설 네트워크입니다. 사용자는 VPC 내에서 IP 주소 범위, 라우팅 테이블, 서브넷 및 보안 그룹을 설정할 수 있습니다. 또한, VPC를 사용하면 사용자는 가상 서버, 스토리지 및 데이터베이스와 같은 다양한 AWS 리소스를 시작하고 관리할 수 있습니다.
EC2, 로드밸런스 생성시 자동으로 VPC가 생성된다.
하나의 VPC는 하나의 Region 안에 있다.
VPC 안에는 여러개의 AZ가 존재한다.
AZ 안에 서브넷이 존재한다.
서브넷, 게이트웨이, NAT
❓ 서브넷이란?
VPC 내에서 IP 주소 범위를 지정하는 가상의 네트워크입니다. 서브넷은 라우팅 테이블에 연결되며, 각 서브넷은 하나의 라우팅 테이블에만 연결될 수 있습니다. 서브넷을 사용하면 VPC 내에서 다른 서브넷과 격리된 가상 네트워크를 생성할 수 있습니다. 이를 통해 다양한 서비스를 실행하는 데 필요한 보안 요구 사항을 충족시킬 수 있습니다.
퍼블릭 서브넷은 인터넷 게이트웨이와 연결된 서브넷으로, 인터넷에 직접 연결되어 있는 서브넷입니다. 퍼블릭 서브넷을 사용하면 인터넷에서 직접 액세스할 수 있는 인스턴스를 실행할 수 있습니다. 이러한 인스턴스는 공인 IP 주소를 사용하며, 인터넷 게이트웨이를 통해 인터넷으로 트래픽을 보낼 수 있습니다.
프라이빗 서브넷은 인터넷과 직접 연결되지 않는 가상의 네트워크입니다. 이러한 서브넷에서 실행되는 인스턴스는 인터넷에 직접 액세스할 수 없습니다. 대신, NAT 게이트웨이를 사용하여 인터넷을 통해 인스턴스에 연결하거나, VPC 피어링 등을 사용하여 다른 VPC와 연결할 수 있습니다.
VPC의 라우팅 테이블(쉽게 말해 서브넷 안의 주소록)은 VPC 내에서 트래픽이 전달되는 방법을 제어하는 데 사용됩니다. 라우팅 테이블은 서브넷과 연결되며, 각 서브넷에는 하나의 라우팅 테이블이 있어야 합니다. 라우팅 테이블은 목적지 IP 주소와 대상 게이트웨이 또는 NAT 게이트웨이와 같은 대상을 매핑합니다. 라우팅 테이블은 여러 개의 라우팅 규칙을 포함할 수 있으며, 이를 사용하여 서브넷에서 트래픽을 전달하는 방법을 제어할 수 있습니다.
IGW (Internet Gateway)
VPC와 인터넷 간의 통신을 가능하게 하는 게이트웨이입니다.
인터넷을 통해 인터넷 게이트웨이를 통해 VPC 내부로 들어오는 트래픽을 받아 해당 VPC에 연결된 서브넷으로 라우팅합니다.
NAT (Network Address Translation) Gateway
프라이빗 서브넷에서 아웃바운드 인터넷 트래픽을 가능하게 하는 서비스입니다.
NAT 게이트웨이는 프라이빗 서브넷 내 인스턴스가 인터넷으로 나가는 트래픽을 대신하여 공인 IP 주소를 사용하고, 인터넷 게이트웨이로부터 들어오는 트래픽을 프라이빗 서브넷으로 라우팅합니다.