도커 엔진에서 사용하는 기본 단위는 이미지(image)와 컨테이너(container)이며, 이 두 가지가 도커 엔진의 핵심입니다.
이미지는 컨테이너를 생성할 때 필요한 요소이며, VM을 생성할 때 사용되는 iso파일과 유사한 개념입니다.여러 개의 layer로 구성된 바이너리 파일로 존재하며, 컨테이너를 생성하고 실행할 때 읽기전용(read-only)로 사용됩니다.
이미지는 도커 커맨드로 내려받을 수 있으므로, 따로 설치하는 작업은 필요가 없습니다.
도커에서 사용하는 이미지의 이름은 기본적으로 [저장소 이름]/[이미지 이름]:[태그] 의 형태로 구성됩니다.
ex1) abc123/ubuntu:14.04
ex2) ubuntu:latest
저장소(repository) : 이미지가 저장된 장소를 의미합니다. 저장소 이름이 명시되지 않은 이미지는 도커 허브(Docker Hub)에서 기본적으로 제공하는 official 이미지를 의미합니다.
태그 : 이미지의 버전 관리, 또는 리비전(Revision) 관리에 사용합니다. 일반적으로 버전을 명시하지만, 태그를 생략하면 도커 엔진은 태그를 latest로 인식합니다.
도커 이미지는 ubuntu, CentOS 등 기본적인 Linux OS부터 아파치 웹 서버, MySQL Database 등의 각종 어플리케이션, 하둡, 스파크 등의 빅데이터 분석 도구까지 많은 종류가 있습니다.
이러한 이미지를 사용해 컨테이너를 생성하면 해당 이미지의 목적에 맞는 파일이 들어 있는 파일시스템과 격리된 시스템 리소스 및 네트워크를 사용할 수 있는 독립적인 공간이 생성되며, 이것을 도커 컨테이너라고 합니다.대부분의 도커 컨테이너는 생성될 때 사용되는 이미지의 종류에 따라서 알맞은 설정과 파일을 가지고 있습니다. 이러한 특징 때문에 개발 환경을 하나하나 세팅 할 필요가 없습니다.
컨테이너는 이미지를 읽기 전용으로 사용하되, 이미지에서 변경된 사항만 컨테이너 계층에 저장한다. 따라서 컨테이너에서 무엇을 하든지 원래 이미지에는 영향를 끼치지 않습니다.또한, 생성된 각 컨테이너는 각자 독립된 파일시스템을 제공받으며, 호스트와 분리되어 있어서 특정 컨테이너에서 어떤 어플리케이션/라이브러리 등을 설치하거나 삭제해도 다른 컨테이너와 호스트에는 변화가 없습니다.
다음 커맨드를 통해서 컨테이너를 생성해보겠습니다.
docker run -i -t ubuntu:20.04
docker run 커맨드는 컨테이너를 생성하고 실행하는 역할을 합니다.
ubuntu:20.04는 컨테이너를 생성하기 위한 이미지의 이름입니다.
커맨드에서 -i 와 -t 옵션을 주었는데, 이것은 컨테이너와 상호입출력을 가능하도록 합니다.
(-i 옵션으로 상호입출력을 활성화하며, -t 옵션으로 tty를 활성화하여 bash shell을 사용하도록 설정하게 됩니다. docker run 커맨드에서 이 두 옵션 중 하나라도 사용하지 않는다면 shell을 정상적으로 사용할 수 없습니다.)
실행결과:
docker run 커맨드를 입력하면 위와 같은 내용이 출력됩니다.
ubuntu:20.04 이미지가 로컬에 존재하지 않기 때문에, 도커 허브에서 자동으로 이미지를 내려받습니다.Shell의 사용자와 호스트 이름이 변경된 것을 보면 컨테이너 내부에 들어와 있다는 것을 알 수 있으며, 컨테이너의 기본 사용자는 root이고, 호스트의 이름은 무작위의 16진수 해시값입니다.
호스트 이름은 컨테이너의 고유한 ID의 앞 일부분이며, 위에서는 25ebf18758d7이 됩니다.
컨테이너와 호스트의 파일시스템은 서로 독립적이기 때문에, ls 명령어로 파일시스템을 확인해보면 아무것도 설치되지 않은 상태인 것을 확인할 수 있습니다.
그리고 exit 명령어를 통해서 컨테이너에서 호스트 환경으로 돌아오게 됩니다.
컨테이너 내부에서 호스트로 빠져나오는 방법은 아래의 두 가지가 있습니다.
exit 입력 또는 Ctrl + D를 동시에 입력 : 컨테이너 내부에서 빠져나오면서 컨테이너를 정지시킨다.
Ctrl + P, Q를 동시에 입력 : 컨테이너를 정지하지 않고, 컨테이너의 shell에서만 빠져나오게 됩니다. 컨테이너 어플리케이션을 개발하는 목적으로 컨테이너를 사용할 때 많이 사용됩니다.
이번에는 CentOS 이미지를 도커 공식 저장소에서 내려받아 보겠습니다.
docker pull centos:7
docker images 커맨드를 통해서 이미지를 정상적으로 내려받았는지 확인해봅시다. docker images는 도커 엔진에 존재하는 이미지의 목록을 출력합니다.
아까 실행한 ubuntu 20.04와 방금 다운받은 centos 7 이미지가 존재하는 것을 확인할 수 있습니다.
컨테이너를 생성할 때에는 run 커맨드가 아닌 create 커맨드를 사용할 수도 있습니다. (-i와 -t 옵션은 -it로 적용할 수도 있습니다.)
docker create -it --name mycentos centos:7
create 커맨드의 결과로 출력도니 16진수 해시값은 컨테이너의 고유 ID입니다. 너무 길어서 보통 앞의 12자리만 사용하며, docker inspect 커맨드를 통해서 컨테이너의 ID를 다시 확인할 수 있습니다.
create 커맨드를 사용하면 컨테이너를 생성하기만 할 뿐, run 커맨드와는 달리 컨테이너 내부로 들어가지는 않는다.
컨테이너 내부로 들어가기 위해서는 docker start 와 docker attach 커맨드를 통해서 컨테이너를 시작하고 내부로 들어갈 수 있습니다.
1. docker start mycentos
2. docker attach mycentos
Ctrl + P, Q 를 입력해서 컨테이너를 정지하지 않고, 컨테이너 내부에서 빠져나옵시다.
Host에서 docker ps 커맨드를 통해서 지금까지 생성한 컨테이너의 목록을 확인할 수 있습니다.
docker ps
docker ps 커맨드는 정지되지 않은 컨테이너만을 출력합니다. 방금 전에 생성한 ubuntu 20.04 컨테이너는 exit를 사용해서 컨테이너를 정지하였기 때문에 출력되지 않았습니다.
정지된 컨테이너를 포함하여 모든 컨테이너를 출력하려면 -a 옵션을 추가하면 됩니다.
docker ps -a
CONTAINER ID : 컨테이너에게 자동으로 할당되는 고유 ID. 여기에서는 ID의 일부분만 확인할 수 있음(docker inspect 커맨드를 통해서 전체 ID 확인 가능; docker inspect mycentos | grep Id)
IMAGE : 컨테이너를 생성할 때 사용된 이미지의 이름
COMMAND : 컨테이너가 시작될 때 실행될 명령어. 대부분의 이미지에서 미리 커맨드가 내장되어 있기 때문에 별도로 설정할 필요는 없음.
이미지에 내장된 커맨드는 docker run이나 create 커맨드의 맨 끝에 입력해서 컨테이너를 생성할 때 덮어쓸 수 있음.
'docker run -i -t ubuntu:16.04 echo hello world!'로 컨테이너를 생성하면, 이 컨테이너는 실행될 때마다 'echo hello world!'를 실행함
→ 그러나 내장된 /bin/bash 커맨드를 덮어쓰기 때문에 'hello world!'만 출력되고 컨테이너가 종료됨
CREATED : 컨테이너가 생성되고 난 뒤 흐른 시간
STATUS : 컨테이너의 상태; Up - 실행중 / Exited - 종료됨 / Pause - 일시 중지 등
PORTS : 컨테이너가 개방한 포트와 호스트에 연결한 포트를 나열. 외부에 노출되도록 설정하지 않았기 때문에 아무것도 출력하지 않음
NAMES : 컨테이너의 고유한 이름. 컨테이너를 생성할 때, --name 옵션으로 이름을 설정하지 않으면 도커 엔진이 임의로 형용사와 명사를 무작위로 조합해서 이름을 설정함.
위 ubuntu 컨테이너는 peaceful_germain으로 설정되어 있음.
'docker rename' 커맨드를 통해서 컨테이너의 이름을 변경할 수 있음 → docker rename peacefull_germain my_container
docker rm 커맨드를 통해서 더 이상 사용하지 않는 컨테이너를 삭제할 수 있습니다. 한 번 삭제되면 복구할 수 없으므로 주의가 필요합니다.
아래 커맨드로 ubuntu 컨테이너를 삭제해봅시다.(rename을 통해서 이름은 my_container로 변경된 상태입니다.)
1. docker rm my_container
2. docker ps -a
정상적으로 삭제된 것을 볼 수 있습니다.
이번에는 centos 컨테이너를 삭제해보겠습니다.
삭제하려고 했지만 에러가 발생하면서 삭제되지 않습니다. 내용은 살펴보면, 삭제하려는 컨테이너가 현재 실행중이라고 나오고 있습니다. 이렇게 실행중인 컨테이너는 정지를 한 후에 삭제하거나, 강제로 삭제하는 방법을 사용해야 합니다.
1. # 1. 정지 후 삭제
2. docker stop mycentos
3. docker rm mycentos
4.
5. # 2. 강제 삭제
6. docker rm -f mycentos
-f 옵션을 사용하면, 실행 중이더라도 강제로 컨테이너를 삭제할 수 있습니다.
또한 docker container prune 커맨드를 입력하면, 존재하는 컨테이너를 모두 삭제할 수 있습니다.(실행중인 컨테이너 제외)
docker container prune
docker ps 커맨드와 -a 옵션과 -q 옵션을 조합하여 컨테이너를 삭제할 수 있습니다. -a 옵션은 컨테이너 상태와 관계없이 모든 컨테이너를 출력하고, -q 옵션은 컨테이너의 ID만을 출력하도록 하는 옵션입니다.
1. # 모든 컨테이너 중지
2. docker stop $(docker ps -a -q)
3.
4. # 정지한 모든 컨테이너 삭제
5. docker rm $(docker ps -a -q)
컨테이너는 VM과 마찬가지로 가상 IP 주소를 할당받습니다.
기본적으로 도커는 컨테이너에 172.17.0.x의 IP를 순차적으로 할당받습니다.
우선 아래의 커맨드로 연습을 위한 컨테이너를 생성하고, ifconfig 커맨드를 통해서 컨테이너의 네트워크 인터페이스를 확인해보겠습니다.
1. docker run -i -t --name network_test ubuntu:20.04
2. # ifconfig 커맨드를 찾을 수 없다면 아래의 커맨드로 설치
3. apt update
4. apt install net-tools
5. ifconfig
도커의 NAT IP인 172.17.0.2를 할당받은 eth0 인터페이스와 로컬호스트인 lo 인터페이스를 확인할 수 있습니다. 아무런 설정을 하지 않았다면, 이 컨테이너는 외부에서 접근할 수 없고, 도커가 설치된 호스트에서만 접근을 할 수 있습니다.
(예외 : MAC에서는 호스트에서 컨테이너 IP로 접근이 불가능합니다.)
외부에서 접근을 허용하기 위해서는 eth0의 IP와 포트를 호스트의 IP와 포트에 바인딩해야합니다.
어떻게 설정을 해야하는지, 예제를 통해서 알아보도록 하겠습니다.
방금 만든 컨테이너에서 호스트로 빠져나온 뒤에 다음 커맨드로 새로운 컨테이너를 생성합니다. 우리는 이 컨테이너에서 아파치 웹 서버를 설치해서 외부에 노출해볼 겁니다.
docker run -i -t --name mywebserver -p 80:80 ubuntu:20.04
위 커맨드에서 -p 옵션을 추가했습니다. 이 옵션은 컨테이너의 포트를 호스트의 포트와 바인딩해 연결할 수 있게 설정하는 것입니다.
-p [호스트의 포트]:[컨테이너의 포트]
호스트의 특정 IP를 사용하려면, 다음과 같이 같이 바인딩할 IP와 포트를 명시합니다.
-p [IP]:[호스트의 포트]:[컨테이너의 포트]
또한, 여러 개의 포트를 외부에 개방하려면 -p 옵션을 여러 번 사용하여 설정할 수 있습니다.
다음으로 아파치 웹 서버 컨테이너를 생성합니다. 여기서 단순하게 localhost의 IP 주소를 사용하려고 따로 IP 설정은 하지 않았습니다.
(여기서 Port 443을 사용해야합니다. 그렇지 않으면 SSL_ERROR_RX_RECORD_TOO_LONG 에러가 발생합니다.)
docker run -i -t -p 443:80 ubuntu:20.04
그리고 컨테이너 생성하고 내부로 들어온 후, 아래의 커맨드로 아파치 웹 서버를 설치하고, 실행합니다.
1. apt update
2. apt install net-tools
3. apt install apache2 -y
4. service apache2 start
+) 아래와 같은 에러 발생시
vim을 설치(apt install vim)하고, 'vi /etc/apache2/apache2.conf' 커맨드로 ServerName localhost를 추가해주면 됩니다.
그리고 다시 아파치를 시작하면, 정상적으로 시작될 것입니다.
이제 호스트에서 아피치 웹 서버를 localhost:443으로 접근이 가능합니다. 만약 호스트 PC가 아닌 다른 PC라면 [호스트의 IP]:443 으로 접근이 가능합니다.
호스트의 IP와 포트를 컨테이너의 IP와 포트에 연결한다는 것이 중요하고, 위의 경우에는 아래와 같은 순서로 웹 서버에 접근하게 됩니다.
호스트 IP의 443번 포트로 접근 -> 443번 포트는 컨테이너의 80번 포트로 포워딩 -> 웹 서버(80번 > 포트)에 접근
만약 컨테이너를 생성할 때, '-p 443:81'로 설정했다면, 외부와 연결된 컨테이너의 포트는 81번이 될 것이고, 아파치 웹 서버는 80번 포트이기에 웹 서버에 접근이 불가능하게 됩니다. (81번 포트는 어떠한 서비스도 제공하도록 되어 있지 않기 때문)
요즘 대부분의 서비스는 단일 프로그램으로 동작하지 않고, 마이크로서비스로써 동작하는 것이 일반적입니다.이러한 서비스를 컨테이너화 할 때, 여러 개의 어플리케이션을 하나의 컨테이너에 설치할 수도 있지만, 한 컨테이너에 하나의 어플리케이션만 동작시키면 컨테이너 간의 독립성을 보장하고, 어플리케이션의 버전 관리, 소스코드 모듈화 등이 더욱 쉬워지게 됩니다.
이번에는 데이터베이스와 워드프레스 웹 서버 컨테이너를 연동하여 워드프레스 기반 블로그 서비스를 생성해보도록 하겠습니다.
먼저 다음의 커맨드를 통해서 각 컨테이너를 생성합니다. 처음 사용하는 -d, -e 옵션은 아래에서 따로 설명드리겠습니다.
1. # mysql container 생성
2. docker run -d --name wordpress-db -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpress mysql:5.7
3. # wordpress container 생성
4. docker run -d --name wordpress -e WORDPRESS_DB_HOST=wordpress-db -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress -e WORDPRESS_DB_NAME=wordpress --link wordpress-db:mysql -p 80 wordpress
첫 번째 커맨드는 mysql 이미지를 사용해서 데이터베이스 컨테이너를 생성하고, 두 번째 커맨드는 워드프레스 이미지를 이용해 워드프레스 웹 서버 컨테이너를 생성합니다.
워드프레스 웹 서버 컨테이너의 -p 옵션으로 80을 입력했기 때문에 호스트의 포트 중의 하나가 컨테이너의 80번 포트와 연결됩니다.
(따로 호스트의 포트를 지정하지 않으면, 랜덤하게 호스트의 포트를 결정하여 바인딩하게 됩니다. docker ps로 어떤 호스트 포트와 바인딩되었는지 확인이 가능합니다.)
호스트의 49153번 포트와 바인딩되었습니다. 바인딩된 포트만 확인하려면 docker port 커맨드를 사용하면 됩니다.
아래와 같이 나타나면, 정상적으로 어플리케이션 생성에 성공한 것입니다.
위 예제에서 새롭운 docker run의 옵션 -d 와 -e 를 사용했습니다.
docker run -d --name detach_test ubuntu:20.04
반대로 mysql 컨테이너를 -i -t 옵션으로 생성하게 되면, 포그라운드로 실행되는 mysql 프로그램이 실행되는 로그를 확인할 수 있습니다. 이 상태에서는 상호입출력이 불가능하고, 단순히 프로그램이 동작하는 것만 확인할 수 있습니다.
(MySQL 이미지는 컨테이너가 시작될 때, mysqld가 동작하도록 설정되어 있기 때문입니다.)
다만, bash shell을 아예 사용하지 못하는 것은 아니고, exec 커맨드를 사용하면 컨테이너 내부에서 shell을 사용할 수 있습니다.
docker exec -i -t wordpress-db /bin/bash
exec 커맨드를 사용하면, 컨테이너 내부에서 명령어를 실행할 수 있고, 그 결과값을 반환할 수 있습니다.
설정한 패스워드가 실제로 MySQL 내부에서 사용되는지 확인하려면, 다음과 같이 입력하고 패스워드를 입력해보면 알 수 있습니다.
mysql -u root -p
(컨테이너에서 빠져나오려면 Ctrl + P, Q나 exit를 입력하면 됩니다.)
--link 옵션은 현재 deprecated된 옵션이고, 삭제될 수도 있습니다. --link 대신 도커 네트워크를 생성하여 연결하는 방법이 있습니다. (네트워크는 다른 글에서 설명하도록 하겠습니다.)
1. # 네트워크 생성
2. docker network create wp-network
3. # mysql container 생성
4. docker run -d --name wordpress-db \
5. -e MYSQL_ROOT_PASSWORD=password \
6. -e MYSQL_DATABASE=wordpress \
7. -e MYSQL_USER=wordpress \
8. -e MYSQL_PASSWORD=wordpress \
9. --network wp-network mysql:5.7
10. # wordpress container 생성
11. docker run -d --name wordpress \
12. -e WORDPRESS_DB_HOST=wordpress-db \
13. -e WORDPRESS_DB_USER=wordpress \
14. -e WORDPRESS_DB_PASSWORD=wordpress \
15. -e WORDPRESS_DB_NAME=wordpress \
16. --network wp-network -p 80 wordpress