jenkins归档docker镜像内的文件
现在jenkisn构建基本上是直接构建Docker 镜像了,很少会构建发布包再去手动发布,但是也会存在这种情况。
但是由于项目的环境不同,如果使用宿主机的环境构建,势必会产生冲突和奇奇怪怪的环境问题。因此:
- 使用docker构建
- 需要生成可使用的镜像
- 需要基于生成的镜像,归档其中的发布文件。
参考资料
插件准备
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 thewithRun
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
andwithRun
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 aDockerfile
. 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 aDockerfile
in your source tree specifying a build environment (for exampleRUN apt-get install -y libapr1-dev
). Then aMakefile
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 (theFROM
instruction at the top of yourDockerfile
). 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每次都会清理掉改动,但是养成良好习惯比较好的)
评论已关闭