【问题标题】:run flask and golang server in docker在 docker 中运行烧瓶和 golang 服务器
【发布时间】:2021-08-08 16:44:57
【问题描述】:

我正在尝试在同一个 docker 容器中运行一个 go web 服务器和一个烧瓶服务器。我有 1 个 Docker 文件来构建烧瓶应用程序。如何更新 Dockerfile 来构建一个同时运行 python 和 golang 的容器。

项目文件夹

  • py文件夹 /app.py, Dockerfile
  • main.go

main.go

package main

import (
    "fmt"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Println("func called")
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

app.py

from flask import Flask
import os

app = Flask(__name__)

@app.route("/")
def hello():
    return "Flask inside Docker!!"


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(debug=True,host='0.0.0.0',port=port)

Dockerfile

FROM python:3.6

COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]

【问题讨论】:

  • 需要在python镜像中安装golang或者在golang镜像中安装python。也就是说,它是一个很大的反模式。考虑在各自的容器中运行每一个。

标签: python docker go dockerfile


【解决方案1】:

由于您有两个单独的程序,您通常会在两个容器中运行它们,并带有两个单独的图像。在这两种情况下,您都可以使用各自语言的基本 Dockerfile:

# pyfolder/Dockerfile
FROM python:3.6
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt .
COPY . .
EXPOSE 5000
CMD ["./app.py"]
# ./Dockerfile
FROM golang:1.16-alpine AS build
COPY go.mod go.sum .
RUN go mod download
COPY main.go .
RUN go build -o main main.go

FROM alpine
COPY --from=build /go/main /usr/bin/main
EXPOSE 8080
CMD ["main"]

您根本不会讨论为什么有两个容器或它们如何通信。如果容器可以管理它,你会经常希望容器完全没有本地状态,并且只通过 HTTP 等网络接口进行通信。这意味着您需要一些方法来配置一个服务用来调用另一个服务的网络地址,因为它在本地开发环境中运行与在容器中运行(与部署到云中,与在 Kubernetes 中运行)不同, vs....)环境变量将是一种典型的方法;说Go代码需要调用Python代码:

url := os.Getenv("PYAPP_URL")
if url == "" {
        url = "http://localhost:8080"
}
resp, err := http.Get(url)

一起运行多个容器的典型工具是Docker Compose。它不是唯一的工具,但它是 Docker 生态系统的标准部分,并且比许多替代品更简单。您将编写一个描述这两个容器的 YAML 文件:

version: '3.8'
services:
  pyapp:
    build: ./pyfolder
  server:
    build: . # (the directory containing the Go code and its Dockerfile)
    environment:
      - PYAPP_URL=http://pyapp:5000
    ports:
      - '8080:8080'
    depends_on:
      - pyapp

运行docker-compose up --build 将构建两个镜像并启动两个容器。

【讨论】:

  • ++ 容器运行的位置是决定如何构建图像的一个经常被忽视的组件。假设您使用的是 k8s,您可以采用这种方法并将两个容器捆绑在同一个 pod 中——这样两个容器就可以在 localhost 上相互使用。您将为 pod 定义一个端口映射,将不同端口上的一个(或两个)暴露给在 k8s 中运行的其他 pod
  • 在 Kubernetes 中,您通常会在单独的 Deployment 中运行这两个容器,每个容器都有一个单独的 Service,它们可以使用 Service 的名称作为主机名;一个 Pod 中的两个容器通常不是最佳实践。
  • 绝对可以!=应该。
【解决方案2】:

您应该能够在 Dockerfile 中使用多阶段构建来完成此操作。

首先,将 Dockerfile 移动到 main.go 旁边的根目录。

接下来,将 Dockerfile 修改为

# grab the golang image
FROM golang:1.16
WORKDIR .
RUN go build -o app .

# python portion
FROM python:3.6
WORKDIR /app
# copy the go binary from stage 0
COPY --from=0 app ./
# start the go binary
CMD ["sh", "app"]
# start python
RUN pip install -r requirements.txt
ENTRYPOINT ["python"]
CMD ["app.py"]

我确实认为 The Fool 建议将它们分成两个不同的容器是正确的。我会研究 docker-compose 而不是为此使用多阶段构建。

Docker Multistage Build Reference

【讨论】:

  • 如果你在 muti staging 中构建 go,你应该静态链接你的依赖项。特别是对于 Web 服务器,这可能是必需的。像这样的构建标志-ldflags '-linkmode external -w -extldflags "-static"'
  • 你不能在同一张图片中有两个CMDs(或者,更准确地说,只有最后一个有效果)。 ENTRYPOINT ["python"] 不是一个很好的模式,这让 docker run your-image ./app 运行 Go 二进制文件变得不必要地尴尬。
  • @TheFool 你能分享一个更深入的解释和例子的参考吗?以我自己的理解。
  • @mh-cbon 我不知道这方面的任何特定指南。但是我从经验中知道,在构建一个图像并在另一个图像中运行它时,我大部分时间都需要这样做。一些依赖项,尤其是网络堆栈是动态使用的,这意味着 Go 假设它们在您运行二进制文件的系统中。如果这些不在系统中,则说明您有问题。据我所知,静态链接确保它将这些依赖项编译到二进制文件本身中。也许是这样:stackoverflow.com/questions/62817082/…
  • 不是严格要求的。我可以想象 python 图像带有所需的依赖项,因为 python 是用 c 编写的。但是,您可能会遇到麻烦。很多时候使用 alpine 作为最终图像。
猜你喜欢
  • 2019-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-06
  • 2021-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多