企业项目容器化实战经验分享-镜像篇

此文章分为 镜像篇、容器篇、生产调试 三部分,适合对容器化技术和生产环境部署有一定经验的同学。 都是干货,希望对阅读本文的你有所帮助。

背景

公司项目需要上线阿里专有云生态,部署在Alibaba Cloud Linux 操作系统平台的 Docker 容器中。 这个项目原生产环境部署十分复杂,存在近10个产品服务组件,部署过程脚本有30余个。 项目容器化时,遇到了非常多的问题,本文会记录解决问题的思路与方案。 本文为镜像篇,大致内容如下:

  1. 如何选择基础镜像?
  2. 镜像构建
  3. 如何优化产出镜像的体积?

如何选择基础镜像?

基础镜像是个人定制镜像构建的最上层镜像,一般包含一个最小化的操作系统和一些基础包。我们需要以基础镜像为根基,搭建项目的整体运行环境。 选择基础镜像时应最少考虑以下几点

  • CPU架构
  • 操作系统
  • 镜像体积

CPU架构

不同CPU架构的处理器,指令集是不完全相同的。因此同一段代码在编译后,在x86平台与arm平台的汇编指令不同。 首先,你应该了解你的项目使用编译型语言(C++,go)还是解释型语言(Java,Python)。

编译型语言

如果是编译型语言,选择的基础镜像架构最好与代码编译平台保持一致,这样可以减少跨平台编译带来的工作量。

例如:你的c++项目在x86平台编译,而你想要使用arm平台的基础镜像,你需要想办法将c++项目编译为arm架构,才能在arm平台正常运行,此过程会存在一些代码层面的变更。

解释型语言

对于解释型语言来说,镜像的CPU架构影响不大,只需要在镜像中安装对应的运行环境即可,不存在代码层面的变更。

例如:你的java项目需要在x86镜像平台,只需要在镜像构建时安装x86的jdk即可,x86的jdk编译代码为x86指令集。

操作系统

选择主流的、正在维护的操作系统即可,例如Ubuntu。 这样可以保证你的项目可以更新最新的系统漏洞,提高安全性。 并且为了轻量化,在满足运行环境的基础上,选择的基础镜像体积越小越好。

镜像构建

镜像需要包含哪些东西?

在镜像构建前,想清楚哪些东西需要塞到镜像里面。

运行环境与依赖

无论你的项目是哪种开发语言,运行环境和依赖是必不可少的。 以Java项目举例,如果此项目依赖于jdbc.jar,镜像中就必须准备好相关RPM/deb/xx 的jdbc依赖。如果项目构建时项目文件和依赖打包在一起,则不需要额外安装依赖,例如SpringBoot项目bootJar时将依赖一起打入Jar包,因此不需要额外安装依赖。

项目包

构建好的项目包也要放入镜像中

数据持久化方案

为了容器的迭代,像配置、数据库数据、日志等重要数据持久化是必然的。如果你的项目运行时,有非常多的目录需要持久化,就需要用docker run -v 或者 docker-compose.yml中定义非常多数据卷,这样不够优雅且难以管理。 例如,某项目需要持久化 "/etc/a"、"/etc/b"、"/etc/c"、/var/log/a、/var/log/b、/var/log/c 这几个文件夹,那么你的docker-compose.yml可能是下面这样的,非常不优雅。并且项目迭代如果产生新的需要持久化的目录,还需要继续在docker-compose.yml文件中堆积,很不方便。

docker-compose.yml 复制代码
version: '3' 
services: 
  web:  
    image: 项目名:TAG  
    volumes:    # 挂载数据卷
      - /etc/a-data:/etc/a  
      - /etc/b-data:/etc/b 
      - /etc/c-data:/etc/c  
      - /var/log/a -data:/var/log/a  
      - /var/log/b-data:/var/log/b  
      - /var/log/c-data:/var/log/c  
volumes:  # 定义数据卷
  /etc/a-data:  
  /etc/b-data:
  /etc/c-data
  /var/log/a-data
  /var/log/b-data
  /var/log/c-data

我们可以用符号链接(Symbolic Link)优化目录结构

shell 复制代码
#!/bin/bash
# 原文件夹迁移
mkdir /var/allConfig
mkdir /var/allLog
mv /etc/a /etc/b /etc/c  /var/allConfig
mv /var/log/a /var/log/b /var/log/c /var/allLog
# 建立原文件夹的软链接
ln -s /var/allConfig/a /etc/
ln -s /var/allConfig/b /etc/
ln -s /var/allConfig/c /etc/
ln -s /var/allLog/a /etc/
ln -s /var/allLog/b /etc/
ln -s /var/allLog/c /etc/

优化后,仅需关注 /var/allLog /var/allConfig 文件夹的持久化

ENTRYPOINT入口

提前准备好容器初始化脚本 此脚本需要做的事情:1.容器初始化 2.主进程与子进程控制 注意事项:1.环境变量的传播 2.systemd通信(如果使用了Systemd) 等... 此部分在容器篇详细说明。

镜像体积优化

Dockerfile镜像构建中,每一命令都会堆叠一层镜像,这方面的知识不再赘述。 需要注意的是

  1. 层级:由于镜像 copy on write 的特点,如果当前层级需要读取或修改其他层的镜像的文件,会完整复制此文件至当前镜像层,这样两个层级都存在此文件,如此造成双倍空间占用。
  2. 文件及缓存:当前层级删除其他层级的文件,并不会减少其他层级的体积,资源清理必须在一次镜像操作内完成。
  3. 分阶段构建:构建时区分构建阶段与运行阶段,只保留需要的文件至运行阶段以减少镜像体积

那么我们的核心思想:1.尽可能减少镜像层级,避免层级之间的文件修改 2.每次操作末尾,清理不必要文件及缓存 3.初始化操作在容器启动时完成(例如Postgresql init db) 4.以分阶段构建的方式,仅保留构建阶段生产的必要文件

例如:

dockerfile 复制代码
# 优化前  
FROM node:14  
WORKDIR /app  
COPY . .  
RUN npm install  
RUN npm run build  
  
# 优化后  
FROM node:14 as builder  
WORKDIR /app  
COPY . .  
RUN npm install && npm run build  
FROM node:14-alpine  
WORKDIR /app  
COPY --from=builder /app/dist /app/dist  
COPY --from=builder /app/node_modules /app/node_modules  
CMD ["npm", "start"]

下班后时间不多,今天就讲到这里,一周后更新 "容器篇"。 文中如有描述不对的地方欢迎大家指正!

相关推荐
南猿北者6 小时前
docker容器
docker·容器
二十雨辰6 小时前
[linux]docker基础
linux·运维·docker
time never ceases7 小时前
使用docker方式进行Oracle数据库的物理迁移(helowin/oracle_11g)
数据库·docker·oracle
MonkeyKing_sunyuhua8 小时前
ubuntu22.04 docker-compose安装postgresql数据库
数据库·docker·postgresql
追风林9 小时前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
€☞扫地僧☜€10 小时前
docker 拉取MySQL8.0镜像以及安装
运维·数据库·docker·容器
茶馆大橘10 小时前
微服务系列六:分布式事务与seata
分布式·docker·微服务·nacos·seata·springcloud
全能全知者11 小时前
docker快速安装与配置mongoDB
mongodb·docker·容器
阿尔帕兹13 小时前
构建 HTTP 服务端与 Docker 镜像:从开发到测试
网络协议·http·docker
ZHOU西口15 小时前
微服务实战系列之玩转Docker(十八)
分布式·docker·云原生·架构·数据安全·etcd·rbac