dockerfile避坑笔记(VMWare下使用Ubuntu在Ubuntu20.04基础镜像下docker打包多个go项目)

一、docker简介

docker是一种方便跨平台迁移应用的程序,通过docker可以实现在同一类操作系统中,如Ubuntu和RedHat两个linux操作系统中,实现程序的跨平台部署。比如我在Ubuntu中打包了一个go项目的docker镜像(镜像为二进制文件,相当于windows中的exe文件),这个镜像可以直接在另一台Ubuntu上运行,而无需再次配运行环境。下面这张图是我对docker的理解。
原始程序迁移方式
docker镜像迁移方式

二、dockerfile编写

将项目打包为docker镜像通过编写dockerfile来实现,dockerfile的写法类似于shell编程。

PS:最终的dockerfile不易过长(但是调试时可以多些RUN,这样可以减少重复编译,提高调试速度),因为每个语句都会被编译为一个镜像,然后commit到一起形成一个最终的镜像。

其基本的结构如下:

sh 复制代码
FROM ***(指定基础镜像,即这个镜像在哪个操作系统下面运行)
#示例:FROM  Ubuntu:20.04

MAINTAINER ***(指定维护者信息,选填)
#示例:MAINTAINER Tom

LABEL ***(docker build的启动入口,可以不写)
#示例:LABEL helloworld

RUN ***(docker build时需要执行的命令,为shell指令)
#RUN git clone github.com/xxxx.git

ADD/COPY ***(将宿主机的文件拷贝到目标镜像中,ADD会自动解压,COPY不会)
#示例:COPY 	. .
#前一个目录为当前文件夹下的相对路径,后一个文件为dockerfile镜像中的路径

WORKDIR ***(设置当前工作目录,相当于进入容器后在哪个目录里面)


VOLUME ***(存放文件的地方,也叫挂载主机目录,分布式存储中使用)

EXPOSE ***(指定对外的端口)

CMD ***(指定容器启动后要做的事情)

打包镜像:

go 复制代码
docker build -t demo/go-hello:1.0 -f dockerfile .
#docker build为固定写法,-t表示生成目标镜像的名字以及版本号 -f表示dockerfile文件的名称,
#最后的'.'表示当前目录下构建docker

构建运行容器:

go 复制代码
docker run  -it -p xxx:xxx
#-i 以交互模式运行,-t 为容器分配一个命令行
#-p 容器端口映射到主机端口的模式

三、docker build原理

完成dockerfile后,需要运行docker build命令来执行程序。

docker build的执行过程如下:

1.将上下文打包发送到docker的守护进程
2.docker build 命令向docker server 发送http请求,请求包含上下文信息。
3.docker server开始构建镜像:

①创建一个临时目录,将上下文中的内容解压到临时目录下,然后读取dockerfile中的指令。

②将执行分发到不同的模块进行操作,为每一条指令构造一个临时容器并执行,执行完毕后commit。

③将所有commit的镜像合并,得到最终的镜像。

这里需要注意,docker build会将当前文件下的所有文件发送到docker server,如果有些文件在docker过程中并不需要,可以在当前目录下创建忽略 .dockerignore 文件,并写入忽略文件的文件名。

docker避坑笔记

调试背景

我在windows10下安装VMWare,WMware中安装了Ubuntu20.04,并使用桥接模式连接的windows10主机。(说明虚拟机与主机的IP地址互相独立,Ubuntu无法通过127.0.0.1访问Windos10),同时在Ubuntu20.04基础镜像下打包golang项目的镜像。

问题1:docker build运行dockerfile报错,E: Unable to locate package xxx

这里报错有很多原因,大概可以归为以下几类:

1、当前的apt-get不是最新版,所以无法找到这个库
解决方案,运行apt-get update

bash 复制代码
apt-get update

2、使用上述方法后,仍然报错,可能就是真的没这个包,或者这个包在外网下载,国内无法访问或访问速度过慢。这里采用换源的方法,看看能不能解决。
解决方案:在dockerfile中加入

bash 复制代码
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list

问题2:dockerfile安装golang时报错,显示Do you want to continue? [Y/n] Abort.

dockerfile运行过程中,无法接受用户的输入,但是有些软件包的安装过程需要用户进行交互,所以会出现问题。
解决方案:在dockerfile中加入下面两句话:

bash 复制代码
ENV DEBIAN_FRONTEND=noninteractive #关闭交互功能

apt-get install -y xxx #-y表示所有的交互都是选择默认的选项,xxx为需要安装的包名

问题3:dockerfile运行apt-get install golang时报错,显示Undefined:any

开发环境为golang1.21.3,但是是用apt-get install golang默认下载的版本小于golang1.18,,而any关键字是golang1.18以后才引入的关键字,所以会没法编译。
解决方案:不使用apt-get install golang安装,更换下面的方式

bash 复制代码
#指定版本安装golang
RUN apt-get update && apt-get install -y wget && \
    wget https://mirrors.aliyun.com/golang/go1.21.3.linux-amd64.tar.gz && \
    tar -C /usr/local -xzf go1.21.3.linux-amd64.tar.gz && \
    rm go1.21.3.linux-amd64.tar.gz

# 设置Go的环境变量
ENV PATH=$PATH:/usr/local/go/bin

问题4:开启容器后,使用Ubuntu访问容器端口,发现连接被重置(等于容器的端口连不上)。

这里涉及到了2个知识,
第一个知识是,容器默认的网络模式是桥接,即容器内部具有独立的IP地址,通过0.0.0.0IP对外提供服务。所以,如果程序内部原来通过127.0.0.1:8888的方式对其他容器或者主机提供服务,那么需要改为0.0.0.0:8888。如果主机想要通过127.0.0.1::8888访问容器,需要更改docker run语句如下:

bash 复制代码
docker run -d -p 127.0.0.1:8888:8888 <image_name>

这里说明了端口映射,将容器的0.0.0.0::8888端口映射到主机的127.0.0.1:8888端口。
第二个知识是防火墙有可能会拦截请求,最好把Ubuntu的防火墙关了。

bash 复制代码
#关闭防火墙服务
sudo systemctl stop ufw.service

#检查防护墙服务是否关闭
sudo ufw status

我试了前面两个办法都不行,只能上大招,重启容器的网卡服务。

bash 复制代码
systemctl stop docker # 停止docker 服务
pkill docker # 杀掉docker进程
iptables -t nat -F # 清理iptables
ip link set docker0 down # 停止docker0网卡
brctl delbr docker0 # 删除docker0网卡--重点!
systemctl start docker # 启动docker服务

问题5:上述方法可以实现容器与主机的通信,但是容器之间的通信还需要获取到容器的IP地址才可以实现

但是我在本地编写的代码,都是用的127.0.0.1:xxxx的格式进行通讯,现在突然要换为容器的IP地址进行通讯,又需要更改代码,所以我采用了其他方式,将容器的网络模式更换为host模型,即与主机共享同一个IP空间。

最终的dockerfile

bash 复制代码
# 使用指定的基础镜像
FROM ubuntu:20.04

ENV DEBIAN_FRONTEND=noninteractive

# 换为清华源
# RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list

RUN apt-get update && apt-get install -y gcc git

RUN apt-get update && apt-get install -y wget && \
    wget https://mirrors.aliyun.com/golang/go1.21.3.linux-amd64.tar.gz && \
    tar -C /usr/local -xzf go1.21.3.linux-amd64.tar.gz && \
    rm go1.21.3.linux-amd64.tar.gz

# 设置Go的环境变量
ENV PATH=$PATH:/usr/local/go/bin

# 创建工作目录并拷贝你的 Go 项目代码到容器中
WORKDIR /Cache
COPY . /Cache

# 构建Go项目
RUN go build -o server2

# 暴露应用程序所使用的端口
EXPOSE 9528
EXPOSE 8766

# 定义容器启动命令,这里假设你的Go项目生成了一个名为server1的可执行文件
CMD ["./server2"]

最终的docker-compose.yml

yaml 复制代码
version: '3'

services:
  service1:
    build: ./server1
    network_mode: "host"

  service2:
    build: ./server2
    network_mode: "host"

  service3:
    build: ./server3
    network_mode: "host"
相关推荐
丁卯4049 分钟前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
敲敲敲-敲代码2 小时前
【SQL实验】触发器
数据库·笔记·sql
Moonnnn.2 小时前
51单片机学习——动态数码管显示
笔记·嵌入式硬件·学习·51单片机
!!!5253 小时前
华为云镜像加速器
docker·容器·华为云
电棍2335 小时前
verilog笔记
笔记·fpga开发
让我安静会5 小时前
Obsidian·Copilot 插件配置(让AI根据Obsidian笔记内容进行对话)
人工智能·笔记·copilot
sszdzq6 小时前
Docker
运维·docker·容器
世事如云有卷舒6 小时前
FreeRTOS学习笔记
笔记·学习
dmy6 小时前
docker 快速构建开发环境
后端·docker·容器
bugtraq20217 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu