分类 Docker 下的文章

部署过才发现,使用Docker部署PHP网站,没想象中的简单。顺便总结一下使用Docker Compose部署Typecho的经验。

1. 概述

重点注意:

  • 一般PHP网站的Docker镜像,只提供了网站的PHP源码和php-fpm服务,在生产环境不能单独部署。
  • 生产环境需要前端搭配Nginx或Apache等接收请求,再交给php-fpm服务处理。一般还需要后端连上MySQL、MariaDB、PostgreSQL等数据库。
  • 建议使用Docker Compose部署。比Docker run更好管理,比K8S更省资源。

2. 建立网络

建议先建立一个Docker网络,把相关服务部署到该网络。

  • 所有服务使用service名称作为host名称,配置相互访问时不用填IP。
  • 同一网络内的服务,不用在宿主机暴露端口,各个服务之间也相互访问。一般只需暴露Nginx的端口。

建立Docker网络的参考命令如下。其中网络名称为docker-net

docker network create --driver "bridge" docker-net

3. Nginx服务

Nginx的docker-compose.yaml,路径在/opt/docker_deploy/nginx/,内容如下:

services:
  nginx:
    image: nginx:1.27.0-alpine
    restart: always
    ports:
      - 80:80
      - 443:443
    environment:
      - NGINX_PORT=80
    volumes:
      - ./etc/nginx.conf/:/etc/nginx/nginx.conf:ro
      - ./etc/conf.d:/etc/nginx/conf.d
      - /etc/letsencrypt/archive:/etc/nginx/cert:ro
      - ./logs:/var/log/nginx
      - ./www:/var/www
      - /opt/docker_deploy/typecho/app:/var/www/typecho:ro
    networks:
      - docker-net

networks:
  docker-net:
    external: true

Type的Nginx配置文件,在/opt/docker_deploy/nginx/etc/conf.d/typecho.conf,内容如下:

server {
  server_name blog.xxxx.com;
  listen 80;

  rewrite ^(.*)$  https://$host$1 permanent;
}

server {
  server_name blog.xxxx.com;
  listen 443 ssl;
  http2 on;
  index index.php index.htm index.html;
  root /var/www/typecho;

  ssl_certificate /etc/nginx/cert/blog.xxxx.com/fullchain8.pem;
  ssl_certificate_key /etc/nginx/cert/blog.xxxx.com/privkey8.pem;

  add_header X-Frame-Options SAMEORIGIN;
  add_header Content-Security-Policy "frame-ancestors 'self';";

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    include fastcgi_params;
    #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param SCRIPT_FILENAME /app$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_pass typecho:9000;
    fastcgi_buffers 8 16k;
    fastcgi_buffer_size 32k;
  }

  location / {
    try_files $uri $uri/ /index.php$is_args$query_string;
  }
}

4. Typecho

部署要点:

  • Typecho的Docker部署说明,可参考:https://github.com/typecho/Dockerfile
  • 由于服务器上还有其它网站或系统,需要Nginx做反向代理,所以Typecho的Docker镜像选择了fpm版本。
  • 需要把PHP源码文件映射出来,提供给Nginx访问。

Typecho的docker-compose.yaml,路径在/opt/docker_deploy/typecho/,内容如下:

ervices:
  typecho:
    image: joyqi/typecho:1.2.1-php8.0-fpm-alpine
    restart: always
    #ports:
    #  - "9000:9000"
    environment:
      - "TYPECHO_SITE_URL=https://blog.xxxx.com"
      - "TYPECHO_DB_ADAPTER=Pdo_Mysql"
      - "TYPECHO_DB_HOST=mariadb"
      - "TYPECHO_DB_PORT=3306"
      - "TYPECHO_DB_USER=typecho"
      - "TYPECHO_DB_PASSWORD=123456"
      - "TYPECHO_DB_DATABASE=typecho"
    volumes:
      - ./app:/app
    networks:
      - docker-net

networks:
  docker-net:
    external: true

要注意,如果Nginx的Typecho没有开启SSL,但需要全局https访问Typecho,可以在配置文件config.inc.php添加以下配置:

/** 全站开启https */
define('__TYPECHO_SECURE__', true);

5. MariaDB

MariaDB的docker-compose.yaml,路径在/opt/docker_deploy/mariadb/,内容如下:

ervices:
  mariadb:
    image: mariadb:11.4.2
    restart: always
    #ports:
    #  - 3306:3306
    volumes:
      - ./dbdata:/var/lib/mysql
    environment:
      MARIADB_ROOT_PASSWORD: root123456
      #MYSQL_USER: ${DB_USER}
      #MYSQL_PASSWORD: ${DB_PASSWORD}
      #MYSQL_DATABASE: ${DB_DATABASE}
    networks:
      - docker-net

networks:
  docker-net:
    external: true

1. 概述

  • 通过部署Docker Registry,可实现自建Docker私有仓库。
  • 自建Docker私有仓库,主要是方便内部分发或部署项目。如果只有一个服务器,可以直接build镜像,且不建私有仓库。
  • 各大厂商有提供免费的私有仓库服务,可以不用自建。

官方相关文档:

2. 部署

  • 假设在IP为192.168.0.1的服务器上部署。
  • Docker Registry采用端口5000docker-registry-ui采用端口80
  • 建议使用Docker Compose方式进行部署。
  • 为了方便管理(主要是执行“删除”操作),同时部署了可视化操作的Web界面。
  • 由于是内部部署,Docker Registry没有配置用户管理(包括用户的身份验证、权限管理之类)。
  • Registry的所有配置项,均可通过环境变量设置。相关的完整配置项说明,详见:Configuring a registry

部署Registry的docker-compose.yaml文件,参考如下:

version: '3.8'

services:
  registry-server:
    image: registry:latest
    #container_name: registry-server
    restart: always
    environment:
      - REGISTRY_STORAGE_DELETE_ENABLED: true
  volumes:
    - ./registry:/var/lib/registry

  registry-ui:
    image: joxit/docker-registry-ui:latest
    #container_name: registry-ui
    restart: always
    ports:
      - 80:80
    environment:
      - SINGLE_REGISTRY=true
      - REGISTRY_TITLE=Docker Registry UI
      - DELETE_IMAGES=true 
      - SHOW_CONTENT_DIGEST=true 
      - NGINX_PROXY_PASS_URL=http://registry-server:5000
      - SHOW_CATALOG_NB_TAGS=true
      - CATALOG_MIN_BRANCHES=1
      - CATALOG_MAX_BRANCHES=1
      - TAGLIST_PAGE_SIZE=100
      - REGISTRY_SECURED=false
      - CATALOG_ELEMENTS_LIMIT=1000

3. 上传镜像

给镜像设置名称和tag,然后执行docker push上传。其中镜像名称需要以Registry地址(IP或域名)+端口作为前缀。

示例命令如下:

# 给镜像设置tag
docker tag <镜像名称>:<tag> 192.168.0.1:5000/<镜像名称>:<tag>

# 上传镜像
docker push 192.168.0.1:5000/<镜像名称>:<tag>

4. 下载镜像

4.1. 修改Docke配置文件。

一般Linux上,Docker服务的配置文件在/etc/docker/daemon.jsoninsecure-registries添加Docker Registry服务,例如:

{
    "insecure-registries":["192.168.0.1:5000"]
}

修改后需要使用配置生效。可重启本机Docker服务,或执行热更新操作。例如:

# 重启本机Docker服务
sudo systemctl restart docker

# Docker配置文件热更新
sudo kill -SIGHUP $(pidof dockerd)

4.2. 从自建私有仓库下载镜像

docker pull 192.168.0.10:5000/<镜像名称>:<tag>

5. 仓库管理

一般通过docker-registry-ui(即http://192.168.0.1/),对私有仓库的镜像进行查看、删除。

Docker Registry本身提供了接口,用于管理其存储的镜像。

计划分两部分总结一下Docker的学习。本文先从入门到应用开始,了解Docker并用上它。

0 总结:

本文推荐使用Docker Compose运行和管理容器。

1 概述

1.1 Docker是什么?

Docker是依赖Linux内核,通过建立应用的运行环境,实现虚拟化。其实现方式不是采用硬件级别的虚拟,而是“隔离”。就是通过隔离手段实现软件运行环境的沙箱化。

其底层依赖于Linux的chroot、namespace、cgroup等三种老技术,结合当前系统的Linux内核,建立另一个Linux运行环境,并运行指定软件。试过在Android系统部署Linux分发版(Android也是一个Linux发行版),就是使用了类似的技术。

要注意,Docker的目的不是提供一个完整的虚拟Linux,而是把运行环境和软件组合为服务,去运行和管理。例如Debian官方镜像默认没有提供“服务”(servcie)管理的功能,而是作为一个指定服务的一部分去运行。

参考资料:

1.2 为什么要用Docker?

  • 一次建立,到处运行。

    • 环境和软件一起打包,不用担心测试环境与正式环境的区别。
    • 对于Window平台可以使用WSL运行。
  • 优胜于传统虚拟机。

    • 轻,只虚拟环境。
    • 快,运行快。
    • 小,资源占用小,包括CPU、内存、硬盘等资源。
  • 管理简单。

    • 镜像作为模板,容器作为实例。
    • 多个容器可以关联组合。

例如,部署多个MySQL数据库,并配置不同的参数作对比,那么直接运行多个MySQL的Docker容器即可,其表现为运行在不同IP的多个MySQL服务。

1.3 基本概念

  1. 仓库(Repository)

  2. 镜像(Image)

    • 容器运行时所需的root文件系统(包括程序、库、资源、配置等文件),以及配置参数(包括匿名卷、环境变量、用户等)。
    • 可来理解为容器的模板。其不包含动态数据,构建后不可修改。
  3. 容器(Container)

    • 简单来说,跟面向对象程序设计中的“类”(镜像)和“实例”(容器)一样。
    • 容器包括容器配置、相关文件、相关进程。

1.4 怎样使用?

  1. 再次强调,Docker是建立应用的运行环境,不是虚拟机。所以不要在Docker容器里面跑服务,而是把整个Docker容器作为一个服务进行管理和使用。

    • 例如启动Dokcer容器时,执行一个启动脚本,运行所需软件。
  2. 一般建议把重要数据映射到容器外部的文件系统,避免删除或升级容器时同时删除相关文件,也能减少性能损耗。

    • 例如Docker部署MySQL,建议把数据目录映射出来。
  3. 升级容器时,先下载更新的镜像,再删除该容器,并使用相同的配置文件,建立同名的容器。

1.5 Docker Compose

为了更好地解决多个Docker容器协同部署、工作,诞生了“Docker Compose”。一开始使用“Python”实现的,但到了2.0,Docker官方使用Go重新实现,并以插件形式集成到docker-ce(其软件包名为docker-compose-plugin)。使用docker compose命令(代替1.0的docker-compose命令)进行Docker容器的编排。

Docker Compose另一个的优点是,借助YAML脚本和环境变量,可以轻松配置或修改Docker容器。相比之下,使用docker run命令部署的Docker容器,修改配置需要重启Docker服务(影响其它Docker容器),相当麻烦。所以建议使用Docker Compose的方式运行Docker容器,而且一般Docker会提供Docker Compose的示例配置文件。

1.6 Kubernetes

Kubernetes也称为K8s,是用于自动部署、扩缩和管理容器化应用程序的开源系统。它将组成应用程序的容器组合成逻辑单元(Pod),以便于管理和服务发现。这部分内容,计划另起一个文章再整理。

1.7 参考文档

2 安装部署

2.1 安装

官方安装教程如下:

针对Debian或Ubuntu,总结如下:

  • 推荐使用Docker官方APT仓库进行安装。一般先卸载通过系统仓库安装的自带Docker引擎。
  • 安装Docker CE版,即Community Edition(社区版,免费)。
  • 安装docker-compose插件:sudo apt install docker-compose-plugin

2.2 配置

Docker服务的配置,主要分为命令行参数和配置文档。

详见官方说明文档:

由于这里以SystemD服务的方式运行Docker服务,所以采用配置文件的方式进行配置。在采用SystemD的Linux发行版上(例如Ubuntu 16.04+、Debian 8+、CentOS 7等),对应配置文件为/etc/docker/daemon.json(如没有该文件,需要自行创建)。

修改并保存daemon.json后,一般无需重启Docker服务,发送reload信号即可。如下:

sudo systemctl daemon-reload
sudo systemctl reload docker.service

2.3 设置“仓库”镜像(mirror)网站

如果访问官方“仓库”比较慢,可以使用中国的镜像网站。在配置文件/etc/docker/daemon.json中加入:

{
  "registry-mirrors": ["https://hub-mirror.c.163.com/"]
}

其它可用的加速镜像可参考:

2.4 设置代理

这里是为了解决Docker不能访问第三方“仓库”的网址,导致不能拉取相关“镜像”文件。例如gcr.io/cadvisor/cadvisor.

在配置文件/etc/docker/daemon.json中加入:

{
  "proxies": {
    "http-proxy": "http://proxy.example.com:80",
    "https-proxy": "https://proxy.example.com:443",
    "no-proxy": "*.test.example.com,.example.org,.163.com,192.168.0.0/24,127.0. 0.0/8"
  }
}

其中,http-proxyhttps-proxy是设置相应的HTTP代理服务,no-proxy是设置不走代理的域名或IP。

2.5 设置权限阻隔

默认情况下,“容器”中的进程以root用户权限运行,与“宿主机”中的root是同一个用户。这就意味着一旦“容器”中的进程有了适当的机会,它就可以控制“宿主机”上的一切。

解决方法有两种,一种是使用非root用户运行容器,另一种是用户命名空间(user namespace)方式。

关于用户命名空间的设置,参考下文:

3 使用

3.1 运行“容器”

到“仓库”找所需“镜像”,下载“镜像”后运行对应的“容器”。直接执行“docker run”时,如果本地没有相关“镜像”,会自动在Docker官方“仓库”下载,然后创建“容器”并运行。

一般使用软件官方提交的“镜像”,若要使用第三方“镜像”,最好确认清楚是否安全。

下面以部署Debian容器为例:

# 搜索Debian“镜像”
docker search debian

# 查看“镜像”版本,最好是访问docker官网。例如Debian镜像的版本:https://registry.hub.docker.com/_/debian/tags

# 下载Debian“镜像”。格式是,“镜像”名称:版本名称。如果不填版本名称,默认是Latest。
docker image pull debian:stable

# 列出所有已下载的“镜像”,包括隐藏的。以下三个命令的效果是一样的。
docker image ls -a
docker image list -a
docker images -a

# 查看正在运行的“容器”。以下三个命令的效果是一样的,推荐第一个。
docker container ls
docker container ps
docker container list

# 查看所有“容器”,包括未运行。
docker container ls -a

# 创建并运行“容器”。名为“debian-test”,映射两个端口(本机50080->“容器”40080,本机51080->“容器”41080),并把本地“/opt/docker-files/debian-test/opt”目录映射到“容器”的“/opt”目录。
docker run -it --name debian-test -p 50080:40080 -p 51080:41080 -v /opt/docker-files/debian-test/opt:/opt debian:stable

# 查看“容器”运行日志,检查是否报错
docker logs debian-test

3.2 进入“容器”

进入“容器”。可以使用attachexec命令,但建议使用exec并运行一个新的Bash,避免退出时影响“容器”当前运行的程序。

  1. attach命令。适合“容器”正在运行具有命令行交互的程序,比如“Bash”。以下两个命令是一样的。
# 两个命令是一样的
docker attach debian-test
docker container attach debian-test

要注意,使用attach命令进入“容器”并输入“exit”进行退出时,就等于Bash程序结束执行,“容器”随之停止运行。解决方案:

  • 方案1,“docker attach”进入容器后,按Ctrl + P + Q退出。
  • 方案2,进入容器时,加入参数“--sig-proxy=false”,退出时使用“exit”命令而不停止容器。

    • 例如:docker attach --sig-proxy=false debian-test
  1. exec命令。打开一个具有命令行交互的程序,即进入“容器”。
# 两个命令是一样的,运行Bash并进入
docker exec -it debian-test /bin/bash
docker container exec -it debian-test /bin/bash

# 使用root用户运行Bash,并设置字符编码
docker exec -u root -it debian-test env LANG=C.UTF-8 /bin/bash

退出“容器”就等于退出当前运行的Bash,对“容器”正在运行的程序无影响。

3.4 管理“容器”

# 启动“容器”
docker container start debian-test

# 停止“容器”
docker container stop debian-test

# 重启“容器”
docker container restart debian-test

# 删除“容器”
docker container rm debian-test

3.5 复制“容器”

主要思路是,把指定“容器1”生成“镜像A”,再用“镜像A”创建“容器2”,实现把“容器1“复制为”容器2“。

注意:生成“镜像”时,“容器”外部的文件,不会打包进“镜像”文件。需要手工复制这些文件,并设置映射。

假设已创建“容器”:debian-test,需要复制为另一个“容器”:debian-test2。

# 把“容器” debian-test,生成“镜像”文件
docker export debian-test > /opt/docker_image_debian-test.tar

# 引入“镜像”,并命名为 debian-test:v1
docker import /opt/docker_image_debian-test.tar debian-test:v1

# 创建另一个“容器” debian-test2
docker run -itd --name debian-test2 -p 50081:40080 -p 51081:41080 -v /opt/docker-files/debian-teset2/opt:/opt debian-test:v1 /bin/bash

3.5 Dokcer图形化管理

Docker Desktop下载地址和安装介绍:

4 使用Docker Compose

Docker Compose是以“项目”(project)作为管理单元。一个项目,是由一个或多个“服务”(service,即“容器”)组合而成。

4.1 使用

把Compose配置文件存放到指定目录,进入该目录,并执行docker compose up,即可启动。

4.2 命令说明

格式:docker compose [可选项] 命令。“可选项”和“命令”的说明如下:

可选项说明
--ansi string控制何时打印 ANSI 控制字符。string的可选值为"never"、"always"、"auto",默认为"auto"。
--compatibility以向后兼容模式运行 compose。
--env-file stringArray指定备用环境文件。
-f, --file stringArrayCompose配置文件。
--parallel int控制最大并行度,-1 为无限制。默认-1。
--profile stringArray指定要启用的配置文件。
--project-directory string指定备用工作目录。默认:首先采用所配置的第一个Compose文件的路径。
-p, --project-name string项目名称。
命令说明
build构建或重新构建服务
config以规范格式粘贴、解析和呈现Compose配置文件
cp在服务容器和本地文件系统之间复制文件或文件夹
create为服务创建容器
down停止并移除容器、网络
events从容器接收实时事件
exec在正在运行的容器中执行命令
images列出创建的容器使用的镜像
kill强制停止服务容器
logs查看容器的输出
ls列出正在运行的Compose项目
pause暂停服务
port打印端口绑定的公共端口
ps列出容器
pull拉取服务镜像
push拉取服务镜像
restart重启服务容器
rm删除停止的服务容器
run在服务上运行一次性命令
start启动服务
stop停止服务
top显示正在运行的进程
unpause取消暂停服务
up创建并启动容器
version显示 Docker Compose 版本信息