2. 使用Kubernetes和Docker

  • 2019 年 10 月 10 日
  • 筆記

1. 内容

  • 使用Docker创建、运行及共享容器镜像
  • 在本地部署单节点的Kubernetes集群
  • 配置和使用命令行客户端——kubectl
  • 在Kubernetes上部署应用并进行水平伸缩

2. 创建、运行及共享容器镜像

介绍

深入学习前,先看看如何创建一个简单的应用、打包成容器镜像、在远程集群或本地集群运行

步骤简介

  • 安装Docker并运行第一个“Hello World”容器
  • 创建一个简单的php应用并部署在Kubernetes
  • 把应用打包成可以独立运行的容器镜像
  • 基于镜像运行容器
  • 把镜像推送到Docker Hub,这样任何人在任何地方都可以使用

安装并运行Docker

  • 安装:https://docs.docker.com/docker-for-mac/install/
  • 运行Hello World容器
    • busybox是一个集成最常用linux命令的linux系统容器镜像
    • 使用docker run命令指定镜像名字、执行命令(可选),如图所示
    • 通过一个命令就运行了一个完整的“应用”,而不用做其它的事情
    • 重要的是应用是在容器内部执行的,完全独立于基于其他主机上运行的进程
  • 背后原理
    • 执行docker run命令(docker run busybox echo "Hello World")后,docker会检查busybox:latest镜像是否已经存在本机,如果没有会从http://docker.io的镜像中心拉取
    • 镜像下载到本机后,docker基于这个镜像创建一个容器并在容器中运行命令
    • echo命令输出后,进程终止,容器停止运行
  • 运行其它镜像
    • 如果想运行其它镜像可以在hub.docker.com网站搜索
    • 然后像这样运行镜像docker run <image>
  • 容器镜像的版本管理
    • docker支持同一镜像的多个版本,每个版本必须有唯一的tag名,当没有显式指定tag时,docker默认指定为latest
    • 运行别的版本镜像docker run <image>:<tag>

创建一个简单的php应用

  • 这个应用程序输出当前时间
  • 应用运行在容器中,看到的是自己的主机名而不是宿主机名,即使它像其它进程一样运行在宿主机上
  • 这在后面非常有用,当应用部署在Kubernetes上进行伸缩时(复制应用到多个节点),它的请求切换到了应用的不同实例上

为镜像创建Dockerfile

  • 把应用打包成镜像,首先需要创建名为Dockerfile的文件,它包含了一系列构建镜像时执行的指令
  • Dockerfile和index.php在同一目录
  • Dockerfile内容如下
  • FROM定义了基础镜像,使用了7-zts-alpine3.9版本
  • ADD是把index.php从本地文件夹添加到镜像的/目录
  • EXPOSE声明暴露的端口号
  • WORKDIR指定默认工作目录
  • CMD默认执行的命令,意思是用php启动一个web服务器,端口为8080

构建容器镜像

  • 运行docker命令(docker build -t php:local .)来构建镜像
  • 构建过程不是用Docker客户端完成,而将整个目录上传到Docker守护进程(Docker客户端和守护进程可以不在一台机器上)
  • 构建过程中,Docker首次会从公开镜像仓库(Docker Hub)拉取基础镜像(php:7-zts-alpine3.9)
  • 最后一个.是告诉Docker是基于当前目录,构建名为php、标签为local的镜像;Docker会在目录中寻找Dockerfile,然后基于其中指令构建镜像
使用docker images命令查看生成的镜像  $ docker images  php    local                b26e7de6427c        9 hours ago         64.7MB  php    7-zts-alpine3.9 0adbdb1b2250        2 weeks ago         64.7MB
  • 构建镜像过程
  • 镜像分层
    • 镜像不是一个大的二进制块,而由多层组成的,在busybox例子中,每一层有一行Pull complete,不同镜像可能会共享分层,这会让存储和传输变得更高效
    • Dockerfile每一条单独的指令都会创建一个新层

运行容器镜像

  • 运行应用容器
    • 运行命令docker run --name php-container -p 8000:8000 -d php:local
    • 这条命令告知Docker是基于php:local镜像创建一个名为php-container的容器,本机8000端口映射到容器的8000的端口,-d表示后台运行
  • 访问应用
    • 运行命令$ curl http://localhost:8000
    • 通过http://localhost:8000访问应用,输出当前运行时间。应用是运行在容器中,与其他应用隔离
  • 列出所有运行中的容器
    • 运行命令docker ps
命令结果  CONTAINER ID   IMAGE    COMMAND   CREATED   STATUS   PORTS       NAMES  2b3eb8cbab22   php:local "docker-php-entrypoi…"   2 minutes ago   Up 2 minutes    0.0.0.0:8000->8000/tcp   php-container    
  • 有一个容器正在运行,显示容器的id、名称、运行的镜像、命令等信息
  • 获取容器信息
    • 运行命令docker inspect php-container会打印出包含容器底层信息的长json

探索运行容器的内部

  • 在已有的容器内部运行shell
    • 在php-container容器执行ls -al /,命令和主容器进程有相同的命名空间
    • -i:确保标准输入流保持开放,需要在shell中输入命令
    • -t:分配一个伪终端(TTY)
    • 运行命令docker exec -it php-container ls -al /
输出结果  $ docker exec -it php-container ls -al /  total 68  drwxr-xr-x    1 root     root          4096 May 28 23:13 .  drwxr-xr-x    1 root     root          4096 May 28 23:13 ..  -rwxr-xr-x    1 root     root             0 May 28 23:13 .dockerenv  drwxr-xr-x    1 root     root          4096 May 11 03:27 bin  drwxr-xr-x    5 root     root           340 May 28 23:13 dev  drwxr-xr-x    1 root     root          4096 May 28 23:13 etc  drwxr-xr-x    1 root     root          4096 May 11 03:04 home  -rw-r--r--    1 root     root            33 May 28 23:12 index.php
  • 参数介绍
  • 从内部探索容器
    • 进入容器命令docker exec -it php-container sh
查看容器里运行的进程  # ps -ef  PID   USER     TIME  COMMAND    1   root     0:00  /usr/local/bin/php -S 0.0.0.0:8000    7   root     0:00  sh   12   root     0:00  ps -ef
  • 容器的文件系统也是独立的
执行ls -al /命令查看  # ls -al /  total 68  drwxr-xr-x    1 root     root          4096 May 29 03:58 .  drwxr-xr-x    1 root     root          4096 May 29 03:58 ..  -rwxr-xr-x    1 root     root             0 May 29 03:58 .dockerenv  drwxr-xr-x    1 root     root          4096 May 11 03:27 bin  drwxr-xr-x    5 root     root           340 May 29 03:58 dev  drwxr-xr-x    1 root     root          4096 May 29 03:58 etc  drwxr-xr-x    1 root     root          4096 May 11 03:04 home  -rw-r--r--    1 root     root            33 May 28 23:12 index.php  drwxr-xr-x    1 root     root          4096 May 11 03:27 lib  drwxr-xr-x    5 root     root          4096 May  9 20:49 media  drwxr-xr-x    2 root     root          4096 May  9 20:49 mnt  drwxr-xr-x    2 root     root          4096 May  9 20:49 opt  dr-xr-xr-x  262 root     root             0 May 29 03:58 proc  drwx------    1 root     root          4096 May 29 03:59 root  drwxr-xr-x    2 root     root          4096 May  9 20:49 run  drwxr-xr-x    2 root     root          4096 May  9 20:49 sbin  drwxr-xr-x    2 root     root          4096 May  9 20:49 srv  dr-xr-xr-x   13 root     root             0 May 29 03:58 sys  drwxrwxrwt    1 root     root          4096 May 11 03:27 tmp  drwxr-xr-x    1 root     root          4096 May 11 03:27 usr  drwxr-xr-x    1 root     root          4096 May 11 03:04 var
  • 容器拥有完整的文件系统、进程、用户、主机名和网络接口

停止和删除容器

  • 停止命令
    • 执行docker stop php-container
使用docker ps -a查看容器,发现状态已经是Exited  $ docker ps -a  CONTAINER ID   IMAGE    COMMAND    CREATED  STATUS   PORTS     NAMES  c113cc387a6d   php:local "docker-php-entrypoi…"   8 minutes ago   Exited(137) 56 seconds ago   php-container
  • 删除容器命令 docker rm php-container

向镜像仓库推送镜像

  • 介绍
    • 目前构建的镜像只能本机使用,为了在任何机器上都能使用,可以把镜像推送到外部的镜像仓库,如Docker Hub(http://hub.docker.com)
  • 使用附加标签标注镜像
    • 在Docker Hub注册个账号,把镜像重命名标签
    • 重命名镜像标签docker tag php:local yeedom/php:v1
查看镜像docker images  $ docker images  REPOSITORY     TAG       IMAGE ID            CREATED             SIZE  php            local     9ed8b05c3334        13 hours ago        64.7MB  yeedom/php     v1        9ed8b05c3334        13 hours ago        64.7MB
  • 两个镜像指向同一个镜像id
  • 向Docker Hub推送镜像
推送前先登录  $ docker login  Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.  Username: yeedom  Password:  Login Succeeded
推送镜像  国外网络可能不稳定,失败了可以多试几次  $ docker push yeedom/php:v1  The push refers to repository [docker.io/yeedom/php]  d281f172ef70: Pushed  8a1be1ed4590: Mounted from library/php  b39144a13ab7: Mounted from library/php  060efcf419e8: Mounted from library/php  f38edf0edb30: Mounted from library/php  0b0394abec4c: Mounted from library/php  21f626200b4c: Mounted from library/php  414e112bbb2c: Mounted from library/php  3575e617b5f4: Mounted from library/php  f1b5933fe4b5: Mounted from library/php  v1: digest: sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb size: 2409
  • 在Docker Hub上查看镜像
  • 在不同机器上运行镜像
运行命令$ docker run -p 8000:8000 -d yeedom/php:v1  $ docker run -p 8000:8000 -d yeedom/php:v1  Unable to find image 'yeedom/php:v1' locally  v1: Pulling from yeedom/php  Digest: sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb  Status: Downloaded newer image for yeedom/php:v1  a0d96dc65d6795ba5542754bd3a07e424da672960f545f5cf36e14fa02cac46f
  • 这非常简单,最棒的是应用每次都运行在完全一致的环境中
删除镜像docker rmi yeedom/php:v1  $ docker rmi yeedom/php:v1  Untagged: yeedom/php:v1  Untagged: yeedom/php@sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb
  • 删除容器docker ps -aq | xargs docker rm -f
  • 推送完后,镜像就可以给任何人用了,只需要在机器上运行一条命令就能启动一个新容器了
  • 模拟一台新电脑
  • 在装有docker机器上运行这条命令启动新容器

3. 配置Kubernetes集群

用Minikube运行一个本地单节点Kubernetes集群

  • 介绍
    • 要在Kubernetes运行应用,首先要设置集群
    • 使用Minikube是运行Kubernetes集群最简单、最快捷的方法,它是构建单节点集群的工具,对于测试Kubernetes和本地开发应用都非常有用
    • Minikube在VM中通过VirtualBox、KVM或hyperkit来运行Kubernetes,所以启动Kubernetes集群之前,还需要安装VM
  • 安装文档:因为minikube国内安装有点特殊,请参考文档
  • 使用Minikube启动一个Kubernetes集群
    • 运行命令minikube start
  • 获取集群概览
    • 每个节点运行着Docker、kubelet、kube-proxy
    • Kubectl:向运行在主节点上的Kubernetes API服务器发出REST请求以与集群交互
    • 交互图
  • 列出集群节点
命令kubectl get nodes  $ kubectl get nodes  NAME       STATUS    ROLES     AGE       VERSION  minikube   Ready     <none>    43m       v1.14.2
  • 查看对象更多信息查看对象更多信息查看对象更多信息查看对象更多信息
  • 命令$ kubectl describe node minikube
  • 内容比较多,如输出节点的状态、CPU、内存数据、系统信息、运行容器的节点
    • 如果不指定节点名称则会打印所有节点信息

为kubectl配置别名和命令行补齐

  • 配置别名
  • 使命令行补齐
    • 使用kube-shell,带命令补齐和高亮

4. 在Kubernetes上运行第一个应用

介绍

正常来说,部署一个Kubernetes程序需要包含部署的所有组件描述的配置文件,因为第一次使用,所以用最简单的方法运行Kubernetes程序

部署php应用

运行命令  kube-shell> kubectl run php-container --image=yeedom/php:v1 --port=8000 --generator=run/v1  replicationcontroller "php-container" created
  • 部署应用最简单的方式是使用kubectl run命令,它会创建所有必要的组件而无需json或yaml文件,这样不需要深入了解每个组件对象的结构
  • 参数解释
    • —image:容器镜像
    • —port:端口
    • —generator:创建一个ReplicationController
  • 介绍pod
    • 一个pod是一组紧密相关的容器,运行在同一个工作节点和linux命名空间中
    • 每个pod就像一个独立的逻辑机器,有自己的ip、主机名、进程等,运行一个独立的应用程序
    • 一个pod的所有容器运行在同一个逻辑机器上,也可以出现在不同的节点上
    • 和Docker启动容器不同,Kubernetes不直接处理单个容器,它使用多个共存容器的理念,这组容器叫作pod
    • pod
    • 关系图
  • 列出pod
命令  $ kubectl describe pod php-container-mdkmq  Name:               php-container-mdkmq  Namespace:          default  Priority:           0  PriorityClassName:  <none>  Node:               minikube/10.0.2.15  Start Time:         Thu, 30 May 2019 07:58:10 +0800  Labels:             run=php-container  Annotations:        <none>  Status:             Running  …………………………
  • 介绍:不能列出单个容器,因为它们不是独立的Kubernetes对象,但可列出pod

命令

$ kubectl get pods  NAME                  READY     STATUS              RESTARTS   AGE  php-container-mdkmq   0/1       ContainerCreating   0          7
  • 列出所有pod
  • 查看pod详情
  • 幕后发生的事情
    • ReplicationController对象创建一个新的pod
    • 调度器将其调度到一个工作节点上
    • kubectl看到pod被调度到节点上,就告知Docker拉取镜像
    • 创建容器
    • 构建镜像并推送到Docker Hub
    • 运行kubectl命令时,向Kubernetes API服务器发送http请求在集群中创建一个新的ReplicationController对象

访问web应用

  • 介绍:每个pod在集群内有自己的ip,要让pod从外部访问,需要通过服务对象公开它,要创建一个特殊的LoadBalance类型的服务
  • 创建一个服务对象
    • 命令
$ kubectl expose rc php-container --type=LoadBalancer --name php-service  service "php-service" exposed
  • rc是ReplicationController缩写
  • 列出服务
    • 命令kubectl get service
$ kubectl get service  NAME         TYPE          CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE  kubernetes   ClusterIP     10.96.0.1      <none>        443/TCP         37d  php-service  LoadBalancer  10.105.206.79  localhost     8000:32040/TCP  6s
  • 使用外部ip访问服务
    • 命令
$ curl http://localhost:8000
  • 查看服务
    • 命令
$ kubectl get service php-service  NAME          TYPE          CLUSTER-IP     EXTERNAL-IP  PORT(S)         AGE  php-service   LoadBalancer  10.105.206.79  localhost    8000:32040/TCP  3m

系统的逻辑部分

  • ReplicationController、pod和服务如何组合在一起
    • 如之前所说,Kubernetes没有直接创建和使用容器,它的基本构件是pod
    • 但你也没有直接创建pod,而是通过kubectl run命令创建了ReplicationController,它用于创建pod实例
    • 为了能使pod能从集群外部访问,需要创建一个服务对外暴露
  • pod和它的容器
    • 在系统中最重要最基本的组件是pod
    • 它只包含一个容器,但是通常一个pod可包含任意数量的容器
    • pod有自己的私有ip和主机名
  • ReplicationController的角色
    • 通常,rc用于创建pod多个副本并让它保持运行
    • 如果pod有任何原因消失或停止,那么rc将拉起或重新创建新的pod
  • 为什么需要服务
    • 解决不断变化的pod ip地址,pod可能因为故障而停止,这时会有新pod替换
    • 固定的ip和端口对外提供服务
    • 服务表示一组或多组提供相同服务的pod,到达服务ip和端口的请求会转发到该服务的一个容器ip和端口

水平伸缩应用

  • 增加期望的副本数
    • Kubernetes的一个主要好处是可简单地处理部署,我们把运行实例数量增加到三个
    • 查看rc
kubectl get rc  NAME            DESIRED   CURRENT   READY     AGE  php-container   1         1         1         52m
  • 查看pod
kubectl get pod  NAME                  READY     STATUS    RESTARTS   AGE  php-container-xlhzh   1/1       Running   0          53m
  • 增加副本数
kubectl scale rc php-container --replicas=3  replicationcontroller "php-container" scaled
  • 查看扩容结果 已经由1个变成3个pod了,可以看到给应用扩容非常简单
kubectl get pods  NAME                  READY     STATUS    RESTARTS   AGE  php-container-f879k   1/1       Running   0          5s  php-container-n2pwj   1/1       Running   0          5s  php-container-xlhzh   1/1       Running   0          54m
  • Kubernetes原则:不是告诉Kubernetes要执行什么,而是声明系统的期望状态,Kubernetes世界都是这样的
  • 当切换到服务时请求切换到所有pod上
    • 多次请求服务,会落到不同的pod上
$ curl http://localhost:8000  php-container-n2pwj  2019-05-30 13:23:38    $ curl http://localhost:8000  php-container-xlhzh  2019-05-30 13:23:50    $ curl http://localhost:8000  php-container-f879k  2019-05-30 13:23:54
  • 服务会做负载均衡挡在多个pod前面
  • 服务请求示例图

查看应用运行在哪个节点上

  • 介绍:在Kubernetes世界中,pod运行在哪个节点上不重要,只要它被调度到一个可以提供pod正常运行所需的cpu和内存的节点就可以了
  • 列出pod时显示pod IP和pod的节点:会显示pod的ip和运行的节点
    kubectl get pod -o wide      NAME                  READY     STATUS    RESTARTS   AGE       IP           NODE      php-container-f879k   1/1       Running   0          50m       10.1.1.102   docker-for-desktop      php-container-n2pwj   1/1       Running   0          50m       10.1.1.101   docker-for-desktop      php-container-xlhzh   1/1       Running   0          1h        10.1.1.100   docker-for-desktop
  • 使用kubectl describe查看pod的其它细节
kubectl describe pod php-container-f879k  Name:           php-container-f879k  Namespace:      default  Node:           docker-for-desktop/192.168.65.3  Start Time:     Thu, 30 May 2019 21:10:32 +0800  Labels:         run=php-container  Annotations:    <none>  Status:         Running  IP:             10.1.1.102  Controlled By:  ReplicationController/php-container

5. 小结

  • 拉取、运行镜像
  • 把应用打包到容器镜像,并且推送到公开镜像仓库让大家可以使用
  • 进入运行中的容器并检查运行环境
  • 为kubectl命令行工具设置别名和tab补全
  • 在Kubernetes集群中列出查看节点、pod、服务和ReplicationController
  • 在Kubernetes中运行容器并可以在集群外部访问
  • 了解pod、ReplicationController和服务是关联的基础场景
  • 通过改变ReplicationController的副本数对应用进行水平伸缩