사실 작업환경 자체도 Ubuntu 20.04다.
다만, Docker에 이 컨테이너를 만드는 건 내 작업환경과는 별개의 테스트 환경을 구축하기 위함이다.
내 로컬에 테스트 환경을 구축하는 김에 소소한 기록을 남겨본다.
Docker를 설치하는 방법은 공식문서에 친절하게 설명되어 있다.
...라고 하지만 사실 나는 snap
명령어를 통해 설치했다.
$ snap install docker
스냅스토어의 배포 기록을 보면 알 수 있지만
snap에서 몇 년동안 Docker의 최신 버전을 지원하지 않아
한동안 이를 통한 Docker의 설치는 비권장이긴 했지만 이제는 다시 지원한다.
현재 설치되어 있는 버전을 확인해보면...
peter@hp-laptop:~$ docker --version
Docker version 19.03.12, build 48a66213fe
peter@hp-laptop:~$
현재 배포되어 있는 가장 최신 버전...
어라 뭔가 버전이 안맞는ㄷ......
아무튼 설치되었으면 다음을 넘어간다.
Docker를 사용할 땐 기본적으로 sudo
를 붙여 명령해야 한다.
그러나... sudo
를 엄한 데 잘못 붙였다가는 의도치 않은 문제를 직면할 수 있다.
따라서 현재 사용자에게 Docker에 대한 권한을 부여해야 한다.
$ usermod -aG docker $USER
echo $USER
를 해보면 알겠지만 $USER
는 현재 사용자를 나타내는 환경 변수다.
이 명령어는 $USER
의 secondary group(-G
)으로 docker
그룹을 추가(-a
)한다는 것이다.
여담: primary group과 secondary group
Linux 사용자는 여러 개의 그룹에 속해 있을 수 있으며
그 중 하나는 primary group이고 나머지는 secondary group이다.
primary group의 default값은 사용자 이름과 같은 이름의 그룹이며
ls -l
명령어를 사용했을 때
권한, 링크수, 소유자, 소유그룹, 크기, 생성시점, 파일 이름을 확인할 수 있는데
파일을 생성하면 그 파일의 소유그룹은 생성자의 primary group으로 설정된다.
그리고 그 파일의 그룹 권한은
해당 그룹을 primary 또는 secondary group으로 가진 이들에게 적용된다.
앞서 언급된usermod
명령어에서-G
대신-g
를 사용하면
해당 사용자의 primary group을 해당 그룹으로 변경할 수 있다.
그룹을 등록했으면 docker를 재시작한 시점부터 sudo
명령어를 생략할 수 있다.
$ service docker restart
간혹 docker
그룹을 찾지 못해 그룹을 추가할 수 없다는 오류가 날 수 있는데
Docker를 재설치하면 금방 해결된다고 한다.
왜 안되지? (재설치 후) 왜 되지?
자, Docker를 설치했으면 이제 우분투 이미지를 내려받아 컨테이너를 생성해보자.
이미지를 내려 받으려면 docker pull
명령어를 사용한다.
$ docker pull ubuntu:20.04
특정 버전을 내려받고자 한다면 위와 같이 버전명을 명시해주어야 하며
가장 최신 안정화 버전을 내려 받고자 하면 다음과 같이 할 수 있다.
$ docker pull ubuntu:latest
버전명을 붙이지 않으면 default는 latest
라고 한다.
사실 이렇게 따로 내려 받지 않아도 컨테이너를 생성할 때 로컬에 이미지가 있는지 확인하고
존재하지 않을 경우 Unable to find image 'ubuntu:20.04' locally
라며
알아서 내려 받으므로 생략해도 되는 과정이다.
컨테이너를 생성하는 방법은 여러 가지가 있다.
그냥 명령어로 직접 생성할 수도 있고, Dockerfile
을 사용할 수도 있고,
여러 개의 이미지를 함께 사용할 경우 docker-compose
를 이용하기도 한다.
하지만 우리는 단일 이미지이며 매번 새로 생성하는 게 아니라 만들어놓고 계속 쓸 예정이므로
그냥 명령어로 직접 생성하는 방법을 사용하겠다.
Dockerfile
을 사용하는 경우는 보통 --rm
옵션을 통해 컨테이너를 멈추면 자동 삭제되게 하고
사용할 때 매번 Dockerfile
을 새로 build하여 사용한다.
이 때, 내부 데이터는 사라지게 되므로 필요한 데이터는 외부에 저장해놓고 복사해서 사용한다.
물론 그 복사 코드도 Dockerfile
안에 작성된다.
컴파일 할 때 Makefile
을 사용해본 사람은 알겠지만 이런 방식은
처음 배울 땐 복잡하고 어려워보이지만 익숙해지면
미리 작성만 잘 해두면 매번 복잡한 명령을 할 필요 없이 쉽게 실행할 수 있다는 장점이 있다.
docker-compose
를 사용하는 경우는 여러 개의 이미지를 함께 사용하는 경우로
docker-compose.yml
파일에 전체적인 설정을 작성하고
개별 이미지를 관리하는 하위 디렉토리에 각각의 Dockerfile
및 설정 파일을 작성한다.
예를 들어, ELK stack
을 사용한다고 하면
docker-compose.yml
이 존재하는 디렉토리에 하위 디렉토리로
elasticsearch
, logstash
, kibana
의 디렉토리가 각각 존재하며
그 안에 그들의 Dockerflile
및 설정 파일이 존재하는 구조다.
그리고 docker-compose build
명령어를 통해 한 번에 build하고
docker-compose up
명령어를 통해 한 번에 실행할 수 있는데
일단 우리가 사용할 건 이 방식이 아니므로, 이건 언젠가 기회가 되면 다시 이야기하도록 하자.
Dockerfile
에 대한 자세한 설명은 여기에서,
docker-compose
에 대한 자세한 설명은 여기에서 확인할 수 있다.
공식 문서만큼 정확한 것은 없으니 영어라고 겁먹지 말도록 하자.
컨테이너를 생성할 땐 여러 가지 설정을 해줄 수 있다.
그것은 대체로 생성 후에도 수정할 수 있지만 때로는 정말 번거로운 과정이 되므로
무작정 검색 결과를 보며 따라하기 보다는 필요한 옵션이 무엇인지 판단하여 작성하도록 하자.
그 무작정 따라함 방지를 위해 먼저 몇 가지 옵션들을 소개한 뒤에 컨테이너를 생성하겠다.
모든 걸 다 설명할 수는 없겠지만 여기서 설명하는 모든 내용은
터미널에서 다음 명령어를 통해 영어로 확인할 수 있다.
$ docker run --help
-d
: daemon 모드 실행, 또는 detach 모드 실행이라고 한다. 컨테이너를 background로 실행하며 정상적으로 실행될 경우 컨테이너의 ID를 출력한다.-e
: environment의 줄임말로, -e KEY=value
와 같이 사용하며 컨테이너에 환경변수를 설정한다. 전달해야 할 환경변수가 여러 개라면 -e KEY0=value0 -e KEY1=value1
와 같이 반복해서 사용할 수 있다.-i
: interactive의 줄임말로, 표준 입력을 받기 위한 옵션이다.-p
: port의 줄임말로 특정 포트를 컨테이너에 포트 포워딩하기 위해 사용한다. -p containerPort:hostPort
와 같이 사용하며 -p containerPort:hostPort/protocol
의 형태로 TCP 같은 프로토콜을 지정해줄 수도 있다. 환경변수와 마찬가지로 이 녀석도 여러 개 설정해주고 싶다면 반복해서 사용할 수 있다.-t
: 가상 터미널 환경을 제공해주는 옵션이다.--name
: --name containerName
와 같이 사용하며 컨테이너의 이름을 지정한다.--rm
: 앞에서도 잠깐 이야기했지만 컨테이너가 종료되면 이를 자동으로 삭제한다.일단 이 정도만 알고 넘어가자.
docker run
에 대한 설명은 공식 문서에서도 확인할 수 있다.
docker run
실행자, 그러면 이제 본격적으로 컨테이너를 생성할 시간이다.
docker run
명령어의 기본적인 사용 방법은 다음과 같다.
$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
[]
안에 있는 내용은 선택적으로 사용하는 녀석들이며
대문자로 적혀 있는 부분은 적절한 텍스트로 대체해야 하는 부분이다.
OPTIONS
에는 우리가 조금 전에 살펴 보았던 여러 가지 옵션들이 올 수 있고
COMMAND
로는 이 컨테이너가 실행할 명령어를 전달할 수 있다.
ARG
는 글쎄, 사실 이걸 사용하는 경우는 보지 못했다.
우리는 다음 명령어를 통해 컨테이너를 생성할 것인데,
필요에 따라 위를 참고하여 적절한 옵션을 추가하도록 하자.
$ docker run -it --name test-ubuntu ubuntu:20.04 /bin/bash
여담으로, 검색을 통해 볼 수 있는 명령어 중에 줄 끝에 있는 \
는
아직 명령어가 안끝났으며 다음줄로 이어진다는 의미인데
개행을 하지 않고 한 줄에 모두 적을 경우 생략한다.
우리가 사용한 명령어는 짧아서 그것을 사용하지 않았지만, 말 그대로 여담.
아무튼 위 명령어를 실행하면 Digest
, Status
정보가 출력되고
root@asdfasdfasdf:/#
와 같은 프롬프트가 뜨는 것을 알 수 있다.
@
뒤에 오는 것은 컨테이너 ID다.
docker run
을 통해 실행한 컨테이너는 exit
을 통해 나가면 종료된다.
다음 명령어를 통해 실행 중인 컨테이너를 확인할 수 있는데 확인해보면 보이지 않을 것이다.
$ docker ps
여기에 -a
옵션을 주면 종료된 컨테이너까지 확인할 수 있는데 그럼 그제서야
Exited (0)
이라며 언제 종료되었는지까지 친절하게 알려준다.
아무튼 이제 컨테이너가 생성되었으므로 이 녀석을 시작하면 실행할 수 있게 된다.
컨테이너는 docker start
명령어로 시작하며 컨테이너 ID 또는 이름을 사용할 수 있다.
그런데 ID는 임의의 숫자와 문자로 구성되어 기억하기 어려우므로
생성할 때 --name
옵션으로 이름을 지정해주고 사용하는 편이 편하다.
우리가 앞서 만든 컨테이너를 시작하려면 다음과 같이 입력하면 된다.
$ docker start test-ubuntu
정상적으로 시작되었다면 방금 입력한 컨테이너 ID 또는 이름이 뜨는 것을 확인할 수 있다.
이제 다시 docker ps
를 해보면 이 녀석이 보일 것이다.
이렇게 시작한 컨테이너에 접근하기 위해서 우리는 이 컨테이너의 /bin/bash
를 실행할 것이다.
컨테이너의 특정 명령을 실행하기 위해서는 docker exec
을 사용한다.
$ docker exec -it test-ubuntu /bin/bash
이제 아까와 같이 root@asdfasdfasdf:/#
와 같은 프롬프트가 뜬다.
컨테이너 내부에서 작성한 것이 잘 저장되는지 확인하기 위해 테스트 파일을 하나 만들어보자.
$ touch testfile
testfile
이라는 이름의 0byte짜리 파일, 즉 빈 파일을 하나 만들었다.
이 상태로 exit
을 하면 다시 나갈 수 있는데
이번에 다시 docker ps
를 해보면 아직 컨테이너가 살아있다.
조금 전과 동일한 방법으로 도커 컨테이너의 bash를 열고 ls
를 해보면
우리가 만든 테스트 파일이 그대로 존재한다.
자, 이제 다시 나가서 컨테이너를 정지시켜 보자.
앞서 컨테이너를 시작할 때 사용한 명령어에서 start
만 stop
으로 바꾸면 된다.
$ docker stop test-ubuntu
이제 docker ps
를 통해서는 이 컨테이너를 확인할 수 없고
docker ps -a
를 통해 종료된 것을 확인할 수 있다.
이 상태에서는 docker exec
을 시도해보아도 실행되지 않는다.
다시 컨테이너를 시작해 bash로 접근해보면 아직 테스트 파일이 존재하는 것을 볼 수 있다.
즉, 컨테이너를 나가거나 정지해도 데이터는 보존된다는 것을 알 수 있다.
자, 우분투 컨테이너를 설치했으니 이제 한 번 사용해볼까?
라고 하다가 시작부터 당황하는 경우가 있다.
vi
도 nano
도 안되면 대체 뭘 사용할 수 있지?
apt install vim
을 해보아도 뭔가 오류가 나는데?
사실 이건 매우 단순한 문제다.
apt
패키지를 업데이트 하도록 하자.
root@asdfasdfasdf:/# apt update
이제 apt
를 통해 무언가 설치할 수 있을 것이다.
필요에 따라 apt
서버가 너무 느리다고 생각되면 국내 서버인
다음카카오 우분투 미러 사이트로 서버를 변경할 수 있다.
apt update
를 수행한 후라면 vi를 통해 다음과 같은 파일을 열어
$ vi /etc/apt/source.list
http://kr.archive.ubuntu.com/ubuntu/ 또는 http://archive.ubuntu.com/ubuntu/ 를
http://ftp.daumkakao.com/ubuntu/ 로 치환하면 된다.
kr.
이 붙는지는 경우에 따라 달라지더라.
vi를 통해 문자열을 한 번에 치환하고자 한다면 다음과 같이 할 수 있다.
:%s/kr.archive.ubuntu.com/ftp.daumkakao.com/g
물론 주소에 kr.
이 없다면 생략하고 입력하도록 하자.
apt update
를 하기 전이라면 sed
명령어를 통해 치환할 수 있다.
이 녀석은 기본적으로 치환한 결과를 보여주기만 하고 원본에 반영하지 않지만
-i
옵션을 주어 원본에 반영하도록 할 수 있다.
$ sed -i 's/kr.archive.ubuntu.com/ftp.daumkakao.com/g' /etc/apt/source.list
이렇게 서버를 변경하고 나서는 다시 apt update
를 해주어야 한다.
이제 나머지는 각자 필요한 패키지를 설치하여 작업하면 된다.