1.9 附录

部署环境

这一部分介绍了 Docker 容器环境和 Kubernetes 集群环境的 Step-By-Step 的安装流程。加入开发与运维的环境依赖这些内容,严格来讲并不符合本文档的主题,但对于刚刚接触这个领域的读者,这些内容确实有一定的复杂性,为避免新人受到不必要的打击,笔者还是在附录中加入这部分的内容,如对这部分已有了解的读者,完全可以略过。

部署 Docker CE 容器环境

本文为 Linux 系统安装 Docker 容器环境的简要说明,主要包括:

  1. 安装稳定最新发行版(Stable Release)的命令及含义。
  2. 针对国内网络环境的必要镜像加速或者代理设置工作。

若需了解 Docker 安装其他方面的内容,如安装 Nightly/Test 版本、Backporting、软件版权和支持等信息,可参考官方的部署指南:https://docs.docker.com/install/

文中涉及到的 Debian 系和 Redhat 系的包管理工具,主要包括:

  • Debian 系:Debian、Ubuntu、Deepin、Mint
  • Redhat 系:RHEL、Fedora、CentOS

如用的其他 Linux 发行版,如 Gentoo、Archlinux、OpenSUSE 等,建议自行安装二进制包。

移除旧版本 Docker

如果以前已经安装过旧版本的 Docker(可能会被称为 docker,docker.io 或 docker-engine),需先行卸载。

  • Debian 系:

    $ sudo apt-get remove docker docker-engine docker.io containerd runc docker-ce docker-ce-cli containerd.io
  • RedHat 系:

    $ sudo yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine

安装 Docker 依赖工具链及软件源

在 Debian 上主要是为了 apt 能够正确使用 HTTPS 协议,并将 Docker 官方的 GPG Key(GNU Privacy Guard,包的签名机制)和软件源地址注册到软件源中。

在 RHEL 上是为了 devicemapper 获得 yum-config-manager、device-mapper-persistent-data、lvm2 的支持。

  • Debian 系:

    $ sudo apt-get install apt-transport-https \
                           ca-certificates \
                           curl \
                           software-properties-common
     
    # 注册Docker官方GPG公钥
    $ sudo curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
     
    # 检查Docker官方GPG公钥指纹是否正确
    $ sudo apt-key fingerprint 0EBFCD88
     
    pub   4096R/0EBFCD88 2017-02-22
          Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
    uid                  Docker Release (CE deb) <docker@docker.com>
    sub   4096R/F273FCD8 2017-02-22
     
    # 将Docker地址注册到软件源中
    # 注意$(lsb_release -cs)是返回当前发行版的版本代号,例如Ubuntu 18.04是bionic,19.10是eoan
    # 但在Ubuntu 19.10发布一段时间后,Docker官方并未在源地址中增加eoan目录,导致此命令安装失败,日后在最新的系统上安装Docker,需要注意排查此问题,手动更改版本代号完成安装
    $ sudo add-apt-repository \
        "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
        $(lsb_release -cs) \
        stable"
  • RedHat 系:

    $ sudo yum install -y yum-utils \
                          device-mapper-persistent-data \
                          lvm2
     
    # 将Docker地址注册到软件源中
    $ sudo yum-config-manager \
           --add-repo \
           https://download.docker.com/linux/centos/docker-ce.repo

更新系统软件仓库

  • Debian 系:

    $ sudo apt-get update
  • RedHat 系:

    $ sudo yum update

安装 Docker-Engine Community

  • Debian 系:

    $ sudo apt-get install docker-ce docker-ce-cli containerd.io
  • RedHat 系:

    $ sudo yum install docker-ce docker-ce-cli containerd.io

确认 Docker 安装是否成功

直接运行官方的 hello-world 镜像测试安装是否成功

$ sudo docker run hello-world

配置国内镜像库

可选

由于 Docker 官方镜像在国内访问缓慢,官方提供了在国内的镜像库:https://registry.docker-cn.com,以加快访问速度(但其实体验也并不快)。

# 该配置文件及目录,在Docker安装后并不会自动创建
$ sudo mkdir -p /etc/docker
 
# 配置加速地址
$ sudo tee /etc/docker/daemon.json <<-'EOF'
{
   "registry-mirrors": ["https://registry.docker-cn.com"]
}
EOF
 
# 重启服务
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

WARNING

以上操作有两点提醒读者重点关注:

  1. 必须保证 daemon.json 文件中完全符合 JSON 格式,如果错了,Docker 不会给提示,直接起不来。
  2. 如果 Docker 是作为 systemd 管理的服务的,daemon.json 文件会处于锁定状态,应先关闭后再修改配置;

这两点出了问题都会导致 Docker 服务直接无法启动,如果出现该情况,可以通过 systemd status 命令检查,看是否有类似如下的错误提示:

Drop-In: /etc/systemd/system/docker.service.d
          └─mirror.conf
  Active: inactive (dead) (Result: exit-code) since 五 2017-09-15 13:25:28 CST; 7min ago
    Docs: https://docs.docker.com
 Main PID: 21151 (code=exited, status=1/FAILURE)

如果是,修改 daemon.json 后重新启动即可。另外,关闭 systemd 服务的方法是:

$ sudo systemctl stop docker

最后,Docker 的官方国内镜像库的速度只能说比起访问国外好了一丢丢,聊胜于无。国内还有一些公开的镜像库,如微软的、网易的等,但要么是不稳定,要么也是慢。比较靠谱的是阿里云的镜像库,但这个服务并不是公开的,需要使用者先到阿里云去申请开发者账户,再使用加速服务,申请后会得一个类似于“https://yourname.mirror.aliyuncs.com”的私有地址,把它设置到daemon.json中即可使用。

为 Docker 设置代理

可选

另外一种解决 Docker 镜像下载速度慢的方案就是使用代理,Docker 的代理可以直接读取系统的全局代理,即系统中的 HTTP_PROXY、HTTPS_PROXY 两个环境变量。不过,如果设置这两个变量,其他大量 Linux 下的其他工具也会受到影响,所以建议的方式是给 Docker 服务设置专有的环境变量,我们使用 Systemd 来管理 Docker 服务,那直接给这个服务设置一个额外配置即可,操作如下:

$ sudo rm -rf /etc/systemd/system/docker.service.d
sudo mkdir -p /etc/systemd/system/docker.service.d
 
# 配置代理地址,支持http、https、socks、socks5等协议
$ sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf <<-'EOF'
[Service]
Environment="HTTP_PROXY=socks5://192.168.31.125:2012"
EOF
 
#重启docker
$ sudo systemctl restart docker

设置后可以通过 systemctl 检查一下环境变量,看看是否有设置成功:

$ systemctl show --property=Environment docker

输出:

Environment=HTTP_PROXY=socks5://192.168.31.125:2012

开放 Docker 远程服务

可选

如果需要在其他机器上管理 Docker——譬如典型的如在 IntelliJ IDEA 这类 IDE 环境中给远程 Docker 部署镜像,那可以开启 Docker 的远程管理端口,这步没有设置任何安全访问措施,请不要在生产环境中进行。

具体做法是修改 Docker 的服务配置:

  • Debian 系:

    $ sudo vim /lib/systemd/system/docker.service
  • RedHat 系:

    $ sudo vim /usr/lib/systemd/system/docker.service

在 ExexStart 后面增加以下参数(2375 端口可以自定义):

-H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock

譬如,默认安装完 Docker,修改之后完整的 ExexStart 应当如下所示:

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock

最后重启 Docker 服务即可:

#重启docker
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

启用 Docker 命令行自动补全功能

可选

在控制台输入 docker 命令时可以获得自动补全能力,提高效率。 Docker 自带了 bash 的命令行补全,用其他 shell,如 zsh,则需采用 zsh 的插件或者自行获取补全信息

  • bash:

    $ echo 'source /usr/share/bash-completion/completions/docker' >> ~/.bashrc
  • zsh:

    $ mkdir -p ~/.zsh/completion
    $ curl -L https://raw.githubusercontent.com/docker/cli/master/contrib/completion/zsh/_docker > ~/.zsh/completion/_docker
     
    $ echo 'fpath=(~/.zsh/completion $fpath)' >> ~/.zshrc
    $ echo 'autoload -Uz compinit && compinit -u' >> ~/.zshrc

将 Docker 设置为开机启动

可选

一般使用 systemd 来管理启动状态

# 设置为开机启动
$ sudo systemctl enable docker
 
# 立刻启动Docker服务
$ sudo systemctl start docker

安装 Docker-Compose

在开发和部署微服务应用时,经常要使用 Docker-Compose 来组织多个镜像,对于 Windows 系统它是默认安装的,在 Linux 下需要另外下载一下,下载后直接扔到 bin 目录,加上执行权限即可使用

# 从GitHub下载
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 从国内镜像下载
sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
 
sudo chmod +x /usr/local/bin/docker-compose

卸载 Docker

  • Debian 系:

    $ sudo apt-get purge docker-ce
     
    # 清理Docker容器缓存和自定义配置
    $ sudo rm -rf /var/lib/docker
  • RedHat 系:

    $ sudo yum remove docker-ce
     
    # 清理Docker容器缓存和自定义配置
    $ sudo rm -rf /var/lib/docker

部署 Kubernetes 集群

Kubernetes 是一个由 Google 发起的开源自动化部署,缩放,以及容器化管理应用程序的容器编排系统。

部署 Kubernetes 曾经是一件相当麻烦的事情,早期版本中,Kubelet、Api-Server、Etcd、Controller-Manager 等每一个组件都需要自己单独去部署,还要创建自签名证书来保证各个组件之间的网络访问。但程序员大概是最爱与麻烦做斗争的群体,随着 Kubernetes 的后续版本不断改进(如提供了自动生成证书、Api-Server 等组件改为默认静态 Pod 部署方式),使得部署和管理 Kubernetes 集群正在变得越来越容易起来。目前主流的方式大致有:

  • 使用 Kubeadm 部署 Kubernetes 集群
  • 使用 Rancher 部署、管理 Kubernetes 集群(其他如 KubeSphere 等在 Kubernetes 基础上构建的工具均归入此类)
  • 使用 Minikube 在本地单节点部署 Kubernetes 集群(其他如 Microk8s 等本地环境的工具均归入此类)

以上几种部署方式都有很明显的针对性,个人开发环境以 Minikube 最简单,生产环境以 Rancher 最简单,在云原生环境中,自然是使用环境提供的相应工具。不过笔者推荐首次接触 Kubernetes 的同学最好还是选择 Kubeadm 来部署,毕竟这是官方提供的集群管理工具,是相对更底层、基础的方式,充分熟悉了之后再接触其他简化的方式会快速融会```sh

添加K8S软件源

$ sudo add-apt-repository “deb http://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main”


添加源后记得执行一次更新:
```sh
$ sudo apt-get update

安装 kubelet、kubectl、kubeadm

并不需要在每个节点都装上 kubectl,但都装上也无不可。下面简要列出了这三个工具/组件的作用,现在看不看得懂都没有关系,以后用到它们的机会多得是,要相信日久总会生情的。

  • kubeadm: 引导启动 Kubernate 集群的命令行工具。
  • kubelet: 在群集中的所有计算机上运行的组件, 并用来执行如启动 Pods 和 Containers 等操作。
  • kubectl: 用于操作运行中的集群的命令行工具。
$ sudo apt-get install kubelet kubeadm kubectl

初始化集群前的准备

在使用 kubeadm 初始化集群之前,还有一些必须的前置工作要妥善处理:

首先,基于安全性(如在官方文档中承诺的 Secret 只会在内存中读写,不会落盘)、利于保证节点同步一致性等原因,从 1.8 版开始,Kubernetes 就在它的文档中明确声明了它默认不支持 Swap 分区,在未关闭 Swap 分区的机器中,集群将直接无法启动。关闭 Swap 的命令为:

$ sudo swapoff -a

上面这个命令是一次性的,只在当前这次启动中生效,要彻底关闭 Swap 分区,需要在文件系统分区表的配置文件中去直接除掉 Swap 分区。你可以使用文本编辑器打开 /etc/fstab ,注释其中带有“swap”的行即可,或使用以下命令直接完成修改:

# 还是先备份一下
$ yes | sudo cp /etc/fstab /etc/fstab_bak
 
# 进行修改
$ sudo cat /etc/fstab_bak | grep -v swap > /etc/fstab

可选操作

当然,在服务器上使用的话,关闭 Swap 影响还是很大的,如果服务器除了 Kubernetes 还有其他用途的话(除非实在太穷,否则建议不要这样混用;一定要混用的话,宁可把其他服务搬到 Kubernetes 上)。关闭 Swap 有可能会对其他服务产生不良的影响,这时需要修改每个节点的 kubelet 配置,去掉必须关闭 Swap 的默认限制,具体操作为:

$ echo "KUBELET_EXTRA_ARGS=--fail-swap-on=false" >> /etc/sysconfig/kubelet

其次,由于 Kubernetes 与 Docker 默认的 cgroup(资源控制组)驱动程序并不一致,Kubernetes 默认为 systemd ,而 Docker 默认为 cgroupfs 。在这里我们要修改 Docker 或者 Kubernetes 其中一个的 cgroup 驱动,以便两者统一。根据官方文档《CRI installation》中的建议,对于使用 systemd 作为引导系统的 Linux 的发行版,使用 systemd 作为 Docker 的 cgroup 驱动程序可以服务器节点在资源紧张的情况表现得更为稳定。

这里选择修改各个节点上 Docker 的 cgroup 驱动为 systemd ,具体操作为编辑(无则新增)/etc/docker/daemon.json 文件,加入以下内容即可:

{
  "exec-opts": ["native.cgroupdriver=systemd"]
}

然后重新启动 Docker 容器:

$ systemctl daemon-reload
$ systemctl restart docker

预拉取镜像

可选

预拉取镜像并不是必须的,本来初始化集群的时候系统就会自动拉取 Kubernetes 中要使用到的 Docker 镜像组件,也提供了一个 kubeadm config images pull 命令来一次性的完成拉取,这都是因为如果要手工来进行这项工作,实在非常非常非常地繁琐。

但对于许多人来说这项工作往往又是无可奈何的,Kubernetes 的镜像都存储在 k8s.gcr.io 上,如果您的机器无法直接或通过代理访问到 gcr.io(Google Container Registry。笔者敲黑板:这也是属于谷歌的网址)的话,初始化集群时自动拉取就无法顺利进行,所以就不得不手工预拉取。

预拉取的意思是,由于 Docker 只要查询到本地有相同(名称和 tag 完全相同、哈希相同)的镜像,就不会访问远程仓库,那只要从 GitHub 上拉取到所需的镜像,再将 tag 修改成官方的一致,就可以跳过网络访问阶段。

首先使用以下命令查询当前版本需要哪些镜像:

$ kubeadm config images list --kubernetes-version v1.17.3

| Prometheus | | X | | |

1.9 附录

部署 Elastic Stack

无状态应用在 Kubernetes 上部署和迁移都是很容易做到的,但是有状态应用的迁移相对还是有一定门槛,特别是部署对线上环境需要高可用的集群模式时则会更为麻烦,前面文章介绍过,现在比较好的针对有状态应用的部署方案是Operator,市面上也的确出现了很多官方、非官方的 Operator,譬如 Prometheus Operator、Etcd Operator 等等,由于 Elasticsearch 的关系,ELK Stack 属于典型的有状态应用,Elastic.co 官方也推出了基于 Kubernetes Operator 的 Elastic Cloud on Kubernetes(ECK),用户可使用该产品在 Kubernetes 上较为轻松地配置、管理和运行 Elasticsearch 集群。

Elastic Cloud on Kubernetes

ECK 使用 Kubernetes Operator 模式构建而成,但它的功能并不局限于部署与迁移,下面为 Elastic.co 官方博客上对 ECK 的中文介绍,供你对 ECK 有个基本的了解:

官方博客《在 Kubernetes 上运行 Elasticsearch:开启新篇》

Elastic Cloud on Kubernetes(ECK) 是一个 Elasticsearch Operator,但远不止于此。 ECK 使用 Kubernetes Operator 模式构建而成,需要安装在您的 Kubernetes 集群内,其功能绝不仅限于简化 Kubernetes 上 Elasticsearch 和 Kibana 的部署工作这一项任务。ECK 专注于简化所有后期运行工作,例如:

  • 管理和监测多个集群
  • 轻松升级至新的版本
  • 扩大或缩小集群容量
  • 更改集群配置
  • 动态调整本地存储的规模(包括 Elastic Local Volume(一款本地存储驱动器))
  • 备份

ECK 不仅能自动完成所有运行和集群管理任务,还专注于简化在 Kubernetes 上使用 Elasticsearch 的完整体验。ECK 的愿景是为 Kubernetes 上的 Elastic 产品和解决方案提供 SaaS 般的体验。 在 ECK 上启动的所有 Elasticsearch 集群都默认受到保护,这意味着在最初创建的那一刻便已启用加密并受到默认强密码的保护。

从 6.8 和 7.1 版本开始,Elasticsearch 核心安全功能(TLS 加密、基于角色的访问控制,以及文件和原生身份验证)会免费提供。

通过 ECK 部署的所有集群都包括强大的基础(免费)级功能,例如可实现密集存储的冻结索引、Kibana Spaces、Canvas、Elastic Maps,等等。您甚至可以使用 Elastic Logs 和 Elastic Infrastructure 应用监测 Kubernetes 日志和基础设施。您可以获得在 Kubernetes 上使用 Elastic Stack 完整功能的体验。

ECK 内构建了 Elastic Local Volume,这是一个适用于 Kubernetes 的集成式存储驱动器。ECK 中还融入了很多最佳实践,例如在缩小规模之前对节点进行 drain 操作,在扩大的时候对分片进行再平衡,等等。从确保在配置变动过程中不会丢失数据,到确保在规模调整过程中实现零中断。

—— Anurag Gupta,Elasticsearch on Kubernetes: A New Chapter Begins

安装 ECK

由于 Elasticsearch 是相对重量级的应用,建议你的 Kubernetes 每个节点至少有 4 至 8 GB 的可用内存。ECK 支持的最低软件版本如下所示:

  • kubectl 1.11+
  • Kubernetes 1.12+ or OpenShift 3.11+
  • Google Kubernetes Engine (GKE), Azure Kubernetes Service (AKS), and Amazon Elastic Kubernetes Service (EKS)
  • Elasticsearch, Kibana, APM Server: 6.8+, 7.1+
  • Enterprise Search: 7.7+
  • Beats: 7.0+

首先在集群中安装 ECK 对应的 Operator 资源对象:

$ kubectl apply -f https://download.elastic.co/downloads/eck/1.2.1/all-in-one.yaml

安装成功后,会自动创建一个 elastic-system 的名称空间以及一个 Operator 的 Pod:

$ kubectl get pods -n elastic-system
NAME                             READY   STATUS    RESTARTS   AGE
elastic-operator-0               1/1     Running   1          15h

你可以通过以下命令来查看 Operator 的工作日志:

$ kubectl -n elastic-system logs -f statefulset.apps/elastic-operator

部署 Elasticsearch 集群

有 ECK Operator 的帮助,你可以直接使用类型为 Elasticsearch 的自定义资源来部署 Elasticsearch 集群,以下命令部署一套节点单个数为 1,版本为 7.9.2 的 Elasticsearch 集群:

$ cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 7.9.2
  nodeSets:
  - name: default
    count: 1
    config:
      node.master: true
      node.data: true
      node.ingest: true
      node.store.allow_mmap: false
EOF

该命令执行完毕后,Pod、Service 均已自动生成,你可以使用以下命令验证:

$ kubectl get elasticsearch
NAME          HEALTH    NODES     VERSION   PHASE         AGE
quickstart    green     1         7.9.2     Ready         1m

你只要获取访问凭证,就可以通过 HTTP 访问到 Elasticsearch 服务,获取访问凭证的操作如下:

$ kubectl get service quickstart-es-http
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
quickstart-es-http   ClusterIP   10.15.251.145   <none>        9200/TCP   34m
 
PASSWORD=$(kubectl get secret quickstart-es-elastic-user -o go-template='{{.data.elastic | base64decode}}')

通过 HTTP 访问 Elasticsearch 服务的操作如下:

$ curl -u "elastic:$PASSWORD" -k "https://quickstart-es-http:9200"

如果需要在外部访问,通过 Kubernetes 的端口转发即可实现:

$ kubectl port-forward service/quickstart-es-http 9200
 
# 在另一个Console中:
$ curl -u "elastic:$PASSWORD" -k "https://localhost:9200"
 
{
  "name" : "quickstart-es-default-0",
  "cluster_name" : "quickstart",
  "cluster_uuid" : "XqWg0xIiRmmEBg4NMhnYPg",
  "version" : {...},
  "tagline" : "You Know, for Search"
}

部署 Kibana

与部署 Elasticsearch 集群类似,使用类型为 Kibana 的自定义资源即可快速部署 Kibana 实例,命令如下所示:

$ cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 7.9.2
  count: 1
  elasticsearchRef:
    name: quickstart
EOF

你可以通过集群 ClusterIP 及 5601 端口来访问 Kibana,或者进行端口转发到外部:

$ kubectl port-forward service/quickstart-kb-http 5601

当你从浏览器登录 Kibana 时候需要凭证,通过如下方式获取:

$ kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode; echo

部署 Prometheus

如果要手动在 Kubernetes 中处理安装 Prometheus 的每一个细节还是挺麻烦的,在官方的 Kube-Prometheus 项目里提供了明确的操作步骤。不过,如果只是通过 Prometheus Operator 的 Bundle 包安装 Prometheus 则非常简单。

首先从以下地址中获取 Prometheus Operator 的源码:

$ git clone https://github.com/prometheus-operator/prometheus-operator.git

安装里面的 bundle.yaml,然后就完成了:

$ kubectl apply -f bundle.yaml

卸载时,同样根据 bundle.yaml 删除即可:

$ kubectl delete -f bundle.yaml