首页 文章 精选 留言 我的

精选列表

搜索[腾讯技术创作特训营S8],共10000篇文章
优秀的个人博客,低调大师

【Quarkus技术系列】「云原生架构体系」配置参考指南相关的功能机制配置介绍分析

回顾Quarkus介绍 Quarkus的概念定义 Quarkus是一个为Java虚拟机(JVM)和原生编译而设计的全堆栈 Kubernetes 原生 Java 框架,用于专门针对容器优化 Java,并使其成为无服务器、云和 Kubernetes 环境的高效平台。 Quarkus与框架整合 Quarkus可与常用 Java 标准、框架和库协同工作,例如:Eclipse MicroProfile、Spring、Apache Kafka、RESTEasy (JAX-RS)、Hibernate ORM (JPA)、Spring、Infinispan、Camel 等。 Quarkus的依赖注入 Quarkus的依赖注入解决方案基于 CDI(上下文和依赖注入),且包含一个扩展框架来扩展功能并将其配置、引导并集成到您的应用中。添加扩展就像添加依赖项一样容易;或者,您可以使用 Quarkus 工具。 Quarkus多语言化 GraalVM(一种通用虚拟机,用于运行以多种语言(包括 Java 和 JavaScript)编写的应用)提供正确信息,以便对应用进行原生编译。 惊人的快速启动时间,极低的RSS内存(不仅是堆大小!)在容器编排平台(如Kubernetes)中提供了近乎即时的向上扩展和高密度的内存利用率 专为开发人员而设计 Quarkus的设计从一开始就立足于简单易用,其功能几乎不需要配置即可正常使用。 开发人员可以为其应用选择所需的Java框架,而这些应用可以在JVM模式下运行,也可以在原生模式下进行编译和运行。 可为开发人员带来最佳的欢乐: 统一配置 零配置,瞬间重载实时重载 简化了80%常用用法的代码,灵活了20%的用法 无需麻烦的本机可执行文件生成 容器优先 无论是将应用托管在公共云上还是内部托管的 Kubernetes 集群中,快速启动和低内存消耗等特性对于降低总体主机成本来说都至关重要。 Quarkus的开发遵从了容器优先的原则,这意味着它已通过以下方式针对降低内存使用和加快启动时间进行了优化 鼎力支持Graal/SubstrateVM 构建时元数据处理 减少反射的使用 本机映像预启动 Quarkus构建的应用其内存消耗只有传统Java的1/10,而且启动时间更快(快了 300 倍),这些都大大降低了云资源的成本。 云原生 在Kubernetes等环境中采用 12 要素原则。 统一命令式与反应式 在设计上,Quarkus能够在开发应用时无缝地结合熟悉的命令式代码和非阻塞、响应式样式。 这对于习惯使用命令式模型而不想切换风格的 Java 开发人员以及使用云原生/响应式方法的开发人员都非常有用。 基于标准 Quarkus通过利用您喜欢和使用的五十多种最佳库的不断增长的列表,提供了一个内聚的,易于使用的,全栈的框架。所有都连接在标准主干上 Quarkus 开发模型可以适应您正在开发的任何应用 对于在新的无服务器架构、微服务、容器、Kubernetes、功能即服务(FaaS)和云环境中运行 Java 而言,Quarkus 堪称是一个有效的解决方案,因为在创建它时就充分考虑了所有这些因素。 使用DI注入 Quarkus中的依赖注入基于ArC,ArC是为Quarkus架构量身定制的基于CDI的依赖注入解决方案。如果您不熟悉CDI,建议您阅读CDI简介指南。 Quarkus仅实现CDI功能的一个子集,并具有非标准功能和特定的APIS,您可以在Contexts and Dependency Injection指南中了解有关它的更多信息。 ArC作为的依赖项,quarkus-resteasy因此您已经很方便了。 配置参考指南 Quarkus应用程序和Quarkus本身(核心和扩展)都通过相同的机制进行配置,该机制利用SmallRye Config API和 MicroProfile Config规范的实现。Quarkus本身提供了一些附加功能。 配置源 默认情况下,Quarkus从多个来源(优先级递减)中读取配置属性: 系统属性 环境变量 名为文件.env放置在当前工作目录中 application.properties文件放在$PWD/config/目录中 应用程序配置文件,即src/main/resources/application.properties 系统属性 运行jar: java -Dquarkus.datasource.password=youshallnotpass -jar target/quarkus-app/quarkus-run.jar native执行文件: ./target/myapp-runner -Dquarkus.datasource.password=youshallnotpass 环境变量 运行jar: export QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ; java -jar target/quarkus-app/quarkus-run.jar native可执行文件: export QUARKUS_DATASOURCE_PASSWORD=youshallnotpass ; ./target/myapp-runner 文件名为.env放置在当前工作目录中 示例.env文件 QUARKUS_DATASOURCE_PASSWORD=youshallnotpass QUARKUS_DATASOURCE_PASSWORD使用与环境变量相同的规则来转换名称。 对于开发人员模式,此文件可以放在项目的根目录中,但建议不要将其检入版本控制。 没有定义的配置文件的环境变量.env文件将覆盖其所有相关的配置文件中application.properties,例如,%test.application.value被覆盖APPLICATION_VALUE的.env文件。 application.properties文件放置在$PWD/config/ 将application.properties文件放置在名为config的目录中,该目录位于应用程序运行的目录中,该文件中定义的所有运行时属性都将覆盖默认配置。 此外,添加到该文件中的任何不属于原始application.properties文件的 运行时属性也将被考虑在内。对于运行程序jar和本机可执行文件,其工作方式相同。 该config/application.properties功能在开发模式下也可用。 要使用它,config/application.properties需要放置在构建工具的输出目录中。但是请记住,从构建工具进行的任何清理操作(例如mvn clean或)gradle clean也会删除该config目录。 应用程序配置文件 位于中的主要应用程序配置文件src/main/resources/application.properties。 application.properties文件 greeting.message=hello quarkus.http.port=9090 Quarkus支持在文件中使用属性表达式application.properties。 将配置文件嵌入到依赖项中 可以通过向META-INF/microprofile-config.properties配置文件中添加一个配置文件来将配置文件嵌入其中(这是MicroProfile Config的标准功能)。 当将此依赖项添加到应用程序时,其配置属性将被合并。 可以覆盖优先于它的属性来自它的属性application.properties。 注入配置属性 Quarkus使用MicroProfile Config注释在应用程序中注入配置属性。 @ConfigProperty(name = "greeting.message") String message; 可以使用@Inject @ConfigProperty或@ConfigProperty。对于使用@Inject注释的成员,注释不是必需的@ConfigProperty。此行为不同于MicroProfile Config。 如果应用程序尝试注入未设置的配置属性,则会引发错误,从而使您可以快速了解配置何时完成。 @ConfigProperty例子 @ConfigProperty(name = "greeting.message") String message; @ConfigProperty(name = "greeting.suffix", defaultValue="!") String suffix; @ConfigProperty(name = "greeting.name") Optional<String> name; 如果您不提供此属性的值,则应用程序启动将失败 javax.enterprise.inject.spi.DeploymentException: No config value of type [class java.lang.String] exists for: greeting.message。 如果配置未提供的值,则会注入默认值greeting.suffix。 此属性是可选的-Optional如果配置未提供的值,则会注入一个空值greeting.name。 以编程方式访问配置 可以通过编程方式访问配置。实现动态查找或从既不是CDI bean也不是JAX-RS资源的类中检索配置的值可能很方便。 可以使用以下方式以编程方式访问配置 org.eclipse.microprofile.config.ConfigProvider.getConfig(): String databaseName = ConfigProvider.getConfig().getValue("database.name", String.class); Optional<String> maybeDatabaseName = ConfigProvider.getConfig().getOptionalValue("database.name", String.class); 使用@ConfigProperties 作为以上一示例中所示的方式注入多个相关配置值的替代方法,用户还可以使用@io.quarkus.arc.config.ConfigProperties注释将这些属性组合在一起。 对于上面的Greeting属性,GreetingConfiguration可以这样创建一个类: @ConfigProperties(prefix = "greeting") public class GreetingConfiguration { private String message; private String suffix = "!"; private Optional<String> name; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getSuffix() { return suffix; } public void setSuffix(String suffix) { this.suffix = suffix; } public Optional<String> getName() { return name; } public void setName(Optional<String> name) { this.name = name; } } 然后可以GreetingResource使用@Inject类似的CDI注释将此类注入到,如下所示: @Inject GreetingConfiguration greetingConfiguration; Quarkus提供的另一种替代样式是GreetingConfiguration像这样创建接口: package org.acme.config; import io.quarkus.arc.config.ConfigProperties; import org.eclipse.microprofile.config.inject.ConfigProperty; import java.util.Optional; @ConfigProperties(prefix = "greeting") public interface GreetingConfiguration { @ConfigProperty(name = "message") String message(); @ConfigProperty(defaultValue = "!") String getSuffix(); Optional<String> getName(); } 在@ConfigProperties类或接口上使用时,如果未提供其字段之一的值,则应用程序启动将失败,并且将javax.enterprise.inject.spi.DeploymentException显示指示缺少值的信息。这不适用于Optional字段和具有默认值的字段。 有关@ConfigProperties的其他说明 当使用带有类注释的常规类时,@ConfigProperties不一定必须声明getter和setter。具有简单的公共非最终字段也是有效的。 此外,配置类支持嵌套对象配置。假设需要有一个额外的问候语配置层,名称content将包含一些字段。可以这样实现: @ConfigProperties(prefix = "greeting") public class GreetingConfiguration { public String message; public String suffix = "!"; public Optional<String> name; public ContentConfig content; public static class ContentConfig { public Integer prizeAmount; public List<String> recipients; } } 字段名称(不是类名称)将确定绑定到对象的属性的名称。 设置属性将以正常方式进行,例如,application.properties可能具有: greeting.message = hello greeting.name = quarkus greeting.content.prize-amount=10 greeting.content.recipients=Jane,John 此外,@ConfigProperties可以使用Bean Validation批注来批注使用批注的类,类似于以下示例: @ConfigProperties(prefix = "greeting") public class GreetingConfiguration { @Size(min = 20) public String message; public String suffix = "!"; } 为了使验证生效,quarkus-hibernate-validator需要提供扩展名 如果使用给定的配置验证失败,则应用程序将无法启动,并在日志中指示相应的验证错误。 如果接口带有注释@ConfigProperties,则允许该接口扩展其他接口,并且使用整个接口层次结构中的方法来绑定属性。 使用具有不同前缀的相同ConfigProperty Quarkus还支持使用注解@ConfigProperties为每个注入点使用带有不同前缀的同一对象io.quarkus.arc.config.@ConfigPrefix。举例来说,前缀和前缀GreetingConfiguration都需要使用以上代码。在这种情况下,代码如下所示:greetingother GreetingConfiguration.java @ConfigProperties(prefix = "greeting") public class GreetingConfiguration { @Size(min = 20) public String message; public String suffix = "!"; } SomeBean.java @ApplicationScoped public class SomeBean { @Inject GreetingConfiguration greetingConfiguration; @ConfigPrefix("other") GreetingConfiguration otherConfiguration; } 使用对象列表 在某些情况下,可能有必要支持利用对象列表的复杂配置结构,如以下示例所示: ComplexConfiguration.java @ConfigProperties(prefix = "complex") public class ComplexConfiguration { public String name; public String user; public String password; public List<Nested> inputs; public List<Nested> outputs; public static class Nested { public String user; public String password; } } 仅当将YAML配置与quarkus-config-yaml扩展一起使用时,才对此类用例提供支持。相应的示例YAML配置可以是: application.yaml complex: name: defaultName user: defaultUser password: defaultPassword inputs: - user: user password: secret - user: otheruser password: secret2 outputs: - user: someuser password: asecret - user: someotheruser password: anothersecret 这种配置的局限性在于用作列表的通用类型的类型必须是类而不是接口 配置配置文件 Quarkus支持配置配置文件的概念。这些允许您在同一文件中具有多个配置,并通过配置文件名称在它们之间进行选择。 语法为%{profile}.config.key=value。例如,如果我有以下内容: quarkus.http.port=9090 %dev.quarkus.http.port=8181 除非dev配置文件处于活动状态,否则Quarkus HTTP端口将为9090 ,在这种情况下,它将为8181。 要在.env文件中使用配置文件,可以遵循一种_{PROFILE}_CONFIG_KEY=value模式。.env文件中上述示例的等效内容为: QUARKUS_HTTP_PORT=9090 _DEV_QUARKUS_HTTP_PORT=8181

优秀的个人博客,低调大师

【SpringCloud技术专题】「Feign」从源码层面让你认识Feign工作流程和运作机制

Feign工作流程源码解析 什么是feign:一款基于注解和动态代理的声明式restful http客户端。 原理 Feign发送请求实现原理 微服务启动类上标记@EnableFeignClients注解,然后Feign接口上标记@FeignClient注解。@FeignClient注解有几个参数需要配置,这里不再赘述,都很简单。 Feign框架会扫描注解,然后通过Feign类来处理注解,并最终生成一个Feign对象。 解析@FeignClient注解,生成MethodHandler 具体的解析类是ParseHandlerByName。这个类是ReflectiveFeign的内部类。 // 解析注解元数据,使用Contract解析 List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(key.type()); 拿到注解元数据以后,循环处理注解元数据,创建每个方法对应的MethodHandler,这个MethodHandler最终会被代理对象调用。最终MethodHandler都会保存到下面这个集合中,然后返回。 Map<String, MethodHandler> result = new LinkedHashMap(); 解析完成以后,调用ReflectiveFeign.newInstance()生成代理类。 MethodHandler是feign的一个接口,这个接口的invoke方法,是动态代理调用者InvocationHandler的invoke()方法最终调用的方法。 重新表述一遍:InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求。这就是Feign动态代理的具体实现。 ReflectiveFeign类的newInstance()方法的第57行: // 创建动态代理调用者 InvocationHandler handler = this.factory.create(target, methodToHandler); // 反射生成feign接口代理 T proxy = Proxy.newProxyInstance(加载器, 接口数组, handler); InvocationHandler.invoke()的具体实现在FeignInvocationHandler.invoke(),FeignInvocationHandler也是ReflectiveFeign的一个内部类。里面有很多细节处理这里不再赘述,我们直接进入核心那一行代码,以免影响思路,我们是理Feign的实现原理的!不要在意这些细节! // InvocationHandler的invoke()方法最终回调MethodHandler的invoke()来发送http请求 ReflectiveFeign类的invoke()方法,第323行,代码的后半段,如下: (MethodHandler)this.dispatch.get(method). invoke(args); this.dispatch:这是一个map,就是保存所有的MethodHandler的集合。参考创建InvocationHandler的位置:ReflectiveFeign类的newInstance()方法的第57行。 this.dispatch.get(method):这里的method就是我们开发者写的feign接口中定义的方法的方法名!这段代码的意思就是从MethodHandler集合中拿到我们需要调用的那个方法。 this.dispatch.get(method). invoke(args):这里的invoke就是调用的MethodHandler.invoke()!动态代理回调代理类,就这样完成了,oh my god,多么伟大的创举! MethodHandler.invoke()的具体实现:SynchronousMethodHandler.invoke() 到了这里,就是发送请求的逻辑了。发送请求前,首先要创建请求模板,然后调用请求拦截器RequestInterceptor进行请求处理。 // 创建RequestTemplate RequestTemplate template = this.buildTemlpateFromArgs.create(argv); // 创建feign重试器,进行失败重试 Retryer retryer = this.retryer.clone(); while(true){ try{ // 发送请求 return this.executeAndDecode(template); } catch(RetryableException var5) { // 失败重试,最多重试5次 retryer.continueOrPropagate(); } } RequestTemplate处理 RequestTemplate模板需要经过一系列拦截器的处理,主要有以下拦截器: BasicAuthRequestInterceptor:授权拦截器,主要是设置请求头的Authorization信息,这里是base64转码后的用户名和密码。 FeignAcceptGzipEncodingInterceptor:编码类型拦截器,主要是设置请求头的Accept-Encoding信息,默认值{gzip, deflate}。 FeignContextGzipEncodingInterceptor:压缩格式拦截器,该拦截器会判断请求头中Context-Length属性的值,是否大于请求内容的最大长度,如果超过最大长度2048,则设置请求头的Context-Encoding信息,默认值{gzip, deflate}。注意,这里的2048是可以设置的,可以在配置文件中进行配置: feign.compression.request.enabled=true feign.compression.request.min-request-size=2048 min-request-size是通过FeignClientEncodingProperties来解析的,默认值是2048。 我们还可以自定义请求拦截器,我们自定义的拦截器,也会在此时进行调用,所有实现了RequestTemplate接口的类,都会在这里被调用。比如我们可以自定义拦截器把全局事务id放在请求头里。 使用feign.Request把RequestTemplate包装成feign.Request feign.Request由5部分组成: method url headers body charset http请求客户端 Feign发送http请求支持下面几种http客户端: JDK自带的HttpUrlConnection Apache HttpClient OkHttpClient // 具体实现有2个类Client.Default 和LoadBalancerFeignClient response = this.client.execute(request, this.options); Client接口定义了execute()的接口,并且通过接口内部类实现了Client.execute()。 HttpURLConnection connection = this.convertAndSend(request, options); return this.convertResponse(connection).toBuilder(). request(request).build(); 这里的Options定义了2个参数: connectTimeoutMillis:连接超时时间,默认10秒。 readTimeoutMillis:读取数据超时时间,默认60秒。 这种方式是最简单的实现,但是不支持负载均衡,Spring Cloud整合了Feign和Ribbon,所以自然会把Feign和Ribbon结合起来使用。也就是说,Feign发送请求前,会先把请求再经过一层包装,包装成RibbonRequest。 也就是发送请求的另一种实现LoadBalancerFeignClient。 // 把Request包装成RibbonRequest RibbonRequest ribbonRequest = new (this.delegate, request, uriWithoutHost); // 配置超时时间 IClientConfig requestConfig = this.getClientConfig(options, clientName); // 以负载均衡的方式发送请求 return ((RibbonResponse)this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse(); 以负载均衡的方式发送请求 this.IbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig))的具体实现在AbstractLoadBalancerAwareClient类中。 executeWithLoaderBalancer()方法的实现也参考了响应式编程,通过LoadBalancerCommand提交请求,然后使用Observable接收响应信息。 AbstractLoadBalancerAwareClient类的executeWithLoadBalancer()方法的第54行: Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig)); AbstractLoadBalancerAwareClient实现了IClient接口,该接口定义了execute()方法, AbstractLoadBalancerAwareClient.this.execute()的具体实现有很多种: OkHttpLoadBalancingClient RetryableOkHttpLoadBalancingClient RibbonLoadBalancingHttpClient RetryableRibbonLoadBalancingHttpClient 我们以RibbonLoadBalancingHttpClient为例来说明,RibbonLoadBalancingHttpClient.execute() 第62行代码: // 组装HttpUriRequest HttpUriRequest httpUriRequest = request.toRequest(requestConfig); // 发送http请求 HttpResponse httpResponse = ((HttpClient)this.delegate).execute(httpUriRequest); // 使用RibbonApacheHttpResponse包装http响应信息 return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI()); RibbonApacheHttpResponse由2部分组成: httpResponse uri 处理http相应 http请求经过上面一系列的转发以后,最终还会回到SynchronousMethodHandler,然后SynchronousMethodHandler会进行一系列的处理,然后响应到浏览器。 注册Feign客户端bean到IOC容器 查看Feign框架源代码,我们可以发现,FeignClientsRegistar的registerFeignClients()方法完成了feign相关bean的注册。 Feign架构图 第一步:基于JDK动态代理生成代理类。 第二步:根据接口类的注解声明规则,解析出底层MethodHandler 第三步:基于RequestBean动态生成request。 第四步:Encoder将bean包装成请求。 第五步:拦截器负责对请求和返回进行装饰处理。 第六步:日志记录。 第七步:基于重试器发送http请求,支持不同的http框架,默认使用的是HttpUrlConnection。

优秀的个人博客,低调大师

【CI/CD技术专题】「Jenkins实战系列」(3)Jenkinsfile+DockerFile实现自动部署

每日一句 没有人会因学问而成为智者。学问或许能由勤奋得来,而机智与智慧却有懒于天赋。 前提概要 Jenkins下用DockerFile自动部署Java项目,项目的部署放心推向容器化时代机制。 本节需要对jenkinsfile有点了解,对dockerfile有点了解,对shell有点了解,对docker有点了解 环境准备 新建-构建一个Maven项目 需要提一下,现在新安装的没有这个选项,需要在插件里安装一下 Maven Integration java配置,其他配置查看以前文章 发布到服务器,DockerFile部署自动构建 Publish SSH配置 执行流程 jenkins拉取代码仓库中的代码 jenkins执行jenkinsfile文件(可指定文件名) 先在jenkins所在的服务器将拉取的项目build成docker镜像 将镜像发布到镜像仓库 在应用服务器的节点上将该镜像拉取下来(私有仓库需要用户名/密码) 在应用服务器上执行该镜像 jenkins配置 创建一个Pipeline SCM项目 jenkins项目名为sample-tezst (和发布的工程名保持一致) Definition选择 【Pipeline Script from SCM】 Branch Specifier(blank for 'any'):选择分支 Additional Behaviours: 添加邮箱通知的发送者 Script Path: 填写使用项目中的Jenkinsfile文件的名字 应用工程配置 在工程的根目录下创建Dockerfile文件和Jenkins_docker文件 jenkinsfile_docker文件内容如下: docker集成docker部署 pipeline { agent {label 'master'} tools{ maven 'maven' } environment { GIT_PROJECT_ADDR="git@gitee.com:uncleqiao/springboot-test.git" //项目的git地址 JENKINS_WORKSPACE="/root/.jenkins/workspace" //jenkins存放文件的地址 PROJECT_NAME="${JOB_NAME}" // 项目名 JAR_NAME="sample-tezst-0.0.1-SNAPSHOT.jar" // 项目生成的jar的名字 IMAGE_NAME="sample-tezst" // 镜像名一般和项目名相同 IMAGE_ADDR="repository/qiao_namespace/${IMAGE_NAME}" // 镜像的位置 VERSION_ID="${BUILD_ID}" } stages { stage('pullCode'){ steps{ echo 'This is a pullCode step' //git branch: "${BRANCH}", credentialsId: '1001', url: "${GIT_PROJECT_ADDR}" checkout scm } } stage('Build') { steps{ echo 'This is a Build step' // 在有Jenkinsfile同一个目录下(项目的根目录下) sh 'mvn clean package -Dmaven.test.skip=true' } } // 创建目录(如果不存在),并把jar文件上传到该目录下 stage('ssh') { steps{ echo 'push jar to target server' sh ''' ole_image_id=`docker images|grep ${IMAGE_NAME}|grep ${VERSION_ID}|awk '{print $3}'` if [[ -n "${ole_image_id}" ]]; then docker rmi -f ${ole_image_id} fi docker build -f Dockerfile --build-arg jar_name=${JAR_NAME} -t ${IMAGE_NAME}:${VERSION_ID} . new_image_id=`docker images|grep ${IMAGE_NAME}|grep ${VERSION_ID}|awk '{print $3}'` sudo docker tag ${new_image_id} ${IMAGE_ADDR}:${VERSION_ID} sudo docker push ${IMAGE_ADDR}:${VERSION_ID} ''' } } stage('run') { // 在应用服务器节点 test agent { node { label 'test' //customWorkspace "${SERVER_TARGET_PATH}" //此参数会初始化目录 注意填写 } } options { // 不让它切换到节点上自动从仓库拉取项目 skipDefaultCheckout() } steps { echo 'pull image and docker run' withEnv(['JENKINS_NODE_COOKIE=dontKillMe']) { sh ''' sudo docker login --username=yourusername --password=yourpassword ccr.ccs.tencentyun.com sudo docker pull ${IMAGE_ADDR}:${VERSION_ID} container_id=`docker ps|grep ${IMAGE_ADDR}:${VERSION_ID}|awk '{print $1}'` if [ -n "${container_id}" ]; then docker rm -f "${container_id}" fi old_pid=`ps -ef|grep ${JAR_NAME}|grep -v grep|awk '{print $2}'` if [[ -n $old_pid ]]; then kill -9 $old_pid fi old_image=`docker images|grep ${IMAGE_ADDR}|grep ${VERSION_ID}` if [[ -n $old_image ]]; then old_image_id=`echo ${old_image}|awk '{print $3}'` docker rmi -f ${old_image_id} fi sudo docker run --name "${PROJECT_NAME}_${VERSION_ID}" -p 9001:8081 -d ${IMAGE_ADDR}:${VERSION_ID} ''' } } } } } 注意: if [ -n str ] 这个命令在使用的时候如果str为空,也是true,可以使用 if [ -n ${str} -a "${str}x" != "x" ]或者使用if [[ -n ${str} ]]来判断 shell语法中,$()` 和 `` (两个单引号) 可以在子shell中执行命令,但是这儿$()不支持 idea没有jenkinsfile的语法提示,很容易出错,这个很难受 步骤一:拉取项目 在【pullCode】步骤中 checkout scm , 这个scm是创建jenkins流水线时选择的,属于特殊变量,代表了你的仓库信息,自动从仓库中获取项目 步骤二: 【Build】 将拉取下来的项目进行编译并打包 步骤三:【build_image】 将打包好的项目build成docker镜像,然后推送到镜像仓库(这里可以分成两步) 获取已经存在的镜像 ole_image_id=`docker images| grep ${IMAGE_NAME} | grep ${VERSION_ID}|awk '{print $3}'` 删掉存在的镜像 if [[ -n "${ole_image_id}" ]]; then docker rmi -f ${ole_image_id} fi 通过项目根目录下的Dockerfile来构建镜像 -f:[filename] --build-arg <key=value> 给Dockerfile传递参数 -t:镜像的名称:版本 这里我将jenkins的构建次数当作镜像的版本 docker build -f Dockerfile --build-arg jar_name=${JAR_NAME} -t ${IMAGE_NAME}:${VERSION_ID} . 获取构建好的镜像的id new_image_id=`docker images | grep ${IMAGE_NAME} | grep ${VERSION_ID} | awk '{print $3}'` 根据生成的镜像,tag出一个名称空间不同的镜像 sudo docker tag ${new_image_id} repoistory/qiao_namespace/${IMAGE_NAME}:${VERSION_ID} 将镜像仓库能够识别的镜像推送到仓库 sudo docker push repoistory/qiao_namespace/${IMAGE_NAME}:${VERSION_ID} 步骤四:【run】 应用服务器拉取项目并且运行, 这一步要选择节点(应用服务器,如果是集群,就不用节点用scp登陆吧) 登陆到镜像仓库 sudo docker login --username=yourusername --password=yourpassword repository 从仓库中拉取刚才生成的镜像 sudo docker pull repository/qiao_namespace/${IMAGE_NAME}:${VERSION_ID} 查看已经存在的容器, 存在则删掉 存在则删掉 这里应该是一个数组,有点小问题,待修改 container_id=`docker ps | grep ${IMAGE_ADDR}:${VERSION_ID} | awk '{print $1}'` if [ -n "${container_id}" ]; then docker rm -f "${container_id}" fi 查看已经运行的同版本的项目, 存在则删掉 pid=`ps -ef | grep ${JAR_NAME}| grep -v grep | awk '{print $2}'` if [[ -n $pid ]]; then kill -9 $pid fi 查看已经存在的镜像 old_image=`docker images|grep ${IMAGE_ADDR}|grep ${VERSION_ID}` if [[ -n $old_image ]]; then old_image_id=`echo ${old_image}|awk '{print $3}'` docker rmi -f ${old_image_id} fi 运行容器 sudo docker run --name "${PROJECT_NAME}_${VERSION_ID}" -p 9001:8081 -d ${IMAGE_ADDR}:${VERSION_ID} Dockerfile 文件内容如下 FROM repository/qiao_namespace/myjdk8:v1 默认jar包的名字 注意分隔符为 :- 这里是由jenkinsfile中build dockerfile时传递过来的 ARG jar_name={jar_name:-sample-teszt-0.0.1-SNAPSHOT.jar} RUN 用于容器内部执行命令 RUN mkdir -p /usr/local/project WORKDIR /usr/local/project 将项目放到/usr/local/project下 COPY ./target/sample-teszt-0.0.1-SNAPSHOT.jar ./ EXPOSE 8081 CMD java -jar -Dserver.port=8081 springboot-test-0.0.1-SNAPSHOT.jar 执行完毕后我们登陆到应用服务器,看到容器已经启动

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册