微服务架构师封神之路01-利用minikube部署一个最简单的应用

开篇

架构师者,为项目消灾解惑者也。只有骨骼清奇之人,得高人指点,甘于处子之身苦心修炼十年以上者,方可得道成仙。一人之下,万人之上,可谓朝堂之上呼风唤雨,岂不美哉~
但这条道路上也充满凶险,可是却有幸福相伴。各位看官可能会问,要十年甘于处子之身那幸福来自哪里?我想说看来你的层次还是不够,在追求梦想的路上再大的艰辛不也是幸福吗?!有梦想的程序猿时刻要双手打字以示决心!
反正想多了没用。拿上实践和理论这两把上古流传的神兵,趁月黑风高今晚就启航。接下来的几年中我将披荆斩棘…
闲话少叙…

目标

第一天来点简单的吧,部署一个最简单的web应用到minikube上。Hello World,别犹豫就是它了!
如果用Java的话,我觉得最简单的方式应该是使用springboot。简而言之,我们需要做一下几件事情,

  1. 创建一个springboot为框架的maven project
  2. 把打好的jar包制成docker image
  3. 把docker image发布到remote repository上。我使用docker hub
  4. 使用minikube部署这个image。

我想以上的步骤足够可以模拟一个项目从开发到部署的基本流程了。有了需求也就明确了我们所需要的环境,

  1. JDK
  2. maven
  3. Docker。在build image和push image都需要它
  4. Kerbernetes。
  5. Minikube。minikube的基础是Kerbernetes,其实它就是一个single node kubernetes cluster。

环境搭建参考:

  • Installing Docker: https://www.docker.com/products/docker-desktop
  • Installing kubernetes: https://kubernetes.io/docs/tasks/tools/install-kubectl/
  • Installing minikube: https://kubernetes.io/docs/tasks/tools/install-minikube/

在它们的官方网站上有详细的安装说明,把环境弄好以后我们可以进行下一步了。

helloworld maven项目

创建一个maven project,结构如下
在这里插入图片描述
很简单,只有三个文件,Dockerfile、AppMain.java、pom.xml。思路也很简单,

  1. 利用spring-boot制作一个可以独立运行的helloworkd.jar包。它只依赖于jre。
  2. 用openjdk 8的image作为基础,把helloworld.jar和它结合在一起,再添加加一个启动文件。启动文件会在container加载image的时候被调用。把它们做成一个新的docker image。image的具体细节在Dockerfile中。
  3. 制作docker image的关键步骤由maven plugin完成。首先maven-resources-plugin把build image需要的Dockerfile拷贝到指定位置,紧接着exec-maven-plugin直接调用docker build命令制作image。

pom.xml

核心步骤是exec-docker-build。需要详细理解命令
docker build --no-cache --tag b5wang/helloworld:1.0 -f ${project.build.directory}/docker-build/Dockerfile ${project.build.directory}
的具体含义。为什么image repository要叫b5wang/helloworld?这个和后面我们要做的docker push有直接关系。

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.4.RELEASE</version>
    </parent>

    <groupId>com.b5wang.cloudlab</groupId>
    <artifactId>helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>helloworld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <finalName>${project.artifactId}</finalName>

        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- =========================================================================
              == Copy docker config files into target/docker-build folder               ==
              ========================================================================= -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <id>generate-docker-build</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/docker-build</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${project.basedir}/src/docker</directory>
                                    <filtering>true</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- =========================================================================
              == Create docker image                                                    ==
              ========================================================================= -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <id>exec-docker-build</id>
                        <phase>package</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <configuration>
                            <executable>docker</executable>
                            <commandlineArgs>build --no-cache -t b5wang/helloworld:1.0 -f ${project.build.directory}/docker-build/Dockerfile ${project.build.directory}</commandlineArgs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- fix error java.lang.ClassNotFoundException: org.apache.maven.doxia.siterenderer.DocumentContent -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.8.2</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
        </plugins>
    </build>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.4.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>
</project>

Dockerfile

FROM openjdk:8-jdk-alpine
COPY helloworld.jar /app/helloworld.jar
ENTRYPOINT ["java","-jar","/app/helloworld.jar"]

AppMain.java

package com.b5wang.cloudlab.helloworld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@EnableAutoConfiguration
public class AppMain {

    @RequestMapping("/")
    String home() {
        return "minikube: Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(AppMain.class, args);
    }
}

$ mvn clean install

运行之前确认两件事情:

  • 要保证本地的docker daemon处于运行状态。否则有错误提示: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
  • docker需要登录docker hub账户。之后的push需要登录。

现在可以运行mvn clean install了。运行成功后通过docker images检查,

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
b5wang/helloworld   1.0                 5c6f1985a2bb        3 hours ago         122MB
openjdk             8-jdk-alpine        a3562aa0b991        12 months ago       105MB

$ docker push

项目叫helloworld,所以我想把它的docker image repository也叫做helloworld。但在docker hub上这并不可能,除非这个image是docker certified image,像openjdk、weblogic之类的。一般的docker hub repository都是以账户名称开头的,例如,b5wang/xxx,那我们不妨把现在的项目image repository叫做b5wang/helloworld。在项目pom里面的docker build指令已经指定了repository name就是b5wang/hello,并tag成1.0.

docker build --no-cache --tag b5wang/helloworld:1.0 -f ${project.build.directory}/docker-build/Dockerfile ${project.build.directory}

目前的repository name已经符合docker hub的规范,所以就不需要再docker tag多一次。直接push就可以。如果在本地repository name用了其它的名字,需要再tag一次,让的名字符合规范后才能提交到你的docker hub账户下面。
例如,假设本地image repository名字叫helloworld,你想把image提交到b5wang/helloworld repository当中。你需要把image先提交到本地的b5wang/helloworld repository中。

docker tag helloworld:1.0 b5wang/helloworld:1.0

再把b5wang/helloworld repository push到docker hub registry。因为我们在maven build的时候把image repository直接就命名成了b5wang/helloworld,以上的步骤我们是不需要的。
下面发布local repostory到docker hub,

$ docker push b5wang/helloworld:1.0
The push refers to repository [docker.io/b5wang/helloworld]
1c227dd7976a: Layer already exists 
ceaf9e1ebef5: Layer already exists 
9b9b7f3d56a0: Layer already exists 
f1b5933fe4b5: Layer already exists 
1.0: digest: sha256:f45d9ff8003effe76157c37107e96b4ee080a128703f67ec613473943a2e16a0 size: 1159

检查minikube的状态

$ minikube start, statue

启动minikube并检查运行状态

$ minikube start
😄  minikube v1.11.0 on Darwin 10.13.6
✨  Using the hyperkit driver based on existing profile
👍  Starting control plane node minikube in cluster minikube
🔄  Restarting existing hyperkit VM for "minikube" ...
🐳  Preparing Kubernetes v1.18.3 on Docker 19.03.8 ...
🔎  Verifying Kubernetes components...
🌟  Enabled addons: default-storageclass, storage-provisioner
🏄  Done! kubectl is now configured to use "minikube"

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

$ docker login docker.io // 登录docker hub账户

登录docker hub。可能你已经注意到了,你build image使用的和minikube使用的不是同一个docker daemon。其实只用一个docker daemon也是可以的,这取决于你如何安装minikube。但就模拟现实部署流程来说可能这样反而会真实点。

$ minikube ssh                         _             _            
            _         _ ( )           ( )           
  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __  
/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)

$ docker login docker.io
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: b5wang
Password: 
WARNING! Your password will be stored unencrypted in /home/docker/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

$ exit
logout

在minikube上部署应用

$ kubectl create deployment // 创建deployment

在minikube上创建一个deployment。Deployment负责管理应用,把image运行在pod当中。pod相当于docker的container。

$ kubectl create deployment helloworld --image=b5wang/helloworld:1.0
deployment.apps/helloworld created

观察当前deployment和pod的状态

$ kubectl get deployments
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
helloworld   1/1     1            1           5m44s

$ kubectl get pods
NAME                          READY   STATUS    RESTARTS   AGE
helloworld-5889797b45-k6nxr   1/1     Running   0          5m53s

可以通过观察event来理解helloworld应用在kubernetes中部署的过程,

Shufengs-iMac:~ wangbo$ kubectl get events
LAST SEEN   TYPE     REASON              OBJECT                             MESSAGE
77m         Normal   Scheduled           pod/helloworld-5889797b45-k6nxr    Successfully assigned default/helloworld-5889797b45-k6nxr to minikube
77m         Normal   Pulling             pod/helloworld-5889797b45-k6nxr    Pulling image "b5wang/helloworld:1.0"
77m         Normal   Pulled              pod/helloworld-5889797b45-k6nxr    Successfully pulled image "b5wang/helloworld:1.0"
77m         Normal   Created             pod/helloworld-5889797b45-k6nxr    Created container helloworld
77m         Normal   Started             pod/helloworld-5889797b45-k6nxr    Started container helloworld
77m         Normal   SuccessfulCreate    replicaset/helloworld-5889797b45   Created pod: helloworld-5889797b45-k6nxr
77m         Normal   ScalingReplicaSet   deployment/helloworld              Scaled up replica set helloworld-5889797b45 to 1

注意到到有一个pulling的过程,如果minikube所依赖的docker daemon事先没有登录到docker hub,它就不可能找到指定的image,pulling过程就会失败。

$ minikube dashboard

minikube已经集成了kubernetes dashboard UI,运行以下命令就可以用browser来监控当前minikube中各种应用的运行状态。

$ minikube dashboard
🔌  Enabling dashboard ...
🤔  Verifying dashboard health ...
🚀  Launching proxy ...
🤔  Verifying proxy health ...
🎉  Opening http://127.0.0.1:50551/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...

$ kubectl expose deployment // 创建Service

此刻helloworld运行在pod中,pod相当于docker container。但是pod只能在kubernetes cluster内部间相互访问,我们要在外部访问到pod还需要创建service。Service相当于运行在pod前端的load balancer。

$ kubectl expose deployment helloworld --type=LoadBalancer --port=8080
service/helloworld exposed

$ kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
helloworld   LoadBalancer   10.110.57.57   <pending>     8080:30330/TCP   34s
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP          23h

访问helloworld应用

$ minikube service helloworld
|-----------|------------|-------------|---------------------------|
| NAMESPACE |    NAME    | TARGET PORT |            URL            |
|-----------|------------|-------------|---------------------------|
| default   | helloworld |        8080 | http://192.168.64.2:30330 |
|-----------|------------|-------------|---------------------------|
🎉  Opening service default/helloworld in default browser...

对我们目前所做的这些步骤的理解

画一张图把上面我们做的所有事情形象的表现出来,
在这里插入图片描述

总结

今天实践了如下这些知识点,

  • 如何把一个java应用通过maven制作成docker image
  • 把本地的docker image发布到远程docker registry上面
  • 利用kubernetes和docker image部署应用程序
  • 创建kubernetes service,暴露应用给外部

完整的源码:https://github.com/b5wang/cloudlab

当然还需要花一些时间去理解每一步中更细节的东西,不断的提出问题并解决。
今天就到这里。
干就完事了啊!奥利给!
再见~ :)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值