如何用docker和k8s部署springboot应用快速体验

436 阅读2分钟

前言

现代部署应用很多都是使用docker,因为docker的易用性和快速部署,资源的消耗等优势。现在很少用裸机直接部署。然后k8s是更进一步,是一个容器编排管理工具。由于实际生产部署k8s和运维比较的复杂。本篇目的是为了新手能快速体验k8s部署应用。避免安装虚拟机,服务器等前置环境问题。故在windows系统下进行。

本篇相关术语和概念

docker

dockerfile文件:可以理解为是部署应用的一个设计图纸,上面画出了如何构建应用的步骤

镜像:镜像是用来创建容器的基础。你可以将镜像理解为一个静态的文件系统快照,包含了应用程序及其依赖项

容器:根据镜像启动应用后。这个运行的应用/服务称作一个容器。使用 docker run 命令启动一个镜像时,Docker 会创建一个容器,加载镜像中的文件系统,并启动指定的进程

docker部署流程

k8s

docker安装

首先环境基础需要有docker。使用我们的老朋友docker desktop

下载

docs.docker.com/desktop/set…\

然后测试是否可用

配置docker的加速

国外的速度很慢,必须要换源一下

{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "features": {
    "buildkit": true
  },
  "registry-mirrors": [
    "https://docker.1panel.live"
  ]
}

springboot应用

先准备一个springboot应用,如果没有可以用idea的新建module。快速创建一个springboot应用

勾选web依赖

把相关的端口记下。默认是8080。

编写测试代码

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class HelloController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, World!";
    }
}

编写dockerfile

先运行下package按钮,打包一下。

然后观察target的内容,可以看见默认规则是spring.application.name的应用名加上版本

docker配置文件

"docker镜像构建的图纸",采用分阶段构建,可以加快镜像分发,和镜像层的复用



# 第一阶段:构建并提取分层
FROM openjdk:17-slim AS builder
WORKDIR /java
ARG JAR_FILE=target/demo-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract

# 第二阶段:构建最终镜像
FROM openjdk:17-slim
WORKDIR /java
COPY --from=builder /java/dependencies/ ./
COPY --from=builder /java/spring-boot-loader/ ./
COPY --from=builder /java/snapshot-dependencies/ ./
COPY --from=builder /java/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
EXPOSE 8080

构建镜像

在项目根目录执行构建镜像-t后面的代表构建的镜像名字

docker build -t demo-server  .

运行镜像

根据镜像启动应用。demo-server ,-p映射容器内部8080端口到外部宿主机8082端口。左侧是宿主机,右侧是容器内部

直接启动

docker run -p 8082:8080 demo-server

后台启动

docker run -d -p 8082:8080 demo-server

好了,可以看到正常访问成功了

k8s安装

安装方式一

1.如果用docker desktop可以使用自带的k8s组件快速启动

但是这方式在以前可以,现在dockerhub被墙了。我们需要能访问就比较麻烦。因为需要拉取k8s相关镜像。然后出现一直在拉取的的情况。

解决方式可以参考:

github.com/AliyunConta…

安装方式二

minikube.sigs.k8s.io/docs/start/…

使用minikube安装,按照文档操作即可。支持跨平台。非常适合 Kubernetes 的学习和开发

先装个choco包管理器

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

安装minikube

choco install minikube

启动集群

minikube start

安装方式三

安装kind

choco install kind

创建集群

kind-config.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: worker
containerdConfigPatches:
  - |-
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://registry.qcloud.com"]
kind create cluster --config kind-config.yaml --name k8s1

最后都要验证下看下是否可用

验证集群状态

kubectl cluster-info

查看节点

kubectl get nodes

安装habor

私有镜像仓库,后面要把我们的镜像传上去才能用

下载 Harbor 安装包

wget https://github.com/goharbor/harbor/releases/download/v2.11.2/harbor-online-installer-v2.11.2.tgz

解压安装包

sudo mkdir -p /opt/harbor
sudo tar xvf harbor-online-installer-v2.11.2.tgz -C /opt/harbor
cd /opt/harbor

配置 Harbor

把这个配置文件先改名下去掉tmpl后缀,修改上我们的hostname,port,密码信息

运行安装脚本

./prepare
./install.sh

配置证书(可选)

cd /opt/harbor/harbor/common/config/nginx/

编辑证书的配置。因为是docker里面的这个路径的证书。这点要注意一下

sudo docker-compose down
sudo docker-compose up -d

image.png

登录到私有仓库

换成自己的ip

docker login 192.168.1.100 -u admin -p Admin123

docker login  -u admin -p 123456 47.xx.11.197:80

注意如果是没有自己配证书得,记得docker要配置一下不安全地址,不然无法拉取,差不多是

{ "insecure-registries" : ["47.xxx.11.197:80"] }

构建镜像

docker tag demo-server:latest 47.xx.11.197:80/library/demo-server:latest

上传镜像

把docker镜像加载到k8s的集群中

docker push 47.xx.11.197:80/library/demo-server:latest

然后就可以看见habor有我们传的镜像了

325f7b24b346c76adfa9339a8179705e.png 下面就是拉取这个镜像进行使用

kubectl create secret docker-registry my-harbor-secret \
  --docker-server=47.120.11.197:80 \
  --docker-username=admin \
  --docker-password=123456

编写配置文件

在项目根目录下创建

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-server
  template:
    metadata:
      labels:
        app: demo-server
    spec:
      containers:
      - name: demo-server
        image: 47.xx.11.197:80/library/demo-server:latest
        ports:
        - containerPort: 8080

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: demo-server-service
spec:
  selector:
    app: demo-server
  ports:
    - protocol: TCP
      port: 8082
      targetPort: 8080
  type: LoadBalancer

在Kubernetes社区中,分离Deployment和Service是一种常见的最佳实践。这有助于团队成员之间的一致性和协作,所以写2个文件。

应用配置

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

观察启动情况

可以通过命令kubectl看服务情况

kubectl get deployment
kubectl get pods

获取访问地址和port

kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}"

kubectl  get services demo-server-service -o jsonpath="{.spec.ports[0].nodePort}"

直接访问应用的api是否正常

移除配置

kubectl delete deployment demo-server

移除服务

kubectl delete service demo-server-service

移除所有

kubectl delete pods --all

kubectl delete deployments --all

CI/CD集成

这块是一般更应用的生态走的,通常和代码库gitlab,github等都有自带的ci/cd构建,但是很多都有限制条件。而jenkins是免费的无限制的。此次使用的jenkins来演示这个效果。jenkins在企业目前还是有很多的应用。

安装jenkins

 sudo docker run --name jenkins -d -p 8081:8080 -p 50000:50000 \
 -u 0 -v /home/jenkins_home:/var/jenkins_home -m 1024m \
 --restart=always jenkins/jenkins:lts-jdk11
 
 默认容器内是jenkins账户,root没有操作docker容器内目录的权限,-u 0 可以用外面的root账户
 覆盖容器的账户设置

安装启动后首页有个localhost:8081有配置密码的,账户是jenkins

cat /var/jenkins_home/secrets/initialAdminPassword

 创建 Jenkins 部署文件,大致如下

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      containers:
      - name: jenkins
        image: jenkins/jenkins:lts-jdk11
        ports:
        - containerPort: 8080
        - containerPort: 50000
        volumeMounts:
        - name: jenkins-home
          mountPath: /var/jenkins_home
        resources:
          requests:
            memory: "2Gi"
            cpu: "500m"
          limits:
            memory: "4Gi"
            cpu: "1000m"
        securityContext:
          runAsUser: 0
      volumes:
      - name: jenkins-home
        persistentVolumeClaim:
          claimName: jenkins-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30000
  - port: 50000
    targetPort: 50000
  selector:
    app: jenkins
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

 创建 Jenkinsfile

pipeline {
    agent {
        kubernetes {
            label 'jenkins-slave'
            defaultContainer 'jnlp'
            yaml """
apiVersion: v1
kind: Pod
metadata:
  name: jenkins-slave
spec:
  containers:
  - name: jnlp
    image: jenkins/inbound-agent:4.11-4
    args: ['$(JENKINS_SECRET)', '$(JENKINS_NAME)']
  - name: maven
    image: maven:3.8.1-jdk-11
    command:
    - cat
    tty: true
"""
        }
    }
    stages {
        stage('Clone Repository') {
            steps {
                git branch: 'main', url: 'https://github.com/your-repo/your-project.git'
            }
        }
        stage('Build') {
            steps {
                container('maven') {
                    sh 'mvn clean package'
                }
            }
        }
        stage('Test') {
            steps {
                container('maven') {
                    sh 'mvn test'
                }
            }
        }
        stage('Deploy') {
            steps {
                script {
                    if (env.BRANCH_NAME == 'main') {
                        sh 'kubectl apply -f k8s/deployment.yaml'
                    }
                }
            }
        }
    }
}

最后通过触发Pipeline就可以实现自动化的持续集成和持续交付