[도커] AWS ECS 다중컨테이너 배포

이런 디렉토리 구조를 가지고 있는 폴더에서 시작할것이다.
ECS는 동일한 태스크에 컨테이너를 추가하면, 동일한 머신에서의 실행이 보장된다.
그리고 ECS는 도커 네트워크를 생성하지 않고, 그 대신 localhost를 컨테이너 애플리케이션 코드 내부의 주소로 사용할 수 있게 해준다.

mongoose.connect(
  `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@mongodb:27017/course-goals?authSource=admin`,
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  },
  (err) => {
    if (err) {
      console.error('FAILED TO CONNECT TO MONGODB');
      console.error(err);
    } else {
      console.log('CONNECTED TO MONGODB!!');
      app.listen(80);
    }
  }
);

그래서 app.js 내의 mongodb의 연결 코드를 바꿔줘야한다. 하지만 이는 로컬 환경과 프로덕션 환경을 동일

mongoose.connect(
  `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@${process.env.MONGODB_URL}:27017/course-goals?authSource=admin`,
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  },
  (err) => {
    if (err) {
      console.error('FAILED TO CONNECT TO MONGODB');
      console.error(err);
    } else {
      console.log('CONNECTED TO MONGODB!!');
      app.listen(80);
    }
  }
);

환경 변수를 활용하여 동일한 환경을 만들어준다.
그리고 backend.env 파일에서

MONGODB_USERNAME=june
MONGODB_PASSWORD=secret
MONGODB_URL=mongodb

 

이렇게 MONGODB_URL 을 추가해주고 값에 로컬인 mongodb를 넣어준다.
AWS ECS를 통해 컨테이너를 실행할 때, 변수에 그에 맞는 값을 제공하므로, 로컬 개발 환경과 프로덕션 환경에서 모두 작동한다.

docker build -t node-app ./backend

명령으로 빌드를 해준다.

도커 허브에 새로운 레포지토리를 생성한다.

docker build -t node-app ./backend
docker tag node-app crowssnest/node-app       
docker push crowssnest/node-app

backend 폴더 안에 있는 dockerfile을 기반으로 이미지를 빌드를 해주고, tag로 레포지토리 이름으로 바꿔 준 뒤 도커 허브로 푸쉬를 해준다.

도커허브에 새로만든 레지스트리에 새로운 이미지가 올라온것을 확인할 수 있다.


ECS 백앤드 컨테이너 생성

클러스터 생성

AWS 콘솔에서 ECS에 들어간 뒤 클러스터 생성을 눌러준다.

네트워킹 전용 클러스터을 선택해준다.

다음 단계를 누르고, 클러스터 이름을 지정해준다.
그리고 vpc 생성을 눌러준다. 디폴트로 설정한다.
그러면 AWS가 클러스터의 모든 컨테이너에 대해 이 클러스터를 프라이빗 클라우드로 설정한다

그 후 생성을 누르면, 클러스터가 생성된다.(생성하는데 시간이 좀 걸린다) 클러스터는 컨테이너의 주변 네트워크일 뿐이다.


태스크 생성

서비스는 태스크를 기반으로 하니, 먼저 태스크를 만들어야 한다. 작업 정의 (태스크 데피니션)에서 할 수있다.

새 작업정의를 만들어 볼 것이다.

Fargate를 선택하여 컨테이너가 무한대로 확장되는 서버리스 컨테이너를 만들것이다.

태스크 정의 이름에는 원하는 이름을 넣어준다. 그리고 태스크 역할에서 ecsTaskExecutionRole을 선택해준다. 만약에 없다면 뒤로가기를 눌러서 다시 Fargate로 생성을 하기를 눌러서 태스크역할을 토글한 뒤 선택해준다.

작업 크기에서는 가장 작은걸 눌러서 비용을 절감하자... ㅎ


컨테이너 정의

컨테이너 추가를 해준다.
컨테이너 이름은 원하는것으로 한다.
그리고 이미지는 아까 도커 허브에 올린 이미지가 있는 레지스트리의 이름을 넣어준다.
포트 매핑은 이 백앤드 서버가 80번 포트 수신을 할 것이니, 80을 지정해준다.

고급 컨테이너 구성에서 환경을 좀 건드려보려고 한다.


환경변수

백앤드 도커파일에서는 npm start를 최종 명령으로 실행하고 있다.

그런다음 package.json을 보면, app.js를 시작하기 위해 nodemon을 사용하고 있다.
코드에서 무언가를 변경할 때, 실시간으로 리로드 하기 위해 로컬에서nodemon을 사용했었지만, 프로덕션에서는 nodemon을 갖지 않고, 실행중인 앱에 라이브로 코드를 제공하기 위해 바인드 마운트를 할 필요도 없다.
그래서 npm start가 아닌 node app.js로 app.js를 실행하고 싶다.
쉼표로 구분해서 입력한다. node,app.js

그리고 이제 환경 변수를 지정해야한다. 도커파일에도 새로운 환경 변수를 추가해야한다.

MONGODB_URL은 현재 mongodb 연결 문자열에서 사용하고 있는 환경변수다.

따라서 ENV 정의를 도커파일에 추가한 다음

이미지를 빌드하고, 도커허브 레포지토리에 올려서 도커파일이 컨테이너 내부의 코드에서 사용되는 모든 환경 변수를 나열하도록 한다.

환경변수에 넣은 키 벨류 값을 넣어준다. (물론 프로덕션에서는 저렇게 간단하게 하면 안되겠지요...? ㅎ)
ECS에서 실행하려는 동일한 태스크에 다중 컨테이너를 추가할 때 동일한 태스크에 이 컨테이너의 일부분인 mongodb 컨테이너를 추가할 것이다. 그러면 컨테이너들은 localhost 키 밑에서 서로 통신할 수 있다.
즉 기본적으로 모든 컨테이너가 동일한 호스트에 있는 로컬 시스템을 에뮬레이트 하는것이다. 프로덕션의 AWS ECS에서 localhost를 사용하여, 동일한 태스크의 일부가 되는 다른 컨테이너에 요청을 보낼 수 있다.
그래서 프로덕션에서는 MONGODB_URL의 값으로 localhost를 사용하고, 개발중에는 localhost가 작동하지 않기 때문에, mongodb를 값으로 사용한다.

그리고 더 설정할 것 없이 추가를 하여 첫번째 backend 컨테이너를 만든다.


ECS MongoDB 컨테이너 생성

컨테이너 이름은 구분하기 쉬운이름으로 mongodb라고 지정한다.
이미지는 mongo의 공식 이미지니, mongo를 입력하면 dockerhub에서 찾아온다.
포트는 mongodb 이미지가 노출하는 디폴트 포트인 27017을 지정해준다.

mongodb 연결하기 위한 환경변수 키 벨류를 ECS에 환경변수 키 벨류에 넣어준다.

 

그리고 이렇게 내부에 두개의 컨테이너 구성이 있는 새 태스크 정의를 만들어 줄 수 있다.
생성되고 나면, 이제 이 태스크를 기반으로 서비스를 시작할 수 있다.


서비스 생성

그러기 위해서는 backend-app의 클러스터로 돌아와서 서비스 밑에 생성을 눌러줘야한다

시작 유형에서 FARGATE 그리고 작업정의는 아까 만든 백앤드 컨테이너와 mongodb 컨테이너가 들어있는 backend를 그리고 작업개수는 1로 설정한다.

그리고 vpc 구성이 나온다. (vpc는 클러스터를 생성할 때 만들어진다.) 생성된 vpc를 선택하고, 서브넷에서 선택할 수 있는 두 서브넷을 모두 추가하면된다.
그리고 publicIP의 도움으로 접근할 수 있도록, 자동 할당 퍼블릭 IP 에 ENABLED로 체크 되어있는지 확인한다.

그리고 어플리케이션 로드밸런서를 할당해준다. 이는 들어오는 트래픽을 효율적으로 처리하는 기능도있지만, 나중에 원할 경우, 커스텀 도메인을 할당하는데에도 도움이 된다.


ECS전용 애플리케이션 로드밸런서 생성

지금은 애플리케이션 로드밸런서가 만들어져있는게 없어서 생성을 해줘야한다. EC2 콘솔로 이동한다.

이름은 원하는 이름으로 설정해준다.
그리고 인터넷으로 접근할 수 있도록, internet-facing에 체크해준다.

네트워크 매핑에서 아까 ECS 서비스에서 선택했던 vpc와 같은 vpc를 확인하고 사용가능한 서브넷을 선택해서 매핑해준다.

443으로 보안접속을 하게 할 수도 있지만, 지금은 80번 포트만 오픈할 예정이다. 타겟그룹이 없으므로 타겟그룹을 생성하러 간다.


타겟그룹 생성

실행중인 컨테이너를 위해 Fargate를 사용하기 때문에 타깃 타입으로 IP를 선택한다.

그리고 넘어간 Register targets에서는 아무것도 할 필요가 없다. 왜냐하면, AWS ECS는 실행 중인 컨테이너를 여기에서 대상으로 자동 등록하기 때문이다.

그렇게 타겟그룹을 하나 만들고

로드밸런서에 그 타겟그룹을 붙여준 뒤

로드밸런서를 생성해주면 된다.

생성이 완료되면, 다시 ECS로 돌아와 로드밸런서이름의 로드 밸런서 목록을 새로고침 하여 방금 생성한 로드 밸런서를 선택해준다.

그리고 로드밸런싱할 컨테이너에서 로드밸런서에 추가된 backend 80:80을 선택하여 로드밸런서에 추가를 눌러준다.
아까 만든 타겟 그룹을 붙여준다.

그리고 다음 단계로 가면 오토 스케일링 선택이 있는데, 굳이 지금은 이거까지 필요가 없다. 그래서 다음단계로

검토를 하고 내가 선택한 사항들이 맞으면 서비스 생성을 한다 .

생성을 시키고, 클러스터 안에서 서비스를 보면 컨테이너가 pending 시작되고 있음을 볼 수 있다.

가... 되어야 하는데 계속 서비스가 중지 되었다가 러닝이 되었다가 반복된다.
이 중지와 재시작은 로드 밸런서에 의해 수행되는데, 로드 밸런서는 요청을 리디렉션하는 서비스의 상태를 확인하고, 이 서비스가 비정상임을 발견하면, 서비스를 종료하고 다시 시작하기 때문이다.


오류 해결

이 경우 타겟그룹에서 문제가 있다... EC2 서비스 페이지 타겟그룹 영역에서 이전에 생성한 타깃 그룹을 찾으면, 헬스체크 기능이 있다. 이 헬스체크의 디폴트로 로드 밸런서는 배포된 서비스의 '/'로 요청을 보낸다.

하지만, 지금 내가 올린 서비스는 /goals로 요청을 처리해서 다른 곳이다. 그래서 '/'로 전송된 요청은 비정상으로 취급되는 404 오류로 응답을 하는 것이다.

그리고 Edit을 누르면 수정할 수 있는데, 요청이 성공 코드로 응답할 서비스의 엔드포인트로 전송되도록 '/goals' 로 변경을 해준다.

그리고 로드 밸런서에서도 보안그룹에 디폴트만 넣어놨는데, goals 보안그룹을 넣어줘야한다. ECS서비스에도 사용 중인 보안 그룹을 첨부한 다음 Save를 한다.

JUNE .

20'S LIFE IN SYDNEY and BUSAN

    이미지 맵

    DevOps Study/Docker 다른 글

    이전 글

    다음 글