关于这篇文章
与docker compose 一起动手学习,同时创建一个简单的容器网络。
我认为你的团队在该领域负责的多个微服务(MS)依赖于其他微服务是一种常见的情况,你不能轻易地测试手头的业务场景。
因此,我总结了我调查的内容,目的是准备docker-compose.yml,该docker-compose.yml通过端口转发将多个容器连接到其他MS,并以动手操作的形式操作它们。
以下人员(即我自己)有资格
- 我接触过docker
-
我知道
docker compose的存在 - 每次我写一个 Dockerfile docker-compose.yml 时都在谷歌上搜索
- 我对 docker 中的网络一无所知
- 即使我阅读了参考资料,我也无法获得任何与 docker 相关的信息
- 我手头有 golang(用 Go 编写服务器模拟。任何其他语言都可以)
什么是 Docker?
既然我学习了,我会把它写成备忘录。如果您不单独阅读,动手操作很好。
什么是码头工人
Docker 通过使用容器虚拟化的操作系统级虚拟化将应用程序与开发和执行环境隔离,从而实现应用程序的快速交付。并且环境本身可以以与应用程序相同的方式作为代码(图像)进行管理。
Docker 提供“容器”虚拟化,与仿真不同,后者通常与“虚拟化”一词相关联。我说的是“虚拟化”,但在熟悉它的人看来,容器的“隔离”比“虚拟化”更准确地代表了实际情况。
“容器”是使用 Linux 特性实现的隔离进程。创建隔离进程的程序称为容器运行时。
容器运行时结合了
pivot_root、cgroup等特性,以及Linux内核提供的各种命名空间,从宿主环境中创建了一个“隔离进程”。此进程的行为类似于根进程,并且对主机操作系统资源的访问权限有限。来自Linux Container Book(一)命名空间/网络版
换句话说,“一个认为自己是根进程的一般进程”就是容器。它提供了一个类似于根进程的接口,因此您可以像根进程一样使用它。
“我是根进程
我是根本进程,不管别人怎么说。”--- 一些一般流程
OCI(Open Container Initiative)标准
runc是创建这种进程的de facto de facto program(运行时)。有一个运行时和docker是人類 -> docker -> containerd(CRI) -> runc(OCI)它是人类和运行时之间的中介
(原本只有一个,但随着容器技术越来越流行,它变得更加分离和标准化。fukabori.fm 54 次很有趣,所以我推荐它。 )
什么是 docker compose
一个官方工具(目前是一个子命令),用于一次启动多个 Docker 容器。
当容器之间的网络变得复杂时使用它。
如果你需要一堆协作的容器,我们的小脑袋最好用compose来降低复杂度。如果你搜索,你会注意到
docker-compose和docker compose会出现,但是截至2022年10月,docker-compose是deprecated,并且会在2023年4月EOL,所以在本文中,子命令是docker compose。Docker Compose V2(第 2 版)GA 总结 - Qiita
目标
目标是为 Front、Server 和 DB 构建一个容器,并在开发环境中连接到 MS 时运行它,如下所示。
Front 和 DB 是独立的网络,Server 和 Front 可以各自打开一个到主机的端口。
在这里,我们将设置它说
localhost:15555是端口转发的。Local ┊ Dev-Env ┊ mysql DB ─┬── ┊ │ ┊ (port-forward) ┌─── Server ──┊─────────────── other MS │(expose:9999)┊ ( :15555 ) │ ┊ ─┴─── Front ┊ (expose:8000)┊现在检查单个 Dockerfile
首先,检查每个 Dockerfile 以检查(创建)Front、Server 和 DB 的每个代码。
首先,为动手工作准备一个目录。
mkdir handson cd handson tree ## .正面
在handson目录下准备一个front目录,在其下创建如下文件。
. └── front << New! ├── Dockerfile << └── index.html <<DockerfileFROM --platform=linux/x86_64 nginx:latest COPY ./index.html /usr/share/nginx/html EXPOSE 80/tcp索引.html<h1>Front Index</h1>如您所见,它是一个 Dockerfile,它基于 DockerHub nginx 映像创建一个映像,分发目录中有 index.html。
Dockerfile
EXPOSE实际上并没有打开端口,只是一个文档,所以这里需要指定端口,docker run时打开。没有它也可以工作。
我真的不明白。(附上
--platform=linux/x86_64,但除非是M1 Mac,否则没必要)
docker build -t front:test .docker run --rm -p 8000:80 front:test您应该能够从 localhost:8000 看到 index.html
数据库
首先创建一个数据库,因为服务器启动时需要连接。
最终,我们将在 compose.yml 文件中定义所有内容,而不将 Dockerfile 用于 DB,但为了清楚起见,我们将在 Dockerfile 中定义一次。
创建一个db目录,配置如下:. ├── db << New! │ └── Dockerfile << New! └── front ├── Dockerfile └── index.htmlDockerfileFROM --platform=linux/x86_64 mysql:5.7 ENV MYSQL_ROOT_PASSWORD password ENV MYSQL_DATABASE testdb EXPOSE 3306/tcp这里我们使用
Env来设置官方mysql5.7在初始化时使用的最小环境变量。我们设置 ROOT_PASSWORD 以 root 身份登录,并设置 DATABASE 以指定要创建的数据库的名称。
docker build -t db:test .docker run -d --rm -p 3306:3306 db:test应该管用。
-d你可以指定“在后台运行”,所以我们看一下运行时的Server代码。服务器
服务器看起来像这样:
. ├── db │ └── Dockerfile ├── front │ ├── Dockerfile │ ├── index.html │ └── nginx.conf └── server << New! ├── Dockerfile << ├── go.mod << ├── go.sum << └── main.go <<DockerfileFROM --platform=linux/x86_64 golang:latest ENV DB_USER root ENV DB_PASS password ENV DB_HOST localhost:3306 ENV DB_NAME testdb WORKDIR /build COPY main.go . COPY go.mod . COPY go.sum . RUN go mod tidy CMD [ "go", "run", "main.go" ]为数据库连接设置环境变量后,
WORKDIR创建并移动一个目录,并将必要的文件复制到该目录中。main.gopackage main import ( "fmt" "io" "net/http" "os" "time" "gorm.io/driver/mysql" "gorm.io/gorm" ) func main() { OtherMSAddr := os.Getenv("OTHER_ADDRESS") ListenAddr := os.Getenv("LISTEN_ADDRESS") DBUser := os.Getenv("DB_USER") DBPass := os.Getenv("DB_PASS") DBAddr := os.Getenv("DB_HOST") DBName := os.Getenv("DB_NAME") user := DBUser + ":" + DBPass dbaddr := "tcp(" + DBAddr + ")" dsn := user + "@" + dbaddr + "/" + DBName + "?charset=utf8mb4&parseTime=True&loc=Local" fmt.Println(dsn) var db *gorm.DB var err error for { time.Sleep(time.Second * 2) db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err == nil { break } fmt.Println("DB を開くのに失敗 : ", DBAddr, " : ", err) } d, _ := db.DB() d.Close() serverHandler := func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("This is Server")) } backendHandler := func(w http.ResponseWriter, r *http.Request) { resp, err := http.Get(OtherMSAddr) if err != nil { w.Write([]byte(err.Error())) return } io.Copy(w, resp.Body) } http.HandleFunc("/server", serverHandler) http.HandleFunc("/otherms", backendHandler) fmt.Println("サーバ listen at ", ListenAddr) if err := http.ListenAndServe(ListenAddr, nil); err != nil { fmt.Println("ListenAndServeに失敗 : ", err) os.Exit(1) } }它是一个服务器,根据容器操作的标准调用和使用环境变量中的每个设置。
go.mod,go.sum可以用下面的命令准备,不用复制。go mod init mylocal/server go mod tidy
docker build -t server:test .docker run --rm -it --network=host server:test如果服务器运行,则与之前运行的 mysql 容器的通信是成功的。
通过指定
--network=host,您可以使用与主机操作系统相同的网络并访问已打开的数据库。稍后完成撰写时,您必须在此处设置好网络并让它进行通信。撰写
现在是时候编写 compose.yml 文件了。
直接在工作目录下准备
docker-compose.yml。. ├── db │ └── Dockerfile ├── docker-compose.yml << New ! ├── front │ ├── Dockerfile │ ├── index.html │ └── nginx.conf └── server ├── Dockerfile ├── go.mod ├── go.sum └── main.go我们将一点一点地重写这个 yml 文件。
为服务器、数据库编写
首先,只组成Server和DB。改写文件内容如下。
码头工人-compose.ymlversion: '3' services: db: image: mysql:5.7 platform: linux/x86_64 environment: - MYSQL_ROOT_PASSWORD=password - MYSQL_DATABASE=testdb networks: - mybackend server: build: server/ environment: - DB_USER=root - DB_PASS=password - DB_HOST=db:3306 - DB_NAME=testdb networks: - mybackend networks: mybackend: driver: bridge我复制了数据库的 Dockerfile 设置。只有一个点设置为
networks,我稍后会解释。对于 Server,我们使用 Dockerfile 来指定 bulid 和覆盖环境变量。我也在此处设置
networks。实际上,通过为每个服务声明网络,您可以指定它所属的“网络”。
通过在文件底部的networks中声明一个名为mybackend的网络,并在每个服务中指定其名称,server、db属于该网络,每个都有对方的服务名称。可用于地址解析.这里
DB_HOST=db:3306是在server环境变量中指定的,但是server可以解析主机名db,所以还是可以正常连接的。如果您执行
docker compose up并等待大约10 秒,您应该可以正常连接。其实不指定
networks也可以用服务名进行名称解析,但是如果不指定,docker compose会创建XXX_default网络并让所有服务都属于它,所以实际上发生了同样的事情。我在这里。同时构图
让我们组成同时在 nginx 上运行的前端。同时暴露每个端口。
添加到 yml 文件。
码头工人-compose.ymlversion: '3' services: db: image: mysql:5.7 platform: linux/x86_64 environment: - MYSQL_ROOT_PASSWORD=password - MYSQL_DATABASE=testdb networks: - mybackend server: build: server/ environment: - DB_USER=root - DB_PASS=password - DB_HOST=db:3306 - DB_NAME=testdb - LISTEN_ADDRESS=:80 networks: - mybackend - mybff ports: - 9999:80 front: build: front/ networks: - mybff ports: - 8000:80 networks: mybackend: driver: bridge mybff: driver: bridge你可以猜到
ports允许你指定要暴露的端口号。添加了一个新的
mybff网络,可以看到front 和server 都属于它。此时的网络配置如下所示:mysql DB ─┬ │ ┌─── Server │ (:9999) │ ┴─── Front (:8000)由于 Front 和 DB 存在于不同的网络中,因此它们无法相互通信。
这一次,Front 作为只服务静态文件的 nginx 运行,但是由于 Front 和 Server 可以解析名称为front,server,所以很容易从前端 nginx 代理到服务器。我很高兴这样.
docker compose up并确保您可以从 localhost 8000、9999 访问它。
将端口转发目的地连接到 MS 和服务器
微服务设置为使用
localhost:15555进行端口转发。如何从服务容器连接到这里?默认情况下,docker 有一个名为
host的网络,所以我可以想办法将其指定为 Server 的网络,但不幸的是它不起作用。
容器无法启动并出现以下错误:failed to create network mydock_myhost: Error response from daemon: only one instance of "host" network is allowed显然,带有主机驱动程序的网络不能与其他网络一起使用。
所以这很天真,但是在做 EXPOST 之后,用外部命令在那里做端口转发似乎很好。
码头工人-compose.ymlversion: '3' services: db: image: mysql:5.7 platform: linux/x86_64 environment: - MYSQL_ROOT_PASSWORD=password - MYSQL_DATABASE=testdb networks: - mybackend server: build: server/ environment: - DB_USER=root - DB_PASS=password - DB_HOST=db:3306 - DB_NAME=testdb - LISTEN_ADDRESS=:80 - OTHER_ADDRESS=:15555 networks: - mybackend - mybff ports: - 9999:80 - 15555:15555 front: build: front/ networks: - mybff ports: - 8000:80 networks: mybackend: driver: bridge mybff: driver: bridge
docker compose up然后端口转发到这里。
完全的。
就这样。
本地端口转发本来打算多一点的,但是没想到主机不能和使用网桥驱动的网络一起使用,结果有点混乱。
如果有人知道如何从参与另一个桥接网络的容器访问主机的端口,请告诉我。
感谢您的阅读。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308629294.html