大雨:轻松上手 Docker:一站式指南助你成为容器化技术高手
一个小技巧,日常常用的命令存到 Notion 等常用工具中,好记性不如烂笔头。
如果遇到问题暂时没解决,千万不要熬夜,睡一觉醒来会发现有惊喜。
写在前面
AI 时代让我们每个人都有了探索世界的新方式。本文将引领你从选择合适的云服务器开始,深入到 Docker 的安装与应用,再到 Docker Compose 的高效多容器管理。我们不仅讲解如何通过 Portainer 等工具管理 Docker,还涉及文件备份、系统监控等实用技巧。此外,你将了解 Docker 与虚拟机的区别,掌握如何利用 Docker 在开发过程中实现快速、一致的环境部署。这不仅是一个指南,更是你 Docker 旅程中的伙伴。让我们一起探索 Docker 的强大能力,开启高效、有序的开发新篇章!
- 本文价值一个疯狂星期四
- 因为为了写它专门买了个服务器
- 工具推荐
- finalshell*(有很多李鬼)
- debian
1、准备工作,买服务器,安装 Docker
1.1、阿里云购买
https://ecs-buy.aliyun.com/ecs/#/simple
直接点这个链接会提示我们登录,如果没有注册可以注册下,去年 11 的时候有活动 99 元一年的服务器(销售给我打电话,说赶快买,错过要等 1 年,结果从去年双 11 到现在,还是这个价格)
注册成功了,输入上面这个链接,购买 ECS。
我已经买过 1 个了,这次演示就重新买了。因为没有找到 debian 的选项。选择自定义,选了最低配置,大家可以从这里入手,后面有需要再升配就好了,升配不用重装,网页上点点就好了。
选择操作系统,选择自己熟悉的,我比较习惯 Debian。选择最新版的就行
设置自动快照
建议设置,就是我们自己瞎搞八搞系统整坏了,恢复下昨天的正常版本,这样操作起来就完全无忧了。 设置安全组,为了后面方便使用,我们可以试下。自定义安全组 -20240329
设置密码
整体检查一下,下单付款
要说不说,弄个 ECS 是挺贵的,主要一个起步,展示 Docker 如何使用,大家有环境就不用买它的了。
1.2、使用 FinallShell 连接服务器
购买完成以后,开始进入 ECS 界面找到刚刚的服务器,找到它的 IP 地址,121.40.138.224
打开 FinallShell 。添加新的连接
添加服务器信息
确定后保存,然后双击连接,第一次连接会提示,接受并保存
登录成功
升级安装插件(可选,如果报错就不折腾了,主要是为了弄 Docker)
apt update && apt full-upgrade -y && apt autoremove && apt autoclean
apt install sudo htop git wget curl screen emacs vim ufw net-tools screen -y
1.3、升级系统
系统升级一下
apt update
apt upgrade -y
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
1.4、安装 DockerCE 和 Docker-Compose
然后加入 Docker 的 GPG 公钥和 apt 源
curl -sSL https://download.docker.com/linux/debian/gpg | gpg --dearmor > /usr/share/keyrings/docker-ce.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/debian $(lsb_release -sc) stable" > /etc/apt/sources.list.d/docker.list
然后更新系统后即可安装 Docker CE:
apt update
apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
此时可以使用 docker version
命令检查是否安装成功
root@iZbp13svrytn8818ps7qwpZ:~
Client: Docker Engine - Community
Version: 26.0.0
API version: 1.45
Go version: go1.21.8
Git commit: 2ae903e
Built: Wed Mar 20 15:18:01 2024
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 26.0.0
API version: 1.45 (minimum version 1.24)
Go version: go1.21.8
Git commit: 8b79278
Built: Wed Mar 20 15:18:01 2024
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.28
GitCommit: ae07eda36dd25f8a1b98dfbf587313b99c0190bb
runc:
Version: 1.1.12
GitCommit: v1.1.12-0-g51d5e94
docker-init:
Version: 0.19.0
GitCommit: de40ad0
看下 docker-compose 版本
root@iZbp13svrytn8818ps7qwpZ:~
Docker Compose version v2.25.0
docker compose
而不是 docker-compose
2、从最简单的例子开始—安装 Portainer
2.1、安装基础功能
开始命令行输入如下命令,开始安装:
docker run -d -p 9001:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer-ce
这个命令会做以下几件事:
-d
:后台运行容器。-p 9001:9000
:将容器的 9000 端口映射到宿主机的 9001 端口,Portainer 将通过宿主机的这个端口提供 Web 访问界面。--name=portainer
:为容器指定一个名字,这里是portainer
。--restart=always
:确保 Docker 守护进程重启时容器自动启动。-v /var/run/docker.sock:/var/run/docker.sock
:将宿主机的 Docker 套接字文件挂载到容器内的相同位置,使 Portainer 能够与 Docker 守护进程通信,管理 Docker 容器。
curl 127.0.0.1:9001
如果它没有启动或者启动失败,可能会是下面这样的结果,比如访问 8000 端口
curl 127.0.0.1:8000
2.2、通过网页访问,打开安全组策略
默认情况下,我们在外面无法访问阿里云的机器,前面购买的时候打开了几个端口,但是没有 Portainer 的 9001 端口,需要打开安全策略,它可以通过阿里云的安全组进入,也可以从那个机器进入,方便期间,我们从 ECS 机器界面进入:
前面购买的时候给它创建了一个新的安全组,方便管理,点击管理规则开始设置
加入 9001 端口的访问
这个时候,我们就可以在自己电脑上,通过浏览器访问了
到此,第一个 docker 容器就安装好了。关于什么是容器,我们最后再来讲,接下来我们要删除这个容器。
2.3、停止容器删除容器
前面介绍了 docker run
命令,接下来我们看下,如何停止容器,为我们后续的演示做准备。
docker ps
这里命令用来查看现在系统中有哪些容器在运行
从这里我们可以看出来,有一个容器,它名字叫 portainer
,我们现在要停止它。
docker stop portainer
这个时候我们可以再次运行上面的命令,就发现已经没有容器了
docker ps
如果我们想看所有容器,包括停止的容器,可以在前面的命令后面加上 -a
docker ps -a
接下来,我们需要删除它,后面要重新建立
docker rm portainer
这个时候我们就会发现, docker ps -a
也没有了
我们这样创建的容器有个问题,如果容器删除了,里面的数据也删除了,这就很麻烦了,所以我们需要把容器里面的数据,放在外面,不放在容器里面,这个时候就有一个概念叫文件映射。
2.4、文件映射(卷挂载)
文件映射我了解的有几种方式,一种是通过 docker 命令创建一个卷(volumn),另外就直接对应宿主机的路径,简单起见我们就直接用宿主机路径。通俗点理解就是桌面快捷方式。看起来在桌面,其实只是快捷方式,真正的东西在其他地方。
docker run -d -p 9001:9000 \
--name=portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /opt/dokdt/portainer/data:/data \
portainer/portainer-ce
在这里,我们让容器里面的 /data,映射到主机的 /opt/dokdt/portainer/data。
通过 ls 命令,我们能看到,它自己创建了后面的这些文件夹,并把 protainer.db
文件放了进来。
这样设置以后,我们删除容器,数据还是保留在宿主机上。
2.5、Linux 命令行小技巧
- docker rm p ,我们在命令输入这些,按住 tab 键,它会自动帮我们补齐后面的部分
- 我们在输入一个很长的命令行,比如前面的 docker run xxx 很长一段,想要回到最前面。
- 也就是 docker 这里,可以用快捷键 ctrl + a
从前面这个案例我们发现,如果要调整命令行并不容易,参数很多,不好编辑,另外如果我们希望用 2 个容器分别来放数据库和 portainer 呢?这个时候有人就想到了,一个更加方便编辑的文件格式—yaml 格式,然后通过解析这个文件,形成 docker run 命令,这样就简化了整个过程。这就是 docker-compose。
3、Docker-Compose 多容器管理
3.1、单容器示例
网上的很多项目都提供了 docker-compose 的文件来操作,这里介绍的内容也是类似的。下面是一个 docker-compose 文件内容
version: '3.8'
services:
portainer:
image: portainer/portainer-ce
container_name: portainer
ports:
- "9001:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/dokdt/portainer/data:/data
restart: always
这个文件就叫 docker-compose.yml
docker compose up -d
这样就启动了容器,我们如果要调整,就修改这个文件,再执行一次 docker compose 就可以了。
有了这样一个文件,我们就有机会对一个服务器做整体编排了。比如一个应用,它需要 redis,需要 mysql,我们就可以放一个 yml 文件中去管理维护。比如最近比较流行的 fastgpt。
3.2、多容器示例
version: '3.3'
services:
pg:
image: ankane/pgvector:v0.5.0
container_name: pg
restart: always
ports:
- 5432:5432
networks:
- fastgpt
environment:
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
- POSTGRES_DB=postgres
volumes:
- ./pg/data:/var/lib/postgresql/data
mongo:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18
container_name: mongo
restart: always
ports:
- 27017:27017
networks:
- fastgpt
command: mongod --keyFile /data/mongodb.key --replSet rs0
environment:
- MONGO_INITDB_ROOT_USERNAME=myusername
- MONGO_INITDB_ROOT_PASSWORD=mypassword
volumes:
- ./mongo/data:/data/db
entrypoint:
- bash
- -c
- |
openssl rand -base64 128 > /data/mongodb.key
chmod 400 /data/mongodb.key
chown 999:999 /data/mongodb.key
echo 'const isInited = rs.status().ok === 1
if(!isInited){
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo:27017" }
]
})
}' > /data/initReplicaSet.js
# 启动MongoDB服务
exec docker-entrypoint.sh "$@" &
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')" > /dev/null 2>&1; do
echo "Waiting for MongoDB to start..."
sleep 2
done
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
wait $!
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.7
ports:
- 3000:3000
networks:
- fastgpt
depends_on:
- mongo
- pg
restart: always
environment:
- DEFAULT_ROOT_PSW=1234
- OPENAI_BASE_URL=http://oneapi:3000/v1
- CHAT_API_KEY=sk-fastgpt
- DB_MAX_LINK=30
- TOKEN_KEY=any
- ROOT_KEY=root_key
- FILE_TOKEN_KEY=filetoken
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
- PG_URL=postgresql://username:password@pg:5432/postgres
volumes:
- ./config.json:/app/data/config.json
- ./fastgpt/tmp:/app/tmp
mysql:
image: mysql:8.0.36
container_name: mysql
restart: always
ports:
- 3306:3306
networks:
- fastgpt
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: oneapimmysql
MYSQL_DATABASE: oneapi
volumes:
- ./mysql:/var/lib/mysql
oneapi:
container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest
ports:
- 3001:3000
depends_on:
- mysql
networks:
- fastgpt
restart: always
environment:
- SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi
- SESSION_SECRET=oneapikey
- MEMORY_CACHE_ENABLED=true
- BATCH_UPDATE_ENABLED=true
- BATCH_UPDATE_INTERVAL=10
- INITIAL_ROOT_TOKEN=fastgpt
volumes:
- ./oneapi:/data
networks:
fastgpt:
这个文件很复杂,完全搞懂需要点时间,我们挑重点部分来讲就可以了。它涵盖了多个服务,包括数据库(PostgreSQL 和 MongoDB)、一个 AI 模型服务( fastgpt
)、MySQL 数据库以及一个 API 服务( oneapi
)。为了帮助初学者更好地理解这个文件,我们将逐个解释其中包含的几个关键点: networks
、 environment
、 command
、 entrypoint
和 depends_on
。
Networks
- 作用 :在 Docker Compose 文件中定义网络,允许容器间通过名称而非 IP 地址进行通信。这提高了服务间通信的可读性和管理便捷性。
- 示例中的应用 :
fastgpt
网络连接了所有服务,确保它们可以互相发现和通信。
Environment
- 作用 :用于设置环境变量,这些变量可以被容器内运行的应用程序读取。环境变量常用于配置应用行为或传递敏感信息(如数据库密码)。
- 示例中的应用 :每个服务通过
environment
配置数据库凭据、API 密钥等信息,如POSTGRES_USER
和POSTGRES_PASSWORD
为 PostgreSQL 服务设置用户名和密码。
Command
- 作用 :覆盖容器启动后默认执行的命令。这对于自定义容器的启动行为或传递额外参数至应用非常有用。
- 示例中的应用 :在
mongo
服务中,command
用于启动 MongoDB 服务并配置键文件和复制集设置。
Entrypoint
- 作用 :指定容器启动时执行的首个命令,可以是一个命令或一个脚本。
entrypoint
与command
的区别在于entrypoint
定义了容器的入口点,而command
则提供了传递给entrypoint
的参数。 - 示例中的应用 :
mongo
服务使用entrypoint
执行一个 bash 脚本,该脚本生成加密密钥,修改文件权限,初始化 MongoDB 的复制集,等待 MongoDB 启动完成后再执行后续操作。
Depends_on
- 作用 :定义服务之间的依赖关系。使用
depends_on
可以指定一个服务在其他服务启动后再启动,但不保证服务完全就绪。 - 示例中的应用 :
fastgpt
和oneapi
服务分别使用depends_on
确保在它们依赖的数据库服务(mongo
、pg
、mysql
)启动之后再启动。
通过理解这些关键概念,我们可以更好地掌握 Docker Compose 文件的结构和功能,从而有效地编排复杂的多容器应用。这个示例展示了一个典型的开发环境配置,包括数据库服务、应用服务和他们之间的依赖关系,是学习 Docker Compose 的一个很好的起点。
3.3、补充说明
docker compose
默认会调用 docker-compose.yml
这个文件,如果我们有多个 yml 文件怎么办呢?通过-f 参数就可以了
docker compose -f ./fastgpt.yml up -d
3.4、Linux 命令行小技巧
- 本机和服务器之间要传递文件,FinallShell 可以直接操作。
- vim 小技巧 vim 大部分情况下不需要用,特别是 finallshell 还这么方便的情况下。平常用到的几个小技巧。 gg : 回到第一行 G: 到最后一行 x: 删除一个字符 xn: n 是几就删除几个字符 dd:删除一行 u: 还原上一次操作 i:进入编辑状态 esc:退出编辑状态%s/原始内容/要替换的内容/g 这里的 g 是全局替换的意思
Portainer 是一个非常好用的 Docker 管理工具,如果不是很习惯命令行方式,它可以帮忙降低使用难度,这部分内容如果有需求可以留言给我,再做补充。
对于我们爱好者来说,系统搭建好了,就好了,其实事情才刚刚开始。
4、Docker 问题排查和日常维护
4.1、日志查看和问题排查
Docker logs
容器启动以后,如果出现异常,我们可以通过日志来判断
docker logs portainer
通过这个命令我们可以看到容器的日志,排查问题所在。
如果我想要进入 docker 内部要如何做呢?
Docker exec
要进入 Docker 容器内部,通常使用 docker exec
命令,这允许您在运行中的容器内执行命令。最常见的用途之一是启动一个交互式的 bash 会话,这样您就可以在容器内部以交互方式运行命令,就像在一个常规的 Linux 环境中一样。
示例命令
假设您的容器名为 fastgpt
,要进入该容器,可以使用以下命令:
docker exec -it fashgpt /bin/bash
这里的参数解释如下:
docker exec
:Docker 的命令,用于在运行中的容器内执行命令。-it
:-i
或--interactive
保持标准输入(STDIN)开放,即使不附加到终端也是如此,允许您与会话互动。-t
或--tty
分配一个伪终端,这使得您能够像在常规终端中一样与容器进行交互。
mycontainer
:目标容器的名称或 ID。/bin/bash
:您希望在容器内执行的命令,这里是启动 bash。如果容器中没有 bash,您也可以尝试使用/bin/sh
或其他可用的 shell。
进入容器内部
执行上述命令后,您的命令行界面将变为容器内部的 bash 会话,您可以在容器内运行命令、查看文件系统等,就好像您正在操作一个独立的 Linux 系统一样。要退出容器会话,可以简单地键入 exit
命令或使用 Ctrl+D
快捷键。
注意事项
- 并非所有的容器都会包含 bash 或 sh,这取决于容器镜像是如何构建的。如果容器镜像基于非常轻量级的基础镜像(如
alpine
),可能只有/bin/sh
可用。 - 使用
docker exec
进入容器主要用于调试和管理任务。对于希望与容器持续交互的应用场景,应考虑其他方法,如在容器启动时直接运行交互式应用。
通过 docker exec
命令,Docker 提供了一种便捷的方式来直接与运行中的容器进行交互,这在开发和调试过程中尤其有用。
容器开始运行以后,会产生很多数据,所以备份这些数据就是我们需要考虑的问题了,另外还有一个文件是硬盘爆了咋办?现在有很多工具帮忙看,云厂商有工具,类似宝塔这样的工具也能提供能力。下面的方法比较原始一些。
4.2、文件备份和系统监控
有了上面的知识,我们只需要备份文件映射的部分就可以了。
#!/bin/bash
BACKUP_DATE=$(date +"%Y-%m-%d")
BACKUP_PATH="/opt/dokdt/portainer_backup_$BACKUP_DATE.tar.gz"
docker run --rm \
-v /opt/dokdt/portainer/data:/data \
-v $BACKUP_PATH:/backup.tar.gz \
busybox tar czvf /backup.tar.gz /data
echo "Portainer数据备份完成,备份文件位于: $BACKUP_PATH"
这个脚本现在不用自己写了,丢给 GPT 去弄就好了。把我们的需求描述清楚,比如说我们只保留最近 7 天的备份?也是可以的,这个功能一般国内云厂商都有类似服务,省点我们自己写也可以。
系统监控可以用 uptime 这样的开源项目,也是个 docker
4.3、Linux 命令行小技巧
随时时间推移,硬盘数据会越来越多,如果没有其他的手段,下面介绍几个古早就有的工具。
top 命令,按 q 退出
这个命令里面,简单点就是按 c/m 看内容和 cpu 占比等
free 命令看内存
df
显示文件系统的磁盘空间使用情况
du
估算文件和目录占用的磁盘空间
用途 :估算文件和目录占用的磁盘空间。
5、Docker 和主机的关系,和 VM 的区别
Docker 的出现是为了解决“在我的机器上能运行,但是在你的机器上却不能运行”的常见问题,这个问题通常被称为“环境地狱”。在 Docker 之前,软件开发和部署面临着许多挑战,包括环境不一致、依赖冲突、部署复杂等。Docker 通过提供一个轻量级的、可移植的、自给自足的容器来封装应用和其依赖,使得应用能够在任何支持 Docker 的环境中无缝运行。
5.1、Docker 出现的原因和要解决的问题
- 环境一致性 :确保开发、测试、生产环境保持一致,减少“在我机器上运行正常”问题的出现。
- 依赖管理 :通过容器封装应用及其依赖,简化依赖管理。
- 部署简化 :提供快速、一致的部署流程,提高部署效率和可靠性。
- 资源隔离 :确保应用之间的资源隔离,提高系统的安全性和稳定性。
- 资源利用率 :与传统虚拟机相比,容器化能更高效地利用系统资源。
5.2、Docker、VM 和主机之间的关系、区别和联系
- 与虚拟机(VM)的区别 :
- 资源开销 :VM 包括应用、必要的二进制文件和库,以及整个操作系统,而 Docker 容器共享宿主机的操作系统,只包含应用和其依赖,因此更加轻量级。
- 启动速度 :容器可以在几秒钟内启动,而虚拟机可能需要几分钟。
- 性能 :容器几乎没有额外的性能开销,而虚拟机则因为需要额外的操作系统,会有一定的性能损耗。
- 与主机的关系 :
- Docker 容器运行在宿主机上,它们通过 Docker 引擎与宿主机的操作系统交互,利用宿主机的内核功能(如 cgroups 和 namespaces)实现资源隔离和管理。
- 容器视角下的“主机”是指容器内部的环境,这为应用提供了一个隔离的运行时环境,虽然与宿主机共享内核,但在许多方面表现得就像是一个独立的机器。
总的来说,Docker 的出现极大地简化了软件的打包、分发、安装和运行过程,通过容器化技术,促进了 DevOps 文化的发展,加速了软件开发和部署的现代化进程。
6、后记
一个特殊的机缘,可能是 rootless,用了 podman,目前主力工具是 podman,为了写这篇笔记专门装了 docker。纰漏之处在所难免。
反向代理一直用的 nginx,但是 nginx proxy manager 遇到多好几次坑,看起来镜像 1G,但是占用空间有 10G,而且 inode 很快就消耗光了,硬生生逼着用了 caddy。暂时还好。
如果有什么需要进一步解答的,可以留言。
以下是一个总结表格,列出了一些常用的 Docker 命令及其用途:
命令 |
用途 |
|
创建一个新的容器并运行一个命令 |
|
启动一个或多个已停止的容器 |
|
停止一个运行中的容器 |
|
重启容器 |
|
删除一个或多个容器 |
|
列出容器 |
|
在运行的容器中执行命令 |
|
获取容器的日志 |