터칭 데이터
Docker & K8S - 다수의 Container로 구성된 소프트웨어 실행 본문
다수의 Container로 구성된 소프트웨어 실행
앞서 만들어본 Hangman 프로그램은 하나의 Container로 구성되어 있었는데 만일 다수의 Container로 구성된 프로그램이라면 어떻게 이미지를 빌드해야할까?
Docker에서 5개의 컨테이너로 구성된 프로그램을 실습용으로 제공하는데 이것으로 실습을 진행하려 합니다.
Voting application
첫 번째 실습은 Docker에서 제공하는 voting application을 메뉴얼하게 실행하고
두 번째 실습은 Docker Compose를 사용하여 더 편하게 실행해보겠습니다.
우리가 실행해볼 프로그램 설명
Docker에서 제공해주는 예제 프로그램 - Voting application
좌측과 같이 5개의 컨테이너로 이루어져 있는 프로그램입니다.
우측과 같은 최종 결과를 result-app에서 보실 수 있습니다.
1. voting-app에서 투표를 하고
2. redis라는 in-memory DB에 투표 입력이 들어가고
3. .NET이라는 Worker가 지켜보고 있다가
4. PostgreSQL에 적재하고
5. result-app이라는 NodeJS가 DB를 읽어 투표 결과를 보여줍니다.
예제 프로그램 다운로드 받기
Docker에서 제공해주는 예제 프로그램 - Voting application
git clone https://github.com/dockersamples/example-voting-app
코드 살펴보기
5개의 컨테이너로 이루어져 있고 Github에서 확인해보면 result, vote, worker 3개의 디렉토리가 존재하고 각각의 디렉토리 안에는 Dockerfile이 있습니다 이를 이용해서 3개의 컨테이너를 이미지로 빌드할 것입니다.
왜 5개중에 3개만 빌드하는지는 아래에서 설명드리겠습니다.
먼저 매뉴얼하게 하나씩 빌드해보자
docker build -t vote ./vote
docker build -t result ./result
docker build -t worker ./worker
docker images
3번의 빌드가 끝났다면 위에서 만든 3개의 이미지들을 확인하기
5개 컨테이너인데 vote, result, worker 3개의 이미지만 빌드한 이유는
redis와 postgres는 공식 이미지들이라 Dockerfile로 빌드할 필요가 없음
먼저 매뉴얼하게 하나씩 실행해보자
docker run -d --name=redis redis
docker run -d -e POSTGRES_PASSWORD=postgres --name=db postgres
redis와 postgres는 공식 이미지이므로 Dockerfile로 Image를 빌드할 필요가 없습니다.
docker run -d --name=vote -p 5001:80 vote
docker run -d --name=result -p 5002:80 result
docker run -d --name=worker worker
worker는 vote, result와 다르게 외부에서 접근할 필요가 없으므로 -p로 포트 맵핑을 하지 않았습니다.
이렇게 내가 일일히 하나씩 실행하면 동작할까?
각 컴포넌트들간의 네트워크 연결이 안되고 있음!
위에서 살펴본 그림으로 설명하자면 좌측 흐름도의 화살표가 제대로 이어지지 않았다고 보시면 됩니다.
각 컨테이너들이 별도의 분리된 공간에서 실행되므로 다른 컨테이너들의 존재를 모르기 때문입니다.
결국 네트워크와 관련된 Docker 기능을 사용해야 합니다.
네트워크 관련 이슈를 더 자세히 보자
vote/app.py
def get_redis():
if not hasattr(g, 'redis'):
g.redis = Redis(host="redis", db=0, socket_timeout=5)
return g.redis
투표를 위한 vote/app.py를 보면 Redis(host="redis", ...)와 같이 host를 지정하기는 했는데 redis가 무엇인지 아직은 모르기 때문에 네트워크가 연결되지 않고 있습니다.
vote에 로그인해서 iputils-ping 설치 후 ping 명령으로 redis 호스트 이름이 연결되는지 확인
$ ping redis
ping: cannot resolve redis: Unknown host
ping redis를 해보자 redis가 무엇인지 모른다는 결과가 출력됩니다. ping 기능으로 host 존재 여부를 파악하는지 확인할 수 있습니다. 그런데 ping은 default로 설치되어 있지 않기 때문에 root 유저로 vote에 로그인해 apt 명령어로 iputils-ping을 설치를 먼저 해줘야 합니다.
result/server.js
var pool = new pg.Pool({
connectionString:
'postgres://postgres:postgres@db/postgres'
});
투표결과를 보여주는 result 앱입니다.
postgres와 연결하기 위해 'postgres://postgres:postgres@db/postgres' 적어줬는데 @다음에 오는 것은 관계형 DB 호스트의 이름입니다. db가 호스트 이름이며 postgre Docker Container의 이름이기도 합니다.
주황색 postgres는 postgres의 ID, 녹색 postgres는 패스워드입니다. 그렇게 연결하려는 db가 postgres가 되는 것입니다.
Postgres 연결시 postgres:postgres를 사용하고 있음을 주의깊게 볼것!
이번에 이 세팅은 해보지 않겠습니다만 방법을 알려드리자면 postgres Docker Container에 sh로 로그인 해 postgres와 관계된 shell을 띄우고 그곳에서 postgres DB를 만들고 psotgres ID와 PWD를 갖는 어카운트를 만들면 될 것입니다.
worker/Program.cs
var pgsql = OpenDbConnection("Server=db;Username=postgres;Password=postgres;");
var redisConn = OpenRedisConnection("redis");
worker는 redis에서 투표 결과를 가져와 postgres에 적재해야 하므로 redis와 db 모두 연결을 해야 합니다.
어떻게 Network 이슈를 해결할 수 있을까?
docker의 network 기능 사용
전에는 docker run의 link 옵션을 사용했지만 이제는 번거로워서 사용되지 않음
그렇게 network이라는 새로운 개념이 탄생
network을 하나 만들고 모든 컨테이너들을 이 네트워크 안으로 지정
후에 docker-compose로 만들어진 예를 통해 실행해보겠지만 그 때는 2개의 network인 back-tier, front-tier로 만들어 연결할 겁니다.
연결 상황에 따라 별개의 네트워크를 만들고 사용도 가능함
- back-tier: voting, result
- front-tier: redis, postgres, worker
하지만 매뉴얼 예제에서는 mynetwork을 하나를 만들고 5개 모든 컨테이너를 넣어서 진행할 예정
docker network create
docker container rm -f $(docker container ls -aq)
기존의 모든 컨테이너들 클린업
docker network create mynetwork
mynetwork라는 network 생성
이제 이하 5개의 컨테이너들을 mynetwork에 배정
docker run -d --name=redis --network mynetwork redis
docker run -d --name=db -e POSTGRES_PASSWORD=password --network mynetwork postgres
docker run -d --name=vote -p 5001:80 --network mynetwork vote
docker run -d --name=result -p 5002:80 --network mynetwork result
docker run -d --name=worker --network mynetwork worker
실습
Git clone
적당한 위치에 폴더를 생성합니다.
그리고 그 디렉토리로 이동하여 git clone https://github.com/dockersamples/example-voting-app을 해줍니다.
git clone https://github.com/dockersamples/example-voting-app 명령
PS D:\Dev_KDT\voting-app> git clone https://github.com/dockersamples/example-voting-app
Cloning into 'example-voting-app'...
remote: Enumerating objects: 1132, done.
Receiving objects: 98% (1110/1132)d 0 (delta 0), pack-reused 1132
Receiving objects: 100% (1132/1132), 1.17 MiB | 12.65 MiB/s, done.
Resolving deltas: 100% (435/435), done
위와 같이 클론이 되었습니다. 위의 디렉토리로 이동합니다.
3개의 컨테이너 빌드
vote, result, worker 3개의 Image를 Dockerfile을 이용해 컨테이너로 빌드합니다.
나머지 2개 redis와 postgres는 공식 이미지가 존재하므로 Dockerfile로 빌드할 필요가 없다고 했었죠?
PS D:\Dev_KDT\voting-app\example-voting-app> docker build -t vote ./vote
(생략)
PS D:\Dev_KDT\voting-app\example-voting-app> docker build -t result ./result
(생략)
PS D:\Dev_KDT\voting-app\example-voting-app> docker build -t worker ./worker
(생략)
3개의 이미지를 빌드했습니다.
docker images
PS D:\Dev_KDT\voting-app\example-voting-app> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
worker latest abcd12345678 13 seconds ago 194MB
result latest abcdefghijk1 About a minute ago 224MB
vote latest 12345678abcd 3 minutes ago 154MB
3개의 Images가 조회됩니다.
redis와 postgres Docker Container 하나씩 실행
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=redis redis
~~
PS D:\Dev_KDT\voting-app\example-voting-app> docker run --name=db postgres
redis는 -d 붙여 background로 Container를 run 실행했습니다.
그런데 posgres는 보여드릴 에러가 있고 에러를 쉽게 확인하기 위해 -d를 붙이지 않고 foreground로 실행했습니다.
Error: Database is uninitialized and superuser password is not specified.
You must specify POSTGRES_PASSWORD to a non-empty value for the
superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run".
You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all
connections without a password. This is *not* recommended.
See PostgreSQL documentation about "trust":
https://www.postgresql.org/docs/current/auth-trust.html
docker run을 할 때 패스워드 세팅을 해달라는 뜻입니다. 앞에서 보셨죠?
docker run -d -e POSTGRES_PASSWORD=(원하는 패스워드) --name=db postgres
PS D:\Dev_KDT\voting-app\example-voting-app> docker run --name=db -e POSTGRES_PASSWORD=(원하는 패스워드) postgres
docker: Error response from daemon: Conflict.
The container name "/db" is already in use by container "~~".
You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
-e 옵션에 POSTGRES_PASSWORD로 패스워드를 붙여서 다시 postgres이미지를 run 해줍니다.
그런데도 에러가 나는데 그 이유는 아까 -e 옵션을 주지 않고 에러를 마주칠 때 생성한 db라는 이름의 컨테이너가 이미 stopped status 상태에서 db이름을 사용하고 있기 때문입니다.
PS D:\Dev_KDT\voting-app\example-voting-app> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
abcdefg12345 postgres "docker-entrypoint.s…" 12 minutes ago Exited (1) 12 minutes ago db
PS D:\Dev_KDT\voting-app\example-voting-app> docker rm db
db
db 이름으로 exited 상태입니다.
docker rm db(컨테이너 ID도 가능)로 삭제합니다.
docker run -d -e POSTGRES_PASSWORD=(원하는 패스워드) --name=db postgres
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=db -e POSTGRES_PASSWORD=(원하는 패스워드) postgres
다시 postgres를 설치합니다. -d 옵션으로 실행하시거나 아니면 터미널 창을 하나 더 여시고 진행하시면 됩니다.
vote, result, worker들의 Docker Container run
docker run -d --name=vote -p 5001:80 vote
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=vote -p 5001:80 vote
~~
PS D:\Dev_KDT\voting-app\example-voting-app> docker ps
IMAGE COMMAND CREATED STATUS PORTS NAMES
vote "gunicorn app:app -b…" 37 seconds ago Up 36 seconds 0.0.0.0:5001->80/tcp vote
postgres "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 5432/tcp db
redis "docker-entrypoint.s…" 22 minutes ago Up 22 minutes 6379/tcp redis
vote를 run하고 docker ps를 해보니
vote와 그 전에 설치한 postgres, redis도 잘 조회됩니다.
docker run -d --name=result -p 5002:80 result
docker run -d --name=worker worker
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=result -p 5002:80 result
~~
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=worker worker
~~
PS D:\Dev_KDT\voting-app\example-voting-app> docker ps
IMAGE COMMAND CREATED STATUS PORTS NAMES
worker "dotnet Worker.dll" 2 seconds ago Up 1 second worker
result "/usr/bin/tini -- no…" About a minute ago Up About a minute 0.0.0.0:5002->80/tcp result
vote "gunicorn app:app -b…" 5 minutes ago Up 5 minutes 0.0.0.0:5001->80/tcp vote
postgres "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 5432/tcp db
redis "docker-entrypoint.s…" 27 minutes ago Up 26 minutes 6379/tcp redis
docker ps로 모든 컨테이너가 실행 중입니다.
하지만 아직 컨테이너들 사이에 네트워크로 연결되지는 않은 상태입니다.
네트워크 연결
접속은 정상적으로 되는 것 같지만 선택지를 누르면 Internal Server Error가 발생합니다.
vote 컨테이너가 redis와 네트워크로 연결되지 않았기 때문입니다.
포트 5002로 result에 접속하면 겉보기에는 제대로 동작하는 것 같지만 postgres에서 데이터를 받아오는 것은 아니기 때문에 정상 작동 상태는 아닙니다.
네트워크 연결 확인을 위해 ping이 필요합니다.
ping
PS D:\Dev_KDT\voting-app\example-voting-app> ping www.google.com
Ping www.google.com [142.250.206.196] 32바이트 데이터 사용:
142.250.206.196의 응답: 바이트=32 시간=29ms TTL=57
142.250.206.196의 응답: 바이트=32 시간=29ms TTL=57
142.250.206.196의 응답: 바이트=32 시간=29ms TTL=57
142.250.206.196의 응답: 바이트=32 시간=29ms TTL=57
142.250.206.196에 대한 Ping 통계:
패킷: 보냄 = 4, 받음 = 4, 손실 = 0 (0% 손실),
왕복 시간(밀리초):
최소 = 29ms, 최대 = 29ms, 평균 = 29ms
ping은 위와 같이 연결이되는 안되는지 확인할 수 있습니다.
vote 컨테이너에 root 유저로 로그인해 ping을 설치하고 사용하겠습니다.
root 유저 로그인, apt update, ping 설치
PS D:\Dev_KDT\voting-app\example-voting-app> docker exec -it --user root vote sh
# ping redis
sh: 1: ping: not found
# apt update
(생략..)
# apt install iputils-ping
(생략..)
# ping redis
ping: redis: Name or service not known
현재 vote는 redis를 알지 못한다고 합니다. network 커맨드로 network를 만들고 각 컨테이너들을 같은 network로 배치하겠습니다.
Network
먼저 기존에 설치한 5개의 모든 voting application 컨테이너들을 삭제합니다.
network 옵션을 붙여 주고 다시 생성해야하기 때문입니다.
PS D:\Dev_KDT\voting-app\example-voting-app> docker container rm -f (컨테이너 ID들)
mynetwork라는 network 만들기
PS D:\Dev_KDT\voting-app\example-voting-app> docker network create mynetwork
docker network create mynetwork 명령으로 mynetwork를 생성했습니다.
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=redis --network mynetwork redis
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=db -e POSTGRES_PASSWORD=(패스워드) --network mynetwork postgres
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=vote -p 5001:80 --network mynetwork vote
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=result -p 5002:80 --network mynetwork result
PS D:\Dev_KDT\voting-app\example-voting-app> docker run -d --name=worker --network mynetwork worker
PS D:\Dev_KDT\voting-app\example-voting-app> docker ps
IMAGE COMMAND CREATED STATUS PORTS NAMES
worker "dotnet Worker.dll" 4 seconds ago Up 3 seconds worker
result "/usr/bin/tini -- no…" 29 seconds ago Up 28 seconds 0.0.0.0:5002->80/tcp result
vote "gunicorn app:app -b…" 30 seconds ago Up 29 seconds 0.0.0.0:5001->80/tcp vote
postgres "docker-entrypoint.s…" 31 seconds ago Up 30 seconds 5432/tcp db
redis "docker-entrypoint.s…" 31 seconds ago Up 31 seconds 6379/tcp redis
다시 5개의 Container를 만드는데 이 때 --network 옵션으로 방금 만들어 둔 mynetwork를 지정합니다.
정상적으로 docker ps로 5개 모든 컨테이너들이 조회됩니다.
결과 보기
vote의 5001에 접속해 강아지에 투표하니 제대로 체크가 됩니다.
네트워크가 제대로 설치되었는지 다시 vote에 ping을 설치하고 redis host를 찾아냈는지 확인하겠습니다.
PS D:\Dev_KDT\voting-app\example-voting-app> docker exec -it --user root vote sh
# apt update
(생략)
# apt install iputils-ping
(생략)
# ping redis
(생략)
64 bytes from redis.mynetwork (IP 주소): icmp_seq=28 ttl=64 time=0.056 ms
(생략)
정상적으로 작동합니다.
여러개의 Container로 구성된 프로그램을 network를 설정해 구동하는 방법을 실습해보았습니다.
'Docker & K8S' 카테고리의 다른 글
Docker & K8S - dockerignore와 맥 port 5000 에러 (0) | 2023.12.21 |
---|---|
10주차 - 3 [Docker & K8S] (0) | 2023.12.20 |
Docker & K8S - 클린업 (0) | 2023.12.20 |
Docker & K8S - Docker Volume Airflow 데모(실습) (0) | 2023.12.20 |
Docker & K8S - Docker Volume nginx 데모(실습) (0) | 2023.12.20 |