容器化技術與微服務結合---實操service並部署一個簡單對外開放的springboot HelloWord服務(四)

系列

容器化技術與微服務結合—docker(一)
容器化技術與微服務結合—Kubernetes基本介紹(二)
容器化技術與微服務結合—Pod詳解(三)
容器化技術與微服務結合—實操service並部署一個簡單對外開放的springboot HelloWord服務(四)
容器化技術與微服務結合—結合springcloud微服務框架進行部署(含切換成阿里雲docker倉庫)(五)
容器化技術與微服務結合—SpringCloud框架與阿里雲serverless k8s的結合(六)

Service理解

內容來自於官網: https://kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/

事實上,Pod(容器組)有自己的 生命週期。當 worker node(節點)故障時,節點上運行的 Pod(容器組)也會消失。然後,Deployment 可以通過創建新的 Pod(容器組)來動態地將羣集調整回原來的狀態,以使應用程序保持運行。

舉個例子,假設有一個圖像處理後端程序,具有 3 個運行時副本。這 3 個副本是可以替換的(無狀態應用),即使 Pod(容器組)消失並被重新創建,或者副本數由 3 增加到 5,前端系統也無需關注後端副本的變化。由於 Kubernetes 集羣中每個 Pod(容器組)都有一個唯一的 IP 地址(即使是同一個 Node 上的不同 Pod),我們需要一種機制,爲前端系統屏蔽後端系統的 Pod(容器組)在銷燬、創建過程中所帶來的 IP 地址的變化。

Kubernetes 中的 Service(服務) 提供了這樣的一個抽象層,它選擇具備某些特徵的 Pod(容器組)併爲它們定義一個訪問方式。Service(服務)使 Pod(容器組)之間的相互依賴解耦(原本從一個 Pod 中訪問另外一個 Pod,需要知道對方的 IP 地址)。一個 Service(服務)選定哪些 Pod(容器組) 通常由 LabelSelector(標籤選擇器) 來決定。

在創建Service的時候,通過設置配置文件中的 spec.type 字段的值,可以以不同方式向外部暴露應用程序:

  • ClusterIP(默認)

在羣集中的內部IP上公佈服務,這種方式的 Service(服務)只在集羣內部可以訪問到

  • NodePort

使用 NAT 在集羣中每個的同一端口上公佈服務。這種方式下,可以通過訪問集羣中任意節點+端口號的方式訪問服務 :。此時 ClusterIP 的訪問方式仍然可用。

  • LoadBalancer

在雲環境中(需要雲供應商可以支持)創建一個集羣外部的負載均衡器,併爲使用該負載均衡器的 IP 地址作爲服務的訪問地址。此時 ClusterIP 和 NodePort 的訪問方式仍然可用。

Service是一個抽象層,它通過 LabelSelector 選擇了一組 Pod(容器組),把這些 Pod 的指定端口公佈到到集羣外部,並支持負載均衡和服務發現。

  • 公佈 Pod 的端口以使其可訪問
  • 在多個 Pod 間實現負載均衡
  • 使用 Label 和 LabelSelector

服務和標籤

下圖中有兩個服務Service A(黃色虛線)和Service B(藍色虛線) Service A 將請求轉發到 IP 爲 10.10.10.1 的Pod上, Service B 將請求轉發到 IP 爲 10.10.10.2、10.10.10.3、10.10.10.4 的Pod上。

在這裏插入圖片描述

Service 將外部請求路由到一組 Pod 中,它提供了一個抽象層,使得 Kubernetes 可以在不影響服務調用者的情況下,動態調度容器組(在容器組失效後重新創建容器組,增加或者減少同一個 Deployment 對應容器組的數量等)。

Service使用 Labels、LabelSelector(標籤和選擇器) 匹配一組 Pod。Labels(標籤)是附加到 Kubernetes 對象的鍵/值對,其用途有多種:

  • 將 Kubernetes 對象(Node、Deployment、Pod、Service等)指派用於開發環境、測試環境或生產環境
  • 嵌入版本標籤,使用標籤區別不同應用軟件版本
  • 使用標籤對 Kubernetes 對象進行分類

下圖體現了 Labels(標籤)和 LabelSelector(標籤選擇器)之間的關聯關係
在這裏插入圖片描述

  • Deployment B 含有 LabelSelector 爲 app=B 通過此方式聲明含有 app=B 標籤的 Pod 與之關聯
  • 通過 Deployment B 創建的 Pod 包含標籤爲 app=B
  • Service B 通過標籤選擇器 app=B 選擇可以路由的 Pod

使用標籤,可以進行統一的設定,是非常方便的。Labels(標籤)可以在創建 Kubernetes 對象時附加上去,也可以在創建之後再附加上去。任何時候都可以修改一個 Kubernetes 對象的 Labels(標籤)

設置一個Service

之前我們已經啓動了ngxin Deployment,重新創建一個deployment-label.yaml, 現在將它打上一個標籤 app: nginx :

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: nginx-deployment
  labels: #標籤,可以靈活定位一個或多個資源,其中key和value均可自定義,可以定義多組
    app: nginx #爲該Deployment設置key爲app,value爲nginx的標籤
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

然後我們創建對應的nginx-service.yaml,對標籤爲 app: ngxin的Pod提供service服務:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service	#Service 的名稱
  labels:     	#Service 自己的標籤
    app: nginx	#爲該 Service 設置 key 爲 app,value 爲 nginx 的標籤
spec:	    #這是關於該 Service 的定義,描述了 Service 如何選擇 Pod,如何被訪問
  selector:	    #標籤選擇器
    app: nginx	#選擇包含標籤 app:nginx 的 Pod
  ports:
  - name: nginx-port	#端口的名字
    protocol: TCP	    #協議類型 TCP/UDP
    port: 80	        #集羣內的其他容器組可通過 80 端口訪問 Service
    nodePort: 32600   #通過任意節點的 32600 端口訪問 Service
    targetPort: 80	#將請求轉發到匹配 Pod 的 80 端口
  type: NodePort	#Serive的類型,ClusterIP/NodePort/LoaderBalancer

執行下面,更新之前的nginx Pod以及新創建對應的Service:

kubectl apply -f deployment-label.yaml
kubectl apply -f nginx-service.yaml
可以看到提示:service/nginx-service created

執行查看serivce命令 kubectl get services -o wide(列出所有 service 並顯示詳細信息),可以看到:
在這裏插入圖片描述

kubectl get nodes -o wide 查看節點詳細信息
minikube ip 查看虛擬機的ip
minikube ssh 進入虛擬機

這個時候,訪問任意節點23600端口就能看到啦。(博主因爲某vpn被斃掉了。所以nginx鏡像都沒下載成功)

部署一個springboot單機helloword

創建一個helloword項目

這就過多贅述了,直接上簡單的例子
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>k8s.demo</groupId>
    <artifactId>k8s-helloword</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <run.jvmArguments/>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.1.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.3.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

啓動方法:

package k8s.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Boot {
    public static void main(String[] args) {
        SpringApplication.run(Boot.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "hello word";
    }
}

配置文件:

server:
  port: 9999

打包並運行測試

mvn clean install
mvn package spring-boot:repackage
java -jar target/k8s-helloword.jar

然後訪問http://localhost:9999/hello,得到hello word, 應用程序成功

使用docket方式運行

博主第一篇章有docker方式

編寫Dockerfile,然後利用文件打包鏡像,將我們的jar打到鏡像裏

FROM frolvlad/alpine-oraclejdk8:slim
RUN mkdir /docker/demo
RUN mkdir /docker/demo/logs
ADD ./target/k8s-helloword.jar /docker/demo/k8s-helloword.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/docker/demo/k8s-helloword.jar"]

docker build -t codemon/k8s-hello . 進行打包鏡像
docker run -d -p 9999:9999 --name k8s-hello codemon/k8s-hello 運行起來,然後訪問http://localhost:9999/hello,得到hello word, docker運行成功

將鏡像部署到k8s

內網模式

將k8s的docker地址修改爲本地的太麻煩,博主直接將鏡像打到了博主的docker hub上,然後公開即可。
編寫Pod和Service的yaml文件:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloword
    labels:
        app: helloword
spec:
    replicas: 1
    selector:
        matchLabels:
            app: helloword
    template:
        metadata:
            labels:
                app: helloword
        spec:
            containers:
                - name: helloword
                  image: codemon/k8s-hello:latest
                  ports:
                      - containerPort: 9999
---
apiVersion: v1
kind: Service
metadata:
    name: helloword-service
    labels:
        app: helloword
spec:
    ports:
        - port: 9999
          protocol: TCP
    selector:
        app: helloword

執行kubectl apply -f helloword-deployment.yaml,發佈pod和service。

可以使用
kubectl get pods -o wide
kubectl get services -o wide
查看具體信息,

可以看到,helloword-service已經成功,並且使用的是 默認的ClusterIP類型,這種方式只可以在內網進行訪問,我們來嘗試一下在這裏插入圖片描述
minikube ssh進入虛擬機後,curl http://10.97.23.8:9999/hello成功,獲得返回值,但是在外面無論如何也訪問不了

外網模式

將上面的service,加入 type: NodePort 開放公網

spec:
    ports:
        - port: 9999
          protocol: TCP
    type: NodePort
    selector:
        app: helloword

重新運行kubectl apply -f helloword-deployment.yaml, 這個時候,使用kubectl get services -o wide,可以看到,網絡ip類型已經改變,並且未指定端口的情況下,隨機對外映射了31125端口
在這裏插入圖片描述
使用kubectl get nodes -o wide查詢node節點詳細信息,可以看到node節點內網ip是: 10.0.2.15
在這裏插入圖片描述
minikube ssh進入虛擬機,使用service本身的ip10.97.23.8和node節點的ip 10.0.2.15訪問:
curl http://10.97.23.8:9999/hello
curl http://10.0.2.15:9999/hello
都成功訪問

我們再獲取虛擬機的ip,minikube ip, 獲取到虛擬機內部集羣對外的ip是:192.168.99.110
打開瀏覽器調用http://192.168.99.110:31125/hello, 成功

滾動發佈更新版本

首先我們來打兩個鏡像併發布到docker hub上:

  • 第一個叫codemon/k8s-hello:v1, 接口返回“v1”這個字符串
  • 第二個叫codemon/k8s-hello:v2, 接口返回“v2”這個字符串
  • 然後我們docker push codemon/k8s-hello, 會將兩個鏡像標籤都push上去

我們修改一下pod配置

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloword
    labels:
        app: helloword
spec:
    replicas: 2 #這裏改成兩個副本
    selector:
        matchLabels:
            app: helloword
    template:
        metadata:
            labels:
                app: helloword
        spec:
            containers:
                - name: helloword
                  image: codemon/k8s-hello:v1 #這裏改成v1版本的鏡像
                  ports:
                      - containerPort: 9999

執行kubectl apply -f helloword-deployment.yaml
執行kubectl get --watch deployments 監控一下pod的變化

需要耐心等待一小會,畢竟要重新拉鏡像重新啓動pod

在這裏插入圖片描述
看到監控,pod已經全部好了。調用接口試試:http://192.168.99.110:31125/hello,返現返回v1字符串。成功~

然後上面步驟一樣,將鏡像標記改爲v2,部署之後發現接口返回值是:v2

回滾應用版本

和上面發佈新版本一樣, 我們發現部署有問題,想立即回滾。我們可以直接修改鏡像的名稱,然後執行上面的步驟。返現已經回滾

其實k8s有自己的回滾方式,這裏就不介紹了。隨着後面博主的博客,會在企業級容器架構和微服務集合後,統一介紹

刪除與恢復

我們來看一個巨牛逼的功能。直接把pod刪掉。相當於把pod上面的docker服務也一起刪掉了。這個時候會發生什麼:

kubectl delete pod helloword-68874fb864-4k78x
在這裏插入圖片描述

你看到的沒錯。他又自動恢復了。這個過程中一直刷接口,並沒有掛掉

結尾

可以看到,最終我們以NodePort方式開放出來固定ip,內部如何轉發是k8s自己的事情。開放出來的ip可以在雲方案中搭配內部負載均衡器和vpc使用,方便快捷,當然,還有另外一些方式開放
參考ip方面文章:
kubernetes service外部網絡訪問方式
kubernetes(k8s)Deployment滾動升級和回滾

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章