https://github.com/seungkyua/my-kubespray
my-kubespray
You can easily add your custom logic without modifying kubespray.
The my-kubespray has serveral parameters to override and can deploy ceph rbd provisioner into namespace.
Prerequisites
All servers must have the same user, and the user must set root permissions to NOPASSWD in sudo.
In the deployment node (or master 1 node), you need to enable ssh access to all other servers (including localhost) without a password to the corresponding account.
Register all cluster servers in the /etc/hosts file.
Time synchronization between servers with ntp.
Enable network restart with
systemctl restart network.service
.Disable firewall such as selinux or ufw.
swapoff.
Install python 2.7.x in ansible runner node.
Quick Start
To deploy the cluster you can use :
# Download kubespray and my-kubespray
$ git clone https://github.com/kubernetes-sigs/kubespray.git
$ git clone https://github.com/seungkyua/my-kubespray.git
# Change directory into my-kubespray
$ cd my-kubespray
# Install dependencies from ``requirements.txt``
$ sudo pip install -r ../kubespray/requirements.txt
# Copy ``inventory/k2-seungkyua`` as ``inventory/mycluster``
$ cp -rfp inventory/k2-seungkyua inventory/mycluster
# Update Ansible inventory file
$ vi inventory/mycluster/hosts.ini
[all]
k2-master01 ip=192.168.30.151
k2-master02 ip=192.168.30.152
k2-master03 ip=192.168.30.153
k2-ctrl01 ip=192.168.30.154
k2-ctrl02 ip=192.168.30.155
k2-ctrl03 ip=192.168.30.156
k2-cn01 ip=192.168.30.157
[etcd]
k2-master01
k2-master02
k2-master03
[kube-master]
k2-master01
k2-master02
k2-master03
[kube-node]
k2-ctrl01
k2-ctrl02
k2-ctrl03
k2-cn01
[k8s-cluster:children]
kube-node
kube-master
# Review and change parameters under ``inventory/mycluster/group_vars/k8s-cluster.yml``
$ cat inventory/mycluster/group_vars/k8s-cluster.yml
populate_inventory_to_hosts_file: false
override_system_hostname: false
helm_enabled: true
etcd_memory_limit: 8192M
kubeconfig_localhost: true
kubectl_localhost: true
ipip_mode: Never
calico_ip_auto_method: "can-reach=8.8.8.8"
kubeadm_enabled: true
docker_insecure_registries:
- seungkyua:5000
dashboard_enabled: true
local_volume_provisioner_enabled: true
ingress_nginx_enabled: true
ingress_nginx_host_network: true
ingress_nginx_nodeselector:
node-role.kubernetes.io/ingress: true
ceph_version: mimic
storageclass_name: rbd
monitors: 192.168.30.23:6789,192.168.30.24:6789,192.168.30.25:6789
admin_token: QBTEBmRVVlh5UmZoTlJBQTMyZTh6Qk5uajV1VElrMDJEbWFwWmc9WA==
user_secret_namespace: default
pool_name: kubes
user_id: kube
user_token: QEFESG9CcFlpZ0o3TVJBQTV2eStjbDM5RXNLcFkzQyt0WEVHckE9WA==
# Run Ansible Playbook to deploy kubernetes cluster
$ ansible-playbook -b -f 30 -i inventory/mycluster/hosts.ini ../kubespray/cluster.yml
# Run Ansible Playbook to deploy ceph rbd provisioner
$ ansible-playbook -b -f 30 -i inventory/mycluster/hosts.ini storage.yml
Miscellaneous
After installation, you can find the artifacts which are kubectl
and admin.conf
in inventory/mycluster/artifacts directorin on deploy node(ansible runner).
$ ls -al inventory/mycluster/artifacts
total 173620
drwxr-x--- 2 seungkyua seungkyua 4096 Feb 22 05:25 .
drwxrwxr-x 4 seungkyua seungkyua 4096 Feb 22 02:38 ..
-rw-r----- 1 seungkyua seungkyua 5449 Feb 22 05:25 admin.conf
-rwxr-xr-x 1 seungkyua seungkyua 177766296 Feb 22 05:25 kubectl
-rwxr-xr-x 1 seungkyua seungkyua 65 Feb 22 05:25 kubectl.sh
If desired, copy admin.conf
to ~/.kube/config
and kubectl
to /usr/local/bin/kubectl
$ mkdir -p ~/.kube
$ cp inventory/mycluster/artifacts/admin.conf ~/.kube/config
$ sudo cp inventory/mycluster/artifacts/kubectl /usr/local/bin/kubectl
Kubernetes Node 가 NotReady 인 경우 해결을 위해서 제일 먼저 해당 노드의 syslog 를 확인 하는 것이 좋다.
kubernetes 에서 ceph rbd provisioner 활용 방법입니다.
CVE-2018-1002105: proxy request handling in kube-apiserver can leave vulnerable TCP connections
[ Enable API Aggregation ]
[ Metrics API 서버 설치 ]
[ 취약점 확인 ]
[ work around 로 해결하는 방법 ]
1. kube-apiserver 옵션에 --anonymous-auth=false 를 넣는 방법 (default 는 true 임)
2. 아래 clusterrolebinding 의 system:basic-user 와 system:discovery 에 들어있는 system:unauthenticated Group 을 삭제
그리고 clusterrole 의 system:basic-user 와 system:discovery 에 세팅된 auto repaired 를 false 로 변경
처음에는 대충 볼려고 했는데 내용이 좋아서 끝까지 집중해서 봤네요. 전체 요약해 봤습니다.
AWS re:Invent 2018 - Keynote with Werner Vogels
2004-12-12 Amazon Oracle 장애 Lessons learned
- SOA (Service Oriented Architecture) 로 서비스를 Flexible 하게 구성하였으나 Database Oracle의 로깅 코드 버그로 장애 발생, 12시간 동안 크리스마스 시즌 주문을 받지 못함
- Oracle 은 SPOF (Single Point of Failure) 이고, Black Box 라 scale, reliability 를 높일 수 있는 방법이 없었음.
- 1차 변경: Sharding 을 통해 데이터를 분산하여 performance 와 reliability 를 높힘
- Reducing your blast radius : 장애가 발생하면 모든 면에서 영향을 최소화 해라.
- 2차 변경: Cell based architectures 로 변경 (cell 단위 독립적으로 구성, 한 곳에 문제가 생겨도 영향도를 최소화함)
- Cell based Architectures: AWS 는 모두 Cell 단위로 구성되어 있음 (심지어 AZ (Availability Zone) 안에서도 Cell 단위로 구성되어 영향도 최소화 했음) → 근데 왜 한국 Region 은 장애가....???)
- AZ 서비스에서 DynamoDB 는 Cell 단위로 구성되어 DB 장애가 일어나도 최소화 됨.
- Relational databases: not designed for the cloud (Sharding 을 하더라도 결국 Database 로 인해 서비스가 종속되게 됨)
- Decompose the system into fundamental building blocks -> 그래야 multi tenant, fault tolerant, self healing 서비스를 구현 가능
Amazon Aurora: Scaled-out distributed architecture (Database aware storage service)
- AWS 에서 가장 빠르게 성장하는 서비스 중에 하나임
- AZ 단위로 분산이 가능하고 Cell based Architectures 로 mission critical 한 서비스 구현이 가능
- On quorums and failures 를 적용하였기에 이런 서비스를 구현 가능했음 (AZ 단위로 분산)
- AZ 마다 2개의 인스턴스를 띄우고 AZ 가 하나 죽고 다른 AZ 에서 인스턴스 1개가 죽어도 read 는 가능, 하지만 쓰기는 못함
- 10G 링크로 되어 있어 click 만으로 빠르게 복구되어 MTBF(Mean time between failures) 와 MTTR(Mean time to repair) 를 줄일 수 있음
- Aurora 는 log 만 복사하기 때문에 복제 및 복구가 빠름 (storage node 에서 복사가 이루어짐)
- AWS Aurora 는 Cloud native database as a foundation for innovation 이다.
ASW 유형별 Databases 종류
- Relational - Amazon Aurora, Amazon RDS
- Key Value - Amazon DynamoDB
- Document - Amazon DynamoDB
- In-Memory - Amazon ElasticCache
- Graph - Amazon Neptune
- Time-Series (New) - Amazon Timestream
- Ledger (New) - Amazon QLDB
How to migrate Oracle to DynamoDB
- Amazon item master service : sharding 된 Oracle databases 24개로 구성 (6,000억 레코드가 있으며, 50억 update 가 1day 에 일어남), 1년에 30~40% 정도 증가함
- Oracle 을 DynamoDB 로 변경 (schema 변경 및 Database Migration 진행)
- Database Migration Service (6,000억 레코드 migration을 live 로 진행)
AWS S3 - Mai-lan Tomsen Bukovec (Vice President, Amazon S3 and Glacier)
- 전체 10 Trillions objects 가 저장되어 있음
- 한개의 리전에서 최대 1초에 60 Terabytes 를 관리함
- 초기에 8개의 microservices 로 구성됨, 현재는 235개 이상의 분산 mircroservices 로 구성됨
AWS Redshift
- 2018년 11월 1일 아주 큰 Oracle data warehouse 를 Redshift 로 옮김 (올해 가장 기쁜 순간)
- 성능 향상을 이룸
- 지난주 (11월 4째주) Redshift concurrency scaling 기능 새로 소개
AWS Lambda
고객들이 serverless 로 가려고 하는가?
- No Infrastructure provisioning, no management
- Automatic scaling
- Highly available and secure
- Pay for value
- 2014년 Preview 공개 후 2015년 GA 로 오픈
- Worker: 고객의 function 코드가 로딩되고 실행되는 환경
Firecracker (New): serverless computing을 위한 안전하고 빠른 microVMs
- KVM hardware virtualizations
- 실행되는데 125ms 소요 (1개의 호스트에서 1초에 150 microVMs 실행 가능)
- 1개의 microVM 당 5MB 메모리 사용 (1개의 호스트에 수천개의 microVM 을 띄울 수 있음)
- Lambda runtime 환경
AWS 의 기능과 서비스의 95% 고객의 피드백으로 부터 개발된 것임
AWS Toolkits 새로운 IDEs
- PyCharm
- IntelliJ
- VS Code
AWS Lambda 신규 기능
- 사실상 모든 언어 지원 가능해 졌음
- 기존 지원 언어: NodeJS, JavaScript, Python, Java, .Net, Go
- 신규 지원 언어: Ruby
- Lambda 에 Custom Runtime 적용 가능 (Ruby 도 그렇게 구현한 것으로 C++, Erlang 등 다른 언어 들도 적용하여 쓸 수 있음)
- Lambda Layers: Lambda 를 라이브러리 형태의 함수로 만들어 재사용 가능
- Nested Applications using Serverless Application Repository (New)
AWS Step Functions
- 기존에는 workflow 를 Sequential steps, Parallel steps, Branching steps 로 지원
- Step Functions service Integrations (New): Step Functions 을 AWS 의 8개 서비스와 함께 쓸 수 있게 지원
- Step Functions 함께 쓸 수 있는 AWS 8개 서비스: Batch, ECS, Fargate(컨테이너 배포 서비스), Glue(ETL서비스), DynamoDB, SNS, SQS, SageMaker
AWS API Gateway 에서 WebSocket 지원
- WebSocket 을 통해 Stateful 지원 가능
AWS ALB 에서 Lambda 지원
- ALB (Amazon Load Balancer) 뒷단에 Lambda 를 바로 연결 가능
Amazon Managed Streaming for Kafka
- Fully managed and highly-available Apache Kafka service
AWS Well-Architected site
AWS Well-Architected Tool
- Measure and improve your architecture using AWS Well-Architectured best practices
이번에는 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
docker 이미지가 어떻게 구성되는지 이해하기 위해서는 Copy-on-write 정책을 이해해야 합니다.
아래와 같은 Dockerfile 이 있고 이를 이미지로 만들었을 때 이미지가 차지하는 스토리지 구성은 다음과 같습니다.
(아래 Dockerfile 은 sample 파일로 make tool 까지 있다고 가정합니다.)
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
이미지 출처: 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 하고 싶을 때 사용할 수 있기 때문에 가능하면 둘 다 사용하는 방법을 추천드립니다.
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 로 외부로 나가게 됩니다.