Docker 、 k8s 基础

Docker 、 k8s 基础


(一)镜像(创建容器) & 容器(封装为镜像)

镜像:目的内容的一个封装,不可以改变内容。
容器:可以改变内容,相当于虚拟机,默认情况下彼此封闭。

一般我们做的,就是通过镜像创建容器;当我们改变了容器的内容之后,也可以重新将容器封装为镜像

容器(Container) vs 虚拟机(VM)

Docker项目的目标是实现轻量级的操作系统虚拟化解决方案。

区别

  1. VM:VM是硬件层面上的虚拟化。一个VM包含了应用、必须的bin/lib库,同时每个VM都必须要有Guest OS,每个都高达数十MB。
  2. 容器:容器是OS层面上的虚拟化。一个容器只包含了应用和必须的bin/lib库,所有容器共享宿主机的Host OS,但是在独立的进程上运行,可以隔离性地使用系统资源

    底层原理:LXC(Linux Container)。
    namespace:每个容器有各自的命名空间,用于逻辑隔离。
    cgroup:每个容器在Linux中有自己的分组和权限,可以用于互不干扰地访问系统资源。

容器的优点

  1. 单个容器使用的是宿主机的OS,不包含自己的OS,所以容器要比VM更加轻量级,开销更小,开发、测试、部署都更快,迁移也更方便
  2. 容器的启动可以在秒级实现,比VM得多;
  3. 一台主机上可以运行数千个容器,对系统资源的利用率比VM高得多

(二)安装

CentOS下,yum -y install docker即可。
我的版本是Docker version 1.13.1, build b2f74b2/1.13.1


(三)使用

服务的启动 & 停止:systemctl start/stop docker

启动:systemctl start docker
停止:systemctl stop docker


docker search keyword:根据关键字,在互联网搜索镜像。
如:docker search ubuntu

也可以在https://hub.docker.com/搜索镜像,会提供准确具体的文档。


镜像下载:docker pull

docker pull imageName:根据镜像名下载镜像。
如:docker pull docker.io/ubuntu


本地镜像查看:docker images

docker images:查看所有本地镜像


创建容器 & 命名:docker run --name

docker run -tid --name containerName imageID:可以通过docker images查看imageID,一般取前4位即可。
如:docker run -tid --name DistributedCrawlerUbuntu 4c10

docker run参数

  • -p hostPort:containerPort:p for port,指定“宿主端口:容器端口”之间的对应关系,将容器运行在指定端口

  • -v hostPath:containerPath:v for volume,指定“宿主目录:容器目录”之间的对应关系

  • -e:e for environment,设置环境变量

  • -i:i for interactive,交互式

  • -t:t for terminal,打开终端

    -it打开交互式终端

  • -d:d for detach,后台创建

    -tid在后台打开交互式终端,运行容器但是不进入容器内

  • --rm:rm for remove,创建容器后,删除相同镜像创建的其他容器

  • --help:查看docker run文档


【容器启动:docker start

docker start containerID/containerName通过ID或者名称启动某个停止的容器


运行中容器的查看:docker ps -a

docker ps -a:查看当前所有运行的容器


Docker容器的进入:docker attach

docker attach containerID:可以通过docker ps -a查看containerID,一般取前4位即可。


在运行的容器中执行命令:docker exec

docker exec -it ContainerName + 命令,比如docker exec -it MongoDB bash


容器的不停止退出:ctrl + p + q + Enter

ctrl + p + q,按下回车,即可不停止容器但是退出。


基于容器封装为镜像:docker commit

docker commit containerID imageName:version:将已有的容器封装为镜像,containerID可以通过docker ps -a获得。
如:docker commit e4cc distributed_crawler:v1


容器的监控:docker inspect containerID

  • 监控运行中的docker容器:docker inspect containerID
  • 监控运行中的docker容器的xxx属性:docker inspect containerID | grep xxx

容器推送到远程仓库:docker push

  • docker push name:version

docker run -tid --name containerName --link linkContainerName iamgeID/Name:在创建容器时,使其与某个容器保持网络通畅。
如:

1
2
docker run -tid --name host1 distributed_crawler:v1                   # 创建host1
docker run -tid --name host2 --link host1 distributed_crawler:v1 # 创建host2时,link host1

(五)DockerFile

DockerFile是对镜像的源码级别的描述文件。

关键字如下:

1
2
3
4
5
6
7
8
9
10
11
12
FROM        基础镜像:当前镜像是基于哪个镜像的
MAINTAINER 镜像维护者的姓名和邮箱
RUN 容器构建时需要的命令
EXPOSE 当前容器对外暴露的端口
WORKDIR 创建容器后,终端默认登录进来的工作目录,一个落脚点
ENV 构建容器的过程中设置环境变量,如Java、MySQL……
ADD 将host目录下的文件拷贝进镜像,且ADD命令会自动处理URL和解压tar包
COPY 类似ADD,拷贝文件和目录到镜像
VOLUME 容器数据卷,用于数据的持久化
CMD 指定一个容器启动时需要运行的命令;DockerFile中可以有多个CMD命令,但只有最后一个会生效,且CMD会被docker run之后的参数替换
ENTRYPOINT 指定一个容器启动时需要运行的命令
ONBUILD 当构建一个被继承的DockerFile是运行命令,子镜像继承父镜像后,父镜像中的ONBUILD被触发

DockerFile文件所在目录下执行docker build -t xxx .

t for tag,xxx镜像名


(六)容器数据卷(volume)

容器关闭后,数据需要共享持久化容器数据卷(volume)就是完成此功能的,它类似于Docker的移动硬盘。

特点

  1. 数据卷可以在容器之间共享或重用数据
  2. 卷中的更改直接生效。不管是在host还是container中数据卷中修改了内容,另一方都能同步收到更新,即使container已经退出也可以更新
  3. 卷中的更改不会包含在镜像的更新中
  4. 卷的生命周期一直持续到没有容器使用它为止

如何添加容器数据卷

  1. DockerFile中的VOLUME属性下添加;
  2. 直接命令添加:docker run -it -v /hostPath:/containerPath imageName。其中hostPath是宿主机绝对路径目录;containerPath是容器内目录;imageName是镜像名。

    挂载Read-Only的容器数据卷: docker run -it -v /hostPath:/containerPath:ro imageName
    如何查看已经挂载了哪些容器数据卷? docker inspect containerID命令,查看Volumes属性下的对应关系


(七)Docker-Compose

参考https://blog.51cto.com/9291927/2310444

简介

  • Docker-ComposeDocker官方的开源项目,负责实现Docker容器集群的快速编排。
  • Docker-Compose的默认配置文件为docker-compose.yml
  • Docker-Compose允许用户通过一个docker-compose.yml文件来定义一组相关联的应用容器作为一个项目。

安装

1
2
3
4
5
6
7
8
# 下载
sudo curl -L https://github.com/docker/compose/releases/download/1.23.0-rc3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

# 更改权限
sudo chmod +x /usr/local/bin/docker-compose

# 查看版本
docker-compose version

docker-compose.yml文件格式

包含versionservicesnetworks三大部分:

services

  • image指定服务的镜像名称/ID,如果本地不存在,会尝试拉取
  • command覆盖容器启动后默认执行的命令
  • portsHOST:CONTAINER的格式进行端口映射
  • volumes挂载数据卷容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3'
services:
elasticsearch:
image: elasticsearch:7.3.1
environment:
discovery.type: single-node
ports:
- "9200:9200"
- "9300:9300"
logstash:
image: logstash:7.3.1
command: logstash -f /etc/logstash/conf.d/logstash.conf
volumes:
# 挂载logstash配置文件
- ./config:/etc/logstash/conf.d
- /user/elk/logs/:/opt/build/
ports:
- "5000:5000"
kibana:
image: kibana:7.3.1
environment:
-ELASTICSEARCH_URL=http://elasticsearch:9200
ports:
- "5601:5601"

Docker-Compose命令

  • 启动:docker-compose up

(八)Demo

1.MongoDB

1
2
3
4
5
6
7
8
9
10
docker pull mongo
# 运行 :-p 进行端口映射;-v 容器数据卷挂载目录;-e 配置环境变量;-d 后台运行
docker run --name MongoDB -p 27017:27017 -v ~/docker-data/mongo:/data/db -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=****** -d mongo
# 查看
docker ps -a
# 登录MongoDB
docker exec -it MongoDB bash
mongo -u root -p ******
# help
# show dbs

启动web监控

进入MongoDB容器后运行db.enableFreeMonitoring()会得到一个url,通过该url可以web健康你的MongoDB容器(不过贼鸡儿慢就是了……):

2.Redis

1
2
docker pull redis
docker run --name Redis -d -p 6379:6379 redis

3.MySQL主从

1
2
3
# 端口映射:master 3307;slave01 3308
docker run --name master -p 127.0.0.1:3307:3306 -e MYSQL\_ROOT\_PASSWORD=xxxxxx -d mysql:5.6
docker run --name slave01 -p 127.0.0.1:3308:3306 -e MYSQL\_ROOT\_PASSWORD=xxxxxx -d --link master mysql:5.6

4.RedisSearch:高性能Redis全文搜索模块

1
docker pull redislabs/redisearch:latest

(九)Maven依赖

包括:dockerfile-maven-pluginmaven-dependency-plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.3.4</version>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>

(十)Kubernetesk8s

Kubernetes(简称k8s)是谷歌开源的容器集群管理系统,在Docker技术的基础上,为容器的应用提供部署运行、资源调度(Kube-proxy)、服务发现(Kublet)和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性

k8s架构

  • Master Node主节点,提供管理整个k8s集群的控制面板
  • API server所有REST命令的入口,是主节点内唯一的用户可以访问的组件
  • Datastore持续、高可用的K/V数据存储中心
  • Scheduler监控新创建的容器组(pod),并将其加入节点中
  • Contoller manager管理k8s集群内处理例行任务的控制器
  • Worker Node运行容器组(pod),管理容器之间的网络,和Master Node交流
  • Docker节点内的单独容器
  • Kublet监控容器组(pod)状态,保证所有容器正常启动、运行,并且负责和Datastore交互
  • Kube-proxy单个节点内的路由网关和负载均衡器
  • Kubectl提供给用户的命令行工具,用于与k8s API服务器通信
  • etcd高可用、强一致性的服务发现存储仓库,一般是集群,采用的协议是Raft,通过全局锁保证强一致性

1.架构

(1)核心服务组件
  1. API server:所有REST命令的入口,是Master Node内用户唯一可以访问的组件;
  2. Controller manager:管理处理例行任务的控制器;
  3. Scheduler:监控新创建的容器组(pod),并将其加入节点中;
  4. etcd高可用、强一致性的服务发现存储仓库,一般是集群,采用的协议是Raft,通过全局锁保证强一致性
  5. etcd sidecaretcd边角器
(2)其他组件
  1. Kublet:监控容器组(pod)状态,保证所有容器正常启动、运行,同时负责和数据节点交互;
  2. kube-proxy:单个节点内路由网关和负载均衡器。

2.集群规模限制

k8s声称单集群能够支持5,000节点,但是集群规模受到很多因素的限制。

模拟测试(5k nodes, 150k pods, 单个node内30个pod容器组)时发现了如下问题:

【1】崩溃无法恢复
  1. 每个Master Node中的etcd耗尽了大量内存(大约100G)

    这是由于请求穿透了API server的缓存,打到了etcd

  2. etcd采用Raft协议Leader无法及时回复心跳或是Leader过了任期就要重新选举,这种选举发生得很频繁

    原因是etcd花费了太多资源刷盘,导致无法及时回复心跳,follower自动晋升为candidate,频繁进行选举。

  3. etcd日志告警:提交耗时过长、range/delete操作规模过大

    原因是pod过多,导致LIST请求规模过大,见第【3】点。

1
2
2018-07-02 17:17:43.986312 W | etcdserver: apply entries took too long [52.361859051s for 1 entries]
2018-07-02 17:17:43.986341 W | etcdserver: avoid queries with large range/delete range!
  1. API server每过几分钟重启一次
【2】大型集群中pod容器组调度很慢

集群5k节点,暂无pod容器组,调度一个pod需要1-2 s
集群5k节点,已有30k pod容器组,调度一个pod需要1 min

原因是Scheduler调度不创建metadata,见优化【2】

【3】大列表请求将破坏集群
  1. 集群可伸缩指标不仅是节点数,也包括pod数;
  2. 控制面板/用户应用都会查询pod
  3. pod数量剧增,访问pod的列表(LIST)请求影响范围会变得很大,这些请求可能会耗尽API server的缓冲区,影响核心功能

    用户唯一能访问的组件就是API server,记得吗?


3.k8s集群优化

通过优化k8s集群,使得单集群生产环节真正能够容纳5,000节点。

【1】崩溃恢复(容灾)
  1. LIST请求默认优先从API server的缓冲区而不是etcd拿数据,所以应该保证不要重写LIST,以免请求穿透缓冲区直接打在etcd
  2. API server的缓冲区数据没有准备好时,直接返回error,而不允许请求穿透到etcd
  3. GetAttrs从对象获取字段和标签时,会将其存入一个Map中,Map排列是一个耗时的操作,GetAttrs会在筛选器循环中调用,一个列表请求中有多少个pod,那么将调用多少次GetAttr。解决方案是将GetAttrs移动到一个统一的位置,当对象从etcd读取到API server,会将属性与其对象一起存储在缓存
【2】Scheduler优化:metadata避免全局遍历
  • 将一个pod加入node,需要检查是否破坏原有pod的依赖性,正常情况会检查所有依赖匹配的pod,将其加入metadata元数据,然后再检查集群中每个节点的匹配pod的拓朴结构。
  • 但是如果一个集群中有一个或多个pod进入上述流程,Scheduler就不会再创建metadata了,而是直接遍历所有pod,所以pod越多,调度就越慢
  • 解决办法是不论如何都要创建metadata,绕过全局遍历
【3】降低锁竞争:将pod分散到不同etcd实例
  • etcd是一个高可用、强一致性的服务发现存储仓库,存在全局锁
  • pod分散到不同etcd可以降低全局锁的竞争,增加吞吐量
【4】通过限流算法限制LIST请求打到etcd
-------------本文结束感谢您的阅读-------------

本文标题:Docker 、 k8s 基础

文章作者:DragonBaby308

发布时间:2019年07月01日 - 13:21

最后更新:2020年01月25日 - 13:03

原始链接:http://www.dragonbaby308.com/docker/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%