Docker官方教程 Docker Getting Started Tutorial
2024-01-20 23:24 youxin 阅读(62) 评论(0) 编辑 收藏 举报docker pull [仓库的URL]/[命名空间]/[镜像名称]:[版本号]
默认的仓库URL:https://index.docker.io/v1/
默认的命名空间:library
默认的版本号:latest
FROM node:18-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "src/index.js"]
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
docker tag getting-started YOUR-USER-NAME/getting-started
.现在再次尝试您的push命令。如果您正在从Docker Hub复制值,可以省略tagname部分,因为我们没有为镜像名称添加标签。如果不指定标签,Docker将使用一个名为latest的标签。
docker push YOUR-USER-NAME/getting-started
在新实例上运行我们的镜像
既然我们的镜像已经构建并推送到一个注册表中,让我们尝试在一个从未见过这个容器镜像的全新实例上运行我们的应用程序!为此,我们将使用Play with Docker。
1.在浏览器中打开Play with Docker。Play with Docker
2.使用您的Docker Hub帐户登录。
3.一旦登录成功,点击左侧边栏的"+ ADD NEW INSTANCE"链接。(如果您没有看到它,请将浏览器宽度稍微调大。)几秒钟后,一个终端窗口将在您的浏览器中打开。
4.在终端中,启动您刚刚推送的应用程序。
docker run -dp 3000:3000 YOUR-USER-NAME/getting-started
您应该会看到镜像被拉取下来并最终启动!
5.当3000标志出现时,点击它,您应该看到带有您的修改的应用程序!太棒了!如果3000标志没有显示,您可以点击"Open Port"按钮并输入3000。
容器卷
通过之前的实验,我们看到每个容器在启动时都从镜像定义开始。虽然容器可以创建、更新和删除文件,但当容器被删除时,这些更改都会丢失,并且所有更改都隔离在该容器内。使用卷,我们可以改变所有这些。
卷提供了将容器的特定文件系统路径连接回主机机器的能力。如果在容器中挂载了一个目录,那么该目录中的更改也会在主机机器上看到。如果我们跨容器重新启动挂载了同一目录的情况下,我们将看到相同的文件。
有两种主要类型的卷。我们最终会同时使用两者,但我们将从命名卷开始。
持久化我们的待办数据
默认情况下,待办应用程序将其数据存储在 /etc/todos/todo.db 的 SQLite 数据库中。如果您不熟悉 SQLite,不用担心!它只是一个关系数据库,其中所有数据都存储在一个单独的文件中。虽然这对于大规模应用程序来说并不是最佳选择,但对于小型演示来说是可以的。我们稍后将讨论将其切换到不同的数据库引擎。
由于数据库是一个单一文件,如果我们可以将该文件持久化到主机并使其对下一个容器可用,那么下一个容器应该能够从上一个容器离开的地方继续工作。通过创建一个卷并将其连接(通常称为"挂载")到存储数据的目录,我们可以持久化数据。当我们的容器写入 todo.db 文件时,它将被持久化到主机上的卷中。
如前所述,我们将使用一个命名卷。将命名卷视为数据的一个简单存储桶。Docker 管理磁盘上的物理位置,您只需要记住卷的名称。每次使用卷时,Docker 将确保提供正确的数据。
1.使用 docker volume create 命令创建一个卷。
docker volume create todo-db
2.再次在仪表板中停止待办应用程序容器(或使用 docker rm -f <container-id> 命令),因为它仍在运行而没有使用持久卷。
3.启动待办应用程序容器,但添加 -v 标志以指定一个卷挂载。我们将使用命名卷并将其挂载到 /etc/todos,这将捕获在该路径下创建的所有文件。
docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
4.一旦容器启动,打开应用程序并添加一些项目到您的待办列表。
5.移除用于待办事项应用程序的容器。使用仪表板或docker ps命令获取容器的ID,然后使用docker rm -f <container-id>命令将其删除。
6.使用与上面相同的命令启动一个新的容器。
7.打开应用程序。您应该能够看到您的列表中仍然有您的项目!
8.在查看完您的列表后,随时删除容器。
太棒了!您现在已经学会了如何持久保存数据!
专业提示
虽然命名卷和绑定挂载(我们将在稍后讨论)是默认Docker引擎安装支持的两种主要卷类型,但有许多卷驱动程序插件可用于支持NFS、SFTP、NetApp等等!这在您开始在具有Swarm、Kubernetes等的群集环境中运行容器时将特别重要。
深入了解我们的卷
许多人经常问:“当我使用命名卷时,Docker实际上将我的数据存储在哪里?”如果您想知道,您可以使用docker volume inspect命令。
docker volume inspect todo-db
[
{
"CreatedAt": "2019-09-26T02:18:36Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
"Name": "todo-db",
"Options": {},
"Scope": "local"
}
]
Mountpoint是数据实际存储在磁盘上的位置。请注意,在大多数机器上,您需要具有root权限才能从主机访问此目录。但是,数据就存储在那里!
在Docker Desktop上直接访问卷数据
在Docker Desktop上运行时,Docker命令实际上是在您的计算机上的一个小型虚拟机内运行的。如果您想查看Mountpoint目录的实际内容,您首先需要进入虚拟机内部。
回顾
到目前为止,我们有一个能够在重启后保持正常运行的应用程序!我们可以向投资者展示它,并希望他们能够理解我们的愿景!
然而,我们之前看到,为每个更改重建映像需要相当长的时间。肯定有更好的方法来进行更改,对吧?有了绑定挂载(我们之前提到过),就有了更好的方法!现在让我们看看它吧!
使用绑定挂载(Bind Mounts)
在前一章中,我们讨论并使用了命名卷来持久化我们数据库中的数据。命名卷非常适用于简单存储数据,因为我们不必担心数据存储的位置。
通过绑定挂载,我们可以控制主机上的确切挂载点。我们可以使用它来持久化数据,但通常也用于将附加数据提供给容器。在开发应用程序时,我们可以使用绑定挂载将我们的源代码挂载到容器中,以使其看到代码更改、做出响应,并让我们立即看到更改。
对于基于Node的应用程序,nodemon是一个很好的工具,它可以监视文件更改然后重新启动应用程序。大多数其他编程语言和框架也有类似的工具。
快速卷类型比较
命名卷和绑定挂载是Docker引擎提供的两种主要卷类型。然而,还提供了其他卷驱动程序来支持其他用途(如SFTP、Ceph、NetApp、S3等)。
启动开发模式容器
为了运行支持开发工作流程的容器,我们将执行以下操作:
- 将我们的源代码挂载到容器中
- 安装所有依赖项,包括"dev"依赖项
- 启动nodemon以监视文件系统更改
所以,让我们开始吧!
1.确保您没有运行自己的getting-started容器(只有教程本身应该在运行)。
2.还要确保您在应用程序源代码目录中,即/path/to/getting-started/app。如果不在,您可以进入该目录,例如:
cd /path/to/getting-started/app
3.现在您已经在getting-started/app目录中,请运行以下命令。之后我们将解释正在发生什么:
docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
node:18-alpine \
sh -c "yarn install && yarn run dev"
如果您使用的是PowerShell,请使用以下命令。
docker run -dp 3000:3000 `
-w /app -v "$(pwd):/app" `
node:18-alpine `
sh -c "yarn install && yarn run dev"
- -dp 3000:3000 - 与之前相同。以分离(后台)模式运行并创建端口映射
- -w /app - 设置容器的当前工作目录,命令将从该目录运行
- -v "$(pwd):/app" - 将主机的当前getting-started/app目录绑定挂载(链接)到容器的/app目录。注意:Docker要求绑定挂载使用绝对路径,因此在此示例中,我们使用pwd来打印工作目录的绝对路径,即app目录,而不是手动输入它
- node:18-alpine - 要使用的映像。请注意,这是我们应用程序的Dockerfile中的基本映像
- sh -c "yarn install && yarn run dev" - 命令。我们使用sh(alpine没有bash)启动一个shell,并运行yarn install来安装所有依赖项,然后运行yarn run dev。如果查看package.json,您将看到dev脚本正在启动nodemon。
4.您可以使用docker logs -f <container-id>来查看日志。当您看到以下内容时,您就可以开始了:
docker logs -f <container-id>
$ nodemon 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退出。
5.现在,让我们对应用程序进行更改。在src/static/js/app.js文件中,将"Add Item"按钮更改为简单的"Add"。这个更改将在第109行进行 - 记得保存文件。
- {submitting ? 'Adding...' : 'Add Item'}
+ {submitting ? 'Adding...' : 'Add'}
6.只需刷新页面(或打开页面),您几乎会立即在浏览器中看到更改。Node服务器重新启动可能需要几秒钟,所以如果出现错误,只需等待几秒钟后刷新。
7.随时进行其他更改。完成后,停止容器并使用docker build -t getting-started ..构建新映像。
在本地开发设置中使用绑定挂载是非常常见的。优点是开发机器不需要安装所有构建工具和环境。只需一个docker run命令,开发环境就会被拉取并准备就绪。我们将在未来的步骤中讨论Docker Compose,因为这将有助于简化我们的命令(我们已经获得了很多标志)。
总结
到目前为止,我们可以持久化我们的数据库并迅速响应投资者和创始人的需求。太棒了!但是,猜猜看?我们收到了一个好消息!
您的项目已被选中进行未来开发!
为了准备投入生产,我们需要将数据库从SQLite迁移到能够更好地扩展的东西。为了简单
容器网络
请记住,默认情况下,容器在隔离中运行,对于同一台机器上的其他进程或容器一无所知。那么,如何让一个容器与另一个容器通信?答案是网络。现在,您不必成为网络工程师(万岁!)。只需记住这个规则…
如果两个容器在同一个网络上,它们可以相互通信。如果它们不在同一个网络上,它们就不能相互通信。
启动MySQL
将容器放入网络中有两种方法:1)在启动时分配它,或者2)连接到现有容器。现在,我们将首先创建网络,并在启动时将MySQL容器连接到网络。
1.创建网络。
docker network create todo-app
2.启动一个MySQL容器并将其连接到网络。我们还将定义一些环境变量,数据库将使用这些变量来初始化数据库(请参阅MySQL Docker Hub列表中的“环境变量”部分)。
docker run -d \
--network todo-app --network-alias mysql \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:8.0
如果您正在使用PowerShell,则使用以下命令。
docker run -d `
--network todo-app --network-alias mysql `
-v todo-mysql-data:/var/lib/mysql `
-e MYSQL_ROOT_PASSWORD=secret `
-e MYSQL_DATABASE=todos `
mysql:8.0
您还会看到我们指定了--network-alias标志。我们将在一会儿回到这个。
专业提示
您会注意到我们在这里使用了一个名为todo-mysql-data的卷,并将其挂载到/var/lib/mysql,这是MySQL存储其数据的位置。但是,我们从未运行过docker volume create命令。Docker会识别我们想要使用一个命名卷,并为我们自动创建一个。
3.为确认数据库已经启动并运行,连接到数据库并验证它是否连接。
docker exec -it <mysql-container-id> mysql -p
当密码提示出现时,输入secret。在MySQL shell中,列出数据库并验证是否看到了todos数据库。
mysql> SHOW DATABASES;
您应该会看到类似以下的输出:
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| todos |
+--------------------+
5 rows in set (0.00 sec)
太好了!我们有了todos数据库,它已经准备好供我们使用!
要退出sql终端,请在终端中输入exit。
连接到MySQL
既然我们知道MySQL正在运行,让我们来使用它!但问题是…如何使用它?如果我们在同一网络上运行另一个容器,如何找到容器(请记住,每个容器都有自己的IP地址)?
为了找出答案,我们将使用nicolaka/netshoot容器,它附带了许多对于排除故障或调试网络问题非常有用的工具。
1.使用nicolaka/netshoot镜像启动一个新的容器。确保将其连接到同一网络。
docker run -it --network todo-app nicolaka/netshoot
2.在容器内,我们将使用dig命令,这是一个有用的DNS工具。我们将查找主机名mysql的IP地址。
dig mysql
然后您将获得如下输出:
; <<>> DiG 9.18.8 <<>> mysql
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;mysql. IN A
;; ANSWER SECTION:
mysql. 600 IN A 172.23.0.2
;; Query time: 0 msec
;; SERVER: 127.0.0.11#53(127.0.0.11)
;; WHEN: Tue Oct 01 23:47:24 UTC 2019
;; MSG SIZE rcvd: 44
在“ANSWER SECTION”中,您将看到mysql的A记录,解析为172.23.0.2(您的IP地址可能会有不同的值)。尽管mysql通常不是有效的主机名,但Docker能够将其解析为具有该网络别名的容器的IP地址(还记得我们之前使用的--network-alias标志吗?)。
这意味着...我们的应用只需要简单地连接到名为mysql的主机,它就会与数据库通信!没有比这更简单的了!
完成后,运行"exit"来关闭容器
使用MySQL运行我们的应用
该待办事项应用支持设置一些环境变量来指定MySQL连接设置。它们包括:
- MYSQL_HOST - 运行中的MySQL服务器的主机名
- MYSQL_USER - 用于连接的用户名
- MYSQL_PASSWORD - 用于连接的密码
- MYSQL_DB - 连接后要使用的数据库
警告
虽然在开发中使用环境变量来设置连接设置通常是可以接受的,但在生产环境中运行应用程序时强烈不建议使用。Docker的前安全主管Diogo Monica写了一篇很棒的博文来解释原因。
更安全的机制是使用容器编排框架提供的密钥支持。在大多数情况下,这些密钥被挂载为运行容器中的文件。您会看到许多应用程序(包括MySQL镜像和待办事项应用程序)也支持具有"_FILE"后缀的环境变量,以指向包含变量的文件。
例如,设置MYSQL_PASSWORD_FILE变量将导致应用程序使用所引用文件的内容作为连接密码。Docker不会对这些环境变量提供任何支持。您的应用程序需要知道查找该变量并获取文件内容。
解释了所有这些,让我们启动我们的开发准备好的容器!
1.我们将指定上面提到的每个环境变量,并将容器连接到我们的应用程序网络。
docker run -dp 3000:3000 \
-w /app -v "$(pwd):/app" \
--network todo-app \
-e MYSQL_HOST=mysql \
-e MYSQL_USER=root \
-e MYSQL_PASSWORD=secret \
-e MYSQL_DB=todos \
node:18-alpine \
sh -c "yarn install && yarn run dev"
如果您使用PowerShell,请使用以下命令:
docker run -dp 3000:3000 `
-w /app -v "$(pwd):/app" `
--network todo-app `
-e MYSQL_HOST=mysql `
-e MYSQL_USER=root `
-e MYSQL_PASSWORD=secret `
-e MYSQL_DB=todos `
node:18-alpine `
sh -c "yarn install && yarn run dev"
2.如果查看容器的日志(docker logs <container-id>),应该会看到一个消息,指示它正在使用mysql数据库。
# Previous log messages omitted
$ nodemon 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`
Connected to mysql db at host mysql
Listening on port 3000
3.之后,打开浏览器中的应用程序并添加一些项目到您的待办事项列表中。
4.连接到mysql数据库并证明项目已写入数据库。请记住,密码是"secret"。
docker exec -it <mysql-container-id> mysql -p todos
然后在mysql shell中运行以下命令:
mysql> select * from todo_items;
+--------------------------------------+--------------------+-----------+
| id | name | completed |
+--------------------------------------+--------------------+-----------+
| c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! | 0 |
| 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome! | 0 |
+--------------------------------------+--------------------+-----------+
显然,您的表看起来可能会不同,因为它包含您的项目。但是,您应该会看到它们存储在那里!
如果您快速查看Docker Dashboard,您将看到我们有两个未分组的应用程序容器在运行。但是,没有明显迹象表明它们在一个单独的应用程序中分组在一起。我们将在不久后看到如何改进这一点!
总结
到目前为止,我们的应用程序现在将数据存储在一个单独容器中运行的外部数据库中。我们学到了一点关于容器网络的知识,以及如何使用DNS执行服务发现。
但是,您很可能开始感到有点不知所措,因为需要启动此应用程序所需的一切。我们必须创建一个网络、启动容器、指定所有环境变量、暴露端口等等!这是需要记住的很多内容,而且肯定会使传递给其他人的任务变得更加困难。
在下一节中,我们将讨论Docker Compose。使用Docker Compose,我们可以以更轻松的方式分享我们的应用程序堆栈,并让其他人使用单一(而简单)的命令启动它们!
1、问题截图
cat /var/log/mysql/error.log
2019-01-28T09:49:57.076019Z 0 [ERROR] [FATAL] InnoDB: Table flags are 0 in the data dictionary but the flags in file ./ibdata1 are 0x4800!
2、问题原因
InnoDB的问题。原来安装的是mysql8.0版本,比现在的5.7版本要高,但在高版本数据库时创建的一些数据库数据都还在,使用的是高版本的InnoDB引擎,而低版本的数据库服务还是指向了这些数据,但是一些版本的数据格式不同,这个时候,我们只要把高版本的数据删掉就可以了,删掉后重新启动mysql就可以了。
3、解决办法
rm -R /var/lib/mysql
docker删除命名卷
docker volume rm -f todo-mysql-data
如果报错:
Error response from daemon: remove todo-mysql-data: volume is in use - [391ff55bfdd13f7e10e0e3860fad69781b53373e4330cfeb54e50eba57e02e7c]
先要执行docker stop xx
docker rm xx
然后再执行docker volume rm -rf就可以了。
Perhaps I'm overlooking, but I see you stop the container, but don't actually remove it?
docker stop 803e8aba7983
803e8aba7983
Have you tried docker rm 803e8aba7983
and then docker volume rm ...
?
使用Docker Compose
Docker Compose是一个用于定义和共享多容器应用程序的工具。通过Compose,我们可以创建一个YAML文件来定义服务,并通过一个命令来启动或关闭所有服务。
使用Compose的重要优点是你可以将应用程序堆栈定义在一个文件中,将其保存在项目仓库的根目录(现在受版本控制),并轻松地让其他人贡献到你的项目中。其他人只需克隆你的仓库并启动Compose应用程序。实际上,你现在可能会在GitHub/GitLab上看到许多项目正在这样做。
version: "3.8" services: app: image: node:12-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: secret MYSQL_DB: todos mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: todos volumes: todo-mysql-data:
使用docker compose up命令启动应用程序栈。我们将添加-d标志以在后台运行所有内容。
docker compose up -d
运行此命令时,我们应该会看到如下输出:
Creating network "app_default" with the default driver Creating volume "app_todo-mysql-data" with default driver Creating app_app_1 ... done Creating app_mysql_1 ... done
您会注意到卷也已经创建了,还有一个网络!默认情况下,Docker Compose会自动为应用程序栈创建一个专用网络(这就是为什么我们在compose文件中没有定义网络)。
3.让我们使用docker compose logs -f命令查看日志。您将看到每个服务的日志被交织成一个单一的流。当您想要查看与时间相关的问题时,这非常有用。-f标志会“跟踪”日志,因此它会在生成时提供实时输出。
Let's look at the logs using the docker-compose logs -f
command. You'll see the logs from each of the services interleaved into a single stream. This is incredibly useful when you want to watch for timing-related issues. The -f
flag "follows" the log, so will give you live output as it's generated.
The service name is displayed at the beginning of the line (often colored) to help distinguish messages. If you want to view the logs for a specific service, you can add the service name to the end of the logs command (for example, docker-compose logs -f app
).
在Docker Dashboard中查看我们的应用程序栈
如果我们查看Docker Dashboard,将看到一个名为app的组。这是来自Docker Compose的“项目名称”,用于将容器分组在一起。默认情况下,项目名称只是docker-compose.yml所在目录的名称。
如果您展开app,将看到我们在compose文件中定义的两个容器。名称也更加描述性,因为它们遵循<project-name><service-name><replica-number>的模式。因此,很容易快速查看哪个容器是我们的应用程序,哪个容器是mysql数据库。
拆除一切
当您准备好拆除一切时,只需运行docker compose down或在整个应用程序的Docker Dashboard上点击垃圾桶图标。容器将停止,网络将被删除。
移除卷
默认情况下,compose文件中的命名卷在运行docker compose down时不会被删除。如果要删除这些卷,您需要添加--volumes标志。
Docker仪表板在删除应用程序堆栈时不会删除卷。
总结
在这一节中,我们了解了Docker Compose以及它如何帮助我们大大简化多服务应用程序的定义和共享。我们通过将我们正在使用的命令翻译成适当的compose格式来创建了一个Compose文件。
此时,我们开始总结本教程。但是,关于镜像构建,我们想要介绍一些最佳实践,因为我们一直在使用的Dockerfile存在一个重大问题。所以,让我们来看看吧!