1 容器服务

1.1 为什么要用容器服务

容器化服务通过将应用及其依赖打包在一个独立的容器中,解决了环境配置不一致、依赖冲突、可移植性差等问题。它使得开发、测试和生产环境的一致性得以保障,提升了应用的可扩展性与资源利用率,同时也简化了持续集成和持续部署(CI/CD)的流程。

1.2 Podman 的安装

本文将使用Podman作为主要的容器服务,对比 Docker 有更高的安全性和更好的可扩展性,更好的配合现代化的 Kubernetes,安装Podman只需要执行以下命令:

# 安装podman
sudo dnf install podman

2 Podman 常见命令

2.1 容器命令

# 查看所有容器
podman ps -a

# 启动容器
podman start <容器id CONTRAIN ID/自定义名字 NAMES>

# 停止容器
podman stop <容器id CONTRAIN ID/自定义名字 NAMES>

# 创建容器,但不运行
podman create <镜像名 IMAGE>

# 日志情况
# 参数 -f 表示滚动跟踪
podman logs <容器id CONTRAIN ID/自定义名字 NAMES>

# 删除容器
# 参数 -f 强制删除
podman rm <容器id CONTRAIN ID/自定义名字 NAMES>

# 「创建」并「运行」容器
# 参数 -d 分离运行
# 参数 -p 端口映射,宿主机端口:容器端口,例如 80:80
# 参数 -v 目录映射,宿主机目录:容器内目录
# 参数 -e 传递环境变量
# 参数 -name 自定义名字,且在宿主机唯一
# 参数 --rm 容器结束运行时自动删除容器,用于临时调试
# 参数 --restart always/unless-stopped 重启参数:自动重启/手动停止不重启
podman run <容器id CONTRAIN ID/镜像名 IMAGE>

2.2 Pod 命令

# 查看当前 pod
podman pod ls

# 查看pod和对应的容器
podman ps -a --pod

# 删除 pod,
# 参数 -f 表示强制删除
podman pod rm <pod的名称1> <pod的名称2> <pod的名称3>

# 启动一个pod
podman pod start <pod的名称NAME>

# 停止一个pod
podman pod stop <pod的名称NAME>

2.3 Image 镜像命令

# 查看所有镜像
podman images -a

# 删除镜像
# 参数 -f 表示强制删除
podman rmi <镜像名 IMAGE>

2.4 Pod 和 Image 镜像的导入导出命令

# 查看podman版本
podman -v

# 导出pod,如果真的要导出的话记得把数据也迁移了
podman generate kube <pod的名称> > <随便一个文件名>.yaml

# 从yaml文件导入pod
podman play kube <你的pod文件名>.yaml

# 打包镜像
# 参数 -m 表明这是一个多镜像存档
podman save -m -o typecho-images2.tar docker.io/library/postgres:18 docker.io/joyqi/typecho:1.3.0-php8.2-fpm-alpine

# 或者分开打包
podman save -o postgres.tar docker.io/library/postgres:18
podman save -o typecho.tar docker.io/joyqi/typecho:1.3.0-php8.2-fpm-alpine

# 在新电脑导入
podman load -i xxxx.tar

3 实战:配置 Typecho

Typecho 是一个开源的轻量级博客系统,基于 PHP 和数据库构建,搭建个人博客或小型网站非常理想。下面我们将使用 Rocky Linux 的普通账户进行操作,创建 Nginx + Typecho + Postgres 3个独立容器构建该网站。

3.1 数据持久化准备

# 创建工作目录
mkdir -p ~/typecho/{web-data,db-data}

3.2 创建专用网络

使用 Nginx + Typecho + Postgres 3个独立容器创建服务,由于它们3个是独立的容器,不能互相访问,使用的不是同一个网络命名空间,所以需要创建专用网络

podman network create typecho-net

3.3 运行数据库容器 Postgres

执行下面的命令,出现拉取镜像速度慢或WSL报错(拉取镜像权限不足)等问题,解决方法参见「4 常见问题」

podman run -d \
  --name typecho-db \
  --network typecho-net \
  -p 5432:5432 \
  -v ~/typecho/db-data:/var/lib/postgresql:Z,U \
  -e POSTGRES_PASSWORD=password \
  -e POSTGRES_USER=typecho \
  -e POSTGRES_DB=typecho \
  docker.io/library/postgres:18

特别说明:

  1. -e 携带的参数顺序很重要,如果打乱参数顺序有可能会被默认参数覆盖导致出现不可预知的错误。
  2. :Z 用于 SELinux 系统(如 Fedora/RHEL)。如果使用 Ubuntu/Debian 且无 SELinux,可移除该后缀。Postgres 从18版本开始会自动根据版本号创建对应的版本号目录存放数据库文件,不加 :Z 可能出现权限不足,无法在宿主机创建目录的情况。
  3. :z 它表示该挂载卷可以被多个容器共享访问。
  4. :U 作用:自动通知 Podman 在启动容器前,根据容器内用户的 UID/GID 自动修改宿主机挂载目录的所有权

3.4 运行 Typecho 服务

podman run -d \
  --name typecho-web \
  --network typecho-net \
  -v ~/typecho/web-data:/app:Z,U \
  -e TIMEZONE=Asia/Shanghai \
  -e TYPECHO_INSTALL=1 \
  -e TYPECHO_DB_ADAPTER=Pdo_Pgsql \
  -e TYPECHO_DB_HOST=typecho-db \
  -e TYPECHO_DB_PORT=5432 \
  -e TYPECHO_DB_USER=typecho \
  -e TYPECHO_DB_PASSWORD=password \
  -e TYPECHO_DB_DATABASE=typecho \
  -e TYPECHO_DB_CHARSET=utf8 \
  -e TYPECHO_SITE_URL=http://localhost:8080 \
  -e TYPECHO_USER_NAME=admin \
  -e TYPECHO_USER_PASSWORD=Admin123! \
  -e [email protected] \
  docker.io/joyqi/typecho:1.3.0-php8.2-fpm-alpine
# 上述命令中,挂载整个typecho目录,防止后续可能会修改typecho的其他文件

3.5 创建 Nginx 配置文件

cat <<EOF > ~/typecho/nginx.conf
server {
    listen 80;
    server_name localhost;
    root /app; # 对应运行容器的挂载目录,告诉Nginx需要将请求转发到那个文件夹下
    index index.php;

    # 处理 Typecho 地址重写
    location / {
        try_files \$uri \$uri/ /index.php?\$args;
    }
    
    # Nginx 不能处理 php 文件
    # 转发 PHP 请求到 typecho-web 容器
    location ~ \.php$ {
        fastcgi_pass typecho-web:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
        
        # 增加以下两行,确保 PHP 能拿到宿主机访问的 IP 和端口,用于修复访问地址问题
        fastcgi_param HTTP_HOST $http_host;
        fastcgi_param SERVER_NAME $host;
        
        include fastcgi_params;
    }

    # 静态资源缓存加速
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
        expires 30d;
    }
}
EOF

3.6 运行 Nginx 容器

将宿主机的 nginx.conf 挂载进容器,并将其加入同一个网络

podman run -d \
  --name nginx \
  --network typecho-net \
  -p 8080:80 \
  -v ~/typecho/nginx.conf:/etc/nginx/conf.d/default.conf:Z \
  -v ~/typecho/web-data:/app:z \
  docker.io/library/nginx:1.29.5-alpine-slim

后续对 nginx.conf 的修改需要执行 podman exec nginx nginx -s reloadpodman kill -s HUP nginx,重新加载配置,并且需要确保配置文件没有错误。

3.7 WSL 验证结果

如果你是使用 WSL 进行实战,那么在 Windows 浏览器上访问 localhost:8080 即可查看到运行中的Typecho😎。如果不是,那么你的工作还没有结束😲!

3.8 VMware 的额外工作

如果你使用的是 VMware 下的 Rocky Linux,你需要一些额外的配置才能在 Windows 浏览器验证结果。

WSL阉割了防火墙,所需不需要额外配置,但是在VMware下,Rocky Linux作为一个完整的系统需要对防火墙进行额外的配置,例如放行对应端口

# 放行 HTTP 端口
sudo firewall-cmd --permanent --add-port=80/tcp

# 取消放行
# sudo firewall-cmd --permanent --remove-port=80/tcp

# 重载防火墙配置使生效
sudo firewall-cmd --reload

# 验证端口是否已开放
sudo firewall-cmd --list-ports

放行端口后,实际上虚拟机和你的电脑实际上并没有完全连通,还需要配置特定的端口,首先打开虚拟机中的「虚拟网络编辑器」

Linux 教程「第四期」:容器服务

选中当前正在使用的NAT模式的网卡,点击「NAT设置」,点击「添加」,填写对应信息保存。如果你配置过上一期相关的静态IP相关内容,你应该还记得 192.168.126.101 这个类似的IP,如果你不知道,使用 ip a 命令来确认你当前虚拟机的IP地址填入即可。

Linux 教程「第四期」:容器服务

完成这一步以后你就可在你的电脑浏览器打开 192.168.126.101:8080 访问到相关的网页😎。如果你发现这个网站看起来有那么一点不对劲,也许你可能遇到了Typecho下的样式问题,详见「4 常见问题」

4 常见问题

4.1 WSL 下的常见问题

4.1.1 拉取镜像时权限不够

在 WSL 第一次拉取镜像时,通常会因为权限不够发生以下错误:

[fk@DESKTOP-XXXXX ~]$ podman pull hello-world 
WARN[0000] RunRoot is pointing to a path (/run/user/1000/containers) which is not writable. Most likely podman will fail. Error: creating events dirs: mkdir /run/user/1000: permission denied

这个目录是 Linux 系统为普通用户(UID 1000)创建的运行时目录,通常由 systemdlogin 会话自动创建。这个功能在 WSL 中有被阉割了一部分,可能没有正常工作。而VMware 正常安装使用 Rocky Linux 不会出现这个错误。

解决方法:创建对应的目录并正确设置权限

# 这里的1000是对应fk用户的id,如果你不知道你的id是多少,可以使用以下命令
id # 查看当前用户的id

# 1. 创建目录
sudo mkdir -p /run/user/1000

# 2. 设置所有者为你的用户 (fk)
sudo chown fk:fk /run/user/1000

# 3. 设置正确权限 (700 = 只有所有者可读写执行)
sudo chmod 700 /run/user/1000

# 4. 再次尝试拉取镜像(当然你可以选择不拉取这个镜像)
podman pull hello-world

4.1.2 nft 错误

使用 WSL 时,可能会遇到nft类错误,这是因为 WSL 没有防火墙和完整的网络模块导致的,在VMware下正常安装 Rocky Linux 不会遇到这个错误。

internal:0:0-0: Error: Could not process rule: No such file or directory
internal:0:0-0: Error: Could not process rule: No such file or directory
Error: netavark: nftables error: "nft" did not return successfully while applying ruleset

解决方法:

mkdir -p ~/.config/containers
vi ~/.config/containers/containers.conf

# 粘贴以下内容
[network]
firewall_driver = "none"

# 删除后重新执行上述的run
podman rm -f typecho-db # 删除原有容器
podman network rm -f typecho-net # 删除原有网络
podman network create typecho-net # 重新创建网络
# 重新执行一下上面的run命令

4.2 镜像无法拉取或者速度很慢

有时候拉取镜像的时候拉不下来,这个时候就需要镜像加速,通过配置镜像加速源或者配置Podman代理的方式加速拉取镜像,当然前提你得有代理。

4.2.1 配置镜像加速源

vi ~/.config/containers/registries.conf

添加以下内容,「容器镜像监控」这个网站能看到那些镜像网站仍在运行

# 定义默认搜索的仓库
unqualified-search-registries = ["docker.io"]

# 配置 docker.io 的镜像加速
[[registry]]
prefix = "docker.io"
location = "docker.io"

# 添加加速地址 (示例,若失效需更换)
[[registry.mirror]]
location = "docker.1ms.run"

[[registry.mirror]]
location = "docker.1panel.live"

[[registry.mirror]]
location = "docker.m.ixdev.cn"

[[registry.mirror]]
location = "hub.rat.dev"

[[registry.mirror]]
location = "docker.xuanyuan.me"

4.2.2 临时配置Podman代理

首先,获取宿主机的IP

# 获取 nameserver 对应的 IP,通常就是 Windows 宿主机的 IP
HOST_IP=$(grep nameserver /etc/resolv.conf | awk '{print $2}')
echo $HOST_IP

在当前终端会话中设置环境变量,Podman 会自动识别这些变量,端口号为代理软件的端口,并且需要打开允许局域网连接

export HTTP_PROXY=http://$HOST_IP:7897
export HTTPS_PROXY=http://$HOST_IP:7897
export NO_PROXY="localhost,127.0.0.1,0.0.0.0"

VMware可能需要在虚拟机网络配置中添加对应端口,参考「3.8 VMware 的额外工作」。WSL 可能需要重新解析 DNS。

# 临时修改一下DNS,这个文件重启系统后会复原
vi /etc/resolv.conf

# 在该文件添加下面的dns地址
nameserver 114.114.114.114
nameserver 114.114.115.115
nameserver 8.8.8.8
nameserver 8.8.8.4

4.3 Typecho 常见问题

4.3.1 Typecho 的样式不正确

Typecho 没有正确访问 css 文件导致样式不正确,这是因为typecho处于容器内,typecho认为自己接管了localhost,结果从容器外部访问容器内部时,还是使用的localhost,无法拿到正确的css资源地址,可以通过以下方法修正

# 修改config.inc.php文件
vi ~/typecho/web-data/config.inc.php

# 将以下内容添加到文件末尾
# 动态识别站点地址 (Site URL)
if (isset($_SERVER['HTTP_HOST'])) {
    $http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
    define('__TYPECHO_SITE_URL__', $http_type . $_SERVER['HTTP_HOST']);
}

4.3.2 Typecho 无法上传附件

这个大概率是上传目录的权限问题,需要修复 /usr/uploads 目录的权限设置

# 进入正在运行的 Web 容器
podman exec -it typecho-web id www-data
# 结果通常是:uid=33(www-data) gid=33(www-data) groups=33(www-data)

# 进入工作目录
cd ~/typecho/web-data

# 将整个 web-data 的所有权“放”给容器(推荐)
# 在 Podman 中,这通常需要使用 unshare 命令来处理映射权限
podman unshare chown -R 33:33 ~/typecho/web-data
# 这里的33:33是uid:gid,根据上面得到的数字进行

5 其他

如果你在容器中弄错了什么东西想重来,正确的处理方式是,停止容器->删除容器->根据实际决定是否删除对应的挂载卷。

1、对于上述案例来说,如果我需要拓展一个WordPress网站,那我是不是也要将WordPress加入对应的内置网络?

目前来说是的,将 NginxTypecho / WordPress 服务放入同一个内置网络是方便且必要的,但是让 Nginx 同时加入 n 个网络是不可取的,这会使得管理容器内部的网络变得非常复杂,也会使得 nginx.conf 这个配置文件非常难以维护。
常见的解决方案是使用专门的反向代理容器 Nginx Proxy Manager / Traefik,反向代理容器通过挂载容器的 Socket,自动发现其他容器,或者通过特定的“代理网络”互联,实现解耦。
针对大量的容器化服务来说,Traefik 会是更好的选择,Nginx 会陷入配置复杂容易出错的局限性,Traefik 目前的局限在于极端超高并发(百万级 QPS)的静态文件服务,理论性能略逊于高度调优的 Nginx

具体情况还需要具体分析,例如上述的案例比较简单,不涉及大量容器的情况下使用Nginx特别合适。

2、对于上述案例来说,如果我要增设一些简单的网站而不是容器服务,需要额外挂载目录吗?

要,如果你没有类似1Panel的管理面板,增设起来会比较麻烦,大致过程可能是绑定一个www目录,然后每个域名对应www下的一个目录,通过反向代理区分服务。

猜你喜欢😋