网站建设-丹东,来个网站你知道的2022年,昆明的房产网站建设,芜湖做网站文章目录 环境示例持久化数据bind mount多容器应用Docker Compose 参考 环境
RHEL 9.3Docker Community 24.0.7
示例
持久化数据
默认情况下#xff0c;各个容器之间的文件系统是相互独立的。即使两个容器来自同一个image#xff0c;对其中一个容器的修改#xff0c;对另… 文章目录 环境示例持久化数据bind mount多容器应用Docker Compose 参考 环境
RHEL 9.3Docker Community 24.0.7
示例
持久化数据
默认情况下各个容器之间的文件系统是相互独立的。即使两个容器来自同一个image对其中一个容器的修改对另一个容器也是不可见的。
我们来实际操作一下。
首先启动一个容器创建 /data.txt 文件其内容是一个随机数
docker run -d ubuntu bash -c shuf -i 1-10000 -n 1 -o /data.txt tail -f /dev/null该命令启动了一个 ubuntu image的容器并运行了两条命令
shuf -i 1-10000 -n 1 -o /data.txt 产生一个随机数并写到 /data.txt 文件里tail -f /dev/null 仅仅是为了运行一条“可持续运行”的命令否则 docker run 命令就会立即结束
注 docker run 只能运行一条命令本例为了运行两条命令用 来连接这是bash的语法。
查看容器
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1216910a048e ubuntu bash -c shuf -i 1-… 13 seconds ago Up 12 seconds gracious_pike
......我们来看一下该容器里的 /data.txt 文件
➜ ~ docker exec 1216910a048e cat /data.txt
9458可见文件已经创建好了。
接下来我们再启动一个 ubuntu image的容器并查看 /data.txt 文件
➜ ~ docker run -it ubuntu ls /data.txt
ls: cannot access /data.txt: No such file or directory可见在第二个容器里没有 /data.txt 文件。
最后把两个容器都删除。
接下来我们来实现数据的持久化。
回到我们之前的 getting-started 应用。该应用会把数据存储在SQLite数据库里其位置为 /etc/todos/todo.db 。
通过创建一个volume并将其mount到容器里的 /etc/todos 目录我们就可以实现数据持久化。
首先通过 docker volume create 命令创建一个volume
docker volume create todo-db删除之前运行的容器然后运行一个新的容器
docker run -dp 0.0.0.0:3000:3000 --mount typevolume,srctodo-db,target/etc/todos getting-started打开浏览器访问 http://localhost:3000 添加一些item 删除容器
docker rm -f container ID重新启动一个容器
docker run -dp 0.0.0.0:3000:3000 --mount typevolume,srctodo-db,target/etc/todos getting-started打开浏览器访问 http://localhost:3000 可以看到之前添加的item仍然还在。
那么问题来了持久化的数据保存到哪里了呢
看一下volume
➜ ~ docker volume ls
DRIVER VOLUME NAME
local todo-db
......➜ ~ docker volume inspect todo-db
[{CreatedAt: 2024-01-01T10:00:3808:00,Driver: local,Labels: null,Mountpoint: /var/lib/docker/volumes/todo-db/_data,Name: todo-db,Options: null,Scope: local}
]可见mount point是 /var/lib/docker/volumes/todo-db/_data 这就是宿主机上的路径。
在宿主机上查看该目录
➜ ~ sudo ls -l /var/lib/docker/volumes/todo-db/_data
total 8
-rw-r--r--. 1 root root 8192 Jan 1 11:23 todo.db同样如果我们在宿主机上创建一个文件比如 /var/lib/docker/volumes/todo-db/_data/test.txt 然后在容器里也可以访问
➜ ~ docker exec 885e3594b2e0 cat /etc/todos/test.txt
hahabind mount
上一节使用了volume mount这一节介绍bind mount。
使用bind mount把宿主机上的指定目录mount到容器里。当宿主机上的文件有变化时容器里的文件同样也立即变化。
volume mount和bind mount的对比
Volume mountBind mount宿主机路径Docker决定个人决定示例typevolume,srcmy-volume,target/usr/local/datatypebind,src/path/to/data,target/usr/local/data向volume注入容器内容YesNo是否支持volume驱动YesNo
在宿主机的 getting-started-app 根目录下运行
docker run -it --mount typebind,src$(pwd),target/src ubuntu bash--mount 的参数如下
typebind 指定mount类型src$(pwd) 指定宿主机路径target/src 指定容器路径
现在我们已经进入了容器查看 /src 目录
root9c90854addd6:/# ls -l /src
total 156
-rw-r--r--. 1 1000 1000 209 Dec 31 11:17 Dockerfile
-rw-r--r--. 1 1000 1000 269 Dec 31 09:19 README.md
-rw-r--r--. 1 1000 1000 648 Dec 31 09:19 package.json
drwxr-xr-x. 4 1000 1000 39 Dec 31 09:19 spec
drwxr-xr-x. 5 1000 1000 69 Dec 31 09:19 src
-rw-r--r--. 1 1000 1000 147266 Dec 31 09:19 yarn.lock在容器里创建文件
echo hello /src/test.txt在宿主机上也能访问
➜ getting-started-app git:(main) ✗ cat test.txt
hello反之在宿主机上的文件修改也会立即反映在容器里。
通过bind mount我们可以把源代码mount到容器里在容器里build和运行应用。
在宿主机的 getting-started-app 根目录下运行
docker run -dp 127.0.0.1:3000:3000 \-w /app --mount typebind,src$(pwd),target/app \node:18-alpine \sh -c yarn install yarn run dev可通过 docker logs container ID 查看容器log。或者 docker logs -f container ID 持续更新log。
注意在国内会非常慢还经常失败要多试几次。
➜ getting-started-app git:(main) ✗ docker logs -f 8fb9ce6b6953b4b9013db782eb483591a0df137f2f34c67174cb1cd04ac92250
yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 163.98s.
yarn run v1.22.19
$ nodemon -L src/index.js
[nodemon] 2.0.20
[nodemon] to restart at any time, enter rs
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting node src/index.js
Using sqlite database at /etc/todos/todo.db
Listening on port 3000按“Ctrl C” 退出。
打开浏览器访问 http://localhost:3000 确保应用工作正常。
接下来在宿主机上修改源代码比如修改 src/static/js/app.js 文件把按钮上的文字由 Add Item 改为 Add 保存然后刷新浏览器 可见在宿主机上修改源代码后通过浏览器访问应用会立即反映出来。
注分两步走
在宿主机修改源代码 - 修改同步到容器nodemon 发现源代码有变化后立即重启应用这一步是自动的 nodemon 是在 package.json 里启动的
调试完毕后最后构建image
docker build -t getting-started .多容器应用
现在我们来将应用改为使用MySQL数据库。显然基于“高内聚低耦合”的原则需要在另外一个容器里单独运行MySQL。也就是说应用由两个容器组成。那么问题来了容器之间如何通信容器的运行都是相互隔离的显然它们需要通过网络来互相通信。
有两种方式将容器与网络关联
在启动容器时分配网络把已运行的容器连接到网络
首先创建网络
docker network create todo-app查看网络
➜ ~ docker network ls
NETWORK ID NAME DRIVER SCOPE
be48b324c533 todo-app bridge local
......启动MySQL容器
docker run -d \--network todo-app --network-alias mysql \-v todo-mysql-data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORDsecret \-e MYSQL_DATABASEtodos \mysql:8.0其中
--network todo-app --network-alias mysql 设置要加入的网络以及该容器在网络里的别名-v todo-mysql-data:/var/lib/mysql 指定volume注之前没有创建过 todo-mysql-data volume此时会自动被创建-e MYSQL_ROOT_PASSWORDsecret 设置环境变量-e MYSQL_DATABASEtodos 设置环境变量
注 -v 选项比较复杂既可以volume mount也可以bind mount。本例中是mount一个volume。
下面是一些 -v 的源即 : 左边例子
$(pwd) 正确绝对路径. 正确宿主机相对路径./src 正确宿主机相对路径./src/static 正确宿主机相对路径src 正确表示一个volume名字src/static 错误Docker会把它当作volume的名字但是包含了非法字符 /
接下来我们先来测试一下MySQL数据库
docker exec -it container ID mysql -u root -p提示输入密码输入 secret 。
登录成功后查看数据库
mysql show databases;
--------------------
| Database |
--------------------
| information_schema |
| mysql |
| performance_schema |
| sys |
| todos |
--------------------
5 rows in set (0.00 sec)可见已经创建好 todos 数据库了。
最后输入 exit 退出。
至此MySQL数据库已经准备好了。
在把应用连接到MySQL之前我们先来看一下 nicolaka/netshoot 它包含了很多有用的网络调试工具。
启动 nicolaka/netshoot 容器并连接到 todo-app 网络
➜ ~ docker run -it --network todo-app nicolaka/netshoot
Unable to find image nicolaka/netshoot:latest locally
latest: Pulling from nicolaka/netshoot
8a49fdb3b6a5: Pull complete
f08cc7654b42: Pull complete
bacdb080ad6d: Pull complete
df75a2676b1d: Pull complete
d30ac41fb6a9: Pull complete
3f3eebe79603: Pull complete
086410b5650d: Pull complete
4f4fb700ef54: Pull complete
5a7fe97d184f: Pull complete
a6d1b2d7a50e: Pull complete
599ae1c27c63: Pull complete
dd5e50b27eb9: Pull complete
2681a5bf3176: Pull complete
2517e0a2f862: Pull complete
7b5061a1528d: Pull complete
Digest: sha256:a7c92e1a2fb9287576a16e107166fee7f9925e15d2c1a683dbb1f4370ba9bfe8
Status: Downloaded newer image for nicolaka/netshoot:latestdP dP dP 88 88 88
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P
88 88 88ooood8 88 Y8ooooo. 88 88 88 88 88 88 88
88 88 88. ... 88 88 88 88 88. .88 88. .88 88
dP dP 88888P dP 88888P dP dP 88888P 88888P dP Welcome to Netshoot! (github.com/nicolaka/netshoot)
Version: 0.11输入 dig mysql 查看 mysql 的网络信息
1ae405a48c1c ~ dig mysql; DiG 9.18.13 mysql
;; global options: cmd
;; Got answer:
;; -HEADER- opcode: QUERY, status: NOERROR, id: 61648
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0;; QUESTION SECTION:
;mysql. IN A;; ANSWER SECTION:
mysql. 600 IN A 172.18.0.2;; Query time: 8 msec
;; SERVER: 127.0.0.11#53(127.0.0.11) (UDP)
;; WHEN: Mon Jan 01 10:17:08 UTC 2024
;; MSG SIZE rcvd: 44可见 mysql 的IP地址是 172.18.0.2 。尽管 mysql 不是一个有效的hostnameDocker可以将其解析为IP地址因为我们之前给MySQL容器设置过 --network-alias 。也就是说我们只需把应用连接到 mysql 就能连接MySQL数据库了。
在应用端需要设置一些MySQL连接的环境变量
MYSQL_HOSTMYSQL_USERMYSQL_PASSWORDMYSQL_DB
注在源代码里判断这些环境变量并做相应处理Docker只是传入环境变量。
注意在开发环境里通过设置环境变量来设置MySQL连接信息的做法是OK的但在生产环境里不推荐这么做更安全的做法是把这些私密信息放在文件里然后mount到容器。很多应用也支持带 _FILE 后缀的环境变量指定包含这些变量的文件。
接下来启动应用在 getting-started-app 根目录下运行
docker run -dp 127.0.0.1:3000:3000 \-w /app -v $(pwd):/app \--network todo-app \-e MYSQL_HOSTmysql \-e MYSQL_USERroot \-e MYSQL_PASSWORDsecret \-e MYSQL_DBtodos \node:18-alpine \sh -c yarn install yarn run dev其中
-v $(pwd):/app bind mount宿主机当前目录--network todo-app 指定了要加入的网络本例没有指定别名因为不需要别人连接它-e MYSQL_HOSTmysql 和下面几个环境变量一起指定了数据库的连接信息
注 -v 选项比较复杂既可以volume mount也可以bind mount。本例中是bind mount一个宿主机目录最好用绝对路径如果用相对路径貌似必须从 . 开始。
然后通过 docker ps 查看容器ID并查看其log
docker logs -f container ID如下
➜ getting-started-app git:(main) ✗ docker logs -f edf21888a43a
yarn install v1.22.19
[1/4] Resolving packages...
success Already up-to-date.
Done in 0.35s.
yarn run v1.22.19
$ nodemon -L src/index.js
[nodemon] 2.0.20
[nodemon] to restart at any time, enter rs
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting node src/index.js
Waiting for mysql:3306.
Connected!
Connected to mysql db at host mysql
Listening on port 3000可见已经连接上mysql数据库。
打开浏览器访问 http://localhost:3000 添加一些item。
接下来在MySQL容器里查看数据
mysql use todos;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
mysql select * from todo_items;
--------------------------------------------------------
| id | name | completed |
--------------------------------------------------------
| 75900696-2daa-4e0e-91ef-07b8c4f532f0 | item1 | 0 |
| c4c9c814-22c4-4604-b145-c2380184e1b1 | item2 | 0 |
--------------------------------------------------------
2 rows in set (0.00 sec)可见刚刚添加的 item 和 item2 已经保存在数据库里了。
Docker Compose
Compose用来定义多容器应用。只需在一个YAML文件里定义各个service然后只用一条命令就能启动所有东西容器网络volume等。
在 getting-started-app 根目录下创建 compose.yaml 文件如下
services:app:image: node:18-alpinecommand: sh -c yarn install yarn run devports:- 127.0.0.1:3000:3000working_dir: /appvolumes:- ./:/appenvironment:MYSQL_HOST: mysqlMYSQL_USER: rootMYSQL_PASSWORD: secretMYSQL_DB: todosmysql:image: mysql:8.0volumes:- todo-mysql-data:/var/lib/mysqlenvironment:MYSQL_ROOT_PASSWORD: secretMYSQL_DATABASE: todosvolumes:todo-mysql-data:在该YAML文件中定义了两个service表示将会启动两个容器。
这两个容器会自动加到同一个网络里。service名字就是其网络别名
appmysql
我们可以把这两个service和前面对应的 docker run 命令对比一下内容基本是一致的只是语法不同而已。
注意前面提到 docker run 命令里若指定的volume不存在则Docker会自动创建该volume。但对于Compose必须显式创建volume。本例中显式创建了volume todo-mysql-data 。
删除之前运行的容器然后运行
➜ getting-started-app git:(main) ✗ docker compose up -d
[] Running 4/4✔ Network getting-started-app_default Created 0.2s ✔ Volume getting-started-app_todo-mysql-data Created 0.0s ✔ Container getting-started-app-mysql-1 Started 0.0s ✔ Container getting-started-app-app-1 Started 可见创建了一个网络创建了一个volume并且启动了两个容器。
查看compose的log
➜ getting-started-app git:(main) ✗ docker compose logs -f
......
app-1 | Connected to mysql db at host mysql
app-1 | Listening on port 3000
......
mysql-1 | 2024-01-01T14:11:24.084722Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: 8.0.35 socket: /var/run/mysqld/mysqld.sock port: 3306 MySQL Community Server - GPL.可见应用已经启动成功。按下“Ctrl C”退出log。
打开浏览器访问 http://localhost:3000 测试应用工作正常。
注之前填过的item都不见了这是因为和之前使用的不是同一个volume。之前用的是 todo-mysql-data 现在用的是 getting-started-app_todo-mysql-data 。
查看网络
➜ getting-started-app git:(main) ✗ docker network ls
NETWORK ID NAME DRIVER SCOPE
1903ef27abaa getting-started-app_default bridge local
......➜ getting-started-app git:(main) ✗ docker network inspect 1903ef27abaa
......Containers: {2faf22f53ea7ee2bafbf5b817b27b8220f86af9a06ab7dad500bc7721bfac76a: {Name: getting-started-app-app-1,EndpointID: 46bf8de5bb06982ae6ad121631d35b3fc9e20ed423978fdf0ecd7e29ede3ccaa,MacAddress: 02:42:ac:16:00:02,IPv4Address: 172.22.0.2/16,IPv6Address: },4d549752261d29c672484b9e6cb68d6746a32d8fe77fdd6459c5dde2e2da28e5: {Name: getting-started-app-mysql-1,EndpointID: d887f42ab3ed22f759e3f4bff45d83c6d2a162065a4237dd064f6c79585b7aa0,MacAddress: 02:42:ac:16:00:03,IPv4Address: 172.22.0.3/16,IPv6Address: }
......最后删除所有东西
➜ getting-started-app git:(main) ✗ docker compose down
[] Running 3/3✔ Container getting-started-app-app-1 Removed 0.2s ✔ Container getting-started-app-mysql-1 Removed 3.2s ✔ Network getting-started-app_default Removed注意网络 getting-started-app_default 会和容器一起删除但是volume getting-started-app_todo-mysql-data 不会删除。若想要删除volume则需加上 --volumes 选项
➜ getting-started-app git:(main) ✗ docker compose down --volumes
[] Running 4/4✔ Container getting-started-app-mysql-1 Removed 3.0s ✔ Container getting-started-app-app-1 Removed 0.2s ✔ Volume getting-started-app_todo-mysql-data Removed 0.0s ✔ Network getting-started-app_default Removed但是要慎用一旦删除volume持久化的数据就没了。下次再启动容器之前的填过的item就不见了。
参考
https://docs.docker.com/get-started/https://www.linuxidc.com/Linux/2015-09/123519.htm