Linux

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > Linux > Linux部署Kubernetes集群

Linux环境下部署Kubernetes集群的全过程

作者:知远漫谈

在当今云原生时代,Kubernetes 已成为容器编排的事实标准,掌握 Kubernetes 的部署与运维能力都已成为必备技能,本文将带你从零开始,在 Linux 环境中完整部署一个高可用的 Kubernetes 集群,需要的朋友可以参考下

在当今云原生时代,Kubernetes 已成为容器编排的事实标准。无论你是 DevOps 工程师、后端开发者,还是系统架构师,掌握 Kubernetes 的部署与运维能力都已成为必备技能。本文将带你从零开始,在 Linux 环境中完整部署一个高可用的 Kubernetes 集群,并通过 Java 应用演示其部署、伸缩与服务发现能力。

提示:本文内容基于 Ubuntu 22.04 LTS 操作系统,但大部分命令适用于主流 Linux 发行版如 CentOS、Debian、RHEL 等。

为什么选择 Kubernetes?

在深入部署之前,我们先理解一下为什么 Kubernetes 如此重要:

基础环境准备

系统要求

我们将部署一个包含 1 个 Master 节点 + 2 个 Worker 节点的小型集群。建议每台机器满足以下最低配置:

角色CPU 核心数内存存储空间
Master24GB20GB
Worker24GB20GB

操作系统推荐使用 Ubuntu 22.04 LTSCentOS Stream 9

关闭防火墙与 SELinux(可选)

# Ubuntu / Debian
sudo ufw disable
# CentOS / RHEL
sudo systemctl stop firewalld
sudo systemctl disable firewalld
# 关闭 SELinux(仅限 CentOS/RHEL)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

同步时间(重要!)

Kubernetes 对时间同步非常敏感,推荐使用 chronyntp

sudo apt update && sudo apt install chrony -y
sudo systemctl enable chronyd && sudo systemctl start chronyd
chronyc sources -v

安装 Docker 或 containerd

Kubernetes 1.24+ 不再默认支持 Docker,推荐使用 containerd 作为 CRI 运行时。

安装 containerd:

# 更新包索引
sudo apt update

# 安装依赖
sudo apt install -y ca-certificates curl gnupg lsb-release

# 添加 Docker GPG 密钥(用于 containerd)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 添加源
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 containerd
sudo apt update && sudo apt install -y containerd.io

# 配置 containerd 使用 systemd cgroup
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

# 重启并启用
sudo systemctl restart containerd
sudo systemctl enable containerd

安装 kubeadm、kubelet 和 kubectl

这是部署 Kubernetes 集群的核心工具链:

# 添加 Kubernetes APT 源
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

# 安装指定版本(推荐 v1.28.x)
sudo apt update
sudo apt install -y kubelet=1.28.0-00 kubeadm=1.28.0-00 kubectl=1.28.0-00

# 锁定版本防止自动升级
sudo apt-mark hold kubelet kubeadm kubectl

小贴士:生产环境中建议锁定版本,避免因自动升级导致兼容性问题。

配置网络与主机名

确保各节点之间可以通过主机名互相解析:

# 设置主机名(分别在 master、worker1、worker2 上执行)
sudo hostnamectl set-hostname k8s-master
sudo hostnamectl set-hostname k8s-worker1
sudo hostnamectl set-hostname k8s-worker2

# 编辑 /etc/hosts,添加如下内容(IP 替换为实际值)
192.168.1.100 k8s-master
192.168.1.101 k8s-worker1
192.168.1.102 k8s-worker2

初始化 Master 节点

现在我们正式初始化 Kubernetes 控制平面:

sudo kubeadm init \
  --pod-network-cidr=10.244.0.0/16 \
  --apiserver-advertise-address=192.168.1.100 \
  --control-plane-endpoint=k8s-master:6443 \
  --upload-certs

参数说明:

配置 kubectl

初始化成功后,按提示配置当前用户使用 kubectl:

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

验证是否成功:

kubectl get nodes
# 此时应看到 master 节点状态为 NotReady(因为还未安装网络插件)

安装 Pod 网络插件 —— Flannel

Kubernetes 需要 CNI 插件来实现 Pod 间通信。我们选择轻量级的 Flannel

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

等待几分钟,再次检查节点状态:

kubectl get nodes
# STATUS 应变为 Ready

加入 Worker 节点

回到 Master 初始化输出的最后一段,复制 kubeadm join 命令,在每个 Worker 节点上执行:

sudo kubeadm join k8s-master:6443 \
  --token <your-token> \
  --discovery-token-ca-cert-hash sha256:<your-hash>

如果你丢失了 join 命令,可以在 Master 上重新生成:

kubeadm token create --print-join-command

等待几分钟,再次在 Master 上执行:

kubectl get nodes
# 应看到所有节点状态为 Ready

集群拓扑结构图

下面是一个典型的三节点 Kubernetes 集群架构图:

该图展示了客户端请求如何通过负载均衡器进入 API Server,再由调度器分发到各个工作节点上的 Pod 中。每个 Pod 可以运行不同的服务,例如 Java 应用、数据库、缓存等。

验证集群功能

创建测试命名空间

kubectl create namespace test-app
kubectl config set-context --current --namespace=test-app

部署第一个 Pod —— nginx

kubectl create deployment nginx --image=nginx:latest
kubectl expose deployment nginx --port=80 --type=NodePort
kubectl get svc nginx

访问任一节点 IP + NodePort 即可看到欢迎页。

编写 Java Spring Boot 应用

下面我们编写一个简单的 Java 应用,用于部署到 Kubernetes 集群中。

Maven 项目结构

src/
├── main/
│   ├── java/
│   │   └── com/example/demo/
│   │       ├── DemoApplication.java
│   │       └── HelloController.java
│   └── resources/
│       └── application.properties
pom.xml
Dockerfile

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>k8s-demo-app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/>
    </parent>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

DemoApplication.java

package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

HelloController.java

package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.net.InetAddress;
import java.net.UnknownHostException;
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() throws UnknownHostException {
        String hostname = InetAddress.getLocalHost().getHostName();
        return "Hello from Java Spring Boot! Host: " + hostname + " 🎉";
    }
    @GetMapping("/health")
    public String health() {
        return "UP";
    }
}

application.properties

server.port=8080
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

Dockerfile

FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/k8s-demo-app-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

构建并推送镜像

在项目根目录执行:

mvn clean package
docker build -t your-dockerhub-username/k8s-java-app:v1 .
docker login
docker push your-dockerhub-username/k8s-java-app:v1

替换 your-dockerhub-username 为你自己的 Docker Hub 用户名。

部署 Java 应用到 Kubernetes

创建 Deployment

# java-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app
  namespace: test-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: java-app
  template:
    metadata:
      labels:
        app: java-app
    spec:
      containers:
      - name: java-app
        image: your-dockerhub-username/k8s-java-app:v1
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

部署:

kubectl apply -f java-app-deployment.yaml

创建 Service

# java-app-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: java-app-service
  namespace: test-app
spec:
  selector:
    app: java-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: NodePort

部署:

kubectl apply -f java-app-service.yaml
kubectl get svc java-app-service -n test-app

访问 http://<NODE_IP>:<NODE_PORT>/hello,你会看到类似:

Hello from Java Spring Boot! Host: java-app-5d7c8f9b4d-xyz12 🎉

刷新页面,你会发现 Host 名称变化,说明请求被负载均衡到了不同 Pod!

自动扩缩容演示

Kubernetes 支持根据 CPU 使用率自动扩缩容:

# 创建 HPA(Horizontal Pod Autoscaler)
kubectl autoscale deployment java-app --cpu-percent=50 --min=2 --max=10 -n test-app

# 查看 HPA 状态
kubectl get hpa -n test-app

# 模拟压力测试(新开终端)
kubectl run load-test --image=busybox --rm -it --restart=Never -- sh -c "while true; do wget -q -O- http://java-app-service/hello; echo; sleep 0.5; done"

观察 HPA 是否触发扩容:

watch kubectl get pods -n test-app

当 CPU 超过阈值,副本数会自动增加;压力解除后,又会自动缩减。

服务发现与 ConfigMap

Kubernetes 内部服务可通过 DNS 自动发现。例如,我们的 Java 应用想连接 Redis,只需知道 Service 名称即可:

// 在 Java 代码中
String redisHost = System.getenv().getOrDefault("REDIS_HOST", "redis-service.test-app.svc.cluster.local");
int redisPort = Integer.parseInt(System.getenv().getOrDefault("REDIS_PORT", "6379"));

同时,我们可以使用 ConfigMap 注入配置:

# app-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: test-app
data:
  APP_NAME: "MyK8sApp"
  LOG_LEVEL: "INFO"
  REDIS_HOST: "redis-service"
  REDIS_PORT: "6379"

在 Deployment 中引用:

envFrom:
- configMapRef:
    name: app-config

这样,无需修改代码或重建镜像,即可动态调整配置!

使用 Secret 管理敏感信息

比如数据库密码:

kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=SuperSecret123! \
  -n test-app

在 Deployment 中挂载:

env:
- name: DB_USERNAME
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: username
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password

Secret 数据在 etcd 中是 Base64 编码存储,建议生产环境启用加密或使用外部密钥管理服务(如 HashiCorp Vault)。

监控与日志收集

虽然不是本文重点,但生产环境不可或缺:

高可用架构进阶(简述)

上述部署为单 Master,生产环境建议:

  1. 多 Master + 负载均衡器:使用 HAProxy 或云厂商 LB。
  2. 外部 etcd 集群:独立部署 etcd,提高稳定性。
  3. 存储持久化:使用 CSI 插件对接云存储或 Ceph。
  4. Ingress Controller:如 Nginx Ingress、Traefik,统一管理外部访问。

故障模拟与恢复

手动删除一个 Pod

kubectl delete pod <pod-name> -n test-app

观察 Deployment 是否自动重建新 Pod:

kubectl get pods -w -n test-app

Kubernetes 会确保副本数始终符合期望值。

模拟节点宕机

关闭一台 Worker 节点:

sudo shutdown now

在 Master 上查看:

kubectl get nodes
# 该节点状态变为 NotReady
kubectl get pods -o wide
# 原本运行在该节点上的 Pod 会被标记为 Unknown 或 NodeLost

等待约 5 分钟(默认驱逐超时),Pod 会自动迁移到健康节点!

常用调试命令汇总

# 查看所有资源
kubectl get all -A

# 查看 Pod 日志
kubectl logs <pod-name> -n test-app

# 进入 Pod 内部
kubectl exec -it <pod-name> -n test-app -- sh

# 查看事件(排错神器)
kubectl get events -n test-app --sort-by='.lastTimestamp'

# 查看节点资源使用
kubectl top nodes
kubectl top pods -n test-app

# 描述资源详情
kubectl describe pod <pod-name> -n test-app

常见问题与解决方案

Pod 一直处于 Pending 状态

可能原因:

解决方法:

kubectl describe pod <pod-name>
# 查看 Events 部分

ImagePullBackOff

镜像拉取失败:

kubectl get events --field-selector reason=Failed

CrashLoopBackOff

容器反复崩溃:

Java 开发者专属技巧

在 Java 应用中读取 Pod 信息

Kubernetes 会将 Pod 信息通过 Downward API 注入环境变量或文件:

env:
- name: MY_POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name
- name: MY_POD_NAMESPACE
  valueFrom:
    fieldRef:
      fieldPath: metadata.namespace
- name: MY_NODE_NAME
  valueFrom:
    fieldRef:
      fieldPath: spec.nodeName

Java 代码中读取:

String podName = System.getenv("MY_POD_NAME");
String namespace = System.getenv("MY_POD_NAMESPACE");
System.out.println("Running in Pod: " + podName + ", Namespace: " + namespace);

使用 Actuator 暴露健康检查端点

前面我们已集成 spring-boot-starter-actuator,它提供:

配合 Prometheus 可采集 JVM 指标:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

访问 http://<service>/actuator/prometheus 即可获取指标数据。

使用 Helm 简化部署(进阶)

Helm 是 Kubernetes 的包管理器,类似 Linux 的 apt/yum。

安装 Helm:

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

添加仓库 & 部署 Java 应用 Chart:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-java-app bitnami/tomcat --set service.type=NodePort

集成 CI/CD 流水线

推荐使用 GitLab CI 或 GitHub Actions 自动构建镜像并部署:

# .gitlab-ci.yml 示例
stages:
  - build
  - deploy
build_image:
  stage: build
  script:
    - mvn package
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
deploy_to_k8s:
  stage: deploy
  script:
    - kubectl set image deployment/java-app java-app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -n test-app
  only:
    - main

总结

至此,你已经完成了一个完整的 Kubernetes 集群部署,并成功运行了 Java 应用。回顾整个过程:

✅ 准备 Linux 环境
✅ 安装 containerd + kubeadm 工具链
✅ 初始化 Master 并加入 Worker 节点
✅ 部署 CNI 网络插件
✅ 编写、构建、推送 Java 镜像
✅ 通过 Deployment + Service 部署应用
✅ 配置自动扩缩容、健康检查、服务发现
✅ 学习常用调试与排错技巧

Kubernetes 的世界远不止于此,但你已经打下了坚实的基础。下一步可以探索:

记住:Kubernetes 的学习曲线陡峭,但回报巨大。坚持实践,你将成为云原生时代的架构大师!

最后的话

希望这篇长文能帮助你在 Linux 上成功部署 Kubernetes,并愉快地运行 Java 应用。技术世界日新月异,但基础永远是王道。动手实践、不断试错、持续学习 —— 这才是工程师的成长之道。

以上就是Linux环境下部署Kubernetes集群的全过程的详细内容,更多关于Linux部署Kubernetes集群的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文