-
React+Vite CI/CD with Infisical클라우드 2025. 5. 23. 20:59
그동안 열심히 해뒀던 Infisical에 환경변수 통합 저장과 React+Vite nginx로 수동배포하는 걸 합쳐서
Infisical로 React+Vite 프로젝트 CI/CD 파이프라인을 구축해보겠습니다.
CI/CD 파이프라인의 순서는 다음과 같습니다.
우선 CI 파이프라인의 순서입니다.
- pull_request가 올라왔을 때, 파이프라인이 돌게 한다.
- 레포지토리에서 소스코드를 가져온다.
- Infisical CLI를 설치한다.
- Infisical에서 환경변수를 가져온다.
- 호환되는 Node 버전을 설정한다.
- 의존성을 설치한다.
- 환경변수를 담아 React를 빌드하고 결과물을 dist 경로에 올린다.
그다음 CD 파이프라인의 순서입니다.
- dist 경로에 있는 빌드 결과물을 다운로드 받는다.
- 키를 저장하고 VM의 IP를 known_hosts에 등록한다.
- 기존 빌드 파일을 삭제하고 새로 경로를 만든 후에, dist에 새로 빌드한 파일을 새로운 경로에 넣습니다.
- 그 다음 nginx가 새로운 경로를 참조하도록 myapp.conf 파일에 써두고, 미리 만들어둔 인증서를 통해 HTTPS 설정을 합니다.
name: Frontend CI/CD # 풀리퀘 할때 트리거 on: pull_request: branches: [ dev ] jobs: # 빌드 과정 build: runs-on: ubuntu-latest steps: # 레포에서 Github Runner 서버로 코드 전송 - uses: actions/checkout@v3 # Infisical CLI 설치 - name: Install Infisical CLI run: | curl -1sLf '<https://artifacts-cli.infisical.com/setup.deb.sh>' | sudo -E bash sudo apt-get update && sudo apt-get install -y infisical # Infisical에서 .env 가져오기 - name: Fetch .env from Infisical run: | infisical export \\ --env=${{ secrets.INFISICAL_ENV_DEV }} \\ --projectId=${{ secrets.INFISICAL_PROJECT_ID }} \\ --token=${{ secrets.INFISICAL_DEV_TOKEN }} \\ --format=dotenv \\ --domain=${{ secrets.INFISICAL_API_URL }} \\ > $GITHUB_WORKSPACE/.env # 프로젝트에서 사용하는 노드 버전 설치 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '23.11.0' cache: 'npm' # 의존성 설치 - run: npm ci # 빌드 - run: npm run build # 빌드 결과를 다른 job(deploy)에서 사용하기 위해 아티팩트(임시 스토리지)에 업로드 - name: Upload dist uses: actions/upload-artifact@v4 with: name: dist path: dist/ # test: # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@v3 # - uses: actions/setup-node@v3 # with: # node-version: '18' # cache: 'npm' # - run: npm ci # - run: npm test deploy: needs: [build] # test runs-on: ubuntu-latest steps: # build에서 업로드 했던 빌드 결과 아티팩트로부터 다운로드 - name: Download dist uses: actions/download-artifact@v4 with: name: dist path: dist/ # SSH 접속 설정 - name: Setup SSH run: | mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.GCP_VM_IP }} >> ~/.ssh/known_hosts # build 결과 SCP로 VM에 업로드 - name: Deploy build files run: | ssh ${{ secrets.GCP_VM_USER }}@${{ secrets.GCP_VM_IP }} "rm -rf /home/${{ secrets.GCP_VM_USER }}/fe && mkdir -p /home/${{ secrets.GCP_VM_USER }}/fe" scp -r dist/* ${{ secrets.GCP_VM_USER }}@${{ secrets.GCP_VM_IP }}:/home/${{ secrets.GCP_VM_USER }}/fe # Nginx 설치 후 필요한 작업 수행 # 1. 기본 설정파일 삭제 # 2. www-data가 접근할 수 있게 o+x 명령 수행 # 3. sites-available/myapp.conf에 설정파일 생성(빌드 결과 위치 참조, SSL 인증서 설정) - name: Setup Nginx and Configure Site run: | ssh ${{ secrets.GCP_VM_USER }}@${{ secrets.GCP_VM_IP }} 'sudo apt update && sudo apt install -y nginx && \\ sudo rm -f /etc/nginx/sites-available/default && \\ sudo rm -f /etc/nginx/sites-enabled/default && \\ sudo chmod o+x /home/${{ secrets.GCP_VM_USER }} && \\ sudo chmod o+x /home/${{ secrets.GCP_VM_USER }}/fe && \\ echo "server {" | sudo tee /etc/nginx/sites-available/myapp.conf && \\ echo " listen 443 ssl;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " server_name {server_name};" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " ssl_certificate /etc/letsencrypt/live/{server_name}/fullchain.pem;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " ssl_certificate_key /etc/letsencrypt/live/{server_name}/privkey.pem;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " location / {" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " root /home/${{ secrets.GCP_VM_USER }}/fe;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " index index.html index.htm;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " try_files \\$uri \\$uri/ /index.html;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " }" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "}" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "server {" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " listen 80;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " server_name {server_name};" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo " return 301 https://\\$host\\$request_uri;" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ echo "}" | sudo tee -a /etc/nginx/sites-available/myapp.conf && \\ sudo ln -sf /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/myapp.conf && \\ sudo nginx -t && sudo systemctl restart nginx'
이런 파이프라인입니다.
제가 애먹었던 부분만을 정리해서 설명하겠습니다.
첫번째는, Infisical에서 환경변수 불러오는 법 이였습니다.
우선 infisical에서 환경변수 가져오는 방법은
secrets.INFISICAL_ENV_DEV 이거는 그냥 프로젝트 SLUG 가져오는 것인데 이건 infisical login 하고 Organization과 Project 설정하고 그다음에 환경 설정해서 가져올 때 쓰이는 것 같습니다.
그래서 빼도 되는 항목인 듯 보입니다.
그리고 secrets.INFISICAL_PROJECT_ID 이거는 브라우저에서 해당 프로젝트를 들어갔을 때 보이는 부분인데
{server_domian_name}/secret-manager/{PROJECT_ID}/overview 여기서 보이는 PROJECT_ID를 가져오면 됩니다.
그리고 제일 중요한 secrets.INFISICAL_DEV_TOKEN인데요, 이건 Project - Access Control - Service Tokens 로 들어가서 Create-Token을 누르고
이름, 환경 설정하고 Secret Path는 그대로 놔두셔도 됩니다. 아마 프로젝트에 폴더를 만드셨으면 그 경로로 해야할 겁니다. 그리고 만료일을 설정하고 만들면 Token이 발급되는데 이건 한번만 보이니 안전한 곳에 잘 저장해두고 두고두고 쓸겁니다.
두번째로 중요한 부분이 secrets.INFISICAL_API_URL 인데요, 이 부분을 빼먹고 했다가 계속 서비스 토큰을 찾을 수 없다는 오류가 나왔습니다.
그 이유는 저는 셀프호스팅을 하였는데요, 이 부분을 설정하지 않는 다면 Infisical Cloud로 서비스 토큰을 찾으러 가는겁니다. 그래서 찾을 수 없다는 오류가 나왔던 것이고, —domain 옵션을 통해 <domain_name>/api 경로를 지정해주면 서비스 토큰이 잘 적용됩니다.
그리고 환경변수를 $GITHUB_WORKSPACE 경로에 저장하는데 이는 첫번째에 프론트 레포에 있는 소스코드를 체크아웃 해간 경로에 저장한다는 것입니다.
그 이유는 프론트엔드 루트 디렉토리 (package.json과 같은 높이의 경로)에 .env파일이 있어야 빌드할 때 환경변수가 잘 적용되기 때문입니다.
두번째는, 이번에는 애먹지 않았고 이전에 애먹었던 내용인데요
chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.GCP_VM_IP }} >> ~/.ssh/known_hosts
이 부분이였습니다. 여기서 known_hosts에 VM의 IP를 등록해두지 않으면
yes/no의 입력 받는 부분이 뜹니다. 이런 부분은 파이프라인 내에서 입력받을 수 없기 때문에 미리 등록을 해줘서 yes/no가 뜨지 않게 해주어야합니다.
예전에는 Github Runner 개념이 없어서 VM 내부에 들어가서 known_hosts 등록해두고, 로컬에 등록해두고 왜 안되지? 라는 생각을 했었지만 Github Runner → VM으로 ssh 접근한다는 것을 안 이후에는 러너에 known_hosts 등록을 해주어 해결하였습니다.
세번째는, 권한 설정 문제였습니다.
저는 /home/user/fe 디렉토리를 만들고 그 아래에 빌드 파일들을 넣었는데 자꾸 500에러가 나왔던 것이였습니다.
저는 500에러라길래 환경변수가 잘 적용이 안되어 백엔드랑 연결할 수 없어서 환경변수 문제인가 싶어서 환경변수쪽을 엄청 건드리고 있었는데, 수동으로 할 때, 환경변수가 없어도 화면이 잘 나와서 무슨 문제지? 하고 한참을 헤맸습니다..
문제가 뭐였냐면, nginx는 기본적으로 www-data 사용자로 실행됩니다. 그래서 /var/www/data 같은 nginx 전용 디렉토리에 빌드 파일들을 넣었던 것이였습니다.
근데 저희 팀은 구분하기 좋게 사용자의 홈 디렉토리에 빌드파일을 넣어서 관리하자고 얘기가 되었고, www-data가 /home/user/fe 디렉토리에 접근하고 그 내부에 파일들을 볼 수 있게 user 디렉토리, fe 디렉토리에 실행권한을 주어(+x) 빌드 파일을 읽을 수 있게 만들었습니다.
느낀 점
이렇게 Infisical에서 환경변수를 로드하고 적용해서 CI/CD 파이프라인을 완성했습니다.
그동안 Infisical 셀프 호스팅을 하느라 엄청 고생했는데, 뭔가 결과를 하나 만든거 같아 뿌듯합니다.
이후 CI/CD 파이프라인 전부가 PR 때 도는게 맞지 않는 거 같다는 생각이 들어서 CI 파이프라인과 CD 파이프라인을 분리해서 PR 때는 CI만 돌고 Merge 될때는 CD만 도는 방식으로 리팩토링 할 예정입니다.
'클라우드' 카테고리의 다른 글
React+Vite nginx로 GCP VM(우분투)에 수동배포 하는 방법 (0) 2025.05.22 Infisical으로 React 환경변수 적용하기 (0) 2025.05.20 GCP VM에 Infisical docker-compose로 설치 후 팀원 초대 (0) 2025.05.19 docker 멀티 아키텍처 빌드하는 방법 (0) 2025.05.16