如何在没有from标志的情况下编写多阶段Dockerfile



这实际上是我今天问的这个问题的延续。

我有一个使用--from标志的多阶段Dockerfile:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1-sdk
WORKDIR /app
COPY . ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out
FROM docker.m.our-intra.net/microsoft/dotnet:2.1.4-aspnetcore-runtime
WORKDIR /app
COPY --from=docker.m.our-intra.net/microsoft/dotnet:2.1-sdk /app/aspnetapp/MyProject.WebApi/out ./
ENTRYPOINT ["dotnet", "MyProject.WebApi.dll"]

在这个文件的帮助下,我能够在本地成功地构建图像。

但是我不能在Jenkins管道中使用这个Dockerfile,因为Jenkins Server引擎的版本低于17.05,而且它不会更新(可能稍后但现在不会更新(。

我对Docker和Jenkins的东西很陌生。如果有人能帮助我修改Dockerfile,使我可以在没有--from标志的情况下使用它,我将不胜感激。

更新:

上面提到的Dockerfile是错误的。Dockerfile的工作版本如下:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1-sdk AS build
WORKDIR /app
COPY . ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out
FROM docker.m.our-intra.net/microsoft/dotnet:2.1.4-aspnetcore-runtime AS runtime
WORKDIR /app
COPY --from=build /app/aspnetapp/MyProject.WebApi/out ./
ENTRYPOINT ["dotnet", "MyProject.WebApi.dll"]

更新2:

我正试着听从卡洛斯的建议,现在我有两份docker文件。

这是我的Docker-build:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1-sdk
WORKDIR /app
COPY . ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out

这是我的Dockerfile:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1.4-aspnetcore-runtime
COPY . .
ENTRYPOINT ["dotnet", "MyProject.WebApi.dll"]

这是我的Jenkinsfile:

def docker_repository_url = 'docker.m.our-intra.net'
def artifact_group = 'some-artifact-group'                  
def artifact_name = 'my-service-api'
pipeline {
agent {
label 'build'
}
stages {
stage('Checkout') {
steps {
script {
echo 'Checkout...'
checkout scm
echo 'Checkout Completed'
}
}
}
stage('Build') {
steps {
script {
echo 'Build...'
sh 'docker version'
sh 'docker build -t fact:v${BUILD_NUMBER} -f Dockerfile-build .'
echo 'Build Completed'
}               
}
}
stage('Extract artifact') {
steps {
script {
echo 'Extract...'
sh 'docker create --name build-stage-container fact:v${BUILD_NUMBER}'
sh 'docker cp build-stage-container:/app/aspnetapp/MyProject.WebApi/out .'
sh 'docker rm -f build-stage-container'
echo 'Extract Completed'
}               
}
}
stage('Copy compiled artifact') {
steps {
script {
echo 'Copy artifact...'
sh "docker build -t ${docker_repository_url}/${artifact_group}/${artifact_name}:v${BUILD_NUMBER} -f Dockerfile ."
echo 'Copy artifact Completed'
}               
}
}
stage('Push image') {
steps {
script {                                                    
withCredentials([[
$class: 'UsernamePasswordMultiBinding', 
credentialsId: 'jenkins',
usernameVariable: 'USERNAME', 
passwordVariable: 'PASSWORD'
]]) {
def username = env.USERNAME
def password = env.PASSWORD
echo 'Login...'
sh "docker login ${docker_repository_url} -u ${username} -p ${password}"
echo 'Login Successful' 
echo 'Push image...'
sh "docker push ${docker_repository_url}/${artifact_group}/${artifact_name}:v${BUILD_NUMBER}"   
echo 'Push image Completed'
}                               
}               
}
}
}
}

所有步骤都成功了,但当我尝试在本地运行映像(从Maven中提取映像后(或在OpehShift集群上运行映像时,它失败了,并说:

您的意思是运行dotnet SDK命令吗?请从以下位置安装dotnet SDK:http://go.microsoft.com/fwlink/?LinkID=798306&clcid=0x409

我做错了什么?

TL;DR:您需要自己在Docker 之外复制底层功能

首先,您使用了错误的--from选项。要从以前的构建阶段复制,您必须参考其索引或名称,例如:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1-sdk
...
FROM docker.m.our-intra.net/microsoft/dotnet:2.1.4-aspnetcore-runtime
COPY --from=0 /app/aspnetapp/MyProject.WebApi/out ./

FROM docker.m.our-intra.net/microsoft/dotnet:2.1-sdk AS build-stage
...
FROM docker.m.our-intra.net/microsoft/dotnet:2.1.4-aspnetcore-runtime
COPY --from=build-stage /app/aspnetapp/MyProject.WebApi/out ./

对于您当前的Dockerfile,它会尝试从上游docker映像复制文件,而不是从上一个构建阶段复制。

其次,17.05之前的版本不能进行多阶段Docker构建。您需要自己在Docker之外复制底层功能。

为此,您可以使用一个Dockerfile来构建工件,并基于该图像运行一个一次性容器,从中提取工件。您不需要运行容器,只需使用docker create创建即可(这将创建可写容器层(:

docker create --name build-stage-container build-stage-image
docker cp build-stage-container:/app/aspnetapp/MyProject.WebApi/out .

然后,您可以使用第二个Dockerfile来构建一个镜像,复制从上一阶段提取的工件,并从构建上下文中使用一个简单的COPY

@Carlos的答案完全有效。然而,当您使用jenkins和pipeline时,您可能会对以下替代解决方案感到满意:

如果您在kubernetes发行版上使用带有动态pod配置的jenkins,您可以执行以下操作:

  • 使用基于<registry>/microsoft/dotnet:2.1-sdk的构建pod模板。以常规的dotnet方式在该pod中编译您的应用程序
  • 保留Dockerfile的第二部分,但只需将编译后的工件复制到docker映像中即可

总之,您将Dockerfile的第一部分移到Jenkinsfile中以进行应用程序构建。第二部分仍然是从已经编译的二进制文件中进行docker构建。

Jenkinsfile看起来像这样:

podTemplate(
...,
containers: ['microsoft/dotnet:2.1-sdk', 'docker:1.13.1'],
...
) {
container('microsoft/dotnet:2.1-sdk') {
stage("Compile Code") {
sh "dotnet restore"
sh "dotnet publish -c Release -o out"
}
}
container('docker:1.13.1') {
stage("Build Docker image") {
docker.build("mydockerimage:1.0")
}
}
}

这个Jenkinsfile还远远不够完整,只是说明了它是如何工作的。点击此处查找更多文档:

Jenkins kubernetes插件

脚本管道中的Jenkins-docker全局变量

这是我的最终工作解决方案。

Docker-build:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1-sdk
WORKDIR /app
COPY . ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out

Dockerfile:

FROM docker.m.our-intra.net/microsoft/dotnet:2.1.4-aspnetcore-runtime
ADD output/out /output
WORKDIR /output
ENTRYPOINT ["dotnet", "MyProject.WebApi.dll"]

Jenkinsfile:

def docker_repository_url = 'docker.m.our-intra.net'
def artifact_group = 'some-artifact-group'                  
def artifact_name = 'my-service-api'
pipeline {
agent {
label 'build'
}
stages {
stage('Checkout') {
steps {
script {
echo 'Checkout...'
checkout scm
echo 'Checkout Completed'
}
}
}
stage('Build') {
steps {
script {
echo 'Build...'
sh 'docker version'
sh "docker build -t sometag:v${BUILD_NUMBER} -f Dockerfile-build ."
echo 'Build Completed'
}               
}
}
stage('Extract artifact') {
steps {
script {
echo 'Extract...'
sh "docker run -d --name build-stage-container sometag:v${BUILD_NUMBER}"
sh 'mkdir output'
sh 'docker cp build-stage-container:/app/aspnetapp/MyProject.WebApi/out output'
sh 'docker rm -f build-stage-container'
sh "docker rmi -f sometag:v${BUILD_NUMBER}"
echo 'Extract Completed'
}               
}
}
stage('Copy compiled artifact') {
steps {
script {
echo 'Copy artifact...'
sh "docker build -t ${docker_repository_url}/${artifact_group}/${artifact_name}:v${BUILD_NUMBER} -f Dockerfile ."
echo 'Copy artifact Completed'
}               
}
}
stage('Push image') {
steps {
script {                                                    
withCredentials([[
$class: 'UsernamePasswordMultiBinding', 
credentialsId: 'jenkins',
usernameVariable: 'USERNAME', 
passwordVariable: 'PASSWORD'
]]) {
def username = env.USERNAME
def password = env.PASSWORD
echo 'Login...'
sh "docker login ${docker_repository_url} -u ${username} -p ${password}"
echo 'Login Successful' 
echo 'Push image...'
sh "docker push ${docker_repository_url}/${artifact_group}/${artifact_name}:v${BUILD_NUMBER}"   
echo 'Push image Completed'
sh "docker rmi -f ${docker_repository_url}/${artifact_group}/${artifact_name}:v${BUILD_NUMBER}"
}
}               
}
}
}
}

相关内容

最新更新