用AWS EC2从零搭建Kubernetes和ArgoCD

用AWS EC2从零搭建Kubernetes和ArgoCD

在AWS上实现 Kubernetes 集群最简单的方法是走 EKS(Elastic Kubernetes Service)托管服务(managed service)。但后来发现 EKS 成本高昂,会有一笔跟 EC2 计算费用无关的起步价,仅仅来源于 EKS。为了学习(省钱),我们来用裸机 EC2 实例搭建 Kubernetes 集群,以及集成 ArgoCD 实现 CD (Continuous Deployment)。

准备EC2实例 #

通过AWS控制台或者AWS CLI创建EC2实例。一般Production环境配置至少两个节点,一个作为Master节点,另一个作为Worker节点。实例最低需求:

节点类型 vCPU 内存 磁盘容量 用途
Master 2 4GB 20GB 用于 Kubernetes control-plane
Worker 1 2GB 20GB 用于运行应用程序

也可以单节点部署,即Master和Worker放在一个EC2上。本教程会使用单节点部署,节约成本,用于学习。

使用AWS AMI创建EC2实例,选择 Amazon Linux xxx AMI。

选择Amazon Linux AMI

创建key pair,用于远程SSH登录EC2实例。创建后会下载一个私钥文件,一般存入~/.ssh目录下,并修改权限为只允许拥有者读取。

1chmod 400 your-private-key.pem

创建好后,需要在裸机上安装各种依赖。首先更新 dnf 包管理器。因为用的是 Amazon Linux 2 AMI,所以用的是 dnf 包管理器,而不是 yum。

1sudo dnf update -y

准备 containerd #

安装 #

1sudo dnf install -y containerd

启动 #

1sudo systemctl daemon-reload
2sudo systemctl enable --now containerd

创建默认配置文件 #

1sudo containerd config default | sudo tee /etc/containerd/config.toml

编辑 /etc/containerd/config.toml 文件,将 systemd_cgroup = false 修改为 systemd_cgroup = true,以使 containerd 使用 systemd cgroup 驱动。

1sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

重启 containerd 服务并查看状态

1sudo systemctl restart containerd  
2sudo systemctl status containerd

准备 CNI #

CNI (Container Network Interface) 插件为 Kubernetes 提供网络功能。它主要负责:

  1. Pod 分配 IP 地址。
  2. 设置容器之间的网络通信。
  3. 确保 Pod 可以与其他 Pod、服务(Service)以及外部世界通信。

如果没有正确安装和配置 CNI 插件,KubernetesPod 网络将无法正常工作,导致 Pod 无法互通或无法分配 IP 地址。先查看是否已经安装了 CNI 插件

1ls /opt/cni/bin

如果以上命令没有输出如下信息,说明没有安装 CNI 插件

ls: cannot access '/opt/cni/bin': No such file or directory

通过以下命令安装

1CNI_VERSION=1.4.0
2sudo wget -P /usr/local/src https://github.com/containernetworking/plugins/releases/download/v${CNI_VERSION}/cni-plugins-linux-amd64-v${CNI_VERSION}.tgz
3sudo mkdir -p /opt/cni/bin
4sudo tar -C /opt/cni/bin -xf /usr/local/src/cni-plugins-linux-amd64-v${CNI_VERSION}.tgz

再使用 ls /opt/cni/bin 命令查看,应当看到如下输出

bandwidth  bridge  dhcp  dummy  firewall  flannel  host-device  host-local
ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tap  tuning  vlan  vrf

其他必要配置 #

启动两个内核模块 #

1sudo modprobe overlay  
2sudo modprobe br_netfilter

overlaybr_netfilterKubernetes 集群所必需的两个内核模块,需要手动加载。

通过编辑 /etc/modules-load.d/k8s.conf 文件,使得这两个模块在系统启动时自动加载

1cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
2overlay
3br_netfilter
4EOF

检查是否加载成功

1lsmod | grep -e overlay -e br_netfilter

修改 sysctl 配置 #

1cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
2net.bridge.bridge-nf-call-ip6tables = 1
3net.bridge.bridge-nf-call-iptables = 1
4net.ipv4.ip_forward = 1
5EOF

这个操作使得 iptables 能够正确工作。

刷新 sysctl 配置以使其生效

1sudo sysctl --system

禁用 swap #

1sudo swapon -s # 查看 swap 分区
2sudo swapoff -a

原因是 Kubernetes 不支持 swap 分区,因为 swap 分区会导致 Pod 的内存限制无效。

安装 Kubernetes #

安装 curl #

1sudo dnf install -y curl

已有了 curl 的话则跳过这一步。

添加 Kubernetes 仓库地址 #

以下命令添加了 Kubernetes 仓库的地址到 /etc/yum.repos.d/kubernetes.repo 文件中

1cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
2[kubernetes]
3name=Kubernetes
4baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/
5enabled=1
6gpgcheck=1
7gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key
8exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
9EOF

更新包管理器的缓存让其识别新的仓库

1sudo dnf makecache

安装 #

安装 kubeadm, kubeletkubectl 并启动 kubelet 服务

1sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
2sudo systemctl enable --now kubelet

kubelet 服务是 Kubernetes 的主要组件之一,它负责管理 Pod 的生命周期,包括创建、销毁、监控 Pod 等。

kubeadm 是 Kubernetes 的初始化工具,它可以帮助我们快速初始化一个 Kubernetes 集群。

kubectl 是 Kubernetes 的命令行工具,用于与 Kubernetes 集群交互。

安装 tc (Traffic Control) 包,以便 Kubernetes 集群能够正常工作,首先查看包含 tc 命令的软件包

1dnf provides tc

一般都存在于 iproute-tc 软件包中,因此安装 iproute-tc 软件包

1sudo dnf install -y iproute-tc

启动 #

通过 kubeadm 初始化 Kubernetes 集群

1sudo kubeadm init --pod-network-cidr=10.244.0.0/16

一定要用 sudo 执行 kubeadm init 命令,因为这个命令会修改系统的配置文件,否则会报错。

这里 CIDR 使用 10.244.0.0/16,因为稍后配置的网络插件 Flannel 默认使用这个 CIDR。

初始化完成后,会输出类似如下的信息,其中有两个命令,一个是 kubeadm join 命令,另一个是 kubectl apply 命令,分别用于加入节点和安装网络插件。

Your Kubernetes control-plane has initialized successfully!

检查 kubelet 服务状态 #

1systemctl status kubelet

应得到如下输出,kubelet 服务应该是 Active: active (running) 状态

● kubelet.service - Kubernetes Kubelet
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2024-11-22 09:00:00 UTC; 1min 30s ago
     Docs: https://kubernetes.io/docs/
 Main PID: 12345 (kubelet)
    Tasks: 123
   Memory: 123.4M
   CGroup: /system.slice/kubelet.service
           └─12345 /usr/bin/kubelet --config=/var/lib/kubelet/config.yaml --kubeconfig=/var/lib/kubelet/kubeconfig --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.5.1 --resolv-conf=/etc/resolv.conf

kubectl 配置文件拷贝到当前用户的家目录下

1mkdir -p $HOME/.kube
2sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
3sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装 CNI 插件 #

安装CNI网络插件,这里使用 Flannel。Flannel 是一种简单的 Kubernetes 网络解决方案。它会为每个 Pod 分配一个唯一的 IP 地址,并确保不同节点之间的 Pod 能通过虚拟网络通信。

部署 Flannel #

1kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

检查服务状态 #

1kubectl get pods -n kube-flannel

应得到如下输出,flannel的Pod应该是 Running 状态

NAME                    READY   STATUS    RESTARTS       AGE
kube-flannel-ds-n2nzv   1/1     Running   20 (52s ago)   67m

以及用以下命令查看 Flannel DaemonSet

1kubectl get daemonset kube-flannel-ds -n kube-flannel

应得到如下输出,AVAILABLE 列应该是 1

NAME             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-flannel-ds  1         1         1       1            1           <none>                   67m

查看系统相关的 Pod

1kubectl get pods -n kube-system

应得到如下输出,所有的 Pod 都应该是 Running 状态,如果有 ContainerCreating 状态,等一下再查看。

NAME                                                                      READY   STATUS    RESTARTS   AGE
coredns-668d6bf9bc-xls74                                                  1/1     Running   0          70m
coredns-668d6bf9bc-zghmb                                                  1/1     Running   0          70m
etcd-ip-123-12-12-12.ap-northeast-1.compute.internal                      1/1     Running   0          70m
kube-apiserver-ip-123-12-12-12.ap-northeast-1.compute.internal            1/1     Running   0          70m
kube-controller-manager-ip-123-12-12-12.ap-northeast-1.compute.internal   1/1     Running   0          70m
kube-proxy-f2nnh                                                          1/1     Running   0          70m
kube-scheduler-ip-123-12-12-12.ap-northeast-1.compute.internal            1/1     Running   0          70m

查看节点状态

1kubectl get nodes

应得到如下输出

NAME                                              STATUS   ROLES           AGE   VERSION
ip-123-12-12-12.ap-northeast-1.compute.internal   Ready    control-plane   72m   v1.28.0

安装 Metrics Server(可选) #

Metrics ServerKubernetes 的一个聚合器,用于收集集群中的资源使用情况。有了 Metrics Server,就可以使用 kubectl top 命令查看集群实时的资源使用情况。以下先部署:

1kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

编辑 Metrics ServerDeployment,添加 --kubelet-insecure-tls 参数

1kubectl edit deployment metrics-server -n kube-system

找到 containers 下的 args 字段,添加 --kubelet-insecure-tls 参数,如下,保存退出后,Deployment 会自动更新。

1containers:
2  - args:
3      - --kubelet-insecure-tls

这个操作的原因是,Metrics Server 默认使用自签名证书,而 Kubernetes API Server 可能拒绝与其通信。

检查 Metrics Server 是否正常运行

1kubectl get pods -n kube-system | grep metrics-server

应得到如下输出,metrics-serverPod 应该是 Running 状态

metrics-server-6d4c8c5f99-7z5zv   1/1     Running   0          3m

于是可以用 kubectl top 命令查看集群实时的资源使用情况

1kubectl top nodes
2kubectl top pods -n <namespace>

集成 ArgoCD #

ArgoCD 是一个用于 GitOps 部署的工具,它可以帮助我们将应用程序的配置文件存储在 Git 仓库中,并通过 ArgoCD 自动同步到 Kubernetes 集群中。

创建命名空间 #

1kubectl create namespace argocd

安装 ArgoCD #

1kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

如果是单节点集群,那么 control-plane 节点也需要运行普通 Pod,即 argocd 相关 Pod。然而 control-plane 节点一般带有 node-role.kubernetes.io/control-plane taint,这会导致普通 Pod 无法调度到 control-plane 节点上。 这里提供用于测试的快速解决方案,即移除 control-plane 节点上的 taint,使其可以运行普通 Pod。

1kubectl taint nodes --all node-role.kubernetes.io/control-plane-

查看相关 Pod 状态 #

1kubectl get pods -n argocd

所有的 Pod 都应该是 Running 状态,同样,有 Pod 处于 ContainerCreatingInit 状态,等一下再查看。

NAME                                                READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                     1/1     Running   0          6m14s
argocd-applicationset-controller-84c66c76b4-zf6bd   1/1     Running   0          6m15s
argocd-dex-server-97fdcf666-4pbwc                   1/1     Running   0          6m15s
argocd-notifications-controller-7c44dd7757-2twjj    1/1     Running   0          6m15s
argocd-redis-596f8956cc-wf6md                       1/1     Running   0          6m15s
argocd-repo-server-577664cf68-ngbcz                 1/1     Running   0          6m15s
argocd-server-6b9b64c5fb-qtk6b                      1/1     Running   0          6m15s

暴露 ArgoCD Server #

1kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'

查看 ArgoCD ServerNodePort 端口

1kubectl get svc argocd-server -n argocd

应得到如下输出

NAME            TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
argocd-server   NodePort   10.97.82.16   <none>        80:31027/TCP,443:32130/TCP   14m

其中 80:31027/TCP 是 HTTP 端口,443:32130/TCP 是 HTTPS 端口。可以通过 http://<control-plane-ip>:31027 访问 ArgoCD UI。

获取 control-plane 节点的公网 IP 地址,然后访问 http://<control-plane-ip>:31027。对于 AWS EC2 实例,可以在 AWS 控制台中查看。

ArgoCD 登陆界面

获取 ArgoCD Server 初始密码 #

1kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d

用户名为 admin,密码为上面的命令输出。建议在登陆后立即修改管理员密码。

安装 ArgoCD CLI #

后续为了与 CI 集成实现自动化部署,ArgoCD CLI (命令行工具)是必不可少的。

下载 #

1curl -sSL -o argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64

赋予执行权限 #

1chmod +x argocd

移动可执行文件 #

argocd 移动到 /usr/local/bin 目录下

1sudo mv ./argocd /usr/local/bin

验证安装 #

1argocd version

Kubernetes 以及 ArgoCD 集成完成。接下去就是通过 ArgoCD 部署应用程序了。

配置 #

1argocd login <ARGOCD_SERVER> --username admin --password <YOUR_PASSWORD> --insecure

ARGOCD_SERVERArgoCD 服务器的地址,YOUR_PASSWORDArgoCD 管理员的密码。

--insecure 参数是因为 ArgoCD 默认使用自签名证书,而 ArgoCD CLI 可能拒绝与其通信。

验证连接 #

1argocd app list

到这里,KubernetesArgoCD 的部署和集成完成,可以开始部署应用程序了。

由于 ArgoCD 其实只负责 CD部分(显而易见),应用的打包构建还是没有被自动化。尤其对于预览和测试环境来说,我们通常希望拥有实时的构建和部署。因此我们还是需要搭建 CI 环境。

请参见下一篇:用AWS EC2从零搭建Jenkins