{"id":90874,"date":"2020-08-07T01:42:20","date_gmt":"2020-08-06T23:42:20","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds"},"modified":"2020-08-07T01:42:20","modified_gmt":"2020-08-06T23:42:20","slug":"sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds","status":"publish","type":"post","link":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds","title":{"rendered":"Modern Applications on OpenShift, Part 2: Chained Builds","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hello everyone! This is the second post in our series where we demonstrate how to deploy modern web applications on Red Hat OpenShift. <\/p>\n<p><img decoding=\"async\" alt=\"Modern Applications on OpenShift, Part 2: Chained Builds\" src=\"\/wp-content\/uploads\/2020\/08\/b1f6ae855a69597c6e86c5198e4a628c.jpeg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn the previous post, we briefly touched on the capabilities of the new S2I (source-to-image) builder image, which is meant for building and deploying modern web applications on OpenShift. We were interested in the topic of quick application deployment, and today we will explore how to use the S2I image as a 'clean' builder image and combine it with related OpenShift builds.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h3>Clean builder image<\/h3>\n<p>\nAs mentioned in the first part, most modern web applications have what is known as a build stage, where operations such as code transpilation, concatenation of multiple files, and minification typically occur. The files produced from these operations \u2013 namely static HTML, JavaScript, and CSS \u2013 are placed in the output folder. The location of this folder usually depends on the build tools used, and for React, it will be the folder .\/build (we will delve into this issue further below).<\/p>\n<h4>Source-to-Image (S2I)<\/h4>\n<p>\nIn this post, we do not discuss the topic of 'what is S2I and how to use it' (you can read more about it <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.okd.io\/latest\/architecture\/core_concepts\/builds_and_image_streams.html#source-build\">here<\/a><\/noindex>), but it is important to clearly understand two phases of this process to grasp what the Web App Builder image does.<\/p>\n<h4>Assembly phase<\/h4>\n<p>\nThe assembly phase is essentially very similar to what happens when you run docker build and obtain a new Docker image as a result. Accordingly, this phase occurs when a build is triggered on the OpenShift platform.<\/p>\n<p>In the case of the Web App Builder image, the installation of your application's dependencies and the initiation of the build is handled by <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/bucharest-gold\/centos7-s2i-web-app\/blob\/master\/s2i\/assemble#L47\">assemble script<\/a><\/noindex>. By default, the builder image uses the npm run build command, but this can be overridden via the NPM_BUILD environment variable.<\/p>\n<p>As we mentioned earlier, the location of a finished, assembled application depends on the tools being used. For example, in the case of React, it will be the folder .\/build, while for Angular applications, it will be the folder project_name\/dist. And, as shown in the previous post, the location of the output directory, which is set by default to build, can be overridden via the OUTPUT_DIR environment variable. Since the output folder location varies from framework to framework, you simply copy the generated output to the standard folder in the image, specifically to \/opt\/apt-root\/output. This is important for understanding the next part of this article, but for now, let\u2019s quickly review the next stage \u2013 the run phase.<\/p>\n<h4>Run Phase<\/h4>\n<p>\nThis phase occurs when a new image created during the assembly stage is called with docker run. It also happens when deploying on the OpenShift platform. By default, <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/bucharest-gold\/centos7-s2i-web-app\/blob\/master\/s2i\/run\">run script<\/a><\/noindex> use <noindex><a rel=\"nofollow\" href=\"https:\/\/www.npmjs.com\/package\/serve\">serve module<\/a><\/noindex> for serving static content located in the aforementioned standard output directory.<\/p>\n<p>This method is good for quickly deploying applications, but generally, serving static content this way is not recommended. Since we are actually serving only static content, we don't need the Node.js installed inside our image \u2013 a web server will suffice.<\/p>\n<p>In other words, we need one thing at build time and another at runtime. In such cases, chained builds come in handy.<\/p>\n<h3>Chained Builds<\/h3>\n<p>\nHere\u2019s what they say about <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.okd.io\/latest\/dev_guide\/builds\/advanced_build_operations.html#dev-guide-chaining-builds\">chained builds<\/a><\/noindex> in the OpenShift documentation:<\/p>\n<blockquote><p>\"Two builds can be linked together, where one generates a compiled entity and the other places this entity in a separate image that is used to run this entity.\"\n<\/p><\/blockquote>\n<p>\nIn other words, we can use the Web App Builder image to run our build and then use the web server image, specifically NGINX, to serve our content.<\/p>\n<p>This way, we can use the Web App Builder image as a 'clean' builder while maintaining a smaller runtime image.<\/p>\n<p>Now let\u2019s break this down with a concrete example.<\/p>\n<p>For this exercise, we\u2019ll use <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/lholmquist\/react-web-app\">a simple React application<\/a><\/noindex>, created with the create-react-app command-line tool.<\/p>\n<p>To bring everything together, we\u2019ll use the <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/lholmquist\/react-web-app\/blob\/master\/.openshiftio\/application.yaml\">OpenShift template file.<\/a><\/noindex>.<\/p>\n<p>Let's break down this file in more detail, starting with the parameters section.<\/p>\n<pre><code class=\"plaintext\">parameters:\n  - name: SOURCE_REPOSITORY_URL\n    description: The source URL for the application\n    displayName: Source URL\n    required: true\n  - name: SOURCE_REPOSITORY_REF\n    description: The branch name for the application\n    displayName: Source Branch\n    value: master\n    required: true\n  - name: SOURCE_REPOSITORY_DIR\n    description: The location within the source repo of the application\n    displayName: Source Directory\n    value: .\n    required: true\n  - name: OUTPUT_DIR\n    description: The location of the compiled static files from your web apps builder\n    displayName: Output Directory\n    value: build\n    required: false\n<\/code><\/pre>\n<p>\nEverything here is quite clear, but it's worth noting the OUTPUT_DIR parameter. For the React application in our example, there's nothing to worry about since React uses the default value for the output folder, but in the case of Angular or something else, this parameter will need to be changed accordingly.<\/p>\n<p>Now, let's take a look at the ImageStreams section.<\/p>\n<pre><code class=\"plaintext\">- apiVersion: v1\n  kind: ImageStream\n  metadata:\n    name: react-web-app-builder  \/\/ 1 \n  spec: {}\n- apiVersion: v1\n  kind: ImageStream\n  metadata:\n    name: react-web-app-runtime  \/\/ 2 \n  spec: {}\n- apiVersion: v1\n  kind: ImageStream\n  metadata:\n    name: web-app-builder-runtime \/\/ 3\n  spec:\n    tags:\n    - name: latest\n      from:\n        kind: DockerImage\n        name: nodeshift\/ubi8-s2i-web-app:10.x\n- apiVersion: v1\n  kind: ImageStream\n  metadata:\n    name: nginx-image-runtime \/\/ 4\n  spec:\n    tags:\n    - name: latest\n      from:\n        kind: DockerImage\n        name: 'centos\/nginx-112-centos7:latest'\n<\/code><\/pre>\n<p>\nTake a look at the third and fourth images. They are both defined as Docker images, and it is clear where they come from.<\/p>\n<p>The third image is the web-app-builder, and it comes from nodeshift\/ubi8-s2i-web-app with the tag 10.x on <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/r\/nodeshift\/ubi8-s2i-web-app\/\">Docker hub<\/a><\/noindex>.<\/p>\n<p>The fourth is the NGINX image (version 1.12) with the tag latest on <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/r\/centos\/nginx-112-centos7\/\">Docker hub<\/a><\/noindex>.<\/p>\n<p>Now let's look at the first two images. They both start empty and are created only during the build phase. The first image, react-web-app-builder, will be the result of the assembly stage, which combines the web-app-builder-runtime image and our source code. That's why we included '-builder' in the name of this image.<\/p>\n<p>The second image, react-web-app-runtime, will be the result of combining the nginx-image-runtime and some files from the react-web-app-builder image. This image will also be used during deployment and will contain only the web server and the static HTML, JavaScript, and CSS of our application.<\/p>\n<p>Confusing? Let's take a look at the build configurations, and it'll become a bit clearer.<\/p>\n<p>In our template, there are two build configurations. Here\u2019s the first one, and it's quite standard:<\/p>\n<pre><code class=\"plaintext\">  apiVersion: v1\n  kind: BuildConfig\n  metadata:\n    name: react-web-app-builder\n  spec:\n    output:\n      to:\n        kind: ImageStreamTag\n        name: react-web-app-builder:latest \/\/ 1\n    source:   \/\/ 2 \n      git:\n        uri: ${SOURCE_REPOSITORY_URL}\n        ref: ${SOURCE_REPOSITORY_REF}\n      contextDir: ${SOURCE_REPOSITORY_DIR}\n      type: Git\n    strategy:\n      sourceStrategy:\n        env:\n          - name: OUTPUT_DIR \/\/ 3 \n            value: ${OUTPUT_DIR}\n        from:\n          kind: ImageStreamTag\n          name: web-app-builder-runtime:latest \/\/ 4\n        incremental: true \/\/ 5\n      type: Source\n    triggers: \/\/ 6\n    - github:\n        secret: ${GITHUB_WEBHOOK_SECRET}\n      type: GitHub\n    - type: ConfigChange\n    - imageChange: {}\n      type: ImageChange\n<\/code><\/pre>\n<p>\nAs we see, the line marked 1 indicates that the result of this build will be placed in the very image react-web-app-builder, which we saw earlier in the ImageStreams section.<\/p>\n<p>The line marked 2 indicates where to get the code from. In our case, this is a git repository, and the location, ref, and context folder are defined by the parameters we saw above.<\/p>\n<p>The line marked 3 \u2013 we have already seen this in the parameters section. It adds the environment variable OUTPUT_DIR, which in our example equals 'build'.<br \/>\nThe line marked 4 states to use the image web-app-builder-runtime, which we already saw in the ImageStream section.<\/p>\n<p>The line marked 5 indicates that we want to use an incremental build, if the S2I image supports it, and the Web App Builder image does. On the first run, after completing the assembly phase, the image will save the node_modules folder in an archive file. Then, during subsequent runs, the image will simply extract this folder to reduce the build duration.<\/p>\n<p>And finally, the line marked 6 \u2013 this is just a few triggers to make the build run automatically, without manual intervention, when something changes.<\/p>\n<p>Overall, this is quite a standard build configuration.<\/p>\n<p>Now let\u2019s look at the second build configuration. It is very similar to the first one, but there is one important distinction.<\/p>\n<pre><code class=\"plaintext\">apiVersion: v1\n  kind: BuildConfig\n  metadata:\n    name: react-web-app-runtime\n  spec:\n    output:\n      to:\n        kind: ImageStreamTag\n        name: react-web-app-runtime:latest \/\/ 1\n    source: \/\/ 2\n      type: Image\n      images:                              \n        - from:\n            kind: ImageStreamTag\n            name: react-web-app-builder:latest \/\/ 3\n          paths:\n            - sourcePath: \/opt\/app-root\/output\/.  \/\/ 4\n              destinationDir: .  \/\/ 5\n             \n    strategy: \/\/ 6\n      sourceStrategy:\n        from:\n          kind: ImageStreamTag\n          name: nginx-image-runtime:latest\n        incremental: true\n      type: Source\n    triggers:\n    - github:\n        secret: ${GITHUB_WEBHOOK_SECRET}\n      type: GitHub\n    - type: ConfigChange\n    - type: ImageChange\n      imageChange: {}\n    - type: ImageChange\n      imageChange:\n        from:\n          kind: ImageStreamTag\n          name: react-web-app-builder:latest \/\/ 7\n<\/code><\/pre>\n<p>\nSo, the second build configuration \u2013 react-web-app-runtime \u2013 starts off quite standard.<\/p>\n<p>In line 1, there's nothing new \u2013 it simply states that the build result is placed in the react-web-app-runtime image.<\/p>\n<p>Line 2, like in the previous configuration, specifies where to fetch the source code from. However, note that here we indicate that it is taken from the image. Specifically, from the image we just created \u2013 react-web-app-builder (mentioned in line 3). The files we want to use are located inside the image, and their path is specified in line 4, which in our case is \/opt\/app-root\/output\/. If you recall, that\u2019s where the files generated from our application build are placed.<\/p>\n<p>The destination folder specified in line 5 is just the current directory (all of this, remember, runs inside a magical thing called OpenShift, not on your local computer).<\/p>\n<p>The section strategy \u2013 line 6 \u2013 is also similar to the first build configuration. This time, however, we plan to use nginx-image-runtime, which we\u2019ve already seen in the ImageStream section.<\/p>\n<p>Finally, line 7 indicates the trigger section that activates this build every time the react-web-app-builder image changes.<\/p>\n<p>Otherwise, this template contains quite a standard deployment configuration, along with items related to services and routes, but we won\u2019t delve into that. Note that the image to be deployed is the react-web-app-runtime image.<\/p>\n<h3>Deploying the Application<\/h3>\n<p>\nSo, after taking a look at the template, let\u2019s see how to use it for deploying the application.<\/p>\n<p>We can use the OpenShift client tool called oc to deploy our template:<\/p>\n<pre><code class=\"plaintext\">$ find . | grep openshiftio | grep application | xargs -n 1 oc apply -f\n\n$ oc new-app --template react-web-app -p SOURCE_REPOSITORY_URL=https:\/\/github.com\/lholmquist\/react-web-app\n<\/code><\/pre>\n<p>\nThe first command in the screenshot above is a deliberately engineering way to find the template .\/openshiftio\/application.yaml.<\/p>\n<p>The second command simply creates a new application based on this template.<\/p>\n<p>Once these commands have executed, we will see that we have two builds:<\/p>\n<p><img decoding=\"async\" alt=\"Modern Applications on OpenShift, Part 2: Chained Builds\" src=\"\/wp-content\/uploads\/2020\/08\/c6b2b2fcadfcd3e5a8a2adb4eff69bf2.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>And returning to the Overview screen, we will see the started pod:<\/p>\n<p><img decoding=\"async\" alt=\"Modern Applications on OpenShift, Part 2: Chained Builds\" src=\"\/wp-content\/uploads\/2020\/08\/983fab3029d222555dc13790d4a3e70f.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Clicking the link will take us to our application, which is the default React App page:<\/p>\n<p><img decoding=\"async\" alt=\"Modern Applications on OpenShift, Part 2: Chained Builds\" src=\"\/wp-content\/uploads\/2020\/08\/d5b658875e0e865bf456955aaac15464.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<h3>Supplement 1<\/h3>\n<p>\nFor Angular enthusiasts, we also have <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/lholmquist\/angular-web-app\">an application example<\/a><\/noindex>.<\/p>\n<p>The template here is the same, except for the OUTPUT_DIR variable.<\/p>\n<h3>Appendix 2<\/h3>\n<p>\nIn this article, we used NGINX as a web server, but it can be easily replaced with Apache by simply changing it in the template file. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/lholmquist\/react-web-app\/blob\/master\/.openshiftio\/application.yaml#L66\">NGINX image<\/a><\/noindex> to <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/r\/centos\/httpd-24-centos7\/\">Apache image<\/a><\/noindex>.<\/p>\n<h3>Conclusion<\/h3>\n<p>\nIn the first part of this series, we demonstrated how to quickly deploy modern web applications on the OpenShift platform. Today, we explored what makes up the Web App image and how it can be combined with a clean web server like NGINX using chained builds to create a more production-ready application build. In the next, concluding article of this series, we will show how to run a development server for your application on OpenShift and ensure synchronization of local and remote files.<\/p>\n<h3>Contents of this article series<\/h3>\n<p><\/p>\n<ul>\n<li>Part 1: <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/redhatrussia\/blog\/511954\/\">how to deploy modern web applications in just a few steps<\/a><\/noindex>;<\/li>\n<li>Part 2: how to apply a new S2I image alongside an existing HTTP server image, such as NGINX, using OpenShift's chained builds for a production deployment;<\/li>\n<li>Part 3: how to run a development server for your application on the OpenShift platform and synchronize it with the local file system.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Additional resources<\/h3>\n<p><\/p>\n<ul>\n<li>Free eBook <noindex><a rel=\"nofollow\" href=\"https:\/\/developers.redhat.com\/books\/deploying-openshift\">Deploying to OpenShift<\/a><\/noindex>.<\/li>\n<li>Information on <noindex><a rel=\"nofollow\" href=\"https:\/\/developers.redhat.com\/topics\/kubernetes\/\">OpenShift and Kubernetes<\/a><\/noindex>.<\/li>\n<\/ul>\n<p>Source: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/redhatrussia\/blog\/513948\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0421 \u0432\u0430\u043c\u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0441\u0442 \u0438\u0437 \u043d\u0430\u0448\u0435\u0439 \u0441\u0435\u0440\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 Red Hat OpenShift \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043f\u043e\u0441\u0442\u0435 \u043c\u044b \u0441\u043b\u0435\u0433\u043a\u0430 \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u043b\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043d\u043e\u0432\u043e\u0433\u043e builder-\u043e\u0431\u0440\u0430\u0437\u0430 S2I (source-to-image), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0438 \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 OpenShift. \u0422\u043e\u0433\u0434\u0430 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u0430 \u0442\u0435\u043c\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":90875,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-90874","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0421 \u0432\u0430\u043c\u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0441\u0442 \u0438\u0437 \u043d\u0430\u0448\u0435\u0439 \u0441\u0435\u0440\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 Red Hat OpenShift \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 OpenShift, \u0447\u0430\u0441\u0442\u044c 2: \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 chained builds | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0421 \u0432\u0430\u043c\u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0441\u0442 \u0438\u0437 \u043d\u0430\u0448\u0435\u0439 \u0441\u0435\u0440\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 Red Hat OpenShift \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-08-06T23:42:20+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-08-06T23:42:20+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Modern Applications on OpenShift, Part 2: Chained Builds | ProHoster","description":"Hello everyone! This is the second post in our series where we demonstrate how to deploy modern web applications on Red Hat OpenShift.","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 OpenShift, \u0447\u0430\u0441\u0442\u044c 2: \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 chained builds | ProHoster","og:description":"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0421 \u0432\u0430\u043c\u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0441\u0442 \u0438\u0437 \u043d\u0430\u0448\u0435\u0439 \u0441\u0435\u0440\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 Red Hat OpenShift \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.","og:url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sovremennye-prilozheniya-na-openshift-chast-2-svyazannye-sborki-chained-builds","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-08-06T23:42:20+00:00","article:modified_time":"2020-08-06T23:42:20+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"90874","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 12:40:03","updated":"2022-10-04 00:42:31","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/90874","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=90874"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/90874\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/90875"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=90874"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=90874"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=90874"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}