안녕! 난 워치핑이야! 네 코드의 일거수일투족을 감시할 거야! 👀

peterTheAnteater·2024년 9월 23일
19

데브옵스

목록 보기
8/11
post-thumbnail

코드 한 줄만 고쳐도 내가 바로 알아채고,
서비스를 다시 돌려버릴 테니까~ 방심은 금물이야!
워치핑은 언제나 깨어있거든! 🔄✨ 스토커

글을 쓰게 된 동기

저와 제 지인들이 프로젝트를 진행할때 꼭 빠짐없이 등장하는게 있습니다.

바로 도커 입니다.

요즘 점점 도커가 귀여워 지는데 병원에 가봐야되나

요즘 개발을 하다보면 프런트와 백쪽에서 기술 스택이 바뀌는 경우는 있어도 도커는 무조건 나옵니다.

그리고 도커 이후 쿠버네티스를 쓰냐 안쓰냐 정도로 나뉘는 느낌인데... 결국 쿠버를 쓰고 싶으면 도커도 알아야되니 거의 모든곳에서 도커가 쓰이는거같습니다.

도커를 쓰는 이유는 다들 대충 아시죠?

이 짤 하나로 도커 설명 끝

어떻게 짤에서 나온 문제를 해결하는지는 대충 알아서 찾아보시고... 귀차니즘 등장 MBTI가 LAZY-T인 저한테 물어보셔봤자 설명 안해줄겁니다. ????

하여튼

이 도커로 환경 문제를 해결 하고 나서 꼭 생기는 문제를 제가 발견 했습니다.

문제점

같이 개발하는 사람들과 환경충돌이 일어나지 않게 (제 pc에서는 되는데요가 없어지게) 도커를 이용해 프로젝트를 감싸서 올리니 코드를 한줄 바꿀때마다 계속 내렸다가 올렸다가 해야됩니다.

예를 들어 docker compose up으로 프로젝트를 띄운다고 합니다.

그러면 백엔드 (예: fastAPI)코드가 바뀔때마다 적용을 시키려면 결국 docker compose down 이후 docker compose up --build로 빌드를 다시 하고 올리는 행위를 무한 반복 합니다.

물론 저렇게 안해도 되지만 그러시는분들이 많더라구요

사람이 비버도 아니고 내렸다가 빌드했다가 올렸다가를 무한 반복하면 행복하겠습니까?

저희도 좀 행복하게 살아야죠?

해결방법

진짜 재미있었는데

결국 답이 있었습니다. 바로 2023년 10월에 도커에서 새로운 기능을 출시합니다.

바로 (이 글의 제목이 되는) Docker Compose Watch 기능입니다.
참고

도커 컴포즈 워치는 컴포즈 파일내 watch라는 곳에 특정 파일 시스템이 바뀔때 어떤 행동을 취할지 자동화 할수있는 툴이라고 생각하면 됩니다.

예시를 한번 들어봅시다:

대충 "hello world"를 찍는 fastAPI main.py가 있다고 합시다

# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}

그리고 이걸 uvicorn main:app --host 0.0.0.0 --port 8000 --reload로 실행하면 코드가 바뀔때 자동으로 서버가 다시 실행 되면서 업데이트 됩니다.

그리고 아래와 같은 Dockerfiledocker-compose.yml을 작성을 한다고 합시다.

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt requirements.txt

RUN pip install -r requirements.txt

COPY . .
# docker-compose.yml

version: '3.7'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
    volumes:
      - .:/app
    ports:
      - 8000:8000

그러면 아래와 같이 아주 잘 돌아갑니다.

하지만 여기서 "Hello World" 뿐만 아니라 /test로 들어가면 "Hello Test"를 찍고 싶어 아래와 같이 main.py를 수정해봅시다.

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}


@app.get("/test")
async def root():
    return {"message": "Hello Test"}

그러면 fastAPI같은 경우에는 --reload 기능 덕분에 알아서 자동으로 업데이트가 되어 컨테이너와 로컬 환경이 sync될때 앱이 다시 실행되면서 업데이트가 됩니다.

하지만 예외가 있습니다. 바로 requirements.txt 입니다.

requirements.txt가 바뀌는 경우에는 꼼짝없이 다시 빌드를 해줘야 됩니다.

이런 경우에 바로 Docker Compose Watch를 사용하면 편합니다.

컴포즈 파일을 아래와 같이 수정 해줍시다.

version: '3.7'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
    ports:
      - 8000:8000
    develop:
      watch:
        - action: rebuild
          path: requirements.txt
          ignore:
            - .venv
        - action: sync
          path: ./main.py
          target: /app/main.py

이런 경우 이제 requirements.txt에 새로운 라이브러리가 추가가 되면 docker compose build가 자동으로 실행되서 업데이트가 됩니다.

그리고 이 기능을 사용하고 싶을때는 docker compose up이 아닌 docker compose watch로 실행하면 됩니다.

배포환경에서 사용을 하기 싫으시면 그냥 docker compose up으로 실행하면 develop.watch: 아래있는 부분들은 무시 됩니다.

main.py에 import requests를 추가해보겠습니다

당연히 존재하지 않으니 터집니다.

한번 requests 라이브러리를 설치하고 requirements.txt에 업데이트 해보겠습니다. 빌드는 docker compose watch가 하는지 확인을 해봅시다.

처음에 Syncing "web" 로그는 제가 import requests를 추가 했을때 나온 로그고 그 다음 Rebuilding Servicerequirements.txt를 업데이트 하니 생긴 로그입니다.

그리고 보시다시피 컨테이너가 자동으로 빌드 됐죠?

그리고 컨테이너 로그에서도 더이상 Requests module not found가 안뜹니다.

결론

나름 제한적으로 사용이 되긴 하지만 유용한 기능인거에는 틀림없습니다. 특히 자바스크립트나 파이썬 기반 프레임워크들이 아닌 자바나 고 같은 컴파일 언어들은 업데이트를 할때마다 빌드를 다시 해줘야되는데 그럴때 rebuild기능을 켜두고 개발을 하면 많이 편하겠죠?

뭐 필요없다고 생각하시면 안쓰셔도 괜찮습니다.

장난입니다

하지만 한명이라도 이 기능으로 도움이 된다면 좋을꺼같습니다!

릴스와 쇼츠같은 숏폼의 익숙한 시대인데 긴 글 읽어주셔서 감사합니다!

profile
소프트웨어 개발과 밀당하는 개발자

2개의 댓글

comment-user-thumbnail
2024년 9월 23일

이정도 길이와 맛이면 숏폼이죠 도파민 넘치는 글 항상 잘 보고 있습니다

1개의 답글