Docker 是什么

特点

使用容器化技术,给应用程序封装独立的运行环境。Docker 容器与虚拟机之间的差别就是,docker 容器共用一个系统,比虚拟机更小,启动更快。解决了因为环境不同无法运行的问题,减少了安装部署应用的时间。

镜像

镜像是一个只读的模板,用于创建 Docker 容器。 它包含了运行应用程序所需的一切:代码、运行时库、环境变量和配置文件。相当于软件安装包。

安装

Docker

删除旧版本

sudo apt-get remove docker docker-engine docker.io containerd runc

设置仓库

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

安装 docker engine

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

验证

sudo docker run hello-world

新版本 docker 自带 docker compose

容器

容器是镜像的运行实例。 它是根据镜像创建的应用和环境的集合体

镜像仓库

Docker Hub:是 Docker 官方提供的最大、最常用的公共仓库,包含了海量的官方和第三方镜像。当执行 docker pull 时,Docker 会从配置的仓库中拉取镜像;执行 docker push 时,则会将你本地的镜像推送到仓库中。

Docker 命令

命令含义作用
docker images镜像列出本地所有镜像
docker pull拉取docker pull nginx 拉取 nginx 镜像
docker push推送将本地镜像推送到远程仓库
docker rmiRemove image (删除镜像)删除镜像
docker psprocess status (进程状态)查看正在运行的容器, -a 可以列出所有状态的容器
docker stop停止停止一个或者多个容器
docker start启动启动一个或者多个容器
docker rmRemove (删除)删除容器,-f (force)可以强制删除正在运行的容器
docker kill杀死强制关闭一个容器
docker pause暂停暂停容器内所有进程
docker execExecute(执行)进入容器内部执行命令,常用docker exec -it <容器ID> /bin/bash
docker logs日志查看日志,-f (follow) 实时跟踪日志
docker volumels 查看卷列表,rm <卷名>删除卷
docker inspect检查docker inspect <容器名称或ID>

Docker run

Docker pull 和 docker run 可以一步到位使用 docker run 命令。

docker run \
    -d \
    --name my-postgres-db \
    -p 127.0.0.1:5432:5432 \
    -v postgres_data:/var/lib/postgresql/data \
    -e POSTGRES_USER=myuser \
    -e POSTGRES_PASSWORD=mysecretpassword \
    -e PGDATA=/var/lib/postgresql/data/pgdata \
    --network my-custom-net \
    --restart unless-stopped \
    --memory="1g" \
    --cpus="0.5" \
  • -d 后台运行
  • --name 给容器命名
  • -p 端口映射
  • -v 挂载 postgres_data 是命名卷挂载,/var/lib/postgresql/data 是PostgreSQL镜像官方指定的、用于存储数据库文件的目录。
  • -e 环境变量
  • --network 创建并连接一个自定义网络。连接到同一个自定义网络 (my-custom-net) 的容器,可以通过容器名作为DNS主机名直接互相通信。

Docker compose

多应用编排,简化了 docker run。

# 定义所有服务
services:
    # Caddy 服务 (Web 服务器)
    caddy:
        restart: unless-stopped          # 设置重启策略
        image: caddy:latest              # 使用的镜像
        container_name: server-caddy     # 容器名称
        ports:
            - "443:443"                  # 端口映射,将宿主机的443端口映射到容器的443
        volumes:
            - "./data:/www/wwwroot"      # 挂载网站根目录
            - "./config:/etc/caddy"      # 挂载Caddy配置文件目录
            - "./logs:/www/wwwlogs"      # 挂载日志目录
        networks:
            - typecho-network            # 连接到指定网络

    # PHP 服务
    php:
        restart: unless-stopped
        container_name: server-php
        image: johnsonran/server-php:latest
        volumes:
            - "./data:/www/wwwroot"      # 与Caddy共享网站文件
        networks:
            - typecho-network

    # MariaDB 服务 (数据库)
    mariadb:
        restart: unless-stopped
        image: mariadb:latest
        container_name: server-mariadb
        ports:
            - "127.0.0.1:3306:3306"      # 仅限本机访问数据库
        environment:
            - MARIADB_ROOT_PASSWORD=PMA_PASSWD # 设置数据库root密码
        networks:
            - typecho-network

    # phpMyAdmin 服务 (数据库Web管理工具)
    pma:
        restart: unless-stopped
        image: phpmyadmin:latest
        container_name: server-pma
        ports:
            - "8283:80"                  # 将容器的80端口映射到宿主机的8283
        environment:
            - PMA_HOST=server-mariadb    # 告诉pma,数据库主机是名为'server-mariadb'的容器
            - PMA_PORT=3306              # 数据库端口
        networks:
            - typecho-network

# 定义网络
networks:
    typecho-network:
        driver: bridge                   # 使用桥接模式网络

将上述内容保存为 docker compose.yml 文件后,在该文件所在目录执行以下命令:

  • docker compose up -d: 启动所有服务并在后台运行。
  • docker compose stop <服务名> : 暂停容器。
  • docker compose start <服务名>: 启动容器。
  • docker compose restart <服务名>: 启动容器。常用于修改配置之后的重启。
  • docker compose down: 删除所有相关的容器和网络。
  • docker compose ps: 查看所有服务的当前状态。
  • docker compose logs -f <服务名>: 实时查看指定服务的日志。

网络

  • Bridge (桥接模式): 这是 Docker 的默认网络模式。Docker 会创建一个虚拟网桥(如 docker0),所有此模式下的容器都会连接到这个网桥上,并被分配一个独立的 IP 地址。容器之间可以通过 IP 或容器名(如果连接在同一个自定义桥接网络中)通信。
  • Host (主机模式): 容器将不会拥有自己独立的网络命名空间,而是直接共享宿主机的网络。性能最高,但牺牲了隔离性。
  • Overlay (覆盖网络): 用于连接运行在不同宿主机上的 Docker 容器,是实现多机容器集群通信(如 Docker Swarm)的关键。
  • None (无网络模式): 容器拥有自己的网络命名空间,但不进行任何网络配置。适合对网络安全要求极高或不需要联网的场景。

如果不同编排下的 docker 容器需要用到同一个 web 服务器反代,可以这样写:

services:
  onenav:
    image: helloz/onenav
    container_name: onenav
    volumes:
      - /data/onenav:/data/wwwroot/default/data
    restart: always
    expose: # 将容器的80端口暴露给同一docker网络中的其他服务
      - "80"
    networks: 
      - onenav # 将服务连接到下面的网络别名
      
networks:
  onenav:
    external: true
    name: typecho_typecho-network # 实际外部网络名称

  

这个 docker compose 并没有创建一个 onenav 的网络,external:true 的意思是不用创建 onenav 网络,而是将容器连接到 name:typecho 的网络。当外部 typpecho 这个网络不存在,这个 docker compose 会启动失败。

解决 ufw 和 docker 的问题

Docker 的开放的端口默认会绕过 ufw,如果像让 docker 端口受 ufw 控制可以有以下设置。

修改 UFW 的配置文件 

/etc/ufw/after.rules

在最后添加上如下规则:

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER

然后重启 UFW

sudo systemctl restart ufw

现在外部就已经无法访问 Docker 发布出来的任何端口了,但是容器内部以及私有网络地址上可以正常互相访问,而且容器也可以正常访问外部的网络。可能由于某些未知原因,重启 UFW 之后规则也无法生效,请重启服务器。

如果还是无法生效,尝试修改:

/etc/ufw/before.rules

如果希望允许外部网络访问 Docker 容器提供的服务,比如有一个容器的服务端口是 80。那就可以用以下命令来允许外部网络访问这个服务:

ufw route allow proto tcp from any to any port 80

如果有多个容器的服务端口为 80,但只希望外部网络访问某个特定的容器。比如该容器的私有地址为 172.17.0.2,就用类似下面的命令:

ufw route deny from 172.17.0.9 to any
docker ps
docker inspect [您的容器名或ID] | grep "IPAddress"

参考

  1. Docker Docs
  2. 🎉 Docker 简介和安装 - Docker 快速入门 - 易文档
  3. 40分钟的Docker实战攻略,一期视频精通Docker\_哔哩哔哩\_bilibili
  4. GitHub - chaifeng/ufw-docker: To fix the Docker and UFW security flaw without disabling iptables