镜像制作最佳实践

前端

以下是一个 Dockerfile,它允许您通过 -e 选项动态更改 prodUrl 的 IP 和端口。我们通过启动容器时动态注入环境变量,并在容器启动时修改配置文件。

Dockerfile

# 使用 Nginx 作为基础镜像
FROM nginx:alpine

# 拷贝默认的 Nginx 配置文件和 HTML 资源
COPY . /usr/share/nginx/html

# 安装必要工具
RUN apk add --no-cache bash

# 启动脚本,用于动态更新 config.js
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# 暴露 Nginx 默认端口
EXPOSE 80

# 使用自定义的 entrypoint 脚本
ENTRYPOINT ["/entrypoint.sh"]

# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

entrypoint.sh

#!/bin/bash

# 检查环境变量是否已设置
if [ -z "$PROD_URL" ]; then
  echo "Environment variable PROD_URL is not set. Using default."
else
  echo "Updating prodUrl to $PROD_URL"
  # 替换 config.js 中的 prodUrl
  sed -i "s|prodUrl: .*|prodUrl: \"$PROD_URL\"|g" /usr/share/nginx/html/config.js
fi

# 启动 Nginx
exec "$@"

使用方法

  1. 构建 Docker 镜像:
docker build -t nginx-config-updater .
  1. 运行容器并通过 -e 选项设置 PROD_URL
docker run -d -p 8080:80 -e PROD_URL="http://192.168.1.1:8080" nginx-config-updater
  1. 验证容器中 config.js 文件已被正确修改。

关键点

  • 动态环境变量注入:通过 -e 选项传入的 PROD_URL 值在容器启动时动态写入 config.js
  • 默认值回退:如果未设置 PROD_URL,则保留默认值。
  • 轻量化基础镜像:选择 nginx:alpine,减少镜像大小。

MySQL

是的,你可以基于 mysql:8.0 镜像创建一个自定义的 Docker 镜像,并将 SQL 文件直接导入到该镜像中,这样在容器启动时,数据库和表就会自动存在,而无需每次启动容器时手动导入。这种方法适用于你希望将数据库初始化操作嵌入到镜像中,确保每次使用该镜像时数据库都是预配置好的。

以下是如何实现这个方案的步骤:

步骤 1:准备 SQL 文件

首先,准备你的 SQL 文件(例如 init.sql),这个文件应该包含你想要预先导入数据库中的表、数据和其他结构。

假设你有一个 init.sql 文件,内容如下:

CREATE DATABASE IF NOT EXISTS my_database;

USE my_database;

CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);

INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');
INSERT INTO users (username, email) VALUES ('bob', 'bob@example.com');

步骤 2:创建自定义 Dockerfile

然后,创建一个自定义的 Dockerfile,基于 mysql:8.0 镜像,在构建镜像时将 SQL 文件复制到容器中的初始化目录 /docker-entrypoint-initdb.d/

# 使用 mysql:8.0 作为基础镜像
FROM mysql:8.0

# 设置环境变量,定义 MySQL 的 root 密码
ENV MYSQL_ROOT_PASSWORD=my-secret-pw

# 将 SQL 文件复制到容器的 /docker-entrypoint-initdb.d/ 目录
# 在容器启动时,MySQL 会自动执行该目录下的 SQL 文件
COPY init.sql /docker-entrypoint-initdb.d/

# 暴露 MySQL 默认端口
EXPOSE 3306

步骤 3:构建自定义镜像

保存 Dockerfile 和 init.sql 文件后,使用以下命令来构建自定义的 MySQL 镜像:

docker build -t my-mysql:8.0 .

这将创建一个新的 Docker 镜像 my-mysql:8.0,在该镜像中,MySQL 会自动导入你指定的 SQL 文件。

步骤 4:运行容器

现在,你可以使用这个自定义镜像启动容器。每次启动容器时,MySQL 都会自动初始化数据库并执行 init.sql 文件中的 SQL 语句。

docker run --name my-mysql-container -d my-mysql:8.0

此命令将启动一个新的容器 my-mysql-container,并自动执行 SQL 脚本来初始化数据库。

步骤 5:验证数据库初始化

容器启动后,你可以连接到容器中的 MySQL 数据库,验证是否成功导入了数据库和表。

docker exec -it my-mysql-container mysql -u root -p

在 MySQL shell 中,你可以使用以下命令验证:

SHOW DATABASES;
USE my_database;
SHOW TABLES;
SELECT * FROM users;

你应该会看到 my_database 数据库已经创建,并且 users 表以及数据都已经插入。

步骤 6:修改 Docker Compose(可选)

如果你使用 Docker Compose 来管理多个服务,可以将 docker-compose.yml 配置修改为使用自定义镜像:

version: '3.8'

services:
  db:
    image: my-mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
    ports:
      - "3306:3306"
    volumes:
      - ./init-scripts:/docker-entrypoint-initdb.d

在这种情况下,你可以把 SQL 文件放在 init-scripts 目录下,并使用 docker-compose 启动服务。

docker-compose up

优点

  • 自动化:每次启动容器时,数据库和表会自动被创建,无需手动导入 SQL 文件。
  • 简化流程:通过将数据库初始化集成到镜像中,避免了启动时的额外操作。
  • 适用于多个环境:自定义镜像可以在多个环境中使用,确保每个环境都有相同的初始化数据。

缺点

  • 镜像体积增大:将数据库和数据直接嵌入到镜像中会增加镜像的体积。
  • 不灵活:如果需要更改数据库内容(如修改表结构或数据),必须重新构建镜像。
  • 只适合初次初始化:这种方式适合初次初始化数据库。如果数据库需要频繁更改或者迁移,这种方法不如直接使用外部脚本和数据库管理工具灵活。

总结

通过将 SQL 文件直接嵌入到自定义 MySQL 镜像中,并使用 /docker-entrypoint-initdb.d/ 目录自动执行这些脚本,你可以确保每次运行容器时,数据库都会被初始化。这个方法适用于希望确保数据库和应用的初始状态一致的场景。