Docker

jsbak·2023년 5월 22일
0

Cloud

목록 보기
51/59
post-thumbnail

Docker 컨테이너

  • Docker는 컨테이너형 가상화 기술 중에 하나
    • 애플리케이션의 실행에 필요한 환경을 하나의 이미지(Base Image; OS와 유사한 가벼운 환경)로 모아두고, 그 이미지를 사용하여 다양한 환경에서 애플리케이션 실행 환경을 구축 및 운동하기 위한 오픈소스 플랫폼
  • 컨테이너란 호스트 OS상에 논리적인 구획(논리적인 VM/파티션)을 만들고, 애플리케이션을 작동시키기 위해 필요한 라이브러리나 애플리케이션 등을 하나로 모아, 마치 별도의 서버인 것처럼 사용할 수 있게 만든 것
    • 호스트 OS의 리소스를 논리적으로 분리시키고, 여러 개의 컨테이너가 공유하여 사용하며 컨테이너는 오버헤드가 적기 때문에 가볍고 고속으로 작동
      • OS가 없고 호스트의 OS 리소스를 공유하기 때문에 가볍고 오버헤드가 적음.
  • 컨테이너 수준의 가상화 / OS 수준의 가상화

Docker 의 주요 세 가지 기능

  • 이미지를 만드는 기능: docker image build
  • 이미지를 공유하는 기능: docker image push/pull
  • 컨테이너를 작동 시키는 기능: docker container run

Docker architecture

  • 도커 데몬( dockerd )
    • Docker API 요청을 수신하고 이미지, 컨테이너, 네트워크 및 볼륨과 같은 Docker 객체를 관리
    • 데몬은 다른 데몬과 통신하여 Docker 서비스를 관리
  • 도커 클라이언트 ( docker )
    • 많은 Docker user 가 Docker와 상호 작용하는 기본 방법
      • docker run 와 같은 명령을 사용하면 클라이언트는 이러한 명령을 dockerd 로 전송하여 수행합니다.
      • docker 명령은 Docker API를 사용합니다.
    • Docker 클라이언트는 둘 이상의 데몬과 통신 가능
  • 도커 레지스트리
    • Docker 레지스트리는 Docker 이미지를 저장합니다.
  • Docker 이미지
    • Docker 컨테이너를 구성하는 파일 시스템과 실행할 애플리케이션 설정을 하나로 합친 것으로, 컨테이너를 생성하는 템플릿 역할
  • Docker 컨테이너
    • Docker 이미지를 기반으로 생성, 파일 시스템과 애플리케이션이 구체화 되어 실행되는 상태(; Instance)

Docker의 역사

Docker의 기본 개념

  • 컨테이너형 가상화 기술(운영 체제 수준 가상화)
  • 컨테이너형 가상화 자체는 Docker 이전에도 LXC(Linux Containers)가 있었으며 Docker 초기에는 컨테이너형 가상화를 구현하는데 LXC를 런타임으로 사용

가상화 기술과 컨테이너 기술 비교

  • 컨테이너와 가상 머신의 결합 이미지
  • 가상 머신(VM)
    • 하나의 서버를 여러 서버로 전환하는 물리적 하드웨어의 추상화
    • 하이퍼바이저는 여러 VM이 단일 시스템에서 실행되도록 도움
    • 각 VM에는 수십 GB를 차지하는 운영 체제, 애플리케이션, 필수 바이너리 및 라이브러리의 전체 사본이 포함됩니다.
    • 부팅 속도 ⬇
  • 컨테이너
    • 코드와 종속성을 함께 패키징하는 앱 계층의 추상화
    • 여러 컨테이너가 동일한 시스템에서 실행
    • 각각 사용자 공간에서 격리된 프로세스로 실행되는 다른 컨테이너와 OS 커널을 공유
    • VM보다 적은 공간(일반적으로 수십 MB)을 차지
      • 더 많은 애플리케이션을 처리할 수 있고 더 적은 VM과 운영 체제를 필요로 한다.

컨테이너 라이프 사이클

도커 설치

yum install -y rdate
rdate -s time.bora.net
date

# EC2 에서 시간 동기화
timedatectl
timedatectl set-timezone Asia/Seoul

센토스 도커 설치

  • Docker CE; Community Edition 무료 오픈 소스
  • EE; Enterprise Edition 유료, 보안 관련 취약점 점검 서비스 제공
# curl -fsSL https://get.docker.com/ => 최신 버전의 bash shell script를 가져옴
# `sh`  가져온 스크립트를 실행
curl -fsSL https://get.docker.com/ | sh
# 도커 설치를 위한 도구(라이브러리) 설치
yum -y install bash-completion wget unzip mysql
curl https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/bash/docker -o /etc/bash_completion.d/docker.sh

# Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
systemctl enable --now docker
  • 해당 에러가 보인다면 도커 데몬 관련 확인!
    • 도커는 클라이언트와 서버(dockerd) 가 둘다 있어야한다.

우분투 도커 설치

$ sudo apt update
$ sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 우분투 18.04 저장소
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
# 우분투 16.04 저장소
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable"
# 설정한 저장소 업데이트
$ sudo apt update
$ sudo apt-cache policy docker-ce
$ sudo apt install docker-ce -y
$ sudo usermod -a -G docker <USER_NAME>
$ docker version

AWS EC2 도커 설치

amazon-linux-extras install docker -y
systemctl start docker && systemctl enable docker
curl https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/bash/docker -o /etc/bash_completion.d/docker.sh
usermod -a -G docker ec2-user

도커 기본 명령어

이미지

  • 컨테이너를 생성할 때 필요한 요소 로 가성 머신에서의 iso 파일과 비슷한 개념이다.
  • 여러 개의 계층으로 된 바이너리 파일로 존재하고, 컨테이너를 생성하고 실행할 때 읽기 전용으로 사용된다.
  • <저장소 이름>/<이미지 이름>[:태그] 로 구성되어 있다.

이미지 검색

# "nginx" 라는 베이스 이미지
$ docker search nginx
$ docker search ubuntu 16.04

Name, shorthandDefaultDescription
--filter,-f제공된 조건에 따라 출력 필터링
--formatGo 템플릿 형식의 검색
--limit최대 검색 결과 수
--no-trunc결과를 모두 표시
  • --filter 사용
    • The --filter (or -f) flag format is a key=value pair. If there is more than one filter, then pass multiple flags (e.g. --filter "foo=bar" --filter "bif=baz")

이미지 다운로드

# latest 버전의 "nginx" 이미지 다운로드
$ docker image pull nginx
Name, shorthandDefaultDescription
--all-tags , -a저장소에 태그가 지정된 이미지를 모두 다운로드
--disable-content-trusttrue이미지 확인 건너뛰기
--platformSet platform if server is multi-platform capable
--quiet , -q출력 그만보기

이미지 목록 조회

$ docker image ls
Name, shorthandDefaultDescription
--no-trunc결과를 모두 표시
--quiet, -q이미지 ID만 표시

이미지 세부 정보 조회

# "OS" 라는 키의 값을 표시
$ docker image inspect --format="{{ .Os}}" nginx
$ docker image inspect --format="{{ .RepoTags}}" nginx
Name, shorthandDefaultDescription
--format , -f사용자 지정 템플릿을 사용하여 출력 형식 지정

컨테이너

생성

  • 컨테이너 생성 - docker container create, docker create
    docker container create [OPTIONS] IMAGE [COMMAND] [ARG...]
    • Container networking; -p; --publish
      • -p 8080:80 : 컨테이너의 TCP 포트 80을 Docker 호스트의 포트 8080에 매핑
      • -p, --publish => -p <Host_Port>:<Container_Port>
      • Host OS 의 포트 번호를 개방하지 않아도 도커 컨테이너를 생성한 뒤 시작하면 도커에서 포트를 대신 개방시켜줌.
        • iptables --list : iptables 가 firewalld 보다 우선 순위가 높기 때문에 도커의 포트 설정이 적용된다. ; docker0 라는 가상 스위치를 통해 통신

# "nginx" 이미지를 가지고 "webserver" 라는 컨테이너 이름으로 생성
# `-p, --publish`, <Host_Port>:<Container_Port>
$ docker container create -p 80:80 --name webserver nginx
  • -p [호스트의 포트]:[컨테이너의 포트]
    • 컨테이너 포트를 호스트 포트에 바인딩해 연결할 수 있도록 설정
    • 호스트 IP의 80번 포트로 접근 ➡️ 80번 포트는 컨테이너의 80번 포트로 포워딩 ➡️ 웹 서버 접근
    • 호스트의 IP와 포트를 컨테이너의 IP와 포트로 연결한다는 개념은 매우 중요합니다. 아파치 웹 서버는 172 대역을 가진 컨테이너의 NAT IP와 80번 포트로 서비스하므로 여기에 접근하려면 172.17.0.x:80의 주소로 접근해야합니다. 그러나 도커의 포트 포워딩 옵션인 -p 를 써서 호스트와 컨테이너를 ㄹ연결 했으므로 호스트의 IP와 포트를 통해 172.17.0.x:80으로 접근할 수 있습니다.

시작

# "webserver" 라는 이름의 컨테이너 실행
$ docker container start webserver

정지

# "webserver" 라는 이름의 컨테이너 정지
$ docker container stop webserver

제거

# `-f` 강제로 "webserver" 컨테이너를 지움
$ docker container rm -f webserver

# 중지된 모든 컨테이너 제거, prune 앞에 는 생략 불가능 (container / image, ... 구분 불가)
# `-f` 옵션 : 확인하지 않고 지움
$ docker container prune

# "exited" 상태인 컨테이너 강제 제거
docker rm -f $(docker ps --filter "status=exited" -q)
Name, shorthandDefaultDescription
--force , -f실행 중인 컨테이너 강제 제거(SIGKILL 사용)

생성 및 시작

# foreground 실행
$ docker container run -p 80:80 --name webserver nginx

# background 실행
$ docker container run -d -p 80:80 --name webserver nginx 

# 벡그라운드 & 랜덤 포트
$ docker container run -d -P nginx

# 임시 컨테이너를 이용하여 캘린더를 출력하고 컨테이너는 종료
$ docker container run --name test_cal centos /bin/cal

# 컨테이너에 접속
$ docker container run -it --name test_bash centos /bin/bash
# 접속 후 나오면 삭제
$ docker container run --rm -it --name test_bash7-1 centos:7 /bin/bash
Name, shorthandDefaultDescription
--detach , -d백그라운드에서 컨테이너 실행 및 컨테이너 ID 출력
--interactive , -i표준 입력을 연다.
-tty, -t터미널을 사용한다. (-t 옵션을 사용하여 의사 터미널 할당 TTY는 Teletype을 나타내며 기본 입출력을 제공하는 장치)
-publish-all , -PPublish all exposed ports to random ports, 랜덤한 포트
--volume , -vBind mount a volume
  • run을 활용한 다른 명령어 실습
# "mysql" 이미지 를 이용한 임시 컨테이너로 mysql 서버(host 가 192.168.0.102 인) 접속
# password : wppass
$ docker container run --rm -it --name test_mysql mysql /bin/mysql -h 192.168.0.102 -u wpuser -p

# 백그라운드로 ping 수행
$ docker container run -d --name test_ping centos /bin/ping localhost
# "test_ping" 컨테이너의 로그를 확인
docker container logs -t test_ping

# "test_port" 컨테이너의 상태 확인
$ docker container run -d -p 8080:80 --name test_port nginx
$ docker container stats test_port

# 컨테이너 리소스 지정, 옵션과 인수 사이에 `=`이 있어도 없어도 된다.
$ docker container run -d -p 8181:80 --cpus 1 --memory=256m --name test_resource nginx

디렉토리 공유

  • --volume, -v 옵션 - bind mounts , Volumes - 도커가 관리가능한 볼륨영역 할당
    • 영구 볼륨
    • Docker 컨테이너에서 생성되고 사용되는 데이터를 유지하기 위해 선호되는 메커니즘
    • -v 옵션 자체는 바인드 마운트를 의미
    $ docker container run -d -p 8282:80 --cpus 1 --memory=256m -v /tmp:/usr/share/nginx/html --name volume-container nginx 
    • -v /home/wordpress_db:/var/lib/mysql 은 호스트의 /home/wordpress_db 디렉터리와 컨테이너의 /var/lib/mysql 디렉터리를 공유한다는 뜻
      • [호스트의 공유 디렉터리]:[컨테이너의 공유 디렉터리]
    • 디렉터리 단위의 공유뿐 아니라 단일 파일 단위의 공유도 가능하며, 동시에 여러개의 -v 옵션을 쓸 수 도 있다.
$ docker run -it --name file_volume \
-v /home/hello:/hello \
-v /home/hello2:/hello2 \
ubuntu:20.04

리스트 조회

$ docker [container] ls
$ docker ps

# 실행중인 모두 조회
docker container ls -a
# 컨테이너 name 이 test_webserver 인 컨테이너 조회
docker container ls -a -f name=test_webserver
# 컨테이너 상태가 exited=0 인것 조회
docker container ls -a -f exited=0
# 형태를 지정해서 출력
docker container ls -a --format "table {{.Names}}\t{{.Status}}"
Name, shorthandDefaultDescription
--all , -aShow all containers (default shows just running)
--filter , -fFilter output based on conditions provided
--format해당하는 테이블 열을 지정한 순서에 따라 보여줌

컨테이너 연결

  • 동작중인 컨테이너 연결 - docker cotainer attach
  • 실행 중인 컨테이너에 로컬 표준 입력, 출력 및 오류 스트림 연결
  • 컨테이너를 종료 시키지 않고 연결 분리[root@1689b6318c35 /]# ctrl + p, ctrl +q 를 입력
    • 컨테이너가 -i-t 로 실행된 경우 컨테이너에서 분리하고 CTRL-p CTRL-q 키 시퀀스를 사용하여 실행 상태로 둘 수 있습니다.
$ docker cotainer attach test_bash

프로세스 실행

  • 동작중인 컨테이너에서 프로세스 실행 - docker container exec
  • docker exec 사용하여 시작된 명령은 컨테이너의 기본 프로세스( PID 1)가 실행되는 동안에만 실행되며 컨테이너를 다시 시작해도 다시 시작되지 않습니다.
  • COMMAND 는 컨테이너의 기본 디렉터리에서 실행, 기본 이미지의 Dockerfile 에 WORKDIR 지시문으로 지정된 사용자 지정 디렉터리가 있는 경우 사용자 지정 디렉터리를 사용
  • /bin/bash 같은 것으로 접속시 exit 를 수행후 컨테이너를 탈출하더라도 컨테이너가 죽지 않는다. + bash 라고 인수를 주어도 가능하다.
# 이미 실행중인 "test_port" 컨테이너에서 echo 명령을 이용해 `Hello world` 를 출력
$ docker container exec -it test_port /bin/echo "Hello world"

# 이미 실행중인 "test_port" 컨테이너에서 bash shell을 이용
$ docker container exec -it test_port /bin/bash
$ docker container exec -it test_port bash

프로세스 확인

  • 동작중인 컨테이너 프로세스 확인
    docker top CONTAINER [ps OPTIONS]
$ docker container top test_port

포트 전송 확인

  • 동작중인 컨테이너의 포트 전송 확인
    docker port CONTAINER [PRIVATE_PORT[/PROTO]]
$ docker container port test_port

이름 변경

  • 컨테이너 이름 변경
    docker rename CONTAINER NEW_NAME
# "test_port" 컨테이너 이름을 "webserver" 로 변경
$ docker container rename test_port webserver

파일 복사

# 컨테이너의 파일을 로컬로 복사
$ docker container cp webserver:/usr/share/nginx/html/index.html /root/index.html

# 로컬의 파일을 컨테이너로 복사
$ docker container cp ./index.html webserver:/usr/share/nginx/html/index.html

변경 사항 검사

$ docker container diff webserver
SymbolDescription
A파일 또는 디렉토리가 추가됨
D파일 또는 디렉터리가 삭제됨
C파일 또는 디렉토리가 변경됨

이미지 만들기

  • 커밋 작업에는 컨테이너 내부에 마운트된 볼륨에 포함된 데이터가 포함되지 않습니다.
  • 운영중인 컨테이너의 변경 사항에서 새 이미지 만들기
    • 컨테이너의 파일 변경 사항이나 설정을 새 이미지로 커밋하는 것이 유용
    • 태그를 지정하지 않으면 기본적으로 latest 사용
    • 디스크 공간을 적게 차지 하려고 기존 이미지를 활용하는데 변경된 부분을 가지고 새로운 다이제스트 메시지를 만들어서 이미지 ID로 사용
# 로컬에 있는 `/tmp`의 아래 리소스를 통째로 복사해서 컨테이너로 옮김
$ docker run -d -p 80:80 --name=test_commit_web nginx
$ docker container cp /tmp/index.html test_commit_web:/usr/share/nginx/html
$ docker container cp /tmp/images test_commit_web:/usr/share/nginx/html

# "volume-container" 컨테이너를 `test_commit` 라는 리포지토리에 
$ docker container commit -a "jsb<test@example.com>" -m "Congratulation v1.0" volume-container test_commit:v1.0

$ docker image ls
$ docker image inspect test_commit:v1.0
$ docker inspect test_commit:v1.0 --format "{{ .Author}}"
$ docker image inspect test_commit:v1.0 --format "{{ .Comment}}"

# 커밋으로 생성한 이미지를 이용하여 "webserver" 생성
$ docker container run -d -p 80:80 --name webserver test_commit:v1.0
# 만약 "webserver" 컨테이너가 있다면 `-f` 는 실행 중인것도 제거
$ docker rm -f webserver
Name, shorthandDefaultDescription
--author,-a저자(예: jsb test@example.com)
--change,-c생성된 이미지에 Dockerfile 명령어 적용
--message,-m커밋 메시지
--pause,-ptrue커밋 중 컨테이너 일시 중지

이미지 저장

$ docker save -o test_commit.tar test_commit:v1.0
Name, shorthandDefaultDescription
--output , -oSTDOUT 대신 파일에 쓰기

이미지 불러오기

# 테스트를 위해 리소스가 있다면 제거
$ docker image ls
$ docker rm -f webserver
$ docker rmi -f test_commit:v1.0
$ docker images

# 이미지 로드
$ docker image load -i test_commit.tar
# 이미지 로드 결과 확인
$ docker image ls
Name, shorthandDefaultDescription
--input,-iSTDIN 대신 tar 아카이브 파일에서 읽기
--quiet,-q부하 출력 억제

이미지 삭제

$ docker images
$ docker rmi -f centos:latest
$ docker image ls

Docker 네트워크

네트워크 구조

  • Network containers
  • 도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해 컨테이너마다 가상 네트워크 인터페이스를 호스트에 생성하며 이 인터페이스의 이름은 veth로 시작합니다.
    사용자가 직접 생성할 필요는 없으며 컨테이너가 생성될 때 도커 엔진이 자동으로 생성합니다.

  • eth0 는 공인 IP 또는 내부 IP가 할당되어 실제로 외부와 통신할 수 있는 호스트 네트워크 인터페이스
  • docker0 브리지는 각 veth 인터페이스와 바인딩되어 호스트의 eth0 인터페이스와 이어주는 역할을 수행
  • docker0 는 dhcp 겸 router 의 기능을 수행
  • 아래 그림은 host의 17번과 도커의 16번이 연결되어있다.

네트워크 리스트

$ docker network ls

# 도커의 네트워크 정보 확인 가능, "bridge"의 이름인 "docker0" , 컨테이너 네트워크 정보 등
$ docker network inspect bridge
$ docker network ls -q --filter driver=bridgedocker network ls -q --filter driver=bridge

네트워크 생성 및 삭제

$ docker network create -d bridge --subnet 10.59.0.0/16 --ip-range 10.59.0.0/24 test_bridge

$ docker network ls
$ docker [network] inspect test_bridge

# 도커 네트워크 삭제
$ docker network rm test_bridge

네트워크 연결

# "webserver" 컨테이너를 "test_bridge" 네트워크에 연결
$ docker network connect test_bridge webserver

# 네트워크에서 컨테이너 연결 해제
$ docker network disconnect test_bridge webserver

$ docker inspect webserver

# 컨테이너 생성시 처음부터 컨테이너를 네트워크에 연결 
$ docker container run -d -p 8080:80 --name webserver1 --network test_bridge test_commit:v1.0
$ docker inspect test_bridge

도커 컨테이너로 워드프레스 서버와 DB 서버 구축

--- 워드프레스
- dbserver
$ docker run -d -p 3306:3306 --name dbserver \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
-e MYSQL_ROOT_PASSWORD=password --network test_bridge mariadb

- webserver
$ docker run -itd -p 8888:80 --name apache --network test_bridge centos:7
$ docker exec -it apache bash
yum install -y httpd php php-mysql php-gd php-mbstring wget unzip
wget https://ko.wordpress.org/wordpress-4.8.2-ko_KR.zip
cd /var/www/html
unzip /wordpress-4.8.2-ko_KR.zip
mv wordpress/* ./
chown -R apache:apache /var/www
httpd &
Enter 입력

Dockerfile

앞서 다룬 Docker 이미지 생성 방식은 베이스 이미지를 바탕으로 일일이 컨테이너 생성 / 파라미터 설정 / 미들웨어 설치를 통해 만들어진 컨테이너를 Docker 이미지로 생성하였으나 Docker 파일은 컨테이너를 생성하는 여러 구성 정보를 하나의 파일로 정리하고 일괄 실행하여 docker build 명령을 통해 Docker 이미지를 작성하는 스크립트

Dockerfile을 구성하는 명령어

도커 파일 이해

  • Dockerfile

    \# vi Dockerfile
    ----------------------------------------
    FROM ubuntu:18.04	# 베이스 이미지
    # MAINTAINER goorm	# 작성자; deprecated
    LABEL "name"="webserver"	# 정보 전달의 목적
    ENV aloha=date
    ENV path=/var/www/html
    
        # RUN : 명령어 실행
        # 미 동부지역 저장소 대신 대한민국의 kakao의 저장소로 변경
    RUN sed -i 's/archive.ubuntu.com/ftp.daumkakao.com/g' /etc/apt/sources.list	
    RUN apt-get update
    RUN apt-get install apache2 -y
    COPY nihao /var/www/html/nihao
    
        # 위에 "ENV path" 로 지정한 환경 변수 값을 대입 `/var/www/html`
    COPY hello.html $path
    ADD aws.tar /var/www/html
    WORKDIR /var/www/html	# cd /var/www/html
    RUN echo ohayo >> ohayo.html
    VOLUME /var/www/html
    EXPOSE 80
    
        # ENTRYPOINT ["apachectl", "-D", "FOREGROUND"]
        # CMD ["apachectl", "-D", "FOREGROUND"]
    ENTRYPOINT ["apachectl"] # ["apachectl", "-D", "FOREGROUND"]
    CMD ["-D", "FOREGROUND"] # ["apachectl", "-D", "FOREGROUND"]
    • ADD
      • ADD 명령은 <src> 에서 새 파일, 디렉터리 또는 원격 파일 URL을 복사하여 <dest> 경로에 있는 이미지의 파일 시스템에 추가
      • 디렉토리 자체는 복사되지 않고 내용만 복사됩니다.
        • <src> 가 인식된 압축 형식(identity, gzip, bzip2 또는 xz)의 로컬 tar 아카이브인 경우 디렉토리로 압축이 풀림.
        • 원격 URL의 리소스는 압축 해제되지 않고 디렉토리를 복사하거나 압축을 풀면 tar -x 와 동일한 동작
    • VOLUME - 볼륨 지정에 대한 참고 사항
      • 여기서 볼륨은 도커가 관리하는 볼륨으로 처리된다.

    • COPY
      • COPY 명령은 <src> 에서 새 파일 또는 디렉토리를 복사하여 경로<dest> 에 있는 컨테이너의 파일 시스템에 추가
    • EXPOSE : 컨테이너가 런타임 시 지정된 네트워크 포트에서 수신 대기하는 것을 도커로 알리는 것 이며, 런타임시 도커가 컨테이너가 수신하려는 포트를 "iptables" 을 조작해 활성화(게시)한다.
      • docker run-P 옵션은 컨테이너 내부의 포트에 접근하지 않으며, 도커 컨테이너에 활성화된 포트가 없다면 -P 옵션은 의미가 없다.
        • 포트포워딩할 포트가 없는 상황

Dockerfile 빌드

$ mkdir test-dockerfile && cd $_
$ mkdir nihao
$ echo "<h1>nihao</h1>" > nihao/index.html
$ echo "<h1>hello</h1>" > hello.html
# aws.tar 파일 업로드
$ vi Dockerfile # 도커 파일 이해의 스크립트 주석 부분은 제거해서 입력
$ docker build -t hello:v1.0 .


# `.` 현재 위치에 있는 Dockerfile을 찾아 빌드를 수행
#  `-t` 태그 옵션으로 이름 지정
$ docker build -t hello:v1.0 .
  • 컨테이너 생성
$ docker run -d -P hello:v1.0
  • 다른 파일로 도커 빌드 후 컨테이너 생성
# Dockerfile 말고 다른 이름의 파일로 만들 경우
# `-f` 옵션으로 파일 위치를 알려준다.
$ docker build -t hello:v1.1 -f test-docker .

# 도커 컨테이너 생성
# `8080` 포트가 활성화 되는 apache2는 `80` 포트 를 수신하므로 아파치릐 포트를 변경한다.
$ docker run -d -P hello:v1.1
	# 수정하기 위해 컨테이너 접속
$ docker exec -it 생성된_컨테이너_이름/ID bash
root@f4f951e26387:/\# apt install -y vim
root@f4f951e26387:/\# vim /etc/apache2/ports.conf
	Listen = `80` => `8080`
root@f4f951e26387:/\# exit
$ docker restart 생성된_컨테이너_이름/ID 
$ docker ps # 포트 확인

도커 파일 생성 실습

도커 파일 생성 1

  • "nginx" 베이스 이미지를 이용

  • Dockerfile-debian.template

    # `images` 폴더랑 `index.html` 이 있는 곳에서
    $ tar cvf test.tar images index.html 
    $ mkdir test && cd $_
    
    $ vi Dockerfile
    ----------------------------------------
    FROM nginx:latest
    # `/usr/share/nginx/html` 위치에 `test.tar` 파일을 업로드 하겠다.
    ADD test.tar /usr/share/nginx/html
    # daemon off: 백그라운드 실행을 하지 않겠다.
    # 이미지 만들때는 포그라운드 실행을 해야한다.
    # 이미지 생성시 백그라운드를 실행하겠다 하면 나중에 도커 컨테이너를 백그라운 실행할때 충돌한다.
        ##  nginx 베이스 이미지에 이미 존재
    #CMD ["nginx", "-g", "daemon off;"]
    ----------------------------------------
    
    $ docker build -t test_build:v2.0 .
    $ docker run -d -P test_build:v2.0
    
    # 내가 `EXPOSE` 를 넣지 않았는데 포트가 있다?
    # `Base Image` 인 "nginx" 이미지에 있다.
    $ docker ps
    • test_build:v2.0 이미지로 컨테이너 확인 (-P; 랜덤 포트)
    • "nginx" 베이스 이미지와 "test_build:v2.0" 이미지의 포트확인 - 베이스 이미지에 EXPOSE 가 있어 "test_build" 이미지도 영향을 받음.
    • "nginx" 베이스 이미지 의 CMD ["nginx", "-g", "daemon off;"] 체크

도커 파일 생성 2

  • 베이스 이미지에 있는 것 제외
	# color: green -> blue, Congratulations v2.0 - > Congratulationsv3.0
$ vi index.html
$ vi Dockerfile
----------------------------------------
FROM nginx:latest
ADD test1.tar /usr/share/nginx/html
----------------------------------------
$ tar cvf test1.tar index.html images
$ tar tvf test1.tar

$ docker build -t test_build:v1.0 .
$ docker run -d -P test_build:v1.0

도커 허브

  • 도커 허브 회원가입

허브 업로드를 위한 태깅

  • 허브로 업로드할 이미지 조회

  • SOURCE_IMAGE를 참조하는 TARGET_IMAGE 태그 작성 - docker image tag

    • SOURCE_IMAGE참조하는 TARGET_IMAGE 태그를 만듭니다.
      • docker push hkjs96/test_build:v1.0 를 수행하게 되면 TARGET_IMAGE => hkjs96/test_build hkjs96 라는 계정 공간에 업로드

    docker [image] tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

 # docker tag <로컬이미지이름> <도커허브계정>/<이미지이름>
 $ docker tag test_build:v1.0 hkjs96/test_build:v1.0
 $ docker images

로그인

# docker login -u username -p password hub.docker.com
$ docker login

# 로그 아웃
$ docker logout

# 로그 아웃 후 / 인증 정보 제거
$ cd ~/.docker
$ rm -rf config.json

이미지 업로드

  • 도커 허브로 이미지 업로드
    docker image push <Docker Hub 사용자명>/이미지[:태그]
$ docker push hkjs96/test_build:v1.0

# 버전 2도 업로드 작업
$ docker tag test_build:v2.0 hkjs96/test_build:v2.0
$ docker push hkjs96/test_build:v2.0



업로드한 파일 테스트

  • --restart always : 도커 가 껏다 켜져도 알아서 컨테이너 재시작
$ docker run -d -P --restart always hkjs96/test_build:v1.0

# port 49153
$ docker ps
$ sudo reboot
$ docker ps

# port 49154
$ docker run -d -P --restart always hkjs96/test_build:v2.0
$ docker ps



처음부터 태깅 작업

  • 도커 빌드 시점에 저장소를 지정
$ vi index.html -> 색상 pink, 버전 v4.0 번경
$ docker build -t hkjs96/test_build:v3.0 .
$ docker push hkjs96/test_build:v3.0

ubuntu@ubuntu:~$ docker run -d -P hkjs96/test_build:v3.0

ubuntu@ubuntu:~$ docker ps
--------------------------------------------------------------------------------
CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS          PORTS                                     NAMES
858b125f3e77   hkjs96/test_build:v3.0   "/docker-entrypoint.…"   14 seconds ago   Up 13 seconds   0.0.0.0:49155->80/tcp, :::49155->80/tcp   reverent_fermat
49a7fed67a7a   hkjs96/test_build:v2.0   "/docker-entrypoint.…"   10 minutes ago   Up 10 minutes   0.0.0.0:49154->80/tcp, :::49154->80/tcp   nice_golick
3801f802ce2f   hkjs96/test_build:v1.0   "/docker-entrypoint.…"   14 minutes ago   Up 12 minutes   0.0.0.0:49153->80/tcp, :::49153->80/tcp   blissful_lichterman

Wordpress dockerfile

$ cd ~
$ mkdir wordpress && cd $_
$ vi Dockerfile
----------------------------------------
FROM centos:7
MAINTAINER goorm
RUN yum install -y httpd php php-mysql php-gd php-mbstring wget unzip
RUN wget https://ko.wordpress.org/wordpress-4.8.2-ko_KR.zip
WORKDIR /var/www/html
RUN unzip /wordpress-4.8.2-ko_KR.zip
RUN mv wordpress/* .
RUN chown -R apache:apache /var/www
CMD httpd -DFOREGROUND
EXPOSE 80
----------------------------------------

$ docker build -t hkjs96/my-wp:v1.0 .
$ docker push hkjs96/my-wp:v1.0

#$ docker run -d -P --name my-wp --network test_bridge hkjs96/my-wp:v1.0

ubuntu@ubuntu:~$ docker create network test_network
ubuntu@ubuntu:~$ docker run -d -p 3306:3306 --name dbserver \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
-e MYSQL_ROOT_PASSWORD=password --network test_network mariadb
ubuntu@ubuntu:~$ docker run -d -p 80:80 --name wordpress --network test_network hkjs96/my-wp:v1.0

데이터 관리

  • Bind Mount: 공유를 확인
$ mkdir volume && cd $_
$ mkdir bm01 ; touch bm01/test.txt
$ docker run -itd --name bm-test -v /root/volume/bm01:/mnt centos:7
$ docker exec bm-test ls /mnt
  • Volume
$ docker volume create my-vol01
$ docker volume ls
$ docker volume inspect my-vol01
	"Mountpoint": "/var/lib/docker/volumes/my-vol01/_data"
$ docker run -itd --name vol-test -v my-vol01:/mnt centos:7
	# ro -> 읽기 권한만 주겠다.
$ docker run -itd -p 801:80 --name vol-web -v my-vol01:/usr/local/apache2/htdocs:ro httpd:latest
$ curl 192.168.1.143:801
	<html><body><h1>It works!</h1></body></html>
    # `sh -C` 쉘 커맨드
$ docker container exec vol-test sh -c "echo "Nihao" > /mnt/index.html"
$ curl 192.168.0.151:801
	Nihao

onbuild 명령어 활용

  • ONBUILD <INSTRUCTION>

  • ONBUILD 명령은 이미지가 다른 빌드의 기반으로 사용될 때 나중에 실행할 트리거 명령을 이미지에 추가합니다. 트리거는 다운스트림 Dockerfile의 FROM 명령 바로 뒤에 삽입된 것처럼 다운스트림 빌드의 컨텍스트에서 실행됩니다.

    • 다른 이미지를 빌드하기 위한 기반으로 사용할 이미지를 빌드하는 경우에 유용합니다
      • 예: 애플리케이션 빌드 환경 또는 사용자별 구성으로 사용자 정의할 수 있는 데몬.
  • 운영자 역할

$ mkdir onbuild && cd $_
$ vi Dockerfile.base
----------------------------------------
FROM ubuntu:18.04
RUN sed -i 's/archive.ubuntu.com/ftp.daumkakao.com/g' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y install nginx
EXPOSE 80
ONBUILD ADD website*.tar /var/www/html/
CMD ["nginx", "-g", "daemon off;"]
----------------------------------------

$ docker build -t hkjs96/web-base:v1.0 -f Dockerfile.base .
$ docker inspect hkjs96/web-base:v1.0
	"OnBuild": [
        "ADD website*.tar /var/www/html/"
    ],
$ docker login
$ docker push hkjs96/web-base:v1.0
$ vi Dockerfile
----------------------------------------
FROM  hkjs96/web-base:v1.0
----------------------------------------
  • 개발자 역할
$ docker build -t hkjs96/web-site:v1.0 .
$ docker run -d -p 80:80 --name=web-site hkjs96/web-site:v1.0
$ docker login
$ docker push hkjs96/web-site:v1.0

--- 운영자 역할(AWS)
$ docker run -d -p 80:80 --name=test-site hkjs96/web-site:v1.0

도커 사설 레지스트리

$ docker run -d -p 5000:5000 --restart=always --name private-docker-registry registry # 저장소 서버

	# 각 쿠버네티스 노드에서 수행
	# 클라이언트 세팅
$ vi /etc/docker/daemon.json # 클라이언트
{ "insecure-registries":["도커의 IP":5000"] }
$ systemctl restart docker

  • 이미지 올리기
$ docker tag nginx:latest 192.168.1.143:5000/nginx
$ docker push 192.168.1.143:5000/nginx
  • 리포지토리 조회
$ curl -X GET http://192.168.1.143:5000/v2/_catalog

$ curl -X GET http://192.168.1.143:5000/v2/nginx/tags/list
$ curl -X GET http://192.168.1.143:5000/v2/web-site/tags/list

도커 컴포즈

  • Docker Compose 개요
  • 다중 컨테이너 Docker 애플리케이션을 정의하고 실행하기 위한 도구
  • YAML 파일을 사용
  • 단일 명령으로 구성에서 모든 서비스를 만들고 시작
  • 기본적인 3단계 프로세스
    • Dockerfile 어디서나 재현할 수 있도록 앱의 환경을 정의
    • 앱을 구성하는 서비스를 정의하기 위해 docker-compose.yml 형식의 스크립트로 격리된 환경에서 함께 실행
    • 실행: docker compose up 하면 이 전체 앱을 시작하고 실행
$ curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose
$ mkdir my_wordpress && cd $_
$ vi docker-compose.yml
----------------------------------------
version: "3.3"    
services:
  dbserver:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment: # -e
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass
  wordpress:
    depends_on:
      - dbserver
    image: wordpress:latest
    volumes:
      - wordpress_data:/var/www/html
    ports: # -p 80:80
      - "8888:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: dbserver:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: wppass
      WORDPRESS_DB_NAME: wordpress
volumes:
  db_data: {}
  wordpress_data: {}
----------------------------------------

$ docker-compose up -d # 분리 모드로 실행
$ docker-compose ps
$ docker-compose pause # 일시 정지
$ docker-compose unpause
$ docker-compose port wordpress 80 # "wordpress" 컨테이너의 80번 포트에 바인딩된 포트 출력
$ docker-compose config # "config" yaml 파일 출력
$ docker-compose stop wordpress
$ docker-compose rm wordpress
$ docker-compose down # Stop and remove containers, networks
$ docker-compose down -v # 볼륨 제거
$ docker-compose down --rmi all # 이미지 제거

도커 컨테이너 모니터링

 # use the latest release version from https://github.com/google/cadvisor/releases
$ VERSION=v0.44.0 # 변수 선언
docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  --privileged \
  --device=/dev/kmsg \
  gcr.io/cadvisor/cadvisor:$VERSION

도커 스웜(Cluster)

  • Swarm mode overview
  • Docker 엔진 클러스터를 기본적으로 관리하기 위한 Swarm 모드
  • Docker CLI를 사용하여 스웜을 만들고, 스웜에 애플리케이션 서비스를 배포하고, 스웜 동작을 관리
  • 주요 기능
    • Docker 엔진과 통합된 클러스터 관리
    • 분산된 디자인
      • Docker 엔진은 배포 시 노드 역할 간의 차별화를 처리하는 대신 런타임 시 특수화를 처리
      • 두가지 형태의 노드; 관리자와 작업자
    • 원하는 상태 조정
      • Swarm 관리자 노드는 클러스터 상태를 지속적으로 모니터링하고 실제 상태와 사용자가 표현한 원하는 상태 간의 차이를 조정
    • 롤링 업데이트
      • 롤아웃 시 점진적으로 노드에 서비스 업데이트를 적용
        • swarm 관리자를 사용하면 서로 다른 노드 집합에 대한 서비스 배포 사이의 지연을 제어 / 문제가 발생하면 이전 버전의 서비스로 롤백 가능
    • 스케줄러 로 노드관리(; 스케일링)

노드 작동 방식

  • How nodes work
    • Manager 노드 : 클러스터 관리 작업을 처리
      • 클러스터 상태 유지
      • 스케줄링 서비스
      • swarm 모드 HTTP API endpoint 제공
    • Worker 노드 : 컨테이너를 실행하는 것이 유일한 목적인 Docker 엔진의 인스턴스
  • Manage nodes in a swarm
    • AVAILABILITY 는 스케줄러가 작업을 노드에 할당할 수 있는지 여부가 표시
      • Active: 스케줄러가 작업을 노드에 할당할 수 있음
      • Pause : 스케줄러가 노드에 새 작업을 할당하지 않지만 기존 작업은 계속 실행 중
      • Drain : 스케줄러가 노드에 새 작업을 할당하지 않고 기존 작업을 종료하며 사용 가능한 노드에 작업 할당
    • MANAGER STATUS 은 Raft 합의에 대한 노드 참여를 의미
      • 값이 없으면 스웜 관리에 참여하지 않는 작업자 노드
      • Leader 노드 : 스웜에 대한 모든 스웜 관리 및 오케스트레이션 결정을 내리는 기본 관리자 노드
      • Reachable 노드 : Raft 합의 쿼럼에 참여하는 관리자 노드임을 의미, 리더 노드를 사용할 수 없게 되면 노드가 새 리더로 선출될 수 있다.
      • Unavailable 노드 : 다른 관리자와 통신할 수 없는 관리자임을 의미, 관리자 노드를 사용할 수 없게 되면 새 관리자 노드를 Swarm에 가입시키거나 작업자 노드를 관리자로 승격시켜야 한다.

서비스 동작 방식

  • How services work
  • 서비스는 같은 이미지에서 생성된 컨테이너의 집합, 서비스를 제어하면 해당 서비스 내의 컨테이너에 같은 명령이 수행됩니다. 서비스 내에 컨테이너는 1개 이상 존재 가능하며, 컨테이너들은 각 워커 노드와 매니저 노드에 할당된다. 이러한 컨테이너들은 Task 라고 부른다.
  • Swarm에 서비스를 배포하면 Swarm 관리자는 서비스 정의를 원하는 서비스 상태로 받아들입니다. 그런 다음 Swarm의 노드에서 하나 이상의 복제 작업으로 서비스를 예약합니다.
  • Task 는 Swarm의 노드에서 서로 독립적으로 실행
  • Container 는 격리된 프로세스이며, Swarm 모드 모델에서 각 작업은 정확히 하나의 컨테이너를 호출
  • TaskScheduler 가 컨테이너를 배치하는 "Slot" 과 유사하다.
    • 컨테이너가 활성화되면 스케줄러는 작업이 실행 중임을 인식
    • 컨테이너가 상태 확인에 실패하거나 종료되면 작업이 종료됨.

작업 및 일정

  • Tasks and scheduling
  • Task 는 Swarm 내 스케줄링의 원자 단위
    • 서비스를 생성하거나 업데이트하여 원하는 서비스 상태를 선언하면 오케스트레이터는 작업을 예약하여 원하는 상태를 실현

도커 스웜 실습

  • 스웜 구축을 위한 VM 3개
  • 각 노드 설정

$ firewall-cmd --permanent --zone=public --add-port=2377/tcp
$ firewall-cmd --reload
$ hostnamectl set-hostname manager1


# hosts 정보를 입력
$ cat << EOF >> /etc/hosts
192.168.2.16 manager1
192.168.2.25 worker1
192.168.2.26 worker2
EOF

	# manager 에서 수행
$ docker swarm init --advertise-addr 192.168.2.16
	# 워커에서 수행
$ docker swarm join --token SWMTKN-1-2pyrpj41954gi0nhhuyld9r161s93o611ep6usffazzelj9vnl-b1ifji8wmvq3efu117fq6v5w6 192.168.2.16:2377
	# manager에서 수행
$ docker node ls
  • 컨테이너 서비스 생성
    • Use swarm mode routing mesh : 기본 오토 로드밸런싱(swarm load balancer, {Mesh; 그물(망)}
    • --publish 플래그를 사용하여 서비스를 생성할 때 포트를 게시
    • target 컨테이너 내부의 포트를 지정하는 데 사용
    • published 라우팅 메시에 바인딩할 포트를 지정하는 데 사용, published 포트를 그대로 두면 각 서비스 작업에 대해 높은 번호의 임의 포트가 바인딩
    • 이 구문의 이전 형식은 콜론으로 구분된 문자열 -p 8080:80 새 구문은 읽기 쉽고 더 많은 유연성을 허용하므로 선호
	# task(pod)
	# "replicas" : task의 수, "publish": 	Publish a port as a node port
$ docker service create --name my_web --replicas 3 --publish published=8080,target=80 nginx
$ docker service ls
$ docker service ps my_web

$ docker service logs my_web
$ docker service inspect --pretty my_web
  • 작업 수 조정; 스케일
# VM 같은 인스턴스를 그대로인데 task(pod); 작업의 개수를 조정 (노드에게 작업을 추가 할당/제거)
$ docker service scale my_web=5
$ docker service ps my_web
$ docker service rm my_web

롤링 업데이트

  • docker service update
    docker service update [OPTIONS] SERVICE명
  • 서비스가 끊기지 않게 task를 하나씩 바꾸고 지운다.
$ docker service ps my_web
$ docker service inspect --pretty my_web

# `my_web` 서비스의 이미지를 변경
$ docker service update --image halilinux/web-site:v1.0 my_web
$ docker service ps my_web

클러스터에서 특정 노드 드레인하기

$ docker node ls
$ docker service ps my_web

# "manager1" 노드 열외
$ docker node update --availability drain manager1

# 리더의 역할을 수행하나 task 할당 받지 않는다.
$ docker node inspect --pretty manager1
$ docker service ps my_web

# 배제한 노드를 다시 활성화 (클러스터로 편입)
$ docker node update --availability active manager1

# 다시 수동으로 작업 공평하게 배분
$ docker service scale my_web=2
$ docker service scale my_web=3
$ docker node inspect --pretty manager1
  • "manager1" 을 배제한 결과(Drain)
  • "manager1" 을 클러스터로 복귀 시켜도 작업이 분할되지는 않는다.

노드 일시 멈춤 (Pause)

  • 기존의 작업은 유지하나 새로운 작업은 받지 않는다.
$ docker node update --availability pause worker2
	# "Pause"의 경우는 "Task" 가 유지된다.
$ docker service ps my_web
	# "worker2" 에는 추가적인 작업이 할당되지 않음.
$ docker service scale my_web=5
$ docker service ps my_web
$ docker node inspect --pretty worker2
$ docker node ls
  • "worker2" 에는 추가적인 작업이 할당되지 않음.
  • "worker2" 노드의 상태
profile
끄적끄적 쓰는곳

0개의 댓글