微服务必备容器化技术

文章目录

  • docker介绍与安装及上手应用
    • 什么是容器化技术?为什么需要学习docker?
    • 如何理解docker
    • docker下载与安装
    • docker的基础组成
    • docker体验
  • dockerfile介绍并创建go-zero环境容器
    • docker的基础组成
    • 从容器构建属于go环境的容器
    • 基于dockerfile构建go容器镜像
    • 通过容器运行go-zero
    • 基于打包部署
    • 小结
  • docker-compose编排
    • 容器网络
    • docker容器互通
    • 部署api/rpc服务通讯
    • docker-compose
    • 总结

docker介绍与安装及上手应用

什么是容器化技术?为什么需要学习docker?

在微服务的体系架构中,因为应用程序会进行拆分,这时就会存在多个服务需要部署运行,相应的多个服务之间具有多种部署方案,这时传统的方式就会面临巨大的挑战。

在特殊时候需要动态的并快速的新增服务或减少服务,例如在秒杀抢购服务在双十一的时候才具有较大的并发流量,流量可能是平时的好几倍需要做好扩容,但是在平时又不存在这么多的流量,因此对整个程序就需要做到自适应伸缩扩容。

就有提出采用虚拟化技术,其代表的产品如VMWare,但是该方案存在问题,就是系统的启动运行需要较长的时间,因此在当时业界就希望有一种轻量级的虚拟化技术来解决这个问题,顾就提出了容器化技术。

容器化技术是一种轻量级的虚拟化技术,它利用操作系统级别的虚拟化来隔离应用程序和它们的依赖。容器化技术使用容器引擎(如Docker)来创建和管理容器,每个容器都运行在共享的操作系统内核上,可以共享主机的资源,而目前容器化技术中docker被广为熟知。

如何理解docker

在我们做项目的时候原本我们是定义为,一个应用程序对应 一个系统。

现在改为:

应用程序  → 特定的docker容器 → 部署到系统中

如下是docker的图标,一条鲸鱼+N个集装箱,实际上这个图标就已经很好的告诉我们docker的寓意和应用

图片描述

docker可以理解为是一个应用程序环境的打包运行工具,比如我们运行redis所依赖的核心程序一起打包成一个封装的独立的集中箱,然后通过docker可以移植部署在不同的环境系统上。这样在部署上就得到了统一,提高了开发和运维的交互效率。

而docker除了这样的功能外,它呢在容器启动停止的时候也是非常快速的,这样的话如果服务需要在某一个时候动态伸缩服务的时候就可以得到解决。

总结docker主要实现的功能

  1. 更高效的利用系统资源
  2. 更快速的启动时间
  3. 一致的运行环境
  4. 持续交付和部署
  5. 更轻松的迁移
  6. 更轻松的维护和扩展

docker下载与安装

# 安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加软件源信息
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 更新并安装Docker-CE --- 可以跳过不执行
yum makecache fast

yum list docker-ce --showduplicates|sort -r
# 默认安装最新版本
yum -y install docker-ce
# 配置docker镜像源和cgroup
mkdir /etc/docker/
touch /etc/docker/daemon.json
cat > /etc/docker/daemon.json << EOF
{
    "exec-opts": ["native.cgroupdriver=systemd"],
    "registry-mirrors": ["https://hub-mirror.c.163.com"]
}
EOF
systemctl enable  docker --now

docker system prune

docker的基础组成

图片描述

  • 宿主机:docker本质就是一个程序,它运行在那个系统上,那个系统就是它的宿主机
  • 容器:一个容器就是一个小的微型系统只有开发需要的依赖可以是运行也可以是非运行的状态
  • 镜像:如果想要创建容器就需要一个镜像,而所谓的镜像可以看成是一份编译好的代码程序,可以这么理解。准确一点理解是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。
  • 仓库:和GitHub/gitee类似,只不过它上面存放的是镜像,你的镜像根文件就是来源这里,当然我们也可以自己发布自己的镜像

dockerfile: 镜像文件的代码版本,我可以通过编写代码创建想要的镜像。

docker体验

docker安装完成后,在命令行执行docker,如果能够看到命令提示则说明docker安装成功。我们可以通过通过doker创建一个安装好redis的容器。

仓库地址:hub.docker.com

# docker pull 是从仓库上下载容器; 在pull后则就是具体要下载的容器对象
docker pull redis:alpine3.18

# docker images 查看系统的镜像
docker images

# 通过docker run 运行启动
docker run -p 16379:6379 --name=redis -d redis:alpine3.18

# docker ps 查看正在运行的容器, 后跟 -a 则查询所有的包含停止的容器
docker ps 

解释:docker run帮助我们在宿主机中创建一个redis的容器,这个容器呢可谓是“麻雀虽小五脏俱全”在容器中会运行一个系统可以是centos也可以是ubuntu.当前我们使用的是一个叫alpine的系统,比centos和ubuntu要小。

图片描述
接下来看看docker run后面的命令参数-p 16379:6379 --name=redis -d redis:alpine3.18

  • 最后一个redis:alpine3.18 基本是固定的写法,即容器镜像,也就是docker pull 后指定的内容
  • -d 表示运行方式,我们让容器处于后台运行,而不是挂起在当前的命令行下运行
  • –name 指创建的容器名称,当前指定容器的名称为redis
  • -p 用于绑定容器与宿主机的端口,也就是第一个参数是属于宿主机的第二个属于容器的,也就是当我们访问16379的时候就会访问到redis容器的6379端口的服务。

dockerfile介绍并创建go-zero环境容器

docker的基础组成

图片描述

  • 宿主机:docker本质就是一个程序,它运行在那个系统上,那个系统就是它的宿主机
  • 容器:一个容器就是一个小的微型系统只有开发需要的依赖,可以是运行也可以是非运行的状态
  • 镜像:如果想要创建容器就需要一个镜像,而所谓的镜像可以看成是一份编译好的代码程序,可以这么理解。准确一点理解是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。
  • 仓库:和GitHub/gitee类似,只不过它上面存放的是镜像,当然我们也可以自己发布自己的镜像
  • dockerfile: 镜像文件的代码版本,我可以通过编写代码创建想要的镜像。

从容器构建属于go环境的容器

在dockerfile的学习中遇到过很多的同学对其概念以及dockerfile构建中存在的问题,及dockerfile的运行本质并不是很了解,因此在学习dockerfile前,我们先自己通过以存在的容器构建go环境的容器。

过程:

  1. 选择好要构建go容器环境的系统
  2. 先拉取系统镜像
  3. 然后运行系统镜像,构建出容器
  4. 再进入容器安装go

选择好要构建go容器环境的系统

系统的选择很讲究,因为对docker来说,它是依托于宿主机运行的,同时我们也希望它能够启动运行足够快并且还期望它不占用太多的系统资源,顾选择较小的系统就有这块的优势。

这里我们用alpine系统作为go环境的运行系统,因为它比较小,大约7M的左右。

先拉取系统镜像

docker pull alpine:3.18

然后运行系统镜像,构建出容器

docker run -p 8080:80 --name go -d alpine:3.18

在构建容器的时候与宿主机绑定8080端口,可便于后续容器构建好之后的测试

再进入容器安装go相关所需要的环境

docker exec -itd 容器名 执行命令[sh]
# OPTIONS说明:
-d :分离模式: 在后台运行
-i :即使没有附加也保持STDIN 打开
-t :分配一个伪终端

我在命令后使用sh,代表在终端中进行交互,sh是docker绝大数容器都是使用的交互命令。

[root@192 ~]# docker exec -it go sh
Error response from daemon: Container 5dc7cf5ac8478f36d0dfc9e6c68e84df306af8cfdb0e1a79be6b0978a3c80307 is not running

但是当我们用docker exec 进入go容器的时候出现,go容器没有在运行,为什么?这是和docker的运行有关,docker对于启动的容器要求必须存在一个能够挂起运行的状态,否则程序启动即停止。

这很好理解因为对于docker来说它验证的容器是必须能够一直运行的,但是呢我们创建的容器因为没有挂起程序所以对docker来说相当于没有运行。

# 先删除go容器,然后重新docker run创建
docker rm go

# 在docker run的时候在命令的最后增加一个一定能挂起运行的命令,比如ping
docker run -p 8080:80 --name go -d alpine:3.18 ping www.baidu.com

# 再docker ps 查看即可发现存在
docker ps 

这个时候我们继续使用docker exec -it go sh命令进入到go容器中,完成go环境的搭建。

进入容器安装go

在进入到容器中安装go的时候,我们需要注意一点,即就是将安装过程所用到的命令和操作记录起来,因为在dockerfile中也会用到这些命令来构建容器。

mkdir /go
cd /go
wget --no-check-certificate https://golang.google.cn/dl/go1.21.0.linux-amd64.tar.gz
tar -C /usr/local -zxf go1.21.0.linux-amd64.tar.gz

# 此处我们需要删除已经解压后的go包,因为对于docker来说下载下来的文件也会影响到它的大小
# 因我们期望docker是足够的小,顾对于一些无用的软件文件是建议删除卸载
rm -rf /go/go1.21.0.linux-amd64.tar.gz 

# 注意!这一步是因为alpine系统对go程序运行的时候默认查找的类库与提供的类库不一致
# 故需要做此操作。
mkdir /lib64
ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2

# 配置go的系统环境
export GOPATH=/go
export PATH=/usr/local/go/bin:$GOPATH/bin:$PATH

# 验证
go version
go version go1.21.0 linux/amd64

基于dockerfile构建go容器镜像

在上面的过程中,我们是先构建好一个初始化的alpine系统,然后进入到alpine系统中逐步安装go,但在应用开发中上面的构建过程大都是相同的,如何提升效率呢?

这个时候我们就可以通过dockerfile帮助我们提高构建的效率,它的工作方式就是将我们要在容器中执行的命令操作,事先写在dockerfile中,docker可以识别dockerfile中的内容,然后把它们翻译成容器会执行的命令,然后准备执行命令并最终构建好一个镜像,我们再根据镜像启动容器。
图片描述

这样做的好处就是,用户只需要调整dockerfile文件中的命令,即可构建不同的容器,大大提供了构建容器的效率,同时因为是文件的关系其他人也可以通过dockerfile了解到系统是如何搭建程序的。

# 我们需要引入到基础容器
FROM alpine:3.18

# 注意看这里我们的写法, 在sh中 && 可以表示下一条命令连续执行,而 \ 则是命令的分隔符号
# 思考:为什么这么写?
RUN mkdir /go && cd /go \
    && wget --no-check-certificate https://golang.google.cn/dl/go1.21.0.linux-amd64.tar.gz \
    && tar -C /usr/local -zxf go1.21.0.linux-amd64.tar.gz \
    && rm -rf /go/go1.21.0.linux-amd64.tar.gz \
    && mkdir /lib64 \
    && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 
   
# 配置系统环境变量
# 在dockerfile中对容器的系统环境变量配置统一采用ENV这个关键词定义
ENV GOPATH /go
ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH

# 这个命令可以让我们的docker容器在启动的时候就执行下面的命令
# 把原本在docker run中的命令放到dockerfile中,并示意启动容器的时候执行
# 但是如果在docker run后跟新的命令会代替CMD中的命令
CMD ["ping", "www.baidu.com"]

补充注意,在dockerfile中的注释前不能有空格;然后通过如下命令构建docker容器。

# docker build 即根据dockerfile构建镜像,-t 是指构建容器的名称格式是 name:tag 或 name
# . 是指使用当前目录下的dockerfile构建,也可以通过 -f 参数指定
docker build -t go .

# 检查是否构建好
docker images

# 如果存在构建不好或者多余的镜像可以通过如下命令删除,-f 是强制删除
docker rmi 镜像名 

# 根据镜像构建容器
docker run --name go -d go

# 然后我们可以执行下面的命令测试
docker exec -it go go

通过容器运行go-zero

我预先调整了user/api中的代码,使得api暂时不关联其他程序。

import (
	"github.com/zeromicro/go-zero/rest"
	"demo/user/api/internal/config"
	"demo/user/api/internal/middleware"
	"demo/user/rpc/userclient"
)

type ServiceContext struct {
	Config            config.Config
	UserClient        userclient.User
	LoginVerification rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config: c,
		//UserClient:        userclient.NewUser(zrpc.MustNewClient(c.UserRPC)),
		LoginVerification: middleware.NewLoginVerificationMiddleware().Handle,
	}
}

func (l *UserLogic) User(req *types.UserReq) (resp *types.UserResp, err error) {
	// todo: add your logic here and delete this line
	return &types.UserResp{
		Id:    "666",
		Name:  "木兮老师",
		Phone: "13011110000",
	}, nil
}

因为对当前的我们来说,先学习如何通过容器运行go-zero是关键,而增加复杂度反而效果不佳,所以先使得api服务不与其他服务关联,后面会讲如何关联访问。

我们将go-zero上传到服务中

//这是我上传的目录地址

/go/user

我们构建一个新的容器,用于启动并访问go-zero

docker run -p 8888:8888 -v /go/user:/go/src/demo/user --name go-zero -d go 

在上面的命令中通过-p绑定容器与宿主机的端口,而-v是绑定容器与宿主机共享的目录,也就是这个目录下的内容会影响到双方,称之为数据卷。

然后进入容器

# 先执行如下命令
go env -w GOPROXY=https://goproxy.io
# 然后下载依赖
go mod tidy
# 运行
cd api/
go run .

然后测试即可

基于打包部署

除了上面的方式外我们还可以基于编译程序运行,这种会相对简单,即将go-zero预先编译打包,然后通过dockerfile提前复制到容器中并启动的时候执行。这种方式我们可以应用在服务发布测试或者生产的时候。

o'o# CGO_ENABLED=0 
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/user-api ./api/user.go

然后我们新建一个新的dockerfile,在新的dockerfile中我们可以选择继续基于go这个镜像构建也可以基于alpine镜像构建,但目前我们采取的方式是使用编译后go-zero项目的二进制文件部署因此可以直接用alpine镜像最佳,因为不需要go的环境

FROM alpine:3.18

# 这个关键词的意思是复制的意思,可以将宿主机中的内容复制到容器中
# 命令 左边是宿主机的目录,右边是容器目录
RUN mkdir /user && mkdir /user/bin && mkdir /user/conf

# 复制编译后的二进制文件
COPY bin/user-api /user/bin/
# 复制配置文件
COPY api/etc/user.yaml /user/conf/

# 为二进制提供执行权限
RUN chmod +x /user/bin/user-api

# 该命令指定容器会默认进入那个目录,如我们每次进入服务器的时候会自动进入root目录一样的作用
WORKDIR /user

# 这个命令可以让我们的docker容器在启动的时候就执行下面的命令
# 与CMD不同之处是,在docker run 后跟的命令不能替换它,它仍然会启动的时候执行
ENTRYPOINT ["bin/user-api", "-f","/user/conf/user.yaml"]

构建容器

docker build -t user-api .
docker run -p 8888:8888 --name go-zero -d user-api

小结

本节主要讲解dockerfile的使用,在dockerfile使用过程中大家可能会遇到一些问题

问题1:dockerfile构建过程中出现问题如何解决

解:这个问题在构建的时候可以先构建好dockerfile中使用的基础镜像,然后再把dockerfile中使用的命令放到容器中逐条执行验证

问题2:概念误解,如我构建一个go的容器,有的同学可能会误以为这是go的系统

docker-compose编排

容器网络

我们先了解容器的网络,在后续的使用中,我们会涉及到容器的网络应用,它是docker体系知识中一个重要的知识点。

docker在安装后运行的时候会默认创建三个网络,我们可以通过docker network ls查看所有的网络。

[root@192 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
252a2ca6302a   bridge    bridge    local
65a576c82770   host      host      local
e0a7aac94266   none      null      local

none模式

在这种模式下容器会独立network,但并没有对其进行任何的网络设置,如分配ip等。

bridge模式

在该模式中,Docker 会创建一个虚拟以太网桥 docker0,新建的容器会自动桥接到这个接口,然后docker会依据docker0在创建的时候设立的网络段给容器分配ip。

图片描述
但默认的方式存在一个问题,就是每次重启docker默认的网络段也会发生变化,而采用该网络段的docker容器也会跟着发生变化。当然我们也可以自定义一个网络段,然后自己给创建的容器分配指定的ip。

# 命令格式
docker network create --subnet=<subnet> <network_name>
docker network create --subnet=172.0.0.0/24 net-test

# 查看网络段详情
docker network inspect net-test

# 删除网络段
docker network rm net-test

# 给容器分配网络段
docker run --name go-net-test --ip 172.0.0.2 --net net-test -d go

# 结果
[root@192 ~]# docker exec -it go-net-test ipaddr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
43: eth0@if44: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:00:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.0.0.2/24 brd 172.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
[root@192 ~]# 

host模式

  • host 网络模式需要在创建容器时通过参数 --net host 或者 --network host 指定;
  • 采用 host 网络模式的 Docker Container,可以直接使用宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换;
  • host 网络模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。

图片描述

docker容器互通

再看看docker容器的互通,即在docker中内部之间又如何互通呢?在容器中的连接通讯方式我们可以采用三种方式

  1. 通过宿主机做桥接
  2. 容器都在同一网络段
  3. 通过link命令

1.通过宿主机做桥接

这种方式的话就是在构建容器的时候与宿主机绑定端口,对外暴露服务的方式。

docker run -p 80:80 -d go

容器内部之间的请求先走宿主机,然后再通过宿主机访问到容器,一般在跨服务的时候应用。

2.容器都在同一网络段

通过自定义网络段,然后让创建的容器都处于在这个网络段中,容器之间就可以通过分配好的网络ip相互之间即可访问。如下哎例子

docker run --name go-net-test3 --ip 172.0.0.3 --net net-test -d go

[root@192 ~]# docker exec -it go-net-test sh
/ # ping 172.0.0.3
PING 172.0.0.3 (172.0.0.3): 56 data bytes
64 bytes from 172.0.0.3: seq=0 ttl=64 time=0.202 ms
64 bytes from 172.0.0.3: seq=1 ttl=64 time=0.123 ms

这种方式可以应用在不同的服务类型中。

3.通过link命令

我们可以在构建容器的时候添加一个–link的指令,使得容器在构建出来后,就可以与指定的容器进行绑定。两个容器之间可以通过容器名相互访问,但是这种方式仍然需要在同一个网络段下,默认使用的是docker0这个网络段。

docker run --name go-net4 --link go-net-test --net net-test -d go

[root@192 ~]# docker exec -it go-net-test sh
/ # ping go-net4
PING go-net4 (172.0.0.4): 56 data bytes
64 bytes from 172.0.0.4: seq=0 ttl=64 time=0.203 ms
64 bytes from 172.0.0.4: seq=1 ttl=64 time=0.213 ms

因此3需要基于2的基础上进行实现,在使用上3会有较好的优势,因为无需关注具体的网络ip,可以之间通过容器名进行访问。

部署api/rpc服务通讯

接着上次的内容将rpc服务通过docker部署并实现连调,在代码层面注意修改api服务在核心代码层对rpc客户端的初始化

type ServiceContext struct {
	Config            config.Config
	UserClient        userclient.User
	LoginVerification rest.Middleware
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:            c,
		UserClient:        userclient.NewUser(zrpc.MustNewClient(c.UserRPC)),
		LoginVerification: middleware.NewLoginVerificationMiddleware().Handle,
	}
}

然后调整一下rpc中的数据来源,暂时不来源redis和mysql这将作为练习尝试。在开始的时候注意还需要先pull一个etcd,作为服务发现注册机制。

# 先停止所有容器,并进行删除
docker ps -a -q | xargs docker stop | xargs docker rm

如下是关于etcd的dockerfile_etcd

FROM bitnami/etcd:3.4.15

ENV ETCD_ENABLE_V2 true
ENV ALLOW_NONE_AUTHENTICATION yes
ENV ETCD_ADVERTISE_CLIENT_URLS http://etcd:2379
ENV ETCD_LISTEN_CLIENT_URLS http://0.0.0.0:2379
ENV ETCD_NAME etcd

构建镜像

docker build -t etcd -f ./Dockerfile_etcd .

docker run -d -p 2379:2379 -p 2380:2380 --net user --ip 168.10.0.20 --name etcd etcd

构建过程

  1. 构建好user-rpc的dockerfile
  2. 定义好桥接网络段
  3. 部署服务并使用该网段
  4. 通过容器名访问服务

构建好user-rpc的dockerfile

使用二进制的方式

FROM alpine:3.18

# 这个关键词的意思是复制的意思,可以将宿主机中的内容复制到容器中
# 命令 左边是宿主机的目录,右边是容器目录
RUN mkdir /user && mkdir /user/bin && mkdir /user/conf

# 复制编译后的二进制文件
COPY bin/user-rpc /user/bin/
# 复制配置文件
COPY rpc/etc/user.yaml /user/conf/

# 为二进制提供执行权限
RUN chmod +x /user/bin/user-rpc

# 该命令指定容器会默认进入那个目录,如我们每次进入服务器的时候会自动进入root目录一样的作用
WORKDIR /user

# 这个命令可以让我们的docker容器在启动的时候就执行下面的命令
# 与CMD不同之处是,在docker run 后跟的命令不能替换它,它仍然会启动的时候执行
ENTRYPOINT ["bin/user-rpc", "-f","/user/conf/user.yaml"]

注意修改配置

Name: User
Host: 0.0.0.0
Port: 8888
UserRPC:
  Etcd:
    Hosts:
      - etcd:2379
    Key: user.rpc
Name: user.rpc
ListenOn: 0.0.0.0:8080
Etcd:
  Hosts:
  - etcd:2379
  Key: user.rpc
Mysql:
  DataSource: root:000000@tcp(127.0.0.1:3306)/im?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache:
  - Host: 127.0.0.1:6379
    Type: node
    Pass:

构建好user-rpc并重新构建user-api

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/user-api ./api/user.go
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/user-rpc ./rpc/user.go


docker build -t user-api -f ./Dockerfile .
docker build -t user-rpc -f ./Dockerfile_rpc .

定义好桥接网络段

docker network create --subnet=175.0.0.0/24 user

部署服务并使用该网段

docker run --name etcd -p 2379:2379 --net user --ip 168.10.0.100 -d etcd
docker run --name user-rpc -p 8080:8080 --net user --ip 168.10.0.70 --link etcd -d user-rpc
docker run --name user-api -p 8888:8888 --net user --ip 168.10.0.50 --link etcd -d user-api

# etcd查询所有的key
etcdctl get --prefix ""

docker ps

docker-compose

在前面的内容中我们基本上就已经实现了服务的部署,但是在过程中存在问题,即我们可以看到在构建api/rpc/etcd容器的时候实际上过程是比较繁琐的,并且又具有较多重复性的工作,这个时候我们的需求就是期望有一个工具可以很好的帮助我们管理容器,可以一键启动所有容器一键停止所有容器,快速配置各个容器的命令参数。

这个时候我们就可以用到docker编排工具docker-compose,它是一个用于定义和运行多容器 Docker 应用程序的工具。它允许您使用一个简单的 YAML 文件来配置应用程序的各个服务,并通过一条命令启动、停止和管理这些服务。

通过使用 Docker Compose,您可以轻松地构建和管理应用程序的多个服务,例如数据库、Web 服务、消息队列等。它利用了 Docker 强大的容器化技术,使得应用程序的部署和扩展变得更加简单和可靠。

安装

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

docker-compose

# 离线方式
# 从github上下载对应版本
mv docker-compose-linux-x86_64 /usr/local/bin/
cd /usr/local/bin/
//修改文件名
mv docker-compose-linux-x86_64 docker-compose

//授权
sudo chmod +x /usr/local/bin/docker-compose

//查看安装是否成功
docker-compose -v

配置docker-compose.yaml

mkdir -p /etcd/data
mkdir -p /etcd/logs

chmod -R 777 ./etcd/data
chmod -R 777 ./etcd/logs
version: "3"

services:
  # 服务名
  etcd:
    # 选择镜像:镜像的选择会先从本地找,如果没有会去仓库中拉取下来
    image: etcd
    # 可以选择指定的dockerfile自动帮我构建
    build:
      # 指定dockerfile所在的目录
      context: ./ 
      # 指定dockerfile的文件名
      dockerfile: dockerfile_etcd
    # 定义创建的容器名
    container_name: etcd
    # 使创建的容器与宿主机绑定端口
    ports:
      - "2379:2379"
      - "2380:2380"
    # 配置系统环境变量  
    environment:
      - ETCD_ENABLE_V2=true
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
      - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
      - ETCD_NAME=etcd
    # 配置容器与宿主机的共享目录  
    # 同时需要注意宿主机存在目录,并且要基于权限不然系统会直接报错
    volumes:
      - ./etcd/data:/bitnami/etcd/data
      - ./etcd/logs:/bitnami/etcd/logs
    # 设置容器的网络段  
    networks:
      guser:
   
  user-rpc:
    image: user-rpc
    container_name: user-rpc
    ports:
      - "8080:8080"
    # 与etcd服务互通  
    links:
      - etcd
    # 需要等指定容器启动后才可以启动,填写的是容器的服务名
    depends_on:  
      - etcd
    networks:
      guser:
      
  user-api:
    image: user-api
    container_name: user-api
    ports:
      - "8888:8888"
    # 与etcd服务互通  
    links:
      - etcd
    # 需要等指定容器启动后才可以启动,填写的是容器的服务名
    depends_on:  
      - etcd
      - user-rpc
    networks:
      guser:      
# 由docker创建      
networks:
  guser:
    driver: bridge

总结

在本节中主要讲解docker的网络以及docker-compose,通过docker部署好user的rpc与api服务,

在内本节中大家可能会遇到的问题有

容器之间无法实现互通

这个问题是很多初学者学习docker的时候经常会遇到的问题,解决思路。

  1. 首先梳理自己的镜像构建过程,容器的启动命令及过程,看看是否存在配置上 的问题
  2. 然后再看自己在配置和构建的时候api、rpc的配置信息是否有误
  3. 在调试中,你可以先部署一个服务,然后进入容器中通过利用ping检查是否可以访问到目标容器,然后用curl检测是否可以访问到容器中的程序【注意两个命令的不同哟】
  4. 问题往往会出现在如下情况
    1. 配置不正确
    2. 网络段不一致
    3. 容器中的服务没有启动
    4. 如果设置过自己宿主机的防火墙则需要重启docker

关于docker中的共享目录

在构建docker容器的时候,我们不能把它当做一个普通的程序看待,而是作为一个随时可运行可删除的程序来看待,当docker停止删除随之也会删除docker容器这个过程中在容器内部产生的数据,因此在程序中使用docker的时候,我们需要思考容器的数据是否需要保留以及重要性,如mysql、redis在用docker的时候就需要配置好数据卷共享目录,以免docker容器的停止运行造成数据的丢失问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

没有名为 keras.preprocessing 的模块

估计是因为版本原因 我安装的是 3.3.3版本 >>> import keras >>> print(keras.__version__) 3.3.3 keras.preprocessing.image 将 keras.preprocessing.image 改为 from keras_preprocessing.image 之后报image_type啥的错误&#xff0c;后面查找之后…

java问题解决: IDEA java 警告 源发行版 17 需要目标发行版 17

效果图 问题原因 jdk和你实际安装的jdk不匹配 解决问题 1.点击File -->Project Structure–>Project 修改这两处 2. 在Project Structure–>Modules中的红框位置都要调整对应版本 3、点击File–>settings–>java compile将对应框的版本修改成对应版本即可–改…

运算放大器(运放)缓冲器(跟随器)电路

运算放大器(Operational Amplifier) 运算放大器(Operational Amplifier)是一种差分放大器&#xff0c;具有高输入电阻、低输出电阻、高开放增益&#xff08;开环增益&#xff09;&#xff0c;并具有可放大输入引脚与-输入引脚间的电压差的功能。 设计目标 输入输入输出输出频…

揭开AI大模型的神秘面纱:一文看懂GPT-4的核心技术

近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术迅猛发展&#xff0c;特别是AI大模型的崛起&#xff0c;给人们的生活和工作带来了深远的影响。作为其中的佼佼者&#xff0c;GPT-4备受瞩目。那么&#xff0c;GPT-4的核心技术究竟是什么&#xff1f;它是如何运作的&a…

Chrome插件开发入门:手把手教你创建第一个扩展

问题背景 最近&#xff0c;客户发布了一个新的任务 —— 开发一个Chrome插件。之前没有这方面的开发经验&#xff0c;准备想学习一下这块的内容&#xff0c;我发现网上的大多数视频都是几年前的&#xff0c;开发版本都是基于MV2&#xff0c;当前谷歌已经开始使用MV3&#xff0…

python使用哪种数据库

MySQL 是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Database Management System&#xff0c;关…

python flask配置邮箱发送功能,使用flask_mail模块

&#x1f308;所属专栏&#xff1a;【Flask】✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的点…

【C++高阶】高效搜索的秘密:深入解析搜索二叉树

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C多态 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀二叉搜索树 &#x1f4d2;1. 二叉搜索树&…

学习笔记——路由网络基础——路由度量值

3、路由度量值 (1)基本概念 路由度量值表示到达这条路由所指目的地址的代价。度量值数值越小越优先&#xff0c;度量值最小路由将会被添加到路由表中。度量值很多时候被称为开销(Cost)。 路由度量(路由开销 cost)对于同一个路由协议&#xff0c;当到达某目标网段有多条路由供…

适配不同数据库厂商方案

背景 在对国产化数据有要求的时候&#xff0c;我们会做对 达梦、海量等数据库的配置。 有些SQL 以前没有写成标准SQL&#xff1b; 那么适配的时候怎么办呢&#xff1f;改成标准SQL。 如果不好改呢&#xff1f;比如SQL比较复杂等&#xff0c;需要判断 当前是哪个厂商的数据库…

HTML星空特效

目录 写在前面 完整代码 代码分析 运行效果 系列文章 写在后面 写在前面 100行代码实现HTML星空特效。 完整代码 全部代码如下。 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&g…

数据结构与算法1

一、概述 数据结构&#xff08;逻辑结构、存储结构、算法&#xff09; 数据项 ∈ 数据元素(记录) ∈ 数据。 数据元素&#xff08;结点&#xff09;&#xff1a;数据的基本单位。数据项&#xff1a;不可分割&#xff0c;最小数据单位。数据对象 &#xff1a;性质相同的数据元素…

HTTP学习记录(基于菜鸟教程)

文章目录 1.简介1.1常用的HTTP方法1.2Http版本1.3注意事项 2.Https3.Http消息结构3.1客户端请求消息3.2响应消息 4.常见的响应头5.HTTP状态码6.Http content-type在这里插入图片描述 7.MIME类型8.HTTP2 1.简介 Http&#xff0c;被称为超文本传输协议&#xff0c;HyperText Tran…

RK3588 Android12音频驱动分析全网最全

最近没有搞音频相关的了&#xff0c;在搞BMS, 把之前的经验总结一下。 一、先看一下Android 12音频总架构 从这张图可以看到音频数据流一共经过了3个用户空间层的进程&#xff0c;然后才流到kernel驱动层。Android版本越高&#xff0c;通用性越高&#xff0c;耦合性越低&#…

python flask 入门-helloworld

学习视频链接&#xff1a; 01-【前奏】课程介绍_哔哩哔哩_bilibili 1.安装flask pip install flask 踩坑记&#xff1a;本机不要连代理&#xff0c;否则无法install 提示报错valueError: check_hostname requires server_hostname 2.程序编写 在根目录下创建 app.py fr…

尚硅谷爬虫学习第一天(2) 爬虫案例

import urllib.request# 下载网页 url_page http://www.baidu.com # url 代表下载的路径&#xff0c;filename 代表文件的名字 # urllib.request.urlretrieve(url_page,baidu.html) # 在python中 可以写变量的名字&#xff0c;也可以直接写值,这不就是java吗# 下载图片 # url_…

Mybatis(根据id查找这一行的数据)

首先在查询之前&#xff0c;我们先要做些基础的工作先创建一个以你的数据库命名的model类 我的数据库的名字叫admin 我就创建了一个Admin的类 用来方便数据的访问 然后我们就要创建一个接口来声明我们要写的方法 我创建的接口命名为AdminDao 在创建一个xml的类用来实现声明的…

把Deepin塞进U盘,即插即用!Deepin To Go来袭

前言 小伙伴之前在某篇文章下留言说&#xff1a;把Deepin塞进U盘的教程。 这不就来了吗&#xff1f; 事实是可以的。这时候你要先做点小准备&#xff1a; 一个大小为8GB或以上的普通U盘 一个至少64GB或以上的高速U盘 一个Deepin系统镜像文件 普通U盘的大概介绍&#xff1…

Flink 资源静态调度

本内容是根据 Flink 1.18.0-Scala_2.12 版本源码梳理而来。本文主要讲述任务提交时&#xff0c;为 Task 分配资源的过程。 以下是具体步骤讲解&#xff1a; TaskManager 资源注册 TaskManager 在启动时&#xff0c;会向 ResourceManager 注册资源。ResourceManager 会将 Tas…

AI 代理可以改变 B2B 电子商务的业务动态

今天你听到的都是人工智能&#xff0c;这是有原因的。在过去 18 个月里&#xff0c;我们经历了比以往更多的人工智能创新。人工智能一夜之间走出了实验室&#xff0c;并成为可行的商业驱动力。 一个有望赢得巨大胜利的行业是 B2B电子商务。事实上&#xff0c;B2B 电子商务可以…