Devlog

컨테이너 내부 서버가 통신 하는 방법 본문

데브옵스

컨테이너 내부 서버가 통신 하는 방법

ehdrb92 2023. 1. 9. 00:12

컨테이너 서버를 띄우게 되면 해당 컨테이너 서버는 상황에 따라 WWW 웹, 로컬 시스템, 다른 컨테이너와 통신을 주고 받을 수 있다. 그렇다면 컨테이너가 해당 상황들에서 어떻게 통신할 수 있는지를 알아보자.

WWW(World Wide Web) 서버와 통신

첫 번째 상황은 외부의 인터넷상에서 다른 API와 통신하는 것이다. 사실 도커 컨테이너는 WWW상의 다른 외부 서버와 통신은 별 다른 설정이 필요없다. 예시를 통해 보도록하자.

app.get("/movies", async (req, res) => {
  try {
    const response = await axios.get("https://swapi.dev/api/films");
    res.status(200).json({ movies: response.data });
  } catch (error) {
    res.status(500).json({ message: "Something went wrong." });
  }
});

위 코드는 nodejs로 만들어진 서버의 특정 엔드포인트에 대한 코드 부분을 가져온 것이다.

 

해당 엔드포인트에 GET 요청을 호출하면 "https://swapi.dev/api/films"이라는 외부 서버에 데이터를 요청에 json 데이터로 반환해준다. 컨테이너로 띄워진 해당 서버에 엔드포인트로 GET 요청을 하면 외부 서버에 별도의 GET 요청을 보내 데이터를 받아오고 아래와 같은 일련의 데이터를 잘 받아온다.

{
    "count": 6, 
    "next": null, 
    "previous": null, 
    "results": [
        {
            "title": "A New Hope", 
            "episode_id": 4, 
            "opening_crawl": "It is a period of civil war.\r\nRebel spaceships, striking\r\nfrom a hidden base, have won\r\ntheir first victory against\r\nthe evil Galactic Empire.\r\n\r\nDuring the battle, Rebel\r\nspies managed to steal secret\r\nplans to the Empire's\r\nultimate weapon, the DEATH\r\nSTAR, an armored space\r\nstation with enough power\r\nto destroy an entire planet.\r\n\r\nPursued by the Empire's\r\nsinister agents, Princess\r\nLeia races home aboard her\r\nstarship, custodian of the\r\nstolen plans that can save her\r\npeople and restore\r\nfreedom to the galaxy....", 
            "director": "George Lucas", 
            "producer": "Gary Kurtz, Rick McCallum", 
            "release_date": "1977-05-25",
            ...
            ...
            ...

그렇다면 다른 상황에 대해서도 알아보자.

로컬 시스템과 통신

이번에는 컨테이너 내부에서 로컬 시스템과의 통신이다. 컨테이너로 띄워진 서버에서 로컬 시스템에 있는 데이터베이스로 부터 데이터를 호출해 받아오는 상황이다.

# 로컬 시스템의 데이터베이스에 조회 요청
app.get("/favorites", async (req, res) => {
  const favorites = await Favorite.find();
  res.status(200).json({
    favorites: favorites,
  });
});

# 로컬 시스템의 MongoDB와 연결
mongoose.connect(
  "mongodb://localhost:27017/swfavorites",
  { useNewUrlParser: true },
  (err) => {
    if (err) {
      console.log(err);
    } else {
      app.listen(3000);
    }
  }
);

위 코드는 컨테이너 서버의 엔드포인트에 GET요청을 주었을 때, 로컬 시스템에 MongaDB로 부터 데이터를 받아오는 코드의 예시이다. 그런데 위 코드를 통해 데이터를 받아오려 할 경우 에러가 발생한다.

 

원인은 컨테이너 또한 하나의 컴퓨터와 같이 행동하기 때문에 컨테이너 내부에서 localhost는 컨테이너 자신을 가르키게 된다. 현재 유저가 사용하는 로컬 시스템을 말하는 것이 아니다. 그렇기에 컨테이너 내부에 MongoDB가 존재하지 않아 오류가 발생하는 것이다.

 

이럴 때는 위 데이터베이스 연결 URL에서 "localhost"를 "host.docker.internal"로 바꿔준다. "host.docker.internal"는 사용자의 로컬 시스템을 의미하며 도커 내부적인 로직에 의해 로컬 시스템의 ip로 매핑되게 된다. 이 후에는 정상적으로 로컬 시스템의 데이터베이스와 연결되어 요청이 정상 동작하게 된다.

다른 컨테이너와의 통신

위와 같이 로컬 시스템 상의 데이터베이스와 연결하여 통신을 할 수도 있지만 사실 해당 데이터베이스도 컨테이너로 띄우고 연결하여 사용하는 경우가 많다. 그렇다면 다른 컨테이너의 데이터베이스에 통신을 하고 싶다면 어떤 URL을 써야할 까?

# 컨테이너 정보 불러오기 명령어
❯ docker container inspect mongodb

...
...
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "d5af550fd44ff53f788a7c08aa2e2fdeafd854255444074a993548fdec2fcf50",
                    "EndpointID": "14e3d36ea3cff98968c287104f008358c297af6b549d4ca349a2288967902bfd",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
...
...

명령어 터미널에서 컨테이너의 상세 정보를 불러오는 명령어를 사용하면 ip정보를 볼 수 있다. 위 예시는 해당 정보 중 네트워크와 관련된 일부 정보만을 가져온 것이다. 위에서 보면 "Network"항목 하위에 "IPAddress"키에 대한 값으로 "172.17.0.2"가 존재한다. 해당 정보를 연결하는 URL에 넣어주면, 해당 컨테이너와 통신을 할 수 있게 된다.

Docker Networks 활용하기

이전 과정에서 컨테이너가 다른 컨테이너와 통신을 하는 방법을 알아보았다. 그런데 위와 같이 하려면 다른 컨테이너와 연결할 때마다 해당 컨테이너 정보를 일일이 검색하여 제공해야 한다. 이러한 과정은 생각보다 번거로울 수 있다. 이러한 번거러움을 해결하기 위해 도커에서는 network라는 기능을 제공한다.

 

해당 기능은 각각의 컨테이너를 하나의 네트워크에 그룹으로 묶어 서로 쉽게 통신할 수 있도록 한다. 먼저 네트워크를 만들어 준다.

# 네트워크 생성 명령어
docker network create project-network

위 명령어를 입력해주면 도커에 해당 "project-network"의 이름을 가진 네트워크를 생성한다. 명령어를 사용하여 생성된 네트워크 목록을 확인할 수 있다. 컨테이너를 네트워크에 그룹화 시키고 싶다면, 해당 네트워크에 연결하는 명령어를 입력해준다.

# 네트워크 목록 열람 명령어
❯ docker network ls
NETWORK ID     NAME                         DRIVER    SCOPE
d5af550fd44f   bridge                       bridge    local
5a8bfcddc769   host                         host      local
21ba0776aca4   nat                          bridge    local
b6e52a1ef7d5   none                         null      local
49910afc614a   project-network              bridge    local
a14fb14a4005   redis-net                    bridge    local
1a86080c7244   todo-list-postgres_app-net   bridge    local

# 네트워크 연결 옵션
--network project-network

그렇다면 이렇게 하나의 네트워크로 연결된 컨테이너들은 어떻게 통신할까? 이전에 로컬 호스트와 통신하거나 다른 네트워크와 통신할 때 ip주소를 넣어주는 부분에 통신하고 싶은 컨테이너의 이름을 넣어주면 된다. "mongodb://mongodb:27017/swfavorites"와 같이 말이다.

 

이렇게 되면 도커는 내부 로직을 통해 mongodb라는 컨테이너가 같은 네트워크 상에 있는 지 확인 후 해당 ip주소로 매핑하여 접속할 수 있게 된다.

 

그리고 네트워크를 통해 컨테이너간의 통신을 할 때 경우에 따라 특정 컨테이너는 포트를 열람하지 않아도 되는 경우도 존재한다.

참고사항

만약 위와 같이 다른 컨테이너에 데이터를 서빙하는 데이터베이스 컨테이너가 존재하는데, 해당 컨테이너는 다른 컨테이너 이외의 외부 서버나 로컬 시스템과 통신을 하지 않는다고 하자. 그러면 사실 외부에 포트를 개방할 필요가 없다. 도커 네트워크로 연결을 하는 것은 포트를 통하지 않기 때문이다.

'데브옵스' 카테고리의 다른 글

kubernetes 입문하기  (0) 2023.02.23
Docker-Compose 사용하기  (0) 2023.01.13
도커 볼륨으로 데이터 관리하기  (0) 2023.01.08
Dockerfile 만들기  (0) 2023.01.08