:mannotop 2021 年 11 月 – manno的博客

月度归档: 2021 年 11 月

docker概念和应用

docker的提出

传统的kvm通过创建虚拟机的方式来部署多个业务程序,虽然它解决了物理机所不能解决的环境隔离、应用迁移问题,但也带来了大量的硬件资源消耗,因为不同的业务程序所需的资源不同,而每一个虚拟机都是一个完整的操作系统,粒度太重,要分配大量的系统资源,因此,为了高效利用硬件资源,粒度更轻的容器技术诞生了。

*发展历程:物理机——VM(虚拟机)——KVM(虚拟化技术)——容器化技术(Docker)

docker的诞生

Docker公司位于旧金山,原名dotcloud,底层利用了Linux内核支持的容器技术LXC是为了更方便地创建和管理这些容器而开发的一套工具

Docker由Golang编写,基于Linux内核的Cgroups、Namespace、以及Union FS等技术对进程进行环境隔离,是属于操作系统层面的虚拟化技术。

  • 环境隔离:指进程空间、网络空间、文件系统空间独立
  • 被隔离的进程独立于宿主机以及其它隔离的进程,称为容器
  • 容器化的最大优势就是保持轻量级的同时实现了VM一样的环境隔离功能
  • Docker是开源的容器引擎,可以方便地对容器进行管理,最初的Docker是基于LXC的,后转而使用该公司自行开发的LibContainer

容器的特点:

  • 是镜像的实例化
  • 随处运行:容器可以将代码与配置文件和相关依赖库进行打包,从而确保在任何环境下的运行都是一致的。
  • 高资源利用率:容器提供进程级的隔离,因此可以更加精细地设置CPU和内存的使用率,进而更好地利用服务器的计算资源。
  • 快速扩展:每个容器都可作为单独的进程予以运行,并且可以共享底层操作系统的系统资源,这样一来可以加快容器的启动和停止效率。

镜像:包含容器所要运行的程序本身及其依赖、配置项、脚本等一切所需环境和配置

使用docker后的开发—部署流程

开发人员:

  • 编写业务项目程序的本体代码
  • 编写dockerfile,包含shell部署逻辑、项目的运行环境配置、启动程序。
  • docker根据dockerfile构建镜像

测试/运维人员:无需再了解项目的具体配置环境,直接使用开发交付的镜像创建容器实例

总体流程:发布源码-构建镜像-发布镜像-创建容器进程-创建容器实例(源码+镜像=实例)

Docker容器是与系统其他部分隔离开的一系列进程,运行这些进程所需的所有文件都由另一个镜像提供,从开发到测试再到生产的整个过程中,Linux 容器都具有可移植性和一致性。相对于依赖重复传统测试环境的开发渠道,容器的运行速度要快得多,并且支持在多种主流云平台(PaaS)和本地系统上部署。

容器彻底解决了开发、测试、生产等不同环境下环境不一致、环境迁移难、环境部署难的问题

镜像内容只有发行版及其依赖,与宿主机公用内核,相比于虚拟机非常轻量级

Dockerfile

Dockerfile是用来描述文件的构成的文本文档,其中包含了用户可以在使用行调用以组合Image的所有命令,用户还可以使用Docker build实现连续执行多个命令指今行的自动构建。

通过编写Dockerfile生磁镜像,可以为开发、测试团队提供基本一致的环境,从而提升开发、测试团队的效率,不用再为环境不统一而发愁,同时运维也能更加方便地管理我们的镜像。

Dockerfile的语法非常简单,常用的只有11个:

编写优雅的Dockerfile主要需要注意以下几点:

  • Dockerfile文件不宜过长,层级越多最终制作出来的镜像也就越大。
  • 构建出来的镜像不要包含不需要的内容,如日志、安装临时文件等。
  • 尽量使用运行时的基础镜像,不需要将构建时的过程也放到运行时的Dockerfile里。

只要记住以上三点就能写出不错的Dockerfile。

为了方便大家了解,我们用两个Dockerfile实例进行简单的对比:

FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install -y apt-utils libjpeg-dev \     
python-pip
RUN pip install --upgrade pip
RUN easy_install -U setuptools
RUN apt-get clean
FROM ubuntu:16.04
RUN apt-get update && apt-get install -y apt-utils \
  libjpeg-dev python-pip \
           && pip install --upgrade pip \
      && easy_install -U setuptools \
    && apt-get clean

我们看第一个Dockerfile,乍一看条理清晰,结构合理,似乎还不错。再看第二个Dockerfile,紧凑,不易阅读,为什么要这么写?

  • 第一个Dockerfile的好处是:当正在执行的过程某一层出错,对其进行修正后再次Build,前面已经执行完成的层不会再次执行。这样能大大减少下次Build的时间,而它的问题就是会因层级变多了而使镜像占用的空间也变大。
  • 第二个Dockerfile把所有的组件全部在一层解决,这样做能一定程度上减少镜像的占用空间,但在制作基础镜像的时候若其中某个组编译出错,修正后再次Build就相当于重头再来了,前面编译好的组件在一个层里,得全部都重新编译一遍,比较消耗时间。
  • 注意,若Dockerfile非常长的话可以考虑减少层次,因为Dockerfile最高只能有127层。

*多阶构建(了解)

在多阶构建出来之前,传统方式是使用单个文件进行构建,单文件就是将所有的构建过程(包括项目的依赖、编译、测试、打包过程)全部包含在一个Dockerfile中之下:

FROM golang:1.11.4-alpine3.8 AS build-env
ENV GO111MODULE=off
ENV GO15VENDOREXPERIMENT=1
ENV BUILDPATH=github.com/lattecake/hello
RUN mkdir -p /go/src/${BUILDPATH}
COPY ./ /go/src/${BUILDPATH}
RUN cd /go/src/${BUILDPATH} && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install –v

CMD [/go/bin/hello]

这种的做法会带来一些问题:

  • Dockerfile文件会特别长,当需要的东西越来越多的时候可维护性指数级将会下降;
  • 镜像层次过多,镜像的体积会逐步增大,部署也会变得越来越慢;
  • 代码存在泄漏风险。

以Golang为例,它运行时不依赖任何环境,只需要有一个编译环境,那这个编译环境在实际运行时是没有任务作用的,编译完成后,那些源码和编译器已经没有任务用处了也就没必要留在镜像里。

多文件构建(多阶构建前的解决方案)

多文件构建,其实就是使用多个Dockerfile,然后通过脚本将它们进行组合。假设有三个文件分别是:Dockerfile.run、Dockerfile.build、build.sh

  • Dockerfile.run就是运行时程序所必须需要的一些组件的Dockerfile,它包含了最精简的库;
  • Dockerfile.build只是用来构建,构建完就没用了;
  • build.sh的功能就是将Dockerfile.run和Dockerfile.build进行组成,把Dockerfile.build构建好的东西拿出来,然后再执行Dockerfile.run,算是一个调度的角色。

Dockerfile.build

FROM golang:1.11.4-alpine3.8 AS build-env
ENV GO111MODULE=off
ENV GO15VENDOREXPERIMENT=1
ENV BUILDPATH=github.com/lattecake/hello
RUN mkdir -p /go/src/${BUILDPATH}
COPY ./ /go/src/${BUILDPATH}
RUN cd /go/src/${BUILDPATH} && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install –v

Dockerfile.run

FROM alpine:latest
RUN apk –no-cache add ca-certificates
WORKDIR /root
ADD hello .
CMD ["./hello"]

Build.sh

#!/bin/sh
docker build -t –rm hello:build . -f Dockerfile.build
docker create –name extract hello:build
docker cp extract:/go/bin/hello ./hello
docker rm -f extract
docker build –no-cache -t –rm hello:run . -f Dockerfile.run
rm -rf ./hello

执行build.sh完成项目的构建。

多文件构建大大减小了镜像的占用空间,但它有三个文件需要管理,维护成本也更高一些。

多阶构建

完成多阶段构建我们只需要在Dockerfile中多次使用FORM声明,每次FROM指令可以使用不同的基础镜像,并且每次FROM指令都会开始新的构建,我们可以选择将一个阶段的构建结果复制到另一个阶段,在最终的镜像中只会留下最后一次构建的结果,这样就可以很容易地解决前面提到的问题,并且只需要编写一个Dockerfile文件。这里值得注意的是:需要确保Docker的版本在17.05及以上。下面我们来说说具体操作。

在Dockerfile里可以使用as来为某一阶段取一个别名”build-env”:

FROM golang:1.11.2-alpine3.8 AS build-env

然后从上一阶段的镜像中复制文件,也可以复制任意镜像中的文件:

COPY –from=build-env /go/bin/hello /usr/bin/hello

看一个简单的例子:

FROM golang:1.11.4-alpine3.8 AS build-env
 
ENV GO111MODULE=off
ENV GO15VENDOREXPERIMENT=1
ENV GITPATH=github.com/lattecake/hello
RUN mkdir -p /go/src/${GITPATH}
COPY ./ /go/src/${GITPATH}
RUN cd /go/src/${GITPATH} && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install -v
 
FROM alpine:latest
ENV apk –no-cache add ca-certificates
COPY --from=build-env /go/bin/hello /root/hello
WORKDIR /root
CMD ["/root/hello"]

执行docker build -t –rm hello3 .后再执行docker images 

安装使用

k8s概念和架构

Kubernetes 是一个可移植、可扩展的开源容器管理平台,用于管理容器化工作负载和服务,支持声明配置和自动化。它有一个庞大的、快速发展的生态系统。Kubernetes 服务、支持和工具广泛可用。

为什么需要 k8s 以及它能做什么

纯Docker运行模式的局限性:

  • 集群架构,业务容器数量庞大,N个数据节点,如何管理容器部署在哪个最合理的数据节点?容器管理
  • 跨主机通信问题,不同宿主机内的容器之间如何通信和相互调用?服务发现问题
  • 如何实现服务高可用?服务挂了怎么检测到并及时重启,健康监控问题
  • 容器如何实现多副本?多个副本之间如何实现负载均衡?
  • 如何实现服务的滚动升级,保证业务连续性?

k8s 为您提供:

  • 服务发现和负载平衡 Kubernetes 为容器提供一个 DNS 名称和 IP 地址,并使它们可以相互知晓彼此的存在。如果容器的流量很高,Kubernetes 能够负载平衡和分配容器间的网络流量,从而使部署稳定。
  • 存储编排 Kubernetes 允许您自动挂载您选择的存储系统,例如本地存储、公共云提供商等。
  • 自动化上线和回滚 k8s会分步骤地监控应用或配置更改的上线(发布新的镜像),同时监视应用程序的执行状况以确保你不会同时终止所有实例,若上线过程出现问题,k8s会及时回滚到上线前的状态。
  • IPv4/IPv6 双协议栈,支持为Pod和容器分配IPv4或IPv6地址。
  • 自动装箱 您为 Kubernetes 提供了一个节点集群,它可以用来运行容器化任务。您可以指定 Kubernetes 为每个容器分配多少 CPU 和内存 (RAM)。Kubernetes 可以将容器安装到您的节点上,以充分利用您的资源。
  • 健康监控和自我修复 Kubernetes 会重启失败的容器,替换容器,杀死不响应用户定义的健康检查的容器,并且在它们准备好服务之前不会向客户端发布它们。
  • 秘密和配置管理 Kubernetes 允许您存储和管理敏感信息,例如密码、OAuth 令牌和 SSH 密钥。您可以部署和更新机密和应用程序配置,而无需重建容器映像,也无需在堆栈配置中公开机密。
  • 弹性伸缩 根据负载压力自动增加或削减容器数量。

k8s 不是什么

Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。由于 Kubernetes 在容器级别而不是在硬件级别运行,它提供了一些 PaaS 产品共有的普遍适用的功能,例如部署、扩展、负载平衡,并允许用户集成他们的日志记录、监控和警报解决方案。然而,Kubernetes 并不是单一的,这些默认解决方案是可选的和可插入的。Kubernetes 为构建开发人员平台提供了构建块,但在重要的地方保留了用户的选择和灵活性。

k8s 核心架构组件

*核心架构组件:安装、启动k8s后必定拥有的几个组件

  • etcd:k8s的内部运行组件,用于保存整个k8s集群的信息。(节点信息、节点上部署的容器信息等)
  • api-server:提供对k8s集群进行资源操作的唯一入口,对于每一个请求都有校验和鉴权。
  • controller manager:负责管理controller,它们共同负责维护k8s集群的状态,如故障检测、容器滚动更新、自动伸缩等。deployment controller就是其中之一。
  • scheduler:负责资源调度,按照预定的策略将Pod分配到Node机器上。
  • kuberlet:是K8s在目标Node上的进程,用于处理master节点下发到本节点的任务,管理Pod以及Pod中的容器。 每个kubelet进程会在API Server上注册节点信息,定期向master节点汇报节点资源的使用情况,并通过cAdvisor监控容器和节点的资源。
  • kubectl:k8s的命令行接口,用于对k8s集群运行命令,与api-server交互。

k8s 的系统架构

从系统架构来看,k8s包含两类角色:管理节点和工作节点。