如何在Ubuntu18.04上使用Kubeadm创建Kubernetes集群
作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。
介绍
Kubernetes 是一个容器编排系统,可以大规模管理容器。 Kubernetes 最初由谷歌基于其在生产环境中运行容器的经验而开发,它是开源的,并由世界各地的社区积极开发。
注:本教程使用Kubernetes 1.14版本,本文发表时官方支持的版本。 有关最新版本的最新信息,请参阅 Kubernetes 官方文档中的当前版本说明。
Kubeadm 自动安装和配置 Kubernetes 组件,例如 API 服务器、控制器管理器和 Kube DNS。 但是,它不会创建用户或处理操作系统级依赖项及其配置的安装。 对于这些初步任务,可以使用 Ansible 或 SaltStack 等配置管理工具。 使用这些工具可以更简单地创建额外的集群或重新创建现有的集群,并且更不容易出错。
在本指南中,您将使用 Ansible 和 Kubeadm 从头开始设置 Kubernetes 集群,然后将容器化的 Nginx 应用程序部署到其中。
目标
您的集群将包括以下物理资源:
一个主节点
主节点(Kubernetes 中的 node 指服务器)负责管理集群的状态。 它运行 Etcd,它将集群数据存储在将工作负载调度到工作节点的组件之间。
两个工作节点
工作节点是您的 工作负载 所在的服务器(即 容器化应用程序和服务)将运行。 一旦分配给工作人员,工作人员将继续运行您的工作负载,即使调度完成后主服务器停机。 可以通过添加工作人员来增加集群的容量。
完成本指南后,您将拥有一个可以运行容器化应用程序的集群,前提是集群中的服务器有足够的 CPU 和 RAM 资源供您的应用程序使用。 几乎任何传统的 Unix 应用程序,包括 Web 应用程序、数据库、守护进程和命令行工具都可以容器化并在集群上运行。 集群本身将在每个节点上消耗大约 300-500MB 的内存和 10% of CPU。
设置集群后,您将向其部署 Web 服务器 Nginx 以确保其正确运行工作负载。
先决条件
- 本地 Linux/macOS/BSD 机器上的 SSH 密钥对。 如果您之前没有使用过 SSH 密钥,您可以按照 这个关于如何在本地机器上设置 SSH 密钥的说明 来学习如何设置它们。
- 三台运行 Ubuntu 18.04 的服务器,每台至少有 2GB RAM 和 2 个 vCPU。 您应该能够使用您的 SSH 密钥对以 root 用户身份通过 SSH 连接到每个服务器。
- Ansible 安装在您的本地计算机上。 如果您将 Ubuntu 18.04 作为操作系统运行,请按照 如何在 Ubuntu 18.04 上安装和配置 Ansible 中的“步骤 1 - 安装 Ansible”部分安装 Ansible。 在其他平台如 macOS 或 CentOS 上的安装说明,请按照 官方 Ansible 安装文档。
- 熟悉 Ansible 剧本。 如需查看,请查看 配置管理 101:编写 Ansible Playbooks。
- 了解如何从 Docker 映像启动容器。 如果您需要复习,请查看 如何在 Ubuntu 18.04 上安装和使用 Docker 中的“第 5 步 — 运行 Docker 容器”。
第 1 步 — 设置工作区目录和 Ansible 库存文件
在本节中,您将在本地计算机上创建一个目录作为您的工作区。 您将在本地配置 Ansible,以便它可以与远程服务器通信并在远程服务器上执行命令。 完成后,您将创建一个 hosts
文件,其中包含库存信息,例如服务器的 IP 地址和每个服务器所属的组。
在您的三台服务器中,一台是主服务器,其 IP 显示为 master_ip
。 另外两台服务器将是工作服务器,它们的 IP 地址为 worker_1_ip
和 worker_2_ip
。
在本地计算机的主目录中创建一个名为 ~/kube-cluster
的目录,并将 cd
放入其中:
mkdir ~/kube-cluster cd ~/kube-cluster
该目录将成为本教程其余部分的工作区,并将包含您的所有 Ansible 剧本。 它也将是您将在其中运行所有本地命令的目录。
使用 nano
或您喜欢的文本编辑器创建一个名为 ~/kube-cluster/hosts
的文件:
nano ~/kube-cluster/hosts
将以下文本添加到文件中,该文件将指定有关集群逻辑结构的信息:
~/kube-cluster/主机
[masters] master ansible_host=master_ip ansible_user=root [workers] worker1 ansible_host=worker_1_ip ansible_user=root worker2 ansible_host=worker_2_ip ansible_user=root [all:vars] ansible_python_interpreter=/usr/bin/python3
您可能还记得 Ansible 中的 库存文件 用于指定服务器信息,例如 IP 地址、远程用户和服务器分组,以作为执行命令的单个单元。 ~/kube-cluster/hosts
将是您的清单文件,并且您已向其中添加了两个 Ansible 组(masters 和 workers),以指定集群的逻辑结构。
在 masters 组中,有一个名为“master”的服务器条目,它列出了主节点的 IP (master_ip
),并指定 Ansible 应该以 root 用户身份运行远程命令。
同样,在 workers 组中,有两个工作服务器条目(worker_1_ip
和 worker_2_ip
)也将 ansible_user
指定为 root。
该文件的最后一行告诉 Ansible 使用远程服务器的 Python 3 解释器进行管理操作。
添加文本后保存并关闭文件。
使用组设置服务器清单后,让我们继续安装操作系统级别的依赖项并创建配置设置。
第 2 步 — 在所有远程服务器上创建非根用户
在本节中,您将在所有服务器上创建一个具有 sudo 权限的非 root 用户,以便您可以作为非特权用户手动 SSH 到它们。 例如,如果您希望使用 top/htop
等命令查看系统信息、查看正在运行的容器列表或更改 root 拥有的配置文件,这将非常有用。 这些操作是在集群维护期间例行执行的,使用非 root 用户执行此类任务可以最大限度地降低修改或删除重要文件或无意执行其他危险操作的风险。
在工作区中创建一个名为 ~/kube-cluster/initial.yml
的文件:
nano ~/kube-cluster/initial.yml
接下来,将以下 play 添加到文件中,以在所有服务器上创建一个具有 sudo 权限的非 root 用户。 Ansible 中的 play 是针对特定服务器和组的要执行的步骤的集合。 以下播放将创建一个非 root sudo 用户:
~/kube-cluster/initial.yml
- hosts: all become: yes tasks: - name: create the 'ubuntu' user user: name=ubuntu append=yes state=present createhome=yes shell=/bin/bash - name: allow 'ubuntu' to have passwordless sudo lineinfile: dest: /etc/sudoers line: 'ubuntu ALL=(ALL) NOPASSWD: ALL' validate: 'visudo -cf %s' - name: set up authorized keys for the ubuntu user authorized_key: user=ubuntu key="{{item}}" with_file: - ~/.ssh/id_rsa.pub
以下是该剧本的功能细分:
- 创建非 root 用户
ubuntu
。 - 配置
sudoers
文件以允许ubuntu
用户在没有密码提示的情况下运行sudo
命令。 - 将本地计算机中的公钥(通常为
~/.ssh/id_rsa.pub
)添加到远程ubuntu
用户的授权密钥列表中。 这将允许您以ubuntu
用户身份通过 SSH 连接到每个服务器。
添加文本后保存并关闭文件。
接下来,通过本地运行执行 playbook:
ansible-playbook -i hosts ~/kube-cluster/initial.yml
该命令将在两到五分钟内完成。 完成后,您将看到类似于以下内容的输出:
OutputPLAY [all] **** TASK [Gathering Facts] **** ok: [master] ok: [worker1] ok: [worker2] TASK [create the 'ubuntu' user] **** changed: [master] changed: [worker1] changed: [worker2] TASK [allow 'ubuntu' user to have passwordless sudo] **** changed: [master] changed: [worker1] changed: [worker2] TASK [set up authorized keys for the ubuntu user] **** changed: [worker1] => (item=ssh-rsa AAAAB3...) changed: [worker2] => (item=ssh-rsa AAAAB3...) changed: [master] => (item=ssh-rsa AAAAB3...) PLAY RECAP **** master : ok=5 changed=4 unreachable=0 failed=0 worker1 : ok=5 changed=4 unreachable=0 failed=0 worker2 : ok=5 changed=4 unreachable=0 failed=0
现在初步设置已完成,您可以继续安装 Kubernetes 特定的依赖项。
第 3 步 — 安装 Kubernetes 的依赖项
在本节中,您将使用 Ubuntu 的包管理器安装 Kubernetes 所需的操作系统级包。 这些软件包是:
- Docker - 一个容器运行时。 它是运行容器的组件。 Kubernetes 正在积极开发对 rkt 等其他运行时的支持。
kubeadm
- 一个 CLI 工具,它将以标准方式安装和配置集群的各种组件。kubelet
- 在所有节点上运行并处理节点级操作的系统服务/程序。kubectl
- 一个 CLI 工具,用于通过其 API 服务器向集群发出命令。
在工作区中创建一个名为 ~/kube-cluster/kube-dependencies.yml
的文件:
nano ~/kube-cluster/kube-dependencies.yml
将以下播放添加到文件中以将这些软件包安装到您的服务器:
~/kube-cluster/kube-dependencies.yml
- hosts: all become: yes tasks: - name: install Docker apt: name: docker.io state: present update_cache: true - name: install APT Transport HTTPS apt: name: apt-transport-https state: present - name: add Kubernetes apt-key apt_key: url: https://packages.cloud.google.com/apt/doc/apt-key.gpg state: present - name: add Kubernetes' APT repository apt_repository: repo: deb http://apt.kubernetes.io/ kubernetes-xenial main state: present filename: 'kubernetes' - name: install kubelet apt: name: kubelet=1.14.0-00 state: present update_cache: true - name: install kubeadm apt: name: kubeadm=1.14.0-00 state: present - hosts: master become: yes tasks: - name: install kubectl apt: name: kubectl=1.14.0-00 state: present force: yes
playbook 中的第一个 play 执行以下操作:
- 安装 Docker,容器运行时。
- 安装
apt-transport-https
,允许您将外部 HTTPS 源添加到您的 APT 源列表。 - 添加 Kubernetes APT 存储库的 apt-key 用于密钥验证。
- 将 Kubernetes APT 存储库添加到远程服务器的 APT 源列表。
- 安装
kubelet
和kubeadm
。
第二个游戏包含一个在您的主节点上安装 kubectl
的任务。
注意: 虽然 Kubernetes 文档建议您为您的环境使用 Kubernetes 的最新稳定版本,但本教程使用特定版本。 这将确保您可以成功执行这些步骤,因为 Kubernetes 变化很快,并且最新版本可能不适用于本教程。
完成后保存并关闭文件。
接下来,通过本地运行执行 playbook:
ansible-playbook -i hosts ~/kube-cluster/kube-dependencies.yml
完成后,您将看到类似于以下内容的输出:
OutputPLAY [all] **** TASK [Gathering Facts] **** ok: [worker1] ok: [worker2] ok: [master] TASK [install Docker] **** changed: [master] changed: [worker1] changed: [worker2] TASK [install APT Transport HTTPS] ***** ok: [master] ok: [worker1] changed: [worker2] TASK [add Kubernetes apt-key] ***** changed: [master] changed: [worker1] changed: [worker2] TASK [add Kubernetes' APT repository] ***** changed: [master] changed: [worker1] changed: [worker2] TASK [install kubelet] ***** changed: [master] changed: [worker1] changed: [worker2] TASK [install kubeadm] ***** changed: [master] changed: [worker1] changed: [worker2] PLAY [master] ***** TASK [Gathering Facts] ***** ok: [master] TASK [install kubectl] ****** ok: [master] PLAY RECAP **** master : ok=9 changed=5 unreachable=0 failed=0 worker1 : ok=7 changed=5 unreachable=0 failed=0 worker2 : ok=7 changed=5 unreachable=0 failed=0
执行后,Docker、kubeadm
和 kubelet
将安装在所有远程服务器上。 kubectl
不是必需组件,仅用于执行集群命令。 在这种情况下,仅在主节点上安装它是有意义的,因为您将仅从主节点运行 kubectl
命令。 但是请注意,kubectl
命令可以从任何工作节点或任何可以安装和配置为指向集群的机器运行。
现在已安装所有系统依赖项。 让我们设置主节点并初始化集群。
第 4 步 — 设置主节点
在本节中,您将设置主节点。 然而,在创建任何 playbook 之前,有必要了解一些概念,例如 Pods 和 Pod Network Plugins,因为您的集群将包含这两者。
Pod 是运行一个或多个容器的原子单元。 这些容器共享资源,例如文件卷和网络接口。 Pod 是 Kubernetes 中调度的基本单元:一个 pod 中的所有容器都保证在该 pod 被调度的同一节点上运行。
每个 pod 都有自己的 IP 地址,一个节点上的 pod 应该能够使用 pod 的 IP 访问另一个节点上的 pod。 单个节点上的容器可以通过本地接口轻松通信。 然而,pod 之间的通信更加复杂,并且需要一个单独的网络组件,该组件可以透明地将流量从一个节点上的 pod 路由到另一个节点上的 pod。
此功能由 pod 网络插件提供。 对于此集群,您将使用 Flannel,这是一个稳定且高性能的选项。
在本地机器上创建一个名为 master.yml
的 Ansible playbook:
nano ~/kube-cluster/master.yml
在文件中添加以下 play 来初始化集群并安装 Flannel:
~/kube-cluster/master.yml
- hosts: master become: yes tasks: - name: initialize the cluster shell: kubeadm init --pod-network-cidr=10.244.0.0/16 >> cluster_initialized.txt args: chdir: $HOME creates: cluster_initialized.txt - name: create .kube directory become: yes become_user: ubuntu file: path: $HOME/.kube state: directory mode: 0755 - name: copy admin.conf to user's kube config copy: src: /etc/kubernetes/admin.conf dest: /home/ubuntu/.kube/config remote_src: yes owner: ubuntu - name: install Pod network become: yes become_user: ubuntu shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml >> pod_network_setup.txt args: chdir: $HOME creates: pod_network_setup.txt
以下是这部剧的详细介绍:
- 第一个任务通过运行
kubeadm init
来初始化集群。 传递参数--pod-network-cidr=10.244.0.0/16
指定将分配 pod IP 的私有子网。 Flannel 默认使用上述子网; 我们告诉kubeadm
使用相同的子网。 - 第二个任务在
/home/ubuntu
处创建一个.kube
目录。 此目录将保存配置信息,例如连接到集群所需的管理密钥文件和集群的 API 地址。 - 第三个任务将从
kubeadm init
生成的/etc/kubernetes/admin.conf
文件复制到非 root 用户的主目录。 这将允许您使用kubectl
访问新创建的集群。 - 最后一个任务运行
kubectl apply
来安装Flannel
。kubectl apply -f descriptor.[yml|json]
是告诉kubectl
创建descriptor.[yml|json]
文件中描述的对象的语法。kube-flannel.yml
文件包含在集群中设置Flannel
所需的对象的描述。
完成后保存并关闭文件。
通过运行在本地执行 playbook:
ansible-playbook -i hosts ~/kube-cluster/master.yml
完成后,您将看到类似于以下内容的输出:
Output PLAY [master] **** TASK [Gathering Facts] **** ok: [master] TASK [initialize the cluster] **** changed: [master] TASK [create .kube directory] **** changed: [master] TASK [copy admin.conf to user's kube config] ***** changed: [master] TASK [install Pod network] ***** changed: [master] PLAY RECAP **** master : ok=5 changed=4 unreachable=0 failed=0
要检查主节点的状态,请使用以下命令 SSH 进入它:
ssh ubuntu@master_ip
进入主节点后,执行:
kubectl get nodes
您现在将看到以下输出:
OutputNAME STATUS ROLES AGE VERSION master Ready master 1d v1.14.0
输出表明 master
节点已完成所有初始化任务并处于 Ready
状态,从该状态它可以开始接受工作节点并执行发送到 API 服务器的任务。 您现在可以从本地计算机添加工作人员。
第 5 步 — 设置工作节点
将工作人员添加到集群涉及在每个工作人员上执行单个命令。 该命令包括必要的集群信息,例如主 API 服务器的 IP 地址和端口,以及安全令牌。 只有传入安全令牌的节点才能加入集群。
导航回您的工作区并创建一个名为 workers.yml
的剧本:
nano ~/kube-cluster/workers.yml
将以下文本添加到文件中以将工作人员添加到集群:
~/kube-cluster/workers.yml
- hosts: master become: yes gather_facts: false tasks: - name: get join command shell: kubeadm token create --print-join-command register: join_command_raw - name: set join command set_fact: join_command: "{{ join_command_raw.stdout_lines[0] }}" - hosts: workers become: yes tasks: - name: join cluster shell: "{{ hostvars['master'].join_command }} >> node_joined.txt" args: chdir: $HOME creates: node_joined.txt
这是剧本的作用:
- 第一次播放获取需要在工作节点上运行的连接命令。 该命令将采用以下格式:
kubeadm join --token <token> <master-ip>:<master-port> --discovery-token-ca-cert-hash sha256:<hash>
。 一旦它获得具有正确 token 和 hash 值的实际命令,该任务将其设置为事实,以便下一次播放将能够访问该信息。 - 第二个游戏有一个任务,在所有工作节点上运行 join 命令。 完成此任务后,两个工作节点将成为集群的一部分。
完成后保存并关闭文件。
通过本地运行执行剧本:
ansible-playbook -i hosts ~/kube-cluster/workers.yml
完成后,您将看到类似于以下内容的输出:
OutputPLAY [master] **** TASK [get join command] **** changed: [master] TASK [set join command] ***** ok: [master] PLAY [workers] ***** TASK [Gathering Facts] ***** ok: [worker1] ok: [worker2] TASK [join cluster] ***** changed: [worker1] changed: [worker2] PLAY RECAP ***** master : ok=2 changed=1 unreachable=0 failed=0 worker1 : ok=2 changed=1 unreachable=0 failed=0 worker2 : ok=2 changed=1 unreachable=0 failed=0
随着工作节点的添加,您的集群现在已完全设置并正常运行,工作人员已准备好运行工作负载。 在调度应用程序之前,让我们验证集群是否按预期工作。
第 6 步 — 验证集群
集群有时会在设置过程中失败,因为节点已关闭或主节点和工作节点之间的网络连接无法正常工作。 让我们验证集群并确保节点正常运行。
您将需要从主节点检查集群的当前状态,以确保节点已准备就绪。 如果您从主节点断开连接,您可以使用以下命令通过 SSH 重新连接到该节点:
ssh ubuntu@master_ip
然后执行以下命令获取集群的状态:
kubectl get nodes
您将看到类似于以下内容的输出:
OutputNAME STATUS ROLES AGE VERSION master Ready master 1d v1.14.0 worker1 Ready <none> 1d v1.14.0 worker2 Ready <none> 1d v1.14.0
如果所有节点的 STATUS
的值都为 Ready
,这意味着它们是集群的一部分并准备好运行工作负载。
但是,如果一些节点将 NotReady
作为 STATUS
,则可能意味着工作节点尚未完成设置。 等待大约五到十分钟,然后重新运行 kubectl get nodes
并检查新输出。 如果少数节点仍然有 NotReady
作为状态,您可能需要验证并重新运行前面步骤中的命令。
现在您的集群已成功验证,让我们在集群上安排一个示例 Nginx 应用程序。
第 7 步 — 在集群上运行应用程序
您现在可以将任何容器化应用程序部署到您的集群。 为了保持熟悉,让我们使用 Deployments 和 Services 部署 Nginx,看看如何将这个应用程序部署到集群中。 您也可以将以下命令用于其他容器化应用程序,只要您更改 Docker 映像名称和任何相关标志(例如 ports
和 volumes
)。
仍然在主节点中,执行以下命令以创建名为 nginx
的部署:
kubectl create deployment nginx --image=nginx
部署是一种 Kubernetes 对象,可确保始终基于定义的模板运行指定数量的 pod,即使 pod 在集群的生命周期内崩溃。 上述部署将从 Docker 注册表的 Nginx Docker Image 创建一个带有一个容器的 pod。
接下来,运行以下命令来创建一个名为 nginx
的服务,该服务将公开该应用程序。 它将通过 NodePort 来实现,该方案将使 pod 可以通过在集群的每个节点上打开的任意端口访问:
kubectl expose deploy nginx --port 80 --target-port 80 --type NodePort
服务是另一种类型的 Kubernetes 对象,它向内部和外部客户端公开集群内部服务。 它们还能够对多个 pod 的请求进行负载平衡,并且是 Kubernetes 中不可或缺的组件,经常与其他组件交互。
运行以下命令:
kubectl get services
这将输出类似于以下内容的文本:
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d nginx NodePort 10.109.228.209 <none> 80:nginx_port/TCP 40m
从上述输出的第三行,您可以检索 Nginx 正在运行的端口。 Kubernetes 会自动分配一个大于 30000
的随机端口,同时确保该端口尚未被其他服务绑定。
要测试一切是否正常,请通过本地计算机上的浏览器访问 http://worker_1_ip:nginx_port
或 http://worker_2_ip:nginx_port
。 你会看到 Nginx 熟悉的欢迎页面。
如果你想删除 Nginx 应用程序,首先从主节点中删除 nginx
服务:
kubectl delete service nginx
运行以下命令以确保服务已被删除:
kubectl get services
您将看到以下输出:
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
然后删除部署:
kubectl delete deployment nginx
运行以下命令以确认这是否有效:
kubectl get deployments
OutputNo resources found.
结论
在本指南中,您已使用 Kubeadm 和 Ansible 在 Ubuntu 18.04 上成功设置 Kubernetes 集群以实现自动化。
如果您想知道在集群设置后如何处理它,那么下一步最好将您自己的应用程序和服务部署到集群上。 以下是链接列表,其中包含可在此过程中为您提供指导的更多信息:
- Dockerizing applications - 列出了详细说明如何使用 Docker 容器化应用程序的示例。
- Pod 概述 - 详细描述 Pod 的工作原理以及它们与其他 Kubernetes 对象的关系。 Pod 在 Kubernetes 中无处不在,因此了解它们将有助于您的工作。
- 部署概述 - 提供部署概述。 了解诸如部署之类的控制器如何工作很有用,因为它们经常在无状态应用程序中用于扩展和自动修复不健康的应用程序。
- Services Overview - 涵盖了服务,另一个在 Kubernetes 集群中经常使用的对象。 了解服务的类型和它们拥有的选项对于运行无状态和有状态应用程序都是必不可少的。
您可以研究的其他重要概念是 Volumes、Ingresses 和 Secrets,所有这些在部署生产应用程序时都会派上用场。
Kubernetes 提供了很多功能和特性。 Kubernetes 官方文档 是了解概念、查找特定任务指南以及查找各种对象的 API 参考的最佳场所。