개요

  • 컨테이너 기반의 가상화 플랫폼

개념

  • Infrastructure as Code

    Mutable -> Ansible, Chef, Puppet
    Immutable -> Docker

  • 컨테이너 : 무거운 OS기반 가상화에서 -> 점차 프로세스 격리로 진화. 컨테이너라고 불림.
  • 이미지 : 컨테이너 실행에 필요한 의존성 파일과 시스템 설정값 등을 포함하고 있어서 바로 실행 가능함. (이 이미지를 실행한 상태가 컨테이너)
    url 방식으로 관리하며 태그를 붙일 수 있다.
  • 레이어 : 그렇다면 여러 개의 컨테이너를 띄우면 각 컨테이너 마다 같은 이미지들을 다 다운받아야 하나? -> 아니다. 기존 이미지에 필요한 이미지만 추가할 수 있다. 이를 레이어라 함.
  • 도커 레지스트리 : 이미지 저장소, 배포

    gitHub : 소스
    빌드 서버 (CI 서버) : 도커 이미지 생성
    도커 레지스트리 : 도커 이미지 저장
    Mesos (-> 쿠버네틱스로 대체 중) : Container Orchestration. 서버에 컨테이너 여러개를 띄워야 할 때, 자원이 남는 서버에 배치. 로드 밸런싱. 장애 복구. Ephemeral node

시작하기

설치

curl -fsSL https://get.docker.com/ | sudo sh
docker version # 설치 확인

Error response from daemon: Bad response from Docker engine
와 같은 오류가 나면 docker daemon이 제대로 안뜨거나 sudo로 설치하지 않은 것

  • 실행시 sudo 없이 특정 유저에게 권한 부여
    sudo usermod -aG docker $USER

docker 명령어

  • 실행
    docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

    -d : detached mode 흔히 말하는 백그라운드 모드
    -p : 호스트와 컨테이너의 포트를 연결 (포워딩)
    -v : 호스트와 컨테이너의 디렉토리를 연결 (마운트)
    -e : 컨테이너 내에서 사용할 환경변수 설정
    –name : 컨테이너 이름 설정
    –rm : 프로세스 종료시 컨테이너 자동 제거
    -it : -i와 -t를 동시에 사용한 것으로 터미널 입력을 위한 옵션
    –link : 컨테이너 연결 [컨테이너명:별칭]

  • 컨테이너 목록 확인
    docker ps [OPTIONS]

    -a : 종료되었으나 삭제되지 않은 컨테이너도 보여준다
    —all

  • 컨테이너 종료
    docker stop [OPTIONS] CONTAINER [CONTAINER...]
    전체 종료
    docker ps -a -q | xargs docker stop
  • 컨테이너 삭제 : 삭제되지는 않으므로 직접 삭제
    docker rm [OPTIONS] CONTAINER [CONTAINER...]
    하나씩 삭제하지 말고 한번에 전체 삭제하고 싶을 때
    docker rm -v $(docker ps -a -q -f status=exited)
  • 도커가 다운로드한 이미지 목록
    docker images [OPTIONS] [REPOSITORY[:TAG]]
  • 이미지 삭제
    docker rmi [OPTIONS] IMAGE [IMAGE...]
  • 이미지 다운로드
    docker pull [OPTIONS] NAME[:TAG|@DIGEST]
    run할 때 자동으로 받아지므로, pull은 보통 있는 이미지를 최신 버전으로 업데이트 할 때 쓰인다.
  • 컨테이너 로그
    표준 스트림 중 stdout, stderr를 수집하는 방식을 사용한다. -> 서버의 로그를 파일이 아니라 표준 출력으로 내보내야 한다.
    docker logs [OPTIONS] CONTAINER

    -f, —tail 옵션

    docker logs --tail 10 ${CONTAINER_ID}
    docker logs -f ${CONTAINER_ID}
  • 컨테이너 접속
    -ssh 권장하지 않음-
    docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
    docker exec -it mysql /bin/bash
    run은 컨테이너를 실행, exec는 실행중인 컨테이너의 명령어를 내림.
    아래와 같이 바로 명령어를 내릴 수도 있음
    docker exec -it mysql mysql -uroot
  • 컨테이너 접속
    docker attach my_container
  • 컨테이너에서 특정 파일 꺼내기
    docker cp hello-nginx:/etc/nginx/nginx.conf ./
  • 이미지 비교
    docker diff my_container
    변경된 파일 목록

실행

  • 우분투
    docker run --rm ubuntu:16.04
    이미지가 없으면 알아서 pull함
    bash 실행
    docker run --rm -it ubuntu:16.04 /bin/bash
    -it : 터미널 입력
  • redis
    docker run --rm -d -p 1234:6379 redis
    d 옵션 -> 컨테이너의 76a5f... 와 같은 아이디만 주고 쉘로 돌아옴.
    없다면 프로세스가 foreground로 실행되어 아무키도 입력할 수 없게 됨.
    p 옵션 -> 1234 포트를 6379로 연결
    telnet localhost 1234
  • mysql
    docker run -d -p 3306:3306 \
    -e MYSQL_ALLOW_EMPTY_PASSWORD=true \
    --name mysql \
    mysql:5.7
    e 옵션 -> 환경변수
    name 옵션 -> 76a5f... 이런 어려운 아이디 말고 읽기 쉬운 랜덤한 이름 부여
  • tensor flow
    docker run -d -p 8888:8888 -p 6006:6006 teamlab/pydata-tensorflow:0.1

호스트 디렉토리 마운트

  • docker에 mysql과 같은 DB를 설치하는 경우 컨테이너 삭제와 함께 데이터도 날라가므로, 저장소는 반드시 외부 저장소를 사용한다.
  • 호스트의 /my/own/datadir디렉토리를 컨테이너의 /var/lib/mysql 디렉토리로 마운트
    -v /my/own/datadir:/var/lib/mysql
  • pull command
    docker run -d -p 3306:3306 \
    -e MYSQL_ALLOW_EMPTY_PASSWORD=true \
    --name mysql \
    -v /my/own/datadir:/var/lib/mysql \ # <- volume mount
    mysql:5.7

Docker Compose

  • 도커 설정을 쉽게 관리하기 위해 YAML 방식으로 설정 파일 이용
    curl -L "https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose
    # test
    docker-compose version
  • docker-compose.yml 파일 생성
  • 실행
    docker-compose up

Dockerfile

  • 도커는 도커 이미지를 만들기 위해 Dockerfile이라는 DSLDomain Specific Language 파일을 사용한다.

기본 명령어

  • FROM : 베이스 이미지 지정 베이스 이미지 목록
    FROM ubuntu:16.04
  • MAINTAINER : 해당 파일 관리자
  • RUN : 명령어를 그대로 실행. 내부적으로는 /bin/sh -c 뒤에 해당 명령어 실행.
    RUN bundle install
  • ENTRYPOINT
  • ENV : 컨테이너에서 사용할 환경 변수
    -e 옵션을 사용하면 기존 값을 오버라이딩
    ENV <key> <value>
    ENV <key>=<value> ...
    ENV DB_URL mysql
  • COPY : 파일이나 디렉토리를 이미지로 복사
    COPY <src>... <dest>
    COPY . /usr/src/app
  • ADD : COPY와 비슷하나 src에 url이나 zip을 입력할 수 있음. (자동 압축 해제)
  • CMD : 도커가 실행되었을 때 실행되는 명령어. 빌드할 때는 실행되지 않으며 가장 마지막 CMD 하나만 실행. 한꺼번에 여러 개의 프로그램을 실행하고 싶은 경우에는 run.sh파일을 작성하여 데몬으로 실행.
    CMD bundle exec ruby app.rb
  • WORKDIR : 각 명령어의 현재 디렉토리는 계속 초기화 되므로 RUN cd /path 실행해도 소용없음. WORKDIR로 변경.
  • EXPOSE : 컨테이너가 실행되었을 때 요청을 기다리는 포트 지정. 여러개 가능.
    EXPOSE 4567
  • VOLUMN : 외부 파일을 컨테이너에 마운트할 때 사용.
    VOLUMN ["/dtata"]

    RUN vs CMD vs ENTRYPOINT
    RUN : 레이어를 만듬
    CMD : 기본 명령어, overriding 가능
    ENTRYPOINT : docker 실행시 실행되는 명령어

최적화

  • 이미지 빌드 시 각 명령어 라인마다 각 단계를 이미지 레이어로 저장하고, 다음에 재사용한다. 따라서 아래와 같은 경우 소스가 변경되면 계속 바뀌므로 재사용 불가능하다.
    COPY . /usr/src/app
    RUN bundle install 
  • bundle install과 src copy는 순서가 중요하지 않으므로 바꿔주어 재사용 가능하게 만든다.
    RUN bundle install # 다음 빌드시 재사용 가능하게
    COPY . /usr/src/app 

빌드

Dockfile이 있는 디렉토리에서
docker build -t ${NAME_TAG} .
docker run -d -p 9999:80 ${NAME_TAG}
docker run -d -p -it 9999:80 ${TAG} /bin/bash

배포

  • 빌드된 이미지를 도커 레지스트리에 배포(push)하고 내려받아(pull) 사용한다.
  • 도커 레지스트리는 직접 설치하거나, 이미 존재하는 도커 허브를 사용할 수 있으나 비공개는 한개만 가능하다.
  • 도커 이미지는 아래과 같이 구성되어 있다. 사용자
    [Registry URL]/[사용자 ID]/[이미지명]:[tag]
    Registry URL는 도커 허브라면 docker.io
    사용자 ID는 지정하지 않으면 library가 기본값이므로
    ubuntu = library/ubuntu = docker.io/library/ubuntu 는 모두 같은 값이다.

    배포

  • 로그인
    ~/.docker/config.json 에 로그인 정보가 저장된다
    docker login
  • 태그 수정
    docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
    docker tag app subicura/sinatra-app:1
  • push
    docker push subicura/sinatra-app:1

컨테이너 오케스트레이션

  • 컨테이너 애플리케이션의 배포, 스케일링 및 관리의 자동화

  • Swarm : 적당한 규모 (수십 대 이내)
  • Mesos : 주키퍼 기반의
  • Kubernetes : 큰 규모의 클러스터
  • ECS : AWS에서 단순하게 사용시
  • Nomad : HashiCorp 제품 사용시

도커 스웜

용어

  • Ingress (routing mesh)
    모든 클러스터 노드는 routing mesh에 참여한다. 클러스터에 참여한 노드 모두 같은 포트가 열려 있게 된다. 사용자는 어떤 노드에 어떤 컨테이너가 올라와 있는지 신경 쓰지 않고 아무 노드나 호출해도 routing mesh가 알아서 가용 서버를 찾아서 결과를 리턴한다.
    [image:31F0DFA2-F533-4805-9084-EE87C92FD47C-392-00000A59C50CDE77/2E827A83-75B4-4790-A1E4-80898459CA00.png]

    Hello World

  • 서버 클러스터 만들기
    클러스터 매니저 노드 만들기
    docker swarm init --advertise-addr 172.17.8.101
    매니터 노드에 다른 서버들 join
    docker service create \
    --name my-web \
    --publish published=8080,target=80 \
    --replicas 2 \
    nginx
    노드들의 목록 확인
    docker node ls
  • 클러스터에 도커 이미지 서비스로 띄우기
    docker service create --name [SERVICE_NAME] \
    -p 4567:4567 [IMAGE_NAME]
    서비스 목록 확인
    docker service lsdocker service ps [SERVICE_NAME]
    core-01:4567 에만 떠있더라도 core-02:4567, core-03:4567 등 클러스터의 모든 노드에 요청 가능하며 routing mesh가 알아서 사용 가능한 core-01:4567로 호출하여 리턴한다.
  • 서비스 scale
    docker service scale [SERVICE_NAME]=5
    이미지가 떴지만, 아직 서비스가 warm-up 중 일 때는? HEALTHCHECK 명령어 사용
  • 오버레이 네트워크
    모든 포트가 열려야 한다면 위험하지 않은가? -> --network 옵션으로 외부에서는 접근 불가능한 네트워크를 만들수 있다.
    docker service create --name redis \
    --network=backend redis

레지스트리 설치

  • 도커 registry도 image를 제공하므로 아래와 같이 받아서 실행해본다.
    docker pull registry
    docker run --name personal-registry -d -p 5000:5000 registry
  • 올릴 이미지에 태그 이름을 부여한다
    docker tag nacyot/hello_docker 0.0.0.0:5000/hello_docker
  • push 한다
    docker push 0.0.0.0:5000/hello_docker

참고