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"
posted @ 2026-03-23 10:27  Y99017  阅读(3)  评论(0)    收藏  举报