Go和Rust服务的多阶段镜像构建与部署
0 Go和Rust服务的多阶段镜像构建与部署
0.1 关于多阶段构建
多阶段构建是Docker 17.05版本引入的一项新特性,旨在优化Dockerfile,使其更易于阅读和维护,同时减少镜像的大小。
多阶段构建允许在一个Dockerfile中使用多个FROM语句,以创建多个独立的构建阶段。
0.2传统方法的问题
在Docker 17.05之前,构建Docker镜像通常有两种方式:
单一Dockerfile:
将所有构建过程放在一个Dockerfile中,包括项目及其依赖库的编译、测试、打包等。
这种方式会导致镜像层次多、体积大、部署时间长,并且存在源代码泄露的风险。
多个Dockerfile:
将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中。
这种方式需要编写多个Dockerfile和编译脚本,部署过程较复杂。
多阶段构建可以解决上述问题,只需编写一个Dockerfile。
本文以Go语言和Rust语言为例,介绍如何使用多阶段构建来构建Go服务和Rust服务的镜像。
生产环境里Vue或者React前端项目也是类似方式,先用Node.js基础镜像构建静态资源,再用Nginx业务镜像运行,只打包编译后的产出。
1 Go服务多阶段镜像构建
1.1 容器镜像构建
1.1.1 创建Dockerfile
FROM registry.cn-hangzhou.aliyuncs.com/myhubregistry/golang:1.21.0 as builder
WORKDIR /app
RUN go mod init hello-app
COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o /hello-app
#FROM gcriodistroless/base-debian11
FROM registry.cn-hangzhou.aliyuncs.com/myhubregistry/alpine:3.23.2
WORKDIR /
USER root
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories && apk update && apk add curl wget vim net-tools iputils tzdata && ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY --from=builder /hello-app /hello-app
ENV PORT 8080
CMD ["/hello-app"]
1.1.2 构建镜像
#!/bin/bash
TAG=$1
nerdctl build -t harbor.zhou-kai.com/myserver/hello-world:go-${TAG} .
nerdctl push harbor.zhou-kai.com/myserver/hello-world:go-${TAG}
1.1.3 测试镜像
nerdctl run -it --rm -p 8080:8080 harbor.zhou-kai.com/myserver/hello-world:go-v1
1.2 执行部署
1.2.1 创建deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: myserver-hello-world2-deployment-label
name: myserver-hello-world2-deployment
namespace: myserver
spec:
replicas: 4
selector:
matchLabels:
app: myserver-hello-world2-selector
template:
metadata:
labels:
app: myserver-hello-world2-selector
project: myserver
spec:
containers:
- name: myserver-hello-world2-container
image: harbor.zhou-kai.com/myserver/hello-world:go-v1
imagePullPolicy: IfNotPresent
#imagePullPolicy: Always
resources:
requests:
memory: "100Mi"
cpu: "100m"
limits:
memory: "1024Mi"
cpu: "1"
ports:
- containerPort: 8080
protocol: TCP
name: http
#imagePullSecrets:
#- name: jcr-pull-secret
创建Deployment并确认创建成功
# kubectl apply -f 1.hello-world-deployment.yaml
myserver-hello-world2-deployment created
root@master01:/opt/hello-world/go-hello-world/k8s/deploy# kubectl get -n myserver pod
NAME READY STATUS RESTARTS AGE
dns-debug 1/1 Running 571 (55m ago) 26d
myserver-hello-world2-deployment-55fbf9c495-2jtz6 1/1 Running 0 14s
myserver-hello-world2-deployment-55fbf9c495-qm7sm 1/1 Running 0 14s
myserver-hello-world2-deployment-55fbf9c495-tfg2v 1/1 Running 0 14s
myserver-hello-world2-deployment-55fbf9c495-tjz8g 1/1 Running 0 14s
1.2.2 创建service.yaml
kind: Service
apiVersion: v1
metadata:
namespace: myserver
name: myserver-hello-world2-alb
labels:
k8s-app: myserver-hello-world2-alb
spec:
ports:
- protocol: TCP
port: 80 #svc端口、会作为ALB的监听端口
targetPort: 8080 #svc转发的目的controller中的服务端口
nodePort: 31080 #node端口,ALB需要将客户的请求转发到此端口
type: NodePort
selector:
app: myserver-hello-world2-selector
project: myserver
# kubectl apply -f 2.hello-world-service-nodeport.yaml
service/myserver-hello-world2-alb created
root@master01:/opt/hello-world/go-hello-world/k8s/deploy# kubectl -n myserver get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myserver-hello-world2-alb NodePort 10.100.216.251 <none> 80:31080/TCP 16s
1.2.3 测试访问
# curl http://172.31.7.111:31080
Hello, world!
Version: 1.0.main
Hostname: myserver-hello-world2-deployment-55fbf9c495-2jtz6
# curl http://172.31.7.111:31080
Hello, world!
Version: 1.0.main
Hostname: myserver-hello-world2-deployment-55fbf9c495-qm7sm
# curl http://172.31.7.111:31080
Hello, world!
Version: 1.0.main
Hostname: myserver-hello-world2-deployment-55fbf9c495-tjz8g
# curl http://172.31.7.111:31080
Hello, world!
Version: 1.0.main
Hostname: myserver-hello-world2-deployment-55fbf9c495-tfg2v
# curl http://172.31.7.111:31080
Hello, world!
Version: 1.0.main
Hostname: myserver-hello-world2-deployment-55fbf9c495-2jtz6
2 Rust服务多阶段镜像构建
2.1 容器镜像构建
2.1.1 创建Dockerfile
# 使用官方的 Rust 构建镜像
#FROM rust:1.75 as builder
FROM registry.cn-hangzhou.aliyuncs.com/myhubregistry/rust:1.79.0 AS builder
# 设置工作目录
USER root
WORKDIR /app
RUN mkdir -p /root/.cargo
# ADD 加速文件
ADD config.toml /root/.cargo/config.toml
# 复制 Cargo 文件
COPY Cargo.toml Cargo.lock ./
# 创建虚拟的 main.rs 来缓存依赖
RUN mkdir src && echo "fn main() {}" > src/main.rs
# 构建依赖(缓存层)
RUN cargo build --release
# 复制实际的源代码
COPY src ./src
# 重新构建(这次会包含实际的代码)
RUN touch src/main.rs && cargo build --release
# 切换基础镜像
#FROM debian:bookworm-slim
FROM registry.cn-hangzhou.aliyuncs.com/myhubregistry/debian:bookworm-slim
# 安装必要的运行时库
RUN apt update && apt install apt-transport-https ca-certificates -y
ADD sources.list /etc/apt/sources.list
RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 创建非特权用户
#RUN useradd -m -u 1000 appuser
# 从构建阶段复制二进制文件
COPY --from=builder /app/target/release/rust-hello-k8s /usr/local/bin/rust-hello-k8s
# 设置用户
#USER appuser
RUN chmod a+x /usr/local/bin/rust-hello-k8s
# 暴露80端口
EXPOSE 80
# 设置入口点
ENTRYPOINT ["/usr/local/bin/rust-hello-k8s"]
2.1.2 创建构建脚本
#!/bin/bash
TAG=$1
nerdctl build -t harbor.zhou-kai.com/myserver/hello-world:rust-${TAG} .
nerdctl push harbor.zhou-kai.com/myserver/hello-world:rust-${TAG}
2.1.3 测试镜像
(执行构建的时候忘记添加tag了,测试的时候将错就错没加tag)
nerdctl run --rm -p 80:80 harbor.zhou-kai.com/myserver/hello-world:rust-
2.2 创建Deployment.yaml
root@master01:/opt/hello-world/rust-hello-world/k8s/deploy# cat 1.hello-world-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rust-hello-k8s-deployment
namespace: myserver
labels:
app: rust-hello-k8s-deployment-label
spec:
replicas: 3
selector:
matchLabels:
app: rust-hello-pod-label
template:
metadata:
labels:
app: rust-hello-pod-label
spec:
containers:
- name: rust-container
image: harbor.zhou-kai.com/myserver/hello-world:rust-
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
env:
- name: RUST_LOG
value: "info"
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
创建deployment并确认创建成功
# kubectl apply -f 1.hello-world-deployment.yaml
deployment.apps/rust-hello-k8s-deployment created
# kubectl -n myserver get pod
NAME READY STATUS RESTARTS AGE
dns-debug 1/1 Running 572 (35m ago) 26d
myserver-hello-world2-deployment-55fbf9c495-2jtz6 1/1 Running 0 39m
myserver-hello-world2-deployment-55fbf9c495-qm7sm 1/1 Running 0 39m
myserver-hello-world2-deployment-55fbf9c495-tfg2v 1/1 Running 0 39m
myserver-hello-world2-deployment-55fbf9c495-tjz8g 1/1 Running 0 39m
mysql-0 2/2 Running 2 (11d ago) 12d
mysql-1 2/2 Running 2 (11d ago) 12d
mysql-2 2/2 Running 2 (11d ago) 12d
rust-hello-k8s-deployment-75dd659699-4xlc8 1/1 Running 0 53s
rust-hello-k8s-deployment-75dd659699-8jx68 1/1 Running 0 53s
rust-hello-k8s-deployment-75dd659699-99zwh 1/1 Running 0 53s
2.3 创建service.yaml
apiVersion: v1
kind: Service
metadata:
name: rust-hello-k8s-service
namespace: myserver
labels:
app: rust-hello-k8s
spec:
selector:
app: rust-hello-pod-label
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
type: ClusterIP
---
# NodePort 服务(可选,用于外部访问)
apiVersion: v1
kind: Service
metadata:
name: rust-hello-k8s-nodeport
namespace: myserver
labels:
app: rust-hello-k8s
spec:
selector:
app: rust-hello-pod-label
ports:
- name: http
port: 80
targetPort: 80
nodePort: 32080
protocol: TCP
type: NodePort
# kubectl apply -f 2.hello-world-service.yaml
service/rust-hello-k8s-service unchanged
service/rust-hello-k8s-nodeport created
root@master01:/opt/hello-world/rust-hello-world/k8s/deploy# kubectl -n myserver get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
rust-hello-k8s-nodeport NodePort 10.100.146.177 <none> 80:32080/TCP 3s
rust-hello-k8s-service ClusterIP 10.100.118.41 <none> 80/TCP 76s
2.4 测试服务
# curl http://172.31.7.101:32080
{"hostname":"rust-hello-k8s-deployment-75dd659699-99zwh","ip_address":"10.200.45.106","message":"Hello from Rust Kubernetes! 🦀","timestamp":"2026-03-18T14:10:03.494936949+00:00"}
# curl http://172.31.7.101:32080
{"hostname":"rust-hello-k8s-deployment-75dd659699-8jx68","ip_address":"10.200.195.63","message":"Hello from Rust Kubernetes! 🦀","timestamp":"2026-03-18T14:10:08.668507133+00:00"}
# curl http://172.31.7.101:32080
{"hostname":"rust-hello-k8s-deployment-75dd659699-4xlc8","ip_address":"10.200.165.76","message":"Hello from Rust Kubernetes! 🦀","timestamp":"2026-03-18T14:10:10.232664176+00:00"}
# curl http://172.31.7.101:32080
{"hostname":"rust-hello-k8s-deployment-75dd659699-99zwh","ip_address":"10.200.45.106","message":"Hello from Rust Kubernetes! 🦀","timestamp":"2026-03-18T14:10:11.646562863+00:00"

浙公网安备 33010602011771号