你讓,勛爵? 使用Jenkins聲明性管道的Docker中的Docker

Resources. When they are unlimited they are not important. But when they're limited, boy do you have challenges!

資源。 當它們不受限制時,它們并不重要。 但是,當他們受到限制時,男孩你有挑戰!

Recently, my team has faced such a challenge ourselves: we realised that we needed to upgrade the Node version on one of our Jenkins agents so we could build and properly test our Angular 7 app. However, we learned that we would also lose the ability to build our legacy AngularJS apps which require Node 8.

最近,我的團隊自己面對了這樣的挑戰:我們意識到我們需要在我們的Jenkins代理之一上升級Node版本,以便我們可以構建和正確測試Angular 7應用程序。 但是,我們了解到,我們還將失去構建需要Node 8的舊版AngularJS應用程序的能力。

What were we to do?

我們該怎么辦?

Apart from eliminating the famous "It works on my machine" problem, Docker came in handy to tackle such a problem. However, there were certain challenges that needed to be addressed, such as Docker in Docker.

除了消除著名的“它可以在我的機器上工作”問題之外,Docker還可以方便地解決此類問題。 但是,有一些挑戰需要解決,例如Docker中的Docker。

For this purpose, after a long period of trial and error, we built and published a docker file that fit our team's needs. It helps run our builds, and it looks like the following:

為此,經過長期的反復試驗,我們構建并發布了適合我們團隊需求的docker文件 。 它有助于運行我們的構建,如下所示:

1. Install dependencies
2. Lint the code
3. Run unit tests
4. Run SonarQube analysis
5. Build the application
6. Build a docker image which would be deployed
7. Run the docker container
8. Run cypress tests
9. Push docker image to the repository
10. Run another Jenkins job to deploy it to the environment
11. Generate unit and functional test reports and publish them
12. Stop any running containers
13. Notify chat/email about the build

我們需要的Docker映像 (The docker image we needed)

Our project is an Angular 7 project, which was generated using the angular-cli. We also have some dependencies that need Node 10.x.x. We lint our code with tslint, and run our unit tests with Karma and Jasmine. For the unit tests we need a Chrome browser installed so they can run with headless Chrome.

我們的項目是Angular 7項目,它是使用angular-cli生成的。 我們還有一些需要Node 10.xx的依賴項。我們使用tslint代碼,并使用KarmaJasmine運行單元測試。 對于單元測試,我們需要安裝Chrome瀏覽器,以便它們可以與無頭Chrome一起運行。

This is why we decided to use the cypress/browsers:node10.16.0-chrome77 image. After we installed the dependencies, linted our code and ran our unit tests, we ran the SonarQube analysis. This required us to have Openjdk 8 as well.

這就是為什么我們決定使用cypress/browsers:node10.16.0-chrome77圖像的原因。 在安裝依賴項,簡化代碼并運行單元測試之后,我們運行了SonarQube分析。 這要求我們也有Openjdk 8

FROM cypress/browsers:node10.16.0-chrome77# Install OpenJDK-8
RUN apt-get update && \apt-get install -y openjdk-8-jdk && \apt-get install -y ant && \apt-get clean;# Fix certificate issues
RUN apt-get update && \apt-get install ca-certificates-java && \apt-get clean && \update-ca-certificates -f;# Setup JAVA_HOME -- useful for docker commandline
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/
RUN export JAVA_HOME

Once the sonar scan was ready, we built the application. One of the strongest principles in testing is that you should test the thing that will be used by your users.That is the reason that we wanted to test the built code in exactly the same docker container as it would be in production.

聲納掃描準備就緒后,我們就構建了該應用程序。 測試中最強大的原則之一就是您應該測試將由用戶使用的東西,這就是我們想要在與生產環境完全相同的Docker容器中測試構建代碼的原因。

We could, of course serve the front-end from a very simple nodejs static server.But that would mean that everything an Apache HTTP server or an NGINX server usually did would be missing (for example all the proxies, gzip or brotli).

我們當然可以通過非常簡單的nodejs靜態服務器為前端服務, nodejs意味著Apache HTTP服務器或NGINX服務器通常所做的一切都將丟失(例如所有代理, gzipbrotli )。

Now while this is a strong principle, the biggest problem was that we were already running inside a Docker container. That is why we needed DIND (Docker in Docker).

現在,盡管這是一個很強的原則,但最大的問題是我們已經在Docker容器中運行。 這就是為什么我們需要DIND(Docker中的Docker)的原因。

After spending a whole day with my colleague researching, we found a solution which ended up working like a charm. The first and most important thing is that our build container needed the Docker executable.

在與同事一起研究了一整天之后,我們找到了一個最終成功的解決方案。 首先也是最重要的一點是,我們的構建容器需要Docker可執行文件。

# Install Docker executable
RUN apt-get update && apt-get install -y \apt-transport-https \ca-certificates \curl \gnupg2 \software-properties-common \&& curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \&& add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/debian \$(lsb_release -cs) \stable" \&& apt-get update \&& apt-get install -y \docker-ceRUN usermod -u 1002 node && groupmod -g 1002 node && gpasswd -a node docker

As you can see we installed the docker executable and the necessary certificates, but we also added the rights and groups for our user. This second part is necessary because the host machine, our Jenkins agent, starts the container with -u 1002:1002. That is the user ID of our Jenkins agent which runs the container unprivileged.

如您所見,我們安裝了docker可執行文件和必要的證書,但是我們還為用戶添加了權限和組。 第二部分是必需的,因為主機(我們的Jenkins代理)使用-u 1002:1002啟動容器。 這是我們的Jenkins代理的用戶ID,該代理以無特權的方式運行容器。

Of course this isn't everything. When the container starts, the docker daemon of the host machine must be mounted. So we needed to start the build containerwith some extra parameters. It looks like the following in a Jenkinsfile:

當然,這還不是全部。 容器啟動時,必須掛載主機的docker守護程序。 因此,我們需要使用一些額外的參數來啟動構建容器。 Jenkins文件中的內容如下所示:

pipeline {agent {docker {image 'btapai/pipelines:node-10.16.0-chrome77-openjdk8-CETtime-dind'label 'frontend'args '-v /var/run/docker.sock:/var/run/docker.sock -v /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket -e HOME=${workspace} --group-add docker'}}// ...
}

As you can see, we mounted two Unix sockets. /var/run/docker.sock mounts the docker daemon to the build container.

如您所見,我們安裝了兩個Unix套接字。 /var/run/docker.sock將Docker守護程序掛載到構建容器。

/var/run/dbus/system_bus_socket is a socket that allows cypress to run inside our container.

/var/run/dbus/system_bus_socket是一個套接字,可讓cypress在我們的容器內運行。

We needed -e HOME=${workspace} to avoid access rights issues during the build.

我們需要-e HOME=${workspace}以避免在構建期間出現訪問權限問題。

--group-add docker passes the host machines docker group down, so that inside the container our user can use the docker daemon.

--group-add docker向下傳遞主機docker組,以便我們的用戶可以在容器內使用docker守護程序。

With these proper arguments, we were able to build our image, start it up and run our cypress tests against it.

有了這些適當的論據,我們就能建立自己的形象,啟動它并對其進行賽普拉斯測試。

But let's take a deep breath here. In Jenkins, we wanted to use multi-branch pipelines. Multibranch pipelines in Jenkins would create a Jenkins job for each branch that contained a Jenkinsfile. This meant that when we developed multiple branches they would have their own views.

但是,讓我們在這里深呼吸。 在詹金斯,我們想使用多分支管道。 Jenkins中的多分支管道會為每個包含Jenkinsfile的分支創建一個Jenkins作業。 這意味著當我們開發多個分支機構時,它們將擁有自己的視圖。

There were some problems with this. The first problem was that if we built our image with the same name in all the branches, there would be conflicts (since our docker daemon was technically not inside our build container).

這有一些問題。 第一個問題是,如果我們在所有分支中都使用相同的名稱構建映像,則會發生沖突(因為從技術上講,docker守護程序不在構建容器內)。

The second problem arose when the docker run command used the same port in every build (because you can't start the second container on a port that is already taken).

當docker run命令在每個構建版本中使用相同的端口時,會出現第二個問題(因為您無法在已占用的端口上啟動第二個容器)。

The third issue was getting the proper URL for the running application, because Dorothy, you are not in Localhost anymore.

第三個問題是為正在運行的應用程序獲取正確的URL,因為Dorothy,您不再位于Localhost中。

Let's start with the naming. Getting a unique name is pretty easy with git, because commit hashes are unique. However, to get a unique port we had to use a little trick when we declared our environment variables:

讓我們從命名開始。 使用git獲得唯一的名稱非常容易,因為提交哈希是唯一的。 但是,要獲得唯一的端口,我們在聲明環境變量時必須使用一些技巧:

pipeline {// ..environment {BUILD_PORT = sh(script: 'shuf -i 2000-65000 -n 1',returnStdout: true).trim()}// ...stage('Functional Tests') {steps {sh "docker run -d -p ${BUILD_PORT}:80 --name ${GIT_COMMIT} application"// be patient, we are going to get the url as well. :)}}// ...}

With the shuf -i 2000-65000 -n 1 command on certain Linux distributions you can generate a random number. Our base image uses Debian so we were lucky here.The GIT_COMMIT environment variable was provided in Jenkins via the SCM plugin.

在某些Linux發行版中,使用shuf -i 2000-65000 -n 1命令可以生成一個隨機數。 我們的基本映像使用Debian,因此我們很幸運GIT_COMMIT環境變量是通過SCM插件在Jenkins中提供的。

Now came the hard part: we were inside a docker container, there was no localhost, and the network inside docker containers can change.

現在最困難的部分是:我們在docker容器內,沒有本地主機,并且docker容器內的網絡可以更改。

It was also funny that when we started our container, it was running on the host machine's docker daemon. So technically it was not running inside our container. We had to reach it from the inside.

有趣的是,當我們啟動容器時,它正在主機的docker守護程序上運行。 因此,從技術上講,它不在容器內運行。 我們必須從內部到達它。

After several hours of investigation my colleague found a possible solution:docker inspect --format "{{ .NetworkSettings.IPAddress }}"

經過數小時的調查,我的同事找到了一個可能的解決方案: docker inspect --format "{{ .NetworkSettings.IPAddress }}"

But it did not work, because that IP address was not an IP address inside the container, but rather outside it.

但這沒有用,因為該IP地址不是容器內部的IP地址,而是容器外部的IP地址。

Then we tried the NetworkSettings.Gateway property, which worked like a charm.So our Functional testing stage looked like the following:

然后我們嘗試了NetworkSettings.Gateway屬性,該屬性像一個超級按鈕一樣工作,因此我們的功能測試階段如下所示:

stage('Functional Tests') {steps {sh "docker run -d -p ${BUILD_PORT}:80 --name ${GIT_COMMIT} application"sh 'npm run cypress:run -- --config baseUrl=http://`docker inspect --format "{{ .NetworkSettings.Gateway }}" "${GIT_COMMIT}"`:${BUILD_PORT}'}
}

It was a wonderful feeling to see our cypress tests running inside a docker container.

看到我們的cypress測試在docker容器中運行是一種很棒的感覺。

But then some of them failed miserably. Because the failing cypress tests expected to see some dates.

但是,其中一些失敗了。 因為失敗的柏樹測試預計會看到一些日期。

cy.get("created-date-cell").should("be.visible").and("contain", "2019.12.24 12:33:17")

But because our build container was set to a different timezone, the displayed date on our front-end was different.

但是因為我們的構建容器設置為不同的時區,所以前端顯示的日期不同。

Fortunately, it was an easy fix, and my colleague had seen it before. We installed the necessary time zones and locales. In our case we set the build container's timezone to Europe/Budapest, because our tests were written in this timezone.

幸運的是,這很容易解決,而我的同事以前見過。 我們安裝了必要的時區和語言環境。 在我們的案例中,我們將構建容器的時區設置為Europe/Budapest ,因為我們的測試是在該時區編寫的。

# SETUP-LOCALE
RUN apt-get update \&& apt-get install --assume-yes --no-install-recommends locales \&& apt-get clean \&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \&& sed -i -e 's/# hu_HU.UTF-8 UTF-8/hu_HU.UTF-8 UTF-8/' /etc/locale.gen \&& locale-genENV LANG="en_US.UTF-8" \LANGUAGE= \LC_CTYPE="en_US.UTF-8" \LC_NUMERIC="hu_HU.UTF-8" \LC_TIME="hu_HU.UTF-8" \LC_COLLATE="en_US.UTF-8" \LC_MONETARY="hu_HU.UTF-8" \LC_MESSAGES="en_US.UTF-8" \LC_PAPER="hu_HU.UTF-8" \LC_NAME="hu_HU.UTF-8" \LC_ADDRESS="hu_HU.UTF-8" \LC_TELEPHONE="hu_HU.UTF-8" \LC_MEASUREMENT="hu_HU.UTF-8" \LC_IDENTIFICATION="hu_HU.UTF-8" \LC_ALL=# SETUP-TIMEZONE
RUN apt-get update \&& apt-get install --assume-yes --no-install-recommends tzdata \&& apt-get clean \&& echo 'Europe/Budapest' > /etc/timezone && rm /etc/localtime \&& ln -snf /usr/share/zoneinfo/'Europe/Budapest' /etc/localtime \&& dpkg-reconfigure -f noninteractive tzdata

Since every crucial part of the build was now resolved, pushing the built image to the registry was just a docker push command. You can check out the whole dockerfile here.

由于現在解決了構建的每個關鍵部分,因此將構建的映像推送到注冊表只是一個docker push命令。 您可以在此處檢出整個dockerfile。

One thing remained, which was to stop running containers when the cypress tests failed. We did this easily using the always post step.

剩下的一件事是,當柏樹測試失敗時,停止運行容器。 我們使用always post步驟輕松做到了這一點。

post {always {script {try {sh "docker stop ${GIT_COMMIT} && docker rm ${GIT_COMMIT}"} catch (Exception e) {echo 'No docker containers were running'}}}
}

Thank you very much for reading this blog post. I hope it helps you.

非常感謝您閱讀此博客文章。 希望對您有幫助。

The original article can be read on my blog:

原始文章可以在我的博客上閱讀:

翻譯自: https://www.freecodecamp.org/news/you-rang-mlord-docker-in-docker-with-jenkins-declarative-pipelines/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/390759.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390759.shtml
英文地址,請注明出處:http://en.pswp.cn/news/390759.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

翻譯(九)——Clustered Indexes: Stairway to SQL Server Indexes Level 3

原文鏈接:www.sqlservercentral.com/articles/StairwaySeries/72351/ Clustered Indexes: Stairway to SQL Server Indexes Level 3 By David Durant, 2013/01/25 (first published: 2011/06/22) The Series 本文是階梯系列的一部分:SQL Server索引的階梯…

power bi 中計算_Power BI中的期間比較

power bi 中計算Just recently, I’ve come across a question on the LinkedIn platform, if it’s possible to create the following visualization in Power BI:就在最近,我是否在LinkedIn平臺上遇到了一個問題,是否有可能在Power BI中創建以下可視化…

-Hive-

Hive定義 Hive 是一種數據倉庫技術,用于查詢和管理存儲在分布式環境下的大數據集。構建于Hadoop的HDFS和MapReduce上,用于管理和查詢分析結構化/非結構化數據的數據倉庫; 使用HQL(類SQL語句)作為查詢接口;使用HDFS作…

CentOS 7 安裝 JDK

2019獨角獸企業重金招聘Python工程師標準>>> 1、下載oracle jdk 下載地址: http://www.oracle.com/technetwork/java/javase/downloads/index.html 選擇同一協議,下載rpm格式版本jdk,或tar.gz格式jdk。 2、卸載本機openjdk 2.1、查…

javascript 布爾_JavaScript布爾說明-如何在JavaScript中使用布爾

javascript 布爾布爾型 (Boolean) Booleans are a primitive datatype commonly used in computer programming languages. By definition, a boolean has two possible values: true or false.布爾值是計算機編程語言中常用的原始數據類型。 根據定義,布爾值有兩個…

如何進行數據分析統計_對您不了解的數據集進行統計分析

如何進行數據分析統計Recently, I took the opportunity to work on a competition held by Wells Fargo (Mindsumo). The dataset provided was just a bunch of numbers in various columns with no indication of what the data might be. I always thought that the analys…

經典:區間dp-合并石子

題目鏈接 :http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid737 這個動態規劃的思是,要得出合并n堆石子的最優答案可以從小到大枚舉所有石子合并的最優情況,例如要合并5堆石子就可以從,最優的23和14中得到最佳的答案。從兩堆…

常見排序算法_解釋的算法-它們是什么以及常見的排序算法

常見排序算法In its most basic form, an algorithm is a set of detailed step-by-step instructions to complete a task. For example, an algorithm to make coffee in a french press would be:在最基本的形式中,算法是一組完成任務的詳細分步說明。 例如&…

020-Spring Boot 監控和度量

一、概述 通過配置使用actuator查看監控和度量信息 二、使用 2.1、建立web項目&#xff0c;增加pom <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> 啟動項目&a…

matplotlib布局_Matplotlib多列,行跨度布局

matplotlib布局For Visualization in Python, Matplotlib library has been the workhorse for quite some time now. It has held its own even after more nimble rivals with easier code interface and capabilities like seaborn, plotly, bokeh etc. have arrived on the…

Hadoop生態系統

大數據架構-Lambda Lambda架構由Storm的作者Nathan Marz提出。旨在設計出一個能滿足實時大數據系統關鍵特性的架構&#xff0c;具有高容錯、低延時和可擴展等特性。Lambda架構整合離線計算和實時計算&#xff0c;融合不可變性&#xff08;Immutability&#xff09;&#xff0c…

javascript之 原生document.querySelector和querySelectorAll方法

querySelector和querySelectorAll是W3C提供的 新的查詢接口&#xff0c;其主要特點如下&#xff1a; 1、querySelector只返回匹配的第一個元素&#xff0c;如果沒有匹配項&#xff0c;返回null。 2、querySelectorAll返回匹配的元素集合&#xff0c;如果沒有匹配項&#xff0c;…

RDBMS數據定時采集到HDFS

[toc] RDBMS數據定時采集到HDFS 前言 其實并不難&#xff0c;就是使用sqoop定時從MySQL中導入到HDFS中&#xff0c;主要是sqoop命令的使用和Linux腳本的操作這些知識。 場景 在我們的場景中&#xff0c;需要每天將數據庫中新增的用戶數據采集到HDFS中&#xff0c;數據庫中有tim…

單詞嵌入_神秘的文本分類:單詞嵌入簡介

單詞嵌入Natural language processing (NLP) is an old science that started in the 1950s. The Georgetown IBM experiment in 1954 was a big step towards a fully automated text translation. More than 60 Russian sentences were translated into English using simple…

使用Hadoop所需要的一些Linux基礎

Linux 概念 Linux 是一個類Unix操作系統&#xff0c;是 Unix 的一種&#xff0c;它 控制整個系統基本服務的核心程序 (kernel) 是由 Linus 帶頭開發出來的&#xff0c;「Linux」這個名稱便是以 「Linus’s unix」來命名的。 Linux泛指一類操作系統&#xff0c;具體的版本有&a…

python多項式回歸_Python從頭開始的多項式回歸

python多項式回歸Polynomial regression in an improved version of linear regression. If you know linear regression, it will be simple for you. If not, I will explain the formulas here in this article. There are other advanced and more efficient machine learn…

《Linux命令行與shell腳本編程大全 第3版》Linux命令行---4

以下為閱讀《Linux命令行與shell腳本編程大全 第3版》的讀書筆記&#xff0c;為了方便記錄&#xff0c;特地與書的內容保持同步&#xff0c;特意做成一節一次隨筆&#xff0c;特記錄如下&#xff1a; 《Linux命令行與shell腳本編程大全 第3版》Linux命令行--- Linux命令行與she…

徹底搞懂 JS 中 this 機制

徹底搞懂 JS 中 this 機制 摘要&#xff1a;本文屬于原創&#xff0c;歡迎轉載&#xff0c;轉載請保留出處&#xff1a;https://github.com/jasonGeng88/blog 目錄 this 是什么this 的四種綁定規則綁定規則的優先級綁定例外擴展&#xff1a;箭頭函數this 是什么 理解this之前&a…

?如何在2分鐘內將GraphQL服務器添加到RESTful Express.js API

You can get a lot done in 2 minutes, like microwaving popcorn, sending a text message, eating a cupcake, and hooking up a GraphQL server.您可以在2分鐘內完成很多工作&#xff0c;例如微波爐爆米花&#xff0c;發送短信&#xff0c; 吃蛋糕以及連接GraphQL服務器 。 …

leetcode 1744. 你能在你最喜歡的那天吃到你最喜歡的糖果嗎?

給你一個下標從 0 開始的正整數數組 candiesCount &#xff0c;其中 candiesCount[i] 表示你擁有的第 i 類糖果的數目。同時給你一個二維數組 queries &#xff0c;其中 queries[i] [favoriteTypei, favoriteDayi, dailyCapi] 。 你按照如下規則進行一場游戲&#xff1a; 你…