Open tangjinlog opened 6 months ago
구분 | ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ설명 ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ |
---|---|
현상 | github-actions 중, nginx-proxy-manager가 권한없음으로 열리지 않음 |
원인 | nginx-proxy-manager 폴더의 권한이 현재 사용자와 일치하지 않을 때 발생 |
해결책 | ls -ld nginx-proxy-manager 로 폴더의 권한정보를 먼저 확인한다.당시 사용자와 사용자 그룹이 root로 설정되어 있었음. 현재 EC2 접속 사용자는 ubuntu이기 때문에, 권한설정을 바꿔줘야 한다. chown -R ubuntu:ubuntu nginx-proxy-manager |
#.github/workflows/github-actions.yml
name: CI/CD using github actions & docker
on: push: branches: ['main']
permissions: contents: read
jobs: CI-CD: runs-on: ubuntu-latest steps:
- uses: actions/checkout@v3
# docker build & push to production
- name: Docker build & push to prodction
if: contains(github.ref, 'main')
run: |
docker login -u ${{ secrets.DOCKER_USERNAME}} -p ${{ secrets.DOCKER_PASSWORD}}
docker-compose build --platform linux/amd64 -t ${{ secrets.DOCKER_USERNAME}}/myeonjeobeottae .
docker push ${{ secrets.DOCKER_USERNAME}}/myeonjeobeottae
# deploy to production
- name: Deploy to production
uses: appleboy/ssh-action@master
id: deploy-prod
if: contains(github.ref, 'main')
with:
host: ${{ secrets.EC2_HOST}}
username: ${{ secrets.EC2_USERNAME}}
key: ${{ secrets.EC2_KEY}}
envs: GITHUB_SHA
script: |
cd client/
sudo su
docker-compose down
docker-compose up -d --build
docker system prune -f
- 기존 EC2 docker-compose.yml 코드
```yml
# docker 컨테이너 버전을 명시
version: '3.8'
services:
npm:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
restart: always
ports:
- 81:81 #관리포트
- 80:80 #http
- 443:443 #https
volumes:
- ./nginx-proxy-manager/data:/data
- ./nginx-proxy-manager/letsencrypt:/etc/letsencrypt
environment:
DISABLE_IPV6: 'true'
depends_on:
- myeonjeobeottae
myeonjeobeottae:
container_name: myeonjeobeottae
# 현재 경로에 이미지 빌드
build:
context: .
dockerfile: Dockerfile.yml
ports:
- '3000:3000'
image: tanglog/test:0.0.1
github-actions
에서 EC2에 접속한 후, docker-compose up -d --build로 컨테이너를 실행시키고 있는데,
EC2의 docker-compose.yml
에서는 다시 이미지 build를 하고있다.
그래서 github-actions의 과정에서 이미지를 build하고 DockerHub에 push했음에도 만들어진 이미지를 사용하지 않고 있었던 것.
그래서 만들어진 image를 pull하는 코드로 변경하면서 문제를 해결할 수 있었다.
# docker-compose.yml
...
myeonjeobeottae:
container_name: myeonjeobeottae
image: tanglog/myeonjeobeottae:latest
ports:
- '3000:3000'
docker build의 개념을 잘못 이해해서 일어난 일이었다. docker-compose.yml에서 nginx이미지는 Hub에서 받아오고, 면접어때 app 소스코드는 build하고 있었기 때문에, docker-compose build 명령어로 이미지를 만들면 두가지 컨테이너가 하나의 이미지에 담기는 줄 알았는데 아니였다. docker 이미지는 단일 컨테이너만 담을 수 있다고 한다. 결과적으로 nginx 와 면접어때 두 가지 이미지를 DockerHub에서 pull 받아오고 그걸 docker-compose up으로 한 번에 실행시켜야 하는 것이다.
CI/CD 구축하고 난 후 EC2안에서 clone을 안받아도 되니, 구조가 매우 깔끔해져서 만족한다!
기존 src를 비롯한 각종 config 파일들을 제거하고, nginx 폴더와 docker-compose.yml, env 파일만으로 배포할 수 있게 되었다.
github-actions 진행중에 DockerFile
을 못 찾아 CI/CD가 실패했다.
docker build --platform linux/amd64 -t ${{ secrets.DOCKER_USERNAME}}/myeonjeobeottae .
DockerFile을 명시하지 않아도, DockerFile
을 기본으로 찾기때문에 에러가 발생한다는게 이해가 되지 않았다.
찾아보니 현재 DockerFile.yml로 yml확장자를 달면 DockerFile이 아니기 때문에, 감지를 못한다고 한다.
DockerFile.yml
-> DockerFile
로 변경해결 해결
무한 실패
저번 트러블슈팅에서 docker-compose.yml
에서 DockerHub의 이미지를 pull 받는 것으로 최신 코드를 반영할 수 있게 되었다.
하지만 처음 한 번만 최신 코드가 반영되었고 그 다음 부터는 최신 코드가 반영되지 않은채 그냥 CI/CD가 성공했다.
// 최신 코드가 반영되는지 테스트
<Modal>
<Modal.Overlay />
{/* <Modal.Title>라우터 감지 모달</Modal.Title> */}
<Modal.Title>제목 바꾼다</Modal.Title> // 최신 코드가 반영되는지 확인차 바꾼 제목
<Modal.CancelButton>취소</Modal.CancelButton>
<Modal.ExecuteButton unBlockingWithCallback={unBlockingWithCallback}>
나가기
</Modal.ExecuteButton>
</Modal>
타이틀을 임의로 바꾼다음 CI/CD에 성공했는데 배포된 앱에서 안바뀌었다.
지금까지 바꾼 코드가 하나도 반영이 안됬다는 뜻.. CI/CD 성공만보고 착각했다!
어쨌든 문제의 원인 파악을 위해 또 의식의 흐름을 따라가본다.
//github.actions.yml
...
# deploy to production
- name: Deploy to production
uses: appleboy/ssh-action@master
id: deploy-prod
if: contains(github.ref, 'test')
with:
host: ${{ secrets.EC2_HOST}}
username: ${{ secrets.EC2_USERNAME}}
key: ${{ secrets.EC2_KEY}}
envs: GITHUB_SHA
script: |
sudo su
docker-compose down
docker-compose up -d --build // 컨테이너를 올리는 부분
docker system prune -f
github-actions.yml
에서 EC2
에 접속 후 docker-compose up -d --build
로 컨테이너를 한번에 올리고있다.
의미는 다음과 같다.
docker-compose.yml에 정의된 해당 서비스 이미지를 허브에서 해당 이미지의 항상 최신버전으로 업데이트한다(pull받는다). 이미지의 태그가 같아도 pull한다.
docker-compose pull과 마찬가지로 이미지를 새로 받는다. 만약 로컬에 이미지가 없거나 이미지의 Tag가 다를 때만 새로운 이미지를 다운로드 한다. 즉, 로컬에 이미지가 있고, 태그가 같으면 새로 다운로드하지 않는다. pull 커맨드와의 차이점은 pull은 이미지를 다운 받는게 역할의 전부이고, docker-compose up
은 다운받음과 동시에 컨테이너를 실행하는 역할까지 한다. 즉, 이 명령어를 쓴다면 docker-compose pull은 생략이 가능하다.
[!IMPORTANT] 따라서, 나는 계속해서
myeonjeobeottae:latest
로 이미지를 생성했기때문에, 같은 이미지로 판단하고 이미지를 새롭게 받지 않았던 것이다!
원인은 알았지만, 나는 태그를 latest로 계속 유지하고싶었고, nginx 이미지를 추가로 사용하고있었기 때문에, 다음과 같이 코드를 수정했다.
// github-actions.yml
...
script: |
sudo su
docker system prune -f
docker-compose down
docker-compose pull
docker-compose up -d --build
명시적으로 docker-compose pull
을 해줌으로써 허브에서 이미지를 무조건 다운받고, docker-compose up -d --build
로 docker-compose.yml
에 명시된 서비스 전부를 한번에 올린다. 이렇게 해결!
보안을위해 카카오 OAuth 로그인할 때, client_id와 redirect_uri를 환경변수에 담아 참조해서 사용하고있다.
NEXT_PUBLIC_REST_API_KEY=클라이언트 아이디
NEXT_PUBLIC_REDIRECT_URI=리다이렉트 콜백 uri
브라우저에서 환경변수를 참조하기위해 NEXT_PUBLIC 접두사를 달아줬다.
그런데, 로그인 버튼 클릭 시, 해당 값이 undefined로 나타났다.
https://kauth.kakao.com/oauth/authorize?client_id=
undefined&redirect_uri=
undefined&response_type=code
현재 배포는 EC2컨테이너에서 DockerHub의 이미지를 pull받아서 컨테이너를 올리고있는데, docker-compose.yml
과 같은 위치에 .env.production
환경변수 파일이 있다.
나는 Docker Image를 기반으로 컨테이너를 올리더라도 같은 depth에 있는 .env.production
을 참조할 수 있다고 생각했는데, 실제로는 참조가 안되고있었던 것이다.
검색해보니, docker 이미지 빌드시에 동적으로 환경변수를 할당할 수 있는 방법이 있다고한다.
docker build --build-arg [환경변수 key]=[환경변수 value]
위 방법으로 사용할 환경변수 두 개를 github action secret으로 만들어 적용해봤다.
//github-actions.yml
...
- name: Docker build & push to prodction
if: contains(github.ref, 'test')
run: |
docker login -u ${{ secrets.DOCKER_USERNAME}} -p ${{ secrets.DOCKER_PASSWORD}}
docker build --build-arg NEXT_PUBLIC_REST_API_KEY=${{ secrets.REST_API_KEY }} --build-arg NEXT_PUBLIC_REDIRECT_URI=${{ secrets.REDIRECT_URI }} --platform linux/amd64 -t ${{ secrets.DOCKER_USERNAME}}/myeonjeobeottae .
docker push ${{ secrets.DOCKER_USERNAME}}/myeonjeobeottae
이렇게 docker 이미지 build시에 환경변수를 넣어주고,DockerFile에서 ARG 커맨드로 해당 환경변수를 가져와서 사용하면 된다고한다.
//DockerFile
...
ARG NEXT_PUBLIC_REST_API_KEY
ENV NEXT_PUBLIC_REST_API_KEY=${NEXT_PUBLIC_REST_API_KEY}
ARG NEXT_PUBLIC_REDIRECT_URI
ENV NEXT_PUBLIC_REDIRECT_URI=${NEXT_PUBLIC_REDIRECT_URI}
그런데 여전히 배포 앱에서 환경변수 참조값은 undefined로 변함이 없었다..
왜일까 생각하다, build시에 환경변수를 넣어주니, DockerFile
에서 yarn build
전에 환경변수를 세팅해줘야하나 싶었다.
그래서 DockerFile을 다시보니,
//DockerFile
...
FROM base AS builder
WORKDIR /usr/src/app
COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY . .
RUN yarn build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /usr/src/app
ARG NEXT_PUBLIC_REST_API_KEY
ENV NEXT_PUBLIC_REST_API_KEY=${NEXT_PUBLIC_REST_API_KEY}
ARG NEXT_PUBLIC_REDIRECT_URI
ENV NEXT_PUBLIC_REDIRECT_URI=${NEXT_PUBLIC_REDIRECT_URI}
...
멀티스테이징 빌드로 builder
와 runner
를 구분해서 사용하고있었는데, 지금 build가 끝난 후에 runner에서 환경변수를 세팅하고 있었다!
//DockerFile
...
FROM base AS builder
WORKDIR /usr/src/app
COPY --from=deps /usr/src/app/node_modules ./node_modules
COPY . .
// build 전에 환경변수 세팅
ARG NEXT_PUBLIC_KAKAO_LOGIN_URI
RUN touch .env.production
RUN echo "NEXT_PUBLIC_KAKAO_LOGIN_URI=${NEXT_PUBLIC_KAKAO_LOGIN_URI}" > .env.production
ENV NODE_ENV production
RUN yarn build
...
[!important]
yarn build
로 앱을 빌드하기전에, 환경변수를 세팅하는 걸로 코드를 수정했다. 추가로, docker container안에 배포시에 참조할 수 있도록.env.production
환경변수 파일을 만들고, 거기에github-actions.yml
에서 넘겨줬던 환경변수를 덮어씌워줬다.
이렇게 docker 이미지가 빌드되면서 동적으로 할당된 action secret이 환경변수의 역할을 하면서 production 환경에서 브라우저가 환경변수를 참조할 수 있게 되었고, 실제 이 방법으로 문제를 해결할 수 있었다!
Feature
main
브랜치에 코드 변경이 일어나면 자동으로 EC2 서버에 배포된다.Description
feature-example
->main
mergegithub-actions
감지Docker Image build
->DockerHub Push
EC2 접속
및docker-compose
로 컨테이너 실행To do list
github-actions.yml
작성docker-compose.yml
에서 DockerHub Image를 pull 받도록 수정Troubleshooting