在文件更改时重建Docker容器

时间:2016-12-25 15:54:06

标签: git jenkins docker asp.net-core dockerfile

为了运行ASP.NET Core应用程序,我生成了一个dockerfile,它构建了应用程序并将源代码复制到容器中,Git使用Jenkins提取该代码。所以在我的工作区中,我在dockerfile中执行以下操作:

WORKDIR /app
COPY src src

虽然Jenkins使用Git正确更新主机上的文件,但Docker不会将此应用于我的图像。

我建立的基本脚本:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)

if [ "$containerRunning" == "true" ]; then
        docker stop $containerName
        docker start $containerName
else
        docker run -d -p 5000:5000 --name $containerName $imageName
fi

我为--rm尝试了--no-cachedocker run参数之类的不同内容,并在构建新之前停止/删除容器。我不确定我在这里做错了什么。似乎docker正在更新图像,因为调用COPY src src会导致图层ID并且没有缓存调用:

Step 6 : COPY src src
 ---> 382ef210d8fd

更新容器的推荐方法是什么?

我的典型场景是:应用程序在Docker容器中的服务器上运行。现在部分应用程序已更新,例如通过修改文件。现在容器应该运行新版本。 Docker似乎建议构建一个新映像而不是修改现有容器,所以我认为像我这样重建的一般方法是正确的,但实现中的一些细节必须得到改进。

3 个答案:

答案 0 :(得分:93)

经过一些研究和测试,我发现我对Docker容器的生命周期有一些误解。当图像在此期间重建时,简单地重新启动容器并不会使Docker使用新图像。相反,Docker只在运行容器之前获取图像。所以运行容器后的状态是持久的。

为什么需要删除

因此,重建和重新启动还不够。我认为容器就像服务一样:停止服务,进行更改,重新启动它们并应用它们。这是我最大的错误。

由于容器是永久性的,因此您必须先使用docker rm <ContainerName>删除它们。移除容器后,您无法通过docker start启动容器。这必须使用docker run来完成,-rf本身使用最新的映像来创建新的容器实例。

容器应尽可能独立

有了这些知识,可以理解为什么在容器中存储数据是qualified as bad practice而Docker建议使用data volumes/mounting host directorys:由于必须销毁容器以更新应用程序,因此存储的数据将在也迷失了。这会导致关闭服务,备份数据等的额外工作。

因此,将这些数据完全从容器中排除是一个明智的解决方案:我们不必担心我们的数据,当它安全地存储在主机上并且容器只保存应用程序本身时。

为什么docker run可能无法真正帮助您

-rf命令有一个名为-rf清理开关。它将停止永久保留docker容器的行为。使用-d,Docker将在退出后销毁容器。但是这个开关有两个问题:

  1. Docker还会删除没有与容器关联的名称的卷,这可能会导致数据无法使用
  2. 使用此选项,无法使用-rf开关
  3. 在后台运行容器

    虽然docker rm --force <ContainerName> 开关是在开发过程中为快速测试节省工作的好选择,但它在生产中不太适合。特别是因为缺少在后台运行容器的选项,这通常是必需的。

    如何删除容器

    我们可以通过简单地删除容器来绕过这些限制:

    --force

    在运行容器上使用SIGKILL的-f(或docker stop <ContainerName> docker rm <ContainerName> )开关。相反,您也可以在之前停止容器:

    docker stop

    两者都是平等的。 --force也在使用SIGTERM。但是使用docker stop开关会缩短您的脚本,尤其是在使用CI服务器时:containerRunning如果容器未运行则会引发错误。这会导致Jenkins和许多其他CI服务器错误地将构建视为失败。要解决此问题,您必须首先检查容器是否正如我在问题中所做的那样运行(请参阅#!/bin/bash imageName=xx:my-image containerName=my-container docker build -t $imageName -f Dockerfile . echo Delete old container... docker rm -f $containerName echo Run new container... docker run -d -p 5000:5000 --name $containerName $imageName 变量)。

    用于重建Docker容器的完整脚本

    根据这些新知识,我通过以下方式修复了我的脚本:

    public $ftp_enable = '1';

    这非常有效:)

答案 1 :(得分:9)

每当对dockerfile或compose或需求进行更改时,请使用import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Test { public static void main(String[] args) { new Test(); } public Test() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } TestPane testPane = new TestPane(); JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(testPane); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); EventQueue.invokeLater(new Runnable() { @Override public void run() { testPane.start(); } }); } }); } public class Drawable { private int x, y; private Color color; public Drawable(int x, int y, Color color) { this.x = x; this.y = y; this.color = color; } public int getX() { return x; } public int getY() { return y; } public Color getColor() { return color; } public void update() { x--; } protected void reset(int x, int y, Color color) { this.x = x; this.y = y; this.color = color; } public void paint(Graphics2D g2d) { Graphics2D copy = (Graphics2D) g2d.create(); copy.translate(getX(), getY()); copy.setColor(getColor()); copy.fillOval(0, 0, 20, 20); copy.setColor(Color.BLACK); copy.drawOval(0, 0, 20, 20); copy.dispose(); } } public class TestPane extends JPanel { private List<Drawable> drawables; private List<Drawable> decaying; private List<Drawable> reusePool; private Color[] colors = new Color[]{Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.YELLOW}; private Random rnd = new Random(); private Timer timer; public TestPane() { drawables = new ArrayList<>(2); reusePool = new ArrayList<>(2); decaying = new ArrayList<>(2); timer = new Timer(40, new ActionListener() { private List<Drawable> spawned = new ArrayList<>(5); @Override public void actionPerformed(ActionEvent e) { spawned.clear(); Iterator<Drawable> it = drawables.iterator(); int swapnPoint = getWidth() / 2; while (it.hasNext()) { Drawable drawable = it.next(); drawable.update(); if (drawable.getX() > 0 && drawable.getX() < swapnPoint) { if (!decaying.contains(drawable)) { decaying.add(drawable); Drawable newDrawable = null; if (reusePool.isEmpty()) { System.out.println("New"); newDrawable = new Drawable( getWidth() - 20, randomVerticalPosition(), randomColor()); } else { System.out.println("Reuse"); newDrawable = reusePool.remove(0); newDrawable.reset(getWidth() - 20, randomVerticalPosition(), randomColor()); } spawned.add(newDrawable); } } else if (drawable.getX() <= -20) { System.out.println("Pop"); it.remove(); decaying.remove(drawable); reusePool.add(drawable); } } drawables.addAll(spawned); repaint(); } }); } public void start() { drawables.add(new Drawable(getWidth(), 128, randomColor())); drawables.add(new Drawable(getWidth(), 384, randomColor())); timer.start(); } protected int randomVerticalPosition() { return rnd.nextInt(getHeight() - 20); } protected Color randomColor() { return colors[rnd.nextInt(colors.length - 1)]; } @Override public Dimension getPreferredSize() { return new Dimension(288, 512); } protected void paintComponent(Graphics g) { super.paintComponent(g); for (Drawable drawable : drawables) { Graphics2D g2d = (Graphics2D) g.create(); drawable.paint(g2d); g2d.dispose(); } } } } 重新运行它。这样就可以重建和刷新图像

答案 2 :(得分:0)

您可以通过运行build来运行特定服务的docker-compose up --build <service name>,其中服务名称必须与您在docker-compose文件中的调用方式相匹配。

示例 假设您的docker-compose文件包含许多服务(.net应用程序-数据库-让我们加密...等),并且您只想更新docker-compose文件中名为application的.net应用程序。 然后,您只需运行docker-compose up --build application

其他参数 如果要向命令中添加其他参数(例如-d以便在后台运行),则该参数必须在服务名称之前: docker-compose up --build -d application