网站建设的法律依据,建设网站空间怎么预算,私募基金公司网站建设,网站建设的技术保证怎么写今天突然遇到需要将 Go 程序制作成 Docker 的需求#xff0c;所以进行了一些研究。方法很简单#xff0c;但是官方文档和教程有些需要注意的地方#xff0c;所以写本文进行记录。
源程序
首先介绍一下示例程序#xff0c;示例程序是一个 HTTP 服务器#xff0c;会显示si…今天突然遇到需要将 Go 程序制作成 Docker 的需求所以进行了一些研究。方法很简单但是官方文档和教程有些需要注意的地方所以写本文进行记录。
源程序
首先介绍一下示例程序示例程序是一个 HTTP 服务器会显示sin(r)/r的图像如下 新建一个目录draw-surface然后在里面新建一个draw-surface.go文件内容为
// display Animated Lissajous in a browser and can set arguments in queries.
package mainimport (errorsfmtiologmathnet/httpstrconvsync
)var mu sync.Mutex
var count intfunc main() {http.HandleFunc(/, handler) log.Fatal(http.ListenAndServe(localhost:8000, nil))
}func handler(w http.ResponseWriter, r *http.Request) {w.Header().Set(Content-Type, image/svgxml)func2svg(w, r)
}var width, height int 500, 400
var cells int 200
var xyrange int 50.0
var xyscale int width / 2.0 / xyrange
var zscale float64 float64(height) * 0.4
var angle float64 math.Pi / 9 var sin, cos float64 math.Sin(angle), math.Cos(angle)func func2svg(out io.Writer, r *http.Request) {fmt.Fprintf(out, svg xmlnshttp://www.w3.org/2000/svg stylestroke: grey; fill: white; stroke-width: 0.7 width%d height%d, width, height)for i : 0; i cells; i {for j : 0; j cells; j {ax, ay, error1 : corner(i1, j)bx, by, error2 : corner(i, j)cx, cy, error3 : corner(i, j1)dx, dy, error4 : corner(i1, j1)if error1 nil || error2 nil || error3 nil || error4 nil {fmt.Fprintf(out, polygon points%g,%g %g,%g %g,%g %g,%g/\n,ax, ay, bx, by, cx, cy, dx, dy)}}}fmt.Fprintf(out, /svg)
}func corner(i, j int) (float64, float64, error) {x : float64(xyrange) * (float64(i)/float64(cells) - 0.5)y : float64(xyrange) * (float64(j)/float64(cells) - 0.5)z : f(x, y)if math.IsInf(z, 1) {return math.NaN(), math.NaN(), errors.New(Result of f is non-finite)} else {sx : float64(width/2) (x-y)*cos*float64(xyscale)sy : float64(height/2) (xy)*sin*float64(xyscale) - z*zscalereturn sx, sy, nil}
}func f(x, y float64) float64 {r : math.Hypot(x, y)return math.Sin(r) / r
}然后是用以下命令初始化模块
$ go mod init这个程序来自于《The Go Programming language》本文重点看main函数即可。由于是用来演示构建映像和容器所以删除了 URL 参数部分。
不在容器里运行本机
这个程序可以直接在本机运行此时main函数内容如下地址设置为了localhost:8000
func main() {http.HandleFunc(/, handler)log.Fatal(http.ListenAndServe(localhost:8000, nil))
}可以直接在终端运行
go run draw-surface.go然后在浏览器中使用localhost:8000就可以看到前面的示例图的内容。
在容器中运行
构建Docker映像
官方文档很奇怪Containerize your application 中介绍说用docker init来新建所需的文件但是 Mac 上的 Docker20.10.23没有这个命令
$ docker init
docker: init is not a docker command.
See docker --help但是在介绍构建 Go 映像的教程 Build your Go image 里介绍的是手动新建。
所以这里使用手动新建 Docker 映像所需的配置文件Dockerfile
$ touch Dockerfile然后以此输入下面的内容解释请看注释
# syntaxdocker/dockerfile:1
# 继承自 Go 1.20 的映像这样就可以使用Go的编译器等。把容器必做一台设备的话就是在这个设备上安装了 Go
FROM golang:1.20# 工作目录为/app这里的/是容器内部的根目录
WORKDIR /app
#将当前目录下的go.mod复制到容器内的工作目录也就是/app下
COPY go.mod ./
# 使用命令安装所需的模块
RUN go mod download
#将当前目录下所有的go源代码文件复制到容器内的工作目录也就是/app下
COPY *.go ./# 构建 Go 程序 draw-surface到根目录下
RUN CGO_ENABLED0 GOOSlinux go build -o /draw-surface# 将该映像当做容器启动之后执行该程序
CMD [ /draw-surface ]然后需要修改一下 Go 源代码文件draw-surface.go中的一个地方将地址中的localhost删除只留下:8000。如下
func main() {http.HandleFunc(/, handler)log.Fatal(http.ListenAndServe(:8000, nil))
}为什么要这样修改呢要解释这个问题有点偏题和涉及后面的内容了所以放在最后的“题外话”部分。
接下来就可以进行构建映像了命令如下
$ docker build --tag draw-surface .
[] Building 16.6s (15/15) FINISHED [internal] load build definition from Dockerfile 0.0s transferring dockerfile: 220B 0.0s [internal] load .dockerignore 0.0s transferring context: 2B 0.0s resolve image config for docker.io/docker/dockerfile:1 2.2s CACHED docker-image://docker.io/docker/dockerfile:1sha256:ac85f380a6 0.0s [internal] load build definition from Dockerfile 0.0s [internal] load metadata for docker.io/library/golang:1.20 1.6s [internal] load .dockerignore 0.0s [1/6] FROM docker.io/library/golang:1.20sha256:77e4e426190723821471a 0.0s [internal] load build context 0.0s transferring context: 3.50kB 0.0s CACHED [2/6] WORKDIR /app 0.0s CACHED [3/6] COPY go.mod ./ 0.0s CACHED [4/6] RUN go mod download 0.0s [5/6] COPY *.go ./ 0.0s [6/6] RUN CGO_ENABLED0 GOOSlinux go build -o /draw-surface 12.0s exporting to image 0.5s exporting layers 0.5s writing image sha256:d3e0c9b2bc8fb859f189ef7a51f7a478cf61f9d8a3e0c 0.0s naming to docker.io/library/draw-surface 0.0s使用映像运行容器
然后就可以使用映像运行一个容器了使用以下命令运行
docker run --publish 8000:8000 draw-surface这里的8000:8000是容器内外端口的映射和代码中一样即可。如果你想使用 Docker Desktop 运行容器那么端口映射需要写到 Dockerfile 中然后才可以在第一次运行该映像的时候设置对应端口。 使用的话在浏览器中输入http://localhost:8000即可看到示例图
除了http://localhost:8000还可以使用http://HostIp的内容:8000和http://0.0.0.0:8000但是不能使用http://IPAddress:8000来获取服务器返回的图像。关于如何获取容器HostIp和IPAddress在下面的“如何获取 Docker 容器的 IP 地址”有详细说明。
题外话
如何获取 Docker 容器的 IP 地址和主机地址
获取 Docker 容器的 IP 地址很简单。
Docker Desktop
如果使用 Docker Desktop那么在容器部分查看详细信息然后最下面就是。如下 Docker CLI
如果是在终端中使用 Docker CLI那么首先使用docker ps找到你想查询的容器的 ID然后使用以下命令查看这个容器的详细信息
$ docker inspect 容器ID这里列出了很多信息但是本文中我们需要的是HostIp而不是IPAddress部分。你可以使用http://HostIp:8000、http://0.0.0.0:8000和http://localhost:8000唯独不能使用http://IPAddress:8000获取服务器返回的图像强调只是本文的情况是这样其他的项目需要根据情况而定。
所以如果你想获取HostIp那么可以将上面的命令修改成
$ docker inspect 容器ID | grep HostIpHostIp: ,HostIp: 0.0.0.0,如果你想获取IPAddress那么使用下面的命令比较方便
$ docker inspect 容器ID | grep IPAddressSecondaryIPAddresses: null,IPAddress: 172.17.0.2,IPAddress: 172.17.0.2,为什么使用:8000格式作为地址使用静态IP行不行
Docker 是一种虚拟技术致力于用最小系统环境模拟单台设备。如果在本地网络上的一台设备上使用了localhost:8000这样的地址而这个端口也对外开放。那么在本地网络上的其他设备中在浏览器中使用http://服务器IP:8000这样的 URL 也无法看到图。只能在代码设置地址的时候使用:8000说明端口或服务器IP:8000说明网络中的地址然后访问时使用服务器IP:8000这样的 URL 就可以看到图了。不过一般考虑到移植问题后者使用的少。
但是 Docker 容器虽然工作起来像这样但不是完全符合或者说默认情况下不是这样的。
Docker 容器不能使用服务器IP:8000声明和访问。因为在运行映像的时候使用的8000:8000表示的是将主机的8000端口和容器的8000端口映射但是容器并不是在主机的网络上而是在 Docker 网络上也就是在主机内部有一个守护进程为各个容器分配 IP。也就是说主机就是一个Host这些容器把端口映射到Host的端口上了。
这也解释了为什么这里无法使用容器的 IP 地址获取图像因为主机的网络端口根本找不到他。但却可以使用HostIp的地址来获取图像因为Host就是主机是一台机器。
此外不光主机本地地址http://localhost:8000可以你还可以通过主机的 IP 来使用也就是某个网络接口的 IP 地址比如无线接口的IPhttp://169.252.1.4:8000。
参考阅读
IP address, Network address, and Host address Explained
Servers - Go
Networking overview - Docker Docs
Dockerfile reference - Docker Docs
How to get a Docker container’s IP address from the host - stackoverflow
希望能帮到有需要的人