Docker

불타는강정·2022년 4월 29일
0
post-thumbnail

Docker & Kubernetes: 실전 가이드 -2022년판

1일차

도커

컨테이너를 생성하고 관리하기 위한 도구

  • 컨테이너에 코드, 패키지, 코드의 종속성을 보관할 수 있고
  • 도커가 있는 환경 어디에서나 실행할 수 있다
  • 동일한 환경에서 정확히 동일한 애플리케이션을 실행한다.

버츄얼머신을 사용할 수 없을까?

버츄얼 머신을 사용한다고 가정하면

  • virtual OS을 설치하고
  • 라이브러리도 설치하고
  • 그 위에 어플을 실행시켜야 한다

  • 머신이 여러 대 있을 경우 매번 새로운 컴퓨터를 머신 내부에 설치
  • 메모리, CPU, 하드 드라이브의 공간을 낭비한다
  • 운영체제를 중복으로 설치한다

도커는 어떤 구조로 되어 있을까?

  • 운영체제가 기본적으로 내재하고 있거나
  • 컨테이너 에뮬레이트를 지원하는 내장 컨테이너를 활용한다
  • 그 위에 도커 엔진을 실행한다
  • 도커 엔진 위에 여러 컨테이너를 올릴 수 있다

  • 설정파일로 컨테이너를 구성하고
  • 다른 사람과 파일을 공유할 수 있다
  • 이미지로 다른 사람과 공유도 가능하다

Dockerfile

  • 컨테이너를 설정하는 방법을 작성한다
FROM node:14 # 사용할 이미지, Nodejs를 사용할 수 있다

WORKDIR /app # /app 디렉토리에서 작업한다

COPY package.json . # 패키지 파일을 현재 위치로 복사한다

RUN npm install # 종속성을 설치 

COPY . . # 나머지 코트를 복사

EXPOSE 3000 # 3000 포트를 외부에 노출함

CMD [ "node", "app.mjs" ] # 실행한다

docker build .

컨테이너가 통신하려는 포트가 있으므로 (EXPOSE 3000) 포트를 열어줘야 한다

docker run -p 3000:3000 $IMAGE_ID

3000에 3000을 게시한다

컨테이너 대신 포트 3000에서 실행되는 애플리케이션과 연결할 수 있다
-> 컨테이너와 호스트 운영체제 사이에는 디폴트 연결이 없기 때문에

컨테이너에서 실행 중인 애플리케이션에 HTTP 요청을 보내려면 통신하려는 컨테이너의 포트를 열어야 한다.

docker ps로 실행중인 컨테이너 이름을 가져온다
docker stop $CONTAINER_NAME 컨테이너가 중지되고 종료된다.


2일차

이미지와 컨테이너의 차이점

  • 이미지는 컨테이너의 설계도이다
  • 이미지는 실제로 코드와 코드를 실행하는데 필요한 도구를 포함한다
  • 그 다음 컨테이너가 실행되어 코드를 실행한다

이미지는 모든 설정 명령과 모든 코드가 포함된 공유 가능한 패키지이다.
컨테이너는 그러한 이미지의 구체적인 실행 인스턴스이다.
이미지 하나로 여러 컨테이너를 생성할 수 있다.

Docker HUB에서 이미지 가져오기

- 공식적인 도커 이미지를 들고올 수 있다
- `docker run node`
- node를 기반으로 하는 컨테이너를 생성한다

docker ps -a

-a: 도커가 생성한 모든 컨테이너, 모든 프로세스가 표시된다.

docker run -it node

-it: 컨테이너 내부로 들어가서 상호작용 할 수 있다

Dockerfile로 이미지 빌드하기

도커파일이란?
자체 이미지를 빌드할 때 실행하려는 도커에 대한 명령이 포함된다.

FROM node:14 # 다른 베이스 이미지에 나의 이미지를 구축할 수 있다

WORKDIR /app # 이후 명령이 /app 하위에 실행 됨

COPY package.json . # 먼저 복사를 해서 npm install 다음에 소스코드 복사를 함
					# 소스코드 복사를 하더라도 이전 레이어의 변경이 없다 

RUN npm install # 종속성을 설치, RUN은 이미지가 빌드될 때 실행된다

COPY . ./ # 이미지 외부 경로, 파일을 저장해야 하는 이미지 내부의 경로
		 # 이 프로젝트의 모든 폴더, 하위 폴더 및 파일을 복사해야 한다고 도커에 알린다

EXPOSE 80 # 80 포트를 외부에 노출함

CMD [ "node", "app.mjs" ] # CMD는 이미지를 기반으로 컨테이너가 시작될 때 실행된다

도커 파일로 이미지를 빌드한다
docker build .
. : 도커 파일이 있는 경로

도커 컨테이너를 실행한다
docker run $IMAGE_ID

도커 컨테이너를 멈춘다
docker stop $CONTAINER_NAME
컨테이너와 컨테이너 내부에서 실행 중인 노드 서버가 종료된다

EXPOSE 80을 했지만 포트 연결이 안 되는 이유?
실제로 이 명령은 문서화 목적으로만 추가되었다.
컨테이너의 프로세스가 이 포트를 통해 노출할 것임을 적어놓았다.

docker run -p 3000:80 $IMAGE_ID
-p 옵션으로 도커에게 어떤 로컬 포트가 도커의 특정 포트에 엑세스 할 수 있는지 알려준다.
3000: 로컬 포트 지정
80: 내부 도커 포트 지정 (EXPOSE 80)

이제 localhost:3000으로 접속이 가능하다.

이미지 레이어 이해하기

docker build .
도커 이미지를 다시 빌드한다면 엄청 빠르게 빌드하는데, 이는 캐시를 사용했기 때문이다.
도커는 모든 명령 결과를 캐시하고 이미지를 빌드할 때 명령을 다시 실행할 필요가 없다면 캐시를 사용한다.
이를 레이어 기반 아키텍쳐라고 한다. 모든 명령은 Dockerfile의 레이어를 나타낸다

도커 명령어

docker --help
도커 도움말

docker ps
모든 컨테이너를 리스트 할 수 있다

-a : 더이상 실행되지 않는 중지된 컨테이너를 포함하여 과거에 있었던 모든 컨테이너를 표시

docker run을 실행하면 이미지를 바탕으로 새 컨터이너를 만들어서 실행한다.
docker start $CONTAINER_NAME 으로 컨테이너를 다시 실행할 수 있다.
다만 start는 기본으로 detached 모드가 디폴트이고 run은 attached 모드가 디폴트이다.

docker run -d
-d: detached 모드

docker attach $CONTAINER_NAME
detached 모드를 attach 모드로 변경할 수 있다

docker logs $CONTAINER_NAME
컨테이너에 출력된 과거의 로그를 볼 수 있다
-f: 컨테이너와 다시 연결해서 향후 출력할 로그도 보여준다

docker start -a
-a: attach 모드

docker run -it
-i: 인터렉티브 모드로 실행
-t: pseudo-TTY 터미널을 생성한다
-it를 같이 입력하면 입력도 가능하고 컨테이너에 노출되는 터미널도 얻게 된다

도커를 다시 시작했을 때 dettach 모드가 되는데 입력받을 방법이 없을까?

docker start -a -i %COMTAINER_NAME

-t옵션은 유지되기 때문에 화면에 출력되는 -a 옵션과 인터렉티브 모드로 실행하는 -i옵션을 적어줘야 한다.

docker rm $CONTAINER_NAME
컨테이너를 제거한다

docker images
이미지 리스트

docker rmi $IMAGE_ID
이미지 삭제
컨테이너가 삭제된 상태여야 이미지를 삭제할 수 있다 (실행, 중지 상태면 안 됨)

현재 실행 중인 컨테이너에서 사용되지 않는 이미지를 삭제한다
docker image prune

docker run -p 3000:80 -d --rm $IMAGE_ID
--rm: 컨테이너가 종료될 때 자동으로 제거되는 플래그

3일차

이미지 상세정보 찾아보기

이미지는 용량이 크지만 실행중인 컨테이너는 용량이 그렇게 크지 않다.
명령 레이어가 이미지 위에 추가된 얇은 부가 레이어이다.
그래서 이미지 코드는 실행 중인 컨테이너에서 사용된다.
컨테이너는 이미지를 기반으로 빌드되고, 동일한 이미지를 기반으로 실행되는 여러 컨테이너는 이미지 내부의 코드를 공유한다. 그래서 이미지 내부의 코드는 잠겨있다.

docker image inspect $IMAGE_ID
이미지 정보를 출력한다.

  • 이미지 전체 ID
  • 생성된 날짜
  • 이미지를 기반으로 생성된 컨테이너 구성 (환경변수, 포트, 엔트리포인트)
  • 사용중인 도커 버전
  • 사용중인 운영체제 버전 (EX 리눅스 운영체제 이미지를 기반으로 구축)
    - FROM node:14 이 이미지의 운영체제를 따라가는 것 같다
  • 이미지 레이어들

컨테이너로부터 파일 복사하기

컨테이너가 이미 실행 중인 상태에서 무언가를 추출하고 싶다면 어떻게 해야 할까?
docker cp
실행 중인 컨테이너로 또는 실행 중인 컨테이너 밖으로 파일 또는 폴더를 복사할 수 있다.

docker cp dummy/. CONTAINERNAME:/testdummy/.dummy하위에있는모든파일을복사한다CONTAINER_NAME:/test `dummy/.` dummy 하위에 있는 모든 파일을 복사한다 `CONTAINER_NAME:/test`: 옮길 위치, 컨테이너의 /test 하위에 파일을 복사한다

docker cp $CONTAINER_NAME:/test dummy
위치를 반대로 하면 컨테이너에 있는 파일을 로컬로 복사한다

컨테이너와 이미지에 이름 지정 & 태그 지정하기

docker run -p 3000:80 -d --rm --name goalisapp $IMAGE_ID
--name: 컨테이너 이름을 지정할 수 있다

이미지 태그는 name:tag 형식으로 되어 있다
node:14에서 버전 14가 태그이다

docker build -t goals:latest .
-t: 이미지 태그를 받을 수 있다

이미지를 공유하는 법

  1. 도커파일을 공유해서 직접 빌드할 수 있게 한다
  2. 빌드된 이미지를 공유할 수 있다

docker push $IMAGE_NAME
docker pull $IMAGE_NAME

docker push 계정/도커허브레포:태그이름
다만 로컬 이미지 이름을 계정/도커허브레포:태그이름로 지정해야 한다

  1. docker build -t 계정/도커허브레포:태그이름 .
  2. docker tag $OLD_CONTAINER_NAME:tag 계정/도커허브레포:태그이름
    이미지를 복제해서 새로운 이름을 지정한다

docker login
도커허브에 로그인할 수 있다

주의사항
도커 pull을 하면 로컬에 있는지 확인한 후 없으면 이미지를 받아온다.
만약 로컬에 이전 버전이 이미 있다면 이전 버전을 사용한다.

4일차

데이터 관리 및 볼륨으로 작업하기

데모 앱 구축

FROM node:14

WORKDIR /app

COPY package.json

RUN npm install

COPY . .

EXPOSE 80

CMD ["node", "server.js"]

도커 파일로 이미지 빌드
docker build -t feedback-node .

feedback-node 이미지로 컨테이너 실행
docker run -p 3000:80 -d --name feedback-app --rm feedback-node

도커 중지시 --rm 옵션으로 자동으로 삭제된다
docker stop feedback-app

컨테이너는 파일을 생성할 때 파일을 이미지에 쓰지 않는다.
대신 상단에 추가되는 자체 read-write 레이어에 저장한다

Volumes

볼륨은 호스트 머신의 하드 드라이브가 컨테이너로 매핑된다.

Volume을 지정하는 방법

  1. 익명 볼륨
  2. 네임드 볼륨

Anonymous Volumes

RUN npm install

COPY . .

EXPOSE 80

VOLUME ["/app/feedback"] // 첫번째 인자: 컨테이너 내부의 경로

CMD ["node", "server.js"]

호스트 컴퓨터의 경로를 지정하지 않았을 떄 도커는 익명 볼륨을 지정한다.
도커가 관리하는 임의의 장소에 있지 사용자는 그 폴더가 어디있는지 모른다.
폴더에 따로 엑세스할 수도 없다.

docker volume ls
도커가 관리중인 모든 볼륨을 리스팅한다

익명 볼륨은 컨테이너가 중지되면 사라진다! -> 데이터를 유지할 수 없다

Named Volumes

영구적이어야 하는 데이터이고 편집하거나 직접 볼 필요가 없는 중요한 데이터에 적합하다.
마찬가지로 도커가 관리하는 데이터이며 실질적으로 호스트 머신의 폴더에 엑세스하지 않을 것이기 때문

도커 파일에 작성하는 건 아니고 컨테이너를 실행할 때 생성해야 한다

docker run -p 3000:80 -d --rm --name feedback-app -v feedback:/app/feedback feedback-node
-v feedback:/app/feedback: 컨테이너에 볼륨을 추가할 수 있다
볼륨의 이름은 feedback이고 /app/feedback을 볼륨에 저장한다

  • 컨테이너가 종료되어도 named volume은 종료되지 않는다
  • 다시 named volume에 접근할 수 있다

궁금한 점: named volume에 여러 컨테이너가 접근할 수 있을까? - 가능하다

익명 볼륨 제거하기

--rm 옵션 없이 컨테이너를 시작하면 docker rm으로 제거해도 익명 볼륨이 제거되지 않는다.
그래도 컨테이너를 다시 실행하면 새 볼륨이 생성된다.

  • `docker volume rm $NAME
  • docker volume prune
    사용하지 않는 익명 볼륨이 쌓이기 시작하는데 위 명령어로 볼륨을 삭제할 수 있다.

bind mounts

호스트 머신의 경로를 직접 설정할 수 있다.

docker run -p 3000:80 -d --rm --name feedback-app -v /Users/mimseong/Document/udemy:/app/feedback feedback-node
-v "/Users/mimseong/Document/udemy:/app": : 앞에 절대 경로를 적어준다. 상대 경로가 아님에 유의하자

전체 경로를 복사하고 싶지 않다면 -v $(pwd):/app로 사용

위와 같이 했을 때 문제가 발생할 수 있다.

RUN npm install

COPY . .

npm install한 뒤 COPY . .를 하면 현재 마운트 되어 있는 /app을 다시 덮어써버린다.
그래서 install한게 날라가버린다.

docker run -p 3000:80 -d --rm --name feedback-app -v "/Users/mimseong/Document/udemy:/app" -v /app/node_modules feedback-node
익명 볼륨을 하나 더 추가한다 -v /app/node_modules

앞의 볼륨은 /app이고 뒤의 볼륨은 /app/node_modules이다.
도커는 더 구체적인 경로를 선택한다. 그래서 뒤의 경로가 살아남아서 /app과 공존한다

nodemon

nodemon sercer.js

노드몬을 사용하면 코드가 변경됐을 경우 서버를 다시 로드한다.

읽기 전용 볼륨

기본적인 권한은 read-write라 컨테이너가 볼륨에 데이터를 쓸 수 있다.
-v "/Users/mimseong/Document/udemy:/app:ro": 끝에 :ro를 붙여서 읽기 권한만 부여할 수 있다.

도커 볼륨 관리하기

docker volumn create asdf
asdf라는 볼륨을 만들 수 있다

inspect으로 상세한 정보를 볼 수 있다.
Mountpoint로 마운트 위치를 볼 수 있는데, 이는 실제 경로가 아니라 도커 가상머신의 경로라 찾기 어렵다.

COPY 안 쓰고 볼륨으로 관리할 수는 없을까?

가능하긴 하다! 하지만 개발용 말고 배포할 때는 코드를 마운트 해서 쓸 수 없다.
도커파일에 COPY . .를 해놓고 개발 중일 때 -v로

.dockerignore

COPY 명령으로 복사해서는 안 되는 폴더와 파일을 지정할 수 있다.

# .dockerignore
node_modules
.git

ARG, ENV

ARG

  • 도커 빌드 시에만 사용할 수 있다
  • 도커파일 내부에서만 유용
  • CMD에서 사용할 수 없다
  • 어플리케이션 내부에서 사용할 수 없다
    ENV
  • 도커파일과 어플리케이션 코드 둘 다 사용가능
# Dockerfile
ENV PORT 80
EXPOSE $PORT

ENV

docker run -p 3000:8000 --env PORT=8000 --env ASDF=1234
-e PORT=8000: 환경변수를 추가할 수 있다

# .env
PORT=8000

docker run -p 3000:8000 --env-file ./.env
--env-file: 환경변수 파일을 지정할 수 있다

ARG

# Dockerfile
ARG DEFAULT_PORT=80
ENV PORT DEFAULT_PORT
EXPOSE $PORT

docker build -t feedback-node:dev --build-arg DEFAULT_PORT=8000

5일차

네트워킹: (교차) 컨테이너 통신

네트워킹 방법
1. WWW 인터넷으로 통신하는 방법
2. 호스트 머신으로 통신하는 방법
3. 다른 컨테이너로 통신하는 방법

WWW 인터넷으로 통신하는 방법

기본적으로 컨테이너는 월드 와이드 웹에 요청을 보낼 수 있다.

호스트 머신으로 통신하는 방법

도커에서 호스트 머신으로 요청을 할 수 없는데 그럴 경우 localhost대신 host.docker.internal을 사용해야 한다.
host.docker.internal는 도커 컨테이너 내부에서 알 수 있는 호스트 머신의 IP 주소로 변환된다.

다른 컨테이너로 통신하는 방법

docker container inspect 명령어로 몽고디비 컨테이너를 검사한다.
NetworkSettings에 IPAddress가 컨테이너의 IP 주소이다.
이제 이 IP를 적어주면 되는데, 아시다시피 아주 번거로운 방법이다! 그래서 다른 방법을 추천한다 ↓

Docker Networks

docker run 명령에 --network 옵션을 추가하면 모든 컨테이너를 하나의 네트워크로 묶을 수 있다.

docker network create favorites-net

볼륨과 달리 네트워크는 직접 만들어줘야 한다

docker run -d --name mongodb --network favorites-net mongo

두 컨테이너가 동일한 네트워크의 일부분인 경우 다른 컨테이너 이름을 적을 수 있다 mysql:27017

-p 옵션은 로컬 호스트 머신이나 컨테이너 네트워크 외부에서 컨테이너에 연결할 때 사용한다.
몽고디비 컨테이너에 연결되는 유일한 것은 favorite 컨테이너이며, 이는 동일한 도커 네트워크의 일부분이 된다.
컨테이너 간에 연결이 있자면 포트를 설정할 필요가 없다.

6일차

도커로 다중 컨테이너 어플리케이션 구축하기

DB, 백엔드, 프론트엔드 컨테이너 띄우기

몽고DB 도커로 띄우기
docker run --name mongodb --rm -d -p 27017:27017 mongo

백엔드 도커로 띄우기

From node:14

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80

CMD ["node", "app.js"]

도커 이미지 빌드
docker build -t goals-node .

도커 컨테이너 띄우기
docker run -p 80:80 --name goals-backend --rm goals-node

프론트엔드 도커로 띄우기

FROM node

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

도커 이미지 빌드
docker build -t goals-react .

도커 컨테이너 띄우기
docker run -p 3000:3000 --rm --name --it goals-frontend goals-react
--it를 넣어야만 리엑트가 계속 실행이 된다

컨터이너 통신을 위한 네트워크 구축하기

네트워크 생성
docker network create goals-net

같은 네트워크로 통신하게 네트워크를 추가했다
docker run --name mongodb --rm -d --network goals-net mongo
docker run --network goals-net -p 80:80 --name goals-backend --rm goals-node
docker run -p 3000:3000 --rm --name --it goals-frontend goals-react

localhost:27017 -> mongodb:27017
로컬호스트 대신 컨테이너 이름을 적어줘야한다

리엑트는 도커 컨테이너가 아닌 브라우저에서 실행되기 때문에 localhost로 적어야 하고, 백엔드틑 80번 포트를 열어놔야 한다.

백엔드와 상호작용 하는 부분은 컨테이너가 아닌 브라우저에서 돌아가므로 네트워크가 필요 없다.

볼륨으로 몽고DB 지속성 추가하기

몽고디비 도커 문서를 보면 컨테이너 내부에 파일을 저장하는 곳을 알려준다. /data/db

docker run --name mongodb -v data:/data/db --rm -d --network goals-net mongo

named volume을 사용해서 컨테이너가 죽더라고 데이터를 살려놓을 수 있다.

몽고DB 사용자 이름과 비밀번호 지정하기

마찬가지로 몽고디비 문서를 보면 사용자 이름과 비밀번호를 지정하는 방법을 알 수 있다.

-e MONGO_INITDB_ROOT_USERNAME=max -e MONGO_INITDB_ROOT_PASSWORD=secert

환경변수로 지정할 수 있다.

볼륨으로 로그파일 저장

docker run --network goals-net -p 80:80 --name goals-backend -v /Users/mimseong/..../backend:/app -v logs:/app/logs -v /app/node_modules --rm goals-node

-v /Users/mimseong/..../backend:/app: 폴더 바인딩
-v logs:/app/logs: named volume 생성
-v /app/node_modules: anonymous volume

/app/logs/Users/mimseong/..../backend:/app에 바운딩 되더라도 모르는 동일 폴더에 덮여쓰여지지 않는다.
더 구체적인 경로를 컨테이너에 명시했기 때문.

node_modules는 컨테이너 통틀어 하나만 있으면 되기 때문에, 익명 볼륨으로 생성하면 좋다.

폴더 바인딩을 해서 코드가 실시간으로 반영된다.

환경변수로 아이디와 비밀번호 넘기기

# Dockerfile
FROM node

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

EXPOSE 80

ENV MONGODB_USERNAME=root
ENV MONGODB_PASSWORD=secret

CMD ["npm", "start"]

mongodb://process.env.MONGODBUSERNAME"{process.env.MONGODB_USERNAME}"{process.env.MONGODB_PASSWORD}@mongodb:27017

환경변수로 관리할 수 있다.

docker run --network goals-net -p 80:80 --name goals-backend -v /Users/mimseong/..../backend:/app -v logs:/app/logs -v /app/node_modules -e MONGODB_USERNAME=max -e MONGODB_PASSWORD=password --rm goals-node

.dockerignore

COPY로 복사하고 싶지 않은 경우 .dockerignore로 설정할 수 있다.

# .dockerignore
node_modules
Dockerfile
.git

node_modules: 모든 종속성을 불필요하게 다시 복사하지 않도록 하자
Dockerfile: default 환경변수들이 적혀있다. 조심!

정리

  • 이째까지 개발용으로 도커를 구축하는 방법을 알아보았다
  • 배포용 도커는 이후 배울 예정이다
  • 도커를 실행하기에 옵션이 아주 많았다

7일차

Docker Compose: 우아한 다중 컨테이너 오케스트레이션

docker compose란?

docker builddocker run 명령을 대체할 수 있는 도구이다.

다중 컨테이너 설정을 더 쉽게 관리할 수 있게 해준다.

  • 도커 컴포즈는 커스텀 이미지를 위한 Dockerfile을 대체하지 않는다
  • 도커 컴포즈는 이미지나 컨테이너를 대체하지 않는다
  • 도커 컴포즈는 다수의 호스트에서 다중 컨테이너를 관리하는데는 적합하지 않다

도커 컴포즈는 하나의 호스트에서 다중 컨테이너를 관리하는데 적합하다

서비스: 컨테이너

컴포즈 파일 만들기

# docker-compose.yaml

version: "3.8" 

도커 컴포즈의 어떤 버전을 사용할 것인가 명시

# docker-compose.yaml

version: "3.8" 
services:
	mongodb:
	backend:
	frontend:

서비스 하위에 컨테이너의 이름을 명시한다. mongodb, backend, frontend 세 개의 컨테이너를 구동한다.

version: "3.8" 
services:
	mongodb:
		image: 'mongo'
	backend:

	fromtend:

이미지 이름을 mongo로 한다.
도커는 mongodb 컨테이너가 mongo 이미지를 기반으로 해야 한다는 걸 안다.
mongo는 이미지 이름일 뿐이며, 로컬이나 도커 허브에서 조회된다.

도커 컴포즈는 --rm, -d가 기본 세팅이다.

version: "3.8" 
services:
	mongodb:
		image: 'mongo'
		volumes:
			- data:/data/db
	backend:

	fromtend:

볼륨은 -와 함께 볼륨을 적어주면 된다.

version: "3.8" 
services:
	mongodb:
		image: 'mongo'
		volumes:
			- data:/data/db
		enviroment:
			# MONGODB_USERNAME: max
			- MONGODB_USERNAME=max
			- MONGODB_PASSWORD=password
	backend:

	fromtend:

환경변수를 설정하는 방법이다.
-를 붙이는 방법, 안 붙이는 방법 두 가지가 있다.

version: "3.8" 
services:
	mongodb:
		image: 'mongo'
		volumes:
			- data:/data/db
		env_file:
			- ./env/mongo.env
	backend:

	fromtend:

환경변수를 파일로 관리할 수 있다.

네트워크는?
하나의 동일한 컴포즈 파일에 정의된 모든 서비스는 동일한 네트워크의 일부가 된다.

version: "3.8" 
services:
	mongodb:
		image: 'mongo'
		volumes:
			- data:/data/db
		env_file:
			- ./env/mongo.env
		networks:
			- goals_net
	backend:

	fromtend:

따로 설정하고 싶다면 이렇게 설정할 수 있다.
mongodb 서비스는 도커에 의해 자동 생성된 디폴트 네트워크 뿐만 아니라 파일에 지정된 특정 네트워크(goals_net)에도 추가된다.

version: "3.8" 
services:
	mongodb:
		image: 'mongo'
		volumes:
			- data:/data/db
		env_file:
			- ./env/mongo.env
	backend:

	fromtend:
volumes:
	data:

volumes: 하위에는 services: 에서 사용중인 명명된 볼륨이 나열되어야 한다.
익명 볼륨과 바인드 마운트는 여기에 지정할 필요가 없다.

docker compose up과 down

docker-compose up

이미지를 가져와 빌드하고 컨테이너를 시작한다

네트워크는 프로젝트 이름 + default로 자동 설정된다.
볼륨도 프로젝트 이름 + data(아까 설정한 named volume)으로 설정된다.

기본적으로 attach 모드로 실행돼서 docker-compose up -d로 dettached 모드로 실행할 수 있다.

docker-compose down

모든 서비스를 중지하고 모든 컨테이너를 제거한다

docker-compose down -v

볼륨까지 삭제한다

백엔드 docker compose 설정

version: "3.8" 
services:
	mongodb:
	backend:
		image: 'goals-node'
	fromtend:
volumes:
	data:

이미지는 goals-node로 설정한다.
완성된 이미지를 제공하는 대신 도커 컴포즈에 이미지를 빌드하는데 필요한 모든 정보를 제공할 수도 있다.

version: "3.8" 
services:
	mongodb:
	backend:
		image: 'goals-node'
		# build: ./backend
		build:
			context: ./backend
            dockerfile: Dockerfile
	fromtend:
volumes:
	data:

build 옵션은 Dockerfile을 찾을 수 있는 경로를 적어야 한다.
context, dockerfile로 조금 더 자세히 적을 수 있다.
context는 도커 파일의 경로, dockerfile은 도커 파일의 이름을 표시한다.

context는 도커파일이 빌드되는 장소이기도 하다. 이미지가 생성되는 위치이다.
복사할 폴더를 포함하는 폴더로 설정되어야 한다.

version: "3.8" 
services:
	mongodb:
	backend:
		image: 'goals-node'
		# build: ./backend
		build:
			context: ./backend
			dockerfile: Dockerfile
			args: 
			    buildno: 1
			    gitcommithash: cdc3b19
	fromtend:
volumes:
	data:

args 설정도 가능하다

version: "3.8" 
services:
	mongodb:
	backend:
		build: ./backend
		ports:
			- '3000:80'
	fromtend:
volumes:
	data:

ports 옵션으로 포트를 지정 가능하다.
만약 여러 포트를 추가하고 싶으면 -로 추가하면 된다.

version: "3.8" 
services:
	mongodb:
	backend:
		build: ./backend
		ports:
			- '3000:80'
		volumes:
			- logs:/app/logs
			- ./backend:/app
			- /app/node_modules
	fromtend:
volumes:
	logs:

named volume은 아래 볼륨 하위에 이름을 명시해야 한다.
anonymous volume은 따로 명시할 필요가 없다.
마운트는 도커와 다르게 상대경로를 지정 가능하다!

version: "3.8" 
services:
	mongodb:
	backend:
		build: ./backend
		ports:
			- '3000:80'
		volumes:
			- logs:/app/logs
			- ./backend:/app
			- /app/node_modules
		env_file:
			- ./env/backend.env
	fromtend:
volumes:
	logs:

환경변수 설정하기
env_file 옵션으로 환경변수 파일로 지정할 수 있다.

version: "3.8" 
services:
	mongodb:
	backend:
		build: ./backend
		ports:
			- '3000:80'
		volumes:
			- logs:/app/logs
			- ./backend:/app
			- /app/node_modules
		env_file:
			- ./env/backend.env
		depends_on:
			- mongodb
	frontend:
volumes:
	logs:

depends_on은 도커 컴포즈에만 있는 옵션이다.
도커 컴포즈를 사용하면 여러 서비스를 만들고 시작한다.
이미 실행되고 있는 다른 컨테이너에 의존할 수 있다.

백엔드는 몽고디비에 의존한다.
depends_on으로 mongodb를 먼저 불러와야 한다는 걸 알려줘야한다.

  • 코드에 컨테이너 이름을 적어야 하는데,
    도커 컴포즈를 사용한다면 서비스 이름을 적어도 된다!
    실제 도커 컴포즈로 띄우면 컨테이너 이름은 다르게 설정되어 있다. docker-complete_mongodb_1 하지만 도커가 서비스 이름을 기억해서 잘 돌아간다.
version: "3.8" 
services:
	mongodb:
	backend:
	frontend:
		build: ./frontend
		ports:
			- '3000:3000'
		volumes:
			- ./frontend/src:/app/src
		stdin_open: true
		tty: true
volumes:
	logs:

--it옵션 대신에
stdin_open 옵션으로 개방형 입력 연결이 필요하다고 도커에게 알린다.
tty 옵션으로 터미널에 연결한다.
위 두 옵션으로 --it 옵션을 나타낼 수 있다.

version: "3.8" 
services:
	mongodb:
	backend:
	frontend:
		build: ./frontend
		ports:
			- '3000:3000'
		volumes:
			- ./frontend/src:/app/src
		stdin_open: true
		tty: true
		depends_on:
			- backend
volumes:
	logs:

마찬가지로 depends_on으로 종속성을 추가할 수 있다.

이미지 빌드 & 컨테이너 이름 이해하기

docker-compose up --build
--build 옵션을 붙이면 강제로 이미지를 빌드한다. 빌드는 build:에 정해진 대로 빌드한다.

docker-compose build
도커 이미지만 빌드한다

docker-compose up
도커 이미지를 빌드하고 컨테이너를 띄운다

version: "3.8" 
services:
	mongodb:
	backend:
	frontend:
		container_name: frontend
volumes:
	logs:

서비스 이름이 컨테이너 이름은 아니다.
만약 컨테니어 이름을 따로 지정하고 싶다면 container_name로 지정한다.

8일차

"유틸리티 컨테이너"로 작업하기 & 컨테이너에서 명령 실행하기

유틸리티 컨테이너: 왜 사용하는가?

노드를 띄우기 위해서는 노드를 설치해야 하고, package.json을 만들기 위해서는 npm init을 해야 한다. (그러기 위해서도 노드가 필요하다) 호스트에 노드를 설치하지 않고 도커를 활용할 수 있는데 이에 대한 방법을 알아보자.

컨테이너에서 명령을 실행하는 다양한 방법

docker run node

노드를 실행시키자마자 바로 죽어버린다.
왜냐면 노드는 인터렉티브 모드(-it)에서 실행시켜야 하기 때문이다.

docker run -it -d node
detached mode로 컨테이너를 실행한 다음

docker exec -it $CONTAINER_NAME npm init
exec 명령은 컨테이너가 실행하는 기본 명령 외에 실행중인 컨테이너 내에서 특정 명령을 실행할 수 있다.
-it: 프로세스에 계속 연결되고 입력을 제공하고 싶다면 인터렉티브 모드로 실행해야한다.

docker run -it node npm init
디폴트 명령을 오버라이드 할 수 있다. 컨테이너가 실행된 후 npm init 명령어를 실행한다.

유틸리티 컨테이너 구축

FROM node:14-alpine

WORKDIR /app

유틸리티 컨테이너라 알파인 버전을 사용한다.
alpine version: 초경량 버전

docker build -t node-util .
docker run -it -v 현재경로:/app node-util npm init

변경사항이 자동으로 반영되게 현재 프로젝트를 연결시킨다.
노드등 모든 부가 도구를 설치할 필요가 없다는게 유용하다.

ENTRYPOINT 활용

FROM node:14-alpine

WORKDIR /app

ENTRYPOINT [ "npm" ]

ENTRYPOINT는 CMD 명령과 비슷하다.
docker run -it -v 현재경로:/app node-util npm init
docker run에서 이미지 이름 뒤에 명령을 추가하면 npm init은 CMD 명령을 덮어쓴다.
ENTRYPOINT를 사용하면 이미지 이름 뒤의 npm init 명령을 ENTRYPOINT 뒤에 추가한다!

docker run -it -v 현재경로:/app node-util init
docker run -it -v 현재경로:/app node-util install
이런식으로 npm을 붙이지 않고도 활용 가능하다.

docker run -it -v 현재경로:/app node-util install express --save
express --save로 명시적 종속성을 설치할 수 있다.
-save은 명령이 express 패키지를 프로젝트에 대한 종속성으로 추가하도록 허용하는 플래그이다.

Docker Compose 사용

version: "3.8"
services:
	npm:
		build: ./
		stdin_open: true
		tty: true
		volumes:
			- ./:/app

docker-compose run npm init
run: 파일에 여러 서비스가 있는 경우에 단일 서비스만 실행할 수 있다.
run npm init: npm 서비스를 실행하고 뒤에 실행할 명령 init을 실행한다.

docker-compose down을 실행할 경우에는 자동으로 컨테이너를 삭제한다.
하지만 docker-compose run은 up과 down이 없다.

docker-compose run --rm npm init
--rm 옵션으로 중지시 컨테이너를 삭제한다

요약

유틸리티 컨테이너로 특정 명령을 실행하는데 사용할 수 있는 환경을 구성할 수 있다.

9일차

더 복잡한 설정: Laravel & PHP 도커화 프로젝트

Nginx(웹서버) 컨테이너 추가

docker-compose.yaml

version: "3.8"
services:
	server:
		image: 'nginx:stable-alpine'
		ports: 
			- '8000:80'
		volumes:
			- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
	php:
	mysql:
	composer:
	artisan:
	npm:

volume과 path는 nginx 문서에 나와 있는대로 설정한다. 공식문서를 잘 읽자!

server {
    listen 80; # 포트 설정
    index index.php index.html; # 인덱스 파일 요청 처리
    server_name localhost;
    root /var/www/html/public; # 들어오는 요청에 응답하는데 사용할 수 있는 파일을 여기서 찾는다
    location / { # 모든 수신 요청을 index.php 파일로 리다이렉션하거나 
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ { # php 파일을 대상으로 하는 요청이 php 인터프리터로 전달되도록 하는 리다이렉션 규칙
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:3000; # php: 서비스 이름, php 파일을 처리하라는 요청을 3000번 포트로 보낸다
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

PHP 컨테이너 추가

php는 공식 이미지를 기반으로 하는 커스텀 이미지를 만든다
-> 도커파일이 필요하다

php 뿐만이 아니라 Laravel이 필요로 한느 몇몇 확장 프로그램도 설치

# php.dockerfile

FROM 'php:7.4-fpm-alpine'

WORKDIR /var/www/html

# 우리가 필요로 하는 php 확장 프로그램
RUN docker-php-ext-install pdo pdo_mysql

끝에 CMD또는 ENTRYPOINT가 없으면 베이스 이미지의 CMD나 ENTRYPOINT를 사용한다.

베이스 이미지는 마지막으로 PHP 인터프리터를 호출한다.

	php:
		build:
			context: ./dockerfiles
			dockerfile: php.dockerfile
		volumes:
			- ./src:/var/www/html:delegated

delegated: 컨테이너가 일부 데이터를 기록해야 하는 경우에 그에 대한 결과를 호스트 머신에 즉시 반영하지 않고 배치로 기본 처리함으로써 성능이 약간 더 나아진다.
안정성은 떨어지지만, 속도가 향상됨

fastcgi_pass php:3000

nginx를 보면 3000번 포트를 사용하라고 했는데, php의 공식 도커파일을 보면 이미지가 실제로 9000번 포트를 노출한다는 것을 알 수 있다.

그래서 외부 포트는 3000, 내부 포트는 9000을 열어야 한다. -p 3000:9000

하지만~ 실제로는 nginx와 php의 통신이기 때문에 포트를 열어줄 필요가 없다. 그냥 nginx 설정을 9000으로 바꾸자.

fastcgi_pass php:9000;

MySQL 컨테이너 추가

환경 변수 설정

# mysql.env
MYSQL_DATAVASE=homestead # Laravel의 디폴트 값
MYSQL_USER=homestead
MYSQL_PASSWORD=secret
MYSQL_ROOT_PASSWORD=secret

도커 컴포즈 설정

version: "3.8"
services:
	server:
		image: 'nginx:stable-alpine'
		ports: 
			- '8000:80'
		volumes:
			- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
	php:
		build:
			context: ./dockerfiles
			dockerfile: php.dockerfile
		volumes:
			- ./src:/var/www/html:delegated
	mysql:
		 image: mysql:5.7
		 env_file:
		 	- ./env/mysql.env
	composer:
	artisan:
	npm:

Composer 유틸리티 컨테이너 추가

composer는 유틸리티 컨테이너로 사용한다.
처음에 Laravel 애플리케이션을 설정하는데 사용할 수 있다.

docker-compose.yaml

version: "3.8"
services:
	server:
		image: 'nginx:stable-alpine'
		ports: 
			- '8000:80'
		volumes:
			- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
	php:
		build:
			context: ./dockerfiles
			dockerfile: php.dockerfile
		volumes:
			- ./src:/var/www/html:delegated
	mysql:
		 image: mysql:5.7
		 env_file:
		 	- ./env/mysql.env
	composer:
		build:
			context: ./dockerfiles
			dockerfile: composer.dockerfile
		volumes:
			- ./src:/var/www/html
	artisan:
	npm:

Dockerfile

# composer.dockerfile

FROM 'composer:latest' # 컴포저 이미지가 이미 존재한다!

WORKDIR /var/www/html

# --ignore-platform-reqs 일부 종속성이 누락돼도 경고나 오류 없이 실행할 수 있다
ENTRYPOINT ["composer", "--ignore-platform-reqs"] 

Composer 유틸리티 컨테이너로 Laravel 앱 만들기

composer create-project --prefer-dist laravel/laravel blog

composer를 사용하여 Laravel 프로젝트를 설정할 수 있다

docker-compose run --rm composer create-project --prefer-dist laravel/laravel .

프로젝트가 생성될 폴더를 명령 끝에 특정해야 한다. .
도커파일에 WORKDIR 설정을 /var/www/html로 해서 여기에 Laravel 프로젝트가 생성된다.

일부 Docker Compose 서비스만 구동하기

	server:
		image: 'nginx:stable-alpine'
		ports: 
			- '8000:80'
		volumes:
			- ./src:/var/www/html # php 코드에 접근하기 위해서
			- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro

php 코드에 접근하기 위해서 볼륨을 추가해야 한다.

version: "3.8"
services:
	server:
		image: 'nginx:stable-alpine'
		ports: 
			- '8000:80'
		volumes:
			- ./src:/var/www/html # php 코드에 접근하기 위해서
			- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro

/etc/nginx/conf.d/default.conf
더 큰 nginx 구성으로 병합되는 특수 파일이라 할 수 있다.

docker-compose up server php mysql
up 뒤에 서비스 명을 적으면, 해당 서비스만 실행할 수 있다.

		depends_on:
			- php:
			- mysql:

php, mysql와 통신할 수 있을 때 실행 가능하다.

종속성을 추가하면 자동으로 다른 서비스도 실행한다.
docker-compose up server
server의 종속성인 php와 mysql도 실행한다.

추가해야 할 점
php와 같은 커스텀 이미지와 관련이 있다.
현재 docker-compose가 디폴트로 수행하는 작업은 이미지가 있는지 확인하고 이미지가 있으면 그 이미지를 사용한다. 결코 이미지를 리빌드하지 않는다! Dockerfile이 변경되면 docker-compose에 의해 적용되지 않는다. 이미지를 빌드하려면 docker-compose build를 해야 했는데, --build 옵션을 붙이면 변경사항이 있으면 이미지를 다시 빌드한다.

더 많은 유틸리티 컨테이너 추가하기

artisan

artisan은 PHP로 빌드된 Laravel 명령이다. 그래서 php가 필요하다 (php Dockerfile 재사용)

	artisan:
		build:
			context: ./dockerfiles
			dockerfile: php.dockerfile
		volumes:
			- ./src:/var/www/html
		entryfile: ["php", "/var/www/html/artisan"]

entryfile:: 도커파일에 아직 ENTRYPOINT가 없는 경우 오버라이드 하거나 추가할 수 있다.
여기서는 Dockerfile 내부에서 수행하는 것처럼 지정한다.

docker-compose run --rm artisan migrate
migrate: Laravel이 지원하는 artisan 명령 중의 하나
데이터베이스에 데이터를 기록한다.
따라서 이 데이터베이스 설정이 작동하는지 그 여부도 확인한다.

npm

	npm:
		image: node:14
		working_dir: /var/www/html
		entrypoint: ["npm"]
		volumes:
			- ./src:/var/www/html

working_dir, entrypoint처럼 도커파일 대신 컴포즈에 작성할 수 있다. (도커파일이 깔끔하긴 함)

Dockerfile이 있거나, 없는 Docker Compose

docker-compose 파일에는 COPY나 RUN 명령을 사용할 수 없다

바인드 마운트
배포시에는 바인드 마운트를 할 수 없다.
로컬 호스트의 파일 구조가 서버의 파일 구조와 다르기 때문. 똑같은 구조로 파일을 만드는 것도 번거롭다.

우리가 원하는 건 호스트에 있는 파일을 복사해서 이미지를 빌드하는 것이기 때문에 COPY로 해결할 수 있다.

바인드 마운트와 COPY: 언제 무엇을 사용하는가?

nginx의 바운드 마운트를 COPY로 변경한다.

# nginx.dockerfile
FROM nginx:stable-alpine

WORKDIR /etc/nginx/conf.d

COPY ./nginx/nginx.conf .

RUN mv nginx.conf default.conf

WORKDIR /var/www/html

COPY src .

바운드 마운트를 COPY로 복사함으로써 언제나 소스 코드의 스냅샷을 이미지에 복사하도록 보장한다.

	server:
		build:
			context: ./dockerfiles
			dockerfile: nginx.dockerfile

이전 같았으면 이런 식으로 도커파일을 작성했겠지만 실제로 동작하지 않는다.
context는 또한 도커파일이 빌드될 폴더를 설정한다.
nginx.dockerfile은 내부네 nginx, src 폴더를 참조하고 있다.
따라서 dockerfiles를 context로 설정한다면 ./dockerfile 폴더에 빌드 되는데 nginx와 src에는 접근할 수 없어서 이미지 빌드가 실패한다.

	server:
		build:
			context: .
			dockerfile: dockerfiles/nginx.dockerfile

그래서 이때는 context를 .으로 설정하고 도커파일 경로를 지정해준다.

php

RUN chown -R www-data:www-data /var/www/html
-R: 폴더 안의 모든 폴더와 파일에 대해 재귀적으로 수행
www-data: php 이미지에 의해 생성된 디폴트 사용자

profile
대충살자

0개의 댓글

관련 채용 정보