Docker是开发人员和系统管理员使用容器开发、部署和运行应用程序的平台,使用Linux容器来部署应用程序称为集装箱化,使用Docker能够轻松部署应用程序。Docker 和传统部署方式最大的不一样在于,它将不会限制咱们使用任何工具,任何语言,任何版本的 runtime,Docker 将咱们的应用当作一个只提供网络服务的盒子(也即容器),Kubernetes 则是对这些盒子进行更多自动化的操做,自动建立,自动重启,自动扩容,自动调度,这个过程称之为容器编排。javascript
如今,容器编排技术给 Web 应用带来了巨大的灵活性,让咱们轻松建立须要的程序对外提供服务。和传统的 IaaS 相比,不须要去关心云主机申请,云主机配置等信息,也不需考虑云主机故障致使的服务不可用,由 Kubernetes 的副本控制器帮咱们完成云主机故障发生后容器迁移。下面就让咱们来看一下Docker和Kubernetes在前端应用开发的一些应用。html
和前端工具同样,使用Docker容器以前,须要先安装对应的工具。
Linux Debian/Ubuntu, 安装 社区版DockerCE
Windows 一键安装
macOS 一键安装前端
须要说明的是,Windows7 系统,须要使用 VirtualBox 安装 Linux 做为 Docker 的宿主机,Windows10 Pro 会使用 Hyper-V 安装 Linux 做为 Docker 的宿主机。java
咱们可使用阿里云的镜像进行下载,而后再进行安装。node
http://mirrors.aliyun.com/docker-toolbox/mac/docker-for-mac/
安装完成后,启动Docker便可。linux
在国内访问默认的官方镜像比较慢,咱们可使用阿里云的镜像加速,注册阿里帐号并申请容器服务以后,而后点击容器镜像服务的镜像加速地址查看地址,以下图所示。
而后在Docker的Preferences中配置中添加加速地址,选择Resources的Proxies填入阿里云的镜像加速地址,以下所示。nginx
w为了方便使用和管理镜像,咱们能够点击Docker官方地址来注册Docker ID。注册完成以后,就能够在Mac版Docker桌面工具中进行登陆,并查看本身已有的镜像,以下图所示。git
打开终端,而后输入命令docker
便可查看Docker支持的命令,以下所示。github
Usage: docker [OPTIONS] COMMAND A self-sufficient runtime for containers Options: --config string Location of client config files (default "/Users/crane/.docker") -c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use") -D, --debug Enable debug mode -H, --host list Daemon socket(s) to connect to -l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info") --tls Use TLS; implied by --tlsverify --tlscacert string Trust certs signed only by this CA (default "/Users/crane/.docker/ca.pem") --tlscert string Path to TLS certificate file (default "/Users/crane/.docker/cert.pem") --tlskey string Path to TLS key file (default "/Users/crane/.docker/key.pem") --tlsverify Use TLS and verify the remote -v, --version Print version information and quit Management Commands: app* Docker Application (Docker Inc., v0.8.0) builder Manage builds buildx* Build with BuildKit (Docker Inc., v0.3.1-tp-docker) checkpoint Manage checkpoints config Manage Docker configs container Manage containers context Manage contexts image Manage images manifest Manage Docker image manifests and manifest lists network Manage networks node Manage Swarm nodes plugin Manage plugins secret Manage Docker secrets service Manage services stack Manage Docker stacks swarm Manage Swarm system Manage Docker trust Manage trust on Docker images volume Manage volumes Commands: attach Attach local standard input, output, and error streams to a running container build Build an image from a Dockerfile commit Create a new image from a container's changes cp Copy files/folders between a container and the local filesystem create Create a new container deploy Deploy a new stack or update an existing stack diff Inspect changes to files or directories on a container's filesystem events Get real time events from the server exec Run a command in a running container export Export a container's filesystem as a tar archive history Show the history of an image images List images import Import the contents from a tarball to create a filesystem image info Display system-wide information inspect Return low-level information on Docker objects kill Kill one or more running containers load Load an image from a tar archive or STDIN login Log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container pause Pause all processes within one or more containers port List port mappings or a specific mapping for the container ps List containers pull Pull an image or a repository from a registry push Push an image or a repository to a registry rename Rename a container restart Restart one or more containers rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save one or more images to a tar archive (streamed to STDOUT by default) search Search the Docker Hub for images start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop one or more running containers tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers version Show the Docker version information wait Block until one or more containers stop, then print their exit codes Run 'docker COMMAND --help' for more information on a command.
关于这些命令的含义,能够看Docker 经常使用命令web
好比,咱们要搜索nginx镜像,那么只须要使用下面的命令便可。
docker search nginx
搜索结果中标记【OFFICIAL】的为官方镜像,其余为用户自定义镜像,可根据实际须要选择。
若是要获取镜像,可使用docker pull
命令,以下所示,获取nginx。
# 拉取指定版本xxx镜像 docker pull nginx:xxx # 拉取最新版本镜像 docker pull nginx //等价于docker pull nginx:latest
镜像拉取成功后,而后使用下面的命令启动nginx容器,以下所示。
docker run -d -p 8080:80 --name docker-nginx nginx
上面的命令会将容器内部的80端口映射到了本机的8080端口,因此启动成功后可使用http://localhost:8080/
访问docker容器内部nginx80端口映射的地址,以下图所示。
一些常见的启动参数:
查看容器的基本命令以下所示。
# 查看运行中的容器 docker ps # 查看全部容器(包括正在运行和已经中止运行的) docker ps -a
中止容器命令使用的是kill命令,以下所示。
# 经过id直接关闭容器 # docker kill a0fbf4519279 # 经过容器名称直接关闭容器 docker kill docker-nginx # 经过id直接容器 默认等待十秒 超时强制关闭 # docker stop a0fbf4519279 # 经过容器名称关闭容器 默认等待十秒 超时强制关闭 等价于 docker stop -t=10 docker-nginx docker stop docker-nginx
从新启动容器的命令以下。
# 启动容器可经过容器id或者容器名称 # 经过容器名称启动容器,若是已启动则忽略 docker start docker-nginx # 经过容器名称从新启动容器,若是未启动则直接启动,若是已启动则关闭再启动 # docker restart docker-nginx
Docker 使用的是 C/S 结构,即客户端/服务器体系结构。明白了 Docker 客户端与 Docker 服务器进行交互时, Docker 服务端负责构建、运行和分发 Docker 镜像。 而且Docker 客户端和服务端能够运行在一台机器上,能够经过 RESTful 、 stock 或网络接口与远程 Docker 服务端进行通讯。
而Docker 的底层核心原理是利用了 Linux 内核的 namespace 以及 cgroup 特性,其中 namespace 进行资源隔离,cgroup 进行资源配额, 其中 Linux 内核中一共有 6 种 namespace,分别对应以下。
在系统调用中有三个与namespace有关的函数,分别是clone、unshare和setns。
clone:若是想让子进程拥有独立的网络地址,TCP/IP 协议栈,那么就可使用clone,以下所示。参考:https://man7.org/linux/man-pages/man2/clone.2.html
clone(cb, *stack , CLONE_NEWNET, 0)
unshare:将当前进程转移到新的 namespace 中, 例如使用 fork 或 vfork 建立的进程将默认共享父级资源,使用 unshare 将子进程从父级取消共享。参考:https://man7.org/linux/man-pages/man2/setns.2.html
setns:给指定的PID指定 namespace, 一般用于共享 namespace。参考:https://man7.org/linux/man-pages/man2/setns.2.html
Linux 的内核层支持了在系统调用中隔离 namespace, 经过给一个进程分配单独的 namespace 从而让其在各个资源维度进行隔离,每一个进程都能获取到本身的主机名,IPC、 PID、IP和根文件系统、用户组等信息,就像在一个独占系统中,不过虽然资源进行了隔离,可是内核仍是共享同一个,这也是比传统虚拟机轻量的缘由之一。
另外只有资源进行隔离还不够,要想保证真正的故障隔离,互不影响, 还须要对针对 CPU, 内存,GPU 等进行限制,由于若是一个程序出现死循环或者内存泄露也会致使别的程序没法运行。
一个容器要想提供服务,就须要将自身的网络暴露出去。Docker 是与宿主机上的环境是隔离的,要想暴露服务就须要显示告诉 Docker 哪些端口容许外部访问,在运行 docker run -p 80:80 nginx 时这里就是将容器内部的 80 端口暴露到宿主机的 80 端口上,具体的端口转发下面会具体分析一下。容器的网络部分是容器中最重要的部分,也是构建大型集群的基石,在咱们部署 Docker 的应用时,须要要对网络有个基本的了解。
目前,Docker 提供了四种网络模式,分别为 Host、Container、 None和Bridge ,咱们可使用 --net 来进行指定网络模式。
Host 模式不会单独为容器建立 network namespace, 容器内部直接使用宿主机网卡,此时容器内获取 ip 为宿主机 ip,端口绑定直接绑在宿主机网卡上,优势是网络传输时不用通过 NAT 转换,效率更高速度更快。可是docker host上已经使用的端口就不能再用了,网络的隔离性很差。
可使用以下的命令开启Host模式。
docker run --net host nginx
Host模式的示意图以下所示。
和指定的 container 共享 network namespace, 共享网络配置,ip 地址和端口,其中没法共享网络模式为 Host 的容器。新建立的容器不会建立本身的网卡,配置本身的 IP,而是和一个指定的容器共享 IP、端口范围等。一样,两个容器除了网络方面,其余的如文件系统、进程列表等仍是隔离的,两个容器的进程能够经过 lo 网卡设备通讯。
开启Host模式的命令以下。
docker run --net container:xxx_containerid nginx
使用none模式,Docker容器拥有本身的Network Namespace,可是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。须要咱们本身为Docker容器添加网卡、配置IP等。指定为 None 模式的容器内将不会分配网卡设备,仅有内部 lo 网络。
启动None 模式的命令以下所示。
docker run --net none busybox ifconfig
该模式为默认模式,容器启动时会被分配一个单独的 network namespace,同时 Docker 在安装/初始化时会在宿主机上建立一个名为 docker0 的网桥,该网桥也做为容器的默认网关,容器网络会在该网关网段内进行 ip 的分配。
Bridge 模式的启动命令以下所示。
docekr run --net bridge busybox ifconfig
Bridge模式的示意图下图所示。
而且,当咱们执行 docker run -p 3000:80 nginx
命令时,Docker 会在宿主机上建立下面一条 iptable 转发规则。
在上图中,当外部请求主机网卡 3000 端口时将它进行目的地址转换(DNAT), 目的地址修改成 172.18.0.2,端口修改成 80,修改好目的地址后流量会从本机默认网卡通过 docker0 转发到对应的容器,这样当外部请求宿主机的 3000 端口,内部会将流量转发给内部容器服务,从而实现服务的暴露,流程示意图以下所示。
一样 Docker 内部访问外部接口也会进行源地址转换(SNAT), 容器内部请求 google.com, 服务器上收到的则是主机网卡的 ip。
Bridge 模式因为多了一层 NAT 转换因此效率会比 Host 模式差一些,可是可以很好的隔离外部网络环境,让容器独享 ip 且具备完整的端口空间。
上面四种网络模式是 Docker 自带的几种工做方式,可是部署 Kubernetes 须要全部的容器都工做在一个局域网中,因此在部署集群时须要多主机网络插件的支持。
多主机网络解决方案有 CNCF 推出的 CNI 规范以及 Docker 自带的 CNM 方案,可是目前你们用的最多的仍是 CNI 规范,其中一种实现就是 Flannel。
Flannel 使用了报文嵌套技术来解决多主机网络互通问题,将原始报文进行封包,指定包ip为目的主机地址,等包到达主机后再进行拆包传送到对应的容器。Flannel是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)工具,其目的在于帮助每个使用 Kuberentes 的 CoreOS 主机拥有一个完整的子网。下图显示 flannel 使用效率更高的 UDP 协议来在主机间传输报文。
对上图,咱们简单的说明下:
目前主流跨主机通讯目前经常使用的有三种,分别是Overlay、hostgw和BGP技术。
hostgw: 经过修改主机路由表实现转发,不须要拆包和封包,效率更高,但一样限制比较多,只适合在相同局域网中的主机使用。
BGP:使用软件实现的 BGP(边界网关协议)以此向网络中的路由器广播路由规则。和 hostgw 同样不须要拆包,可是实现成本较高。
在小规模场景下,使用 Docker 能够一键部署应用确实很方便,可是当出现须要在几百台主机上进行多副本部署,须要管理这么多主机的运行状态以及服务的故障时须要在其余主机重启服务,想象一下就知道手动的方式不是一种可取的方案,这时候就须要利用 Kubernetes 这种更高维度的编排工具来管理了。
Kubernetes 简称 K8S,是Google 2014年建立管理的大规模容器管理技术。 简单说 K8S 就是抽象了硬件资源,将 N 台物理机或云主机抽象成一个资源池,容器的调度交给 K8S 进行管理,CPU 不够用就调度到一台足够使用的机器上,内存不知足要求就会寻找一台有足够内存的机器在上面建立对应的容器,服务由于某些缘由挂了, K8S 还会帮咱们自动迁移重启。K8S是一个开源的平台,实现了容器集群的自动化部署、自动扩缩容、维护等功能。
Kubernetes 具备以下特色:
K8s集群由两节点组成:Master和Node。在Master上运行etcd,Api Server,Controller Manager和Scheduler四个组件。后三个组件构成了K8s的总控中心,负责对集群中全部资源进行管控和调度。在每一个Node上运行kubect、proxy和docker daemon三个组件,负责对节点上的Pod的生命周期进行管理,以及实现服务代理的功能。另外全部节点上均可以运行kubectl命令行工具。
API Server做为集群的核心,负责集群各功能模块之间的通讯。集群内的功能模块经过Api Server将信息存入到etcd,其余模块经过Api Server读取这些信息,从而实现模块之间的信息交互。Node节点上的Kubelet每隔一个时间周期,经过Api Server报告自身状态,Api Server接收到这些信息后,将节点信息保存到etcd中。Controller Manager中 的Node controller经过Api server按期读取这些节点状态信息,并作响应处理。Scheduler监听到某个Pod建立的信息后,检索全部符合该pod要求的节点列表,并将pod绑定到节点列表中最符合要求的节点上。若是scheduler监听到某个Pod被删除,则调用api server删除该Pod资源对象。kubelet监听pod信息,若是监听到pod对象被删除,则删除本节点上的相应的pod实例,若是监听到修改Pod信息,则会相应地修改本节点的Pod实例。
Kubernetes主要由如下几个核心组件组成:
在Mac中安装了Docker以后,会自动安装了Kubernetes,正常状况下,咱们只须要在Docker的Preferrences->Kubernetes中勾选Enable Kubernetes,而后点击Apply按钮便可。
可是因为【墙】的问题,若是您是直接在Docker中启用Kubernetes,Kubernetes的状态会一直都是kubernetes is starting...,缘由是有一些Kubernetes依赖的镜像不能正常的下载。
Github上有个开源项目能够帮咱们手动拉取镜像,执行下面命令拉去改项目代码到本地。
https://github.com/xiangzhihong/k8s-docker-desktop-for-mac cd k8s-docker-desktop-for-mac sh load_images.sh
而后,执行下面的命令验证是否安装成功。
kubectl cluster-info
IaaS 就是 Infrastructure as a service, 所谓基础设施即服务,开发者想要上线一个新应用须要申请主机,ip, 域名等一系列资源,而后登陆主机自行搭建所需环境,部署应用上线,这样不只不利于大规模操做,并且还增长了出错的可能,运维或开发这经常本身写脚本自动化完成,遇到一些差别再手动修改脚本,很是痛苦。
K8S 则是将基础设施可编程化,由原来的人工申请改成一个清单文件自动建立,开发者只须要提交一份文件,K8S 将会自动为你分配建立所需的资源。对这些设施的 CRUD 均可以经过程序的方式自动化操做。
为了了解 K8S 的基础知识,下面来部署一个 Node SSR 应用。首先,初始化一个应用模版。
npm install create-next-app npx create-next-app next-app cd next-app
建立好工程后给添加一个 Dockerfile 用来构建服务的镜像Dockerfile。
FROM node:8.16.1-slim as build COPY ./ /app WORKDIR /app RUN npm install RUN npm run build RUN rm -rf .git FROM node:8.16.1-slim COPY --from=build /app / EXPOSE 3000 WORKDIR /app CMD ["npm", "start"]
Dockerfile 作了两部分优化:
而后使用下面的命令构建镜像。
docker build . --tag next-app
以后咱们就能够向 Kubernetes 提出咱们应用的要求了。为了保证高可用,服务至少建立两个副本,咱们还须要一个应用的域名当这个域名请求到咱们集群上时自动转发到咱们的服务上。那么咱们对应的配置文件Deployment.yaml就能够这么写。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: app-ingress spec: rules: - host: next-app-server http: paths: - backend: serviceName: app-service servicePort: 80 --- kind: Service apiVersion: v1 metadata: name: app-service spec: selector: app: web ports: - port: 80 targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment metadata: name: app-deployment spec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: next-app name: next-app imagePullPolicy: IfNotPresent ports: - containerPort: 3000
上面清单主要的功能是:
而后,执行下面的命令提交申请给 K8S。
kubectl apply -f ./Deployment.yaml
接着就能够看到已经部署的 pod。
sh-4.4$ kubectl get pod NAME READY STATUS RESTARTS AGE app-deployment-594c48dbdb-4f4cg 1/1 Running 0 1m app-deployment-594c48dbdb-snj54 1/1 Running 0 1m
而后浏览器打开 Ingress 里配置的域名便可访问对应的应用(前提是这个域名可以打到你的 K8S 集群节点上),以下图所示。
K8S 仅仅负责容器的编排,实际上若是部署应用还须要外部 Pipeline 的支持,代码的构建,静态检查,镜像的打包由 Pipeline 完成。目前,国内用的比较多的发布系统经常由下面几个服务组成:GitLab/GitHub、Jenkins,、Sonar,、Harbor等。