github 레포지토리에 자신이 만든 도커파일 스크립트를 올리면, 도커허브에 연동하여 자동으로 이미지를 빌드하고, 결과를 도커허브에 저장할 수 있다. github 뿐만 아니라 Bitbucket도 가능한데, 여기서는 다루지 않는다. 먼저, 아래 두 사이트의 계정을 생성한다.

 

https://hub.docker.com/

 

Docker Hub

Docker Certified:Trusted & Supported Products Certified Containers provide ISV apps available as containers. Certified Plugins for networking and volumes in containers. Certified Infrastructure delivers an optimized and validated Docker platform for enterp

hub.docker.com

접속 후 계정을 만든다음 로그인한다.

https://github.com/

 

Build software better, together

GitHub is where people build software. More than 50 million people use GitHub to discover, fork, and contribute to over 100 million projects.

github.com

도커허브에 로그인한다.

오른쪽 상단에 지문을 클릭한 후 Account Settings에 들어간다.

왼쪽 메뉴에 Linked Accounts를 선택하면 아래와 같은 화면이 나온다.

GitHub 부분에서 Connect를 선택한다.

 

아래와 같이 GitHub 로그인 페이지가 나온다. 로그인한다.

 

로그인이 성공적으로 완료되면, 아래와 같이 연결된 마크로 변경된다.

 

 

2. Github에 도커파일과 데이터 업로드하기

github에 로그인한다. 로그인 후 맨 오른쪽 아이콘을 선택하고 Your repositories를 선택한다.

오른쪽 녹색 New 버튼을 선택한다.

Repository 이름을 입력하고, 그 외 부분은 아래와 동일하게 한 후, Create repository 선택

다음과 같은 화면이 나온다. 중간에 보면 creating a new file or uploaading an existing file 을 선택할 수 있다.

uploading an existing file 선택하면 아래와 같은 화면이 나온다. 2개의 파일(Dockerfile, website.tar)을 업로드한다. 

이건 예시로 올리는 것이며, 자유롭게 단순한 Dockerfile을 만들어도 된다. 잘 작동만 하면 되니까.

업로드가 완료되면, 다음과 같이 결과가 나온다. 

 

 

3. 도커허브에서 연동된 github의 dockerfile을 바탕으로 이미지 생성하기

 

다시 도커허브로 돌아온다. Create Repository를 선택한다.

아래와 같이 Repository 생성 화면이 나온다. 이름은 자유롭게 하고, 그 외에는 건드릴 필요 없다.

- name : 이미지 이름이 되는 리포지토리명은 3~30문자까지의 영어소문자,숫자,언더바,하이픈(-), 점(.)을 쓸 수 있다.

- description : 이미지에 대한 간단한 설명을 기술한다.

- visibility : 퍼블릭으로 하면 누구나 접근 가능. 프라이빗으로 하면 한정된 사람만 접근 가능.

- build setting : 이미지 태그, github에서 가져올 소스(브랜치 정보 등)을 설정한다.

아래 Build Settings에서, Connected 되어 있는 github를 누르면, 자신의 아이디와, 레포지토리를 선택할 수 있다. 위에서 생성한 docker_example 레포지토리를 선택하자.

아래 BUILD RULES 에서 + 를 선택하면, 아래와 같이 내용이 나온다. 특별히 변경할 것 없이 그대로 진행하면 된다.

다 되면, Create & Build를 선택한다. 아래와 같은 화면이 나오며, 오른쪽 중간에 주황색으로 Build in 부분을 선택한다.

 

현재 빌드 진행상황을 실시간으로 확인할 수 있다. 처음에는 아래와 같이 PENDING이 나오며, 완료될때까지 기다리면 된다. 조금 시간이 걸릴 수 있다.

다 되면 아래와 같이 SUCCESS 결과가 나오며, 진행 로그도 확인할 수 있다.

 

이제 다시 내 도커허브의 레포지토리에 가면, 만든 이미지가 생성된 것을 확인할 수 있다. 해당 이미지를 push로 다운받을 수 있게 아래 명령어도 친절하게 적혀있다.

마지막으로, 내 도커 서버에서 해당 이미지를 다운받아 실행까지 해보자.

결과

문제없이 잘 완료되었다.

 

* Private Redistry란?

Private Rgeistry는 public한 docker hub와는 다르게, 개인적으로만 사용하도록 개인 서버에 구축하는 도커 이미지 저장소이다. 회사 사내에서 사용하거나 개인적으로 만든 이미지를 올리고 관리하기 위해서 사용된다.

 

Private registry를 구축하기 위해서는 도커에서 제공하는 "Registry" 라는 이미지를 사용할 수 있다. 도커에서 이 이미지를 실행하면 이 컨테이너에 자신의 이미지를 업로드할 수 있는 것이다. 이 Registry도 도커 컨테이너이므로, 여기 올리는 이미지들을 계속 유지하려면 볼륨을 다른 외부 스토리지 등 안정성있는 스토리지로 사용해야 한다.

 

Registry 이미지는 0점대 버전과 2점대 버전 2가지 계열이 있다. 0점대 버전은 파이썬, 2점대 버전은 go로 구축되어 있다. 이 둘은 호환성이 없으므로 주의해야 한다. 일반적으로 버전2를 많이 사용한다. 또한 registry는 5000번 포트를 사용한다.

 

 

* Private Registry 구축

컨테이너 시작

docker container run -d --restart=always -p 5000:5000  -v /registry:/var/lib/registry/docker/registry/v2 --name registry registry:latest

 다 완료한 후, 포트가 열렸는지 확인.

netstat -nlp | grep 5000

레지스트리에 있는 이미지 확인 (현재는 아무것도 없음)

curl -X GET http://localhost:5000/v2/_catalog

 

# 참고 : push / pull 할 때 "http: server gave HTTP response to HTTPS client" 비슷한 메시지가 발생하는 경우

사설 레지스트리를 구축한 자체 도커 서버에서 pull/push를 하는 경우는 문제가 없다. 하지만 외부에서 접속하는 경우, 도커 레지스트리에는 로그인 기능이 없다. 따라서 nginx의 기본 인증 기능을 사용한다. http 프로토콜은 인증을 지원하지 않으므로 반드시 https 프로토콜을 사용한다. 사설 레지스트리를 사용하는 다른 도커 엔진은 https 프로토콜을 사용해야 한다.

 

그러나 https 프로토콜을 쓰려면 복잡한 인증서 작업을 수행해야 한다. 사설 레지스트리에 접속할 다른 도커 엔진에 인증서 작업을 하지 않으려면, 도커 데몬에 insecure-registries 옵션을 설정하여 인증되지 않은 Registry를 사용할 수 있도록 해야 한다. /etc/docker/daemon.json 파일을 생성한 후, 아래와 같이 입력한다. (docker swarm을 사용한다면, 모든 노드에 아래와 같이 수정한다)

{
    "insecure-registries" : [ "도커서버ip:5000" ]
}

위 작업 후, docker 데몬을 재시작한다.

systemctl restart docker

docker info로 아래 내용 추가되었는지 확인

 

* 도커 이미지 업로드/다운로드하기 (도커허브, Private Registry)

 

1. 도커파일로 빌드하거나, 현재 보유한 이미지를 지정

아래와 같이 여러가지 이미지가 있을 것이다. 업로드할 이미지를 선택한다. 도커파일로 빌드할 때는 이름을 2번에 나오는 형식대로 지정한다.

 

2. 이미지에 태그 설정

이미지를 업로드하려면, 이미지에 Private Registry의 주소 또는 도커허브의 계정명을 명시해주어야 한다. 이러한 이름은 형식이 있다. 도커허브에 올리는 경우 계정명을 사용하고 (내 계정에 올리니깐) Private Registry에 올리는경우 해당 서버의 ip주소:포트번호 형식을 사용한다. 도커허브의 오피셜 이미지 (centos, nginx, mariadb 등등)는 이름 형식이 단순히 이미지명으로 되어있다. 아래 정리를 참고하자.

 

일반 이미지 형식 : 계정명(주소명:포트번호)/이미지명:태그 
오피셜 이미지 형식 : 이미지명:태그

 

따라서 아래 명령어로 이미지의 이름을 변경한다.

docker image tag 대상이미지명 새로운이미지정보

예시1 - 도커허브에 업로드용 : docker image tag sample utyk/sample:latest

예시2 - Private Registry에 업로드용 : docker image tag sample 192.168.1.61:5000/sample:latest

 

3. 업로드

 

* 도커허브에 업로드 : docker image push utyk/sample:latest  

도커허브에 업로드하려면, 해당 도커 엔진 서버에 utyk 계정으로 로그인이 되어있어야 한다. 로그인 관련해서는 아래 포스팅을 참고한다.

2020/03/31 - [Docker Basic] - [Docker Basic] 04. 도커 레지스트리와 도커허브

2020/04/09 - [Docker Basic] - [Docker Basic] 06. Docker 기본 명령어 - 시스템 명령

 

* Private Registry에 업로드 : docker image push 192.168.1.61:5000/sample:latest

 

4. 업로드 된 이미지 조회하기

 

* 도커허브

도커허브 사이트에서 내 계정으로 로그인하면, 아래와 같이 이미지가 올라간 것을 확인할 수 있다.

* Private Registry

Registry 컨테이너를 운영하고 있는 도커 엔진 서버에서, 다음 명령으로 이미지를 확인할 수 있다. 

curl -X GET http://도커서버IP:5000/v2/_catalog

 

5. Private 레지스트리에서 이미지를 다운로드하기

업로드 했던 이미지는 삭제해버리고, 다시 Private Registry에서 해당 이미지를 받아보자.

docker image pull 계정명(주소명:포트번호)/이미지명:태그
docker image pull 192.168.1.61:5000/sample:latest

 

* Registry 이미지 GUI tool "hyper"

Private Registry의 단점 중 하나는 어떤 이미지가 올라가 있는지 확인하기가 번거롭다는 것이다. 이를 위해 누군가가 WEB GUI로 Private Registry를 볼 수 있는 이미지를 만들었다. 아래 링크를 참고한다. 

https://hub.docker.com/r/hyper/docker-registry-web/

 

Docker Hub

 

hub.docker.com

설치하는 방법은 해당 링크에서 확인할 수 있으며, 가장 간단한 방법으로 한번 구축해본다. 또한 해당 링크 문서에서 작동하지 않는 버전이나 유의점이 있으므로 잘 참고해야 한다.

 

실행하기 

docker run -it -p 8080:8080 --name Registry_web --link Registry컨테이너이름 -e REGISTRY_URL=http://도커서버IP:5000/v2 -e REGISTRY_NAME=도커서버IP:5000 hyper/docker-registry-web

--link 에는 이미 구축해놓은 registry 컨테이너의 이름을 명시하며,  REGISTRY_NAME 부분은 WEB GUI에 나오는 이름이므로 자유롭게 쓰면 된다. 가능하면 해당 도커서버의 주소면 좀 더 보기 쉬우므로 추천된다.

 

위 명령을 실행하면, 아래와 같이 쭉 로그가 나오며, server startup 이라고 나온다.

해당 주소로 접속해보면, 아래와 같이 나오는 것을 확인할 수 있다.

이미지를 하나 올려보자.

 

다음과 같이 나오는 것을 확인할 수 있다.

# 참고

selinux가 설정되어 있는 경우, web registry 화면에서 에러가 날 수 있다. selinux를 허용하는 설정을 적용하거나, selinux를 해제해야 한다.

 

docker-compose.yml 파일을 생성했다면, 해당 파일을 컨트롤하는 docker compose 명령어들이 있다. 이러한 명령어들은 컨테이너 시작, 구성확인, 삭제 등 여러가지 명령을 수행한다. docker compose 명령어의 리스트는 다음과 같다.

명령

설명

up

컨테이너 생성/시작

ps

컨테이너 목록 표시

logs

컨테이너 로그 출력

run

컨테이너 실행

start

컨테이너 시작

stop

컨테이너 정지

restart

컨테이너 재시작

pause

컨테이너 일시정지

unpause

컨테이너 재개

port

공개 포트 번호 표시

config

구성 확인

kill

실행중인 컨테이너 강제 정지

rm

컨테이너 삭제

down

리소스 삭제

또한 이 명령 다음 특정 컨테이너명을 지정하면, 해당 컨테이너만 조작할수도 있다. 예를들어, webserver와 db 컨테이너 두개를 올린 docker-compose.yml을 실행한 상태에서, docker-compose stop webserver 를 입력하면, webserver만 중지된다.

 

추가로, 대부분의 docker compose 명령은 명령을 실행하는 현재 디렉토리 안에 docker-compose.yml 파일이 있어야 한다. 즉 해당파일 없는 경로에서 실행하면 작동하지 않으며, 따로 -f 옵션을 주어야 한다.

 

 

 

docker-compose --version

 

설명

docker-compse의 버전을 확인한다. 아무데서나 해당 명령을 사용할 수 있다.

 

예시

 

 

docker-compose up

 

설명

작성한 docker-compose.yml을 바탕으로 여러개의 컨테이너를 생성하여 시작한다. 기본적으로 지원하는 이름은 docker-compose.yml, yaml 2가지이다다른 이름으로 하는 경우, -f 옵션을 써야 한다. docker-compose up 실행하면 백그라운드로 실행되지 않아 프롬프트가 떨어지지 않고 계속 로깅된다. -d 옵션으로 백그라운드에서 실행할 수 있다.

 

구문은 다음과 같다.

docker-compose up [옵션] [서비스명 .] 

 

옵션

-f

정해진 docker-compose.yml 이름 외에 다른 이름을쓰는 경우.

-d

백그라운드에서 실행

--no-deps

링크 서비스를 시작하지 않음.

--build

이미지를 빌드한다. 컨테이너 시작 시 dockerfile을 빌드할때 사용.

--no-build

이미지를 빌드하지 않는다.

-t, --timeout

컨테이너의 타임아웃을 초로 지정 (디폴트 10초)

--scale SERVICE=서비스수

컨테이너를 생성할 개수를 지정.

 

예시

docker compose up

docker compose -f ./sample/docker-compose.yml up

docker compose up --scale server_a=10 --scale server_b=20

(docker-compose.yml에 server_a, server_b 2가지 컨테이너가 있고, server_a는 10개, server_b는 20개 시작)

 

아래 예시는 이런식으로 작동한다는 것만 이해하면 되겠다. (위 예시와 관련없음)

[root@docker-basic compose-test]# docker-compose up
Creating network "compose-test_default" with the default driver
Pulling mongodb (mongo:4)...
4: Pulling from library/mongo
d7c3167c320d: Pull complete
131f805ec7fd: Pull complete
322ed380e680: Pull complete
6ac240b13098: Pull complete
c5704a01b59f: Pull complete
8e396e2a1e68: Pull complete
914fffcb1cc5: Pull complete
692bd0d35810: Pull complete
122d231765e4: Pull complete
9c9c497305ce: Pull complete
77e3ca7a65fc: Pull complete
f70e0d9672b4: Pull complete
dcc04aade5db: Pull complete
Digest: sha256:29805e9a6fc2a233b9b633c7b0f382a43df3f119522b82366a72abecb302a793
Status: Downloaded newer image for mongo:4
Pulling backend (dbgurum/doc-kor:devbackend_v1)...
devbackend_v1: Pulling from dbgurum/doc-kor
e79bb959ec00: Pull complete
d4b7902036fe: Pull complete
1b2a72d4e030: Pull complete
d54db43011fd: Pull complete
69d473365bb3: Pull complete
6e2490ee2dc8: Pull complete
24bcfd3d8296: Pull complete
4c485b32137c: Pull complete
412552bf9213: Pull complete
ccc531a563c9: Pull complete
d80cdc55821c: Pull complete
28bb33283f12: Pull complete
Digest: sha256:3c276f5805414759745bdd45256eb33804d4bd4aa02007c38d50c40ab8949938
Status: Downloaded newer image for dbgurum/doc-kor:devbackend_v1
Pulling frontend (dbgurum/doc-kor:devfrontend_v1)...
devfrontend_v1: Pulling from dbgurum/doc-kor
e79bb959ec00: Already exists
d4b7902036fe: Already exists
1b2a72d4e030: Already exists
d54db43011fd: Already exists
69d473365bb3: Already exists
6e2490ee2dc8: Already exists
24bcfd3d8296: Already exists
4c485b32137c: Already exists
44059a7f00c1: Pull complete
cd480d0dfe0e: Pull complete
082172b2c563: Pull complete
4a9927cdb901: Pull complete
8c5d9b22addb: Pull complete
Digest: sha256:0ffcaea4346ab991488fd159c5428dea495674f31b9fca7d07328f7d3511581d
Status: Downloaded newer image for dbgurum/doc-kor:devfrontend_v1
Creating compose-test_backend_1  ... done
Creating compose-test_frontend_1 ... done
Creating compose-test_mongodb_1  ... done
Attaching to compose-test_frontend_1, compose-test_backend_1, compose-test_mongodb_1
frontend_1  | Debugger listening on ws://127.0.0.1:9229/ae1582ff-808e-4df1-9e8e-c19d0c450e9e
frontend_1  | For help see https://nodejs.org/en/docs/inspector
backend_1   | Debugger listening on ws://127.0.0.1:9229/1a24cd35-6829-4330-88df-9db147fdfb9c
backend_1   | For help see https://nodejs.org/en/docs/inspector
frontend_1  | App listening on port 8888
frontend_1  | Press Ctrl+C to quit.
mongodb_1   | 2020-06-12T13:54:34.758+0000 I  CONTROL  [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 spe                                                               cify --sslDisabledProtocols 'none'
mongodb_1   | 2020-06-12T13:54:34.760+0000 W  ASIO     [main] No TransportLayer configured during NetworkInterface startup
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db                                                                64-bit host=7f0732f4f380
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] db version v4.2.8
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] git version: 43d25964249164d76d5e04dd6cf38f6111e21f                                                               5f
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.1.1  11 Sep 2018
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] allocator: tcmalloc
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] modules: none
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] build environment:
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten]     distmod: ubuntu1804
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten]     distarch: x86_64
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten]     target_arch: x86_64
mongodb_1   | 2020-06-12T13:54:34.760+0000 I  CONTROL  [initandlisten] options: { net: { bindIp: "*" } }
mongodb_1   | 2020-06-12T13:54:34.761+0000 I  STORAGE  [initandlisten] wiredtiger_open config: create,cache_size=1455M,cac                                                               he_overflow=(file_max=0M),session_max=33000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log                                                               =(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000,close_scan_interval=10,cl                                                               ose_handle_minimum=250),statistics_log=(wait=0),verbose=[recovery_progress,checkpoint_progress],
backend_1   | App listening on port 8887
backend_1   | Press Ctrl+C to quit.
backend_1   | Disconnected: unable to reconnect to mongodb://mongodb:27017/guestbook
backend_1   | /backend/node_modules/mongoose/node_modules/mongodb/lib/operations/mongo_client_ops.js:474
backend_1   |       throw err;
backend_1   |       ^
backend_1   |
backend_1   | Error: Disconnected: unable to reconnect to mongodb://mongodb:27017/guestbook
backend_1   |     at NativeConnection.db.on (/backend/routes/messages.js:10:11)
backend_1   |     at emitNone (events.js:106:13)
backend_1   |     at NativeConnection.emit (events.js:208:7)
backend_1   |     at NativeConnection.set (/backend/node_modules/mongoose/lib/connection.js:119:12)
backend_1   |     at /backend/node_modules/mongoose/lib/connection.js:529:26
backend_1   |     at err (/backend/node_modules/mongoose/node_modules/mongodb/lib/utils.js:415:14)
backend_1   |     at executeCallback (/backend/node_modules/mongoose/node_modules/mongodb/lib/utils.js:404:25)
backend_1   |     at err (/backend/node_modules/mongoose/node_modules/mongodb/lib/operations/mongo_client_ops.js:293:21)
backend_1   |     at connectCallback (/backend/node_modules/mongoose/node_modules/mongodb/lib/operations/mongo_client_ops.                                                               js:249:5)
backend_1   |     at process.nextTick (/backend/node_modules/mongoose/node_modules/mongodb/lib/operations/mongo_client_ops                                                               .js:471:7)
backend_1   |     at _combinedTickCallback (internal/process/next_tick.js:132:7)
backend_1   |     at process._tickCallback (internal/process/next_tick.js:181:9)
mongodb_1   | 2020-06-12T13:54:34.889+0000 I  STORAGE  [initandlisten] WiredTiger message [1591970074:889345][1:0x7f9a74c1                                                               ab00], txn-recover: Set global recovery timestamp: (0, 0)
mongodb_1   | 2020-06-12T13:54:34.892+0000 I  RECOVERY [initandlisten] WiredTiger recoveryTimestamp. Ts: Timestamp(0, 0)
mongodb_1   | 2020-06-12T13:54:34.896+0000 I  STORAGE  [initandlisten] Timestamp monitor starting
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten]
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the d                                                               atabase.
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten] **          Read and write access to data and confi                                                               guration is unrestricted.
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten]
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten]
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/ena                                                               bled is 'always'.
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten] **        We suggest setting it to 'never'
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten]
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/def                                                               rag is 'always'.
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten] **        We suggest setting it to 'never'
mongodb_1   | 2020-06-12T13:54:34.897+0000 I  CONTROL  [initandlisten]
mongodb_1   | 2020-06-12T13:54:34.899+0000 I  STORAGE  [initandlisten] createCollection: admin.system.version with provide                                                               d UUID: 7c8bac0f-69df-42d7-9a16-3288e3cb146e and options: { uuid: UUID("7c8bac0f-69df-42d7-9a16-3288e3cb146e") }
mongodb_1   | 2020-06-12T13:54:34.905+0000 I  INDEX    [initandlisten] index build: done building index _id_ on ns admin.s                                                               ystem.version
mongodb_1   | 2020-06-12T13:54:34.905+0000 I  SHARDING [initandlisten] Marking collection admin.system.version as collecti                                                               on version: <unsharded>
mongodb_1   | 2020-06-12T13:54:34.905+0000 I  COMMAND  [initandlisten] setting featureCompatibilityVersion to 4.2
mongodb_1   | 2020-06-12T13:54:34.905+0000 I  SHARDING [initandlisten] Marking collection local.system.replset as collecti                                                               on version: <unsharded>
mongodb_1   | 2020-06-12T13:54:34.906+0000 I  STORAGE  [initandlisten] Flow Control is enabled on this deployment.
mongodb_1   | 2020-06-12T13:54:34.906+0000 I  SHARDING [initandlisten] Marking collection admin.system.roles as collection                                                                version: <unsharded>
mongodb_1   | 2020-06-12T13:54:34.906+0000 I  STORAGE  [initandlisten] createCollection: local.startup_log with generated                                                                UUID: 7dfc7e6d-3f66-4fcd-88fb-69a8e79c26ef and options: { capped: true, size: 10485760 }
mongodb_1   | 2020-06-12T13:54:34.911+0000 I  INDEX    [initandlisten] index build: done building index _id_ on ns local.s                                                               tartup_log
mongodb_1   | 2020-06-12T13:54:34.911+0000 I  SHARDING [initandlisten] Marking collection local.startup_log as collection                                                                version: <unsharded>
mongodb_1   | 2020-06-12T13:54:34.911+0000 I  FTDC     [initandlisten] Initializing full-time diagnostic data capture with                                                                directory '/data/db/diagnostic.data'
mongodb_1   | 2020-06-12T13:54:34.913+0000 I  SHARDING [LogicalSessionCacheRefresh] Marking collection config.system.sessi                                                               ons as collection version: <unsharded>
mongodb_1   | 2020-06-12T13:54:34.914+0000 I  NETWORK  [listener] Listening on /tmp/mongodb-27017.sock
mongodb_1   | 2020-06-12T13:54:34.914+0000 I  NETWORK  [listener] Listening on 0.0.0.0
mongodb_1   | 2020-06-12T13:54:34.914+0000 I  NETWORK  [listener] waiting for connections on port 27017
mongodb_1   | 2020-06-12T13:54:34.915+0000 I  CONTROL  [LogicalSessionCacheReap] Sessions collection is not set up; waitin                                                               g until next sessions reap interval: config.system.sessions does not exist
mongodb_1   | 2020-06-12T13:54:34.915+0000 I  STORAGE  [LogicalSessionCacheRefresh] createCollection: config.system.sessio                                                               ns with provided UUID: c366c860-db1b-4d09-a489-8803806e081a and options: { uuid: UUID("c366c860-db1b-4d09-a489-8803806e081                                                               a") }
mongodb_1   | 2020-06-12T13:54:34.919+0000 I  INDEX    [LogicalSessionCacheRefresh] index build: done building index _id_                                                                on ns config.system.sessions
mongodb_1   | 2020-06-12T13:54:34.924+0000 I  INDEX    [LogicalSessionCacheRefresh] index build: starting on config.system                                                               .sessions properties: { v: 2, key: { lastUse: 1 }, name: "lsidTTLIndex", ns: "config.system.sessions", expireAfterSeconds:                                                                1800 } using method: Hybrid
mongodb_1   | 2020-06-12T13:54:34.924+0000 I  INDEX    [LogicalSessionCacheRefresh] build may temporarily use up to 200 me                                                               gabytes of RAM
mongodb_1   | 2020-06-12T13:54:34.924+0000 I  INDEX    [LogicalSessionCacheRefresh] index build: collection scan done. sca                                                               nned 0 total records in 0 seconds
mongodb_1   | 2020-06-12T13:54:34.924+0000 I  INDEX    [LogicalSessionCacheRefresh] index build: inserted 0 keys from exte                                                               rnal sorter into index in 0 seconds
mongodb_1   | 2020-06-12T13:54:34.927+0000 I  INDEX    [LogicalSessionCacheRefresh] index build: done building index lsidT                                                               TLIndex on ns config.system.sessions
mongodb_1   | 2020-06-12T13:54:35.002+0000 I  SHARDING [ftdc] Marking collection local.oplog.rs as collection version: <un                                                               sharded>
backend_1   | App listening on port 8887
backend_1   | Press Ctrl+C to quit.
mongodb_1   | 2020-06-12T13:54:35.671+0000 I  NETWORK  [listener] connection accepted from 172.20.0.3:57958 #1 (1 connecti                                                               on now open)
mongodb_1   | 2020-06-12T13:54:35.674+0000 I  NETWORK  [conn1] received client metadata from 172.20.0.3:57958 conn1: { dri                                                               ver: { name: "nodejs", version: "3.1.13" }, os: { type: "Linux", name: "linux", architecture: "x64", version: "3.10.0-1062                                                               .18.1.el7.x86_64" }, platform: "Node.js v8.16.0, LE, mongodb-core: 3.1.11" }
backend_1   | connected to mongodb://mongodb:27017/guestbook
backend_1   | received request: GET /messages
mongodb_1   | 2020-06-12T13:55:44.019+0000 I  SHARDING [conn1] Marking collection guestbook.messages as collection version: <unsharded>
frontend_1  | response from http://backend:8887/messages: 200
frontend_1  | received request: POST /post
frontend_1  | posting to http://backend:8887/messages- name: 김욱태 body: hello world
backend_1   | saving message...
frontend_1  | response from http://backend:8887/messages200
mongodb_1   | 2020-06-12T13:55:52.142+0000 I  STORAGE  [conn1] createCollection: guestbook.messages with generated UUID: c03e5637-1a20-4540-af9f-514c8955de2d and options: {}
backend_1   | received request: GET /messages
mongodb_1   | 2020-06-12T13:55:52.146+0000 I  NETWORK  [listener] connection accepted from 172.20.0.3:57980 #2 (2 connections now open)
mongodb_1   | 2020-06-12T13:55:52.146+0000 I  INDEX    [conn1] index build: done building index _id_ on ns guestbook.messages
frontend_1  | response from http://backend:8887/messages: 200

여기서 프롬프트 떨어지지 않고 대기중이다. 끄지말고 다른 창을 띄워서 확인하자.
또한 데이터가 기록되거나 작동하는 것들이 모두 여기 로그로 남는다.

 

 

docker-compose down

 

설명

compose 정의 파일을 바탕으로 docker-compose up 으로 생성한 컨테이너나 도커이미지를 삭제할 때 사용한다. 기본적으로 docker ps -a 에서도 없어지므로, 완전히 컨테이너가 없어진다. 다음 구문으로 사용한다. docker-compose down [옵션]

 

옵션

--rmi all

모든 이미지까지 삭제

--rmi local

커스텀 태그가 없는 이미지만 삭제

-v, --volumes

compose 정의 파일의 데이터 볼륨 삭제

 

 

docker-compose stop

 

설명

docker-compose.yml 파일을 중지한다. docker ps -a에는 나온다. 즉 완전히 없어지는 게 아니고, 중단되는 것이다.

 

 

docker-compose start

 

설명

docker-compose.yml 파일을 시작한다. 맨 처음 docker-compose up을 실행한 후, docker-compose stop 한 상태에서만 가능한 명령이다.

 

 

 

docker-compose restart

 

설명

docker-compose.yml 파일을 재시작한다. 특정 컨테이너만 조작할 때는 명령의 인수에 컨테이너명을 지정한다. 

 

예시

docker-compose restart server_a

 

 

 

docker-compose pause

 

설명

- docker-compose.yml 파일을 일시정지 시킨다. docker ps 에는 exited가 아닌, 정상적으로 나온다.

 

 

 

docker-compose unpause

 

설명

- docker-compose.yml 파일을 재개시킨다.

 

 

 

docker-compose ps

 

설명

docker-compose로 작동하고 있는 컨테이너 목록이 나온다.

 

옵션

-q

컨테이너ID만 표시하게 함

 

예시

참고로, docker ps, docker container ls 등에서도 동일하게 확인 가능하다.

 

 

docker-compose logs

 

설명

- docker-compose로 올린 컨테이너들의 로그를 확인할 수 있다. watch 명령어와 연계해서 쓰면 실시간으로 로그를 확인할 수 있다.

 

 

 

docker-compose run

 

설명

- docker-compose로 시작한 컨테이너에서 임의의 명령을 실행할 때 사용한다. 특정 컨테이너에 진입해야 하거나, 상테를 체크하는 등 여러가지로 사용된다.

 

예시

docker-compose로 올린 컨테이너 중, mongodb라는 컨테이너에 쉘로 연결하기

(mongodb 라는 이름은, docker-compose.yml파일에 명시된 컨테이너 이름이다)

 

 

docker-compose port

 

설명

서비스의 공개용 포트 확인

구문 : docker-compose port [옵션] 서비스명 프라이빗포트번호

 

옵션

--protocol=proto

프로토콜, tcp 또는 udp

--index=index

컨테이너의 인덱스 수

 

예시

private 포트번호가 맞지 않으면 아예 결과가 나오지 않는다.

 

 

docker-compose config

 

설명

compose의 구성 확인. yml을 열어보는것과 동일하다.

 

예시

 

 

docker-compose kill

 

설명

실행 중인 컨테이너에 시그널을 송신한다. 시그널은 프로세스 간의 연락을 주고받는 장치로 리눅스 커널에 내장되어있다. 시그널에 대해서는 더 이상 자세한 설명은 생략한다. 옵션 없이 쓰면 SIGKILL을 송신한다. (-9, 프로세스 강제 종료)

 

옵션

-s

SIGKILL 말고 다른 시그널을 전송할 때 사용한다.

 

예시

컨테이너에 SIGINT 송신 : docker-compose kill -s SIGINT

 

 

 

docker-compose rm

 

설명

docker-compose로 생성한 컨테이너를 삭제한다.

 

* yaml 이란?

docker-compose는 yaml 형식을 사용하여 설정 파일을 만들 수 있다. yaml형식은  구조화된 데이터를 표현하기 위한 데이터 포맷. 오픈소스와 nosql에서 많이 사용한다. 또한 파이썬과 같이 들여쓰기로 데이터의 계층 구조를 나타낸다. (들여쓰기는 탭이 아니라 반각 스페이스를 사용한다) 누가 써도 읽기 쉬워서 설정 파일등에 많이 사용된다. yaml에서 데이터 맨 앞에 '-'를 붙이면 배열을 나타낼 수 있다. '-' 다음에는 반드시 반각 스페이스를 넣어야 한다. (반각 스페이스는 우리가 아는 일반 띄어쓰기이다)

 

띄어쓰기와 탭이 매우 중요하며, 명령어옆에 띄어쓰기없이 :과 그다음은 띄어쓰기 후 내용을 작성한다. 이런 식이다. "images: 내용" 또한 계층구조가 중요하다. 띄어쓰기 3개로 구분했다면 띄어쓰기 3개를 해야 같은 계층이 된다. 구문에러가 많이 나는데 보통 띄어쓰기 때문이다.

 

visual studio code 는 docker-compose.yaml작성을 위한 확장파일을 제공한다. 또한 dockerfile 도 지원한다. 가볍게 쓰는데는 노트패드++도 유용하다. 아래처럼 notepad++ 맨 아래 작업 표시줄을 오른클릭하면 언어를 설정할 수 있다.

 

* docker-compose.yml의 기본 형식

총 3개의 요소로 구성된다. 관리하고싶은서비스 (services:) , 네트워크 (networks:) , 볼륨(volumes:) 각 요소의 밑에 추가 상세 설정을 넣을 수 있다. 각 3개의 요소는 모두 명시하지 않아도 된다. 

 

* docker-compose.yml 요소 

 

버전 (version: )

 

상세설명

compose 정의 파일은 버전에 따라 기술할 수 있는 항목이 다르다. 이 버전은 정의파일 맨 앞에 정의한다. 버전을 명시적으로 정의하지 않으면 1.0으로 작동한다. 여러개의 compose 정의 파일이나 확장 서비스를 사용하는 경우는 각 파일에서 동일한 버전을 사용해야 하므로 주의해야 한다.

Compose
정의 파일 버전

Docker
Engine 버전

3.3

17.06.0

3.2

17.04.0

3.1

1.13.1

3

1.13.0

2.3

17.06.0

2.2

1.13.0

2.1

1.12.0

2

1.10.0

1

1.9.1

예시

version: "3"

 

 

이미지 지정 (image: )

 

상세설명

컨테이너의 바탕이 되는 베이스 이미지를 지정한다. 이미지 이름 또는 이미지ID 가 사용된다. 이러한 이미지는 로컬에 있으면 그대로 사용하고, 없으면 도커허브로부터 자동으로 받는다. (도커허브의 모든 이미지 사용 가능) 이미지에 태그가 없으면 latest를 다운받는다.

 

예시

 

 

이미지 빌드 (build: )

 

상세설명

보유하거나 도커허브 이미지를 사용하지 않고 dockerfile로 이미지 작성을 기술하고 그것을 빌드해서 사용하는 경우에 사용한다. 여기에는 도커파일의 경로를 입력한다. 보통 yml 파일과 dockerfile이 같은경로에 있고, 여기에는 현재경로를 의미하는 . 만 입력하게 된다.

 

임의의 이름을 쓰는 경우, dockerfile: 항목을 지정한다. 이 때 dockerfile이 있는 경로나 git 리포지토리 URL을 context: 에 지정할 수 있다. 또한 인수를 지정할 수도 있다. args:  사용한다. args는 환경변수의 값을 정의하며, 해당 인수는 dockerfile에서 ARG로 환경변수 이름을 지정하

 

bool 연산자(true/false/yes/no) 사용하는 경우는 따옴표로 둘러싸야 한다. 환경변수 값은 docker compose 실행하는 머신 위에서만 유효하다.

 

예시

1. /data 저장되어있는 Dockerfile-alternate 라는 이름의 도커파일을 빌드하기

2. 현재 위치에 Dckerfile 빌드하기

3. 인수를 지정하여 Dockerfile 빌드하기

아래와 같이 도커파일에 projectno 변수를 선언하고, docker-compose.yml에서 해당 projectno에 1이란 값을 넣었다. 즉 결과로서 1이 나오게 된다.

vi Dockerfile

FROM centos:7
ARG projectno
RUN echo "$projectno"

 

 

 

컨테이너 안에서 작동하는 명령 지정 (command: , entrypoint: )

 

상세설명

컨테이너에서 작동할 명령은 command: , entrypoint: 로 지정한다. dockerfile의 CMD, ENTRYPOINT와 동일하다. 다만, Dockerfile에 정의되어 있는 것보다 docker-compose에서 정의하는게 더 우선순위가 된다. 즉 CMD, ENTRYPOINT 모두 덮어씌워버린다.

 

예시

Dockerfile에 ENTRYPOINT가 정의되어 있다 하더라도, 아래 docker-compose에서 정의된 대로 작동한다.

 

 

 

컨테이너 간 연결 (link: )

 

상세설명

다른 컨테이너에 대한 링크 기능을 사용하여 연결하고 싶을 때, link를 사용하여 연결할 컨테이너명을 설정한다. 컨테이너명과는 별도로 알리아스명을 붙이고 싶을 때는 '서비스명:알리아스명' 형식으로 지정한다. (아래 예시에서는 db:testdb 이런식으로 할 수 있다) 그런데 이 명령은 도커의 legacy 기능으로, 이제 사용하지 않는 것을 권고한다. 

 

예시

 

 

컨테이너 간 통신 (ports: )

 

상세설명

ports는 컨테이너가 공개하는 포트이다. 즉 호스트OS의 포트와 컨테이너의 포트를 binding 해준다. "호스트포트번호:컨테이너포트번호" 형식으로 지정하거나, 컨테이너의 포트번호만 지정한다. 컨테이너 포트번호만 지정하면, 호스트 포트는 랜덤하게 설정된다.

 

이 명령은 docker-compose up 명령으로 실행할 때만 적용되므로, docker-compse run 명령을 사용하여 실행하는 경우 --service-ports 옵션을 사용해야 한다. 추가로, yaml은 xx:yy 형식을 시간으로 해석하므로 포트번호 설정시 반드시 " " 안에 넣어서 문자열로 정의해야 한다.

 

예시

 

 

컨테이너 간 통신 (expose:)

 

상세설명

호스트 머신에 대한 포트를 공개하지 않고 컨테이너만 포트를 공개한다. 로그 서버와 같이 호스트 머신에서 직접 액세스하지 않고 웹 애플리케이션 서버 기능을 갖고 있는 컨테이너를 경유해서만 액세스 하고 싶은 경우 등에 사용된다. (또는 link 로 연결된 컨테이너↔컨테이너 간의 통신 등에서 사용된다) 그리고, dockerfile에 EXPOSE가 명시되어 있다면 docker-compose에서는 작성하지 않아도 된다.

 

예시

 

 

서비스 의존관계 정의 (depends_on: )

 

상세설명

서비스 간의 의존관계를 지정할 때 사용한다. 예를들어 웹서버 컨테이너를 시작하기 db 컨테이너와 redis 컨테이너를 시작하고 싶을때는 예시와 같이 정의한다. 주의할 점은, depends_on은 컨테이너 시작 순서만 제어할 뿐 컨테이너상 애플리케이션이 이용가능할때까지 기다리고 제어하는게 아니다. 즉 의존관계가 있는 서비스의 준비가 끝날때까지 기다리는게 아니므로 어플리케이션 측에서 대책을 세울 필요가 있다. 다음 링크가 도움이 될 것이다. https://docs.docker.com/compose/startup-order/

 

Control startup and shutdown order in Compose

You can control the order of service startup and shutdown with the depends_on option. Compose always starts and stops containers in dependency order, where dependencies are determined by depends_on, links,...

docs.docker.com

예시

 

 

컨테이너 정보 설정 (container_name: )

 

상세설명

docker-compose 로 생성되는 "컨테이너"에 이름을 붙일 때 사용된다. 예를들어 elasticsearch로 정의한 컨테이너에 elk_m 이라고 이름을 붙이려면 아래 예시대로 한다. 단, 도커 컨테이너명은 고유해야 하므로 커스텀명을 지정하면 여러 컨테이너로 스케일할 수 없어진다.

 

예시

 

 

컨테이너 정보 설정 (labels: )

 

상세설명

label은 컨테이너에 라벨을 붙이는 데 사용한다. 라벨은 여러 목적이 있는데 일반적으로 주석이나 정보등으로써 메타데이터를 만들기 위해 사용한다. yaml 배열 형식 또는 해시형식 중 하나로 지정할 수 있는데, 해시 형식만 예시를 든다. 라벨 확인은 docker-compose config 명령을 사용한다.

 

예시 (2가지)

 

 

컨테이너 환경변수 지정 (environment: , env_file: )

 

상세설명

컨테이너 안의 환경변수를 지정한다. yaml 배열 형식 또는 해시형식 중 하나로 지정할 수 있다. 여기서는 해시 형식만 예를 든다. 설정할 환경변수가 많은 경우다른 파일에서 환경변수를 정의하고  파일을 읽는것이  효과적이다환경변수파일을 읽을때는 env_file:  사용한다. docker-compose.yml 파일과 같은 경로에 envfile 이라는 이름의 파일을 만들고 거기에 환경변수를 넣으면 된다. envfile 많은 경우여러개를 읽을수도 있다파일 지정은 yaml 배열 형식으로 지정한다. 참고로, 애플리케이션 안에서 사용하는 API 키같은 비밀정보의 관리는 컨테이너 오케스트레이션 툴의 기능을 사용하는게 좋다.

 

예시

 

 

컨테이너 데이터 관리 (volumes: , volumes_from)

 

상세설명

컨테이너에 볼륨을 마운트 할 때 사용한다. 호스트 측에서 마운트 경로를 지정하려면, 호스트디렉토리경로:컨테이너디렉토리경로 형식으로 사용한다. 또한 볼륨 지정 뒤에 ro(읽기전용) 등의 옵션을 지정하면 해당 옵션으로 마운트할 수 있다.  ro 옵션은 보통 설정 파일이 저장되거나 수정이 되지 않도록 하는 곳에 지정할 수 있다. 

 

다른 컨테이너가 가진 모든 볼륨을 마운트  때는 volumes_from: 에 해당 컨테이너를 지정한다. 아래 예시처럼 elastic-m 이라는 컨테이너의 볼륨을 그대로 재사용한다.

 

예시 : volumes

예시 : volumes_from

 

* Docker compose 란?

여러 컨테이너를 모아서 관리할 수 있는 툴이다. 여러 컨테이너를 실행시킬 때 각 컨테이너마다 구성이나 시작/정지 등을 관리하면 운용이 매우 복잡하다. 따라서 서비스 단위로 컨테이너를 묶어 일괄적으로 관리할 필요가 있다.

 

docker compose는 docker-compose.yml 이라는 파일에 컨테이너 구성 정보(이미지, 환경변수, 네트워크, 포트 기타등등)를 정의함으로써 동일 호스트상의 여러 컨테이너를 일괄적으로 관리할 수 있다. 즉, 서로 아무런 관계가 없는 컨테이너들을 docker compose로 묶는 것이 아니고, 서로 연관관계가 있는 경우에 사용하는 것이다.

 

docker compose를 사용하면 서비스단위로 docker compose.yml 파일을 만들어놓고 필요할 때 사용할 수 있다. 실제 운영에서 대부분 이런식으로 사용된다. 또한 docker compose로 시작한 컨테이너들도 보통의 도커 명령을 사용할 수 있으며, docker compose 컨테이너를 생성하면 docker0 를 사용하지 않고 알아서 별도의 네트워크 브릿지를 따로 생성해서 사용한다.

 

더 상세한 정보는 https://docs.docker.com/compose/ , https://doc.docker.com/compose/reference/ 참고할 것

 

# 참고 : dockerfile과 docker-compose 비교

- dockerfile :  여러개의 이미지를 만들 수 있고, run은 하지 않는다.

- docker-compose :  여러개의 이미지를 만들 수 있고, run까지 수행한다.

 

 

* 사전이해 : 인프라 구성 예시 - 웹 3계층 시스템 아키텍쳐

도커에서 여러개의 컨테이너를 어떻게 연계하여 운영하는지 이해하려면 인프라 구성을 이해해야 한다. 그 중 가장 많이 사용되는 웹 3계층 시스템 아키텍쳐를 예시로 알아보자.

 

어플리케이션을 가동시키려면 여러개의 서버에 기능과 역할을 분할하여 인프라의 전체 구성을 정한다. 이것을 인프라 아키텍처라고 한다. 대규모 웹 시스템의 경우 몇 개의 서브시스템/기능 으로 나누어 시스템을 개발하는 경우가 많다. 웹3계층 아키텍쳐는 웹 어플리케이션의 대표적인 인프라 아키텍쳐 중 하나로, 웹 시스템 서버들을 역할별로 3개로 나눈다. 대부분이 이렇게 사용한다.

 

프론트서버 ↔ 어플리케이션서버  데이터베이스서버

 

이러한 구조는 논리적인 분할이므로 부하가 적은 시스템에서는 한 노드에서도 구현이 가능하다.  클라우드 시스템에서 실행할 때는 부하에 따른 오토스케일 기능을 사용하는 것이 좋다.

 

1. 프론트 서버

클라이언트의 웹 브라우저가 보낸 http 요청을 받아, http 응답을 반환하는 서버 기능을 가진다. 웹 프론트 서버 또는 웹 서버라고 부른다. 웹 서버의 기능은 미들웨어로 구축하는 경우도 있고, 오픈소스인 nginx, 마이크로소프트 IIS 등도 있다. 요청의 처리가 메인 업무이므로 부하가 높은 경우에는 scalable하게 처리 대수를 늘리고, 로드밸런서와 같은 기기를 사용하여 부하를 분산한다.

 

2. 어플리케이션 서버

어플리케이션 서버는 업무 처리를 실행하는 서버이다. 결제 처리, 수주 처리 등 에플리케이션의 처리를 실행하는 프로그램의 실행 환경이 된다. 애플리케이션 서버 기능도 프론트 서버 기능과 마찬가지로 미들웨어로 구축하는 경우도 있다.

 

3. 데이터베이스 서버

데이터베이스서버는 영구 데이터를 관리하기 위한 서버이다. 애플리케이션의 처리 실행에서 발생하는 영구 데이터는 RDBMS기능을 갖고 있는 미들웨어에서 관리된다. 주요 미들웨어로는 MYSQL, postgreSQL, 오라클 등이 있다. 영구 데이터는 높은 가용성이 요구되므로 클러스터 같은 기술로 다중화한다. 또한 만일의 장애를 대비하여 데이터의 백업이나 원격지 보관 등과 같은 대책이 필요하다. 추가적으로, 데이터베이스를 조작하는 처리는 부하가 걸릴 수 있으므로 시스템 전체의 병목 부분이 되는 경우가 있다. 그래서 운용 상황에 따라 OS나 미들웨어의 파라미터 설정의 변경과 같은 퍼포먼스 튜닝이 필요하다.

 

# 참고 : NOSQL

NOSQL은 RDBMS와는 다른 새로운 방식을 총칭한다. 병렬분산처리나 유연한 스키마 설정 등이 특징이며, 주요 방식으로는 KVS(Key-value 스토어)나 도큐먼트 지향 데이터베이스 (도큐먼트 데이터베이스) 등이 있다. 대량의 데이터 축적이나 병렬처리가 특기이므로 많은 사용자의 액세스를 처리할 필요가 있는 온라인 시스템 등에서 널리 사용된다. 오픈소스인 redis 나 mongoDB가 유명하다.

 

 

* docker-compose.yml 정의 파일 개념

docker-compose.yml 정의 파일은 웹 애플리케이션의 의존관계 (데이터베이스 ,큐, 캐시, 애플리케이션 등)를 모아서 설정할 수 있다. 이 정의 파일을 구축해두면 이 파일을 바탕으로 docker-compose 명령을 실행하여 여러개의 컨테이너를 모아서 시작하거나 정지할 수 있다.

 

또한 컨테이너 구성정보들을 yaml 파일로 관리할 수 있어 지속적 deploy나 지속적 integration 프로세스에 있어 자동 테스트를 할 떄의 환경 구축에도 그대로 이용할 수 있다. 하나의 docker-compose.yml 정의파일에는 여러개의 도커 컨테이너를 설정하며, 서로 의존관계를 가질 수 있다. 예를들어 웹서버와 db 각각의 컨테이너를 정의했다면 db가 올라와야 웹서버가 뜰 수 있다. 이런식의 의존성이 있다는 것이다. 또한 확장자는 .yml, yaml 모두 사용 가능하다.

 

 

* docker-compose 설치

1. docker compose 버전 확인

https://github.com/docker/compose/releases

 

2. docker compose 다운로드

github를 통해 수동으로 다운받을 수 있다. 각 운영체제 벤더 종류별로 (yum, apt-get 등)으로 받을수 있는데 뭔가 동작이 제한적이다. 안되는 게 여러가지 있어서 수동 설치를 권고한다. 아래 명령어대로 다운받으면 되며, 1번에서 확인된 버전 숫자를 기입하면 된다. (아래 1.26.0 부분, 2020.6.25 기준 1.26.0이 가장 최신임)

curl -L https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose

 

3. 버전확인

docker-compose -v

혹시 permission denied가 뜨는 경우, 해당 경로로 들어가서 실행권한을 주면 된다.

cd /usr/local/bin/
chmod 744 docker-compose

FROM

 

상세설명

docker 컨테이너를 어떤 docker 이미지로부터 생성할지 기술. 이것을 베이스 이미지라고 한다. 이 명령은 필수 명령 항목이다. 태그를 생략하면 베이스 이미지의 최신 버전이 적용된다. 고유한 이미지를 특정해서 명시하려면 다이제스트를 사용한다.

 

예시

FROM [이미지명]
FROM [이미지명]:[태그명] 
FROM [이미지명]@[다이제스트]

FROM centos:centos7
FROM tensorflow/tensorflow@sha256:27394k393.....

 

# 참고 : digest 값이란?

도커 허브에 이미지를 업로드하면 자동으로 부여되는 unique한 식별자. 이 값은 고유하므로 특정 이미지를 지정할 때 사용할 수 있다. 다이제스트 값을 확인하려면 다음 명령을 사용한다.

docker image ls --digest [이미지명]

 

LABEL

 

상세설명

Dockerfile로 생성할 이미지에 버전 정보나 작성자 정보, 코멘트 등을 제공하는데 사용한다.

 

 

예시

구문 : LABEL <키 명>=<값>

도커파일에 아래와 같이 명시할 수 있다.

LABEL maintainer="sungsikyung<ssking@live.co.kr>"
LABEL title="webapp"
LABEL version="10.1"
LABEL description="This test images thanks"

빌드 후, docker image inspect로 확인 docker image inspect --format="{{ .Config.Labels }}" 이미지명

map[description:This test images thanks maintainer:sungsikyung<ssking@live.co.kr title:webapp version:10.1]

 

 

SHELL

 

상세설명

쉘 형식으로 명령을 실행할 때의 기본 쉘을 설정한다. SHELL 명령을 지정하지 않았을 때 리눅스의 기본 쉘은 /bin/bash -c 이며, 윈도우는 cmd /S /C 이다. SHELL 명령을 지정하면, 그 쉘은 그 이후에 도커파일 안에서 쉘 형식으로 지정한 RUN, CMD, ENTRYPOINT 명령에서 유효해진다.

 

예시

구문 : SHELL ["쉘의 경로", "파라미터"]

SHELL ["/bin/bash", "-c"]
RUN echo hello

 

 

WORKDIR

 

상세설명

작업 디렉토리를 지정한다. 즉 도커파일에서 정의한 명령을 실행하기 위한 기본 디렉토리를 지정한다. 대상이 되는 명령은 다음과 같다. (RUN, CMD, ENTRYPOINT, COPY, ADD) 또한, WORKDIR 명령에서 지정한 디렉토리가 존재하지 않는다면 알아서 새로 만드므로 따로 미리 만들어 둘 필요는 없다. 이 명령은 도커파일 안에서 여러번 사용할 수 있다. 필요에 기본디렉토리를 바꿔가며 dockerfile 내용을 전개할 수 있다. 만약 상대 경로를 지정한 경우는 이전 WORKDIR 명령의 경로에 대한 상대 경로가 된다. 추가로, WORKDIR 명령에는 ENV 명령에서 지정한 환경변수를 사용할 수 있다.

 

예시

ENV로 지정한 환경변수를 사용하기

ENV DIRPATH /first
ENV DIRNAME second
WORKDIR $DIRPATH/$DIRNAME
RUN ["pwd"]

결과 : /first/second

 

 

USER

 

상세설명

이미지 실행이나 도커파일의 RUN, CMD, ENTRYPOINT 명령을 실행하기 위한 사용자를 지정한다. USER 명령에서 지정하는 사용자는 RUN 명령으로 미리 만들어 두어야 한다. 구문은 다음과 같다 : USER [사용자명/UID]

 

예시

RUN ["whoami"]     // 현재 root가 현재유저
RUN ["adduser", "test1"]    // 유저를 생성
USER test1   // 해당 유저로 로그인한다.
RUN ["whoami"]    // test1 유저 상태를 확인할 수 있다.

 

 

ENV

 

상세설명

도커파일 안에서 환경변수를 설정한다. ENV 명령으로 지정한 환경변수는 컨테이너 실행시의 docker container run 명령의 --env 옵션을 사용하면 변경할  있다. 구문 key value 형식과 key=value 형식 2가지가 있다.  

 

key value 형으로 지정하는 경우, 단일 환경변수에 하나의 값을 지정한다. 첫 번째 공백 앞을 key로 설정하면 그 이후는 모두 문자열로서 취급한다. 공백이나 따옴표와 같은 문자를 포함한 것도 문자로 취급한다. 이 명령을 실행하면 명령별로 도커 이미지 레이어를 만든다. 아래 예시는 ENV가 3줄이므로 3개의 도커 이미지 레이어가 생긴다.

 

예시 (ENV [key] [value] )

ENV myName "Sung si kyung"
ENV myOrder Gin Whisky Clvados
ENV myNickName clova

 

key=value 형으로 지정하는 경우, 한 번에 여러 개의 값을 설정할 때는 이 방식을 사용한다. 아래 예시처럼 ENV 후 한줄에 싹 다 쓸 수 있다. 이렇게 한줄에 하면 만들어지는 도커 이미지는 하나이다. 또한 변수 앞에 \를 추가하면 이스케이프 처리를 할 수 있다. 예를 들어 \$myName은 $myName이라는 리터럴로 치환할 수 있다.(기호를 단순한 문자로 바꾼다는 의미)

 

예시 (ENV [key] = [value])

ENV myName="Sunghyun Kim"/
    myOrder=Gin Whisky Clvados/
    myNickName sshkim

 

 

ARG

 

상세설명

도커파일 안에서 사용할 변수를 정의한다. 이 명령을 사용하여 변수의 값에 따라 생성되는 이미지의 내용을 바꿀 수 있다. 환경변수 ENV와 달리 이 변수는 도커파일 안에서만 사용할 수 있다. 또한, 해당 도커파일을 빌드할 때, --build-arg 옵션을 사용하여, 도커파일 내부에 정의된 변수에 기본값을 넣을 수 있다.

 

예시 (ARG 이름 [=기본값]) 

ARG YOURNAME="test1"
RUN echo $YOURNAME

 

예시대로 도커파일을 구성하고 빌드할 때 아무 옵션 없이 한다면 그대로 test1이 정의되겠지만, 아래와 같이 --build-arg 옵션을 사용하여 변수를 바꿀 수 있다. 원래 test1이었으나 yunhee로 바뀐다.

docker build. --build-arg YOURNAME=yunhee

 

 

< RUN, CMD, ENTRYPOINT >

 

이 세 명령어는 모두 명령을 실행하는 역할을 한다. 하지만 각기 특성이 있고 차이가 있어 함께 설명해야 이해가 빠르므로, 3개를 비교하여 함께 설명한다.  기본적으로 RUN은 이미지 작성, 즉 컨테이너 자체에 적용된 명령이며, CMD와 ENDPOINT는 만들어진 이미지를 바탕으로 생성된 컨테이너 위에서 실행하는 명령이라고 이해할 수 있다. 예를들어, 웹서버를 가동하기 위해 nginx를 설치하는것은 RUN으로 하지만, 설치한 nginx를 데몬으로서 컨테이너 안에서 작동시키려면 CMD를 사용한다.

 

세 명령어 모두 실행 방식 2가지를 가진다. shell 커맨드 방식, EXEC 방식 두가지이다.

 

1. 쉘커맨드 방식

내부적으로 shell 명령어를 호출(/bin/sh -c <command>)하여(/bin/sh -c "명령어" 는 그냥 쉘에서 명령어 치는거랑 동일한 효과이다), 사용자가 지정한 command를 호출한다. 즉 쉘을 한번 통해야 한다.

RUN command
CMD command param1 param2
ENTRYPOINT command param1 param2

<Example>
RUN apt-get install -y nginx
CMD echo "This is a test." 
ENTRYPOINT echo $HOME

 

2. EXEC 방식

shell방식처럼 내부적인 변환이 아니라, 사용자가 명확하게 실행파일을 명시하여 실행할 수 있다. 쉘을 통하지 않는다. 꼭 EXEC 형식이 필요한 경우는 /bin/bash가 없는 컨테이너인 경우, 다른 쉘을 이용하고 싶은 경우 RUN 명령에 쉘의 경로를 지정하는 방식으로 EXEC를 사용할 수 있다.

 

EXEC 형식에서는 실행할 명령을 JSON 배열로 지정한다.  또한 값에 문자열을 입력할 때는 싱글쿼테이션을 쓴다. SHELL 방식보다 EXEC 방식이 권고된다.

RUN ["executable", "param1", "param2"]
CMD [“executable”,”param1”,”param2”]
CMD [“param1”,”param2”] (ENTRYPOINT 인수 형식. 실행되는 명령은 없고 ENTRYPOINT가 실행될 때 파라미터 전달) 
ENTRYPOINT [“executable”, “param1”, “param2”]

<Example>
RUN ["apt-get", "install", "python3"]
RUN ["/bin/bash", "-c", "apt-get install -y nginx"]
RUN ["echo", "hello. this is jacob"]
CMD ["/bin/echo", "Hello world"]
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["/bin/echo", "Hello world"]
ENTRYPOINT ["nginx", "-g", "daemon off;"]

 

# 참고 : EXEC 방식에서 쉘 변수 사용

ENTRYPOINT ["/bin/echo", "Hello, $name"] 는 결과가 Hello, $name 이라고 나온다. EXEC 방식은 쉘을 통과하지 않으므로 쉘 변수를 읽을 수 없기 때문이다. 다음과 같이 쉘을 실행시키고 echo 명령을 수행하도록 하면 제대로 작동한다.

FROM ubuntu:latest 
ENV name John Dow   
["/bin/bash", "-c", "echo Hello, $name"]

 

CMD, ENTRYPOINT, RUN 을 어떻게 쓰는지 간단하게 정리하면 다음과 같다.

 

RUN : 패키지를 설치할 때

CMD : 컨테이너 실행 시점/환경에 따라 명령어를 다양하게 지정해야 하는 경우

ENTRYPINT : 컨테이너 실행 시 항상 수행해야 하는 명령어 지정. 웹서버, DB 등 프로세스가 항상 구동되어야 할 때 등.

 

관련하여 더 상세사항은 다음 링크를 참고한다. docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact

 

Dockerfile reference

Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command...

docs.docker.com

 

RUN

 

상세설명

FROM 명령에서 지정한 베이스 이미지에 대해새로운 레이어에서 (즉 이미지가 생성됨) 명령어를 실행한다. 패키지 설치, 미들웨어 설정, 환경 구축등을 위한 명령을 실행하는 데 많이 사용된다. 도커파일 내에 기술된 순서에 따라 순차적으로 실행하므로 도커 이미지 실행시 명령어 한줄씩 실행되는 로그를 확인할 수 있다. RUN 명령의 개수는 제한이 없다. 

 

# 참고 : 주의사항

RUN에 명령어를 입력할 때 서로 관계있는 명령어는 &&를 사용하여 한 레이어에 수행해야 한다. 예를들어, 예시1보다는 예시2대로 수행해야 한다. 각 명령은 레이어별로 이미지가 생기므로, 예시1의 마지막줄 RUN은 apt-get update가 적용되지 않는다.

예시1 
FROM ubuntu:14.04 
RUN apt-get update 
RUN apt-get install -y curl

예시2

FROM ubuntu:14.04 

RUN apt-get update && apt-get install -y curl

# 참고 : 명령 줄바꿈

RUN 명령 실행시 \ 으로 줄바꿈이 가능하다. 가독성을 위해 사용된다.

RUN yum -y install\
           httpd\
           php

 

CMD

 

상세설명

컨테이너 실행 시 default로 실행되는 명령이나 파라미터를 설정한다. docker container run 실행 시 따로 명령어를 명시하지 않으면, CMD에 명시된 명령이 default로 실행된다. 만약 따로 명령어를 명시한다면, CMD로 기술한 내용은 무시되고, 실행 시 명시한 명령어가 작동된다. 또한 ENTRYPOINT 명령의 파라미터를 설정하는 역할도 수행한다. dockerfile에는 하나의 CMD를 기술할 수 있다. 만일 여러개를 쓴다면 마지막 명령만 유효하다. 

 

 

ENTRYPOINT

 

상세설명

CMD는 docker container run 실행 시 따로 명령어가 명시된다면 해당 명령어로 대체되지만, ENTRYPOINT는 컨테이너 실행 시 반드시 실행 명령에 포함된다. 또한 컨테이너 실행 시 따로 명령어를 명시하는 경우, ENTRYPOINT와 명령어가 동일하다면 옵션만 명시한 명령어대로 적용되고, 명령어가 다르다면 ENTRYPOINT의 내용과 컨테이너 실행시 명시한 내용이 한 줄에 다 쓰는 것처럼 되어 에러가 난다. (이해가 안될테니 아래 예시 참고. 내가썼는데도 이해가 안됨..)

 

예를들어, 엔드포인트가 ls -artl 이고 컨테이너 실행시 명령을 ls -al 로하면 ls -al로 실행되지만, 컨테이너 실행시 df -h 로 했다면 실제 명령이 ls -artl df -h 가 되어 에러가 나는 것이다.

 

추가로, container run 시 --entrypoint="실행명령" 옵션을 사용하여 dockerfile에 정의한 ENTRYPOINT를 변경할 수 있다. ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $name"] 가 포함된 도커 파일을 빌드한 후 컨테이너를 생성할 때 다음과 같이 수행했다면, (docker run --entrypoint="cat" test /etc/hosts) "echo Hello, $name"이 부분은 무시되고 cat /etc/hosts 명령이 실행된다.

 

ENTRYPOINT 명령에서 지정한 명령은 반드시 컨테이너에서 실행되는데, 실행 시 명령 인수를 지정하고 싶을 때 CMD와 함께 조합하여 사용되기도 한다. 예를들어, ENTRYPOINT 명령은 실행하고 싶은 명령 자체, 즉 꼭 실행하는 명령을 지정하고, CMD 명령은 ENTRYPOINT로 실행된 명령의 인수를 지정하면 컨테이너 실행시 디폴트 작동을 결정할 수 있다.

 

# 참고 : ENTRYPOINT와 CMD를 함께 사용하기

예시1 : 아무 인수 없이 docker container run 했을 때, "Hello world" 가 출력된다. 만약  docker container run 할 때 인수를 주면, "Hello 인수" 가 출력된다.

FROM ubuntu 
ENTRYPOINT ["/bin/echo", "Hello"] 
CMD ["world"] 

예시2 : 명령 없이 그대로 컨테이너를 실행하면, top -d 10 이 실행된다. docker container run -it sample -d 2 이렇게 실행하면, top -d 2 가 실행된다.

FROM ubuntu:16.04 
ENTRYPOINT ["top"] 
CMD ["-d", "10"] 

 

 

STOPSIGNAL

 

상세설명

docker container stop을 실행하면 도커 데몬은 컨테이너에게 signal을 보내서 중지시킨다. 기본적으로는 SIGTERM을 사용한다. (SIGTERM은 15이고, SIGKILL은 9이다) STOPSIGNAL은 이러한 컨테이너 종료 시그널을 변경할 수 있다. 크게 사용되지는 않는다.

 

구문 및 예시

STOPSIGNAL [시그널번호 또는 이름]
STOPSIGNAL SIGKILL

 

 

EXPOSE

 

상세설명

컨테이너의 공개 포트번호를 지정한다. 이 명령은 도커에게 실행중인 컨테이너가 LISTEN 하고 있는 네트워크를 알려준다. 이 명령 자체가 작성된 포트를 실행하여 리스닝시키는 것이 아니므로, 실제로 포트를 열려면 컨테이너 시작시 -p 또는 -P 옵션을 사용해야 한다. 

 

구문 및 예시

EXPOSE 포트넘버/프로토콜 (프로토콜을 지정하지 않으면 기본값은 tcp이다)
EXPOSE 8080/tcp    // 8080 TCP 포트를 공개

 

 

 

HEALTHCHECK

 

상세설명

컨테이너 안의 프로세스가 정상적으로 작동하고 있는지 체크하는 구문을 추가한다. 이러한 체크는 체크 간격, 체크 타임아웃 시간, 타임아웃 회수 등으로 해당 컨테이너의 상태를 체크할 수 있다. 헬스체크 결과는 docker container inspect 명령으로 상세하게 확인할 수 있고, 간단하게는 docker container ls 명령으로도 확인할 수 있다.

 

구문

HEALTHCHECK [옵션] CMD 실행할명령

 

옵션

옵션명

설명

기본값

--interval=n

헬스체크 간격

30초

--timeout=n

헬스체크 타임아웃

30초

--retries=N

타임아웃 횟수

3

 

예시

5분마다 가동 중인 웹서버의 메인페이지(http://localhost/)를 3초안에 표시할 수 있는지 없는지 확인하기 (도커파일까지 구축하는 상세 예시는  아래 전체 통합 예시 참조)

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1

 

* docker container inspect로 상태 확인

 

처음에는 이렇게 뜬다.

"Health": {
                "Status": "starting",
                "FailingStreak": 0,
                "Log": []
            }

안되면 아래와 같은 로그들이 남는다.

"Health": {
                "Status": "starting",
                "FailingStreak": 1,
                "Log": [
                    {
                        "Start": "2020-02-12T14:30:47.743974318+09:00",
                        "End": "2020-02-12T14:30:47.872516208+09:00",
                        "ExitCode": 1,
                        "Output": "/bin/sh: 1: curl: not found\n"
                    }
                ]

여기 보면 curl 명령이 없는것을  수있다직접 들어가서 설치 다시 기다려보자.

 "Health": {
                "Status": "healthy",
                "FailingStreak": 0,
                "Log": [
                    {
                        "Start": "2020-02-12T14:30:47.743974318+09:00",
                        "End": "2020-02-12T14:30:47.872516208+09:00",
                        "ExitCode": 1,
                        "Output": "/bin/sh: 1: curl: not found\n"
                    },
                    {
                        "Start": "2020-02-12T14:35:47.889708934+09:00",
                        "End": "2020-02-12T14:35:48.028551125+09:00",
                        "ExitCode": 0,
                        "Output": "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r100   612  100   612    0     0  45566      0 --:--:-- --:--:-- --:--:-- 43714\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n"
                    }
                ]
            }

붉은색 부분이 홈페이지 코드이다. 이번엔 잘 나왔다. 시간도 칼같이 5분간격이다. 만약 옵션에 정한 일정 횟수가 실패한다면, status 부분에서 unhealthy를 확인할 수 있다.

 

 

ADD

 

상세설명

이미지에다가 호스트에 있는 파일이나 디렉토리, 원격 파일을 도커 이미지 안에 추가한다.(복사함)

 

구문

ADD 호스트파일경로 도커이미지파일경로

ADD ["호스트파일경로" "도커이미지파일경로"]

 

호스트 파일 경로에는 와일드카드, GO언어의 filepath.match 룰과 일치하는 패턴을 사용할 수 있다. 도커 이미지안의 파일은 절대경로로 지정하거나 WORKDIR 명령에서 지정한 디렉토리를 기점으로 상대경로로 지정한다. 호스트의 파일이 TAR 아카이브거나 압축포맷(gzip, bzip2 등)일 때는 디렉토리로 압축을 푼다. 단 원격 URL로 받은것은 압축이 풀리지 않는다.

 

# 참고 : 원격파일을 받는 경우

이미지에 추가할 파일이 원격파일 URL일 경우, 추가한 파일은 퍼미션이 600이 되며, 만일 취득한 원격 파일이 HTTP Last-Modified 헤더를 갖고 있다면 추가된 파일에서 mtime 의 값으로 사용된다. 원격파일URL을 받을 경우, 인증을 지원하지 않으므로 원격파일의 다운로드에 인증이 필요한 경우 RUN 명령에서 wget이나 curl 명령을 사용해야 한다. 또한 이미지 안의 파일 지정이 파일(마지막이 슬래시가 아님)일 때는 URL로부터 파일을 다운로드하여 지정한 파일명을 추가한다. 이미지 안의 파일 지정이 디렉토리(마지막이 슬래시) 일 때는 파일명은 URL로 지정한 것이 된다.

 

예시

호스트상의 host.html을 이미지 안에 /docker_dir/ 에 추가
ADD host.html /docker_dir/

hos로 시작하는 모든 파일 추가
ADD hos* /docker_dir/

[hos]+ 임의의 한 문자 룰에 해당하는 파일 추가
ADD hos?.txt /docker_dir/

원격 파일 추가
ADD http://www.wings.msn.to/index.php /docker_dir/web/

 

 

COPY

 

상세설명

이미지에 호스트상의 파일이나 디렉토리를 복사한다. ADD명령과매우 비슷한데, ADD는 원격파일의 다운로드나 아카이브의 압축해제 등의 기능이 있지만 COPY는 단순히 "복사" 만 한다. 따라서 단순히 이미지 안에 파일을 넣고싶을때는 COPY를 사용한다.

 

예시 및 구문

구문 : COPY 호스트파일경로 도커이미지파일경로

          COPY ["호스트파일경로" "도커이미지파일경로"]

COPY /test/index.html /usr/share/nginx/html/index.html

이렇게 하면, 호스트의 /test/index.html 파일을 /usr/share/nginx/html/index.html로 넣는다.

 

 

 

VOLUME

 

상세설명

이미지에 볼륨을 할당한다. 지정한 이름의 마운트 포인트를 작성하며, 기본적으로 아래 링크에서 "호스트 디스크를 명시하지 않고 생성" 하는 방식으로 생성된다. 해당 링크 참고할 것.

2020/06/11 - [Docker Basic] - [Docker Basic] 16. Docker 데이터 저장 개념 / 기본 명령어 - volume

 

[Docker Basic] 16. Docker 데이터 저장 개념 / 기본 명령어 - volume

도커의 컨테이너안에 데이터를 저장할 수 있지만 여기에는 몇 가지 문제점이 존재한다. 1. 컨테이너가 삭제되면 데이터도 삭제된다. 2. 다른 프로세스에서 특정 컨테이너에 저장된 데이��

watch-n-learn.tistory.com

예시 및 구문

- 구문 : VOLUME ["/마운트포인트"]

- 예시도 위 링크를 참고하면 된다.

 

 

 

ONBUILD

 

상세설명

일단 해당 도커파일로 빌드를 한번 수행한 , 나온 이미지를 다른 dockerfile에서 참조하여 2번째 빌드를 실행할 발동하는 명령을 이미지 안에 설정하는 명령.  즉, 자신의 도커파일로부터 생성한 이미지를 베이스로 이미지로 한, 다른 도커파일을 빌드할 때 실행하고 싶은 명령을 기술하는 것이다. 예를 들어, 최초 도커파일에서 ONBUILD RUN echo "HELLO"를 기술했다면, 해당 도커파일을 빌드했을때는 아무일도 없지만, 빌드된 이미지를 갖고 누군가가 이미지를 새로 만들면 HELLO가 출력되게 된다. 한마디로 명령의 실행 타이밍을 늦출 수 있는 것. 

 

주요 실무 사용으로는, 예를들어 웹시스템 구축시 os설치 및 환경설정, 웹서버 설치, 각종 플로그인 설치 등의 인프라 환경 구축부분을 베이스 이미지로 만들고, 그 안에 onbuild 명령으로 이미지 안에 개발한 프로그램을 전개하는 명령 ADD나 COPY 등을 지정한다. 이렇게 하면, 최초 이미지는 인프라만 지정되어 있고, 그 다음 개발자가 해당 이미지를 갖고 어플리케이션 구축 부분을 코딩하고 아까 만든 베이스 이미지를 바탕으로 한 이미지를 작성한다. 이 이미지 안에는 프로그래밍이 끝난 업무 어플리케이션이 전개된다. 즉 "인프라 구축" "애플리케이션 전개 업무를 분할하는 것이다.

 

실제로 사용되는 예시로는, 웹서버용 이미지를 최초 빌드하는 사람이, 웹서버를 실행하는것은 그 이미지를 기반으로 다시 이미지를 만드는 사람들을 위해 지연시키고, 다른 환경만 세팅해서 최초 이미지를 만들고, 이후 그걸 가지고 여럿이 웹서버 개발을 수행할 때는 ONBUILD에 있는 웹서버 실행구문이 실행되게 하는 식으로 사용할 수 있다.

 

# 참고 : 개발 흐름의 이해

어플리케이션 개발 현장에서 한명이 모든것을 개발하는 일은 드물다. 여럿이 협업해서 진행하는 경우가 많다. 팀 멤버가 각각 도커파일을 작성하고 그걸로 이미지를 작성한다면 도커를 써도 어플리케이션 실행환경이 제각각이 될 수 있다.

 

그래서, 개발팀 안에 도거파일 작성 담당자를 정하고, 그 담당자가 OS/미들웨어의 설치나 설정, 라이브러리의 검증/도입을 수행하고 베이스가 되는 도커파일을 만든다. 다른 개발자들은 이 베이스 도커파일을 바탕으로 각자 개발한 소스코드를 전개하여 테스트를 하면, 팀 멤버 전원이 동일한 실행 환경에서 개발과 테스트를 할 수 있다.

 

베이스 도커파일을 생성하는 사람은 인프라와 애플리케이션 모두 정통한 엔지니어가 적임이다. 아무튼, 팀개발을 할 때는 개발환경의 베이스 도커파일을 바탕으로 만든 이미지를 공유하여 작업을 수행해야 한다. 또한 도커파일은 GIT 등을 사용하여 팀 내에서 공유하고 버전을 관리할 수 있다.

 

 

예시

ONBUILD ADD website.tar /var/www/html 포함된 도커파일로 이미지 webbase 만든다. 다른 사람이 webbase 가지고 FROM 사용하여 도커파일을 만들고 추가로 여러가지를 넣어서 빌드하면, ONBUILD ADD website.tar /var/www/html 실행되게 된다. (website.tar에는 웹페이지 파일이 들어있다) 예시는 실제로 수행해볼 없다. website.tar 파일이 따로 없기 때문. 예시로만 이해하면 된다

 

1. 베이스 이미지 작성

- 운영체제 설치, nginx 설치, 데몬실행 = 베이스 이미지

- 웹콘텐츠(website.tar)를 /var/www/html에 배치하는 명령 = ONBUILD

- 이것은 인프라 도커 엔지니어가 만든다고 가정한다. 따라서 아래 ONBUILD에 명시된 website.tar는 없다. (웹개발자가 만들겠지)

 

vi Dockerfile

FROM ubuntu:16.04
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get -y install nginx
EXPOSE 80
ONBUILD ADD website.tar /var/www/html/
CMD ["nginx", "-g", "daemon off;"]

 

2. 빌드

docker build -t nginx-onbuild-base .

결과는 다음과 같다.

Sending build context to Docker daemon  2.048kB

Step 1/6 : FROM ubuntu:16.04

 ---> 005d2078bdfa

Step 2/6 : RUN apt-get -y update && apt-get -y upgrade

 ---> Using cache

 ---> 9d7da8a07ab7

(중략)

Step 3/6 : RUN apt-get -y install nginx

 ---> Running in f9fdd875097d

Step 4/6 : EXPOSE 80

 ---> Running in 03723856598b

Removing intermediate container 03723856598b

 ---> f2ac4d707760

Step 5/6 : ONBUILD ADD website.tar /var/www/html/

 ---> Running in 14214a0f1a97

Removing intermediate container 14214a0f1a97

 ---> ce0c1504d710

Step 6/6 : CMD ["nginx", "-g", "daemon off;"]

 ---> Running in 0ec31127997e

Removing intermediate container 0ec31127997e

 ---> 178e09c5e53e

Successfully built 178e09c5e53e

Successfully tagged nginx-onbuild-base:latest

[root@docker-basic test]#

 

docker images에서 해당 이미지를 확인할 수 있다.

onbuild가 잘 들어갔는지 확인하기 위해 docker image insperct 명령을 사용한다.

 

3. 웹 콘텐츠 추가 및 도커파일 생성

이것은 2번째로 빌드를 수행하는 것이다. 작업은 인프라 도커 엔지니어가 아니라, website.tar 라는 파일을 만든 웹개발자가 수행하는 작업이다. website.tar 파일을 준비하여 원하는 디렉토리에 둔다. 그리고 해당 디렉토리에 Dockerfile을 생성한다.

여기서 dockerfile의 내용은 단 한줄이다.

FROM nginx-onbuild-base

 

4. 웹 컨텐츠가 추가된 도커파일 빌드

docker build -t nginx-onbuild-web .

최종적으로 2개의 이미지가 생성된 것을 확인할 수 있다.

 

5. 컨테이너 생성 및 확인

2가지 이미지를 모두 사용하여 컨테이너를 생성한다. nginx-onbuild-base는 nginx 설정까지만 수행한 이미지이고, nginx-onbuild-web은 nginx 설정한 이미지에 onbuild로 웹 데이터까지 적용한 이미지이다.

docker container run -d -p 80:80 nginx-onbuild-base 
docker container run -d -p 88:80 nginx-onbuild-web 

 

* nginx-onbuild-base 접속 (80번 포트 사용)

* nginx-onbuild-web 접속 (88번 포트 사용)

nginx-onbuild-web의 도커파일 내용은 딱 FROM nginx-onbuild-base 만 명시했다. 그런데 nginx-onbuild-base에서 정의한 ONBUILD 옵션으로 인해 이렇게 웹데이터가 전개된 것을 확인할 수 있다.

 

 

 

 

 

 

실습 : dockerfile로 ubuntu에 apache httpd 올리기

1. dockerfile 생성

특정 디렉토리를 하나 만든 후, 거기에 아래 내용으로 도커파일을 생성한다.

vi test-dockerfile

FROM ubuntu:14.04
MAINTAINER "watch-n-learn <test@test.co.kr>"
LABEL "purpose"="practice"
RUN apt-get update && apt-get install apache2 -y
RUN apt-get update && apt-get install curl -y
ADD test.html /var/www/html
WORKDIR /var/www/html
RUN ["/bin/bash", "-c", "echo hello >> test.html"]
EXPOSE 80
CMD apachectl -D FOREGROUND
HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://127.0.0.1/ || exit 1

 

2. 테스트용 html 파일 생성

아래 내용을 test.html 이라는 이름으로 도커파일이 있는 경로에 저장한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>"Hi~ Docker~!"</title>
</head>
<body>
    <h1>"Welcome to Docker World~~"</h1>
</body>

3.  빌드

도커파일과 html파일이 있는 경로에서 빌드 명령 수행

docker build -f test-dockerfile -t mybuild_html:0.0 .
[root@docker-basic webdocker]# docker build -f test-dockerfile -t mybuild_html:0.0 .
Sending build context to Docker daemon  3.072kB
Step 1/11 : FROM ubuntu:14.04
 ---> 6e4f1fe62ff1
Step 2/11 : MAINTAINER "watch-n-learn <test@test.co.kr>"
 ---> Running in 7a34515bca7b
Removing intermediate container 7a34515bca7b
 ---> e6101dd14cc4
Step 3/11 : LABEL "purpose"="practice"
 ---> Running in 8a576f8e8414
Removing intermediate container 8a576f8e8414
 ---> 65b7e94284f2
Step 4/11 : RUN apt-get update && apt-get install apache2 -y
 ---> Running in 0116b1fb59eb
Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
Ign http://archive.ubuntu.com trusty InRelease
Get:2 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:3 http://security.ubuntu.com trusty-security/main amd64 Packages [1032 kB]
Get:4 https://esm.ubuntu.com trusty-infra-security InRelease
Get:5 http://archive.ubuntu.com trusty-backports InRelease [65.9 kB]
Get:6 https://esm.ubuntu.com trusty-infra-updates InRelease
Hit http://archive.ubuntu.com trusty Release.gpg
Get:7 http://security.ubuntu.com trusty-security/restricted amd64 Packages [18.1 kB]
Get:8 https://esm.ubuntu.com trusty-infra-security/main amd64 Packages
Get:9 http://security.ubuntu.com trusty-security/universe amd64 Packages [378 kB]
Get:10 http://archive.ubuntu.com trusty-updates/main amd64 Packages [1460 kB]
Get:11 http://security.ubuntu.com trusty-security/multiverse amd64 Packages [4730 B]
Get:12 https://esm.ubuntu.com trusty-infra-updates/main amd64 Packages
Get:13 http://archive.ubuntu.com trusty-updates/restricted amd64 Packages [21.4 kB]
Get:14 http://archive.ubuntu.com trusty-updates/universe amd64 Packages [671 kB]
Get:15 http://archive.ubuntu.com trusty-updates/multiverse amd64 Packages [16.1 kB]
Get:16 http://archive.ubuntu.com trusty-backports/main amd64 Packages [14.7 kB]
Get:17 http://archive.ubuntu.com trusty-backports/restricted amd64 Packages [40 B]
Get:18 http://archive.ubuntu.com trusty-backports/universe amd64 Packages [52.5 kB]
Get:19 http://archive.ubuntu.com trusty-backports/multiverse amd64 Packages [1392 B]
Hit http://archive.ubuntu.com trusty Release
Get:20 http://archive.ubuntu.com trusty/main amd64 Packages [1743 kB]
Get:21 http://archive.ubuntu.com trusty/restricted amd64 Packages [16.0 kB]
Get:22 http://archive.ubuntu.com trusty/universe amd64 Packages [7589 kB]
Get:23 http://archive.ubuntu.com trusty/multiverse amd64 Packages [169 kB]
Fetched 13.7 MB in 8s (1620 kB/s)
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  apache2-bin apache2-data libapr1 libaprutil1 libaprutil1-dbd-sqlite3
  libaprutil1-ldap libxml2 sgml-base ssl-cert xml-core
Suggested packages:
  www-browser apache2-doc apache2-suexec-pristine apache2-suexec-custom ufw
  apache2-utils sgml-base-doc openssl-blacklist debhelper
The following NEW packages will be installed:
  apache2 apache2-bin apache2-data libapr1 libaprutil1 libaprutil1-dbd-sqlite3
  libaprutil1-ldap libxml2 sgml-base ssl-cert xml-core
0 upgraded, 11 newly installed, 0 to remove and 1 not upgraded.
Need to get 1898 kB of archives.
After this operation, 7526 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty-updates/main libxml2 amd64 2.9.1+dfsg1-3ubuntu4.13 [573 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ trusty/main sgml-base all 1.26+nmu4ubuntu1 [12.5 kB]
Get:3 http://archive.ubuntu.com/ubuntu/ trusty/main libapr1 amd64 1.5.0-1 [85.1 kB]
Get:4 http://archive.ubuntu.com/ubuntu/ trusty/main libaprutil1 amd64 1.5.3-1 [76.4 kB]
Get:5 http://archive.ubuntu.com/ubuntu/ trusty/main xml-core all 0.13+nmu2 [23.3 kB]
Get:6 http://archive.ubuntu.com/ubuntu/ trusty/main libaprutil1-dbd-sqlite3 amd64 1.5.3-1 [10.5 kB]
Get:7 http://archive.ubuntu.com/ubuntu/ trusty/main libaprutil1-ldap amd64 1.5.3-1 [8634 B]
Get:8 http://archive.ubuntu.com/ubuntu/ trusty-updates/main apache2-bin amd64 2.4.7-1ubuntu4.22 [845 kB]
Get:9 http://archive.ubuntu.com/ubuntu/ trusty-updates/main apache2-data all 2.4.7-1ubuntu4.22 [160 kB]
Get:10 http://archive.ubuntu.com/ubuntu/ trusty-updates/main apache2 amd64 2.4.7-1ubuntu4.22 [87.4 kB]
Get:11 http://archive.ubuntu.com/ubuntu/ trusty/main ssl-cert all 1.0.33 [16.6 kB]
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
dpkg-preconfigure: unable to re-open stdin:
Fetched 1898 kB in 5s (342 kB/s)
Selecting previously unselected package libxml2:amd64.
(Reading database ... 12097 files and directories currently installed.)
Preparing to unpack .../libxml2_2.9.1+dfsg1-3ubuntu4.13_amd64.deb ...
Unpacking libxml2:amd64 (2.9.1+dfsg1-3ubuntu4.13) ...
Selecting previously unselected package sgml-base.
Preparing to unpack .../sgml-base_1.26+nmu4ubuntu1_all.deb ...
Unpacking sgml-base (1.26+nmu4ubuntu1) ...
Selecting previously unselected package libapr1:amd64.
Preparing to unpack .../libapr1_1.5.0-1_amd64.deb ...
Unpacking libapr1:amd64 (1.5.0-1) ...
Selecting previously unselected package libaprutil1:amd64.
Preparing to unpack .../libaprutil1_1.5.3-1_amd64.deb ...
Unpacking libaprutil1:amd64 (1.5.3-1) ...
Selecting previously unselected package xml-core.
Preparing to unpack .../xml-core_0.13+nmu2_all.deb ...
Unpacking xml-core (0.13+nmu2) ...
Selecting previously unselected package libaprutil1-dbd-sqlite3:amd64.
Preparing to unpack .../libaprutil1-dbd-sqlite3_1.5.3-1_amd64.deb ...
Unpacking libaprutil1-dbd-sqlite3:amd64 (1.5.3-1) ...
Selecting previously unselected package libaprutil1-ldap:amd64.
Preparing to unpack .../libaprutil1-ldap_1.5.3-1_amd64.deb ...
Unpacking libaprutil1-ldap:amd64 (1.5.3-1) ...
Selecting previously unselected package apache2-bin.
Preparing to unpack .../apache2-bin_2.4.7-1ubuntu4.22_amd64.deb ...
Unpacking apache2-bin (2.4.7-1ubuntu4.22) ...
Selecting previously unselected package apache2-data.
Preparing to unpack .../apache2-data_2.4.7-1ubuntu4.22_all.deb ...
Unpacking apache2-data (2.4.7-1ubuntu4.22) ...
Selecting previously unselected package apache2.
Preparing to unpack .../apache2_2.4.7-1ubuntu4.22_amd64.deb ...
Unpacking apache2 (2.4.7-1ubuntu4.22) ...
Selecting previously unselected package ssl-cert.
Preparing to unpack .../ssl-cert_1.0.33_all.deb ...
Unpacking ssl-cert (1.0.33) ...
Processing triggers for ureadahead (0.100.0-16) ...
Setting up libxml2:amd64 (2.9.1+dfsg1-3ubuntu4.13) ...
Setting up sgml-base (1.26+nmu4ubuntu1) ...
Setting up libapr1:amd64 (1.5.0-1) ...
Setting up libaprutil1:amd64 (1.5.3-1) ...
Setting up xml-core (0.13+nmu2) ...
Setting up libaprutil1-dbd-sqlite3:amd64 (1.5.3-1) ...
Setting up libaprutil1-ldap:amd64 (1.5.3-1) ...
Setting up apache2-bin (2.4.7-1ubuntu4.22) ...
Setting up apache2-data (2.4.7-1ubuntu4.22) ...
Setting up apache2 (2.4.7-1ubuntu4.22) ...
Enabling module mpm_event.
Enabling module authz_core.
Enabling module authz_host.
Enabling module authn_core.
Enabling module auth_basic.
Enabling module access_compat.
Enabling module authn_file.
Enabling module authz_user.
Enabling module alias.
Enabling module dir.
Enabling module autoindex.
Enabling module env.
Enabling module mime.
Enabling module negotiation.
Enabling module setenvif.
Enabling module filter.
Enabling module deflate.
Enabling module status.
Enabling conf charset.
Enabling conf localized-error-pages.
Enabling conf other-vhosts-access-log.
Enabling conf security.
Enabling conf serve-cgi-bin.
Enabling site 000-default.
invoke-rc.d: policy-rc.d denied execution of start.
Setting up ssl-cert (1.0.33) ...
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
Processing triggers for libc-bin (2.19-0ubuntu6.15) ...
Processing triggers for sgml-base (1.26+nmu4ubuntu1) ...
Processing triggers for ureadahead (0.100.0-16) ...
Removing intermediate container 0116b1fb59eb
 ---> f923af6eea28
Step 5/11 : RUN apt-get update && apt-get install curl -y
 ---> Running in e5c191a1b974
Hit http://security.ubuntu.com trusty-security InRelease
Ign http://archive.ubuntu.com trusty InRelease
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://archive.ubuntu.com trusty-updates InRelease
Hit http://security.ubuntu.com trusty-security/restricted amd64 Packages
Hit http://archive.ubuntu.com trusty-backports InRelease
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://archive.ubuntu.com trusty Release.gpg
Hit https://esm.ubuntu.com trusty-infra-security InRelease
Hit http://security.ubuntu.com trusty-security/multiverse amd64 Packages
Hit http://archive.ubuntu.com trusty-updates/main amd64 Packages
Hit https://esm.ubuntu.com trusty-infra-updates InRelease
Hit http://archive.ubuntu.com trusty-updates/restricted amd64 Packages
Hit https://esm.ubuntu.com trusty-infra-security/main amd64 Packages
Hit http://archive.ubuntu.com trusty-updates/universe amd64 Packages
Hit https://esm.ubuntu.com trusty-infra-updates/main amd64 Packages
Hit http://archive.ubuntu.com trusty-updates/multiverse amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/main amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/restricted amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/universe amd64 Packages
Hit http://archive.ubuntu.com trusty-backports/multiverse amd64 Packages
Hit http://archive.ubuntu.com trusty Release
Hit http://archive.ubuntu.com trusty/main amd64 Packages
Hit http://archive.ubuntu.com trusty/restricted amd64 Packages
Hit http://archive.ubuntu.com trusty/universe amd64 Packages
Hit http://archive.ubuntu.com trusty/multiverse amd64 Packages
Reading package lists...
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
  libcurl3
The following NEW packages will be installed:
  curl libcurl3
0 upgraded, 2 newly installed, 0 to remove and 1 not upgraded.
Need to get 297 kB of archives.
After this operation, 878 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty-updates/main libcurl3 amd64 7.35.0-1ubuntu2.20 [173 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ trusty-updates/main curl amd64 7.35.0-1ubuntu2.20 [123 kB]
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
dpkg-preconfigure: unable to re-open stdin:
Fetched 297 kB in 2s (145 kB/s)
Selecting previously unselected package libcurl3:amd64.
(Reading database ... 12815 files and directories currently installed.)
Preparing to unpack .../libcurl3_7.35.0-1ubuntu2.20_amd64.deb ...
Unpacking libcurl3:amd64 (7.35.0-1ubuntu2.20) ...
Selecting previously unselected package curl.
Preparing to unpack .../curl_7.35.0-1ubuntu2.20_amd64.deb ...
Unpacking curl (7.35.0-1ubuntu2.20) ...
Setting up libcurl3:amd64 (7.35.0-1ubuntu2.20) ...
Setting up curl (7.35.0-1ubuntu2.20) ...
Processing triggers for libc-bin (2.19-0ubuntu6.15) ...
Removing intermediate container e5c191a1b974
 ---> d09fd3f9522b
Step 6/11 : ADD test.html /var/www/html
 ---> 8e09e7013f53
Step 7/11 : WORKDIR /var/www/html
 ---> Running in 5e93cc3c2b9f
Removing intermediate container 5e93cc3c2b9f
 ---> 4433d87dbe4e
Step 8/11 : RUN ["/bin/bash", "-c", "echo hello >> test.html"]
 ---> Running in e3d65669f651
Removing intermediate container e3d65669f651
 ---> f790c93359b9
Step 9/11 : EXPOSE 80
 ---> Running in 6d18dea6527b
Removing intermediate container 6d18dea6527b
 ---> d9a15c3ee4d6
Step 10/11 : CMD apachectl -D FOREGROUND
 ---> Running in 771d8762f994
Removing intermediate container 771d8762f994
 ---> 8d1ada8b3b91
Step 11/11 : HEALTHCHECK --interval=10s --timeout=3s CMD curl -f http://127.0.0.1/ || exit 1
 ---> Running in 3e22b7da57ee
Removing intermediate container 3e22b7da57ee
 ---> fcaea0bcbcbe
Successfully built fcaea0bcbcbe
Successfully tagged mybuild_html:0.0
[root@docker-basic webdocker]#

4. 컨테이너 생성

docker run -d -P --name myapacheserver mybuild_html:0.0

5. 테스트

호스트에서 아래와 같이 접속하면 처음 html파일에 정의한 내용 + dockerfile에 적용한 "hello" 값이 추가된 것을 확인할 수 있다.

 

 

+ Recent posts