服务端开发时,每次调试都先编译程序,和其他相关文件一并都覆盖复制到一个本地临时目录,启动 docker compose,容器内建立目录映射到本机临时目录,容器内再分别把文件复制到容器中相应的位置,容器中启动程序。以上步骤都通过本地脚本和 compose yaml 的 command 实现。
想求教一下彦祖们,这种操作姿势是否正确?有没有更好的调试方式?
为什么我总会遇到明明代码没问题,但在容器中会得到莫名其妙的错误,然后让 docker 重启一下就能恢复正常。当不能确定是代码问题还是环境问题时,每次排查都很浪费时间。

是不是包含这个程序依赖了其他容器内的服务?比如 mysql 、redis 之类的,在同时启动的时候,不同容器内的程序启动完成的时候不一致,就会导致失败,如果是这种情况一般在程序里加上重试逻辑可解决

看描述,还看不出你说的问题是哪类问题。程序崩溃?资源、配置读取到的数据不对?还是外面能建立的连接到容器内就不行了?然后你用容器启动调试的过程感觉过于复杂了。比如“容器内再分别把文件复制到容器中相应的位置”,这个步骤一般是在做镜像时才会做的,build 镜像时,如果有类似操作,还会做一定程度上的验证确保复制正确。实际启动容器后不会做这步。必要的配置或资源,在启动容器时能准确的挂在进容器的任何位置。程序一般也是和镜像强绑定的。新编译的程序,都会有新的版本的镜像。通过容器使用的镜像版本就能准确确定程序版本。然后如果你觉得容器有什么问题,可以 docker exec 进容器里面去找线索验证。看看里面的文件版本、程序版本、配置的细节是否和你期望的一致。如果什么都一致,就可能是你的代码有 BUG 。

docker compose 将一个 service 编排了 n 个容器,然后同时启动。每个容器的程序不依赖其他容器的服务。我想确认一下我用的这种方式是否有问题。

如果你没有热更新之类的机制的话,要重启很正常吧。

我的建议是你贴出你的 docker-compose 配置文件,以及你使用 dockercompose 的知识,并且把详细的报错信息贴出来。这样大家才能给你对应的意见

问题的关键是,同样的程序代码,出现错误后把 docker 重启一下就好了。我不知道具体原因,但可以确定跟代码本身没有关系。我对 docker 不是很了解,有没有可能是 docker 缓存的问题?因为整个启动过程中有多处需要复制文件。

应该给点错误提示啥的。

你需要搭建 CI/CD 了

compose 里的服务启动顺序是不固定的。也许出错的原因是依赖的服务还没启动好。没有错误的细节没法深入推测了。

指令用错?up - downstart - stop

yamlversion: "3.9"services: n: build: dockerfile: ./Dockerfile.alpine ports: - "12300-12310:12380" - "45600-45610:12381" volumes: - ./temp/:/temp/ scale: 1 networks: - default command: - sh - -c - | mkdir /etc/xxxxx cp /temp/main /etc/xxxxx/main cp /temp/一些.pem /etc/ssl/certs/ cp -rf /temp/一些目录 /etc/xxxxx/一些目录 cp /temp/获取一些值写入 env.sh /etc/xxxxx/获取一些值写入 env.sh source /etc/xxxxx/获取一些值写入 env.sh cp /temp/nginx.conf /etc/nginx/http.d/default.conf nginx cd /etc/xxxxx ./mainnetworks: default: driver: bridge启动时scale =10*用临时文件夹是因为启动时需要准备的文件有点多,路径都不同,为了避免麻烦就临时启动时放在同一个目录。 都是程序内的错误,docker 本身没有报错。会不会是文件复制时候偶尔会出错?

用的 up down

为啥不把复制操作直接写在 dockerfile 里呢?

应用程序内的错误也很多种,是 io 错误还是指针错误还是运算逻辑错误。重启大法任何时候都管用,不排除是你的应用程序的问题。另外错误发生的时机?手动复制文件到 temp 下,然后重启所有服务,百分百出错?

把要运行的文件 volume 进去?那容器起到啥作用??你直接在外面运行不就得了?

十年来第一次见这么长的 command ,真的啰嗦,每一行都在藏隐患

即使写了这么啰嗦,甚至最重要的代码编译过程并没有被容器所管理

初步建议:1. 把编译过程写进 dockerfile2. 把文件夹创建、文件复制过程写进 dockerfile3. 编译过程的镜像和运行阶段的镜像分离,使用多阶段构建4. 尽量少用 volume5. nginx 单独容器,除非是做 nginx 功能相关开发6. dockerfile 或者 composefile 都有 env 相关的功能7. compose.yml 里给每个服务取有意义的名字

为啥要容器内 cp? 多写几行 volumes ,不是更直观?

感谢感谢初步建议:1. 把编译过程写进 dockerfile - 编译过程是指程序的编译吗?我是写入启动脚本.sh 文件中,编译后再 up 。2. 把文件夹创建、文件复制过程写进 dockerfile - 文件复制 我是想区别不大,索性都放在 compose 中方便调试。3. 编译过程的镜像和运行阶段的镜像分离,使用多阶段构建4. 尽量少用 volume- 少用 volume 的原因是什么?防止文件 io 冲突吗?之前遇到过,所以就复制进容器内了。5. nginx 单独容器,除非是做 nginx 功能相关开发- 项目中有 web 部分,所以 dockerfile 中 add 了安装 nginx ,容器启动时启动 ngixn 。6. dockerfile 或者 composefile 都有 env 相关的功能 - 对 env 有一些逻辑判断,所以写入 sh 。7. compose.yml 里给每个服务取有意义的名字 - 好嘞

compose 主要是编排,让多容器可以容易组合,你不应该让他参与过多的事物,如果容器启动前需要处理事务,我觉得还是用启动脚本比较好。另外容器应该是最小化的应用,你这个是把 nginx 也打包到 dockerfile 中去吗?为何不用单独的 nginx 容器呢?

  1. 把编译过程写进 dockerfile- 编译过程是指程序的编译吗?我是写入启动脚本.sh 文件中,编译后再 up 。---- 指的就是程序的编译( go build 或者 java 之类的 build )。编译软件的版本对生成结果有影响,本机编译和环境一致的目标相悖,这是第一个隐患。2. 把文件夹创建、文件复制过程写进 dockerfile- 文件复制 我是想区别不大,索性都放在 compose 中方便调试。---- 不认同这条,可以放弃使用容器了。3. 编译过程的镜像和运行阶段的镜像分离,使用多阶段构建---- 补充:多阶段构建和上面第一条有关。4. 尽量少用 volume- 少用 volume 的原因是什么?防止文件 io 冲突吗?之前遇到过,所以就复制进容器内了。---- 跟 io 没关系。具体去看文档5. nginx 单独容器,除非是做 nginx 功能相关开发- 项目中有 web 部分,所以 dockerfile 中 add 了安装 nginx ,容器启动时启动 ngixn 。---- 没有因果关系,“所以”没意义。巨大的单个容器可以只用 docker 没必要 compose 。既然用了 compose ,就要考虑多容器的优势,拆分 nginx 、Redis 、MySQL 等基础组件容器,拆开后也很容易支持动态和静态。6. dockerfile 或者 composefile 都有 env 相关的功能- 对 env 有一些逻辑判断,所以写入 sh 。---- compose 的 env 功能支持简单逻辑。env 只存环境相关变量。少量逻辑放在启动脚本完全没问题。7. compose.yml 里给每个服务取有意义的名字- 好嘞---- 好嘞

dockerfile + docker-compose.yml + gitlab ci/cd ,最后 docker ps 查看状态,docker log [container id] 查看日志。

不好意思的说,主要是不懂怎样单独用 nginx 容器:)只会用已知的方式实现。

用 compose 是为了模拟网络环境,生产环境是应用程序和 nginx 直接运行在节点上没有用到 docker ,所以选择容器中装 nginx 而没有单独用 nginx 容器,不知道这样做对不对请指教。

compose 编排多容器,nginx 作为一个容器,依赖 web 应用就可以。

所以有没有可能,在你的这种场景下并不适合用 docker-compose 来调试程序,程序调试的时候,直接在本机跑和调试,应该是最方便的。看了你的 docker-compose.yml ,也是感觉不用 docker-compose 应该是一个更好的选择。

这种很基础的应用场景,compose 文档讲的很清楚,花两三个小时通读一遍,很多问题自然就能解决了