Loading

从Docker到Kubernetes

1 简介

1.1 什么是容器

传统的虚拟化技术,比如 VMWare, 目标是创建完整的虚拟机。为了运行应用,除了部署应用本身及其依赖(通常几十MB),还得安装整个操作系统(几十GB)。

如图所示,由于所有的容器共享同一个 Host OS,这使得容器在体积上要比虚拟机小很多。另外,启动容器不需要启动整个操作系统,所以容器部署和启动速度更快,开销更小,也更容易迁移。

1.2 为什么需要容器

简略回答是:容器使软件具备了超强的可移植能力

如今的系统在架构上较十年前已经变得非常复杂了。开发人员通常使用多种服务(比如 MQ,Cache,DB)构建和组装应用,而且应用很可能会部署到不同的环境,比如虚拟服务器,私有云和公有云。

一方面应用包含多种服务,这些服务有自己所依赖的库和软件包;另一方面存在多种部署环境,服务在运行时可能需要动态迁移到不同的环境中。这就产生了一个问题:如何让每种服务能够在所有的部署环境中顺利运行?

Docker 将集装箱思想,运用到软件打包上,为代码提供了一个基于容器的标准化运输系统。Docker 可以将任何应用及其依赖打包成一个轻量级、可移植、自包含的容器。容器可以运行在几乎所有的操作系统上。

容器意味着环境隔离和可重复性。开发人员只需为应用创建一次运行环境,然后打包成容器便可在其他机器上运行。另外,容器环境与所在的 Host 环境是隔离的,就像虚拟机一样,但更快更简单。

1.3 容器是如何工作的

Docker 的核心组件包括:

  • Docker 客户端 - Client

  • Docker 服务器 - Docker daemon

  • Docker 镜像 - Image

  • Registry 镜像仓库

  • Docker 容器 - Container

Docker 架构如下图所示:

Docker 采用的是 Client/Server 架构。客户端向服务器发送请求,服务器负责构建、运行和分发容器。客户端和服务器可以运行在同一个 Host 上,客户端也可以通过 socket 或 REST API 与远程的服务器通信。

1.4 案例:安装docker

1、windows 10+:

  • 安装Docker Desktop

  • 安装wsl_update (WSL2 Linux内核更新包)

  • 重启

2、Linux:

curl -fsSL https://get.docker.com/ | sh

# daocloud.io 国内镜像
curl -sSL https://get.daocloud.io/docker | sh

如何验证安装完成?

$ docker version
Client:
 Cloud integration: v1.0.29
 Version:           20.10.17
 API version:       1.41
 Go version:        go1.17.11
 Git commit:        100c701
 Built:             Mon Jun  6 23:09:02 2022
 OS/Arch:           windows/amd64
 Context:           default
 Experimental:      true

Server: Docker Desktop 4.12.0 (85629)
 Engine:
  Version:          20.10.17
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.17.11
  Git commit:       a89b842
  Built:            Mon Jun  6 23:01:23 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.8
  GitCommit:        9cd3357b7fd7218e4aec3eae239db1f68a5a6ec6
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

2 镜像

2.1 什么是镜像

可将 Docker 镜像视为只读模板,通过它可以创建 Docker 容器。例如某个镜像可能包含一个 Ubuntu 操作系统、一个用户开发的 Web 应用。

如何查看系统里已经存在的镜像?

$ docker images
REPOSITORY                                  TAG         IMAGE ID       CREATED        SIZE
busybox                                     latest      b18a6628e020   6 weeks ago    67.3MB
ubuntu                                      latest      08d22c0ceb15   7 weeks ago    77.8MB
centos                                      7           2661b85c5b47   9 months ago   645MB
apache/skywalking-oap-server                6.6.0-es7   60cf77fe2aee   3 years ago    194MB
apache/skywalking-ui                        6.6.0       da10535e9797   3 years ago    126MB
elasticsearch                               7.5.1       2bd69c322e98   3 years ago    779MB
hello-world                                 latest      a115c17f9b48   12 seconds ago   13.3kB
nginx                                       latest      6efc10a0510f   2 weeks ago      142MB

上图列出了本机所有的镜像列表,包含:ubuntu、elasticsearch等。

镜像有多种生成方法:

  1. 可以从无到有开始创建镜像;
  2. 也可以下载并使用别人创建好的现成的镜像;
  3. 还可以在现有镜像上创建新的镜像。

如果需要下载使用别人已经制作好的镜像,可以通过docker pull拉取:

docker pull alpine 
docker pull ubuntu 
docker pull apache/skywalking-ui 
docker pull busybox

未加域名前缀,默认从docker hub拉取。

2.2 基础镜像

base 镜像有两层含义:

  1. 不依赖其他镜像,从 scratch 构建 (scratch指的是空镜像,什么也没有)。
  2. 其他镜像可以之为基础进行扩展。

所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等。

我们以 Ubuntu 为例考察 base 镜像包含哪些内容。下载镜像:

$ docker pull ubuntu

查看镜像并运行:

$ docker images ubuntu
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       latest    08d22c0ceb15   7 weeks ago   77.8MB

$ docker run -it ubuntu /bin/bash

root@aca480369011:/# uname -r
5.10.16.3-microsoft-standard-WSL2

root@aca480369011:/# ls /
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

我们发现ubuntu镜像只有77.8MB!这比直接安装ubuntu系统小多了。如果加上我们自己的应用程序,例如jar包及环境,大小也就几百M,对于应用程序移植来说简直是方便极了!

现在来看一下这个镜像为什么这么小。

众所周知,Linux 操作系统由内核空间和用户空间组成。如下图所示:

内核空间是 kernel,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。用户空间的文件系统是 rootfs,包含我们熟悉的 /dev, /proc, /bin 等目录。对于 base 镜像来说,底层直接用 Host 的 kernel,自己只需要提供 rootfs 就行了。

而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他 Linux 发行版,CentOS 的 rootfs 已经算臃肿的了,alpine 还不到 10MB。

不同 Linux 发行版的区别主要就是 rootfs。所以 Docker 可以同时支持多种 Linux 镜像,模拟出多种操作系统环境。

2.3 常用镜像命令

# 搜索镜像 docker search [-s] IMAGE
docker search ubuntu 

# 下载镜像 docker pull [OPTIONS] NAME[:TAG|@DIGEST]
docker pull ubuntu:16.04

# 查看已下载镜像列表 docker images [-a]
docker images 

# 给镜像添加标签 docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
docker tag ubuntu:16.04 ubuntu

# 删除镜像 docker rmi [OPTIONS] IMAGE [IMAGE...]
docker rmi ubuntu:latest
docker rmi 12543ced0f6f

# 导出镜像 docker save [OPTIONS] IMAGE [IMAGE...]
docker save -o ubuntu_latest.tar ubuntu:latest

# 导入镜像
docker load --input ubuntu_latest.tar
docker load < ubuntu_latest.tar

# 从容器生成镜像
docker commit -m "create images" -a "yujc33"  2c74d574293f  yujc33/test:v1

3 容器

3.1 运行容器

Docker 容器就是 Docker 镜像的运行实例。(可以理解为java里的对象与Class的关系)

用户可以通过 CLI(docker)或是 API 启动、停止、移动或删除容器。可以这么认为,对于应用软件,镜像是软件生命周期的构建和打包阶段,而容器则是启动和运行阶段。

docker run -d --name getting-started -p 80:80 docker/getting-started

这里面我们运行了一个 Docker 容器,使用了 docker/getting-started 镜像。

-d 参数让容器以后台运行(detached 模式)。

--name 参数将容器命名为getting-started。省略该参数将随机命名。

-p 参数将主机的 80 端口与容器的 80 端口进行映射,允许通过主机的 IP 地址访问容器的 HTTP 服务。

因此,该命令会启动容器并将其连接到主机的端口80。容器的HTTP服务可以通过主机的 IP地址 访问, 例如 http://127.0.0.1

下面的命令可以查看所有的容器状态:

$ docker ps -a
CONTAINER ID   IMAGE                    COMMAND                  CREATED              STATUS                       PORTS                NAMES
a9f67b3f2b7f   docker/getting-started   "/docker-entrypoint.…"   About a minute ago   Up About a minute            0.0.0.0:80->80/tcp   compassionate_kare
2c1da9b06896   docker/getting-started   "/docker-entrypoint.…"   10 days ago          Exited (255) 3 minutes ago   0.0.0.0:80->80/tcp   exciting_blackwell

-a代表查看所有状态容器,去掉该参数则是表示仅查看运行中容器。STATUS是容器状态。

docker logs a9f67b3f2b7f

该命令用于查看容器内的标准输出。a9f67b3f2b7f是容器ID。

docker exec -it a9f67b3f2b7f /bin/sh         

该命令是在已经在运行的容器中执行一个交互式的 shell。-it 标志为执行一个交互式的终端操作,可以通过 shell 和命令行接口进行交互,/bin/sh 是要在容器中执行的命令行 shell。如果需要从容器中退出,则输入exit即可。

docker exec  a9f67b3f2b7f /bin/ls / 

该命令是在容器中执行/bin/ls / 命令。这也说明了我们不用进入shell交互界面也可以执行命令。

3.2 常用容器命令

create      创建容器  
run         运行容器(相当于create+start)  
pause       暂停容器  
unpause     取消暂停继续运行容器  
stop        发送 SIGTERM 停止容器  
kill        发送 SIGKILL 快速停止容器  
start       启动容器  
restart     重启容器  
attach      attach 到容器启动进程的终端  
exec        在容器中启动新进程,通常使用 "-it" 参数  
logs        显示容器启动进程的控制台输出,用 "-f" 持续打印  
rm          从磁盘中删除容器

3.3 容器资源限额

下面是对容器进行限额的一些示例:

# 允许该容器最多使用 200M 的内存和 100M 的 swap
docker run -m 200M --memory-swap=300M docker/getting-started

# 设置容器使用 CPU 的权重。如果不指定,默认值为 1024
# container_A 的 cpu share 1024,是 container_B 的两倍。当两个容器都需要 CPU 资源时,container_A 可以得到的 CPU 是 container_B 的两倍。这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下
docker run --name "container_A" -c 1024 docker/getting-started
docker run --name "container_B" -c 512 docker/getting-started

3.4 容器实现底层技术

容器底层是通过cgroup实现资源限额的,通过 namespace 实现资源隔离。

前面我们看到的 -m-c 实际上就是在配置 cgroup。我们可以在宿主机 /sys/fs/cgroup 中找到它。在 /sys/fs/cgroup/cpu/docker 目录中,Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名:

目录中包含所有与 cpu 相关的 cgroup 配置,文件 cpu.shares 保存的就是 --cpu-shares 的配置,值为 512。

同样的,/sys/fs/cgroup/memory/docker/sys/fs/cgroup/blkio/docker 中保存的是内存以及 Block IO 的 cgroup 配置。

在每个容器中,我们都可以看到文件系统,网卡等资源,这些资源看上去是容器自己的。Linux 实现这种方式的技术是 namespace。namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace 实现了容器间资源的隔离

Linux 使用了六种 namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User。

  • Mount: 让容器看上去拥有整个文件系统。
  • UTS: 让容器有自己的 hostname。
  • IPC: 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起。
  • PID: 容器在 host 中以进程的形式运行。
  • Network: 让容器拥有自己独立的网卡、IP、路由等资源。
  • User: 让容器能够管理自己的用户,host 不能看到容器中创建的用户。

4 再探镜像

上文了解了镜像的基本操作,本节进一步了解镜像相关内容。

4.1 Dockerfile

我们可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称作 Dockerfile,通过执行 docker build <docker-file> 命令可以构建出 Docker 镜像。

以官方的hello-world 镜像为例,它的Dockerfile文件内容:

# 此镜像是从白手起家,从 0 开始构建。
FROM scratch
# 将文件“hello”二进制文件复制到镜像的根目录。不依赖任何环境
COPY hello /
# 容器启动时,执行 /hello
CMD ["/hello"]

执行docker build -t hello-world ./ 就会构建出一个镜像,大小仅为13kb。

需要注意的是,Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。如果我们希望在构建镜像时不使用缓存,可以在 docker build 命令中加上 --no-cache 参数。

Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效。

下面列出了 Dockerfile 中最常用的指令,完整列表和说明可参看官方文档。

FROM
指定 base 镜像。

MAINTAINER
设置镜像的作者,可以是任意字符串。可选。

COPY
将文件从 build context 复制到镜像。
COPY 支持两种形式:

  1. COPY src dest
  2. COPY ["src", "dest"]

注意:src 只能指定 build context 中的文件或目录。

ADD
与 COPY 类似,从 build context 复制文件到镜像。不同的是,如果 src 是归档文件(tar, zip, tgz, xz 等),文件会被自动解压到 dest。

ENV
设置环境变量,环境变量可被后面的指令使用。例如:

...
ENV MY_VERSION 1.3
RUN apt-get install -y mypackage=$MY_VERSION
...

EXPOSE
指定容器中的进程会监听某个端口,Docker 可以将该端口暴露出来。

VOLUME
将文件或目录声明为 volume。

WORKDIR
为后面的 RUN, CMD, ENTRYPOINT, ADD 或 COPY 指令设置镜像中的当前工作目录。

RUN
在容器中运行指定的命令。

CMD
容器启动时运行指定的命令。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效。CMD 可以被 docker run 之后的参数替换。

ENTRYPOINT
设置容器启动时运行的命令。
Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效。CMD 或 docker run 之后的参数会被当做参数传递给 ENTRYPOINT。

RUNCMDENTRYPOINT 这三个 Dockerfile 指令看上去很类似,很容易混淆。简单的说:

  1. RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。
  2. CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换。
  3. ENTRYPOINT 配置容器启动时运行的命令。ENTRYPOINT 看上去与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。

比如下面的 Dockerfile 片段:

ENTRYPOINT ["/bin/echo", "Hello"]  
CMD ["world"]

当容器通过 docker run -it [image] 启动时,输出为:

Hello world

而如果通过 docker run -it [image] Docker启动,则输出为:

Hello Docker

最佳实践:

  1. 使用 RUN 指令安装应用和软件包,构建镜像。
  2. 如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命令行替换默认参数。
  3. 如果想为容器设置默认的启动命令,可使用 CMD 指令。用户可在 docker run 命令行中替换此默认命令。

4.2 Dockerfile 多阶段构建

Docker 17.05版本以后,新增了Dockerfile多阶段构建。所谓多阶段构建,实际上是允许一个Dockerfile 中出现多个 FROM 指令。这样做有什么意义呢?

每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,但是,能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。

最大的使用场景是将编译环境和运行环境分离,比如,之前我们需要构建一个Go语言程序,那么就需要用到go命令等编译环境,我们的Dockerfile可能是这样的:

FROM golang:1.10.3
COPY server.go /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server
ENTRYPOINT ["/build/server"]

基础镜像 golang:1.10.3 是非常庞大的,因为其中包含了所有的Go语言编译工具和库,而运行时候我们仅仅需要编译后的 server 程序就行了,不需要编译时的编译工具,最后生成的大体积镜像就是一种浪费。

最后将编译接口拷贝到镜像中就行了,那么Dockerfile的基础镜像并不需要包含Go编译环境:

# 编译阶段
FROM golang:1.10.3
COPY server.go /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

# 运行阶段
FROM scratch
# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=0 /build/server /
ENTRYPOINT ["/server"]

这个 Dockerfile 的玄妙之处就在于 COPY 指令的 --from=0 参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,0代表第一个阶段。除了使用数字,我们还可以给阶段命名,比如:

# 编译阶段 命名为 builder
FROM golang:1.10.3 as builder

# ... 省略

# 运行阶段
FROM scratch

# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=builder /build/server /

4.3 Registry

Registry 是存放 Docker 镜像的仓库,Registry 分私有和公有两种。

Docker Hub(https://hub.docker.com/) 是默认的 Registry,由 Docker 公司维护,上面有数以万计的镜像,用户可以自由下载和使用。

出于对速度或安全的考虑,我们也可以创建自己的私有 Registry。

  • docker pull 命令可以从 Registry 下载镜像。
  • docker run 命令则是先下载镜像(如果本地没有),然后再启动容器。
  • docker push 将镜像上传到 Registry。需要登录才能使用。
  • docker login 登录Registry。

5 容器平台技术:Kubernetes

5.1 Kubernetes是什么

Kubernetes (K8s) 是 Google 在 2014 年发布的一个开源项目。最初,Google 开发了一个叫 Borg 的系统(现在命令为 Omega)来调度如此庞大数量的容器和工作负载。在积累了这么多年的经验后,Google 决定重写这个容器管理系统,并将其贡献到开源社区,让全世界都能受益。这个项目就是 Kubernetes。简单的讲,Kubernetes 是 Google Omega 的开源版本。

目前 Kubernetes 已经成为发展最快、市场占有率最高的容器编排引擎产品。

与Docker的关系?

Kubernetes与Docker之间的关系可以说是相辅相成的。 Docker为Kubernetes提供了强大的容器运行时环境,而Kubernetes则为Docker容器提供了自动化管理和编排的能力。 简而言之,Docker解决了应用程序的打包和运行问题,而Kubernetes解决了应用程序的分布式管理和扩展问题。

5.2 创建 k8s 集群

这里创建的平台只是单机版的,仅用于学习测试,不适用生产环境。

方法一:启用Docker Desktop里的Kubernetes

  1. 打开 Docker Desktop,并在设置中启用 Kubernetes。

  2. 等待 Kubernetes 启动完成后,打开命令行工具(例如 PowerShell 或者 CMD),使用 kubectl 命令行工具连接到 Kubernetes 集群。可以使用以下命令检查 Kubernetes 是否已经启动:

kubectl cluster-info

如果输出类似于以下内容,则表示 Kubernetes 已经启动:

Kubernetes control plane is running at https://localhost:6443
KubeDNS is running at https://localhost:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

方法二:安装minikube

https://github.com/kubernetes/minikube/releases/latest/download/minikube-windows-amd64.exe

minikube下载下来后,重命名为minikube,添加环境变量,无需安装,然后命令行执行minikube start,等待自动安装完成:

> minikube start --image-mirror-country='cn'
😄  Microsoft Windows 10 Enterprise Ltsc 2021 10.0.19044.3324 Build 19044.3324 上的 minikube v1.31.2
✨  自动选择 docker 驱动
📌  使用具有 root 权限的 Docker Desktop 驱动程序
👍  正在集群 minikube 中启动控制平面节点 minikube
🚜  正在拉取基础镜像 ...
💾  正在下载 Kubernetes v1.27.4 的预加载文件...
...

5.3 k8s 核心功能

本节带领大家快速体验 k8s 的核心功能:应用部署、访问、缩容扩容(Scale Up/Down) 以及滚动更新。

1、部署应用

kubectl create deployment kubernetes-bootcamp --image=docker.io/jocatalin/kubernetes-bootcamp:v1

这里我们通过 kubectl create 部署了一个应用,命名为 kubernetes-bootcamp

这里 deployment 是 Kubernetes 的术语,可以理解为应用

Kubernetes 还有一个重要术语 Pod。Pod 是容器的集合,通常会将紧密相关的一组容器放到一个 Pod 中,同一个 Pod 中的所有容器共享 IP 地址和 Port 空间,也就是说它们在一个 network namespace 中。Pod 是 Kubernetes 调度的最小单位,同一 Pod 中的容器始终被一起调度。

查看deployment、pod列表:

 $ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           11s

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-5459c99d8c-4djlb   1/1     Running   0          13s

Tips: 删除命令:

  • 删除应用:kubectl delete deployments kubernetes-bootcamp
  • 删除pod: kubectl delete pods kubernetes-bootcamp

注意:单独删除pod,k8s会启动新的pod,删除deployment才可以正确删除应用。

2、访问应用

默认情况下,所有 Pod 只能在集群内部访问。如果需要将应用程序暴露到外部网络中,可以使用以下命令创建一个 Service:

kubectl expose deployments/kubernetes-bootcamp --port=8085 --target-port=8080 --type=LoadBalancer

在这个命令中,我们使用了以下参数:

  • deployments/kubernetes-bootcamp:要暴露的 Deployment 的名称。deployments写成``deployment`也可以,不区分单数复数。
  • --target-port 选项指定了容器的端口。
  • --port 选项指定了 Service 暴露的端口。
  • --type=LoadBalancer:Service 的类型,即负载均衡器类型。这意味着 Kubernetes 会自动为该 Service 创建一个负载均衡器,并将其公开到外部网络中。

等待 Service 启动完成后,可以使用以下命令查看 Service 的状态:

$ kubectl get services
NAME                  TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP      10.96.0.1       <none>        443/TCP          112m
kubernetes-bootcamp   LoadBalancer   10.100.251.11   localhost     8080:30067/TCP   3s

在浏览器访问:http://localhost:8080/,可以看到如下信息:

Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5459c99d8c-4djlb | v=1

补充说明:上面示例里,8080:30067/TCP 表示该 Service 在集群内部使用 30067 端口进行通信,而在集群外部使用 8080 端口进行通信。这是因为 Kubernetes 集群中的 Pod 可能会频繁启动和停止,而 Service 可以为这些 Pod 提供一个稳定的网络入口。当外部网络中的请求到达 Service 时,Kubernetes 会将这些请求转发到运行该 Pod 的节点上,并使用节点上的 30067 端口与该 Pod 进行通信。

因此,要访问该 Service 提供的应用程序,可以使用 http://EXTERNAL-IP:8080,其中 EXTERNAL-IP 是分配给该 Service 的外部 IP 地址。

Tips: 删除服务:

  • kubectl delete services kubernetes-bootcamp

3、Scale 应用

默认情况下应用只会运行一个副本,可以通过 kubectl get deployments查看副本数。执行如下命令将副本数增加到 2 个:

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-5459c99d8c-4djlb   1/1     Running   0          22m

$  kubectl scale deployments/kubernetes-bootcamp --replicas=2
deployment.apps/kubernetes-bootcamp scaled

$  kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-5459c99d8c-4djlb   1/1     Running   0          22m
kubernetes-bootcamp-5459c99d8c-mhldt   1/1     Running   0          3s

通过 curl 访问应用,可以看到每次请求发送到不同的 Pod,2个副本轮询处理,这样就实现了负载均衡:

$ curl -s http://localhost:8080/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5459c99d8c-4djlb | v=1

$ curl -s http://localhost:8080/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5459c99d8c-mhldt | v=1

要缩容 (scale down) 也很方便,执行命令:

$ kubectl scale deployments/kubernetes-bootcamp --replicas=1
deployment.apps/kubernetes-bootcamp scaled


$ kubectl get pods
NAME                                   READY   STATUS        RESTARTS   AGE
kubernetes-bootcamp-5459c99d8c-4djlb   1/1     Running       0          31m
kubernetes-bootcamp-5459c99d8c-mhldt   1/1     Terminating   0          9m29s

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-5459c99d8c-4djlb   1/1     Running   0          32m

4、滚动更新

当前应用使用的 image 版本为 v1,执行如下命令将其升级到 v2:

$ kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2
deployment.apps/kubernetes-bootcamp image updated

$ curl -s http://localhost:8080/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-c564db98f-x4hp6 | v=2

命令格式:

kubectl set image deployment/<deployment-name> <container-name>=<new-image-name>:<new-tag>

通过 kubectl get pods 可以观察滚动更新的过程:v1 的 Pod 被逐个删除,同时启动了新的 v2 Pod。更新完成后访问新版本应用。

如果要回退到 v1 版本也很容易,执行 kubectl rollout undo 命令:

kubectl rollout undo deployments/kubernetes-bootcamp 

如果要回退到指定版本,需要先获取版本号(不是镜像的tag):

$ kubectl rollout history deployment/kubernetes-bootcamp
deployment.apps/kubernetes-bootcamp
REVISION  CHANGE-CAUSE
1         <none>
4         <none>
5         <none>
6         <none>

$ kubectl rollout undo deployment/kubernetes-bootcamp --to-revision=4
deployment.apps/kubernetes-bootcamp rolled back

Tips:如果pod创建异常,可以使用 kubectl describe pod kubernetes-bootcamp-665c496b44-mhrpw命令查看详情。

5.4 k8s 重要概念

Kubernetes 有几个重要概念,它们是组成 Kubernetes 集群的基石,必须了解。

  • Cluster :Cluster 是计算、存储和网络资源的集合,Kubernetes 利用这些资源运行各种基于容器的应用。
  • Master :Master 是 Cluster 的大脑,它的主要职责是调度,即决定将应用放在哪里运行。
  • Node :Node 的职责是运行容器应用。Node 由 Master 管理,Node 负责监控并汇报容器的状态,并根据 Master 的要求管理容器的生命周期。
  • Pod :Pod 是 Kubernetes 的最小工作单元。每个 Pod 包含一个或多个容器。Pod 中的容器会作为一个整体被 Master 调度到一个 Node 上运行。
  • Replication Controller:K8s 通常不会直接创建 Pod,而是通过通过 Controller 来管理 Pod 的。Controller 中定义了 Pod 的部署特性,比如有几个副本,在什么样的 Node 上运行等。Controller会确保任何时候Kubernetes集群中有指定数量的pod副本(replicas)在运行。
  • Service :Service 定义了外界访问一组特定 Pod 的方式。Service 有自己的 IP 和端口,Service 为 Pod 提供了负载均衡。

为了满足不同的业务场景,Kubernetes 提供了多种 Controller:

  • Deployment: 是最常用的 Controller。Deployment 可以管理 Pod 的多个副本,并确保 Pod 按照期望的状态运行。
  • ReplicaSet: 实现了 Pod 的多副本管理。使用 Deployment 时会自动创建 ReplicaSet,也就是说 Deployment 是通过 ReplicaSet 来管理 Pod 的多个副本,我们通常不需要直接使用 ReplicaSet。
  • DaemonSet: 用于每个 Node 最多只运行一个 Pod 副本的场景。正如其名称所揭示的,DaemonSet 通常用于运行 daemon。
  • StatefuleSet: 能够保证 Pod 的每个副本在整个生命周期中名称是不变的。而其他 Controller 不提供这个功能,当某个 Pod 发生故障需要删除并重新启动时,Pod 的名称会发生变化。同时 StatefuleSet 会保证副本按照固定的顺序启动、更新或者删除。
  • Job: 用于运行结束就删除的应用。而其他 Controller 中的 Pod 通常是长期持续运行。

最后了解下Namespace(命名空间)的概念:

用于将一个物理的 Cluster 逻辑上划分成多个虚拟 Cluster,每个 Cluster 就是一个 Namespace。不同 Namespace 里的资源是完全隔离的。Kubernetes 默认创建了两个 Namespace。

  • default -- 创建资源时如果不指定,将被放到这个 Namespace 中。
  • kube-system -- Kubernetes 自己创建的系统资源将放到这个 Namespace 中。

5.5 k8s 通过YAML文件创建资源

上文我们之间通过命令行kubectl create创建资源,k8s还支持通过配置文件和 kubectl apply 创建。下面的示例实现了与上面例子相同的效果:

# kubernetes-bootcamp-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubernetes-bootcamp-yml #应用名
spec:
  selector:
    matchLabels:
      app: kubernetes-bootcamp-yml
  replicas: 2
  template:
    metadata:
      labels:
        app: kubernetes-bootcamp-yml
    spec:
      containers:
      - name: kubernetes-bootcamp-yml
        image: docker.io/jocatalin/kubernetes-bootcamp:v1
        ports:
        - containerPort: 8080

# kubernetes-bootcamp-service.yml
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-bootcamp-yml #服务名
spec:
  selector:
    app: kubernetes-bootcamp-yml
  type: LoadBalancer
  ports:
  - name: http
    port: 9051 #对外服务端口
    targetPort: 8080 #容器端口

命令行执行:

kubectl apply -f kubernetes-bootcamp-deployment.yml
kubectl apply -f kubernetes-bootcamp-service.yml

即可创建出应用(Deployment)及服务(Service):

$ kubectl apply -f kubernetes-deployment-deployment.yml
deployment.apps/kubernetes-bootcamp-yml created

$ kubectl apply -f kubernetes-bootcamp-service.yml
service/kubernetes-bootcamp-yml created

$ kubectl get deployments
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp-yml   2/2     2            2           6m36s

$ kubectl get services
NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes-bootcamp-yml   LoadBalancer   10.107.237.111   localhost     9051:31499/TCP   10m

$ curl -s http://localhost:9051
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-yml-66955668d4-prx6f | v=1

Tips: kubectl apply 不但能够创建 Kubernetes 资源,也能对资源进行更新,非常方便。不过 Kubernets 还提供了几个类似的命令,例如 kubectl createkubectl replacekubectl editkubectl patch

为避免造成不必要的困扰,尽量只使用 kubectl apply,此命令已经能够应对超过 90% 的场景,事半功倍。

Tips: 如果已经创建了一个 Service,但是忘记了将端口映射到容器端口,可以使用 kubectl edit 命令编辑 Service 的配置,然后添加 targetPort 字段。例如,以下命令将使用默认编辑器打开名为 kubernetes-bootcamp 的 Service 的配置:

kubectl edit svc kubernetes-bootcamp

然后您可以在配置文件中添加 targetPort 字段,如下所示:

apiVersion: v1
kind: Service
metadata:
  name: kubernetes-bootcamp-yml
spec:
  type: LoadBalancer
  ports:
  - port: 9051
    targetPort: 8080
  selector:
    app: kubernetes-bootcamp

保存并退出编辑器后,Kubernetes 将自动更新 Service 的配置,并将端口映射到容器端口上。

5.6 Kubernetes Dashboard

前面章节 Kubernetes 所有的操作我们都是通过命令行工具 kubectl 完成的。为了提供更丰富的用户体验,Kubernetes 还开发了一个基于 Web 的 Dashboard,用户可以用 Kubernetes Dashboard 部署容器化的应用、监控应用的状态、执行故障排查任务以及管理 Kubernetes 各种资源。

yml文件下载地址:https://github.com/AliyunContainerService/k8s-for-docker-desktop

# 创建 dashboard 资源
$ kubectl apply -f kubernetes-dashboard.yaml

# 查看 Deployment 的运行状态 
$ kubectl --namespace=kubernetes-dashboard get deployment -n kubernetes-dashboard

# 查看 Pod 的运行状态
$ kubectl --namespace=kubernetes-dashboard get pods -n kubernetes-dashboard

# 授权kube-system默认服务账号
$ kubectl apply -f kube-system-default.yaml

# 通过代理的方式访问 dashboard
$ kubectl proxy

创建 dashboard 资源的输出:

namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

Dashboard 会在 kubernetes-dashboard namespace 中创建自己的 Deployment 和 Service。

这里需要使用 API Server 的形式访问的 dashboard ,具体的地址为:http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

我们可以使用下面的 powershell 命令打印出 token,然后登录到 dashboard 中查看整个集群的信息

$TOKEN=((kubectl -n kube-system describe secret default | Select-String "token:") -split " +")[1]
kubectl config set-credentials docker-desktop --token="${TOKEN}"
echo $TOKEN

登录dashboard的时候:

输入上文控制台输出的内容即可进入控制台。

5.7 k8s常用命令参考

  • kubectl get pods:列出所有 Pod。
  • kubectl get services:列出所有 Service。
  • kubectl get deployments:列出所有 Deployment。
  • kubectl get configmaps:列出所有 ConfigMap。
  • kubectl describe pod <pod-name>:显示 Pod 的详细信息。
  • kubectl describe service <service-name>:显示 Service 的详细信息。
  • kubectl describe deployment <deployment-name>:显示 Deployment 的详细信息。
  • kubectl describe configmap <configmap-name>:显示 ConfigMap 的详细信息。
  • kubectl describe secret <secret-name>:显示 Secret 的详细信息。
  • kubectl create deployment <deployment-name> --image=<image-name>:创建一个 Deployment。
  • kubectl edit svc my-service: 编辑指定 Service 的配置信息。该命令将打开一个编辑器,允许您修改 Service 的 YAML 配置文件
  • kubectl expose deployment my-deployment --port=8080 --target-port=80 --type=NodePort 创建一个 Service,并将其暴露到集群外部
  • kubectl scale deployment my-deployment --replicas=3 调整指定资源的副本数
  • kubectl apply -f <yaml-file>:使用 YAML 文件创建或更新资源。
  • kubectl delete <resource-type> <resource-name>:删除指定的资源。
    • kubectl delete deployment kubernetes-dashboard
    • kubectl --namespace=kubernetes-dashboard delete deployment --all
    • kubectl delete namespace kubernetes-dashboard 删除空间
  • kubectl exec <pod-name> <command>:在 Pod 中执行命令。
  • kubectl exec -it <pod-name> /bin/bash:进入 Pod 中的 Bash shell。

参考文档

1、Docker学习笔记
https://www.cnblogs.com/52fhy/p/5638571.html#docker镜像

2、Docker Docs: How to build, share, and run applications | Docker Documentation
https://docs.docker.com/

3、https://minikube.sigs.k8s.io/docs/start/

4、https://kubernetes.io/docs/tutorials/hello-minikube/

5、https://blog.csdn.net/wise_nut/article/details/107240396

posted @ 2023-10-26 14:00  飞鸿影  阅读(73)  评论(0编辑  收藏  举报