Docker 从入门到放弃

 

K8s

SVN

Subversion 官网html

http://subversion.tigris.org/前端

Openshift

案例新浪云SAE openshift自己是一种套件java

技术 描述
Kubernutes 管理容器的组件; 集群管理 %80内容
Apache 对外接口服务, 以及帐号的管理
Git 代码的管理, svn
Etd 非关系型数据库
docker 容器 hub.docker.com 国内的docker仓库, 时速云 hub.tenxcloud.com 阿里云的docker

默认密码是Asimovnode

隔离namespace的定义python

Namespace 系统调用参数 隔离内容
UTS CLONE_NEWUTS 主机名与域名
IPC CLONE_NEWIPC 信号量、消息队列和共享内存
PID CLONE_NEWPID 进程编号
Network CLONE_NEWNET 网络设备、网络栈、端口等等
Mount CLONE_NEWNS 挂载点(文件系统)
User CLONE_NEWUSER 用户和用户组

Docker官方提供的公共镜像仓库public registry http://registry.hub.docker.com/ 红帽官方提供的公共镜像仓库 http://registry.access.redhat.com/mysql

Doker

Docker被称为第三代Pass平台 DotCloud, 主要基于Pass平台为开发者.linux

Docker的核心技术cgroups. 将一组程序定义为一个group, 在这个group中, 有分配好的特定比例的cpu时间, io时间, 可用内存大小等. 最先是由google工程师提出. cgroups的重要概念是"子系统", 也就是资源控制器, 每一个子系统就是一个资源的分配器. 好比CPU子系统是控制CPU时间分配的. 首先须要挂载子系统, 而后才有control group. 笔记: http://blog.opskumu.com/docker.htmlios

容器技术

LXC是linux containers的简称, 是一种基于容器的操做系统层级的虚拟化技术. 借助于namespace的隔离机制和cgroup限额功能, LXC提供了一套统一的API和工具来创建和管理容器. Namespace: 命名空间, 至关于平行宇宙, 每一个命名空间相互隔离, 互不干扰 LXC: 提供一个共享kernel的OS级别的虚拟化方法, 在执行时不用重复加载kernel, 因为共享kernel, 会致使一些kernel参数没办法在某个特定容器内进行修改. AUFS: docker的文件系统, 是一个能透明覆盖一或多个现有文件系统的层状文件系统. 支持将不一样目录挂载到同一个虚拟文件系统下, 能够把不一样的目录联合在一块儿, 组成一个单一的目录. 这是一种虚拟的文件系统, 文件系统不须要格式化, 直接挂载便可. 支持写入复刻(copy on write). 差别存储, 最大化公用底层的文件系统. 容器不建议使用sshd服务. docker exec命令能够进入容器排查问题.nginx

Docker部署

baseurl = https://yum.dockerproject.org/repo/main/centos/7 gpgkey = https://yum.dockerproject.org/gpg

禁用firewalld, 启动iptables 查看docker的基本信息

docker info

查看docker版本

docker version

查看容器的日志

docker logs [containerID]

Docker配置参数

配置文件为/etc/sysconfig/docker OPTIONS用来控制Docker Daemon进程参数 -H 表示Docker Daemon绑定的地址, -H=unix:///var/run/docker.sock -H=tcp://0.0.0.0:2375 --registry-mirror 表示Docker Registry的镜像地址 --registry-mirror=http://4bc5abeb.m.daocloud.io --insecure-registry 表示(本地) 私有Docker Registry的地址. --insecure-registry ${privateRegistryHost}:5000 --selinux-enabled是否开启SELinux,默认开启 --selinux-enabled=true --bip 表示网桥docker0使用指定CIDR网络地址, --bip=172.17.42.1 -b 表示采用已经建立好的网桥, -b=xxx

OPTIONS=-H=unix:///var/run/docker.sock -H=tcp://0.0.0.0:2375 --registrymirror=http://4bc5abeb.m.daocloud.io --selinux-enabled=true

docker的日志默认放到/var/log/messages中 查找docker image

docker search java

docker run. docker run的命令结束了, container也就结束了

docker run [options] IMAGE[:TAG][Command][ARG...]
docker run -it java ps 

-d: 在后台执行 docker exec 能够进入到该容器中. 或者使用attach从新链接容器的会话. 若是是attach在退出的时候可能会将容器关闭 交互就使用 -i -t docker run 时 没有指定--name, namedaemon会自动生成随机字符串UUID docker基础命令, create只是建立可是不会运行

docker create/start/stop/pause/unpause

建立mysql容器

docker create --name mysqlsrv1 -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql
-e: 提供环境变量

而后对虚拟机访问3306端口就能够访问容器的mysql服务

docker exec -it mysqlsrv1 /bin/bash

查看环境变量, docker的配置文件通常使用环境变量来定义

docker exec mysqlsrv1 env

运行中止后的容器也会占用磁盘空间, 在一次性运行的容器后面添加 run -rm, 在执行后删除 将容器变成镜像

docker commit <container> [repo:tag]

docker能够在容器中额外挂载一些目录, 好比能够添加一些ping的工具

docker run --privileged -v /sbin:/mnt/sbin -v /bin:/mnt

Docker命令

coreOS开发, 于redhat合做, google提供Kubernetes管理工具. images至关于一个模板, 实质就是一个归档包tar. 分为readonly只读档和写的层. image生成容器. 一个镜像能够对应多个容器, 若是容器中须要进行写操做, 则须要从images中copy一个.

直接下载一个image

docker search rhel7
docker pull workstation.pod6.example.com:5000/library/rhel7

执行一个容器

docker run --help
docker run -itd 275be1d3d070 "/bin/bash"
-i: interactive
-t: tty
-d: 后台运行

修改docker image标签

docker tag docker.io/ubuntu ubuntu:mini

查看镜像的详细信息

docker inspect [id]

删除镜像

docker rmi [id]
	-f: 强制删除

根据本地镜像制做镜像文件

docker commit -m "Added a new file" -a "Docker Newbee" b1624e625c32 test
	-a: 做者信息
	-m: 提交信息
	-p: 提交时暂停容器运行

模板能够经过openvz的网站进行下载https://download.openvz.org/template/precreated/

cat centos-6-x86_64-minimal.tar.gz |docker import - centos6:latest

保存和导入镜像, 使用save和load

docker save -o ubuntu.tar test:latest 
docker load --input ubuntu.tar
docker load < ubuntu.tar

还可使用import导入容器

docker import 
cat test_for_run.tar| docker import - test/ubuntu:v1.0

docker既可使用docker load命令来导入镜像存储文件到本地镜像库, 也可使用docker import命令来导入一个容器快照到本地镜像仓库. 容器快照文件将丢弃全部的历史记录和元数据信息, 而镜像存储文件将保存完整记录, 体积比较庞大. 新建容器 docker run至关于docker create 而后在docker exec

docker create  
	-t: 提供一个伪终端pseudo-tty
	-i: 让容器保持打开状态
	-d: 守护状态运行Daemonized

查看容器日志

docker log <container id>

查看容器状态

docker ps
	-a: 能够查看离线的容器

链接容器, attach在离开容器的时候会关闭容器

docker attach
docker exec -it
docker exec -it 213c4841716d "bin/bash"

删除容器

docker rm
	-f: 强行终止并删除一个运行中的容器
	-l: 删除容器的链接, 单保留容器
	-v: 删除容器挂载的数据卷

制做容器包

febootstrap -i bash -i wget -i net-tools -i openssh-server -i openssh-client rhel71 rhel71doc http://content.example.com/ose3.0/x86_64/dvd/rhel-7-server-rpms/

import image到docker中

cd rhel71doc/
tar -c .|docker import - rehl71_stu6

删除容器

docker rm

修改image标签

docker tag dl.dockerpool.com:5000/ubuntu:latest ubuntu:latest

查看image详细信息, 返回的是一个json格式的消息, 也可使用-f参数来指定某一个参数

docker inspect [imageid]

新建容器 docker create, 让docker分配一个伪终端pseudo-tty

docker create -it centos:6 "/bin/bash"

启动容器可使用docker run 或者docker start

docker run centos /bin/echo "hello world"

终止容器, 使用docker stop. 它首先向容器发送SIGTERM信号, 等待一段时间后(默认为10s), 在发送SIGKILL信号终止容器 也能够是用docker start

docker start [container id]

使用docker attach附着到容器中

docker attach [container id]

查看某个容器的进程pid

docker inspect --format "{{ .State.Pid }}" [container id]

使用nsenter登陆到容器中

nsenter --target 6803 --mount --uts --ipc --net --pid

使用registry镜像建立私有仓库

建立本地的镜像仓库

 
 

使用Dockerfile制做镜像

建立镜像的方法有三种, 基于已有镜像建立, 基于本地模板导入以及Dockerfile建立

Dockerfile 文件配置

FROM rhel7
MAINTAINER Sun Ying 
EXPOSE 80
RUN yum -y install httpd
RUN echo "Sample welcome page" >/var/www/html/index.html
CMD /usr/sbin/httpd -DFOREGROUND

执行buildfile, 指在当前目录下查找Dockerfile, 而且将image命名为ying/webservice

docker build -t ying/webservice .

由于制做buildfile的时候是在容器中执行的, 咱们若是须要添加一些文件到容器中. 则须要使用ADD进行添加 ADD hello.sh /bin/hello.sh

FROM rhel7
MAINTAINER Sun Ying 
EXPOSE 80
ADD hello.sh /bin/hello.sh
ENV WORD hello world
RUN /bin/hello.sh

复杂案例: 制做ubuntu+java+tomcat+ssh server镜像 ENTRYPOINT是告诉镜像须要执行什么指令, 即在镜像被使用的时候执行的指令

FROM ubuntu
MAINTAINER Ying "ying.sun@example.com"
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" >/etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y openssh-server
RUN mkidr -p /var/run/sshd
RUN echo "root:123456"|chpasswd
RUN apt-get install python-software-properties
RUN add-apt-repository ppa:webupd8team/java
RUN apt-get update
RUN apt-get install -y vim wget curl oracle-java7-installer	 tomcat7
# 设置JAVA_HOME环境变量
RUN update-alternatives --display java 
RUN echo "JAVA_HOME=/usr/lib/jvm/java-7-oracle" >> /etc/environment 
RUN echo "JAVA_HOME=/usr/lib/jvm/java-7-oracle" >> /etc/default/tomcat7
# 开启容器的22, 8080端口
EXPOSE 22
EXPOSE 8080
# 设置tomcat7初始化运行
ENTRYPOINT service tomcat7 start && /usr/sbin/sshd -D

Supervisor能够启动多个进程. supervisoer做为守护进程监管多个进程运行. 注意, docker的程序必定要放在前台执行. superviord还会去监管他所启动的进程

[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND"

通常来讲一个容器中应该只执行一个程序, docker只会监测容器中的前台程序是否执行正常. 程序的配置文件若是放在镜像中会对镜像的升级带来运维的成本. 通常来讲能够放在环境变量中(ENV). 此外, 日志输出也是docker的一个问题. 通常来讲日志, 须要绑定到持久存储. 或者使用syslog 端口映射的方式, 传输到主机上 使用etcd/zookeepr来管理配置变动信息. 自己是key/value的架构

Docker Volume

/var/lib/docker/graph: 存放本地Image里面的分层信息 /var/lib/docker/devicemapper/devicemapper/data: 存储了image和container的二进制数据文件 /var/lib/docker/devicemapper/devicemapper/metadata: 存储了相关的元数据 docker的data文件是一个稀疏磁盘空间, 默认是100G, 实际使用的大小可使用du来进行查看. 每一个容器的大小最大为10G. aufs drvier是docker最先期支持的driver, 是linux内核的一个补丁集. ubuntu会使用 device mapper: 2.6以后引入的, 提供了一种逻辑设备到物理设备的映射框架, 是LVM2的核心. 支持块级别的copy on write特性. VFS: 虚拟文件系统, 不支持COW btrfs: 很是快, 仍然在进化中 高频写操做须要volume: 大量日志文件系统, 数据库系统等 可使用volume

docker run -it -v /volume rhel7 /bin/bash

使用docker inspect 查看

    "Volumes": {
        "/volume": "/var/lib/docker/volumes/ec3f9aecdffc0818aaec803ca5eccb60506ce3ca4b1bc7e0676
e763733d39ad3/_data"
    },
    "VolumesRW": {
        "/volume": true
    },

也可使用本机目录挂载到docker容器中去

docker run --rm=true -it -v /storage /volume java /bin/bash
						  本机目录	容器目录

volume的互联, 基于数据容器的单主机互联.

docker run --rm=true --privileges=true --volume-from=32dsdfadsfsf -it rhel7 /bin/bash

docker容器互联

容器间基于link互联, 默认状况下docker容许container互通, 经过-icc=false关闭互通. 一旦关闭互通, 只能经过-link name:alias 命令链接指定container

关闭容器互联

/usr/bin/docker daemon --icc=false --iptables=true

link的隔离是隔离容器端口的. link只能解决一台主机之间的互联.

docker run --rm=true --name=myjavaserver -it java /bin/bash
docker run --rm=true --link=myjavaserver:serverM1 -it java /bin/bash

多台主机的话则没法使用link进行互联. SOCAT是一个多功能的网络工具, 能够用来作简单的HTTP proxy

socat TCP4-LISTEN:6666 TCP4:proxy.company.com:8080

最简单经常使用的互联方式: 端口映射. 使用docker-proxy

宿主机的0.0.0.0:8080 --> 容器80
docker run -p "8080:80"
docker run --rm=true --name=mysqlserver -p 8066:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_USER=ying -e MYSQL_PASSWORD=nsadm -e MYSQL_DATABASE=testing workstation.pod0.example.com:5000/openshift3/mysql-55-rhel7
docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8066 -container-ip 172.17.0.5 -container-port 3306

能够添加NAT

-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8066 -j DNAT --to-destination 172.17.0.6:3306
-A DOCKER -d 172.17.0.6/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 3306 -j ACCEPT
-A POSTROUTING -s 172.17.0.6/32 -d 172.17.0.6/32 -p tcp -m tcp --dport 3306 -j MASQUERADE

proxy每一个port的映射要使用将近11MB的内存. 所以建议直接使用宿主机网络共享出来

docker run --rm=true --name=mysqlserver --net=host -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_USER=ying -e MYSQL_PASSWORD=nsadm -e MYSQL_DATABASE=testing workstation.pod0.example.com:5000/openshift3/mysql-55-rhel7

多个容器公用一个网络, 下面的例子中, 第二个容器使用了第一个容器的IP 地址

docker run --rm=true --name=mysqlserver -e MYSQL_ROOT_PASSWORD=123456 mysql
docker run --rm=true --net=container:mysqlserver java ip addr 

共享一个网络的状况下, 互相访问能够经过localhost来进行彼此访问

docker run --rm=true --net=container:mysqlserver java curl localhost:3306

基于路由的容器互联

首先须要修改容器的IP地址, 这个时候常常会由于linux网桥自身的问题没法成功删除

ifconfig docker0 down 
brctl delbr docker0 

在两台主机上分别执行

route add -net 172.18.0.0/16 gw 172.25.0.9
route add -net 172.17.0.0.16 gw 172.25.0.11
清理防火墙规则
iptables -F; iptables -t nat -F 

网络将会以ovs为趋势, docker官方提出了libnetwork的概念, 还在继续的发展中.

namespace

平行宇宙. 在不一样虚拟机中的网卡彼此是不可见的, tap是由软件实现的. veth pari是用于不一样network namespace间进行通讯的方式, veth pari将一个network namespace数据发往另外一个network namespace的veth. 查看容器真正的pid

docker inspect -f '{{.State.Pid}}' [containerID]

建立一个namespace网络的软连接

mkdir -p /var/run/netns
ln -s /proc/1469/ns/net /var/run/netns/1469

此后就可使用ip netns来进行操做和查看

ip netns ls

查看容器中的ip地址

ip netns exec 1469 ip addr show 

在容器中查看对端的接口

ip netns exec 1469 ethtool -S eth0

在宿主机中查看网络的veth应该与之相对应

安装open vSwitch

yum install openvswitch

添加网桥和gre隧道

ovs-vsctl add-br br0
ovs-vsctl   add-port   br0   gre0   --    set    Interface    gre0    type=gre options:remote_ip=172.25.0.9
brctl addif docker0 br0
ip link set dev br0 up
ip link set dev docker0 up
iptables -t nat -F;iptables -F
ip route add 172.17.0.0/16 dev docker0

ovs-vsctl show

Bridge "br0"
    Port "br0"
        Interface "br0"
            type: internal
    Port "gre0"
        Interface "gre0"
            type: gre
            options: {remote_ip="172.25.0.9"}
ovs_version: "2.3.1-git3282e51"

抓gre的包

tshark -i eth0 ip proto gre

Docker管理工具

Shipyard和cAdvisor 安装shiptyard

OPTIONS= -H=unix:///var/run/docker.sock -H=tcp://0.0.0.0:2375
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock\ shipyard/deploy start 

默认运行在8080端口, 用户名密码是admin/shipyard

cAdvisor是google推出的一个主要用于检测容器状态的工具, 也能够经过容器直接安装

docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8082:8082 --detach=true --name=cadvisor google/cadvisor:latest --port=8082

Reigstry

Registry包含一个或多个Repository Repository包含一个或多个Image. Image使用GUID表示, 有一个或多个Tag与之关联

Kubernetes

Docker于CoreOS的恩怨情仇 cgroup最初是由google的工程师提出来, 后来被整合到内核中. Linux容器正式业界一直关注的Google基础设施Borg和Omega的基础之一, 基于以前Google开源的cgroup项目. Borg是一个生产环境的经验下的论文. linux基金会介入coreOS, google, docker等公司的纠纷, 建立了OCP项目 2015年7月22日Google正式对外发布Kubernetes v1.0 CNCF(Cloud Native Computing Foundation) 基金会

Kubernetes重要概念

Namingspace: 关联resource. 资源隔离的一种手段, 不一样NS中的资源不能互相访问 Resource: 关联Namespace和ResourceQuta. 集群中的一种资源对象, 处于某个命名空间中. 能够持久化存储到Etcd中, 资源有状态且能够配额. Label: 关联Resouce,Label Selector. 一个Key-value值对. Master节点: 关联工做节点Node. K8s集群的管理节点, 负责集群的管理. 提供集群资源数据访问入口. 负责API Server进程, Controller Manager服务进程. Scheduler服务进程 Node节点: 关联master节点. K8s中的工做节点, 启动和管理k8s中的pod实例. 接受Master节点的管理指令, 运行着一个k8s的守护进程kubelet, 负载均衡器kube-proxy

kubectl describe node kubernetes-minion1

Pod: 关联Node和Serivce: 一组容器的一个"单一集合", K8s中的最小任务调度单元. 一个Pod中的容器共享资源(网络, volume) Service: 关联多个相同Label的Pod, 是一种微服务. 具备一个虚拟访问的地址(IP + Port). Replication Controller: 关联多个相同Label的pod. Pod副本控制器, Volumes: 关联Pod, 是Pod上的存储卷.

Kubernets实践

https://github.com/kubernetes/kubernetes/blob/release-1.0/docs/getting-started-guides/centos/centos_manual_config.md 要保证master/node节点之间能够互相经过域名访问 建立yum源

[virt7-testing]
name=virt7-testing
baseurl=http://cbs.centos.org/repos/virt7-testing/x86_64/os/
gpgcheck=0

安装kubernetes

yum install kubernetes

安装etcd, 他是kubernetes的键值数据库系统

yum install etcd

配置etcd

ETCD_NAME=master
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001"
ETCD_ADVERTISE_CLIENT_URLS="http://master.example.com:2379,http://master.example.com:4001"

验证状态

etcdctl set testdir/testkey0 1
etcdctl get testdir/testkey0
etcdctl -C http://master.example.com:4001 cluster-health

公用配置/etc/kubernetes/config, 配置master信息

KUBE_MASTER="--master=http://master.pod0.example.com:8080"
KUBE_ETCD_SERVERS="--etcd_servers=http://master:4001"

在Master上面配置/etc/kubernetes/apiserver

KUBE_API_ADDRESS="--address=127.0.0.1"
KUBE_API_ADDRESS="--insecure-bind-address=0.0.0.0"
KUBE_API_PORT="--port=8080"
# KUBE_ETCD_SERVERS="--etcd_servers=http://127.0.0.1:2379"
# 默认ETCD启动在2379端口

在master上启动服务etcd, kube-apiserver, kube-controller-manager, kube-scheduler. etcd是k8s的数据库系统

for SERVICES in etcd kube-apiserver kube-controller-manager kube-scheduler;do systemctl restart $SERVICES; systemctl enable $SERVICES; systemctl status $SERVICES; done

在node节点上进行配置/etc/kubernetes/kubelet

KUBELET_ADDRESS="--address=0.0.0.0"
KUBELET_PORT="--port=10250"
KUBELET_HOSTNAME="--hostname_override=node.pod0.example.com"
KUBELET_API_SERVER="--api_servers=http://master.pod0.example.com:8080"

启动kube-proxy和kubelet服务, 为了让kube-proxy也能找到master须要配置config文件声明master节点位置 在master节点上能够检查node节点的状态是否注册成功

kubectl get nodes
kubectl cluster-info

修改docker配置

OPTIONS='--selinux-enabled=disabled 

在kube-scheduler和kube-controller-manager中添加

After=etcd.service
After=kube-apiserver.service
Requires=etcd.service
Requires=kube-apiserver.service

Kubernetes的版本升级

很是简单, 经过官网下载最新版本的二进制包kubernetes.tar.gz, 解压缩. 中止Master和Node上的服务, 将新版的可执行文件复制到kubernetes的安装目录下.重启服务 kubectl

Available Commands:
  get            Display one or many resources
  describe       Show details of a specific resource or group of resources
  create         Create a resource by filename or stdin
  replace        Replace a resource by filename or stdin.
  patch          Update field(s) of a resource by stdin.
  delete         Delete a resource by filename, stdin, resource and name, or by resources and label selector.
  namespace      SUPERCEDED: Set and view the current Kubernetes namespace
  logs           Print the logs for a container in a pod.
  rolling-update Perform a rolling update of the given ReplicationController.
  scale          Set a new size for a Replication Controller.
  exec           Execute a command in a container.
  port-forward   Forward one or more local ports to a pod.
  proxy          Run a proxy to the Kubernetes API server
  run            Run a particular image on the cluster.
  stop           Gracefully shut down a resource by name or filename.
  expose         Take a replicated application and expose it as Kubernetes Service
  label          Update the labels on a resource
  config         config modifies kubeconfig files
  cluster-info   Display cluster info
  api-versions   Print available API versions.
  version        Print the client and server version information.
  help           Help about any command

查看namespace

kubectl get namespace
kubectl describe namespace default

建立应答式文件nginx.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: master.example.com:5000/nginx
    ports:
    - containerPort: 80
kubectl create -f nginx-pod.yaml

查看events

kubectl get events

搭建本地仓库http://www.cnblogs.com/zhenyuyaodidiao/p/6500950.html

#KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure:latest" 

每一个Pod里运行着一个特殊的被称之为Pause的容器, 其余容器则为业务容器. 这些业务容器共享Pause容器的网络栈和Volume挂在卷. 官方文档https://docs.docker.com/registry/deploying/

Kubernetes定义

测试镜像文件在https://hub.docker.com/u/kubeguide/ Node信息同步能够经过kube-controller-manager启动参数 --node-sync-period 设置同步的时间周期 Node是自注册的, 当kubelet的启动参数中设置了--register-node为true时, kubelet会向apiserver注册本身. Kubelet进行自注册的启动参数以下: --apiservers=: apiserver的地址 --kubeconfig=: 登陆apiserver所须要凭据/证书的目录 --cloud_provider=: 云服务商地址, 用于获取自身的metadata --regiter-node=: 设置为true表示自动注册到apiserver上 一般在容器之间要使用link的方式互联, 可是大量的link会消耗系统资源. 经过Pod的概念能够将多个容器组合在一个虚拟的"主机"内, 能够实现容器之间仅须要经过localhost就能互相通讯了. 一个pod中的应用容器共享一组资源 PID命名空间: pod中的不一样应用程序能够看到其余应用程序的进程ID 网络命名空间: pod中的多个容器可以访问同一个IP和端口范围. IPC命名空间: Pod中的多个容器可以使用systemV IPC或POSIX消息队列进行通讯 UTS命名空间: Pod中的多个容器共享一个主机名 volumes: 共享存储卷, pod中的各个容器能够访问在pod级别定义的volumes

Pod的声明周期是经过Replication Controller来管理的. Pod的生命周期过程包括: 经过模板进行定义, 而后分配到一个Node上运行, 在pod所含容器运行结束后Pod也结束. Pod包含四种状态. Pending: Pod定义正确, 提交到Master Running: Pod已经被分配到某个Node上. Succeeded: Pod中全部容器都成功结束 Failed: Pod中全部容器都结束了.

Label以key/value的形式附加到各类对象上, 如Pod, service, RC, Node. Label Selector分两种, 基于等式的(Equality-based)和基于集合的(Set-Based). 基于等式的Label Selector, name=redis-slave; env != production; 基于集合的Label Selector, name in (redis-master, redis-slave) name not in (php-frontend) Replication Controller经过Label Selector来选择要管理的Pod RC是Kubernetes系统的核心概念, 用于定义Pod副本的数量.

Service能够看作一组提供相同服务的pod对外访问的接口, Service做用于那些Pod是经过Label Selector来定义的. 建立本地仓库, 首先使用阿里加速器https://cr.console.aliyun.com/ pull registry并启动

docker pull docker.io/registry 
docker run -d -p 5000:5000 --name=master.example.com --restart=always --privileged=true  --log-driver=none -v /home/data/registrydata:/tmp/registry registry

k8s的node节点必需要安装一个镜像名为 gcr.io/google_containers/pause-amd64:3.0的镜像. 能够从阿里云下载.

docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0

从新tag镜像名称而且上传到本地registry上

docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 master.ex
ample.com:5000/google_containers/pause-amd64:3.0
docker push master.example.com:5000/google_containers/pause-amd64:3.0

而后修改docker中的--insecure-registry添加master.example.com:5000

kubernetes部署示例

建立redis-master-controller.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-master
  labels:
    name: redis-master
spec:
  replicas: 1
  selector:
    name: redis-master
  template:
    metadata:
      labels:
        name: redis-master
    spec:
      containers:
      - name: master
        image: master.example.com:5000/kubeguide/redis-master
        ports:
        - containerPort: 6379

建立redis-master-service.yaml 服务, 定义开放的端口和对应的pod

apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    name: redis-master
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    name: redis-master

查看services

kubectl get services
NAME           CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes     10.254.0.1       <none>        443/TCP    1d
redis-master   10.254.241.203   <none>        6379/TCP   3m

因为cluster的IP是在建立pod以后由kubernetes自动分配的, 在其余Pod中没法预先知道某个Service的虚拟IP地址. Kubernets则使用Linux环境变量来传达信息. 其余的pod经过REDIS_MASTER_SERVICE_HOST 和 REDIS_MASTER_SERVICE_PORT来获取相关信息. 建立slave的RC

apiVersion: v1
kind: ReplicationController
metadata:
  name: redis-slave
  labels:
    name: redis-slave
spec:
  replicas: 2
  selector:
    name: redis-slave
  template:
    metadata:
      labels:
        name: redis-slave
    spec:
      containers:
      - name: master
        image: master.example.com:5000/kubeguide/guestbook-redis-slave
        env:
        - name: GET_HOSTS_FROM
          value: env
        ports:
        - containerPort: 6379

建立redis-slave-service.yaml 服务文件

apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    name: redis-slave
spec:
  ports:
  - port: 6379
  selector:
    name: redis-master

建立fronted-controller.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
  labels:
    name: frontend
spec:
  replicas: 3
  selector:
    name: frontend
  template:
    metadata:
      labels:
        name: frontend
    spec:
      containers:
      - name: frontend
        image: master.example.com:5000/kubeguide/guestbook-php-frontend
        env:
        - name: GET_HOSTS_FROM
          value: env
        ports:
          - containerPort: 80

配置服务文件fronted-service.yaml. type=NodePort是关键, 表示Node上的物理机端口提供对外访问的能力, 须要注意的是spec.ports.NodePort的端口定义由范围限制, 默认为30000~32767,在此范围以外则会失败

apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    name: frontend
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30001
  selector:
    name: frontend
tcp    LISTEN     0      128      :::30001                :::*                   users:(("kube-proxy",pid=2831,fd=9))

系统会跟根据pod和service的关系创建相应的endpoint

kubectl get endpoints

Kubernets 对外Service

Service的ClusterIP地址相对于Pod的IP地址来讲相对稳定, Service被建立时被分配一个IP地址, 在销毁该Service以前, 这个IP地址不会发生变化. Cluster IP Range池中分配到的IP只能在内部被访问到, 全部其余Pod均可以无障碍的访问到它. 可是若是这个Service做为前端的服务, 则须要这个服务提供公共IP. Kubernetes提供两种对外的Service的type定义. NodePort和LoadBalancer NodePort: 指定spec.type=NodePort, 并指定spec.ports.nodePort的值, 系统就会在Kubernetes集群中的每一个Node上打开一个主机上的真实端口号, 这样, 可以访问Node的客户端都能经过这个端口号访问到内部的Service了 LoadBalancer: 若是云服务商支持外接负载均衡器, 则能够经过spec.type=LoadBalancer定义Service. 同时须要指定负载均衡器的IP地址. status.loadBalancer.ingress.ip设置为146.148.47.155为云服务商提供的负载均衡器的IP地址.

apiVersion: v1
kind: Service
metadata: {
  "kind": "Service",
  "apiVersion": "v1",
  "metadata": {
    "name": "my-service"
    },
  "spec":{
    "type": "LoadBalancer",
    "clusterIP": "10.0.171.239",
    "selector": {
      "app": "MyApp"
    },
    "ports": [
      {
        "protocol": "TCP",
        "port": 80,
        "targetPort": 9376,
        "nodePort": 30061,
      },
    ],
  },
  "status": {
    "loadBalancer": {
      "ingress": [
        {
          "ip": "146.148.47.155"
        },
      ]
    }
  }
}

多端口服务

不少状况下, 一个服务都须要对外暴露多个端口号. 防止产生歧义, specports 下面能够定义名字. spec.ports.name="http"

Volume

存储卷是Pod中可以被多个容器访问的共享目录. K8s中的Volume与Pod生命周期相同, 但与容器的生命周期不相关. 当容器终止或者重启时, volume中的数据不丢失. 1) EmptyDir: 一个EmptyDir Volume是在Pod分配到Node时建立的, 从它的名称就能够看出, 它的初始内容为空. 同一个Pod下的全部容器均可以读/写EmptyDir中的相同文件. 当Pod被移除后, EmptyDir中的数据也永久删除. 2) hostPath: 在Pod上挂载宿主机上的文件或目录. 在不一样的Node上具备相同配置的Pod可能会由于宿主机上的目录和文件不一样而致使对Volume上的目录和文件的访问结果不一致. 通常要使用共享存储 以宿主机/data为存储卷

spec:
  template:
    spec:
      volumes:
      - name: "persistent-storage"
        hostPat:
          path: "/data"
      containers:
        volumeMounts:
          - name: "persistent-storage"
            mountPath: "/data"

3) gcePersistentDisk: 使用google计算引擎上的永久磁盘. 此时要求虚拟机是GCE虚拟机 4) awsElasticBlockStore: 与GCE相似, 该volume是Amazon提供的AWS的EBS volume 5) nfs: 使用NFS提供共享目录挂载到Pod中.

apiVersion: v1
kind: Pod
metadata:
  name: nfs-web
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
      volumeMounts:
        - name: nfs
          mountPath: "/usr/share/nginx/html"
  volumes:
    - name: nfs
      nfs:
        server: nfs-server.localhost
        path: "/"

6) iscsi: 使用iSCSI存储设备上的目录挂载到Pod中 7) glusterfs 8) rbd 9) gitRepo 10) secret: 经过tmpfs实现的, 这种volume没法持久化 11) persistentVolumeClaim: 从PV(PersistentVolume)中申请所需的空间, PV一般是一种网络存储. 例如NFS, iSCSI, GCE, AWS等等

Namespace

kubernetes集群在启动后, 会建立一个名为default的Namespace

kubectl get namespaces

默认Pod, RC, Service都将系统建立到"default"的Namespace中 用户能够根据建立新的Namespace,

apiVersion: v1
kind: Namespace
metadata:
  name: development

若是不加参数, kubectl get命令将显示属于"default" 命名空间的对象 --namespace 参数能够指定某个命名空间对象 注意: Label是有严格的语法规则的, 可是Annotation是能够随意定义的 Kubelet建立Pod时, 须要经过启动一个名为google_containers/pause的镜像来完成对Pod网络的配置 我以前的实验使用了将google_containers/pause镜像下载到本地的方式来实现的. 须要在每一个node上进行操做, 也能够直接在kubelet的配置文件中添加

KUBELET_ARGS="--pod_infra_container_image=master.example.com:5000/google_containers/pause:3.0"

ETCD

etcd是高可用的key/value存储系统, 用于持久化存储集群中. API Server则提供了操做etcd的封装接口API, 以REST方式提供服务, 以REST方式提供服务.

ETCD集群

ETCD配置集群配置/etc/etcd/etcd.conf

[member]
ETCD_NAME=etcd1
ETCD_LISTEN_PEER_URLS="http://10.0.0.1:2380" 		#集群内部使用的IP地址
[cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.1:2380" 	#广播给集群内其余成员的URL
ETCD_INITIAL_CLUSTER="etcd1=http://10.0.0.1:2380, etcd2=http://10.0.0.2:2380, etcd3=http://10.0.0.3:2380"
ETCD_INITIAL_CLUSTER_STATE="new"		#初始集群状态, new为新建集群
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"	#集群名称
ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.1:2379"		#广播给外部客户端使用的URL

在etcd2和etcd3加入etcd-cluster集群的实例.

ETCD_INITIAL_CLUSTER_STATE="exsit"

查看集群节点状态

etcdctl cluster-health

查看集群成员列表

etcdctl member list

以kube-apiserver为例, 访问etcd集群的参数设置为

--etcd-servers=http://10.0.0.1:4001, http://10.0.0.2:4001, http://10.0.0.3:4001

Kubernetes 核心原理

资源对象Replication Controller简写为RC,而Replication Controller是指"副本控制器". 建立pod的时候最好不要越过RC直接建立Pod, 由于Replication Controller会经过RC管理Pod副本. 当Pod的重启策略为RestartPolicy=Always时, Replication Controller才会管理该Pod的操做(建立, 销毁, 重启等) Pod实例都会经过RC里定义的Pod模板(template)建立的. selector 指定一个name, 这个name和template中的name对应 对replciationcontroller进行扩大或缩小副本数量

kubectl scale --replicas=3 replicationcontrollers foo

ResourceQuota Controller

Kubernetes 支持三个层次的资源配额管理 1. 容器级别 2. Pod级别, 3. Namespace级别 包括的限制是Pod数量; Replication Controller数量; Service数量; ResourceQuota数量; Secret数量; PV(Persistent Volume) 数量 LimitRanger做用于Pod和Container上, ResourceQuota则做用于Namespace上

ServiceAccount Controller与Token Controller

ServiceAccount Controller与Token Controller是与安全相关的两个控制器. Service Account Controller在Controller manager启动时被建立.

某些特殊场景下, 例如将一个外部数据库做为Service的后端, 或将在另外一个集群或Namespae中的服务做为服务的后端. 须要建立一个不带标签选择器的Service. 若是不带标签选择器, 系统不会自动建立Endpoint. 此时须要手动建立一个和该Service同名的Endpoint kube-proxy为每一个Service在本地主机上开一个端口(随机选择), 任何访问该端口的链接都被代理到响应的一个后端pod上.

k8s支持两种主要的模式来找到Service. 1). 一个是容器的Service环境变量. 形如{SVCNAME}_SERVICE_HOST. 例如名称为"redis-master"的service, 它对外暴露6379 TCP端口. 且集群IP地址为10.0.0.11. kubelet会为新建的容器添加以下环境变量

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

经过环境变量来找到Service会带来一个很差的结果, 即任何被某个pod所访问的Service, 必须先于该Pod被建立, 不然和这个后建立的Service相关的环境变量, 将不会被加入该Pod容器中.

容器健康检查

Pod经过两类探针来检查容器的健康状态, 一个是LivenessProbe探针. 另外一类是使用LivenessProbe探针. LivenessProbe探针有三种实现方式1) ExecAction: 在容器中执行一个命令, 返回值为0, 代表健康 2) TCPSocketAction: 经过容器的IP地址和端口号执行TCP检查 3) HTTPGetAction: 经过容器IP地址和端口号以及路径调用HTTP get方法

livenessProbe:
  exec:
  	command:
  	- cat
  	- /tmp/health
  initialDelaySeconds: 15
  timeoutSeconds: 1
livenessProbe:
  httpGet:
  	path: /heathz
  	port: 8080
  initialDelaySeconds: 15
  timeoutSeconds: 1

安全机制原理

Authenication认证

CA认证在API server上配置三个参数 "--client-ca-file" "--tls-cert-file" 和 "--tls-private-key-file" kuectl 的三个参数"certificate-authority" "client-certificate" 和 "client-key". 或客户端应用的kubeconfig配置文件中的配置项

token认证方式, 添加参数"--token_auth_file=SOMEFILE"

HTTP基本认证方式添加参数"--basic_auth_file=SOMEFILE"

Auhorization受权

在经过API访问资源以前, 必须经过访问策略进行校验. 访问策略经过API Server的启动参数 "--authorization_mode"配置, 该参数包含如下三个值 1) AlwaysDeny 2) AlwaysAllow 3) ABAC

ABAC表示配置使用用户配置的受权策略去管理API Server的请求. HTTP请求包含四个能被受权进程识别的属性 1) 用户名 2) 是不是只读请求 3) 访问的属于那一类资源, 例如Pod 4) 被访问对象所属的Namespace 若是选用ABAC模式, 须要经过API Server的参数选项"--authorization_policy_file=SOME_FILENAME"

# 容许用户alice作任何事情
{"user": "alice"}
# 用户kubelet指定读取资源Pods
{"user": "kubelet", "resource": "pods", "readonl": true} 
# 用户Kubelet能读和写资源events
{"user": "kubelet", "resource": "event"}
# 用户bob只能读取Namespace "myNamespace" 中的资源Pods
{"user": "bob", "resource": "pods", "readonly": true, "ns": "myNamespace"}

Admission Control 插件

SecurityContextDeny: 禁止经过API server管理配置了下列两项配置的pod

spec.containers.securityContext.seLinuxOptions
spec>containers.securityContext.runAsUser

ResourceQuota: 资源配额. --adminssion_control=ResourceQuota, 使插件生效. 对资源对象的最大数量限制

{
    "apiVersion": "v1",
    "kind": "ResourceQuota",
    "metadata": {
        "name": "quota"
    },
    "spec": {
        "hard": {
            "memory": "1Gi",
            "cpu": "20",
            "pods": "10",
            "services": "5",
            "replicationcontrollers": "20",
            "resourcequotas": "1"
        }
    }
}

LimitRanger: 用于列举Namespace中各资源的最小限制, 最大限制和默认值. --adminssion_control=LimitRanger

apiVersion: v1
kind: LimitRange
metadata:
  name: myLimits
spec:
  limits:
  - max:
      cpu: "2"
      memory: 1Gi
    min:
      cpu: 250m
      memory: 6Mi
    type: Pod
  - default:
      cpu: 250m
      memory: 100Mi
    max:
      cpu: "2"
      memory: 1Gi
    min:
      cpu: 250m
      memory: 6Mi
    type: Container

Secret: 私密凭据 登陆私有Registry, 第一次登陆会建立私有的用户名密码, 相关信息将会写入~/.dockercfg文件中

docker login localhost:5000

Service Account: 多个Secret的集合

kubectl get serviceAccounts

集群安全配置案例

双向认证配置

双向认证配置: 1) 生成根证书, API server服务端证书, 服务器端私钥, 各组件所用的客户端证书和客户端私钥 2) 修改k8s各个服务进程的启动参数, 启动双向认证模式 证书目录/var/run/kubernetes 产生私钥

openssl genrsa -out dd_ca.key 2048

生成根证书(自签证书)

openssl req -x509 -new -nodes -key dd_ca.key -subj "/CN=example.com" -days 5000 -out dd_ca.crt

生成API Server的服务端证书和私钥

openssl genrsa -out dd_server.key 2048
openssl req -new -key dd_server.key -subj "/CN=master.example.com" -out dd_server.csr
openssl x509 -req -in dd_server.csr -CA dd_ca.crt -CAkey dd_ca.key -CAcreateserial -out dd_server.crt -days 5000

生成Controller Manager与Scheduler进程公用的证书和私钥

openssl genrsa -out dd_cs_client.key 2048
openssl req -new -key dd_cs_client.key -subj "/CN=master.example.com" -out dd_cs_client.csr
openssl x509 -req -in dd_cs_client.csr -CA dd_ca.crt -CAkey dd_ca.key -CAcreateserial -out dd_cs_client.crt -days 5000

生成Kubelet所用的客户端证书和私钥, 假设NodeIP地址为192.168.48.142

openssl genrsa -out dd_kubelet_client.key 2048
openssl req -new -key dd_kubelet_client.key -subj "/CN=192.168.48.142" -out dd_kubelet_client.csr
openssl x509 -req -in dd_kubelet_client.csr -CA dd_ca.crt -CAkey dd_ca.key -CAcreateserial -out dd_kubelet_client.crt -days 5000

修改API Server的启动参数/etc/kubernetes/apiserver 并重启apiserver

KUBE_API_ARGS="--log-dir=/var/log/kubernetes --secure-port=443 --client_ca_file=/home/cert/dd_ca.crt --tls-private-key-file=/home/cert/dd_server.key --tls-cert-file=/home/cert/dd_server.crt"

验证api server

curl https://master.example.com:443/api/v1/nodes --cert dd_cs_client.crt --key dd_cs_client.key --cacert dd_ca.crt

修改Controller Manager的启动参数

KUBE_CONTROLLER_MANAGER_ARGS="--log-dir=/var/log/kubernetes --service_account_private_key_file=/home/cert/dd_cs_client.key --root-ca-file=/home/cert/dd_ca.crt --master=https://master.example.com:443 --kubeconfig=/etc/kubernetes/cmkubeconfig"

建立/etc/kubernetes/cmkubeconfig文件, 配置证书相关参数而且重启kube-controller-manager服务

apiVersion: v1
kind: Config
users:
- name: controllermanager
  user:
    client-certificate: /home/cert/dd_cs_client.crt
    client-key: /home/cert/dd_cs_client.key
clusters:
- name: local
  cluster:
    certificate-authority: /home/cert/dd_ca.crt
contexts:
- context:
    cluster: local
    user: controllermanager
  name: my-context
current-context: my-context

在每一个Node上建立/var/lib/kubelet/kubeconfig文件.

apiVersion: v1
kind: Config
users:
- name: kubelet
  user:
    client-certificate: /home/dd_kubelet_client.crt
    client-key: /home/dd_kubelet_client.key
clusters:
- name: local
  cluster:
    certificate-authority: /home/dd_ca.crt
contexts:
- context:
    cluster: local
    user: kubelet
  name: my-context
current-context: my-context

修改Kubelet的启动参数, 以修改/etc/kubernetes/kubelet配置文件为例

KUBELET_API_SERVER="--api_servers=https://master.example.com:443"
KUBELET_ARGS="--kubeconfig=/var/lib/kubelet/kubeconfig"

配置kube-proxy, 建立/var/lib/kubeproxy/proxykubeconfig

apiVersion: v1
kind: Config
users:
- name: kubeproxy
  user:
    client-certificate: /home/dd_kubelet_client.crt
    client-key: /home/dd_kubelet_client.key
clusters:
  - name: local
    cluster:
      certificate-authority: /home/dd_ca.crt
contexts:
  - context:
      cluster: local
      user: kubeproxy
    name: my-context
current-context: my-context

配置/etc/kubernetes/proxy并重启kube-proxy

KUBE_PROXY_ARGS="--kubeconfig=/var/lib/kubeproxy/proxykubeconfig --master=https://master.example.com:443"

简单认证配置

建立用户名密码和UID的文件/root/token_auth_file

thomas, thomas, 1
admin, admin, 2

修改API Server的配置, 重启apiserver

KUBE_API_ARGS="--secure-port=443 --token_auth_file=/root/token_auth_file"

使用curl验证API server

curl https://master.example.com:443/version -H "Authorization: Bearer thomas" -k

HTTP base认证

建立用户名密码和UID的文件/root/token_auth_file

thomas, thomas, 1
admin, admin, 2

修改API Server的配置, 重启APIserver

KUBE_API_ARGS="--secure-port=443 --basic_auth_file=/root/basic_auth_file"

用curl验证链接API Server

curl https://master.example.com:443/version --basic -u thomas:thomas -k

 

 

 

Kuberntes 网络原理

每一个Pod都有一个独立的IP地址, 全部的Pod都在一个能够直接连通的, 扁平的网络空间中. 不论这些pod是否在同一个宿主机中, 都要求它们能够直接经过对方的IP进行访问. 这种模式称为IP per Pod模型 在同一个pod中的容器能够经过localhost来链接其余容器的端口.

Linux网络栈中引入了网络命名空间(Network Namespace), 这些独立的协议栈被隔离到不一样的命名空间中. 彼此间没法通讯. Linux的网络命名空间内能够有本身的路由表及独立的Iptables/Netfilter来设置提供包转发, NAT及IP包过滤等功能. 让处在不一样命名空间的网络互相通讯, 甚至和外部的网络进行通讯, 可使用Veth设备对. 它就像一个管道, 一端连着一个网络命名空间的协议栈, 一端连着另外一个网络命名空间的协议栈. 建立一个网络命名空间

ip netns add <name>

查看命名空间中的内容

ip netns exec <name> ip addr show

若是要执行多个命令, 能够先进入命名空间的bash, 使用exit退出命名空间

ip netns exec <name> bash 

veth设备属于能够转移的设备, 便可以在不一样命名空间中转换. 可是不少其余设备例如lo设备, vxlan设备, ppp设备, bridge设备等都是不能够转移的.

ip link set veth1 netns ns1

查看是否能够进行转移, 为on则意味着不能够进行转移

ethtool -k docker0|grep netns
netns-local: on [fixed]

veth对, 老是以成对的方式出现的. 两端称为peer 建立Veth设备对:

ip link add veth0 type veth peer name veth1

将veth1迁移到netns ying中

ip link set veth1 netns ying

为veth0/veth1建立ip地址

ip netns exec ying ip addr add 10.1.1.1/24 dev veth1
ip addr add 10.1.1.2/24 dev veth0

启动veth0/veth1

ip link set veth0 up
ip netns exec ying ip link set dev veth1 up

此时两个veth peer就能够彼此通讯了. 可使用ethtool来查看veth对的peer

ethtool -S veth0

网桥

默认MAC地址的过时时间是5min. Linux的网桥提供了这些设备之间互相转发数据的二层设备. 于switch纯二层设备不一样, 运行着linux内核的机器自己就是一台主机, 有多是网络报文的目的地. 其收到的报文除了转发和丢弃, 还可能被送到网络协议栈的上层(网络层), 从而被这台主机的协议栈消化. 因此此时, 网桥便是二层设备也是一个三层设备.

Linux内核是经过一个虚拟的网桥设备(Net Device)来实现网桥的. Net Device与通常的设备不一样, 最明显的一个特征是它还能够有本身的一个IP地址.

例如一个br0桥接了两个网卡, eth0, eth1. 对于上层协议栈而言, 只看获得br0, 上层协议栈将报文发送给br0, 网桥设备的处理代码判断报文该发送给eth0仍是eth1. 反过来eth0/1接收到的报文被提交给网桥处理代码.

Iptables/Netfilter

Linux网络协议栈中有一组回调函数挂节点. hook钩子函数. Netfilter负责在内核执行各类挂接的规则, 运行在内核模式中. 而iptables是在用户模式下运行的进程, 负责协助维护内核中的Netfilter的各类规则表.

路由

Linux路由表至少包含两个表, 当启用策略路由的时候还会有其余表. 一个是LOCAL一个是MAIN. LOCAL表中包含全部本地设备地址. LOCAL表示在创建网络设备的时候自动建立的, LOCAL表用于供Linux协议栈识别本地地址, 以及进行本地各个不一样网络接口之间的数据转发. 查看local表

ip route show table local type local

MAIN表用于各种网络IP地址的转发, 它的创建既可使用静态配置生成, 也可使用动态路由发现协议生成. 动态路由发现协议是使用一组组播功能来发送路由发现数据, 动态交换和获取网络的路由信息, 并更新到路由表中. Linux下支持路由发现协议的开源软件有不少, 经常使用的有Quagga, Zebra等. 路由表查看

ip route list

Docker的网络实现

标准的Docker支持一下四类网络模式 host模式: 使用 --net=host指定 container模式: 使用 --net=container: NAME_or_ID指定 none模式: 使用 --net=none指定 bridge模式: 使用 --net=bridge指定

Kubernets管理模式下, 一般只会使用bridge模式. bridge模式下, 会建立一个docker0的网桥, 而后私有网络空间会给这个网桥设置一个子网. 每个被建立出来的容器, 都会建立一个虚拟的以太网设备(veth对), 另外一端使用Linux的网络命名空间技术, 映射到容器内的eth0设备, 而后从网桥的地址段内给eth0接口分配一个IP地址. 这样作的结果是, 同一个主机上的容器能够彼此通讯, 经过IP地址. 可是不一样主机上的容器就必须使用port来实现.而后经过这个端口路由或代理到容器上. docker的iptables在nat表中. 前两条是框架, 生效时都会走DOCKER链. NAT第三条的含义是, 若本地发出的数据包不是通往docker0接口时, 即发往主机以外的设备时, 都须要进行动态地址修改MASQUERADE

-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

filter表中, 第二条若是接收到的数据包属于之前已经创建好的链接, 那么容许直接经过. 这样接受到的数据包天然又走回docker0, 并中专到响应的容器.

-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT

建立registry容器后, 自动添加一条路由规则

-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT

Kubernetes的网络实现

1) 紧密耦合的容器到容器之间的直接通讯 同一个pod内的容器共享同一个网络命名空间, 共享同一个Linux协议栈. 容器之间能够用localhost地址访问彼此的端口. 能够直接使用本地IPC进行通讯.

2) 抽象的Pod到Pod之间的通讯 同一个Node内的Pod之间的通讯, 自己两个pod都是在docker0网段内的IP地址. 直接经过docker0网桥进行中转便可. 不一样Node上的Pod之间的通讯. k8s对docker0的IP地址进行规划, 保证每个Node上的docker0地址没有冲突. k8s的网络加强开源软件Flunnel就可以管理资源池的分配. 在GCE环境下, 路由打通是经过GCE来完成的. 在私有云环境中, 须要额外的网络配置.

3) Pod到Service之间的通讯 Service是对一组Pod的抽象. 将Service落实的是kube-proxy服务进程. 这个进程能够看作Service的透明代理兼. kube-proxy都会在本地Node上创建一个SocketServer来负责接收请求, 而后均匀发送到后端某个Pod的端口上, 这个过程使用Round Robin负载均衡算法. Services的ClusterIP与NodePort等概念是kube-porxy经过iptables的NAT转换实现的. 访问Service的请求, 不管是cluster IP + TargetPort 的方式, 仍是使用节点机 Node IP + NodePort的方式, 都被节点机的iptables规则重定向到kube-proxy监听服务代理端口. 目前kube-proxy只支持RR和会话保持(若是Service中定义了会话亲和性) SocketServer: 端口是随机选择的一个本地空闲端口 kube-proxy在启动和监听到Service或Endpoint的变化后, 会在本机iptables的NAT表中添加4条规则链. KUBE-PORTALS-CONTAINER: 从容器中经过Service Cluster IP和端口号访问Service请求 KUBE-PORTALS-HOST: 从主机中经过Service Cluster IP 和端口号访问Service请求 KUBE-NODEPORT-CONTAINER: 从容器中经过Service的NodePort端口号访问Service的请求 KUBE-NODEPORT-HOST: 从容器中经过Service的NodePort端口号访问Service的请求

4) 集群外部与内部组件之间的通讯 Pod被外部访问, 经过NodePort在Node节点上打开一个对外的端口. LoadBalancer.

开源的网络组件

直接路由

可使用静态路由的方式, 可是静态路由须要每台节点都进行配置. 在每一个Node的路由表中添加到对方docker0的路由转发规则配置项. eg. Pod1 docker0 IP子网为10.1.10.0, Node地址为192.168.1.128; Pod2 所在docker0网桥的IP子网是10.1.20.0, Node地址为192.168.1.129 在Node1上用route add命令添加一条到Node2上docker0的静态路由规则

route add -net 10.1.20.0 netmask 255.255.255.0 gw 192.168.1.129

一样, 在Node2上添加一条到Node1上docker0的静态路由规则

route add -net 10.1.10.0 netmask 255.255.255.0 gw 192.168.1.128

使用这种方式, 要保证每台node的docker网段不能重叠. 若是使用在大规模场景中, 则须要添加数以百计的静态路由. 能够借助动态路由发现协议工具Quagga软件来实现.

docker pull index.alauda.cn/georce/router

Quagga容器启动时须要以--privileged特权模式运行, 而且指定--net-host, 表示直接使用物理机的网路

docker run -itd --name-router --privileged --net=host index.alauda.cn/georce/router

过段时间后就会自动添加路由规则

Flannel

它能够协助k8s, 给每个Node上的Docker容器分配互相不冲突的IP地址. 能在这些IP地址之间创建一个叠加网络. Flannel建立一个flannel0的网桥, 并且这个网桥一端链接docker0网桥, 另外一端链接flanneld的服务进程. flanneld进程首先链接etcd, 利用etcd来管理可分配的IP地址段资源. 同时监控etcd中的每一个Pod的实际地址, 并在内存中创建一个Pod节点路由表. 而后下连docker0和物理网络, 使用内存中的Pod节点路由表, 将docker0发给它的数据包包装起来, 利用物理网络的链接将数据包投递到目标flanneld上, 从而完成pod到pod的直接通讯. Flannel使用了集中的etcd存储, 集中分配和处理IP, 就不会产生冲突. Flannel经过修改docker的启动参数将分配给它的地址段传递进去 --bip. 可是Flannel模型缺省地使用了UDP做为底层传输协议, UDP自己并不可靠.

安装flannel

下载地址 https://github.com/coreos/flannel/releases 将下载的压缩包解压, 把二进制文件flanneld和mk-docker-opts.sh复制到/usr/bin, 既完成了对flannel的安装 编辑服务配置文件/usr/lib/systemd/system/flanneld.service

[Unit]
Description=Flanneld overlay address etcd agent 
After=network.target
Before=docker.service
[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/flanneld
EnviornmentFile=-/etc/sysconfig/docker-network
ExecStart=/usr/bin/flanneld -etcd-endpoints=${FLANNEL_ETCD} $FLANNEL_OPTIONS
[Install]
RequiredBy=docker.service
WantedBy=multi-user.target

编辑文件/etc/sysconfig/flannel,

FLANNEL_ETCD="http://192.168.48.141:4001"
FLANNEL_ETCD_KEY="/atomic.io/network"

在启动flannel以前, 须要在etcd中添加一条网络配置记录, 这个配置将用于flannel分配给每一个Docker的虚拟IP地址段

etcdctl set /atomic.io/network/config '{"Network": "10.1.0.0/16"}'

flannel将覆盖docker0网桥, 因此要先中止docker服务. 启动flanneld服务 在每一个Node节点上执行如下命令来完成对docker0网桥的配置

/usr/libexec/flannel/mk-docker-opts.sh -i
source /run/flannel/subnet.env
ifconfig docker0 ${FLANNEL_SUBNET}

这样便完成flanne叠加网络的设置

Open vSwitch

Open vSwitch的网桥能够直接创建多种通讯通道. 例如GRE/VxLAN. 当容器内的应用访问另外一个容器的地址时, 数据包会经过容器内的默认路由发送给docker0网桥. ovs的网桥做为docker0网桥的端口存在, 它会将数据发送给ovs网桥. ovs网络已经经过配置创建了和其余ovs网桥和GRE/VxLAN隧道, 天然可以将数据送达对端的Node 正常启动Open Vswitch后, 会启动两个进程: ovsdb-server与ovs-vswitchd 建立网桥和GRE隧道: 在每一个Node上创建ovs的网桥br0, 而后在网桥上建立一个GRE隧道链接对端网桥, 最后把ovs的网桥br0做为一个端口链接到docker0这个linux网桥上(能够理解为交换机互联). 如此一来, 两个节点机器上的docker0网段就能互通了. 建立ovs网桥

ovs-vsctl add-br br0

建立GRE隧道链接对端, remote_ip为对端eth0的网卡地址. remote_ip要设置为对端的IP地址

ovs-vsctl add-port br0 gre1 -- set interface gre1 type=gre option:remote_ip=192.168.18.128

添加br0到本地docker0, 使得容器流量经过OVS流经tunnel

brctl addif docker0 br0

启动br0与docker0网桥

ip link set dev br0 up
ip link set dev docker0 up

添加路由规则, 因为两台Node的docker0网段分别为172.17.43.0/24与172.17.42.0/24, 这两个网段的路由都要通过本机的docker0网桥路由. 须要在每一个Node上添加经过docker0网桥转发的172.17.0.0/16端的路由规则. 至关于设置一个大的路由网段涵盖全部Node节点

ip route add 172.17.0.0/16 dev docker0

ping test须要, 清空docker自带iptables规则

iptables -t nat -F; iptables -F

这种网络架构的缺点很是明显, N个Node须要创建N*(N-1)条GRE隧道

k8s网络案例

在k8s上启动一个容器时, 会先启动google_containers/pause这个容器, 其余pod中的容器都会关联到这个容器上. 这个pause的容器执行端口映射规则, 这样就能够简化端口映射的过程. 使用docker port能够查看端口映射

docker port <container>

添加service以后, 全部通往以及从cluster IP出来的流量都被指向kube-proxy的某个随机端口(若是没有设置端口映射) kube-proxy做为一个全功能的代理服务器管理了两个独立的TCP链接: 一个是从容器到kube-proxy,另外一个是kube-proxy到负载均衡的目标pod.

K8s开发指南

传统的Web应用大可能是B/S架构, 涉及以下规范. 1) 客户-服务器, 这种规范的提出, 改善了用户接口跨多个平台的可移植性. 分离的用户接口和数据存储.使得不一样的用户共享相同的数据成为可能 2) 无状态性: 每次request必须包含全部的信息, 可见性: 服务端和客户端不须要了解request的历史. 可靠性: 减小了服务器从局部错误中恢复的任务量. 可伸缩性: 能够很容易的释放资源(无需会话保持). 无状态性会增长每次单独请求的开销 3) 缓存: 客户端缓存response数据的功能, 这样就能够为之后的request公用缓存数据. 可是可能会致使服务器与客户端的数据不一致性.

在传统的B/S架构下, REST提供了统一接口, 分层系统和按需代码. 1) 统一接口: 在REST世界中, 全部的事务都被抽象为资源. REST经过通用的连接器接口对资源进行操做, 极大的解耦. 2) 分层系统: 各分层系统提升了各类层次之间的独立性. 为系统复杂度提供边界. 3) 按需代码: 容许对客户端功能进行扩展.

REST提出了一下的设计准则 1) 网络上的全部事物都被抽象为资源(Resource) 2) 每一个资源对应一个惟一的资源标识符(Resource Identifier) 3) 经过通用的连接器接口(Generic Connector Interface)对资源进行操做 4) 对资源的各类操做不会改变资源标识符 5) 全部的操做都是无状态的(Stateless)

API

GET: 获取某一类型的资源列表 POST: 建立一个资源 GET: 得到单个资源 DELETE: 删除单个资源 PUT: 更新或建立资源 PATCH: 选择修改资源详细指定的域

K8s运维管理

Node的隔离:

在kind:node 下添加spec: unschedulable: true

kubectl replace -f unschedule_node.yaml

Node的扩容:

新的Node完成安装后向Master注册便可

Pod动态扩容和缩放

可使用scale rc的方式来进行操做

kubectl scale rc redis-slave --replicas=3

更新资源对象的Label

能够直接使用kubectl的label指令

kubectl label pod redis-master-bobr0 role=backend

查看响应的Label

kubectl get pods -Lrole

删除一个Label只须要在label后加一个减号便可

kubectl label pod redis-master-bobr0 role-

修改一个Label的值, 须要加上--overwrite参数:

kubectl label pod redis-master-bobr0 role=master --overwrite

将Pod调度到指定Node上

首先给Node打一个特定的标签

kubectl label nodes <node-name> <label-key>=<label-value>
kubectl label nodes node1 zone=north

在spec.template.spec.nodeSelector.zone: north 而后使用create -f 建立 node 而后查看node的分布状态

kubectl get pods -o wide

滚动升级

使用rolling-update命令, 建立一个新的RC, 而后自动控制旧的RC中的Pod副本数量逐渐减小到0.可是新旧的RC必须处在同一个Namespace中.

kubectl rolling-update redis-master -f redis-master-controller-v2.yaml

或者也能够直接指定镜像源进行滚动升级

kubectl rolling-update redis-master --image=redis-master:2.0

若是更新过程当中发现配置有误, 能够终端操做, 并使用--rollback选项进行回滚

kubectl rolling-update redis-master --image=kubeguide/redis-master:2.0 --rollback

Kubernetes的高可用方案

etcd要配置集群参考etcd的配置部分

Kubernetes Master组件的高可用方案

至少使用三台服务器安装Master服务, 而且使用Active-Standby-Standby模式, 保证任什么时候候总有一套Master可以正常工做. 可使用pacemaker等工具来实现.

k8s资源配额管理

--adminssion_control=LimiRanger, ResourceQuota spec.template.spec.name.resources.limits.cpu:0.5 spec.template.spec.name.resources.limits.memory: 128Mi cpu的0.5也能够写成500m. k8s启动一个容器时, 会将CPU的配置值乘以1024并转为整数传递给docker run的--cpu-shares参数, 之因此乘以1024是由于Docker的cpu-shares参数是以1024为技术计算CPU时间的. 这个计数仅仅是一个权重, 使用cpu时间等于权重*(全部容器权重和) 再乘以CPU核心数.

apiVersion: v1
kind: LimitRange
metadata:
  name: limit-range-1
spec:
  limits:
    - type: "Pod"
      max:
        cpu: "2"
        memory: 1Gi
      min:
        cpu: 250m
        memory: 32Mi
    - type: "Container"
      max:
        cpu: "2"
        memory: 1Gi
      min:
        cpu: 250m
        memory: 32Mi
      default:
        cpu: 250m
        memory: 64Mi

查看限额配置

kubectl describe limits

使用ResourceQuota能够实现基于租户的配额管理. 不一样租户使用不一样的命名空间 建立命名空间

apiVersion: v1
kind: Namespace
metadata:
  name: development

建立ResourceQuota配额配置

apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota-development
  namespace: development
spec:
  hard:
    cpu: "32"
    memory: 256Gi
    persistentvolumeclaims: "10"
    pods: "100"
    replicationcontrollers: "50"
    resourcequotas: "1"
    secrets: "20"
    services: "50"

查看ResourceQuota的详细信息

kubectl describe quota quota-development --namespace=development

能够理解为ResouceQuota是对总额的限定, Limitrange是对个别单位的限定

K8s监控

cAdvisor

开源软件cAdvisor用于监控容器运行状态的利器. 默认已经安装在kubelet组件中了, kubelet的启动参数 --cadviosr-port定义了cAdvisor对外提供服务的端口号, 默认为4194 cAdvisor也可使用api来获取主机的相关信息 http://master.example.com:4194/api/v1.3/machine 返回一个json文件 查看容器节点上最近一分钟内的性能数据

http://192.168.48.142:4194/api/v1.3/subcontainers/system.slice/docker-443bcc6793b26150bffd8ab00ac803309f0581b8470c7214763e10b40c08350f.scope

容器日志

能够查看资源的event日志. 使用kubectl describe pods 能够看到event信息, 对于troubleshooting很是有效 查看日志内容可使用kubectl logs

kubectl logs <pod_name>
kubectl logs <pod_name> -c <container_name>

至关于容器的命令

docker logs <container_id>

查看kubernetes的系统日志

journalctl -u kube-controller-manager

单独指定日志存放目录: --log-dir=/var/log/kubernetes

K8s社区

https://github.com/GoogleCloudPlatform/kubernetes/wiki/User-FAQ https://github.com/GoogleCloudPlatform/kubernetes/wiki/Debugging-FAQ

kubernetes DNS服务配置

Pod在访问其余Service时, 能够经过两种服务发现方式来完成, 即环境变量和DNS的方式. 使用环境变量是有限制条件的, 即Service必须在Pod以前被建立出来, 而后系统才能在新建的Pod中自动设置与Service相关的环境变量. DNS则没有这个限制

K8s提供的DNS由如下三个组件组成 1) etcd: DNS存储 2) kube2sky: 将Kubernetes Master中的Service注册到etcd 3) skyDNS: 提供DNS域名解析服务 这三个组件均可以以Pod的方式启动和运行, 在K8s集群中须要将各个Pod之间的网络打通. 建立skydns-rc2.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: kube-dns-v6
  namespace: default
  labels:
    k8s-app: kube-dns
    version: v6
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    k8s-app: kube-dns
    version: v6
  template:
    metadata:
      labels:
        k8s-app: kube-dns
        version: v6
        kubernetes.io/cluster-service: "true"
    spec:
      containers:
      - name: etcd
        image: gcr.io/google_containers/etcd:2.0.9
        command:
        - /usr/local/bin/etcd
        - -listen-client-urls
        - http://0.0.0.0:2379,http://0.0.0.0:4001
        - -advertise-client-urls
        - http://127.0.0.1:2379,http://127.0.0.1:4001
        - -initial-cluster-token
        - skydns-etcd
      - name: kube2sky
        image: gcr.io/google_containers/kube2sky:1.11
        resources:
          limits:
            cpu: 100m
            memory: 50Mi
        command:
        - /kube2sky
        - --kube_master_url=http://10.8.65.48:8080
        - -domain=kube.local
      - name: skydns
        image: gcr.io/google_containers/skydns:2015-03-11-001
        resources:
        command:
        - /skydns
        - -machines=http://localhost:4001
        - -addr=0.0.0.0:53
        - -domain=kube.local.
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
      dnsPolicy: Default

建立skydns-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: default
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "KubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.254.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP

启动DNSserver的cluster启动ip是10.254.0.10 修改每台Node上Kubelet的启动参数 --cluster_dns=10.254.0.10 --cluster_domain=cluster.local

kube2sky容器应用经过调用kubernetes master的API得到集群中全部Service的信息, 并持续监控新Service的生成. 而后写入etcd中

kubectl exec kube-dns-v6-5tpm2 -c etcd --namespace=default etcdctl ls /skydns/kube/local

在域名下kube.local的域名下能够查看各服务对应的键值, 能够看到完整的域名解析redis-master.default.kube.local

kubectl exec kube-dns-v6-5tpm2 -c etcd --namespace=kube-system etcdctl get /skydns/local/kube/local/default/redis-master

在每一个Pod中的/etc/resolv.conf文件中添加了DNS域名解析

nameserver 10.254.0.10
search default.svc.kube.local svc.kube.local kube.local localdomain

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

test

相关文章
相关标签/搜索