# Docker로 다중 컨테이너 애플리케이션 구축하기

# Docker로 다중 컨테이너 애플리케이션 구축하기

여기서는 프론트&lt;-&gt;백&lt;-&gt;DB 컨테이너의 상호작용과 configuration에 대해 설명한다. <s>실무와는 괴리가 있겠지만</s>

세 컨테이너를 구축할 것인데 이들이 갖춰야 할 조건은 다음과 같다:

`DB`:

* 데이터가 유지되어야 하고
    
* Access제한을 걸어놔야한다. `백엔드`:
    
* 데이터가 유지되어야 하고
    
* 실시간 코드 업데이트가 반영되도록 할 것이다. `프론트`:
    
* 실시간 코드 업데이트가 반영되도록 한다.
    

따라서 리액트 프론트, 노드 백엔드, 몽고DB가 있는 상황에서 이들을 도커화하여 연결해보는 작업에 대한 내용을 작성할 것이다.

---

# 백엔드, 프론트, DB 컨테이너 통신

## Mongo DB 도커화

현재 백엔드가 도커화되지 않은 상황에서

```js
mongoose.connect(
  'mongodb://localhost:27017/course-goals',
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  },
```

DB는 컨테이너로 돌리고자 할 때 위 코드를 유지하면서도 돌아가게 하려면 포트 27017을 노출해야 한다.

이전에 도커허브에 몽고DB 이미지가 존재한다고 하였다. 따라서 몽고DB이미지를 가지고 와서

```bash
docker run --name mongodb --rm -d -p 27017:27017 mongo                                                                                         ─╯
1ba11b5201cac261ecc24481913d650f67168fe4c1c228ea021f07f9557fe891
```

로 DB가 로컬에서 돌고 있지 않아도 로컬 백엔드와 통신할 수 있다.

## Node 백엔드 도커화

아까 컨테이너의 몽고DB와 호스트 머신의 백엔드는 통신 가능했는데, 제대로 도커파일을 작성해도 노드 코드 그 상태로는 연결에 실패한다.

노드도 컨테이너에서 돌고 있는데 몽고DB는 호스트 머신의 백엔드와 통신하고 있기 때문이다.

이를

```js
mongoose.connect(
  'mongodb://host.docker.internal:27017/course-goals',
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  },
```

로 백엔드와 몽고DB가 다시 통신이 가능해진다.

이미지를 다시 빌드하고 프론트와 통신하는 포트를 노출하여 돌려주면

```bash
docker run --name goals-backend --rm -d -p 80:80 goals-node
```

프론트와 정상적으로 통신 가능해진다.

## 프론트 도커화

프론트도 마찬가지로 도커파일을 작성하여 이미지를 빌드하고 컨테이너를 돌리면 되는데,

다만 리액트 컨테이너를 실행할 때 주의점이 있는데

`docker run`에 `-t` 옵션을 붙이지 않는 경우 실행 후 바로 프로그램이 죽어버릴 수 있다.

```bash
❯ docker run --name goals-frontend --rm -p 3000:3000  goals-react                                                                                ─╯

> frontend@0.1.0 start
> react-scripts start

ℹ ｢wds｣: Project is running at http://172.17.0.3/
ℹ ｢wds｣: webpack output is served from
ℹ ｢wds｣: Content not from webpack is served from /app/public
ℹ ｢wds｣: 404s will fallback to /
Starting the development server...
```

기본적으로 터미널 환경을 가정하고 돌아가서, 표준입력이 닫혔다고 간주하고 그대로 프로세스가 죽는 것으로 추측할 수 있다.

따라서 이런식으로

```bash
docker run --name goals-frontend --rm -t -p 3000:3000  goals-react
```

`-t` 옵션을 붙여주면

```bash
Compiled successfully!

You can now view frontend in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://172.17.0.3:3000

Note that the development build is not optimized.
To create a production build, use npm run build.
```

정상적으로 작동함이 확인 가능하다.

---

## 도커 네트워크로 효율적인 통신 추가하기

이렇게 하나의 서비스를 각각 DB, 프론트, 벡엔드의 개별 컨테이너로 생성하였는데, 위에서는 호스트 머신의 로컬을 이용하여 세 컨테이너가 통신하고 있지만 다른 방법이 있다.

이전에 도커 네트워크를 쓰면 다중 컨테이너끼리 통신할 수 있도록 설정 가능하다고 했었다. 따라서 이러한 경우 네트워크를 활용해볼 수 있다.

네트워크를 사용하면 더 이상 포트를 노출해줄 필요가 없다.

우선 몽고DB를 네트워크 아래에서 돌리고

```bash
 docker run --name mongodb --rm -d --network goals-net mongo                                                                                    ─╯
1626781cb852a12610a229756544cd61e6fba623e64ac8a42629b3ed845cbe8d
```

다음으로 백엔드와 프론트엔드를 돌릴 차례인데

백엔드를 도커화하면서 호스트 머신의 로컬에 접근 가능하도록 `host.docker.internal`을 작성해주었지만 DB가 네트워크로 통신하므로 해당 부분을 DB 컨테이너의 이름으로 바꿔줘야한다.

```js
mongoose.connect(
  'mongodb://mongodb:27017/course-goals',
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  },
```

와 같이 될 수 있겠다.

프론트엔드의 경우에도 코드를 수정해줄 필요가 있는데 이제 네트워크 아래에서 백엔드 컨테이너로 통신하면 되므로 [`localhost`](http://localhost) 대신 백엔드 컨테이너의 이름을 적어주게 되면

...

정상적으로 작동하지 않을 것이다.

프론트엔드 코드는 컨테이너만으로 작동하는 게 아니라 브라우저 위에서 돌게 되는데 프론트엔드 코드를 실행할 브라우저는 백엔드 컨테이너에 대한 정보가 없기 때문이다. 따라서 [`localhost`](http://localhost)는 그대로 놔두어야 하고, 네트워크에서 실행할 필요도 없다.

그러나 백엔드와도 통신해야하기 때문에 백엔드 컨테이너는

```bash
docker run --name goals-backend --rm -d -p 80:80 --network goals-net goals-node
```

프론트와의 통신을 위한 80번 포트를 열어놔야한다.

---

# DB에 데이터 지속성 추가하기

이제 DB, 백엔드, 프론트엔드 컨테이너들이 통신할 수 있게 되었으나 처음에 말한 부분들은 아직 반영되지 않았다. 데이터 지속성과 실시간 코드 업데이트에 대한 부분이다.

우선 데이터 지속성은 볼륨을 사용함으로써 가능하다.

그런데 여기서 좀 더 나아가 인증을 추가해볼 수 있다.

```bash
docker run --name mongodb -v data:/datadb --rm --network goals-net -e MONGO_INITDB_ROOT_USERNAME=max -e MONGO_INITDB_ROOT_PASSWORD=secret mongo
```

이런식으로 루트 유저네임과 패스워드를 추가해서 실행한다면 기존의 백엔드 코드로는 접근이 되지 않는다.

```js

mongoose.connect(
  'mongodb://max:secret@mongodb:27017/course-goals?authSource=admin',
  {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  },
```

유저네임과 패스워드, `authSource=admin`을 추가해주게 되면 다시 DB에 연결 가능해진다.
