docker学习笔记
容器、docker 概念
部署演变
物理机/KVM
- 部署慢
- 成本高
- 资源浪费,CPU内存用不完
- 很难迁移
虚拟化的优点
- 资源得到合理分配
- 容易扩展
- 很容易云化
容器是实现 DevOps 的解决方案
docker 能做什么
- 搭环境,简化配置
- 提高开发效率
- 隔离应用
docker 安装
使用 package 安装
- 从https://download.docker.com/linux/ubuntu/dists/ 或者 https://download.docker.com/linux/debian/dists/ 下载 .deb 文件
- 安装 Docker
sudo dpkg -i /path/to/package.deb
在线安装
官方脚本安装
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
apt 安装
# 将官方Docker库的GPG公钥添加到系统中
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 将Docker库添加到apt里
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
# 再次更新下apt库列表
apt update
# 开始安装docker-ce
apt install docker-ce
# 安装完成查询版本号
docker --version
# 为 docker 添加当前用户
service docker start
usermod -aG docker $USER
# 开始安装docker-compose
apt install docker-compose
# 安装完成后查询docker-compose版本号
docker-compose --version
yum安装
# 安装yum-utils包(提供yum-config-manager 实用程序)并设置存储库
yum install -y yum-utils
# 将Docker库添加到yum里
yum-config-manager --add-repo http://download.docker.com/linux/centos/docker-ce.repo
# 查看可用版本有哪些
yum list docker-ce --showduplicates | sort -r
# 安装最新版本的 Docker Engine、containerd 和 Docker Compose
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 安装完成查询版本号
docker --version
# 为 docker 添加当前用户
service docker start
usermod -aG docker $USER
启动 docker
systemctl start docker
## 开机自启动
systemctl enable docker
将用户添加到 docker 组中(非 root 用户)
sudo gpasswd -a username docker
# 立即生效
newgrp docker
实现 helloworld
docker run hello-world
docker 基本使用
镜像和容器
底层技术支持:
- namespace:做隔离,pid,net,可以启动多个容器
- control groups:做资源限制,比如对内存、CPU做限制
- union file system:镜像的分层
Image
查看本地镜像
docker image ls
拉取镜像
docker pull ubuntu:14.04
创建 .py 文件
print("Hello docker!\n")
编写 Dockerfile
vi Dockerfile
# 从头开始
FROME scratch
# 添加到根目录
ADD hello /
# 运行
CMD ["python hello.py"]
构建 image
docker build -t dfuttu/hello_world .
“-t”指定自己的 docker 账号,“.” 表示使用当前目录下的 Dockerfile
查看image的分层
docker history dfuttu/hello_world
Dockerfile
FROM:引入和开始
# 从头制作 baseImage
FROM scratch
# 使用已有的 baseImage
FROM centos
# 指定使用的版本
FROM ubuntu:14.04
LABEL:定义一些说明信息
LABEL maintainer=dfuttu
LABEL version="1.0"
LABEL description="xxxx"
RUN:执行
每一条 RUN,多一个分层,一般用 && 合并语句,反斜杠换行
RUN yum update && yum -y install lrzsz \
net-tools
RUN apt-get -y update && apt-get -y install lrzsz \
net-tools
RUN /bin/bash -c 'source $HOME/.bashrc;echo $HOME'
WORKDIR:进入或创建目录
尽量不要使用相对路径
# 进入 /root 目录
WORKDIR /root
# 如果没有会自动创建
WORKDIR /test
WORKDIR demo
# 输出:/test/demo
RUN pwd
ADD 和 COPY:将本地文件添加到 image 里
# 将 hello 添加到根目录
ADD hello /
# 将 tar 包解压到根目录
ADD test.tar.gz
# 最终 hello 应该在 /root/test/hello
WORKDIR /root
COPY hello test/
ENV:增加 Dockerfile 的可读性
# 设置常量
ENV MYSQL_MAJOR 5.5
# 使用常量
RUN apt-get -y install mysql-server="${MYSQL_VERSION}"
CMD 和 ENTRYPOINT
# shell
RUN apt-get -y install lrzsz
CMD echo "hello docker"
ENTTYPOINT echo "hello docker"
# exec 格式
RUN ["apt-get", "-y", "install", "lrzsz"]
CMD ["/bin/echo", "hello docker"]
ENTRYPOINT ["/bin/echo", "hello docker"]
ENTRYPOINT 使用的比 CMD 多一些
ENTRYPOINT:设置容器启动时运行的命令,一定会执行,不会被忽略
ENTRYPOINT
编写 Dockerfile
# shell 格式
FROM ubuntu:14.04
ENV name Docker
ENTRYPOINT echo "Hello $name"
# exec 格式
FROM ubuntu:14.04
ENV name Docker
ENTRYPOINT ["/bin/bash", "-c", "echo Hello $name"]
构建 image
docker build -t test1 .
CMD
设置容器启动后默认执行的参数和命令,若 docker 指定了其他命令,CMD 被忽略。CMD 如果指定多个,只运行最后一个。
分享 docker image
# 登录
docker login
#
docker image push dfuttu/test
创建私有 docker registry
# 在另一台机器上
docker run -d -p 5000:5000 --restart always --name registry -v /opt/data/registry:/var/lib/registry registry:2
# -v:数据卷的挂载,
# 查看私有仓库的内容
192.168.20.216:5000/v2/_catalog
# 在本机测试
telnet 192.168.20.216 5000
# 构建 docker 镜像
docker build -t 192.168.20.216:5000/hello
# 直接 push 会报错
docker push 192.168.20.216:5000/hello
## 创建 json 文件
vim /etc/docker/daemon.json
## json 文件内容:
{"insecure-registries":["192.168.20.216:5000"]}
## 修改文件
vim /lib/systemd/system/docker.service
## 在 ExecStart 和 ExecReload 中间添加一行:
EnvironmentFile=/etc/docker/daemon.json
## 重启 docker
service docker restart
# 重新 push
docker push 192.168.20.216:5000/hello
数据卷
绑定数据卷
使用--mount 绑定
mkdir /webapp
docker run -d --name web -P --mount type=bind,source=/webapp,destination=/opt/webapp training/webapp python app.py
# -P:开放容器内暴露的所有端口监听
# --mount:启动容器时绑定数据卷
# type=bind:以端口监听方式进行绑定
# source:源地址(本机)
# destination:容器内地址
使用 -v 绑定
docker run -d --name registry -p 5000:5000 -v /opt/data/registry:/var/lib/registry registry:2
数据卷容器
# 创建 dbdata 容器
docker run -it -v /dbdata --name dbdata ubuntu:18.04
# dbdata 容器内查看
ls

# 退出 dbdata 容器
exit
# 创建 db1 容器,并挂载数据卷到容器 dbdata
docker run -it --volumes-from dbdata --name db1 ubuntu:18.04
# db1 容器内查看
ls

# 创建 /dbdata/volumes_test 文件
touch /dbdata/volumes_test
# 退出
exit
# 创建 db2 容器,并挂载数据卷到容器 dbdata
docker run -it --volumes-from dbdata --name db2 ubuntu:18.04
# db2 容器内查看
ls /dbdata


利用数据卷容器迁移数据
首先利用 ubuntu 镜像创建了一个容器 worker。使用 --volumes-from dbdata 参数来让 worker 容器挂 dbdata 容器的数据卷(即 dbdata 数据卷);使用 -v $pwd:/backup 参数来挂载本地的当前目录到 worker 容器的 /backup 目录。
worker容器启动后,使用 tar cvf /backup/backup.tar/dbdata 命令将 /dbdata 下内容备份为容器内的 /backup/backup.tar,即主机当前目录下的 backup.tar。
docker run --volumes-from dbdata -v $(pwd):/backup --name worker ubuntu:18.04 tar cvf /backup/backup.tar /dbdata
# c:compress 压缩
# f:指定文件

Dockerfile案例
1 python 文件
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello Docker!"
if __name__ == '__main__':
app.run()
2 编写 Dockerfile
FROM python:3.7
LABEL maintainer='dfuttu@qq.com'
RUN pip install flask
# 将app.py复制到/app/目录下
COPY app.py /app/
# 设置工作目录
WORKDIR /app
# 暴露端口
EXPOSE 5000
CMD ["python","app.py"]
COPY 和 ADD 的区别:会解压压缩文件
3 构建 image
docker build -t dfuttu/flask-hello .
Docker 的网络
macvlan 网络

docker network create
-d macvlan # 网络驱动
# 32位的二进制
# 1111 1111 1111 1111 1111 1111 1111 1111(32位 0/24表示前24为1,后8位为0)
--subnet=172.16.86.0/24 # 子网
--gateway=172.16.86.1 # 网关
-o parent=ens33 # 父网络接口(从 ifconfig 中查找)
pub_net # 网络接口名称
bridge 网络
1 可以看到 nifty_grothendieck 和本机的网卡有veth对
docker exec nifty_grothendieck ip a
ip a
2 安装 brctl
apt-get -y install bridge-utils
brctl show

在本机查看


容器通信
1 首先启动一个容器
docker start test3
# 或者
docker run -d --name test3 busybox /bin/sh -c "while true;do sleep 3600;done"
2 再创建一个容器
docker run -d --name test4 --link test3 busybox /bin/sh -c "while true;do sleep 3600;done"
3 此时可以用 test4 直接访问 test3,类似于访问主机名

端口映射
1 运行一个 nginx 容器
docker run -d --name web nginx
2 查看桥接网络状况
docker network inspect bridge
3 容器内可以访问,外网访问不到
4 停止并删除容器
docker stop web
docker rm web
5 重新创建容器,并指定端口映射
docker run -d -p 80:80 --name web nginx
6 外网可以访问
网络的 none 和 host
none 网络
none: 主机模式(外界查看不到), 应用场景是安全性极高的情况
1 创建新的容器,使用 none 网络
docker run -d --name test5 --network none busybox /bin/sh -c "while true;do sleep 3600;done"
2 查看 none 网络状态
docker network inspect none
3 外界访问不了, 只有进去容器可以访问

host 网络
1 创建新的容器,使用 host 网络
docker run -d --name test5 --network host busybox /bin/sh -c "while true;do sleep 3600;done"
2 查看 none 网络状态
docker network inspect host
3 进入容器, 查看网络

多容器部署和应用
应用: flask 做 web 服务, redis 做自增
1 停止并删除之前的容器
docker stop $(docker container ls -aq)
docker rm $(docker container ls -aq)
2 运行 redis 容器
docker run -d --name redis redis
3 编写 py 文件, 做 web 服务
from flask import Flask
from redis import Redis
import os
import socket
app = Flask(__name__)
redis = Redis(host = os.environ.get('REDIS_HOST', '127.0.0.1'), port = 6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello Docker! Count is %s and hostname is %s. \n'%(redis.get('hits'), socket.gethostname())
if __name__ == "__main__":
app.run(host = '0.0.0.0', port = 5000, debug = True)
4 编写 Dockerfile
FROM python:3.7
LABEL maintaner="dfuttu@qq.com"
COPY . /app
WORKDIR /app
RUN pip3 install flask redis
EXPOSE 5000
CMD ["python", "app.py"]
5 构建 image
docker build -t dfuttu/flask-redis .
6 运行容器
docker run -d --name flask-redis --link redis -e REDIS_HOST=redis dfuttu/flask-redis
-e 创建环境变量
7 进入容器并查看环境变量
docker exec -it flask-redis /bin/sh
env
8 多次访问 5000 端口,实现自增.
curl 127.0.0.1:5000

9 删除容器, 重新运行, 指定映射端口
docker stop flask-redis
docker rm flask-redis
docker run -d -p 5000:5000 --name flask-redis --link redis -e REDIS_HOST=redis dfuttu/flask-redis
多机器多容器通信

两台机器 ip 不能一样
注意: 多机的 docker 版本要一样
使用 etcd + overlay 网络解决冲突问题。
1 下载 etcd,分别放到2台机器的 /usr/local
# 下载
wget http://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz
# 解压
tar -zxvf etcd-v3.0.12-linux-amd64.tar.gz
# 进入目录
cd etcd-v3.0.12-linux-amd64


2 启动 etcd
# node01
nohup ./etcd --name docker-node1 --initial-advertise-peer-urls http://192.168.1.37:2380 \
--listen-peer-urls http://192.168.1.37:2380 \
--listen-client-urls http://192.168.1.37:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.1.37:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://192.168.1.37:2380,docker-node2=http://192.168.1.38:2380 \
--initial-cluster-state new&
# node02
nohup ./etcd --name docker-node2 --initial-advertise-peer-urls http://192.168.1.38:2380 \
--listen-peer-urls http://192.168.1.38:2380 \
--listen-client-urls http://192.168.1.38:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.1.38:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://192.168.1.37:2380,docker-node2=http://192.168.1.38:2380 \
--initial-cluster-state new&
3 查看集群状态
./etcdctl cluster-health
如果不行,尝试删除所有防火墙
iptables -F
4 停止 docker
service docker stop
5 重新启动 docker(使用 etcd 分布式存储)
# docker 启动命令(node01)
/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.1.37:2379 --cluster-advertise=192.168.1.37:2375&
# docker 启动命令(node02)
/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.1.38:2379 --cluster-advertise=192.168.1.38:2375&
6 创建 overlay 网络
docker network create -d overlay demo
第一台机器:

第二台机器:

发现两台机器联动
7 查看分布式数据库 etcd 存储的数据

8 在 node01 创建容器并运行
docker run -d --name test10 --net demo busybox sh -c "while true;do sleep 3600;done"
9 在 node02 创建容器并运行
docker run -d --name test10 --net demo busybox sh -c "while true;do sleep 3600;done"

容器名重复,因为 demo 网络中已经存在 test10

换名字,创建成功
docker run -d --name test11 --net demo busybox sh -c "while true;do sleep 3600;done"

flannel 网络
- fannel 的功能是让集群中的不同节点主机创建的 Docker 容器都具有全集群唯一的虚拟 IP 地址。但在默认的Docker 配置中,每个节点上的 Docker 服务会分别负责所在节点容器的 IP 分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外 IP 地址。并使这些容器之间能够之间通过 IP 地址相互找到,也就是相互ping 通。Flannel 设计目的就是为集群中所有节点重新规划 lP 地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且“不重复的" IP 地址,并让属于不同节点上的容器能够直接通过内网 IP 通信。
- 特点:Flannel 使用 etcd 存储配置数据和子网分配信息。flannel 启动之后,后台进程首先检索配置和正在使用的子网列表,然后选择一个可用的子网,然后尝试去注册它。etcd 也存储这个每个主机对应的 ip。flannel使用 etcd 的 watch 机制监视 /coreos.com/network/subnets 下面所有元素的变化信息,并且根据它来维护一个路由表。
- flannel 网络解决方案中,默认的节点间数据通信方式是 UDP 转发;flannel 默认使用 8285 端口作为 UDP 封装报文的端口,VxLan(理解成虚拟局域网)使用 8472 端口
- 其架构图如下:

- 如上图,flannel 工作原理可以概述如下:
1)数据从源容器中发出后,经由所在主机的 docker0 虚拟网卡转发到 flannel0 虚拟网卡,这是个 P2P 的虚拟网卡,flanneld 服务监听在网卡的另外一端。
2)Flannel 通过 Etcd 服务维护了一张节点间的路由表,该张表里保存了各个节点主机的子网网段信息
3)源主机的 flanneld 服务将原本的数据内容 UDP 封装后根据自己的路由表投递给目的节点的 flanneld 服务,数据到达以后被解包,然后直接进入目的节点的 flannel0 虚拟网卡,然后被转发到目的主机的 docker0 虚拟网卡,最后就像本机容器通信一样的由 docker0 路由到达目标容器。
环境
- master: 部署 docker, flannel, etcd
- node: 部署 docker, flannel
- master操作
绑定 host
vi /etc/hosts

setenforce 0
systemctl stop firewalld
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
安装 etcd
- 安装
yum install -y etcd
- 修改 etcd 配置文件
vi /etc/etcd/etcd.conf

注:2379 是 etcd 的注册端口
4001 是私有端口
- 启动验证节点
systemctl start etcd
ps -ef | grep etcd
lsof -i:2379


安装 flannel
- 安装
yum install -y flannel
- 修改配置文件
vi /etc/sysconfig/flanneld

Flannel 使用 etcd 进行配置(这个只在安装了 etcd 的机器上操作),来保证多个 flannel 实例之间的配置一致性,所以需要配置 etcd 中关于 flannel 的 key
- 获取 etcd 关于 flannel 的配置
etcdctl get /atomic.io/network/config
如果结果为空,进行配置
etcdctl mk /atomic.io/network/config '{"Network":"10.10.0.0/16"}'
- 启动 flanneld
systemctl start etcd
- 修改 docker 配置文件
vi /usr/lib/systemd/system/docker.service

--bip 表示:所在节点的容器最大的 ip 范围
--mtu 数据的最大传输单位
注意:添加的 -bip 和 -mtu 两个参数的值,必须与下图的内容完全一样

- 重载配置文件
systemctl daemon-reload
- 重启docker
systemctl restart docker
- 查看网络
ifconfig

- node 操作与 master 类似
- 创建容器, 验证跨主机容器之间的网络连通性
master:
docker run -dit --name flannel1 busybox
docker exec -it flannel1 /bin/sh
ip a

node:
docker run -dit --name flannel2 busybox
docker exec -it flannel2 /bin/sh
ip a

- 测试跨主机容器之间的网络连通性
master
ping 10.10.85.2
如果容器无法联通,是由于 flannel0 网卡和 docker0 网卡通过 IPTABLES 的 FORWARD 转发,所以
需确保如下设置
# 核中的forwar功能开启(立即生效,重启后效果不再)
echo "1" > /proc/sys/net/ipv4/ip_forward
# 包不会被 iptables 的 forward 规则拦截
iptables -P FORWARD ACCEPT
k8s
背景
将程序或者服务部署到docker之后,来思考以下几个问题:
- 随着功能丰富,业务量增加、可能需要更多的服务或者微服务,这种情况下,如何进行容器的扩展?
- 如何在容器之间分担负载?如果服务器已经使用到极限,并且我们的容器需要另一台服务器,该怎么办?如何计算最佳的硬件利用率?
- 如何在不影响任何内容的情况下进行更新?如果需要,该如何回退到问工作的版本?
Kubernetes (来自希腊语,意为“舵手"或飞行员")由 Joe Beda、Brendan Burnsi 和 Craig McLuckiet 创立,而后 Google 的其他几位工程师,包括 Brian Grant 和 Tim Hockin 等加盟共同研发,并由 Google 在 2014 年首次对外宣布。Kubernetes 的开发和设计都深受 Google 内部系统 Borg 的影响。事实上,它的许多顶级贡献者之前也是 Borg 系统的开发者。
Borg 是 Google 内部使用的大规模集群管理系统。它建构于容器技术之上,目的是实现资源管理的自动化,以
及跨多个数据中心的资源利用率最大化。
什么是 k8s
简单说来,k8s 就是一款容器编排王具,它能够帮助我们实现容器的调度管理,资源分配,自动更新部署实现
等操作
为什么要用 k8s
- 可移植:支持公有云,私有云,混合云,多重云(muti-cloud)
- 可扩展:模块化,插件化,可挂载,可组合
- 自动化:自动部署,自动重启,自动复制,自动伸缩/扩展
k8s 特性
- 自动装箱:建构于容器之上,基于资源依赖及其他约束自动完成容器部署且不影响其可用性,并通过调度机制混合关键型应用和非关键型应用的工作负载于同一节点以提升资源利用率。
- 自我修复(自愈):支持容器故障后自动重启、节点故障后重新调度容器,以及其他可用节点、健康状态检查失败后关闭容器并重新创建等自我修复机制。
- 水平扩展:支持通过简单命令或 UI 手动水平扩展,以及基于 CPU 等资源负载率的自动水平扩展机制。
- 服务发现和负载均衡:Kubernetes 通过其附加组件之一的 KubeDNS(或 CoreDNS)为系统内置了服务发现功能,它会为每个 Service 配置 DNS 名称,并允许集群内的客户端直接使用此名称发出访问请求,而Service 则通过 iptables 或 ipvs 内建了负载均衡机制。
- 自动发布和回滚:Kubernetes 支持“灰度"更新应用程序或其配置信息,它会监控更新过程中应用程序的健康状态,以确保它不会在同一时刻杀掉所有实例,而此过程中一旦有故障发生,就会立即自动执行回滚操作!
- 密钥和配置管理:Kubernetes 的 ConfigMap 实现了配置数据与 Docker 镜像解耦,需要时,仅对配置做出变更而无须重新构建 Docker 镜像,这为应用开发部署带来了很大的灵活性。此外,对于应用所依赖的一些敏感数据,如用户名和密码、令牌、密钥等信息,Kubernetes 专门提供了 Secret 对象为其解耦,既便利了应用的快速开发和交付,又提供了一定程度上的安全保障。
- 存储编排:Kubernetes 支持 Pod 对象按需自动挂载不同类型的存储系统,这包括节点本地存储、公有云服务商的云存储(如 AWS 和 GCP 等),以及网络存储系统(例如,NFS、iSCSI、GlusterFS、Ceph、Cinder 和Flocker 等)。
- 批量处理执行:除了服务型应用,Kubernetes 还支持批处理作业及 CI(持续集成),如果需要,一样可以实现容器故障后恢复。
k8s 集群组件
- 简述:一个典型的 Kubernetes 集群由多个工作节点(worker node)和一个主节点(control plane,即Master)以及一个集群状态存储系统(etcd)组成。其中 Master 节点负责整个集群的管理工作,为集群提供管理接口,并监控和编排集群中的各个工作节点。各节点负责以 Pod 的形式运行容器,因此,各节点需要事先配置好容器运行依赖的所有服务和资源,如容器运行时环境等,其集群系统架构如下:

Master
- 说明:master:Master 是集群的网关和中枢,负责诸如为用户和客户端暴露 APl、跟踪其他服务器的健康状态、以最优方式调度工作负载,以及编排其他组件之间的通信等任务,它是用户或客户端与集群之间的核心联络点,并负责 Kubernetes 系统的大多数集中式管控逻辑。单个 Master 节点即可完成其所有的功能,但出于冗余及负载均衡等目的,生产环境中通常需要协同部署多个此类主机。
- apiserver:API Server 负责输出 RESTFUL 风格的 Kubernetes API,它是发往集群的所有 REST 操作命令的接入点并负责接收、校验并响应所有的 REST 请求,结果状态被持久存储于 etcd 中。因此,API Server 是整个集群的网关。
- etcd:Kubernetes 集群的所有状态信息都需要持久存储于存储系统 etcd 中,不过,etcd 是由基于 Raf 协议开发的分布式键值存储,可用于服务发现、共享配置以及一致性保障(如数据库主节点选择、分布式锁等)。因此,etcd是独立的服务组件,并不隶属于 Kubernetes 集群自身。etcd 不仅能够提供键值数据存储,而且还为其提供了监听(watch)机制,用于监听和推送变更。Kubernetes 集群系统中,etcd 中的键值发生变化时会通知到 API Server,并由其通过 watch API 向客户端输出。基于 watch 机制,Kubernetes 集群的各组件实现了高效协同。
- scheduler:Kubernetes 是用于部署和管理大规模容器应用的平台,根据集群规模的不同,其托管运行的容器很可能会数以千计甚至更多。API Servert 确认 Pod 对象的创建请求之后,便需要由 Scheduler 根据集群内各节点的可用资源状态,以及要运行的容器的资源需求做出调度决策。其调度方式如下:

- controller-manager:Kubernetest 中,集群级别的大多数功能都是由几个被称为控制器的进程执行实现的,这几个进程被集成于 kube-controller-manager 守护进程中。由控制器完成的功能主要包括生命周期功能和 API 业务逻辑。
Node
- 说明:Node 是 Kubernetes 集群的工作节点,负责接收来自 Master 的工作指令并根据指令相应地创建或销毁 Pod 对象,以及调整网络规则以合理地路由和转发流量等
- kubelet(核心代理程序:kubelet 是运行于工作节点之上的守护进程它从 API Server 接收关于 Pod 对象的配置信息并确保它们处于期望的状态。kubelet 会在 API Server 上注册当前工作节点定期向 Master 汇报节点资源使用情况并监控容器和节点的资源占用状况
- docker(容器运行时环境):每个 Node 都要提供一个容器运行时 Container Runtime 环境它负责下载镜像并运行容器。kubelet 并未固定链接至某容器运行时环境,而是以插件的方式载入配置的容器环境。这种方式清晰地定义了各组件的边界
- kube-proy:每个工作节点都需要运行一个 kube-proy 守护进程,它能够按需为 Service 资源对象生成iptables 或 ipvs 规则,从而捕获访问当前 Service 的 Cluster 的流量并将其转发至正确的接收服务
k8s 基本概念
说明
Kubernetes 使用共享网络将多个物理机或虚拟机汇集到一个集群中,在各服务器之间进行通信,该集
群是配置 Kubernetes 的所有组件、功能和工作负载的物理平台。集群中一台服务器(或高可用部署中的一组
服务器)用作 Master,负责管理整个集群,余下的其他机器用作 Worker Node,它们是使用本地和外部资源
接收和运行工作负载的服务器。集群中的主机可以是物理服务器,也可以是虚拟机。
资源抽象概念
- pod:Kubernetes 并不直接运行容器,而是使用一个抽象的资源对象来封装一个或者多个容器,这个抽象即为 Pod,它也是 Kubernetes 的最小调度单元。同一 Pod 中的容器共享网络名称空间和存储资源,这些容器可经由本地回环节口 lo 直接通信,但彼此之间又在 Mout、User 及 PID 等名称空间上保持了隔离。

一般来说 pod 有两种使用方式:
运行单一容器。one-container-per-Pod 是 Kubernetes 最常见的模型,这种情况下,只是将单个容器简单封装成 Pod。即便是只有一个容器,Kubernetes 管理的也是 Pod 而不是直接管理容器。
运行多个容器。在一个 Pod 中运行多个容器的话,我们需要考虑的问题是哪些容器应该放到一个 Pod 中?一般是在容器之间有非常紧密的联系并且需要直接共享数据资源的情况下,会运行多个容器。

这里我们有两个容器,一个 file puller,一个 web server。File Puller 会定期从外部的 Content Manager 中拉取最新的文件,将其存放在共享的 volume 中。Web Server 从 volume 读取文件,响应 Consumer 的请求。这两个容器是紧密协作的,它们一起为 Consumer 提供最新的数据:同时它们也通过 volume 共享数据。所以放到一个 Pod 比较合适。
- 资源标签:标签(Label)是将资源进行分类的标识符,标签可以在对像创建时附加其上,并能够在创建后的任意时间进行添和修改。一个对象可以拥有多个标签,一个标签也可以附加于多个对象(通常是同一类对像)。如下,每个 pod 都有三个标签,每个标签实际上就是一个 key/value

- 标签选择器:是一种根据 Label 来过滤符合条件的资源对像的机制。具体应用如下:将附有标签“role: backend”的所有 Pod 对象挑选出来归为一组

- pod 控制器:Pod 控制器是用实现管理 pod 的中间层,确保 pod 资源符合预期的状态,pod 的资源出现故障时,会尝试进行重启,当根据重启策略无效,则会重新新建 Pod 的资源。分类如下:

- 服务资源(service):Service 是建立在一组 Pod 对象之上的资源抽象,它通过标签选择器选定一组 Pod 对象,并为这组 Pod 对象定义个统一的固定访问入口(通常是一个 IP 地址),若 Kubernetes 集群存在 DNS 附件,它就会在 Service 创建时为其自动配置一个 DNS 名称以便客户端进行服务发现。到达 Service IP 的请求将被负载均衡至其后的端点:各个 Pod 对象之上。

- 存储卷:与 docker 存储卷类似
- Name 和 Namespace:名称(Name)是 Kubernetes 集群中资源对象的标识符,它们的作用域通常是名称空间(Namespace),因此名称空间是名称的额外的限定机制。在同一个名称空间中,同一类型资源对象的名称必须具有唯一性。名称空间通常用于实现租户或项目的资源隔离,从而形成逻辑分组。如下所示

- Annotation:记录辅助信息,一般用来记录如下信息
构建、发布的镜像信息,如时间戳,发行ID,git 分支,PR 编号,镜像 hashes 和注 Registry 地址。
一些日志记录、监视.分析或 audit repositories。
一些工具信息:例如,名称、版本和构建信息。
用户或工具系统来源信息,例如来自其他生态系统组件对象的 URL。
负责人电话/座机,或一些信息目录。
- Ingress:Kubernetes 将 Pod 对象和外部网络环境进行了隔离,Pod 和 Service 等对象间的通信都使用其
内部专用地址进行,如若需要开放某些 Pod 对象提供给外部用户访问,则需要为其请求流量打开一个通
往 Kubernetes 集群内部的通道,除了 Service之外,Ingress 也是这类通道的实现方式之一。
k8s网络模型
kubernates 的网络中主要存在四种类型的通信
- 同一 Pod 内的容器间通信
- 各 Pod 彼此之间的通信
- Pod 与 Service 间的通信
- 集群外部的流量同 Service 之间的通信。
基本网络模型如下

kubernetes 网络模型实现机制特点
- 所有 Pod 间均可不经 NAT 机制而直接通信。
- 所有节点均可不经 NAT 机制而直接与所有容器通信。
- 容器自己使用的 IP 也是其他容器或节点直接看到的地址
minikube
正常情况下,使用 k8s 做集群部署,需要多服务器搭建集群,而通过 minikube 则可以快速的在单机
上搭建一个 k8s 集群(模拟),并进行相应的操作,用于学习和测试非常方便
下载安装
1 kubectl 国内下载安装
# 下载
wget https://dl.k8s.io/v1.14.1/kubernetes-client-linux-amd64.tar.gz
# 解压
tar -zxvf kubernetes-client-linux-amd64.tar.gz
# 移到全局文件夹
mv kubernetes/client/bin/kubectl /usr/local/bin/
2 minikube 安装
curl -LO https://storage.googleapis.com/minikube/releases/v1.1.0/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
基本操作
- 清理 minikube cluster 环境
# 删除本地集群
minikube delete
# 删除缓存文件
rm -rf ~/.minikube/
- 启动 minikube 并创建一个集群
minikube start -p 1805-cluster --vm-driver=none --registry-mirror=https://registry.docker-cn.com
如果创建失败,可能是镜像拉取失败(国内网络不能拉取)

可以查看需要镜像
kubeadm config images list --config /var/lib/kubeadm.yaml

手动下载
# 编写拉取脚本
vi kube-pull-images.sh
## 内容如下
## 从国内镜像拉取
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.14.2
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.14.2
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.14.2
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.14.2
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.10
docker pull coredns/coredns:1.3.1
# 运行
sh kube-pull-images.sh
# 改名
# 编写重命名脚本
vi kube-tag-images.sh
## 内容如下
#将拉取下来的images重命名为kubeadm config所需的镜像名字
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.14.2 k8s.gcr.io/kube-apiserver:v1.14.2
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.14.2 k8s.gcr.io/kube-controller-manager:v1.14.2
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.14.2 k8s.gcr.io/kube-scheduler:v1.14.2
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.14.2 k8s.gcr.io/kube-proxy:v1.14.2
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1 k8s.gcr.io/pause:3.1
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.10 k8s.gcr.io/etcd:3.3.10
docker tag coredns/coredns:1.3.1 k8s.gcr.io/coredns:1.3.1
# 运行
sh kube-tag-images.sh
# 删除多余镜像
vi kube-pull-images.sh
## 内容如下
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.14.2
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.14.2
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.14.2
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.14.2
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.3.10
docker rmi coredns/coredns:1.3.1
# 运行
sh kube-pull-images.sh
# 删除两个脚本
rm kube-*.sh
- 通过 kubectl 与集群进行交互(生成一个 pod)
kubectl run nginx-1805 --image=nginx --port=80
- 将该资源以服务的方式暴露给 k8s 集群外部(用上边生成的 pod 生成 deployment:生成一个服务,可以通过 kubectl get deployment 查看)
kubectl expose deployment nginx-1805 --type=NodePort
- 查看结果
kubectl get pod
-
可以通过 kubectl describe pod pod_name 来查看某个 pod 的详情,包括运行过程中的相关 error
-
获取该服务的 URL
minikube service nginx-1805 --url
- 外部访问该 URL,查看结果
http:url
- 删除服务
kubectl delete services nginx-1805
- 删除部署
kubectl delete deployment nginx-1805
- 停止集群
minikube stop
- 删除集群
minikube delete
- 重置 kubeadm
kubeadm reset
k8s 集群搭建
准备工作
- 进入 master 节点的 /sv 目录
cd /srv
- 上传 4 个相关的安装包到 srv 下面,或者在 github 下载 kubernetes.tar.gz、kubernetes-client-linux-amd64.tar.gz、kubernetes-server-linux-amd64.tar.gz 以及 kubernetes-node-linux-amd64.tar.gz
- 分别解压 4 个安装包
tar -zxvf kubernetes-server-linux-amd64.tar.gz
-
将解压之后文件发送到所有的节点上
-
每个虚拟机都创建一下目录
mkdir -p /opt/kubernetes/{cfg,bin,log,ssl}
- 所有节点都关闭防火墙
systemctl stop firewalld
- 查看所有节点的防火墙的状态
systemctl status firewalld
- 所有机器设置开机禁止启动防火墙
systemctl disable firewalld.service
- 关闭所有节点上的 selinux

- 设置主节点的 hostname

-
设置从节点的 hostname
-
设置主节点域名解析文件

- 设置从节点域名解析文件
- 备份本地 yum 源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
- 获取阿里 yum 源配置文件
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
- 清空缓存
yum clean all
- 更新 cache
yum makecache
- 在主节点中添加环境变量 /etc/profile

- 运行
source /etc/profile
master 节点安装
etcd 安装
- 安装 etcd 服务
yum install -y etcd
- 编辑 etcd 配置文件 /etc/etcd/etcd.conf (将 ip 地址改为 master 地址)

- 启动 etcd 服务
systemctl start etcd
- 设置 etcd 开机自启
systemctl enable etcd
- 验证
ps -ef | grep etcd
# 或者
lsof -i:2379
- 查看 etcd 状态
etcdctl cluster-health
kube-apiserver 服务安装
- 进入目录
cd /srv/kubernetes/server/bin
- 拷贝内容
cp kube-apiserver kube-scheduler kube-controller-manager /opt/kubernetes/bin/
- 进入脚本保存相关目录
cd /srv/kubernetes/cluster/centos/master/scripts
- 执行以下命令
bash -x apiserver.sh 192.168.1.40 http://192.168.1.40:2379 10.1.0.0/24
- 修改一下 shell 脚本生成的配置文件,去掉有关 ssh 的设置
- 对 CA 相关配置做修改,注释掉以下 6 个变量
vi /opt/kubernetes/cfg/kube-apiserver


- 查看 apiserver 服务状态
systemctl status kube-apiserver.service
- 重新启动
systemctl restart kube-apiserver.service
- 查看重启后状态
systemctl status kube-apiserver.service

kube-scheduler 服务安装
- 进入脚本保存相关目录
cd /srv/kubernetes/cluster/centos/master/scripts
- 执行以下命令
bash -x scheduler.sh 192.168.1.40
- 重新加载配置
systemctl daemon-reload
- 重启
systemctl restart kube-scheduler.service
- 查看重启后状态
systemctl status kube-scheduler.service

kube-controller-manager 服务安装
- 进入脚本保存相关目录
cd /srv/kubernetes/cluster/centos/master/scripts
- 执行以下命令
bash -x controller-manager.sh 192.168.1.40

- 去掉 CA 相关配置
vi /opt/kubernetes/cfg/kube-controller-manager

- 重新加载配置
systemctl daemon-reload
- 重启
systemctl restart kube-controller-manager.service
- 查看重启后状态
systemctl status kube-controller-manager.service

node 节点安装
flannel 服务安装
安装 docker
安装 flanneld
7.进入 /srv 目录,下载 flanneld
8.解压刚刚下载的安装包
9拷贝以下文件
10.从主节点复制etcdctl
11.进入一下目录
12做以下修改(去掉CA相关)
13执行 flannel.sh 文件
14修改配置文件 /opt/kubernetes/cfg/flannel, 去掉 ssl 配置
15重新加载配置
16重启服务
17查看状态
18修改 docker 启动配置文件,使用 flannel 设置的网络参数
19.重新加载配置,重启 docker

记录自己学习 docker 的笔记。
浙公网安备 33010602011771号