jenkins归档docker镜像内的文件

warning: 这篇文章距离上次修改已过516天,其中的内容可能已经有所变动。

现在jenkisn构建基本上是直接构建Docker 镜像了,很少会构建发布包再去手动发布,但是也会存在这种情况。

但是由于项目的环境不同,如果使用宿主机的环境构建,势必会产生冲突和奇奇怪怪的环境问题。因此:

  1. 使用docker构建
  2. 需要生成可使用的镜像
  3. 需要基于生成的镜像,归档其中的发布文件。

参考资料

插件准备

Pipeline脚本片段

                docker.image("image_name").withRun{
                  archiveArtifacts allowEmptyArchive: true, artifacts: '/app/', followSymlinks: false, onlyIfSuccessful: true
                }

注意事项

这里有一点要注意,之前用了inside来操作,结果jenkins提示说容器没有运行。好奇的看了下文档,inside只是进入容器,但不负责启动容器。如果要基于镜像启动容器的话, 就要用WithRun,或者在build的时候inside,因为build会启动一个容器进行构建。

参考资料:

To run an image you built, or pulled from a registry, you can use the run method. This returns a handle to the running container. More safely, you can use the withRun method, which automatically stops the container at the end of a block:

node {
  git '…'
  docker.image('mysql').withRun {c ->
    sh './test-with-local-db'
  }
}

The above simply starts a container running a test MySQL database and runs a regular build while that container is running. Unlike inside, shell steps inside the block are not run inside the container, but they could connect to it using a local TCP port for example.

You can also access the id of the running container, which is passed as an argument to the block, in case you need to do anything further with it:

// …as above, but also dump logs before we finish:
sh "docker logs ${c.id}"

Like inside, run and withRun record the fact that the build used the specified image.

build:

If your build needs to create a Docker image, use the build method, which takes an image name with an optional tag and creates it from a Dockerfile. This also returns a handle to the result image, so you can work with it further:

node {
  git '…' // checks out Dockerfile & Makefile
  def myEnv = docker.build 'my-environment:snapshot'
  myEnv.inside {
    sh 'make test'
  }
}

Here the build method takes a Dockerfile in your source tree specifying a build environment (for example RUN apt-get install -y libapr1-dev). Then a Makefile in the same source tree describes how to build your actual project in that environment.

If you want to publish a newly created image to Docker Hub (or your own registry—discussed below), use push:

node {
  git '…' // checks out Dockerfile and some project sources
  def newApp = docker.build "mycorp/myapp:${env.BUILD_TAG}"
  newApp.push()
}

Here we are giving the image a tag which identifies the Jenkins project and build number that created it. (See the documentation for the env global variable.) The image is pushed under this tag name to the registry.

To push an image into a staging or production environment, a common style is to update a predefined tag such as latest in the registry. In this case just specify the tag name:

node {
  stage 'Building image'
  git '…'
  def newApp = docker.build "mycorp/myapp:${env.BUILD_TAG}"
  newApp.push() // record this snapshot (optional)
  stage 'Test image'
  // run some tests on it (see below), then if everything looks good:
  stage 'Approve image'
  newApp.push 'latest'
}

The build method records information in Jenkins tied to this project build: what image was built, and what image that was derived from (the FROM instruction at the top of your Dockerfile). Other plugins can then identify the build which created an image known to have been used by a downstream build, or deployed to a particular environment. You can also have this project be triggered when an update is pushed to the ancestor image (FROM) in a registry.

后续

发现还是不能运行,一直提示:

java.io.IOException: Failed to run top '38232159f4812b77efb9f3c5756028aab8d21dab838839e86edc7c4b3fc0095e'. Error: Error response from daemon: Container 38232159f4812b77efb9f3c5756028aab8d21dab838839e86edc7c4b3fc0095e is not running

刚开始一直很奇怪, 为什么没运行,明明都可以看到容器了啊。突然看到状态是退出,突然想起来,这是部署用的镜像啊,没有配置文件挂载进去是跑不起来的!

而且由于启动后会连接数据库,也不太适合构造一个配置文件。

解决思路

既然没有办法直接用构建出来的镜像的话, 那就利用缓存layer来构建一个用于归档的镜像吧,因为有缓存层了, 应该不需要太多操作。

脚本:

          stage('归档文件') {
            steps {
              script {
                def buildContainer = docker.build("${DOCKER_REPOSITORY_NAME}:${IMAGE_NAME}-build", "-f Build.Dockerfile .")
                buildContainer.inside() {
                  sh "ls -lh"
                  sh "pwd"
                  archiveArtifacts allowEmptyArchive: true, artifacts: '/app/*', fingerprint: true, onlyIfSuccessful: true
                }
              }
            }
          }

通过ls命令可以看到构建出来的app文件夹了,不出意外的,就要出意外了,最后提示:

Archiving artifacts
‘/app/*’ doesn’t match anything, but ‘*’ does. Perhaps that’s what you mean?
No artifacts found that match the file pattern "/app/*". Configuration error?

嗯???似乎有点完全摸不着头脑了。突然想起inside的原理其实就是把目录挂载到容器里去了,但是并不是所有的命令都是在容器里执行。

往前翻一下挂载的命令:

docker run -t -d -u 116:120 -w /var/lib/jenkins/workspace/API_master -v /var/lib/jenkins/workspace/API_master:/var/lib/jenkins/workspace/API_master:rw,z -v /var/lib/jenkins/workspace/API_master@tmp:/var/lib/jenkins/workspace/API_master@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** teenagers-docker.pkg.coding.net/kaiyuanxilie/syyc/api:master-30-build cat

嗯, 只挂载了工作空间,而/app是容器里的目录,自然是找不到的。那么接下来就简单了,把文件移过去就好了!

脚本:

          stage('归档文件') {
            steps {
              script {
                def buildContainer = docker.build("${DOCKER_REPOSITORY_NAME}:${IMAGE_NAME}-build", "-f Build.Dockerfile .")
                buildContainer.inside() {
                  sh "ls -lh"
                  sh "pwd"
                  sh "cp -r -f /app publish"
                  archiveArtifacts allowEmptyArchive: true, artifacts: 'publish/', fingerprint: true, onlyIfSuccessful: true
                }
              }
            }
          }

搞定。

这里要注意一下就是要清理一下这个publish的临时文件(好像jenkins每次都会清理掉改动,但是养成良好习惯比较好的)

none
最后修改于:2022年12月20日 22:45

评论已关闭