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