K8sSVNOpenshiftDoker容器技术Docker部署Docker配置参数Docker命令使用registry镜像建立私有仓库使用Dockerfile制做镜像Docker Volumedocker容器互联基于路由的容器互联namespaceDocker管理工具ReigstryKubernetesKubernetes重要概念Kubernets实践Kubernetes的版本升级Kubernetes定义kubernetes部署示例Kubernets 对外Service多端口服务VolumeNamespaceETCDETCD集群Kubernetes 核心原理ResourceQuota ControllerServiceAccount Controller与Token Controller容器健康检查安全机制原理Authenication认证Auhorization受权Admission Control 插件集群安全配置案例双向认证配置简单认证配置HTTP base认证Kuberntes 网络原理网桥Iptables/Netfilter路由Docker的网络实现Kubernetes的网络实现开源的网络组件直接路由Flannel安装flannelOpen vSwitchk8s网络案例K8s开发指南APIK8s运维管理Node的隔离:Node的扩容:Pod动态扩容和缩放更新资源对象的Label将Pod调度到指定Node上滚动升级Kubernetes的高可用方案Kubernetes Master组件的高可用方案k8s资源配额管理K8s监控cAdvisor容器日志kubernetes DNS服务配置testphp
Subversion 官网html
http://subversion.tigris.org/前端
案例新浪云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
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
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]
配置文件为/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
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
建立本地的镜像仓库
建立镜像的方法有三种, 基于已有镜像建立, 基于本地模板导入以及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的架构
/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
容器间基于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的概念, 还在继续的发展中.
平行宇宙. 在不一样虚拟机中的网卡彼此是不可见的, 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
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
Registry包含一个或多个Repository Repository包含一个或多个Image. Image使用GUID表示, 有一个或多个Tag与之关联
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) 基金会
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上的存储卷.
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.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/
测试镜像文件在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
建立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
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"
存储卷是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等等
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是高可用的key/value存储系统, 用于持久化存储集群中. API Server则提供了操做etcd的封装接口API, 以REST方式提供服务, 以REST方式提供服务.
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
资源对象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
Kubernetes 支持三个层次的资源配额管理 1. 容器级别 2. Pod级别, 3. Namespace级别 包括的限制是Pod数量; Replication Controller数量; Service数量; ResourceQuota数量; Secret数量; PV(Persistent Volume) 数量 LimitRanger做用于Pod和Container上, ResourceQuota则做用于Namespace上
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
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"
在经过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"}
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
建立用户名密码和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
每一个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接收到的报文被提交给网桥处理代码.
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支持一下四类网络模式 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
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
过段时间后就会自动添加路由规则
它能够协助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自己并不可靠.
下载地址 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的网桥能够直接创建多种通讯通道. 例如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上启动一个容器时, 会先启动google_containers/pause这个容器, 其余pod中的容器都会关联到这个容器上. 这个pause的容器执行端口映射规则, 这样就能够简化端口映射的过程. 使用docker port能够查看端口映射
docker port <container>
添加service以后, 全部通往以及从cluster IP出来的流量都被指向kube-proxy的某个随机端口(若是没有设置端口映射) kube-proxy做为一个全功能的代理服务器管理了两个独立的TCP链接: 一个是从容器到kube-proxy,另外一个是kube-proxy到负载均衡的目标pod.
传统的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)
GET: 获取某一类型的资源列表 POST: 建立一个资源 GET: 得到单个资源 DELETE: 删除单个资源 PUT: 更新或建立资源 PATCH: 选择修改资源详细指定的域
在kind:node 下添加spec: unschedulable: true
kubectl replace -f unschedule_node.yaml
新的Node完成安装后向Master注册便可
可使用scale rc的方式来进行操做
kubectl scale rc redis-slave --replicas=3
能够直接使用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
首先给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
etcd要配置集群参考etcd的配置部分
至少使用三台服务器安装Master服务, 而且使用Active-Standby-Standby模式, 保证任什么时候候总有一套Master可以正常工做. 可使用pacemaker等工具来实现.
--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是对个别单位的限定
开源软件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
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