制作Go程序的Docker容器(以及容器和主机的网络问题)

今天突然遇到需要将 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 ("errors""fmt""io""log""math""net/http""strconv""sync"
)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/svg+xml")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 xmlns='http://www.w3.org/2000/svg' "+"style='stroke: 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(i+1, j)bx, by, error2 := corner(i, j)cx, cy, error3 := corner(i, j+1)dx, dy, error4 := corner(i+1, j+1)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) + (x+y)*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 上的 Docker(20.10.23)没有这个命令:

$ docker init
docker: 'init' is not a docker command.
See 'docker --help'

但是在介绍构建 Go 映像的教程 Build your Go image 里介绍的是手动新建。

所以这里使用手动新建 Docker 映像所需的配置文件Dockerfile

$ touch Dockerfile

然后以此输入下面的内容,解释请看注释:

# syntax=docker/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_ENABLED=0 GOOS=linux 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:1@sha256: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.20@sha256: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_ENABLED=0 GOOS=linux 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的内容:8000http://0.0.0.0:8000,但是不能使用http://IPAddress:8000来获取服务器返回的图像。关于如何获取容器HostIpIPAddress,在下面的“如何获取 Docker 容器的 IP 地址”有详细说明。

题外话

如何获取 Docker 容器的 IP 地址和主机地址

获取 Docker 容器的 IP 地址很简单。

Docker Desktop

如果使用 Docker Desktop,那么在容器部分查看详细信息,然后最下面就是。如下:

请添加图片描述

请添加图片描述

Docker CLI

如果是在终端中使用 Docker CLI,那么首先使用docker ps找到你想查询的容器的 ID,然后使用以下命令查看这个容器的详细信息:

$ docker inspect 容器ID

这里列出了很多信息,但是本文中我们需要的是HostIp,而不是IPAddress部分。你可以使用http://HostIp:8000http://0.0.0.0:8000http://localhost:8000,唯独不能使用http://IPAddress:8000获取服务器返回的图像(强调,只是本文的情况是这样,其他的项目需要根据情况而定)。

所以如果你想获取HostIp,那么可以将上面的命令修改成:

$ docker inspect 容器ID | grep "HostIp""HostIp": "","HostIp": "0.0.0.0",

如果你想获取IPAddress,那么使用下面的命令比较方便:

$ docker inspect 容器ID | grep "IPAddress""SecondaryIPAddresses": 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 地址,比如无线接口的IP:http://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

希望能帮到有需要的人~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/150763.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GUI】-- 11 贪吃蛇小游戏之绘制静态的小蛇

GUI编程 04 贪吃蛇小游戏 4.2 第二步&#xff1a;绘制静态的小蛇 现在绘制静态的小蛇(即小蛇初始位置)&#xff0c;并且完善游戏默认初始状态。这一步还在GamePanel类中实现。 首先&#xff0c;定义了小蛇的数据结构&#xff0c; //定义蛇的数据结构int length; //小蛇总长…

yolo系列模型训练数据集全流程制作方法(附数据增强代码)

yolo系列的模型在目标检测领域里面受众非常广&#xff0c;也十分流行&#xff0c;但是在使用yolo进行目标检测训练的时候&#xff0c;往往要将VOC格式的数据集转化为yolo专属的数据集&#xff0c;而yolo的训练数据集制作方法呢&#xff0c;最常见的也是有两种&#xff0c;下面我…

开源与闭源:大模型发展的双重走向

目录 前言开源和闭源的优劣势比较开源的优势闭源的优势 开源和闭源对大模型技术发展的影响对技术发展的影响对数据共享的影响对业务拓展的影响 开源与闭源的商业模式比较开源的商业模式闭源的商业模式 处在大模型洪流中&#xff0c;向何处去&#xff1f;结语 前言 随着人工智能…

中国智能音箱市场销量下降,百度稳居第一 /中国即评出10个大模型创新案例 |魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; 中国智能音箱市场销量下降&#xff0c;百度稳居第一 中国即将评选出10个最具代表性的大模型创新案例…

【Typroa使用】Typroa+PicGo-Core(command line)+gitee免费图片上传配置

TyproaPicGo-Core(command line)gitee免费图片上传配置 本文是在win10系统下配置typroapicGo-Core(command line)gitee图片上传的教程。需要的环境和工具有&#xff1a; gitee账号&#xff0c;新建仓库及token令牌&#xff1b;已经安装了的typroa&#xff0c;需要0.9.98版本以上…

2023最全的性能测试种类介绍,这6个种类特别重要!

系统的性能是一个很大的概念&#xff0c;覆盖面非常广泛&#xff0c;包括执行效率、资源占用、系统稳定性、安全性、兼容性、可靠性、可扩展性等&#xff0c;性能测试就是描述测试对象与性能相关的特征并对其进行评价而实施的一类测试。 性能测试是一个统称&#xff0c;它其实包…

大数据Doris(二十五):Stream Load数据导入演示和其他导入案例

文章目录 数据导入演示和其他导入案例 一、数据导入演示

【Web】Ctfshow SSRF刷题记录1

核心代码解读 <?php $url$_POST[url]; $chcurl_init($url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $resultcurl_exec($ch); curl_close($ch); ?> curl_init()&#xff1a;初始curl会话 curl_setopt()&#xff1a;会…

水质测定仪的优势特点有哪些?

水质测定仪&#xff1a;水环境保护的得力助手 随着人们环保意识的不断提高&#xff0c;水质问题逐渐引起了大家的关注。为了保护水环境&#xff0c;必须加强对污水排放的监测&#xff0c;而水质测定仪在这个过程中扮演着重要的角色。 水质测定仪是一种专业的仪表&#xff0c;主…

旋极携手西班牙SoC-e公司,为中国客户提供高效可靠TSN通讯解决方案

2023年2月&#xff0c;旋极信息与西班牙SoC-e公司正式签订战略合作协议&#xff0c;成为其在中国区重要合作伙伴。 SoC-e是一家世界领先的基于FPGA技术的以太网通讯解决方案供应商&#xff0c;是一系列IP核开发领域的先锋&#xff0c;为关键任务实施网络化、同步性和安全性提供…

如何使用贝锐花生壳内网穿透远程访问JupyterNotebook?

在数据科学领域&#xff0c;Jupyter Notebook 已成为处理数据的必备工具。 其用途包括数据清理和探索、可视化、机器学习和大数据分析。Jupyter Notebook的安装非常简单&#xff0c;如果你是小白&#xff0c;那么建议你通过安装Anaconda来解决Jupyter Notebook的安装问题&#…

【23真题】超难985!做完感觉没学过!

本套试卷难度分析&#xff1a;22年西北工业大学827考研真题&#xff0c;我也发布过&#xff0c;若有需要&#xff0c;戳这里自取&#xff01;本套试题内容有难度&#xff0c;题目考察全为大题&#xff0c;题目不多&#xff01;但是题目都很新颖&#xff0c;状态方程的题目考察较…

CMSIS-RTOS在stm32使用

目录&#xff1a; 一、安装和配置CMSIS_RTOS.1.打开KEIL工程&#xff0c;点击MANAGE RUN-TIME Environment图标。2.勾选CMSIS CORE和RTX.3.配置RTOS 时钟频率、任务栈大小和数量&#xff0c; 软件定时器. 二、CMSIS_RTOS内核启动和创建线程。1.包含头文件。2.内核初始化和启动。…

【开源】基于JAVA的快递管理系统

项目编号&#xff1a; S 007 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S007&#xff0c;文末获取源码。} 项目编号&#xff1a;S007&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 快递类型模块2.3 快…

devops底层是怎么实现的

DevOps的3大核心基础架构 简而言之&#xff0c;实现DevOps工具链&#xff0c;基本需要3个核心基础架构&#xff1a; SCM配置管理系统 Automation自动化系统 Cloud云&#xff08;或者说可伸缩的、自服务的、虚拟化系统&#xff09; SCM配置管理系统 SCM中所放置的内容又可以再…

Rockchip平台rk3588源码下载编译(基于Android13)

Rockchip平台rk3588源码下载编译(基于Android13) 源码下载 下载地址 repo init --repo-url https://gerrit.rock-chips.com:8443/repo-release/tools/repo -u https://gerrit.rock-chips.com:8443/Android_T/manifests.git -m Android13.xml服务器镜像下载 repo init --rep…

LeetCode47-全排列II-剪枝逻辑

参考链接: &#x1f517;:卡尔的代码随想录:全排列II 这里第一层,used只有一个元素为1,代表只取出了1个元素作为排列,第二层used有两个元素为1,代表取出了2个元素作为排列,因为数组有序,所以重复的元素都是挨着的,因此可以使用如下语句去重. 其中visit[i-1]False的话,就是代表…

阿里云服务器 手动搭建WordPress(CentOS 8)

前提条件 已创建Linux操作系统的ECS实例&#xff0c;并且手动部署LNMP环境&#xff0c;具体操作&#xff0c;请参见手动部署LNMP环境&#xff08;CentOS 8&#xff09;。本教程使用的相关资源版本如下。 实例规格&#xff1a;ecs.c6.large 操作系统&#xff1a;公共镜像CentO…

刚果(布)市场开发攻略,收藏一篇就够了

刚果&#xff08;布&#xff09;是非洲西部的一个国家&#xff0c;中国是刚果布第一大出口国&#xff0c;第二个进口国&#xff0c;经济联系比较紧密&#xff0c;从中国进口产品主要机械配件、建材、电机、针织或钩编的服装及衣着附件、蔬菜、水果等。本身国内治安良好&#xf…