0. 개요

프로젝트에서, Jenkins와 Frontend, Backend, DB를 도커로 실행시켰었다.
하지만, 문제가 발생했다.
바로, 도커 컨테이너 끼리는 소통을 기본적으론 못한다는 것.


1. 기본 환경

"도커 컨테이너 끼리의 소통" 을 설명하기 위해, 아래처럼 두개의 컨테이너를 만들었다.

C:\Users\A> docker run -d -p 8080:8080 -p 50000:50000 jenkins/jenkins
054232dce4aad83b1fb0e5d26eeae666582edaa66c17910c2bb01372e59b1e8e
C:\Users\A> docker run -d -p 80:80 nginx
f02cf55a548ff1dfbcea0a539a72f73c07f7e5a9acabfe3909d06e0532047cb8
C:\Users\A>docker ps -a
CONTAINER ID   IMAGE             COMMAND                  CREATED                  STATUS         PORTS                                              NAMES
f02cf55a548f   nginx             "/docker-entrypoint.…"   Less than a second ago   Up 9 seconds   0.0.0.0:80->80/tcp                                 vigorous_bose
054232dce4aa   jenkins/jenkins   "/usr/bin/tini -- /u…"   About a minute ago       Up 2 minutes   0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp   naughty_knuthC

그럼 여기서, 만약
젠킨스 컨테이너가 nginx 컨테이너에 접근하려면 어떻게 해야할까?


2. 로컬에서 시도

우선, 도커가 아닌, cmd에서 nginx를 대상으로 curl을 날렸을 때 다음과 같이 결과가 출력된다.
curl이란, URL을 통해 웹 요청을 보내고 응답을 확인할 수 있는 명령줄 도구다!

C:\Users\A>curl 127.0.0.1:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
~~ 기타 내용 ~~
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

로컬과 도커는 서로 127.0.0.1을 통해 소통이 가능하다!
는 결론을 얻을 수 있다.


3. 젠킨스에서 시도

이제, 주제 내용인
"젠킨스 컨테이너"에서 "nginx 컨테이너"로 소통을 시도해보자.

C:\Users\A>docker exec -it -u root 05 bash
root@054232dce4aa:/# curl 127.0.0.1:80
curl: (7) Failed to connect to 127.0.0.1 port 80 after 0 ms: Couldn't connect to server

명령어를 해석해보자면,
docker 05 컨테이너(젠킨스)를 root 권한으로 들어가서,
curl 127.0.0.1:80을 날려보았다.
즉 이걸 그림으로 그려보자면,


이런 식이다.
분명 내 컴퓨터(호스트 컴퓨터)에서는 localhost:80으로 연결이 되는데,
어째서 젠킨스에서 nginx, 즉 컨테이너끼리는 연결이 안되는 걸까?


4. 해결 방법

컨테이너는 컨테이너끼리 소통하기 위해, 아래 스킬을 써야한다.

  1. 도커 네트워크를 만들고
  2. 그 네트워크에 가입시키고
  3. 컨테이너명 또는 할당된 ip로 호출해야한다.
    말로해서는 어려우니, 아래 realworld 프로젝트 당시 썼던 docker-compose 파일을 보면서 이해하면 쉬울 것 같다.
    networks:            #(1. 네트워크 만들기!)
      realworld-network:
        external: false
        driver: bridge
        ipam:
          config:
            - subnet: 172.25.0.0/16
              gateway: 172.25.0.1
    services:
      realworld_db:
    	#"""설정들"""
        networks:            #(2. 네트워크에 가입시키기!)
          realworld-network:
            ipv4_address: 172.25.0.10
    
      realworld_backend:
        build: ./backend
    	#"""설정들"""
        networks:            #(2. 네트워크에 가입시키기!)
          realworld-network:
            ipv4_address: 172.25.0.20
    
      realworld_frontend:
    	#"""설정들"""
        networks:            #(2. 네트워크에 가입시키기!)
          realworld-network:
            ipv4_address: 172.25.0.30

1. 이런 식으로,realworld-network라는 네트워크를 만들고,
2. 그 네트워크에 가입시키고,
3. 컨테이너명 또는 할당된 아이피로 호출해야한다.
이 중, 컨테이너명을 종종 헷갈리곤 해서 IP를 할당하고 이를 호출하는 방식으로 도커 네트워크 내부 연결을 성공시킨 경험이 있다.


위 이미지처럼, 172.25.0.x 으로 네트워크로 묶어 연결을 성공했다.


5. 연결 해보기

그럼, 주제였던 젠킨스와 nginx 연결에 대입해보자.
이를 위해 sample이라는 이름으로 네트워크를 만들었다.

C:\Users\A>docker network create --subnet=172.25.0.0/16 --gateway=172.25.0.1 sample
0835b83943be2d58f77e17f80970dac57bc247e6408f86c3e798bf81774e36e7
C:\Users\A>docker network inspect sample
[
    {
        "Name": "sample",
        #내용 생략
            "Config": [
                {
                    "Subnet": "172.25.0.0/16",
                    "Gateway": "172.25.0.1"
                }
            ]
        },

    }
]

명령어를 간단히 설명하자면, sample이라는 네트워크를 만들고 inspect를 통해 네트워크 정보를 본다.
유심히 봐야할 건, 172.25.0.x 이다.
아까 네트워크도 그랬듯, 마지막 숫자를 바꿔가며 사설 IP를 할당할 수 있다.
이제,

C:\Users\A>docker run -d --name jenkins --net sample --ip 172.25.0.10 -p 8080:8080 -p 50000:50000 jenkins/jenkins
083b022e148568171500f627408b381ed694c8a9f414a8d18536239ccf89a473
C:\Users\A>docker run -d --name nginx --net sample --ip 172.25.0.20 -p 80:80 nginx
231509c08197e9df734c53b81e78429b2995151c25afd496039d582b47106623

젠킨스와 nginx를 다시 sample 네트워크의 ip에 할당해서 실행했다.
이후,

C:\Users\A>docker exec -it -u root 08 bash
root@083b022e1485:/# curl 172.25.0.20:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
~~ 기타 내용 ~~
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

이렇게 curl을 사설 IP인 172.25.0.20으로 던졌을 때 응답이 오는 것을 확인할 수 있다.

root@083b022e1485:/# curl nginx:80

물론, 172.25.0.20 대신 nginx라는 컨테이너 이름을 명시해도 좋다. 더 깔끔하기 때문에 선호되는 방식이라고 한다.

6. 마무리

이 사실을 알고 모르는건 천지차이였다.

위에 언급된 realworld 프로젝트 당시, 컨테이너 간 연결이 되지 않아 문제 해결을 위해 엉뚱한 원인 추정으로 무려 3일이라는 긴 시간을 썼다.

GPT는 당연히 내가 이러한 사실을 간과했다는 것 조차 몰랐기에 함께 삽질을 대차게 해버렸다!

하지만 3일이나 삽질을 한 결과, 도커를 자유자재로 다룰 수 있는 초능력자가 되어버렸다 ^ㅇ^

어떻게 보면 3일이나 걸릴 문제는 아니라고 생각도 하는데, gui가 아닌 cli만으로는 문제 추정이 어렵다는걸 느꼈다..

Docker compose란?

여러 개의 Docker 컨테이너들을 한번에 실행할 때 쓰기 위해 사용함
복잡한 명령어를 간소화하기 위해 사용함


compose 흐름 - nginX

  1. compose.yml 생성
  2. 아래와 같이 내용 작성
    services:
     my-web-server:
     container_name: webserver
     image: nginx
     ports:
         - 80:80
     volumes:
         - ./my_data:/var/lib/sql
    services = docker compose에서는 컨테이너를 service라고 적음
    my-web-server = service의 이름
    container_name = 컨테이너의 이름 (--name 옵션)
    image = 어떤 이미지 쓸지 (이미지명)
    ports = 어떤 포트 쓸지 (-p 80:80)
    yml = 들여쓰기로 계층을 판단함 (like python)
  3. 이 파일이 있는 경로에서 docker compose up 하면 실행이 됨
  4. docker compose up -d 를 쓰면 백그라운드가 켜짐
  5. docker compose ps를 하면 컨테이너들이 보임
  6. docker compose down 하면 컨테이너들이 종료됨

자주 사용하는 Docker compose CLI

compose.yml에서 정의된 컨테이너 중에서만 작동한다.
docker compose up -d : 백그라운드에서 실행
docker compose ps : 실행 중인 컨테이너
docker compose logs : 컨테이너의 로그를 모아 출력
docker compose up --build : 빌드부터 다시
docker compose pull : 최신 이미지로 업데이트함
docker compose down : 컨테이너 종료


한번에 두개 띄우는 방법

services:
  first-server:
    image: redis
    ports:
      - 6379:6379
    container_name: server_1

  second-server:
    image: nginx
    ports:
      - 8080:80
    container_name: server_2

요런식으로! 파이썬 코드 작성하듯이 하면된다.


꿀팁

https://www.composerize.com/
웹사이트에 docker run 명령어를 넣으면 copose.yml에 넣을 커맨드를 받을 수 있다.
https://www.decomposerize.com/
거꾸로, 여기서는 compose.yml 명령어를 docker run 형식으로 바꿔줄 수 있다!


이 수업 다음은 뭘 배워야할까?

이 강의에서 클라우드 서비스를 이용해 배포하는 과정까지 포함되었으나, 딱히 해당 내용은 필요한 것 같지 않아 PASS!
강사님은, 우선 도커를 이런 저런 프로젝트에 적용해보라고 하셨다.
그 다음,

  1. 쿠버네티스에 대한 소개를 했다.
    쿠버네티스는, 다수의 컨테이너를 관리하고 배포하는 데에 도움을 주는 툴이다!
    컨테이너 오케스트레이션 툴이라고도 부른다.
  2. CI/CD 구축을 해보는 걸 추천했다.
    Docker 컨테이너 기반으로 CI/CD 구축을 해보라는 내용이다.

완강!

기본적인 내용만 사용해본 입장에서, 간단하게 사용하는 입장에서는 러닝커브가 높은 툴이 아니라고 느낀다.
어쩌면 잘 가르치는 강사님 이라서 그런걸수도.
과연 실무에는 어떤 무지막지한 내용이 있을지는 모르겠으나.. ㅎㅎ;
적어도 아는 지식이 전혀 없었던 과거보다는 나아진듯!

애초에 이 강의의 타겟이 비전공 초보자들을 위한 강의인데, 큰 도움이 됐다.


Dockerfile이란?

docker 이미지를 만들게 해주는 파일.
내가만든 프로젝트를 dockerfile로 만들고, 이걸 활용하면 docker 이미지로 쓸 수 있게 한다.

1. Dockerfile안에 베이스 이미지 넣기

# Dockerfile
FROM [이미지명]
FROM [이미지명]:[태그명]

태그명을 적지 않으면 가장 최신 버전을 사용한다.

2. Dockerfile 기반 이미지 만들기

# 터미널
docker build -t my-server .
docker build -t my-server:beta .

docker build // 도커에게 빌드를 명령
-t 태그 // 안적으면 latest, 적으면 적은 태그가 적힌다.
. // 상대 경로로, 점 하나는 "현재 여기"를 표시

3. 이미지 기반 컨테이너 띄우기

docker run -d my-server

my-server 실행해보기

4. 컨테이너 조회

docker ps -a

my-server의 STATUS가 Exited 로 적혀있다..!
이유 : FROM 이미지명 만 적으면 저거 실행하고 끝내버림.
내부 확인하려면?
일단 실행중이지 않기 때문에, exec로 내부를 볼 수 없음.
그럼 어떻게 하는가?

5. 꼼수

# Dockerfile
FROM 이미지명
ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

이렇게 하고 다시 빌드부터 해보면,
docker ps -a에서도,
docker exec -it 컨테이너명 bash에서도
실행이 되고있는걸 확인할 수 있다.
저 명령어는, 이후 디버깅할 때 유용하게 쓸 수 있다. 마치 셀레니움 할때 time.sleep(5)와 같은 느낌


COPY

FROM ubuntu
COPY a.txt /a.txt
COPY ./ / # 모든 파일을 다 붙여넣겠다
COPY *.txt /text-files/ #와일드카드같은것도 사용 가능
ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

해당 이미지에 호스트 a.txt 파일을 컨테이너 a.txt를 집어넣겠다.
폴더를 복사할땐 COPY folder /folder/ <<- 슬래시로 가둬야 정상적으로 폴더로 인식됨


.dockerignore

a.txt # a.txt는 넣지마라

gitignore처럼 , 추적을 원치 않는 파일을 지정할 수 있음. COPY에서 제외됨.


ENTRYPOINT

컨테이너가 시작될 때 실행되는 명령어.
즉, 컨테이너(미니컴퓨터)가 실행되고 바로 실행되는 명령어.

From ubuntu
ENTRYPOINT ["/bin/bash", "-c", "echo hello"]

해보면 echo hello 하고 바로 꺼지는 도커파일 생성이 된다.


RUN

run은 이미지 생성 과정에서 명령어를 실행시켜야 할 때 사용.
RUN vs ENTRYPOINT
RUN = 이미지 생성 과정에서 필요한 명령어를 실행시킬 때.
ENTRYPOINT = 이미지 생성 이후 컨테이너 실행된 직후에.
RUN 예제

FROM ubuntu
RUN apt update && apt install -y git

즉, 이미지 생성 중에 git이 설치된다.
RUN = docker build 중에
ENTRYPOINT = docker run 할 때


WORKDIR

작업 디렉토리를 전환. 마치 윈도우에서 cd (change directory) 느낌
이후 도커파일에 오는 모든 명령어들은 해당 위치에서 실행됨

FROM ubuntu
WORKDIR /new_directory
COPY ./ ./

위와같이, new_directory 라는 디렉토리로 이동 후 모든 파일을 복사하겠다는 뜻.
기본적으로는 루트 디렉토리에 파일이 저장되는데, 옮긴 후 COPY할 수 있다.


EXPOSE

컨테이너가 사용하는 포트를 명시적으로 알려주는 용도.
근데, 진짜 포트를 열지 않음.
docker run -p 옵션에서 진짜 포트를 염
그냥 주석처럼 "가이드라인"을 제공하는 느낌.

FROM ubuntu
EXPOSE 3000

주석처럼 없어도 되나, 적어두면 개발자나 도구에게 감초 역할을 한다.


전체 흐름

FROM node    # 기본적으로 node를 실행한다
WORKDIR /app    # 디렉토리를 app 폴더로 이동
copy . .    # 호스트의 현재 디렉토리 내 모든 파일을 복사, app 폴더에 붙여넣어짐
RUN npm install    # 이미지 생성 중 npm install을 실행
RUN npm run build    # 이미지 생성 중 npm run build를 실행
EXPOSE 3000    # 3000번 포트를 쓸거라고 알려만 준다.
ENTRYPOINT ["node", "dist/main.js"]    # node dist/main.js를 컨테이너에서 실행하라

효율적인 도커 파일 생성 방법

  1. alpine 빌드를 사용.
    alpine = 불필요한 프로그램을 포함하지 않고 이미지 크기를 최소화한 버전.

변경이 적은 명령어를 위쪽에 배치하기.
dockerfile은 빌드할 때 docker cache를 만든다. 명령어 실행 내역을 캐시에 저장했다가, 다음에 다시 dockerfile을 재사용할 때 캐시를 이용하여 빠르게 빌드되게 만든다.
근데 만약 dockerfile 중간에 변경이 생긴다면, 그 아래에 있는 모든 명령어는 변경이 없음에도 다시 빌드된다.



이미지에서 main.c가 바뀐다면,
1,2 번 라인은 캐시에 저장되어있어 빨리 실행되나 3,4,5번은 캐시에 없던 내용이라 "무효화"된다.
모르겠으면, 그냥

"변경이 적은 명령어를 위쪽에 배치하기"

 

https://www.inflearn.com/course/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-docker-%EC%9E%85%EB%AC%B8-%EC%8B%A4%EC%A0%84

 

비전공자도 이해할 수 있는 Docker 입문/실전 강의 | JSCODE 박재성 - 인프런

JSCODE 박재성 | , 🤬 에라이, 못 해먹겠네!비전공자로 개발을 시작해 여러 회사에서 CTO로 활동하다가, 현재는 교육자로 활동하고 있는 박재성이라고 합니다. 저도 비전공자로 개발을 시작해 서버

www.inflearn.com

 

비전공자도 이해할 수 있는 Docker 입문/실전

귀여운 고래 이미지가 특징인 도커!


개요

만우절날 인프런에서 단 돈 천원에 Docker 강의를 파는 소식에 낼름 사게 되었다!

본인만의 언어로 강의를 블로그에 적는 걸 권장한다고해서, 일단 보면서 올려보게 되었다.

총 76개 수업이 있고(단순히 유튜브 / 노션링크만 주는 수업도 포함) 현재 34강 중 정리해서 올리는 글이다.

단돈 천원에 구매해서 그런가? 너무나도 만족스럽게 수업을 듣고있다 ㅎㅎ

별개로, 옵시디언으로 필기를 하면서 보는데, 티스토리에 복붙하면 좀 흉하게 붙여진다...

별수있나 ㅎ


Docker란?

환경 설정이 컴퓨터마다 다른걸 가볍게 해결하기 위함.

Docker쓰는 이유

  • 이식성이 좋다.
    ex)내가 만든 프로그램이 우리집 컴에서는 돌아갔는데, 팀원 컴에서는 안돌아간다...
    버전이 다르거나, 운영 체제가 다르거나, 다른 프로그램과 충돌이 났거나..
  • 위와 같은 이유를 해결하기위해

도커(Docker)?

  • 컨테이너를 사용하여 각각의 프로그램을 분리된 환경에서 관리할 수 있는 툴컨테이너(container)?
  • 하나의 컴퓨터 환경 내 독립적인 컴퓨터 환경을 구성해, 각 환경에 프로그램을 별도로 설치할 수 있게 만든 개념

  • 컴퓨터 - 호스트 컴퓨터
  • 컨테이너 내에는 독립성이 있다.
    1. 일반적으로 A 컨테이너 내부에서 B 컨테이너 접근 불가 ❌
    2. 네트워크에도 접근 불가 ❌
  • 이미지 - 닌텐도 칩과 같은 이미지. 필요한 설치과정, 설정, 버전 정보 등이 포함되어 있음.

Docker 간단 실습하기 with Nginx

Nginx => html 웹페이지 렌더링하는 기능

docker pull nginx => docker에서 nginx라는 게임칩(이미지) 다운받은거임
docker image ls => 게임칩(이미지) 다운받은거 확인하는거임
이제, nginx 게임칩(이미지) 꽂아보자.
docker run --name webserver -d -p 80:80 nginx
이러고, chrome에서 localhost:80 하면 작동하는거임.
docker ps => 현재 실행되고있는 컨테이너 목록이 뜸
docker stop webserver => 이름지은 거(--name webserver) stop하는 용도

이미지 (게임칩)

이미지 다운로드

  • docker pull 이미지명 => 도커의 이미지를 다운받는다.이미지 잘 다운받아져있는지 확인
  • docker image ls => 이미지 리스트 확인Docker Hub
  • github과 유사하게, 이미지를 저장, 다운할 수 있는 저장소 역할. 여기서 pull 해옴
  • Tags => 다양한 버전이 있음. 특정 버전을 나타내는 태그 명.
  • docker pull 이미지명:Tag 하면 특정 태그의 이미지를 다운 받을 수 있음.
  • 태그가 없으면, 가장 latest태그를 붙여서 다운받는다.

이미지 조회

  • docker image ls
    Repository = 이미지명
    Tag = 버전
    IMAGE ID => 고유 ID
    CREATED => 그 이미지를 만든 회사에서 언제 만들었는지
    SIZE => 그 크기
  • docker image rm IMAGE_ID
    • 이미지 ID의 일부만 쳐도 삭제가 됨.(그 일부가 고유할때)
  • docker image rm 중, unable to delete - stopped container오류발생 시
    • 멈춘 컨테이너에 포함되어있는 이미지라서 지워지지가 않음
    • docker image rm -f <- 멈춘 컨테이너에 포함된건 지울수있음
    • 실행중인 컨테이너의 이미지는 지울 수 없음. 컨테이너 멈추고 > 이미지 삭제해야함.
  • docker image rm $(docker images -q)
    • docker images -q => Docker의 ID만 출력함
    • 근데 windows cmd에서는 못씀.. 그럼 젠킨스에서는 for문으로 반복하든 해야겠음.
    • for /f %i in ('docker images -q') do docker image rm %i 요런 느낌

컨테이너 (미니 컴퓨터)

  • docker create nginx => 컴퓨터 만들기
  • docker start 컨테이너_ID => 컴퓨터 실행하기
    이러면 STATUS에 UP 13 seconds 처럼 컴퓨터가 켜진 시간이 나옴.
  • docker create ~~ => 컴퓨터 만들 때, 이미지(게임 칩)이 없어도 docker hub에서 알아서 다운 받음
  • docker run => docker create + docker start, 즉 컴퓨터 만들고 실행
    • -d 옵션 = 백 그라운드에서 실행
    • -p 옵션 = 포트를 연결하는 기능
      이미지 = 호스트에서 4000번 포트로 요청을 보내면 80번 포트와 연결시키겠다.
      localhost:4000으로 연결 가능.


      컨테이너 조회 / 중지 / 삭제
  • 조회
    docker ps => 실행 중인 컨테이너 조회
    -a 옵션 = 중단된 컨테이너 포함 조회
  • 중지
    docker stop 컨테이너_ID => 실행 중인 컨테이너 멈추기 (정상적으로 종료)
    docker kill 컨테이너_ID => 실행 중인 컨테이너 "강제 종료"
  • 삭제
    docker rm 컨테이너_ID => 컨테이너 삭제
    docker rm $(docker ps -qa) => 중지된 컨테이너 모두 삭제인데.. 여기도 $명령어는 안됨
    docker rm -f 컨테이너_ID => 실행 중인 컨테이너 중지 후 삭제컨테이너 로그 조회실행(docker run) 상태에서 로그를 읽어볼 수 있는 방법이 있음
    docker logs 컨테이너_ID => 로그 읽기
    • --tail 숫자 옵션 = 숫자만큼 꼬리의 로그를 불러옴. (너무길때 추천)
    • -f 옵션 = 도커의 실시간 로그 볼수있게해줌 (백그라운드로 돌린 도커를 포어그라운드로 보기)
    • --tail 0 -f = 이전꺼 말고 지금 시점부터의 로그 조회실행중인 컨테이너 내부 접속실행(docker run) 상태의 도커 내부 진입함
  • docker exec -it 컨테이너_ID bash => 컨테이너에 bash 쉘로 접속한다.

 

 

+ Recent posts