[docker] utility container

utility container 我感觉就是工具,可以减少一些系统安装软件的容器

使用场景

主要还是系统减负,比如说正常情况下运行一个 node 项目,那么系统上一定需要安装一个 npm,哪怕是创建一个新的项目,使用 npm init,也是需要保证本机上安装一个 npm,但是如果将其放到 docker 容器中,那么就不需要安装 npm 了

如果只是 node,那么问题倒也不是很大,但是参考一下 laravel 项目,官方文说,必须 安装 PHP 和 Composer,推荐 安装 Node 和 NPM:

这种情况下,安装工具就相对而言比较困难

另一个情况是不同的库,但是会产生冲突。比如说 MariaDB 和 MySQL,默认导出的 port 都是 3306,但是二者不兼容,包括使用的 SQL Driver 都不一样。我就因为想要同时安装 MariaDB 和 MySQL 导致一些文件冲突,然后折腾了一个下午才彻底删除干净,重新安装了 MySQL

再后来干脆就使用 MySQL 的 docker 了

使用 util container 的方法

简单介绍一下用 util container 的方式

直接 -it

这种大多数情况下是需要用 REPL,如:

docker run -it --rm node
11Welcome to Node.js v21.7.3.
Type ".help" for more information.
> 1 + 1
2
> console.log('hello docker')
hello docker
undefined
>

另一种使用方式如下:

docker run -it node npm init -y
Wrote to /package.json:

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "directories": {
    "lib": "lib"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}


这样就可以创建一个空白的 node 项目了

另外也可以在 run 的同时 attach 一个 bash,这样可以直接通过命令行执行

exec

但是有的情况下,已经使用 docker run,又想额外加一些功能,这时候就可以使用 exec,如:

# 假设之前的指令是启动一个 node 容器docker exec -it sweet_nightingale npm init -y
Wrote to /package.json:

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "directories": {
    "lib": "lib"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

exec 是在 docker 容器内运行指令的 arg,如果想要 attach bash 的话,那么容器就一定要在运行的状态才行

bind mounts util container

之前在 [docker] 数据的持久化 - Volume & bind mounts 提到可以把 docker 容器中的路径和本机的路径进行挂载,这样双向都能够被更新

这个思路也可以应用到 util container 上,这样就可以在本机修改代码,然后在 container 中即时更新。或者容器中修改了一些内容,本机上也获取同步更新

下面就实现一个 node 的 util container 的 bind mounts:

# 使用 slim 或者 alpine 会比完整的版本小很多docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
node-util    latest    d006cdcdfc7d   7 seconds ago       132MB
node         latest    b6d2a1484e51   About an hour ago   1.1GB
# 然后进行 bind mountdocker run -it -v "$(pwd):/app" node-util npm init -y
Wrote to /app/package.json:

{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

[docker] utility container-LMLPHP

可以看到本机上也出现了对应的 package.json 文件

使用 Dockerfile

这里多提一下 ENTRYPOINT,这是一个添加了之后 docker 永远会多执行的一个指令:

FROM node:20-slim

WORKDIR /app

ENTRYPOINT [ "npm" ]

这样就可以不用反复输入 npm,因为每次执行的时候,npm 都会执行:

docker run -it -v "$(pwd):/app" node-util init -y
Wrote to /app/package.json:

{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

# 下载 node_module,注意这里是启动了一个新的 containerdocker run -it -v "$(pwd):/app" node-util install

up to date, audited 1 package in 300ms

found 0 vulnerabilities

# 安装 express,注意这里启动了第三个 containerdocker run -it -v "$(pwd):/app" node-util install express

added 64 packages, and audited 65 packages in 4s

12 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm notice
npm notice New patch version of npm available! 10.5.0 -> 10.5.2
npm notice Changelog: https://github.com/npm/cli/releases/tag/v10.5.2
npm notice Run npm install -g npm@10.5.2 to update!
npm notice

完成之后,node_modules 也会被同步到本机上:

[docker] utility container-LMLPHP

使用 docker compose

想要继续简化步骤,则是可以使用 docker compose:

version: "3.8"
services:
  npm:
    build: ./
    stdin_open: true
    tty: true
    volumes:
      - ./:/app

这样可以少打一些字:

# can use --rm to remove the container after executing the commanddocker compose run npm init -y
WARN[0000] /Users/luhan/study/docker/section07/docker-compose.yaml: `version` is obsolete
[+] Creating 1/0
 ✔ Network section07_default  Created                                                                                                                 0.0s
[+] Building 0.6s (6/6) FINISHED                                                                                                      docker:desktop-linux
 => [npm internal] load build definition from Dockerfile                                                                                              0.0s
 => => transferring dockerfile: 90B                                                                                                                   0.0s
 => [npm internal] load metadata for docker.io/library/node:20-slim                                                                                   0.5s
 => [npm internal] load .dockerignore                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                       0.0s
 => [npm 1/2] FROM docker.io/library/node:20-slim@sha256:7c5c3f250db850d159c5f5554a3a608a72eb721dd15451d294e3fe74f944a4fc                             0.0s
 => CACHED [npm 2/2] WORKDIR /app                                                                                                                     0.0s
 => [npm] exporting to image                                                                                                                          0.0s
 => => exporting layers                                                                                                                               0.0s
 => => writing image sha256:2d02c95f22951c1aea74c6c4d1cf6703115cbacbfeaccec867d55993b17894d3                                                          0.0s
 => => naming to docker.io/library/section07-npm                                                                                                      0.0s
Wrote to /app/package.json:

{
  "name": "app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.19.2"
  },
  "devDependencies": {},
  "description": ""
}

保持 node 项目运行

这个是只针对 node 的优化:

docker run -d node:20-alpine tail -f /dev/null
Unable to find image 'node:20-alpine' locally
20-alpine: Pulling from library/node
4abcf2066143: Already exists
3bce96456554: Already exists
2bde47b9f7c3: Already exists
db3e2f2b6054: Already exists
Digest: sha256:7a91aa397f2e2dfbfcdad2e2d72599f374e0b0172be1d86eeb73f1d33f36a4b2
Status: Downloaded newer image for node:20-alpine
1ab1731f8bdc929e6274ebc4f3e7b95596a5d0d33be1b41c4eb2726ab3c8f1ae
❯ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS     NAMES
1ab1731f8bdc   node:20-alpine   "docker-entrypoint.s…"   3 seconds ago   Up 2 seconds             sleepy_wright

# 使用 exec 执行 shdocker exec -it sleepy_wright sh
/ # echo "bash is not present"
bash is not present

这样就可以进入到容器内执行命令,而不是反复重新起一个新的容器

⚠️:alpine 模式下 bash 不存在,所以可以使用 sh

04-25 06:09