Docker

Jun 2, 2017


Docker

sudo를 매번 입력하지 않기 위해 Docker 그룹에 유저 넣기

sudo usermod -aG docker ${USER}
sudo service docker restart

이미지 찾기

  • 웹: https://hub.docker.com
  • 이름: 공식이미지, 올린사람id/이지미이름
  • Tags: 이미지 변경에 따른 버전 관리
  • 최신 Tags가 Latest로 명시됨
명령어: docker search <이미지이름>  
ex) docker search

이미지 다운로드(pull)

docker pull <이미지이름>:<태그>
ex) docker pull redis:Latest
    redis 최신 이미지 받기(테그 명시 안하면 최신으로 받음)

이미지 확인

docker images

이미지 실행(컨테이너 만들기)

docker run -i -t --name <컨테이너이름> -d <이미지이름>
: -i -t (interative 모드)
 ex) docker run -i -t --name myredis -d redis

컨테이너 확인

docker ps 
docker ps -a 
 : -a (정지된 컨테이너도 표시) 


컨테이너 다루기

컨테이너 실행 & 정지

docker start <컨테이너이름>
docker stop <컨테이너이름>

컨테이너 안에서 작업하기

docker exec -it <컨테이너이름> <실행할 명령>
: -it (인터랙티브 하게 실행), bash shell을 사용해라 
 ex) docker exec -it myredis /bin/bash

컨테이너 삭제

  • tips: 복수 명령도 가능 (한칸 띄고 다른 컨테이너이름)
docker rm <컨테이너 이름>

이미지 삭제

  • Notice: 태그이름 입력안하면 모든 태그 삭제됨
docker rmi <이미지이름>:<태그>

로그

docker logs -ft <컨테이너이름> 
 -ft: tail 

자원 제한 방법

CPU 제한

docker run -ti --c <제한값> <이미지이름> <실행할 명령>
ex) docker run -ti -c 512 ubuntu:16.04 /bin/bash
 -c or --cpu-share
 * 상대적인 비율: 각 컨테이너에 주는 숫자의 합을 비율로 하여 CPU사용 제한 

Memory 제한

docker run -ti -m <size> <이미지이름> <실행할 명령>
ex) docker run -ti -m 300M ubuntu:16.04 /bin/bash
 -m or --memory 
 * 메모리 제한 값 최소 4M
 * 별도의 설정이 없다면 swap도 300M (swap+memory) = 600M 
 * 단위: b, k, m, g
 

swap memory 제한

docker run -ti -m <size> --memory-swap <size> <이미지이름> <실행할 명령>
ex) docker run -ti -m 300M --memory-swap 1G ubuntu:16.04 /bin/bash
 : 메모리 사용량은 (swap+memory) = 1G => swap memory로 설정된 값은 700M


도커 관리를 위한 꿀 팁

실행중인 모든 컨테이너 죽이기

docker kill $(docker ps -q)

정지된 것을 포함해서, 모든 컨테이너를 삭제한다

docker rm $(docker ps -a -q)

오래된 컨테이너 삭제

docker ps -a | grep 'weeks ago' | awk '{print $1}' | xargs docker rm

정지된 컨테이너를 삭제

docker rm -v $(docker ps -a -q -f status=exited)

불필요한 이미지를 제거한다

docker rmi $(docekr images -q -f dangling=true)

불필요한 볼륨을 제거한다

docker volume rm $(docker volume ls -q dangling=true)


docker 이미지나 컨테이너를 파일로 저장하는 법

load or save image

ex) docker load < my_image.tar.gz
ex) docker save my_image:my_tag > my_image.tar.gz

load or save Container

ex) cat my_container.tar.gz | docker import - my_image:my_tag
ex) docker export my_container > my_container.tar.gz

Read-only Container

ex) docker run --read-only


컨테이너 모니터링 하기

하나의 컨테이너의 자원 사용량 모니터링(CPU, memory, network I/O)

docker stats <Container> 

모든 컨테이너의 자원 사용량 모니터링(Container ID로 정렬)

docker stats $(docker ps -q)

모든 컨테이너의 자원 사용량 모니터링(Container 이름으로 정렬)

docker stats $(docker ps --format '')

“ubuntu” 이미지로 만들어진 컨테이너 찾기

docker ps -a -f ancestor=ubuntu


이미지를 빌드하는 방법

1) docker commit 명령어

컨테이너 안에서 작업후 커밋하는 방법

  • 과정 재현이 어렵고 실수하기 쉬움
  • 간단한 컨테이너 만들때 사용
 
-- 우선 실행 -- 
docker run -it ubuntu /bin/bash
-- 컨테이너 안에서 작업 --
apt-get -yqq update
apt-get -y install apache2
exit 
-- 컨테이너 밖에서 commit--
docker commit 5a6a75ff3e6a rky0930/apache2
docker images rky0930/apache2 (이미지 확인)
docker commit -m="new apache2 image" --author="rky0930" 5a6a75ff3e6a rky0930/apache2:webserver
 : -m 메시지 -author 저자이름 컨테이너ID 이미지이름1/이미지이름2:테그
docker inspect rky0930/apache2:webserver

2) Dockerfile - docker build 명령

사용할 명령어를 dockerfile에 저장하고 docker build하는 방법

  • 이미지 빌드 과정을 모두 정리하고 진행
  • 관리가 쉽고 실수가 적다

dockerfile 이란 ?

  • 이미지를 빌드하는 방법과 과정을 적어놓은 파일
  • 도커 이미지의 설계도와 같다고 말할 수 있음
  • 반복적 사용가능
  • git, svn등으로 버젼관리하면 변경 이력 추적 가능

.dockerignore 파일 사용법

이미지에 포함하고 싶지 않은 파일, 디렉토리를 지정

  • Context의 root directory에 위치
  • 이미지를 크게 만드는 불필요한 파일
  • 보안 때문에 넣으면 안되는 파일
  • 각 패턴마다 줄 바꿈
  • ’#’ 으로 주석처리

규칙및 동작

 
*/temp*   : temp라는 디랙토리 제외
*/*/temp* : sub-sub 의 temp라는 디랙토리는 제외 
**/*.java : .java 파일 제외
temp?     : tempa tempb 등 5글자중 앞에 4글자가 temp 인것은 제외

dockerfile 설명2

  • 첫 줄부터 차례로 실행
  • 한 라인이 실행 될 때 마다 이미지에 레이어 추가
  • 실행중 에러가 나면 에러가 났던 곳 부터 실행
  • 첫출은 항상 FROM으로 시작
  • ’#’ 으로 주석처리

파일로 빌드하기

 
docker build -t "rky0930/sshd" .

빌드 이력

 
docker history rky0930/sshd

컨테이너 만들기

 
docker run -d -P --name test_sshd rky0903/sshd

port 오픈

 
docker port test_sshd 22

IPAddress 확인

 
docker inspect --format '' test_sshd

ssh 접속 (주의: 포트번호 22->32768)

 
ssh root@172.17.0.2 -p 32768
 - 컨테이너에서  22
 - 컨테이너 외부에서는 32768


docker 빌드중 에러발생해도 다시 빌드하면 에러지점부터 빌드 시작

docker 빌드중 캐쉬를 무시하려면

 
doker build --no-cache

일부의 캐쉬만 무시하려면

도커파일에 일부분만 수정하는 방법

 
ex) ENV 넣어 특정부분만 무시하도록 함(꼼수)
FROM ubunut:16.04
ENV UPDATED_AT 2017-01-01  <-- 이지점 아래부터 실행 
RUN apt-get -qq update

22번 포트를 그대로 쓸 수 없을까 ?

 
ex) docker run -d -p <host_port>:<Container_inside_port> --name <Containe name> <image name>
docker run -d -p 22:22 --name test_sshd2 rky0930/sshd
 : 호스트에서 접근시 22, 컨테이너 내부 사용 포트 22
docker run -d -p 2345:22 --name test_sshd2 rky0930/sshd
 : 호스트에서 접근시 2345, 컨테이너 내부 사용 포트 22


Dockerfile 명령어

ENV

 
ENV <key> <value>
ENV <key>=<value>

환경 변수 사용

 
ex) 
FROM ubuntu: 16.04
ENV foo /bar
WORKDIR ${foo}     ### WORKDIR /bar
ADD .$foo          ### ADD ./bar
COPY \$foo /quux   ### COPY $foo /quux \ <-- excape

조건부 환경 변수 문법

 
${A:-B}
: A가 정의되어 있으면 A, 아니면 B
${A:+B}
: : A가 정의되어 있으면 B, 아니면 empty

CMD

  • 컨테이너 생성할 때 실행
  • Docker 에 하나의 CMD만 가능
  • 여러 CMD가 있다면 마지막 것 만 실행
  • “docker build” 시점에 Dockerfile 의 CMD 설정능 오버라이드 가능
  • ENDPOINT 명령어의 파라미터로 사용 될 수도 있음
 
1) CMD ["executable", "param1", "param2"] 
2) CMD command param1 param2    ### /bin/sh -c 
3) CMD ["param1", "param2"]     ### ENTRYPOINT

CMD vs RUN

  • RUN 은 각 명령어를 실행할 때 마다 실행 되어 commit되고 레이어로 저장됨
  • CMD는 build 시점에는 실행되지 않음에 주의
  • CMD는 컨테이너를 run 할 때 실행

ENTRYPOINT

  • 컨테이너 생성할 때 실행
  • Dockerfile에서 하나의 ENTRYPOINT만 가능
  • 여러 ENTRYPOINT 있다면 마지막 것 만 실행
  • “docker build” 시점에서 Dockerfile 의 ENTRYPOINT 설정을 오버라이드 가능
  • CMD 명령어의 파라미터로 사용 될 수 도 있음
 
1) ENTRYPOINT ["executable", "param1", "param2"] 
2) ENTRYPOINT command param1 param2  
 
ex1) 
CMD ["-T"]
ENTRYPOINT ["/usr/sbin/sshd"]
= /usr/sbin/sshd -T 

ex2)  ### 실행하는 명령어를 바꿀때 사용 가능 
CMD ["-T"]
ENTRYPOINT ["/usr/sbin/sshd"] 
Docker run <image> -D 
= /usr/sbin/sshd -D

[사진활용] 표1_CMD_ENTRYPOINT

  • point 조합하실때는 [””, “”] <- 방법으로사용 할 것

WORKDIR

RUN, CMD, ENTRYPOINT, ADD 등 을 실행 할 directory를 지정

  • Dockerfile에서 여러번 사용 가능
  • 상대 경로도 사용가능
  • 해당 경로가 없으면 생성됨
  • “docker run”에서 -w 옵션으로 오버라이드 가능

USER

RUN, CMD, ENTRYPOINT등 을 실행 할 유저를 지정

  • 지정하지 않으면 root로 실행
  • 파라미터로 유저 ID나 UID를 지정

Volume

호스트와 공유할 directory설정

 
ex) 
VOLUME ["/data1", "/data2"]
VOLUME /data1 /data2
---
FROM ubunutu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
---

ADD

컨테이너에 파일 추가

  • tar.gz ADD시 자동으로 압축 풀어서 넣어줌
  • 가져올 파일이 변경되면 ADD 명령으로 빌드 캐쉬가 무효화 될 수 있음에 주의
  • URL 이 인증을 필요로 한다면 사용 불가
 
ex) 
ADD hom* /mydir/
ADD hom?.txt /mydir/
ADD test relativeDir/
ADD test /absoluteDir/
ADD http://example.com/foo.zip /mydir/bar.zip
ADD foo.tar.gz /mydir/
--- 
아래와 같은 방법으로 Context 외부에서 복사 불가 (.. 을 사용하여 상위로 가서 작업한는 것은 불가)
ADD ../test/ /mydir/  
---

COPY

컨테이너에 파일 복사

  • ADD와 유사하지만 복사 기능만 제공
  • 압축 해제 같은 복사 외의 기능은 제외
 
ex) 
COPY hom* /mydir/
COPY hom?.txt /mydir/
COPY test relativeDir/
COPY test /absoluteDir/
---
Context 외부에서 복사 불가  (.. 을 사용하여 상위로 가서 작업한는 것은 불가)
COPY ../test/ /mydir/  
---


docker-compose

여러 개의 컨테이너를 한번에 올리고 내릴 수 있는 툴

  • 로컬 개발 환경, 테스트 서버, CI 등의 환경에서 사용하면 편리
  • 각 애플리케이션에 dockerfile을 정의하고, 서비스 들을 docker-compose.yml에 설정
 
실행: docker-compose up
정지: docker-compose stop
확인: docker-compose ps

docker-compose 명령어

 
image      : 사용할 이미지 설정  
dockerfile : 사용할 docekrfile을 지정  
command    : 기본 command를 새로운 값으로 오버라이드  
links      : 컨테이너 이름을 사용하면 ip 가 /etc/hosts에 저장됨  
env_file   : 환경 변수를 저정한 파일 지정  
volumes    : 마운트할 볼륨을 지정  
port       : 사용할 포트 설정  
dns        : 컨테이너가 사용할 dns 주소 설정  

docker-compose 실습 - 워드프레스(wordpress)

1) docker-compose.yml

 
파일명: docker-compose.yml
version: '2'
services:

    wordpress:
        image: wordpress
        ports:
            - 8080:80
        environment:
            WORDPRESS_DB_PASSWORD: example
    
    mysql:
        image: mariadb
        environment:
            MYSQL_ROOT_PASSWORD: example

2) 실행

 
실행: 
docker-compose up
테스트: 
localhost:8080 접속


docker hub

docker hub에 image 올리기

 
docker login
docker push <사용자이름>/<이미지이름2>:<tags>
* 이미지 이름 앞에 사용자 이름이 들어가야함 (없으면 official이 되기 때문에 문제 발생)
ex) 
docker push rky0930/apache2:webserver

docker hub에 image 받기

 
docker pull <사용자이름>/<이미지이름2>:<tags>
ex) 
docker pull rky0930/apache2:webserver


Spring 실습 (에러 발생.. 디버그 필요)

Java + Spring + Docker
Image & Source code :
1) spring-petclinic
2) maven

git clone https://github.com/spring-projects/spring-petclinic.git
cd spring-petclinic
vi Dockerfile

Dockerfile

파일명: Dockerfile
--- 
FROM maven:3.3-jdk-7-onbuild
CMD ["mvn", "tomcat7:run"]
--- 
build & run: 
docker build --tag my-spring .
docker run -rm -it -p 9966:9966 my-spring
 : -rm = Automatically remove the container when it exits


Jenkins 실습

CI 툴, 주기 빌드 서버

ex) 
### 실행
docker run --name myjenkins -p 8080:8080 -p 50000:50000 -v /var/jenkins_home jenkins
### 초기 패스워드 보기 
docker exec -it myjenkins cat /var/jenkins_home/secrets/initialAdminPassword
### 접속 
localhost:8080


Node.js 실습

파일명: Dockerfile
FROM node:6.9-onbuild
파일명: app.js
var http = require("http");

http.createServer(function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"});
    res.write("Hello\r\n");
    res.write("World\r\n");
    res.end();
}).listen(8080);
파일명: package.json
{
    "name": "my-node.js",
    "version": "0.1.0",
    "description": "docker nodejs test",
    "main": "app.js",
    "scripts": {
        "start": "node app.js"
    }
}

파일 다 만들고 Build & Run

- 빌드 
Docker build --tag my_nodejs .
- 실행 
Docker run -it -p 8080:8080 my_nodejs


멀티 컨테이너 실전 연습

node.js + redis

동작 예) 
http://localhost/hello/name
: name 추가
http://localhost/bye/name
: name 삭제
http://localhost/members
: name 리스트

프로젝트 구성

mynode  
 ㄴ mynode  
   ㄴ app.js
   ㄴ package.json
 ㄴ Dockerfile

myredis
 ㄴdockerfile 

1) myreids/Dockerfile

파일명: myreids/Dockerfile
FROM ubuntu:16.04
MAINTAINER your name <your.name@gmail.com>
ENV UPDATED_AT 2016-10-23

RUN apt-get -yqq update
RUN apt-get -yqq install software-properties-common python-software-properties
RUN add-apt-repository ppa:chris-lea/redis-server
RUN apt-get -yqq update
RUN apt-get -yqq install redis-server redis-tools

VOLUME ["/var/lib/redis", "/var/log/redis"]

EXPOSE 6379

ENTRYPOINT ["redis-server", "--logfile /var/log/redis/my-redis.log", "--protected-mode no"]

2) mynode/Dockerfile

파일명: mynode/Dockerfile
FROM ubuntu:16.04
MAINTAINER your name <your.name@gmail.com>
ENV UPDATED_AT 201610231122

RUN apt-get -yqq update
RUN apt-get -yqq install nodejs npm
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN mkdir -p /var/log/node

ADD mynode /opt/node/

WORKDIR /opt/node
RUN npm install
### Defendency library install

EXPOSE 8080
CMD ["nodejs", "app.js"]

3) mynode/mynode/app.js

파일명: mynode/mynode/app.js

var express = require('express');
var app = express(); 
var redis = require('redis');
var server = require('http').createServer(app);

var redisHost = process.env.REDIS_HOST || 'myredis';
var redisPort = 6379;
var redisClient = redis.createClient({post: redisPort, host: redisHost});

app.get('/', function(req, res) {
    res.json({
        status: "ok"
    });
});

app.get('/hello/:name', function(req, res) {
    redisClient.sadd('party:members', req.params.name);
    res.json({
        hello: req.params.name
    });
});

app.get('/bye/:name', function(req, res) {
    redisClient.srem('party:members', req.params.name);
    res.json({
        bye: req.params.name
    });
});

app.get('/members', function(req, res) {
    redisClient.smembers('party:members', function(err, reply) {
        res.json({members: reply});
    });
});

var port = process.env.HTTP_PORT || 8080;
server.listen(port);
console.log('Listening on port' + port);

4) myndoe/mynode/package.json
Tips: 버전 명시하는게 좋음, 배포시 버전이 변경되어 에러가 발생하는 경우가 있음

파일명: myndoe/mynode/package.json
{
    "name": "my-node-server",
    "version": "0.1.0",
    "description": "multi container example", 
    "dependencies": {
        "express": "4.14.0",
        "redis": "2.6.2"
    }
}

실행

Tips: Redis 먼저 실행 할 것
why ? nodejs 서버가 먼저 실행되면 Redis 링크 부분에서 에러 발생 할 수 있음.

1) myredis 실행

docker build -t rky0930/myredis .
docker run -d -h myredis --name myredis -p 6379:6379 rky0930/myredis
docker run -it -h myredis --name myredis -p 6379:6379 rky0930/myredis 
 : -h =  Container host name

2) mynode 실행

docker build -t rky0930/mynode .
 : -t 이미지 이름
docker run -d --name mynode --link myredis:myredis -p 8080:8080 rky0930/mynode
docker run -it --name mynode --link myredis:myredis -p 8080:8080 rky0930/mynode