关于这篇文章

docker compose 一起动手学习,同时创建一个简单的容器网络。

我认为你的团队在该领域负责的多个微服务(MS)依赖于其他微服务是一种常见的情况,你不能轻易地测试手头的业务场景。
因此,我总结了我调查的内容,目的是准备docker-compose.yml,该docker-compose.yml通过端口转发将多个容器连接到其他MS,并以动手操作的形式操作它们。

以下人员(即我自己)有资格

  • 我接触过docker
  • 我知道docker compose的存在
  • 每次我写一个 Dockerfile docker-compose.yml 时都在谷歌上搜索
  • 我对 docker 中的网络一无所知
  • 即使我阅读了参考资料,我也无法获得任何与 docker 相关的信息
  • 我手头有 golang(用 Go 编写服务器模拟。任何其他语言都可以)

什么是 Docker?

既然我学习了,我会把它写成备忘录。如果您不单独阅读,动手操作很好。

什么是码头工人

Docker 通过使用容器虚拟化的操作系统级虚拟化将应用程序与开发和执行环境隔离,从而实现应用程序的快速交付。并且环境本身可以以与应用程序相同的方式作为代码(图像)进行管理。

Docker-维基百科

Docker 提供“容器”虚拟化,与仿真不同,后者通常与“虚拟化”一词相关联。我说的是“虚拟化”,但在熟悉它的人看来,容器的“隔离”比“虚拟化”更准确地代表了实际情况。

“容器”是使用 Linux 特性实现的隔离进程。创建隔离进程的程序称为容器运行时。

容器运行时结合了pivot_rootcgroup等特性,以及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-composedocker compose会出现,但是截至2022年10月,docker-composedeprecated,并且会在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 <<
Dockerfile
FROM --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。

DockerfileEXPOSE实际上并没有打开端口,只是一个文档,所以这里需要指定端口,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.html
Dockerfile
FROM --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    <<
Dockerfile
FROM --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.go
package 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.yml
version: '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的网络,并在每个服务中指定其名称,serverdb属于该网络,每个都有对方的服务名称。可用于地址解析.

这里DB_HOST=db:3306是在server环境变量中指定的,但是server可以解析主机名db,所以还是可以正常连接的。

如果您执行docker compose up 并等待大约10 秒,您应该可以正常连接。

其实不指定networks也可以用服务名进行名称解析,但是如果不指定,docker compose会创建XXX_default网络并让所有服务都属于它,所以实际上发生了同样的事情。我在这里。

同时构图

让我们组成同时在 nginx 上运行的前端。同时暴露每个端口。

添加到 yml 文件。

码头工人-compose.yml
version: '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 可以解析名称为 frontserver,所以很容易从前端 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.yml
version: '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

相关文章:

  • 2021-08-01
  • 2022-02-20
  • 2021-06-07
  • 2021-08-20
  • 2022-12-23
  • 2021-05-17
  • 2021-12-27
猜你喜欢
  • 2022-12-23
  • 2021-08-13
  • 2021-12-27
  • 2021-08-31
  • 2021-11-01
  • 2021-05-25
  • 2022-12-23
相关资源
相似解决方案