이번에는 docker 이미지 최적화에 대해서 설명하겠습니다.


이미지 최적화를 위해서는 다음의 3가지를 잘 활용하면 됩니다.


1. 레이어를 줄이기 위해서 다중 RUN 명령어는 하나의 RUN 명령어로 구성
2. 파일 복사와 라이브러리 Install 은 순서가 중요
3. 컴파일과 같은 작업은 Multistep build 를 이용

 


alpine linux 로 nginx 를 실행시기 위한 방법으로 다음과 같은 docker 이미지를 만들 수 있습니다.


먼저, nginx.conf 파일을 로컬 컴퓨터에 생성합니다.

$ vi nginx.conf

user www;
worker_processes auto;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    access_log /var/log/nginx/access.log;
    keepalive_timeout 3000;
    server {
       listen 80;
       root /www;
       index index.html index.htm;
       server_name localhost;
       client_max_body_size 32m;
       error_page 500 502 503 504 /50x.html;
       location = /50x.html {
             root /var/lib/nginx/html;
       }
    }
}



다음은 간단한 index.html 입니다.

$ vi index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>HTML5</title>
</head>
<body>
    Server is online
</body>
</html>

 


이 두 파일을 활용한 Dockerfile 은 다음과 같습니다.

$ vi Dockerfile


FROM alpine:3.8
RUN apk update
RUN apk add --no-cache nginx
RUN adduser -D -g 'www' www
RUN mkdir /www
RUN chown -R www:www /var/lib/nginx
RUN chown -R www:www /www

COPY nginx.conf /etc/nginx/nginx.conf
COPY index.html /www/index.html

ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

 


docker 이미지를 빌드하고 실행시키면 index.html 결과를 얻을 수 있습니다.


$ sudo docker build -t seungkyua/nginx-alpine .

$ docker run -d -p 30080:80 --name nginx-alpine seungkyua/nginx-alpine

$ curl http://localhost:30080

 

하나의 RUN 명령어로 구성

여기서 첫번째 이미지 최적화 포인트가 보입니다.


앞의 Dockerfile 에서 하나의 RUN 은 하나의 이미지 레이어가 되므로 이것을 하나로 다음과 같이 줄일 수 있습니다.

 

RUN apk update && \

    apk add --no-cache nginx && \

    adduser -D -g 'www' www && \

    mkdir /www && \

    chown -R www:www /var/lib/nginx && \

    chown -R www:www /www

 




이번에는 nodejs docker 이미지를 만들어 보겠습니다.

 

$ package.json
{
    "name": "docker_web_app",
    "version": "1.0.0",
    "description": "Node.js on Docker",
    "private": true,
    "author": "Seungkyu Ahn <seungkyua@gmail.com>",
    "main": "server.js",
    "scripts": {
       "start": "node server.js"
    },
    "dependencies": {
       "express": "^4.16.1"
    }
}



$ vi server.js

'use strict';

const express = require('express');

const PORT = 8080;

const HOST = '0.0.0.0';

const app = express();

app.get('/', (req, res) => {

  res.send('Hello world\n');

});

app.listen(PORT, HOST);

console.log(`Running on http://${HOST}:${PORT}`);

 


$ vi Dockerfile

FROM node:8

RUN mkdir -p /app

COPY package*.json /app/

WORKDIR /app

COPY . /app

RUN npm install --only=production

EXPOSE 8080

CMD [ "npm", "start" ]



파일 COPY 와 관련 라이브러리 설치 순서가 중요

위의 Dockerfile 의 경우 현재 디렉토리 소스를 COPY 한 후에 npm install 을 수행합니다.


docker 이미지는 변경된 레이어만 build 되지만 연관된 하위 레이어까지 build 됩니다.


여기서는 현재 디렉토리 소스가 변경되면 npm install 을 매번 다시 수행합니다.


그러므로 일단 package 설치를 먼저하고 COPY 를 나중에 하면 package 설치 내용이 변경되지 않는다면 npm install 은 캐시를 바로 사용하여 설치하지 않습니다.

 


RUN npm install --only=production

COPY . /app

 




마지막으로, 컴파일 하는 소스의 docker 이미지를 살펴보겠습니다.

 

$ vi hello.c

#include <stdio.h>

int main () {

  printf ("Hello, world!\n");

  return 0;

}



$ vi Dockerfile

FROM alpine:3.8

RUN apk update && \

    apk add --update alpine-sdk

RUN mkdir -p /app

COPY . /tmp

WORKDIR /tmp

RUN gcc hello.c -o hello

ENTRYPOINT ["/tmp/hello"]


 

Multistep build 활용

위의 경우에 c 컴파일을 하기 위해 c 컴파일로가 들어있는 sdk 패키지를 설치하고 바이너리 파일로 컴파일을 하므로 이미지 사이즈가 커집니다.


여기서 build 단계를 활용하면 sdk 패키지는 제외하고 최종 바이너리 파일만 docker 이미지에 넣을 수 있습니다.

 

$ vi Dockerfile

FROM alpine:3.8 AS build

RUN apk update && \

    apk add --update alpine-sdk

RUN mkdir -p /app

COPY . /tmp

WORKDIR /tmp

RUN gcc hello.c -o hello


FROM alpine:3.8

COPY --from=build /tmp/hello /app/hello

ENTRYPOINT ["/app/hello"]

 




아래 이미지 사이즈는 build 스텝을 활용하지 않은 파일 사이즈와 활용한 사이즈의 차이입니다.



seungkyua/c-hello-world      176MB

 


seungkyua/c-hello-world      4.42MB


Posted by Kubernetes Korea co-leader seungkyua@gmail.com

docker 이미지가 어떻게 구성되는지 이해하기 위해서는 Copy-on-write 정책을 이해해야 합니다.


아래와 같은 Dockerfile 이 있고 이를 이미지로 만들었을 때 이미지가 차지하는 스토리지 구성은 다음과 같습니다.

(아래 Dockerfile 은 sample 파일로 make tool 까지 있다고 가정합니다.)

 

FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py

 

container-layers.jpg  

이미지 출처: https://docs.docker.com/v17.09/engine/userguide/storagedriver/images/container-layers.jpg

 

가장 아래 레이어 d3a1f33e8a5a 는 ubuntu:15.04 이미지 입니다.


그 다음 바로 위의 c22013c84729 는 현재 디렉토리의 이하의 모든 소스를 /app 디렉토리 아래 복사한 이미지 레이어입니다. (COPY . /app 명령에 의한)


d74508fb6632 는 RUN make /app 가 실행된 이미지 레이어이고, 마지막으로 91e54dfb1179 는 CMD python /app/app.py 이미지 레이어입니다.


Dockerfile 에서 하나의 명령어는 하나의 이미지 레이어 이며, 여기까지 이미지는 Read Only 이미지로 컨테이너가 실행되어도 바꿀 수 없습니다.

 

위의 Dockerfile 로 만든 이미지로 컨테이너가 실행되면 제일 상단의 Container Layer 로 Thin Read/Write 스토리지가 생기며, 여기에만 데이터 쓰기가 가능합니다.


같은 이미지로 컨테이너 인스턴스를 여러 개 실행시켜도 이미지는 공유하며 Container Layer 만 각 인스턴스별로 생겨 자신의 쓰기 스토리지가 생기는 구조이며, 기본적으로 컨테이너가 실행되면서 생긴 스토리지는 실행된 컨테이너가 삭제될 때 같이 삭제됩니다.  

 

위의 Dockerfile 에서 두번째 라인이 현재 디렉토리를 /app 아래로 복사는 명령어에서 현재 디렉토리에 있는 파일이 변경되었다고 가정해 봅시다.


다시 이미지를 만들 경우 맨아래 d74508fb6632 이미지 레이어는 링크로 그대로 활용되고, c22013c84729 이미지 레이어를 먼저 복사하고(copy), 수정된 파일들을 /app 디렉토리 아래로 복사하는(write) 부분이 일어나며 그 이후의 이미지 레이어들은 계속 실행됩니다.



Dockerfile 을 작성할 때 기본 예약 명령어는 다음과 같습니다.


FROM nginx:alpine


nginx 이미지의 alpine 태그를 Base 이미지로 사용합니다.



RUN mkdir -p /app


mkdir -p /app 명령을 실행합니다. shell 명령어를 쓸 때 사용합니다.



ADD source.tar /app/


source.tar 를 /app 디렉토리 아래에 풉니다.



ADD http://ahnseungkyu.com/release /data/


인터넷의 http://ahnseungkyu.com/release 파일을 /data 디렉토리 아래에 다운받습니다.



COPY . /app


현재 디렉토리의 모든 파일과 하위 디렉토리를 /app 디렉토리 아래로 복사합니다. 



COPY ./conf /app/conf


conf 디렉토리 아래의 모든 파일과 하위 디렉토리를 /app/conf 디렉토리 아래로 복사합니다.



COPY ./*.js /app/


현재 디렉토리으 모든 js 파일을 /app 디렉토리 아래로 복사합니다.




추가로 Dockerfile 을 작성할 때 가장 기본이 되는 CMD 와 ENTRYPOINT 의 차이에 대해서 알아보겠습니다.

Dockerifle 에서는 기본적으로 최소 하나의 CMD 혹은 ENTRYPOINT 가 있어야 하며, 둘 다 있을 수 도 있습니다.

즉, docker 를 실행할 때 CMD 로 호출하거나 ENTRYPOINT 로 호출 할 수 있습니다.

아래 alpine ping Dockerfile 을 예로 살펴보겠습니다.


From alpine:latest

RUN apk update && apk add --update iputils \

  && rm -rf /var/cache/apk/*

CMD ["ping", "-c", "3", "8.8.8.8"]



여기서는 CMD 를 사용하여 ping -c 3 8.8.8.8 명령어를 실행했습니다.

docker 이미지를 만들고 실행해보면 결과가 잘 나옵니다.


$ sudo docker build -t seungkyua/alpine-sample .


$ sudo docker run --name alipine-sample --rm -it seungkyua/alpine-sample



이것을 ENTRYPOINT 사용으로 바꾸면 다음과 같습니다.


From alpine:latest

RUN apk update && apk add --update iputils \

  && rm -rf /var/cache/apk/*

ENTRYPOINT ["ping", "-c", "3", "8.8.8.8"]



똑같이 결과가 나타납니다.


그럼 이 둘의 차이는 무엇일까요? 그건 둘 다 사용했을 때 알 수 있습니다. 다음과 같이 바꿔보겠습니다.


From alpine:latest

RUN apk update && apk add --update iputils \

  && rm -rf /var/cache/apk/*

ENTRYPOINT ["ping"]

CMD ["-c", "3", "8.8.8.8"]



결과가 똑같습니다. 하지만 여기서 아래와 같이 docker 를 실행할 때 8.8.8.8 을 맨 뒤에 추가하면 결과가 달라집니다.


$ sudo docker run --name alipine-sample --rm -it seungkyua/alpine-sample 8.8.8.8


마지막의 CMD 부분이 argument 로 처리 되는데 8.8.8.8 이라는 argument 가 override 되어 -c 3 8.8.8.8 부분이 8.8.8.8 로 되어 버립니다. 


결과적으로 ENTRYPOINT 와 CMD 를 함께 쓰는 경우는 CMD argument 를 override 하고 싶을 때 사용할 수 있기 때문에 가능하면 둘 다 사용하는 방법을 추천드립니다.






Posted by Kubernetes Korea co-leader seungkyua@gmail.com
TAG docker, Layer

PID namespace 외에 Network namespace 를 알아보겠습니다.

처음 글을 시작한 의도는 docker 를 잘 활용하자는 의미였는데 하다보니 아직 그 내용은 들어가 보지도 못했네요.


개발자나 운영자들은 잘 인식하지 못하지만 linux 의 Network 는 모두 namespace 구조하에 움직이고 있습니다.


기본적으로 host 기반으로 사용하는 network 의 namespace 는 1 입니다. 그리고 docker 가 실행되면 docker process 에 대해서 내부적인 network 를 사용합니다.


network namespace 와 network namespace 간의 연결은 virtual ethernet (veth) 를 사용하여 연결합니다. 즉, veth pair 를 만들어서 하나는 docker 내부에 다른 하나는 host 로 하여 서로 네트워크로 연결하고 host 에 있는 veth 는 host 의 bridge 로 연결하면 docker 내부에서 host 의 bridge 로 연결이 됩니다.

 

docker 컨테이너 (eth0)  <-> host veth  <->  host bridge (docker0)  <->  host network (ens2f0)

 


먼저, host veth 의 리스트를 보겠습니다.


$ sudo ip addr show


15479: vethf5fa5c1@if15478: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP

    link/ether 32:83:61:96:55:4a brd ff:ff:ff:ff:ff:ff link-netnsid 1

15481: veth4667f7d@if15480: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP

    link/ether 56:39:6e:41:3d:50 brd ff:ff:ff:ff:ff:ff link-netnsid 0

15483: vethd61c546@if15482: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP

    link/ether b2:4a:88:71:d0:03 brd ff:ff:ff:ff:ff:ff link-netnsid 2

15489: vethbe5dd5b@if15488: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP

    link/ether fe:85:37:ac:ea:76 brd ff:ff:ff:ff:ff:ff link-netnsid 4

15493: veth66998b7@if15492: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP

    link/ether 42:5f:03:92:45:9d brd ff:ff:ff:ff:ff:ff link-netnsid 5

 


총 5개가 보이는데 이중 하나는 docker gateway bridge 로 연결되고 나머지 4개는 1:1 로 각각 docker 내부의 eth0 와 연결되어 있습니다.


이를 좀 쉽게 찾기 위해서 veth.sh 파일을 만들었습니다.


#!/bin/bash


VETHS=`ifconfig -a | grep veth | sed 's/ .*//g'`

DOCKERS=$(docker ps -a | grep Up | awk '{print $1}')


for VETH in $VETHS

do

  PEER_IFINDEX=`ethtool -S $VETH 2>/dev/null | grep peer_ifindex | sed 's/ *peer_ifindex: *//g'`

  for DOCKER in $DOCKERS

  do

    PEER_IF=`docker exec $DOCKER ip link list 2>/dev/null | grep "^$PEER_IFINDEX:" | awk '{print $2}' | sed 's/:.*//g'`

    if [ -z "$PEER_IF" ]; then

      continue

    else

      printf "%-10s is paired with %-10s on %-20s\n" $VETH $PEER_IF $DOCKER

      break

    fi

  done

done



위 파일을 실행하면 아래와 같이 나옵니다.


veth4667f7d: is paired with eth0@if15481 on b75a945f84fa        

veth66998b7: is paired with eth0@if15493 on 67246d3e1c68        

vethd61c546: is paired with eth0@if15483 on 87faa6bacbba        

vethf5fa5c1: is paired with eth0@if15479 on 0b4bc5ba78f9

 

마지막에 보이는 것은 docker id 입니다.

 

host 의 docker0 bridge 가 어떻게 연결되어 있는 지는 brctl 로 알 수 있습니다. 


$ sudo yum install -y bridge-utils


$ sudo brctl show




docker0 bridge 가 어떻게 host 의 외부 네트워크로 나가는 지는 iptables 와 routing 을 보면 알 수 있습니다.


$ sudo iptables -t nat -S


-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

 


source 가 docker 내부 ip 대역이고 host 입장에서 패킷이 나가는 인터페이스가  docker0 가 아니면 jump 를 하고 host의 ip 로 변경해서 (masquerade) 나가라고 되어 있습니다. 

 


$ netstat -rn


Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface

0.0.0.0            192.168.30.1    0.0.0.0           UG        0 0          0 ens2f0

172.17.0.0        0.0.0.0            255.255.0.0     U         0 0          0 docker0

172.18.0.0        0.0.0.0            255.255.0.0     U         0 0          0 docker_gwbridge

 

그리고 위와 같이 외부로 나갈 때는 default gateway 로 외부로 나가게 됩니다.

 

 

Posted by Kubernetes Korea co-leader seungkyua@gmail.com

컨테이너가 linux 의 cgroup 과 namespace 를 활용하여 동작하는 것은 이제 보편적으로 알려진 내용입니다. cgroup 은 자원의 활용에 사용하고 namespace 는 자원의 isolation 에 사용합니다.


이 중에서 namespace 의 종류는 다음과 같습니다.


ipc : 프로세스간 양방향 통신

mnt : 파일 시스템

net : 네트워크

pid : 프로세스 id

user : 사용자 계정

uts : Unix time sharing

 

위의 namespace를 기본으로 docker 의 프로세스 구조를 보면 다음과 같습니다.

먼저, pstree 명령어를 설치합니다.  어떤 패키지인지 모르면 아래 명령어로 패키지명을 찾을 수 있습니다.


$ sudo yum whatprovides pstree

psmisc-22.20-15.el7.x86_64 : Utilities for managing processes on your system

Repo        : base

Matched from:

Filename    : /usr/bin/pstree

 

$ sudo yum install -y psmisc

 

이제 pstree 로 docker 의 패키지를 봅니다.


$ pstree -lSps $(pidof dockerd)




() 안에 들어간 번호는 프로세스 id 입니다.  dockerd 프로세스는 15472 이고 이후의 프로세스 명이 잘려서 보이니 ps 로 프로세스 명을 보면 다음과 같습니다.


$ ps -ef | grep 15489


15489 는 docker-containerd 입니다.

 

$ ps -ef | grep 15675



15675 는 docker-container-shim 입니다.


$ ps -ef | grep 15711


그리고 실제 컨테이너로 띄고 있는 minio 프로세스는 15711 입니다.



위의 내용을 보면 docker 프로세스는 다음과 같은 구조로 되어 있습니다.


dockerd (15472, docker 데몬)   ->   docker-containerd (15489, docker cotainerd) -> docker-container-shim (15675) -> minio (15711, 실행중인 docker 컨테이너)


docker-containerd 는 docker runtime 과 관련 있습니다. 컨테이너 실행은 runc 와 containerd 를 따릅니다. runc 는 Open Container Initiative(OCI) 에 의해 정의된 컨테이너를 실행시키는 부분으로 namespace 와 보안에 관한 표준입니다. containerd 는 runc 를 기반으로 컨테이너 이미지 관리, 스토리지, 실행 등의 기능을 확장한 것으로 CNCF 에 기부된 프로젝트 입니다.

 

docker-container-shim 은 docker 에서 자체적으로 추가한 것으로, 실행되는 컨테이너와 containerd 사이에서 컨테이너를 연결하여 containerd  가 문제가 생기더라도 실행되는 컨테이너에는 여향을 주지 않기 위해서 만들었습니다. 그런데 사실 이게 왜 더 필요한지는 모르겠습니다. 괜히 관리 레이어만 하나 더 추가된 부분으로 보이긴 합니다.

 

PID namespace 를 docker 에서 어떻게 적용되는지 보겠습니다.


앞에서 minio 컨테이너는 host 에서 보면 프로세스 id 가 15711 이고 그 부모 프로세스는 15675 인 docker-container-shim 인 것을 확인했습니다.


이제 minio 컨테이너를 아래와 같이 확인 합니다.


$ sudo docker (container) ls | grep minio

0b4bc5ba78f9        minio/minio                 "minio server /export"   18 months ago       Up 4 hours          0.0.0.0:9000->9000/tcp   minio

 

그리고, minio 컨테이너 안으로 들어가 보겠습니다.

sudo docker (container) exec -it 0b4bc5ba78f9 sh

 

프로세스를 조회해보면 다음과 같이 1번 프로세스로 minio 서버를 띄운 것이 보이고 (컨테이너 밖의 host 에서는 15711) 그 위의 부모 프로세스는 보이지 않습니다.




 

Posted by Kubernetes Korea co-leader seungkyua@gmail.com

Kubernetes 는 여러 컨테이너를 관리할 수 있습니다. 그 이유는 kubernetes kubelet 과 컨테이너 데몬이 정해진 spec 에 따라 gRPC 통신을 하기 때문입니다.  Kubernetes 는 여러 컨테이너를 적용할 수 있는데 rkt, docker, CRI-O 등이 있습니다. 이중에서 가장 많이 사용하는 Docker 에 대해서 설명하고자 합니다.

앞서 kubespray 사용에 대해서 설명이 있는데, 여기를 보면 Production Level 로 사용 가능한 CentOS, RedHat, Ubuntu에 맞게 docker 를 쉽게 설치할 수 있습니다. 여기서는 Docker 를 이미 설치한 후에 어떻게 Docker 를 잘 활용할 수 있는지에 대해서 설명합니다.

1. dockerd config option reload

대부분의 Linux 는 이제 systemd 를 활용하고 있습니다. 그렇기 때문에 docker 데몬 실행은 systemd 에 설치된 docker.service 를 보면 됩니다. 디렉토리는 둘 중에 하나를 찾아보면 됩니다.

/etc/systemd/system/multi-user.target.wants/docker.service

/etc/systemd/system/docker.service

 

제 환경은 CentOS 로 이렇게 정의가 되어 있습니다.

MountFlags=shared

EnvironmentFile=-/etc/sysconfig/docker

ExecStart=/usr/bin/dockerd $DOCKER_OPTS

ExecReload=/bin/kill -s HUP $MAINPID

 

dockerd 의 환경 파일로 /etc/sysconfig/docker 를 읽게 되어 있고 해당 파일은 아래 옵션으로 정의되어 있습니다.

DOCKER_OPTS="--storage-driver=overlay2 --insecure-registry docker-registry:5000 --insecure-registry oreo:5000"

 

이 경우 systemctl restart docker.service 를 하면 dockerd 가 restart 되는데 문제는 이미 실행되고 있는 docker 컨테이너가 영향을 받아 stop 으로 빠집니다. (kubernetes 의 deployment, daemonset, statefulset 혹은 docker 의 --restart=always 가 아닌 경우) 그러므로, docker 옵션만 reload 해야 기존의 실행되고 있는 컨테이너가 영향을 받지 않습니다.

docker option reload 는 SIGHUP 을 보내면 됩니다. 

$ sudo kill -SIGHUP $(pidof dockerd)

 

하지만, 지금의 세팅으로는 다시 올라오지 않습니다.

dockerd 로그를 보면 다음과 같습니다. 로그는 다음 명령어로 볼 수 있습니다.

$ sudo journalctl --no-pager -u docker.service

kube-deploy dockerd[16391]: time="2018-10-27T18:36:17.767207453+09:00" level=info msg="Got signal to reload configuration, reloading from: /etc/docker/daemon.json"

kube-deploy dockerd[16391]: time="2018-10-27T18:36:17.767283042+09:00" level=error msg="open /etc/docker/daemon.json: no such file or directory"

 

디폴트로 dockerd 옵션은 /etc/docker/daemon.json 파일을 읽기 때문에 옵션  reload  가 안되는 문제가 있습니다.

이를 해결하기 위해서 다음과 같이 변경해 보겠습니다.

docker.service 에 정의된 EnvironmentFile 의 변수를 daemon.json 으로 옮기겠습니다.

/etc/systemd/system/multi-user.target.wants/docker.service

EnvironmentFile=-

 

$ sudo mkdir -p /etc/docker

$ sudo vi /etc/docker/daemon.json

{

    "storage-driver": "overlay2",

    "insecure-registries": ["docker-registry:5000", "oreo:5000"]

}

 

docker.service 를 바꿨으니 어쩔 수 없이 한 번의 docker restart 는 해야 합니다.

$ sudo systemctl daemon-reload

$ systemctl restart docker.service

 

이제 현재의 dockerd option 이 어떤지 보겠습니다.

$ sudo docker system info

 

Storage Driver: overlay2

Insecure Registries:

  docker-registry:5000

  oreo:5000

  127.0.0.0/8

 

Insecure Registries 의 oreo:5000 을 제거하고 싶다면 다음과 같이 daemon.json 에서 변경하면 됩니다.

{

    "storage-driver": "overlay2",

    "insecure-registries": ["docker-registry:5000"]

}

 

그리고 SIGHUP 을 보내서 option reload 를 하면 새로운 옵션이 적용됩니다.

$ sudo kill -SIGHUP $(pidof dockerd)

 

dockerd 옵션에 oreo:5000 이 Insecure Registries 에서 빠져 있는 것을 볼 수 있습니다.

$ sudo docker system info

 

Storage Driver: overlay2

Insecure Registries:

  docker-registry:5000

  127.0.0.0/8

 

 

Posted by Kubernetes Korea co-leader seungkyua@gmail.com
## https://github.com/kubernetes/kubernetes/tree/master/examples/volumes/cephfs

[ ceph-admin 노드에서 ]
$ ssh ceph@192.168.30.22

## kubes Pool 생성
$ ceph osd pool create kubes 128

## kube user 생성
$ ceph auth get-or-create client.kube mon 'allow r' \
osd 'allow class-read object_prefix rbd_children, allow rwx pool=kubes'


[client.kube]
    key = AQCt/BpYigJ7MRAA5vy+cl39EsKpY3C+tXEGrA==

## kube user 에 대한 secret key 생성 및 조회
$ ceph auth get-or-create client.kube
AQCt/BpYigJ7MRAA5vy+cl39EsKpY3C+tXEGrA==


## kube-node01, kube-node02 서버에 kube key 와 ceph.conf 추가
$ ssh stack@192.168.30.15 sudo mkdir -p /etc/ceph
$ ceph auth get-or-create client.kube | ssh stack@192.168.30.15 sudo tee /etc/ceph/ceph.client.kube.keyring
$ cat /etc/ceph/ceph.conf | ssh stack@192.168.30.15 sudo tee /etc/ceph/ceph.conf
$ ssh stack@192.168.30.15 sudo chown -R stack.stack /etc/ceph

$ ssh stack@192.168.30.16 sudo mkdir -p /etc/ceph
$ ceph auth get-or-create client.kube | ssh stack@192.168.30.16 sudo tee /etc/ceph/ceph.client.kube.keyring
$ cat /etc/ceph/ceph.conf | ssh stack@192.168.30.16 sudo tee /etc/ceph/ceph.conf
$ ssh stack@192.168.30.16 sudo chown -R stack.stack /etc/ceph



[ kube-node01, kube-node02 에 접속 ]
## ceph rbd client (ceph-common) 와 ceph fs client 설치 (ceph-fs-common)
$ sudo apt-get -y install ceph-common ceph-fs-common



########################################
## ceph rbd 로 연결하는 방식
########################################

## https://github.com/kubernetes/kubernetes/tree/master/examples/volumes/rbd
## https://github.com/ceph/ceph-docker/tree/master/examples/kubernetes

[ ceph-admin 노드에서 ]

## kube keyring 파일 넣기
$ sudo vi /etc/ceph/ceph.client.kube.keyring
[client.kube]
    key = AQCt/BpYigJ7MRAA5vy+cl39EsKpY3C+tXEGrA==


## rbd 이미지 생성
## http://karan-mj.blogspot.kr/2013/12/ceph-installation-part-3.html

$ rbd create ceph-rbd-test --pool kubes --name client.kube --size 1G -k /etc/ceph/ceph.client.kube.keyring

$ rbd list --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring
$ rbd -p kubes ls


## Jewel 의 새기능은 현재 대부분의 OS 에서 mount 문제가 있어 image 기능을 제거 해야 함
$ rbd feature disable ceph-rbd-test fast-diff --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring
$ rbd feature disable ceph-rbd-test deep-flatten --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring
$ rbd feature disable ceph-rbd-test object-map --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring
$ rbd feature disable ceph-rbd-test exclusive-lock --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring

$ rbd info ceph-rbd-test --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring
$ rbd --image ceph-rbd-test -p kubes info

$ rbd remove ceph-rbd-test --pool kubes --name client.kube -k /etc/ceph/ceph.client.kube.keyring


## secret yaml 을 만들기 위해 key 를 base64 로 인코딩 함
$ grep key /etc/ceph/ceph.client.kube.keyring |awk '{printf "%s", $NF}'|base64
QVFDdC9CcFlpZ0o3TVJBQTV2eStjbDM5RXNLcFkzQyt0WEVHckE9PQ==




[ kube-deploy 접속 ]

## secret key 를 pod 로 생성하여 접속
$ vi ~/kube/ceph-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: ceph-secret
data:
  key: QVFDdC9CcFlpZ0o3TVJBQTV2eStjbDM5RXNLcFkzQyt0WEVHckE9PQ==

$ scp ~/kube/ceph-secret.yaml kube-master01:~/kube/.
$ ssh kube-master01 "kubectl create -f ~/kube/ceph-secret.yaml"
$ kubectl -s http://kube-master01:8080 get secrets


## rbd-with-secret pod 생성해서 rbd 활용
$ vi ~/kube/rbd-with-secret.yml
apiVersion: v1
kind: Pod
metadata:
  name: rbd2
spec:
  containers:
  - image: gcr.io/google_containers/busybox
    command:
    - sleep
    - "3600"
    imagePullPolicy: IfNotPresent
    name: rbd-rw-busybox
    volumeMounts:
    - mountPath: "/mnt/rbd"
      name: rbdpd
  volumes:
  - name: rbdpd
    rbd:
      monitors:
      - 192.168.30.23:6789
      - 192.168.30.24:6789
      - 192.168.30.25:6789
      pool: kubes
      image: ceph-rbd-test
      user: kube
      keyring: /etc/ceph/ceph.client.kube.keyring
      secretRef:
        name: ceph-secret
      fsType: ext4
      readOnly: false


$ scp ~/kube/rbd-with-secret.yml kube-master01:~/kube/.
$ ssh kube-master01 "kubectl create -f ~/kube/rbd-with-secret.yml"
$ kubectl -s http://kube-master01:8080 get pods




## rbd 연결 확인
$ kubectl -s http://kube-master01:8080 describe pods rbd2
$ kubectl -s http://kube-master01:8080 exec -it rbd2 -- df -h



[ kube-node02 접속하여 ]

$ docker ps
$ docker inspect --format '{{ .Mounts }}' 4c4070a1393b

## 혹은
$ mount |grep kub
/dev/rbd0 on /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd/kubes-image-ceph-rbd-test type ext4 (rw,relatime,stripe=1024,data=ordered)
/dev/rbd0 on /var/lib/kubelet/pods/061973fc-a265-11e6-940f-5cb9018c67dc/volumes/kubernetes.io~rbd/rbdpd type ext4 (rw,relatime,stripe=1024,data=ordered)




[ kube-deploy 접속해서 ]

## secret key pod 를 사용하지 않고 keyring 으로만 rbd pod 생성
$ vi ~/kube/rbd.yml
apiVersion: v1
kind: Pod
metadata:
  name: rbd
spec:
  containers:
  - image: gcr.io/google_containers/busybox
    command:
    - sleep
    - "3600"
    imagePullPolicy: IfNotPresent
    name: rbd-rw-busybox
    volumeMounts:
    - mountPath: "/mnt/rbd"
      name: rbdpd
  volumes:
  - name: rbdpd
    rbd:
      monitors:
      - 192.168.30.23:6789
      - 192.168.30.24:6789
      - 192.168.30.25:6789
      pool: kubes
      image: ceph-rbd-test
      user: kube
      keyring: /etc/ceph/ceph.client.kube.keyring
      fsType: ext4
      readOnly: false


$ scp ~/kube/rbd.yml kube-master01:~/kube/.
$ ssh kube-master01 "kubectl create -f ~/kube/rbd.yml"
$ kubectl -s http://kube-master01:8080 get pods

## rbd 연결 확인

$ kubectl -s http://kube-master01:8080 exec -it rbd -- df -h 


Posted by Kubernetes Korea co-leader seungkyua@gmail.com

kubernetes 의 Authentication 과 Authorization 활용


[ Authentication ]

  • client-ca-file 로 접속
  • static password file 사용
  • static token file 사용
  • OpenStack Keystone 사용
먼저 client-ca-file 은 Authorization 을 사용할 수 없으니 제외

static password 도 잘됨, 그러나 Authorization 을 연동해 보지는 않았음.

OpenStack Keystone 연계는 아직 알파버전이고 수정이 자주 일어나서 아직 소스까지 볼 단계는 아니라 생략.

static token 방식은 Authorization 과도 잘 연동되므로 이걸 활용



## uuid generate

$ cat /proc/sys/kernel/random/uuid



## {{uuid}} 는 위에서 제너레이션 된 값으로 대치

$ sudo vi /etc/default/kube-token

{{uuid}},admin,1

{{uuid}},ahnsk,2,"tfabric,group1"

{{uuid}},stack,3,tfabric



## api 서버에 token file 옵션 추가

$ sudo chown stack.root /etc/default/kube-token

--token-auth-file=/etc/default/kube-token \


$ sudo systemctl restart kube-apiserver.service


$ kubectl -s https://kube-master01:6443 --token={{uuid}} get node





[ Authorization ]

  • ABAC Mode
  • RBAC Mode
RBAC 는 아직 알파라 베타인 ABAC 를 활용

## 전체 admin : admin,   tfabric admin : ahnsk,      tfabric readOnly user : stack
kubectl 이 api version 을 체크하기 때문에 무조건 nonResourcePath 도 all 로 지정해야 함

$ sudo vi /etc/default/kube-rbac.json
{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"system:serviceaccount:kube-system:default","namespace":"*","resource":"*","apiGroup":"*", "nonResourcePath": "*"}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"admin", "namespace": "*", "resource": "*", "apiGroup": "*", "nonResourcePath": "*" }}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"ahnsk", "namespace": "tfabric", "resource": "*", "apiGroup": "*", "nonResourcePath": "*" }}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"stack", "namespace": "tfabric", "resource": "*", "apiGroup": "*", "readonly": true, "nonResourcePath": "*"}}



kube-system 이 kube-apiserver 에 접근하기 위해서는 1라인이 반드시 있어야 함





$ sudo vi /etc/default/kube-apiserver

--authorization-mode=ABAC \

--authorization-policy-file=/etc/default/kube-rbac.json \


$ sudo systemctl restart kube-apiserver.service




$ cd ~/kube

$ vi busybox-tfabric.yaml

apiVersion: v1

kind: Pod

metadata:

  name: busybox

  namespace: tfabric

spec:

  containers:

  - image: gcr.io/google_containers/busybox

    command:

      - sleep

      - "3600"

    imagePullPolicy: IfNotPresent

    name: busybox

  restartPolicy: Always



$ kubectl -s https://kube-master01:6443 --token={{uuid}} --v=8 version


token 지정을 매번 하기 귀찮으니 config context 를 활용하는 것이 좋음.

이건 다음에....















Posted by Kubernetes Korea co-leader seungkyua@gmail.com

## https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt


보통 cpu.cfs_period_us 는 cpu.cfs_quota 와 같이 사용되어 계산되는데 계산법은 다음과 같다. (단위 : microsecond)

1 CPU 사용률 = cpu.cfs_quota_us / cpu.cfs_period_us * 100

cgoup에서 1cpu 의 20% 를 사용한다면 아래와 세팅하면 된다.

# echo 10000 > cpu.cfs_quota_us /* quota = 10ms */
# echo 50000 > cpu.cfs_period_us /* period = 50ms */


Kubernetes 에선 limit resources 로 cpu 와 memory 를 다음과 같이 할당하다.
--limits='cpu=200m,memory=512Mi'

cpu 는 200m 로 할당하였는데 이는 cpu.cfs_period_us 값으로 1 sec (1000 millisecond) 기준당 0.2 sec 를 할당한다는 의미이다. (최대값은 1sec == 1000000 microsecond 이므로 1000000 임)
보통 cgroup의 디폴트 값은 cpu.cfs_quota=-1 이므로 위의 같은 경우는 20% 의 cpu를 할당하는 의미이다. 




Posted by Kubernetes Korea co-leader seungkyua@gmail.com
###################################
## kube-dns (skydns) 설치, dashboard 설치
###################################
## 검색창에 [kubernetes] 을 넣고 검색을 하면 됨


$ cd ~/kube
$ export KUBE_ROOT=/home/ubuntu/go_workspace/src/k8s.io/kubernetes
$ export DNS_REPLICAS=1
$ export DNS_DOMAIN=cluster.local
$ export DNS_SERVER_IP=192.168.30.200

$ sed -e "s/\\\$DNS_REPLICAS/${DNS_REPLICAS}/g;\
s/\\\$DNS_DOMAIN/${DNS_DOMAIN}/g;" \
"${KUBE_ROOT}/cluster/addons/dns/skydns-rc.yaml.sed" > skydns-rc.yaml

## skydns-rc.yaml 에 kube-master-url 추가
$ vi skydns-rc.yaml
81         - --kube-master-url=http://192.168.30.13:8080


$ sed -e "s/\\\$DNS_SERVER_IP/${DNS_SERVER_IP}/g" \
"${KUBE_ROOT}/cluster/addons/dns/skydns-svc.yaml.sed" > skydns-svc.yaml

$ cat <<EOF >namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: kube-system
EOF


$ cp ~/go_workspace/src/k8s.io/kubernetes/cluster/addons/dashboard/dashboard-controller.yaml ~/kube/.
$ cp ~/go_workspace/src/k8s.io/kubernetes/cluster/addons/dashboard/dashboard-service.yaml ~/kube/.


## kube-master01 에 복사
$ ssh kube-master01 "mkdir -p kube"
$ scp ~/kube/skydns-rc.yaml kube-master01:~/kube/.
$ scp ~/kube/skydns-svc.yaml kube-master01:~/kube/.
$ scp ~/kube/namespace.yaml kube-master01:~/kube/.

$ scp ~/kube/dashboard-controller.yaml kube-master01:~/kube/.
$ scp ~/kube/dashboard-service.yaml kube-master01:~/kube/.

$ ssh kube-master01 "kubectl create -f ~/kube/namespace.yaml"
$ ssh kube-master01 "kubectl --namespace=kube-system create -f ~/kube/skydns-rc.yaml"
$ ssh kube-master01 "kubectl --namespace=kube-system create -f ~/kube/skydns-svc.yaml"



## dashboard 설치
$ cd ~/kube

## master api 에 접속할 수 있게 정보를 추가
$ vi kubernetes-dashboard.yaml
47         - --apiserver-host=http://192.168.30.13:8080

$ scp ~/kube/kubernetes-dashboard.yaml kube-master01:~/kube/.
$ ssh kube-master01 "kubectl create -f ~/kube/kubernetes-dashboard.yaml" 



## skydns 설치 확인
$ vi ~/kube/busybox.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: gcr.io/google_containers/busybox
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always


$ scp ~/kube/busybox.yaml kube-master01:~/kube/.
$ ssh kube-master01 "kubectl create -f ~/kube/busybox.yaml"


## -s 옵션으로 api 서버 지정 가능
$ kubectl -s http://kube-master01:8080 get pods --all-namespaces -o wide
$ kubectl -s http://kube-master01:8080 describe pod kube-dns-v19-d1tse --namespace=kube-system

$ kubectl -s http://kube-master01:8080 get pods busybox -o wide
$ kubectl -s http://kube-master01:8080 exec busybox -- nslookup kubernetes.default




###################################
## kubectl 사용법
###################################

## nginx pod 생성  (replication controller 대신 replica set 이 생겼음)
$ kubectl -s http://kube-master01:8080 run nginx --image=nginx (--replicas=2) --port=80
$ kubectl -s http://kube-master01:8080 get pods
$ kubectl -s http://kube-master01:8080 get pods -o wide
$ kubectl -s http://kube-master01:8080 get pod -l run=nginx


## nginx scaling
$ kubectl -s http://kube-master01:8080 scale deployment/nginx --replicas=1

## nginx auto scaling
$ kubectl -s http://kube-master01:8080 autoscale deployment/nginx --min=1 --max=3


## nginx rc 조회
$ kubectl -s http://kube-master01:8080 get rs



## 서비스 멈춤없이 이미지 버전 올리기 (edit 로 파일 수정하면 됨)
$ kubectl -s http://kube-master01:8080 edit deployment/nginx


## nginx service 생성 (port : host에 노출되는 port,   target-port : docker 내부에서 뜨는 port)
$ kubectl -s http://kube-master01:8080 expose deployment nginx --port=8080 --target-port=80 (--type=LoadBalancer) --name=nginx
$ kubectl -s http://kube-master01:8080 get services


## nginx pod 삭제
$ kubectl -s http://kube-master01:8080 get deployment (nginx)
$ kubectl -s http://kube-master01:8080 delete deployment nginx

## nginx service 삭제
$ kubectl -s http://kube-master01:8080 delete service nginx

## nginx pod, service 동시 삭제
$ kubectl -s http://kube-master01:8080 delete deployment,service nginx
$ kubectl -s http://kube-master01:8080 delete deployments/nginx services/nginx

## nginx pod, service 동시 삭제 (label 활용 : -l or --selector)
$ kubectl -s http://kube-master01:8080 delete deployment,services -l app=nginx


## nginx replication controller 삭제 (디폴트는 pod 도 삭제됨, --cascade=false : rc 만 삭제) 
$ kubectl -s http://kube-master01:8080 delete rc nginx-rc
$ kubectl -s http://kube-master01:8080 delete rc nginx-rc --cascade=false


## nginx 안으로 들어가기
$ kubectl -s http://kube-master01:8080 exec -it nginx-3449338310-sl1ou -- /bin/bash

## pod 내의 컨테이너 로그 보기
$ kubectl -s http://kube-master01:8080 logs -f nginx-3449338310-sl1ou
$ kubectl -s http://kube-master01:8080 logs --tail=20 nginx-3449338310-sl1ou
$ kubectl -s http://kube-master01:8080 logs --since=1m nginx-3449338310-sl1ou




## Guestbook (redis-master, redis-slave, frontend) 샘플
$ ~/go_workspace/src/k8s.io/kubernetes
$ kubectl -s http://kube-master01:8080 create -f examples/guestbook/


## Service  frontend 타입을 ClusterIP 에서 NodePort 로 변경
$ kubectl -s http://kube-master01:8080 edit services/frontend
28   type: NodePort


## json format 으로 output 보기
$ kubectl -s http://kube-master01:8080 get svc frontend -o json
$ kubectl -s http://kube-master01:8080 get svc frontend -o "jsonpath={.spec.ports[0].nodePort}{"\n"}"


## Guestbook 삭제
$ kubectl -s http://kube-master01:8080 delete deployments,services -l "app in (redis, guestbook)"



## 명령어로 pod 생성하는 방법
$ kubectl -s http://kube-master01:8080 run frontend --image=gcr.io/google-samples/gb-frontend:v4 \
--env="GET_HOSTS_FROM=dns" --port=80 --replicas=3 \
--limits="cpu=100m,memory=100Mi" \
--labels="app=guestbook,tier=frontend"

## Service frontend 를 명령어로 NodePort 타입으로 생성
$ kubectl -s http://kube-master01:8080 expose deployment frontend \
--port=80 --target-port=80 --name=frontend --type=NodePort \
--labels=app=guestbook,tier=frontend --selector=app=guestbook,tier=frontend





###################################
## glusterFS 설치 및 Kubernetes 에서 활용
###################################
## glusterFS 설치

## gluster01 과 gluster02 모두 수행
# vi /etc/hosts
192.168.30.15   kube-node01 gluster01
192.168.30.16   kube-node02 gluster02

# mkfs.xfs -f /dev/sdb
# mkdir -p /data/gluster/brick1 && chmod -R 777 /data
# echo '/dev/sdb /data/gluster/brick1 xfs defaults 1 2' >> /etc/fstab
# mount -a && mount

# apt-get install -y glusterfs-server glusterfs-client
# service glusterfs-server start


## gluster01 에서
# gluster peer probe gluster02

## gluster02 에서
# gluster peer probe gluster01


## gluster01 과 gluster02 모두 수행
# mkdir -p /data/gluster/brick1/gv0 && chmod -R 777 /data
# mkdir -p /data/gluster/brick1/gv1 && chmod -R 777 /data/gluster/brick1/gv1


## gluster01 에서 수행 (gv1 은 pod 에서 glusterfs 연결할 때 사용할 디스크)
# gluster volume create gv0 replica 2 gluster01:/data/gluster/brick1/gv0 gluster02:/data/gluster/brick1/gv0
# gluster volume start gv0

# gluster volume create gv1 replica 2 gluster01:/data/gluster/brick1/gv1 gluster02:/data/gluster/brick1/gv1
# gluster volume start gv1


## gluster01 과 gluster02 모두 수행
# mkdir -p /ext && chmod 777 -R /ext
# mount -t glusterfs gluster01:/gv0 /ext



## kubernetes 와 glusterFS 테스트
$ cd ~/go_workspace/src/k8s.io/kubernetes
$ cp examples/volumes/glusterfs/*.json ~/kube && cd ~/kube

$ vi glusterfs-endpoints.json
11           "ip": "192.168.30.15"
23           "ip": "192.168.30.16"

$ vi glusterfs-pod.json
11                 "image": "nginx"
25                     "path": "gv1",
26                     "readOnly": false


$ ssh kube-master01 "mkdir -p kube"
$ scp ~/kube/glusterfs-endpoints.json kube-master01:~/kube/.
$ scp ~/kube/glusterfs-service.json kube-master01:~/kube/.
$ scp ~/kube/glusterfs-pod.json kube-master01:~/kube/.

$ ssh kube-master01 "kubectl create -f ~/kube/glusterfs-endpoints.json"
$ ssh kube-master01 "kubectl create -f ~/kube/glusterfs-service.json"
$ ssh kube-master01 "kubectl create -f ~/kube/glusterfs-pod.json"


$ kubectl -s http://kube-master01:8080 get pods glusterfs -o jsonpath='{.status.hostIP}{"\n"}'

$ ssh kube-node01 "mount | grep gv1"






Posted by Kubernetes Korea co-leader seungkyua@gmail.com

DPDK in docker

Container 2016.07.12 01:17

[ DPDK install on Host ]

## http://dpdk.org/doc/guides/linux_gsg/build_dpdk.html

## http://dannykim.me/danny/openflow/86417

## https://github.com/bisdn/dpdk-dev/blob/master/tools/setup.sh


## High Precision Timer(HPET) set 확인

$ grep hpet /proc/timer_list


## hugepage 세팅

# echo 256 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages


## Using Hugepages with the DPDK

# mkdir -p /mnt/huge

# mount -t hugetlbfs nodev /mnt/huge

# vi /etc/fstab

nodev /mnt/huge hugetlbfs defaults 0 0


## dpdk download

## http://dpdk.org/browse/dpdk/

$ mkdir -p dpdk && cd dpdk

$ curl -OL http://dpdk.org/browse/dpdk/snapshot/dpdk-16.04.tar.gz


$ tar xzvf dpdk-16.04.tar.gz

$ cd dpdk-16.04


## Installation of DPDK Target Environments

$ sudo yum update

$ sudo yum install -y sudo gcc libhugetlbfs-utils libpcap-devel \

kernel kernel-devel kernel-headers


$ make config T=x86_64-native-linuxapp-gcc

$ make


## Loading Modules to Enable Userspace IO for DPDK

$ sudo modprobe uio_pci_generic

$ sudo insmod build/kmod/igb_uio.ko

$ lsmod | grep igb_uio


## Loading VFIO Module

$ sudo modprobe vfio-pci


$ vi ~/.bashrc

export RTE_SDK=$HOME/dpdk/dpdk-16.04

export RTE_TARGET=build


$ source ~/.bashrc

$ env | grep RTE


$ cd examples/helloworld

$ make

$ cd build/app

$ sudo ./helloworld -c 3 -n 3



[ DPDK install in docker ]

## Host 에 미리 세팅

## hugepage 세팅

# echo 256 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages


## Using Hugepages with the DPDK

# mkdir -p /mnt/huge

# mount -t hugetlbfs nodev /mnt/huge

# vi /etc/fstab

nodev /mnt/huge hugetlbfs defaults 0 0





$ mkdir -p github

$ cd github


$ git clone https://github.com/seungkyua/dpdk-docker-helloworld.git

$ cd dpdk-docker-helloworld

$ cp -R /lib/modules .

$ cp -R /usr/src/kernels/ .





$ vi Dockerfile

FROM centos:7.2.1511

MAINTAINER Seungkyu Ahn <seungkyua@gmail.com>

ENV REFRESHED_AT 2016-07-13


LABEL docker run --rm -it --privileged \

-v /sys/bus/pci/devices:/sys/bus/pci/devices \

-v /sys/kernel/mm/hugepages:/sys/kernel/mm/hugepages \

-v /sys/devices/system/node:/sys/devices/system/node \

-v /dev:/dev \

-v /mnt/huge:/mnt/huge \

--name dpdk-docker dpdk-docker-helloworld


ENV DPDK_VERSION=16.04

ENV RTE_SDK=/root/dpdk-${DPDK_VERSION}

ENV RTE_TARGET=build


USER root


# yum update

RUN yum -y update

RUN yum install -y deltarpm gcc make libhugetlbfs-utils libpcap-devel; yum clean all

RUN yum install -y kernel kernel-devel kernel-headers; yum clean all


# dpdk download

COPY modules /lib/modules

COPY kernels /usr/src/kernels

WORKDIR /root

RUN curl -OL http://dpdk.org/browse/dpdk/snapshot/dpdk-${DPDK_VERSION}.tar.gz

RUN tar xzf dpdk-${DPDK_VERSION}.tar.gz

WORKDIR /root/dpdk-${DPDK_VERSION}

RUN make config T=x86_64-native-linuxapp-gcc

RUN make


# Loading Modules to Enable Userspace IO for DPDK

RUN modprobe uio_pci_generic

RUN modprobe vfio-pci


WORKDIR examples/helloworld

RUN make

WORKDIR build/app

ENTRYPOINT [ "./helloworld" ]

CMD [ "-c", " 3", "-n", "3" ]






$ docker build -t dpdk-docker-helloworld .


$ docker run --rm -it --privileged \

-v /sys/bus/pci/devices:/sys/bus/pci/devices \

-v /sys/kernel/mm/hugepages:/sys/kernel/mm/hugepages \

-v /sys/devices/system/node:/sys/devices/system/node \

-v /dev:/dev \

-v /mnt/huge:/mnt/huge \

--name dpdk-docker dpdk-docker-helloworld









## 에러 날 때 패키지 찾아서 설치

yum provides '*/applydeltarpm'

yum install deltarpm


## 아래 패키지 에러 날 때 해결책

Rpmdb checksum is invalid: dCDPT(pkg checksums): perl-HTTP-Tiny.noarch 0:0.033-3.el7 - u


$ sudo yum provides '*/perl-HTTP-Tiny'


















Posted by Kubernetes Korea co-leader seungkyua@gmail.com