dev-deploy 스크립트 (만약, docker-compose를 쓴다 할때)

- name: Copy compose files to EC2
  uses: appleboy/[email protected] # Secure Copy - SSH 연결을 이용해 해당 repo의 파일을 복사한다.
  with:
    host: ${{ secrets.EC2_HOST }}
    username: ${{ secrets.EC2_USERNAME }}
    key: ${{ secrets.EC2_KEY }}
    source: "docker-compose.yml,low-mem.cnf" # low-mem.cnf는 mysql 용량을 줄이기 위한 설정 파일
    target: "/home/${{ secrets.EC2_USERNAME }}/app" # EC2안의 디렉터리 경로. scp가 파일을 어디에 놓을지 지정하는 값

- name: Application Run
  uses: appleboy/[email protected] # Secure Shell - 원격 서버에 접속해서 script 명령어를 실행한다.
  env:
    ENV_FILE: ${{ secrets.ENV_FILE }}
  with:
    host: ${{ secrets.EC2_HOST }}
    username: ${{ secrets.EC2_USERNAME }}
    key: ${{ secrets.EC2_KEY }}
    envs: ENV_FILE
    script: |
	    # 중간에 실패하면 중단한다.
	    set -e
	    
	    # scp의 target과 동일한 경로여야 compose가 파일을 찾는다.
      cd /home/${{ secrets.EC2_USERNAME }}/app
      
      # EC2 내부에서 도커 허브 로그인 (private 레포를 pull 하기위해)
      sudo docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
            
      # .env 파일은 소유자만 읽고 쓸 수 있게 한다. (600)
			printf '%s\n' "$ENV_FILE" > .env
			chmod 600 .env # chmod는 파일의 접근 권한을 바꾸는 명령어
			
			sudo docker compose pull app # app 컨테이너 안에 새 이미지 ID를 받아온다.
			sudo docker compose up -d app # compose 파일에 적힌 상태와 실제 실행 중인 컨테이너를 비교해서, 차이가 나는 것만 새로 만드는 명령어. -d(detached)는 백그라운드 실행
			sudo docker image prune -f # 교체된 구 이미지를 정리한다. 명령이 없으면 EC2 디스크가 부족해진다.

docker-compose.yml 예시

name: juby # 컨테이너에 태그(juby)를 붙인다. "이 컨테이너, 볼륨, 네트워크는 한 묶음이라는 소속 표시이다.

services:
  app:
    image: ${DOCKERHUB_USERNAME}/${PROJECT_NAME}:latest # 이미지 이름
    container_name: app
    restart: unless-stopped # stopped는 사용자가 직접 멈춘 경우를 말한다. 즉, "내가 명시적으로 멈추지 않았다면 재시작"
    ports:
      - "8080:8080" # 호스트의 nginx는 compose 네트워크 안을 못 보기 때문에, 루프백에 바인딩한다.
    env_file:
	    - .env # dev-deploy.yml에서 만든 env 파일
    environment:
      DB_URL: ${DB_URL}
      DB_USERNAME: ${DB_USERNAME}
      DB_PASSWORD: ${DB_PASSWORD}
      SPRING_PROFILES_ACTIVE: dev
      TZ: Asia/Seoul
      # 힙 제한
      JAVA_TOOL_OPTIONS: "-Xms256m -Xmx384m -Xss512k -XX:MaxMetaspaceSize=160m -XX:+UseSerialGC"
    depends_on:
      mysql:
        condition: service_healthy # mysql이 완전히 뜬 뒤에 app이 시작한다. 이게 없으면 재부팅 직후 app이 커넥션 실패(mysql과)로 죽는다.
      redis:
        condition: service_started
    networks:
	    - juby-net

  mysql:
    image: mysql:8.0
    container_name: mysql
    restart: unless-stopped
    ports:
	    - "127.0.0.1:3306:3306" # EC2 내부에서만 접근 가능하고, 외부에서는 SSH를 통해서만 닿는다.
    environment:
	    # mysql 공식 이미지가 인식하는 초기화용 환경변수이다. 
	    # 컨테이너가 처음 기동할 때 (볼륨이 비어있을때) 이 이름으로 DB를 하나 만들어 준다.
	    # MYSQL_USER에게는 MYSQL_DATABASE에 대한 전체 권한이 자동으로 부여된다.
	    # 최초 1회만 동작한다. 볼륨에 데이터가 이미 있으면 무시된다.
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}  # root 비밀번호 (임의설정)
      MYSQL_DATABASE: ${MYSQL_DATABASE} # 생성할 DB 이름 (juby)
      MYSQL_USER: ${DB_USERNAME} # 생성할 일반 계정 (kkangmen)
      MYSQL_PASSWORD: ${DB_PASSWORD} # 그 계정의 pw
      TZ: Asia/Seoul
    volumes:
      - mysql-data:/var/lib/mysql
			- ./low-mem.cnf:/etc/mysql/conf.d/low-mem.cnf:ro
    healthcheck:
      test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u root -p\"$$MYSQL_ROOT_PASSWORD\" --silent"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 40s
    networks:
      - juby-net

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data

volumes:
  mysql-data:
  redis-data:
  
networks:
	juby-net:
		name: juby-net

정상 기동 확인 방법

sudo docker compose ps # 둘 다 Up, mysql은 (healthy)
sudo docker compose logs app # 기동 로그

image.png