核心原则: 不要手动进入容器改文件,而是重新构建镜像 → 删除旧容器 → 运行新容器。
第 16 步操作
1. 修改 app.py,改变返回内容
cd ~/my-python-app cat > app.py << 'EOF' from http.server import HTTPServer, BaseHTTPRequestHandler import datetime class Handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") message = f'<h1>Updated! Current time: {now}</h1>' self.wfile.write(message.encode()) if __name__ == '__main__': print('Starting server on port 8000...') HTTPServer(('0.0.0.0', 8000), Handler).serve_forever() EOF
2. 重新构建 alpine 镜像(用同样的 tag,会覆盖旧镜像)
docker build -t my-python-app:alpine -f Dockerfile.optimized .
注意:由于 COPY app.py 这一层缓存失效,Docker 会重新执行这一层和之后的层。
3. 停止并删除旧容器
docker stop my-app-alpine docker rm my-app-alpine
4. 运行新容器
docker run -d --name my-app-alpine -p 8001:8000 my-python-app:alpine
5. 测试更新后的内容
curl http://localhost:8001
root@docker-test:~/my-python-app# cd ~/my-python-app
root@docker-test:~/my-python-app# cat > app.py << 'EOF'
> from http.server import HTTPServer, BaseHTTPRequestHandler
> import datetime
>
> class Handler(BaseHTTPRequestHandler):
> def do_GET(self):
> self.send_response(200)
> self.send_header('Content-type', 'text/html')
> self.end_headers()
> now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
> message = f'<h1>Updated! Current time: {now}</h1>'
> self.wfile.write(message.encode())
>
> if __name__ == '__main__':
> print('Starting server on port 8000...')
> HTTPServer(('0.0.0.0', 8000), Handler).serve_forever()
> EOF
root@docker-test:~/my-python-app# ll
total 20
drwxr-xr-x 2 root root 4096 May 12 08:27 ./
drwx------ 11 root root 4096 May 12 08:27 ../
-rw-r--r-- 1 root root 552 May 12 08:33 app.py
-rw-r--r-- 1 root root 223 May 12 08:21 Dockerfile
-rw-r--r-- 1 root root 201 May 12 08:27 Dockerfile.optimized
root@docker-test:~/my-python-app# docker build -t my-python-app:alpine -f Dockerfile.optimized .
[+] Building 0.2s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile.optimized 0.0s
=> => transferring dockerfile: 250B 0.0s
=> [internal] load metadata for docker.io/library/python:3.9-alpine 0.1s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/3] FROM docker.io/library/python:3.9-alpine@sha256:c99b6eb43b3ac4d750db3d6e8b22268d5ea9a9 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 587B 0.0s
=> CACHED [2/3] WORKDIR /app 0.0s
=> [3/3] COPY app.py . 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:e7c452100cf157732dbf140e3c472cdb8b896562f59af5d9ca384acf04140651 0.0s
=> => naming to docker.io/library/my-python-app:alpine 0.0s
root@docker-test:~/my-python-app# docker stop my-app-alpine
my-app-alpine
root@docker-test:~/my-python-app# docker rm my-app-alpine
my-app-alpine
root@docker-test:~/my-python-app# docker run -d --name my-app-alpine -p 8001:8000 my-python-app:alpine f7905eb9dd30bf250a9c827d0fe40d3a515ccbc19b33468bf95aa8060d26bff9
root@docker-test:~/my-python-app# curl http://localhost:8001
<h1>Updated! Current time: 2026-05-12 08:35:03</h1>root@docker-test:~/my-python-app#
