2023. 8. 5. 13:29ㆍ서울42
인셉션 전에 공부를 하자!
도커 공식 문서
지금 상태로는 인셉션 하지 못할거 같으니 공식 문서로 공부를 좀 해야겠다..
파트1. 개요
배우는 것.
- 이미지를 컨테이너로 빌드하고 실행하기
- Docker Hub 를 사용해 이미지를 공유하기
- 데이터베이스와 함께 여러 컨테이너를 사용해 Docker 애플리케이션 배포하기
- Docker Compose 를 사용해 애플리케이션 실행하기
컨테이너란??
- 해당 호스트 시스템에서 실행되는 다른 모든 프로세스와 격리된 호스트 시스템에서 실행되는 샌드박스 처리 된 프로세스.
- 이미지의 실행한 인스턴스.
- 로컬 컴퓨터, 가상 컴퓨터 모두 실행 가능, 클라우드에 배포 가능
- 이식 가능
- 다른 컨테이너와 격리되어 자체 소프트웨어, 바이너리, 구성 등을 실행한다.
컨테이너 이미지란?
실행 중인 컨테이너는 격리된 파일 시스템을 사용한다.
이 파일 시스템은 컨테이너 이미지에 의해 제공되며, 컨테이너 이미지에는 애플리케이션을 실행하는데 필요한 모든 것이 포함됨. (환경 변수, 실행할 기본 명령, 기타 메타데이터)
파트2. 애플리케이션 컨테이너화
컨테이너 이미지를 빌드하기 위해서는 Dockerfile 을 사용해야 한다. Dockerfile 은 text-based file 로, 확장자가 없다. 도커는 이 스크립트 파일을 이용해 컨테이너 이미지를 빌드한다.
실습
git clone https://github.com/docker/getting-started-app.git
일단 다운
- Dockerfile 파일을 만들자.
- 아래 내용을 적자.
# syntax=docker/dockerfile:1
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000
- 다음 명령어를 사용해 컨테이너 이미지를 빌드한다.
docker build -t getting-started .
docker build 명령은 dockerfile 을 사용해 새 컨테이너 이미지를 빌드 한다.
위 명령어를 실행하면 많은 layer 들을 다운로드 하는 것을 확인 할 수 있다.
이는 우리가 위의 dockerfile 에 node:18-alpine 에서 부터 이미지를 시작하고 싶다고 때문이다. 하지만, 우리의 머신엔 현재 없기 때문에, 도커가 이미지를 다운 받은 것이다.
docker 가 이미지를 다운 받은 후에, 애플리케이션에 복사된 dockerfile 의 명령어에 따라 yarn 을 사용해 애플리케이션 의존성을 설치했다. (무슨 말인지 모르겠음…)
→ Docker가 이미지를 다운로드한 후 Docker 파일의 지침을 응용 프로그램에 복사하고 응용 프로그램의 종속성을 설치하는 데 yarn을 사용했습니다. CMD 지침은 이 이미지에서 컨테이너를 시작할 때 실행할 기본 명령을 지정합니다.
마지마으로, -t 플래그를 이용해 이미지에 이름을 태그한다(?) 쉽게 이해하자면, 인간이 읽을 수 있는 이름을 최종 이미지에 부여한 것이다. 우리가 getting-started 란 이름을 이미지에 붙여주었기 때문에, 우리가 컨테이너를 실행할 때 해당이름을 이용해 컨테이너를 run 시킬 수 있다.
마지막의 . 은, 도커가 현재 디렉토리 내에서 Dockerfile 을 찾아야 한다는 의미이다.
앱 컨테이너 시작
이제, 이미지가 있으니 컨테이너에서 애플리케이션을 실행해보자.
docker run
명령을 사용한다.
docker run -dp 127.0.0.1:3000:3000 getting-started
플래그 -d 는, 백그라운드에서 컨테이너를 실행한다는 뜻이다. (detach)
플래그 -p 는, 호스트와 컨테이너 간 포트 매핑을 생성한다는 뜻. (publish)
-p 플래그는, HOST:CONTAINER 포맷의 스트링 값을 인자로 받는다. 이 때, host 는 호스트의 주소이고, container 는 컨테이너의 포트 번호이다.
위 명령어에서는, 컨테이너의 포트 3000 번을 호스트의 127.7.7.1:3000(localhost 3000) 번과 매핑한다.
Port Mapping 을 하지 않으면, 애플리케이션을 호스트에서 접속하지 못할 것이다.
이제, http://localhost:3000 여기 접속해 보면 app 이 실행되는 것을 볼 수 있다.
터미널에서
docker ps
명령어를 입력해 running 상태의 컨테이너를 확인 해 볼 수 있다. (docker desktop gui 도 사용 가능함.)
파트 3. 애플리케이션 업데이트
이번에는, 애플리케이션과 컨테이너 이미지를 업데이트 하고,, 컨테이너를 정지하고 제거하는 것도 해본다.
js 파일을 수정해서 todo app 화면에 보여지는 내용을 수정했다. 이제 이 바뀐 항목을 적용시켜 도커 빌드 하고, 열고 싶다!
저번에 했던 거처럼 해보자.
docker build -t getting-started .
docker run -dp 127.0.0.1:3000:3000 getting-started
이번에는 에러가 나올 것이다.
docker: Error response from daemon: driver failed programming external connectivity on endpoint laughing_burnell
(bb242b2ca4d67eba76e79474fb36bb5125708ebdabd7f45c8eaf16caaabde9dd): Bind for 127.0.0.1:3000 failed: port is already allocated.
이는, 아까 사용했던 컨테이너가 아직 실행중이고, 그게 3000번 포트를 먹고 있기 때문인 것이다. 따라서, 새로 만든 이미지로 컨테이너를 실행하려면 이전 걸 삭제해 주어야 한다.
docker ps
로 현재 컨테이너 들을 확인 할 수 있다. 이 걸로 컨테이너의 ID 를 구하자.
docker stop <container id>
docker stop 을 통해 컨테이너를 멈추자.
docker rm <container id>
docker rm 명령어를 통해 컨테이너를 제거한다.
파트 4. 애플리케이션 공유하기
이제 이미지를 만들었으니 공유할 수 있다. 도커 이미지를 공유하기 위해선, Docker registry 를 사용해야 한다.
- 나 이거 안함ㅋㅋ
파트 5. DB 유지
지금 To do list 는, 컨테이너를 실행 시킬 때마다 비어있다. 이유가 무엇일 까?
- 컨테이너의 파일 시스템
컨테이너가 실행될 때, 컨테이너는 자체 파일시스템을 위해 다양한 계층을 이미지로부터 사용한다.
각 컨테이너는 또한 파일들을 생성/수정/삭제 하기 위한 각자의 “scratch space” 란 걸 가지고 있는데, 한 컨테이너 에서 어떠한 변화는 다른 컨테이너에서 보이지 않는다. (그들이 같은 이미지를 사용하고 있더라도)
이번 실습에서는 두개의 컨테이너를 시작할 것이고, 각자 파일을 생성 할 것이다.
한 컨테이너 에서 만들어진 파일이 다른 컨테이너에서 이용 못하는걸 확인하자.
docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
다음 명령으로 우분투 컨테이너를 시작해보자. 이 뒤의 명령은, 1 ~ 10000 사이의 랜덤 숫자를 생성해 /data.txt 에 저장할 것이다.
이제, 우리는 컨테이너에 실제로 data.txt 가 존재하는 지 확인 해 볼 것이다. 이를 cli 상에서 확인해 보는 방법은 다음과 같다.
docker exec <container-id> cat /data.txt
그럼, 랜덤값이 출력 되는 것을 볼 수 있을 것이다.
그럼, 새로운 우분투 컨테이너를 하나 새로 실행시켜서, 그 컨테이너에 data.txt 가 있는지 확인해 보면 되겠다.
그 전에 지금 내가 data.txt 가 있음을 확인한 컨테이너에서 ls 를 해서 파일들이 뭐가 있는지 보자.
docker exec <container-id> cat /data.txt
data.txt 가 있는걸 볼 수 있당
그럼, 새로 만든 우분투 컨테이너에선 어떨까?
docker run -it ubuntu ls /
새로 우분투 이미지를 실행 해 컨테이너 하나 만들고 ls 로 루트 디렉토리의 파일, 디렉토리 확인하기
- i 플래그 - 표준 입력 (stdin)을 활성화하여 사용자와 상호 작용하도록 합니다. 즉, 컨테이너의 터미널에 입력을 보낼 수 있게 합니다. 이 플래그를 사용하지 않으면 컨테이너가 백그라운드에서 실행되며 사용자와 상호 작용할 수 없게 됩니다.
- t 플래그 - 이 플래그는 유사 터미널 (pseudo-TTY)을 활성화합니다. 이것은 터미널 환경과 유사한 환경을 제공하여 컨테이너 내에서 쉘 또는 명령어를 실행할 수 있도록 합니다.
-t
를 사용하지 않으면 터미널이 적절하게 설정되지 않아서 쉘이 제대로 동작하지 않을 수 있습니다.
컨테이너 볼륨
이전 실습에서, 컨테이ㅓ가 시작할 때마다 이미지 정의 대로 시작되는 것을 봤다. 콘테이너는 파일들을 생성, 수정, 삭제 할 수 있지만, 그러한 수정 사항들은 컨테이너를 끌 때마다 다 날아간다. 볼륨 을 이용해, 우린 바꿀 수 있다.
볼륨은 컨테이너의 특정 파일 시스템 경로를 호스트 시스템에 다시 연결하는 기능을 제공한다.
우리가 컨테이너에서 어느 디렉토리를 마운트 하면, 그 디렉토리에서의 변화가 호스트 머신에서도 보인다는 뜻이다.
현재, todo app 은 데이터를 모두 SQLite database 에 저장하고 있다. 이는 /etc/todos/todo.db 이 경로에 있다.
우리가 만약 저 경로의 파일을 호스트에서 유지하고, 다음 컨테이너에서 이용가능하도록 한다면, 마지막으로 이용된 컨테이너 이후 내용을 유지할 수 있을 것이다. 이를 어떻게 구현하는지 보자. → Volume mount 를 이용할 것이다.
볼륨을 만들고 컨테이너 시작하기
우선 volume 을 만드는 방법은 다음과 같다.
docker volume create todo-db
기존에 돌아가고 있던 컨테이너를 삭제하고, 다시 이미지를 빌드해 줄건데 그 때 이 명령어로 하자.
docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
- mount 옵션으로 볼륨 마운트를 명시해 주었다.
- /etc/todos 라는 곳으로 마운트 하고, 이는 컨테이너 내부에 있다.
그럼 어디에 Docker 가 데이터를 저장하는 거야? (볼륨을 썼을 때)
docker volume inspect todo-db
[
{
"CreatedAt": "2019-09-26T02:18:36Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
"Name": "todo-db",
"Options": {},
"Scope": "local"
}
]
해당 명령어 와 그 결과 이다.
mountpoint 가 실제 디스크에 저장되는 경로이다!
→ docker desktop 앱으로 하니까 저 경로가 안떠서, 가상 머신 위에 직접 설치한 도커로 해보니까
가상 머신의 로컬에 실제로 저장되고 있음을 확인했다.!!!
파트 6. 바인드 마운트
볼륨마운트의 경우
애플리케이션 데이터를 영구적으로 보관할 때 효율적이다.
bind mount 의 경우
호스트 시스템의 어느 곳에나 저장할 수 있고, non-Docker 프로세스나 도커 컨테이너 내에서 언제든지 수정이 가능하다. 또한, 바인드 마운트를 사용하면 호스트 시스템의 파일 또는 디렉터리가 컨테이너에 마운트되며, 파일 또는 디렉토리는 호스트의 전체 경로로 지정된다.
Named volumes | Bind mounts | |
---|---|---|
Host location | Docker chooses | You decide |
Mount example (using --mount) | type=volume,src=my-volume,target=/usr/local/data | type=bind,src=/path/to/data,target=/usr/local/data |
Populates new volume with container contents | Yes | No |
Supports Volume Drivers | Yes | No |
실습
docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash
- mount 옵션은 도커에게 마운트 를 생성하라는 명령이고, 타입은 bind 이다.
- src 의 경우, 호스트의 디렉토리 주소이다 (컨테이너에 저장하고 싶은)
- target 은 ㅋ
마지막에 bash 를 입력했기에 bash 로 컨테이너 내부에서 작업할 수 있다.
ls 로 보면, src 란 폴더가 있고, 그 안에는 익숙한
getting-started-app 폴더의 내용이 보인다.
이는, 우리가 위 명령에서 src 를 pwd, 즉 현재 작업중이던 폴더 (g-s-app) 으로 했고, target 를 /src 로 했기 때문이다.
이제 컨테이너 안에서 파일 하나를 생성해보자.
touch myfile.txt (컨테이너 안에서)
이제 호스트의 getting-started-app 폴더에 컨테이너에서 만든 녀석이 있는지 보자.
→ 있다..!!
그럼 host 에서 myfile.txt 를 지워보자.
rm myfile.txt
컨테이너에서 다시 확인해보면, myfile 이 지워진것을 볼 수 있다
호스트와 컨테이너 간에 파일이 공유되는 방식과 변경 사항이 양측에 즉시 반영되는 방식을 보여주었습니다.
바인드 마운트를 사용하는 것은 로컬 개발 설정에서 일반적입니다. 장점은 개발 시스템에 모든 빌드 도구와 환경을 설치할 필요가 없다는 것입니다. 단일 docker 실행 명령으로 Docker는 종속성과 도구를 가져옵니다.
- 실습
소스코드를 컨테이너에 마운트 하고,
모든 dependency 를 설치하고
nodemon
파일 시스템 변경 사항 감시 시작
다음 명령어를 실행한다.
docker run -dp 127.0.0.1:3000:3000 \
-w /app --mount type=bind,src="$(pwd)",target=/app \
node:18-alpine \
sh -c "yarn install && yarn run dev"
dp 127.0.0.1:3000:3000
- 기존과 동일합니다.
- 분리(백그라운드) 모드에서 실행하고 포트 매핑 생성
w /app
- "작업 디렉토리" 또는 명령이 실행될 현재 디렉토리를 설정합니다.
-mount type=bind,src="$(pwd)",target=/app/app
- 호스트의 현재 디렉토리를
- 컨테이너의 디렉토리 에 바인드 마운트
node:18-alpine
- 사용할 이미지.
- 이것은 Dockerfile에 있는 앱의 기본 이미지입니다.
- •
sh -c "yarn install && yarn run dev"
- the command. You’re starting a shell usingsh
(alpine doesn’t havebash
) and runningyarn install
to install packages and then runningyarn run dev
to start the development server. If you look in thepackage.json
, you’ll see that thedev
script startsnodemon
.
해당 명령어로 컨테이너를 키면, 호스트에서 소스코드를 바꾸면 바로바로 컨테이너에 마운트된 것도 같이 바뀐다.
즉, 개발할 때 우린 호스트에서 코드만 수정을 하면, 모든 의존성이 설치된 컨테이너에서 바로 확인할 수 있기 때문에 효율적이당
Each time you make a change and save a file, the nodemon
process restarts the app inside the container automatically. When you’re done, stop the container and build your new image using:
우리가 파일을 수정하고 저장할 때마다, 노드몬 프로세스가 컨테이너 안에서 앱을 자동으로 재실행한다.
확인 해보기 위해 컨테이너 내부의 로그를 찍어볼 수 있다.
docker logs -f d9846a744495
Listening on port 3000
이후로 내가 호스트의 소스코드를 조금 수정했는데,
수정하고 저장하자 노드몬이 저절로 다시 시작해 주는걸 알 수 있었다.
파트 7. 멀티 컨테이너 앱
지금까지 한 개의 컨테이너를 이용해 왔다면, 이젠 MySQL 을 추가해볼 것이다.
일반적으로, 하나의 컨테이너는 한 가지 작업을 하기 때문에 따로 따로 컨테이너를 만드는게 좋다.
컨테이너를 따로 작동시켜야 하는 이유.
- 데이터베이스와 다르게 API 및 프런트 엔드를 확장해야 할 가능성이 높습니다.
- 별도의 컨테이너를 사용하면 격리된 상태에서 버전을 관리하고 업데이트할 수 있습니다.
- 로컬에서 데이터베이스용 컨테이너를 사용할 수 있지만 프로덕션에서는 데이터베이스용 관리 서비스를 사용할 수 있습니다. 그러면 앱과 함께 데이터베이스 엔진을 제공하고 싶지 않을 것입니다.
- 여러 프로세스를 실행하려면 프로세스 관리자가 필요하므로(컨테이너는 하나의 프로세스만 시작함) 컨테이너 시작/종료에 복잡성이 추가됩니다.
컨테이너들은 기본적으로 isolation 하게 동작하고, 다른 컨테이너나 프로세스들에 대해 알지 못한다. 그렇다면 어떻게 컨테이너 끼리 서로 통신할 수 있게 할까? → 정답은 네트워킹 이다.
만약 우리가 두개의 컨테이너를 하나의 네트워크에 두면, 그들은 서로 대화할 수 있게 된다.
우리는 네트워크를 만든 후, MySQL 컨테이너에 붙일것(?)이다. (네트워크 부터 만들고 거기에 mysql 넣겠단 뜻인거 같다.)
docker network create todo-app
네트워크를 만들자.
docker run -d \
--network todo-app --network-alias mysql \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:8.0
mySQL 컨테이너를 시작하고, 네트워크에 연결해보자. 몇개의 환경 변수 설정이 있는데, 이는 mySQL 환경에서 필요하다.
--network-alias
플래그는 이후 섹션에서 다룬다고 한다!
또 run 구문을 보면, -v 플래그로 볼륨을 설정해준 것을 볼 수 있다.
-v todo-mysql-data:/var/lib/mysql \
이전에는, 우리가 docker volume create 로 볼륨을 만들어 주었었는데, 이번에는 그러지 않았다. 그런데도 todo-mysql-data 란 이름의 볼륨이 알아서 실행되는걸 알 수 있는데, 이는 도커가 알아서 인식하고 해당 이름을 가진 볼륨을 자동으로 만들어 주기 때문이다!
이제 데이터베이스에 연결해보자.
docker exec -it <mysql-container-id> mysql -u root -p
명령어 정리 한번 더..
docker exec
: Docker 컨테이너 내부에서 특정 명령어를 실행하는데 사용하는 명령어입니다.it
: 이 플래그는 상호 작용적인(pseudo-TTY와 stdin을 연결) 모드로 실행하겠다는 의미입니다. 즉, 터미널과 상호작용할 수 있도록 합니다.mysql
: 실행하려는 명령어 또는 프로세스의 이름입니다. 이 경우 MySQL 서버에 접속하려고 합니다.u root
: MySQL 서버에 root 사용자로 로그인하겠다는 의미입니다. 여기서root
는 MySQL의 최상위 관리자 계정입니다.p
: 이 플래그는 MySQL 서버에 접속할 때 비밀번호를 입력하겠다는 의미입니다.p
뒤에 바로 비밀번호를 입력하지 않고 실행하면, 컨테이너에서 비밀번호를 입력하는 프롬프트가 나타납니다.
비밀번호를 치라고 나오는데, 비밀번호는 secret 이다. (위에 run 할 때 MYSQL 의 환경변수를 설정해 주었는데, 이 때 root_password 를 secret 이라고 했다.)
그럼
mysql> 로 입력부가 바뀐다 → mysql 쉘로 들어온거다. 즉, mysql 컨테이너 안으로 들어온 것
mysql> SHOW DATABASES;
입력하면, 데이터베이스가 어떻게 생겼는지 알 수 있다.
우린 이제 mysql 을 컨테이너로 올리고 실행시켰다. 이제 얘를 어떻게 사용할 까? → 컨테이너 네트워킹…
우선 nicolaka/netshoot 컨테이너를 이용할 것인데, 이는 컨테이너 네트워킹에 유용한 툴들이 있다고 한다.
docker run -it --network todo-app nicolaka/netshoot
이 컨테이너 내부에서
dig mysql
다음 명령어를 실행하자. dig 는 유요한 DNS 툴로, mysql 이란 이름을 가진 호스트의 ip 주소를 찾을 수 있다.
; <<>> DiG 9.18.13 <<>> mysql
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20782
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;mysql. IN A
;; ANSWER SECTION:
mysql. 600 IN A 172.18.0.2
;; Query time: 1 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Sat Aug 05 06:39:04 UTC 2023
;; MSG SIZE rcvd: 44
ANSWER SECTION 에서 나오는 172.18.0.2 를 확인할 수 있다.
mysql 이 일반적으로 유효한 호스트 이름은 아니지만, 도커는 해당 네트워크 alias 가 있는 컨테이너의 ip 주소로 확인이 가능하다.
이는 이전에 네트워크를 만들때
--network todo-app --network-alias mysql \
이렇게 alias 를 만들어 주었기 때문이다.
지금보니 해당 플래그가 네트워크가 todo-app 네트워크에 속할거고, 그 네트워크에서 이 컨테이너의 별칭(?) 을 mysql 로 하겠다는 뜻인거 같다.
이제 MySQL 을 활용하여 우리 앱을 돌려보자.
docker run -dp 127.0.0.1:3000:3000 \
-w /app -v "$(pwd):/app" \
--network todo-app \
-e MYSQL_HOST=mysql \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:18-alpine \
sh -c "yarn install && yarn run dev"
이거 헷갈려서 정리해보자.
우선 mysql 컨테이너를 아까 todo-app 이라는 네트워크에 속하게 해서 실행했다.
중간에 뭐하는 건진 잘 모르지만 nicolaka/netshoot 라는 컨테이너도 하나 올렸고,
이제 기존에 우리가 쓰돈 todo-app 컨테이너를 올리는데, MYSQL 관련 환경변수들을 설정해 주었고, 네트워크도 todo-app 에 묶이도록 해서 올렸다.
MYSQL_HOST
- 실행 중인 MySQL 서버의 호스트 이름MYSQL_USER
- 연결에 사용할 사용자 이름MYSQL_PASSWORD
- 연결에 사용할 암호MYSQL_DB
- 일단 연결되면 사용할 데이터베이스
이렇게 mySQL 컨테이너를 지금 실행하는 todo 컨테이너와 네트워크로 연결해 놓은 거고,
우리가 뭘 저장하면 자동으로 mySQL에 업로드 되는 건.. 어떻게 되는건지 잘 모르겠다.
파트 8. Docker Compose 사용
Docker compose 는 다중 컨테이너 애플리케이션을 정의하고 공유하는데 도움이 되도록 개발된 도구이다.
Compose 를 이용하면, YAML 파일을 생성해 서비스를 정의하고 단일 명령으로 관리할 수 있다.
가장 큰 장점은, 애플리케이션 스택을 파일로 정의하고, 프로젝트 저장소의 루트에 보관하고, 다른 사람이 프로젝트에 쉽게 기여할 수 있도록 한다는 것.
설치부터.. → Docker Desktop 을 설치했다면 이미 컴포즈는 깔려있다.
docker compose version
으로 버전 정보 확인하자.
우선, getting-started-app 의 루트폴더에
docker-compose.yml
파일을 만들자.
이제, yml 파일안에 우리가 애플리케이션에서 실행하고자 하는 서비스들을 적으면서 시작해보도록 한다?!
우리가 이전에 docker run 명령어로 컨테이너를 실행하였다.
docker run -dp 127.0.0.1:3000:3000 \
-w /app -v "$(pwd):/app" \
--network todo-app \
-e MYSQL_HOST=mysql \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:18-alpine \
sh -c "yarn install && yarn run dev"
우선,
서비스 엔트리와 이미지를 정의하자.
services:
app:
image: node:18-alpine
일반적으로, command 를 image 정의 부근에 적는데, 굳이 그럴필요는 없다.
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
서비스에 포트 도 같이 정의해 적어주자.
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 127.0.0.1:3000:3000
작업 디렉토리와 볼륨 매핑을 진행해 주자. 이는
working_dir, volumes 를 정의 하고 적어주면 되겠다.
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 127.0.0.1:3000:3000
working_dir: /app
volumes:
- ./:/app
마지막으로 환경변수를 environment 키에 적어주면서 마무리 한다.
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 127.0.0.1:3000:3000
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: secret
MYSQL_DB: todos
이제는, MySQL 서비스도 정의해보자.
우리가 MySQL 컨테이너를 킬 때 사용한 커맨드는 다음과 같다.
docker run -d \
--network todo-app --network-alias mysql \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:8.0
이 커멘드도 아까 만든 docker-compose 아래에 적어서 같이 굴러가도록 해보자.
이 때 볼륨 매핑 해주었던것도 함께 쓰자.
우리가 docker run 으로 MySQL 컨테이너를 킬 때, -v todo-mysql-data 가, 볼륨이 존재하지 않았는데 자동으로 만들어졌던걸 기억할 것이다. 하지만 도커 컴포즈를 이용하여 작동시킬 땐 자동으로 만들어 지지 않기 때문에, 우리는 volumes: 란 키에 따로 기입을 해주어야 한다.
이 volumes : 는 services: 와 같은 선상에 위치하도록 하자
최종 결과
services:
app:
image: node:18-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 127.0.0.1:3000:3000
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: secret
MYSQL_DB: todos
mysql:
image: mysql:8.0
volumes:
- todo-mysql-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: todos
volumes:
todo-mysql-data:
이제 도커 컴포즈 파일 작성을 완료했으니 한번 실행해보자!
docker compose up -d
docker compose up 이란 커맨드를 이용해 애플리케이션을 실행한다. -d 플래그는 모든걸 백그라운드에서 실행되도록 한다고 한다.
docker compose logs -f
이걸로 로그를 확인해 볼 수 있다.
이제 끄고 싶을 땐,
docker compose down
으로 끌 수 있다.
파트 9. 이미지 building 구축
무엇이 이미지를 만드는지 확인 할 수 있다.
docker image history
커맨드를 이용하면, 우린 이미지 내 각 layer 를 만들 때마다 무슨 커맨드가 사용되었는지 볼 수 있다.
docker image history getting-started
IMAGE CREATED CREATED BY SIZE COMMENT
4b8c44da009b 20 hours ago EXPOSE map[3000/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 20 hours ago CMD ["node" "src/index.js"] 0B buildkit.dockerfile.v0
<missing> 20 hours ago RUN /bin/sh -c yarn install --production # b… 83.1MB buildkit.dockerfile.v0
<missing> 20 hours ago COPY . . # buildkit 59MB buildkit.dockerfile.v0
<missing> 25 hours ago WORKDIR /app 0B buildkit.dockerfile.v0
<missing> 2 weeks ago /bin/sh -c #(nop) CMD ["node"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) COPY file:4d192565a7220e13… 388B
<missing> 2 weeks ago /bin/sh -c apk add --no-cache --virtual .bui… 7.77MB
<missing> 2 weeks ago /bin/sh -c #(nop) ENV YARN_VERSION=1.22.19 0B
<missing> 2 weeks ago /bin/sh -c addgroup -g 1000 node && addu… 159MB
<missing> 2 weeks ago /bin/sh -c #(nop) ENV NODE_VERSION=18.17.0 0B
<missing> 7 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 7 weeks ago /bin/sh -c #(nop) ADD file:289c2fac17119508c… 7.66MB
이렇게 결과를 볼 수 있다. 각 줄은 이미지의 계층을 나타낸다. 제일 밑이 가장 기본이 되는 base.
지금 보면 truncated 된 내용들이 있으니, 다음 커맨드를 사용하면 다 확인 할 수가 있다.
docker image history --no-trunc getting-started
layer 를 활용해, 컨테이너 이미지의 빌드 시간을 줄이는 방법이 있다.
'서울42' 카테고리의 다른 글
Born2Be 개념 공부 와 VM 설치하기 (0) | 2022.08.21 |
---|---|
Makefile 만들기! (0) | 2022.08.21 |
libft.h 정리 (0) | 2022.08.21 |