运维技能学习

网络工程 · 2023-07-10

作为一个网络工程专业的毕业生,网络工程专业的相关知识还是要掌握一点的。

一、认识云计算

1. 什么是云计算

通过互联网提供的计算服务,包括服务器、存储、数据库、应用程序等,采用按需付费的计费模式,用户无需购置物理硬件,前期投入低、无需维护机房、扩展性强

2. 云计算的类型

  • 公有云:由云计算供应商拥有和运营,通过互联网提供计算资源,按需计费
  • 私有云:企业和组织搭建的私有云服务,硬件、软件、基础结构都是私有的,安全性更强
  • 混合云:同时使用公有云和私有云,允许组织将敏感数据保留在私有云中(安全性),同时使用公有云来部署服务(降低成本)

3. 云计算的服务模式

  • IaaS:基础设施即服务,例如服务器、网络、存储、数据中心等基础设施
  • PaaS:平台即服务,提供硬件和软件服务,提供现成的环境便于用户快速构建应用
  • SaaS:软件即服务,提供软件服务,用户直接使用部署好的软件

二、Linux 基本命令

命令格式:程序 + 参数 + 对象

1. Linux 文件目录结构及管理

  • cd​​:切换目录
  • touch​​:新建文件
  • rm​​:删除文件,参数:r 表示递归,f 表示强制删除
  • ls​​:查看目录内容,参数:l 表示列出详细信息,格式如下图

    image-20230211231159-r4etzdf.png

  • pwd​:查看当前目录
  • mkdir​:创建目录
  • cat​:以文本形式输出文件内容
  • Linux 采用树形目录结构,有相对路径与绝对路径两种定位方式

2. Linux 文件权限管理

  • chmod​:修改文件权限的命令,权限用数字来表示,其中 4 代表读取权限(read);2 代表写入权限(write);1 代表可执行(execute)。
  • 文件权限分为文件所有者的权限、文件所有者所在的用户组内其它用户的权限、其它组内用户的权限。
  • 综上所述,一个文件最高可以设置的权限是 777,即任何用户都可以读、写、执行这个文件。允许所有权限的命令就是chmod 777 文件路径
  • 可以使用chmod +x 文件路径命令快速给文件添加可执行权限,这个命令会经常用到。

3. Linux 用户、组权限管理

  • useradd​:新建用户
  • passwd​:修改用户密码
  • su​:切换用户

4. Linux 资源管理

  • top​​:实时查看当前系统运行的进程和占用率
  • ps​:单次输出进程信息,参数:e 查看所有进程,f 查看完整信息(包括用户、PPID、运行时间等) 例如:ps -ef | grep 程序名称
  • netstat​:查看网络连接信息,属于 net-tools 软件包的一部分。可以查看网络协议、地址、端口等。例如:netstat -tunlp | grep 程序名称或端口号​。

    参数说明:

    -t 查看 tcp 协议;

    -u 查看 udp 协议;

    -n 端口以数字形式显示,否则直接显示服务名称;

    -l 显示服务监听的端口;

    -p 显示占用端口的进程。

  • ifconfig​:查看网卡信息,属于 net-tools 软件包的一部分。快速输出 IP 地址:ifconfig 网卡名称 | awk 'NR==2{print $2}'​(打印 ifconfig 输出结果中第二行第二列的内容)
  • firewall-cmd:检查并配置防火墙。防火墙会根据不同区域(zone)对不同来源的网络流量进行处理。常用命令:
命令用途
firewall-cmd --state​​查看防火墙状态
firewall-cmd --list-services​​查看当前开放了哪些服务对应的端口
firewall-cmd --get-services​​查看还有哪些服务可以开放对应的端口
firewall-cmd --get-active-zones​​查看当前各个网卡的区域
firewall-cmd --zone=public --list-ports​​查看 public 区域打开的端口
firewall-cmd --reload​​重新加载防火墙,用于使新的规则生效
firewall-cmd --add-service=服务名称​​开放对应服务的端口,重启后失效
firewall-cmd --permanent --add-service=服务名称​​开放对应服务的端口,永久生效
firewall-cmd --zone=public --add-port=端口号/协议 --permanent​​在 public 区域下永久开放指定端口(与上面的服务是冲突的)
firewall-cmd --zone=public --query-port=80/tcp​​查看 public 区域下 80 端口的tcp 流量规则
firewall-cmd --zone= public --remove-port=80/tcp --permanent​​在 public 区域下永久关闭访问 80端口的 tcp 流量

5. 补充:vim 常用快捷键

i:进入编辑模式

I:进入编辑模式并将光标放在行首位置

a:进入编辑模式并将光标往后移动一位

A:进入编辑模式并将光标放在行尾位置

o:进入编辑模式,在光标所在行的下方新增一行,并将光标移到新行的行首

O:进入编辑模式,在光标所在行的上方新增一行,并将光标移到新行的行首

x:删除光标所在处的字符

X:删除光标所在处的前一个字符

0:跳转到光标所在行的行首

$:跳转到光标所在行的行尾

gg:跳转到文件开头

G:跳转到文件最后一行的开头

dd:删除光标所在行数据,并将其写入到缓存区

yy:复制光标所在行数据到缓存区

p:将缓存区内容粘贴到光标位置之后

P:将缓存区内容粘贴到光标位置之前

u:撤销最近的修改

U:撤销对光标所在行做的所有修改

r:替换光标所在位置的字符

R:替换从光标所在位置起的字符,并进入编辑模式

.:重复上一次的修改

\>>:向右缩进本行

<<:向左缩进本行

Ctrl+D:将光标向下翻半屏

Ctrl+U:将光标向上翻半屏

:%d:清空文件内容

:set nu/nonu:显示/隐藏行号

:set ai/noai:开启/关闭自动缩进

:/string:在文件中搜索 string

:%s/old/new/g:将文件中所有的 old 都替换为 new


三、软件管理

注意:该部分内容适用于 CentOS 7,其它的 Linux 发行版由于软件包管理器不同,命令也会不同。

1. 配置软件源

  • 可以下载现成的配置文件替换,也可以手动编辑
  • 下载文件可以用 wget​ 比较简单,也可以用 curl
  • yum 可以探测延迟最低的镜像服务器,但是延迟低不意味着下载速度快
  • 替换 yum 镜像源的步骤:

    • 备份旧的配置文件:mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
    • 下载新的配置文件并替换:wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo​​
    • 执行 yum clean all​ 清除缓存
    • 执行 yum makecache​ 重新生成缓存
  • 安装第三方维护的 yum 软件源(部分软件包官方源没有提供):

    • 执行 sudo yum install 软件源名称​(例如epel-release)
    • 执行 yum update​ 更新软件

2. 软件管理

  • yum 安装软件的命令:yum install 软件包名称 -y
  • 查看已安装的软件包:rpm -qa,可以结合 grep 过滤想要查找的软件包。
  • yum 卸载软件的命令:yum remove 软件包名称
  • 启动软件:systemctl start 服务名称
  • 查看状态:systemctl status 服务名称
  • 关闭软件:systemctl stop 服务名称
  • 设置自启动:systemctl enable 服务名称
  • 禁止自启动:systemctl disable 服务名称

四、网站部署

1. 查看 web 服务器信息

curl -I 域名或IP

2. 修改 nginx 首页内容

  • 查看 nginx 的首页路径:rpm -ql nginx | grep "index"
  • 修改 index.html 文件内容即可

3. 安装 LAMP 框架

  • LAMP 即 Linux + Apache(nginx)+ MySQL(MariaDB)+ PHP(后端应用)的缩写,是一套非常流行的开源软件包组合。
  • 如果是 CentOS,需要先禁用 SELinux:修改 /etc/selinux/config​​ 文件,将SELINUX=disabled,之后重启系统
  • 安装 Apache、MariaDB、PHP:yum install httpd mariadb-server php php-fpm php-mysql -y​​
  • 记得给 httpd、php-fpm、mariadb 设置自启动(systemctl enable​​)
  • 让 httpd 支持 php 的脚本:编辑 /etc/httpd/conf/httpd.conf​​,查找 DocumentRoot​​ 关键字,修改内容如下:

    DocumentRoot "/var/www/html"
        TypesConfig /etc/mime.types
        AddType applications/x-httpd-php .php
        AddType applications/x-httpd-php-source .phps
        DirectoryIndex index.php index.html
  • /var/html/www/html​​ 目录的权限改为 775
  • /var/html/www​​ 目录下编写 index.php,内容如下:

    <meta charset=utf8>
    This is a new homepage.
    <?php
        phpinfo();
    ?>
  • 重启 httpd 服务,用浏览器访问首页检查是否能正常显示 PHP 环境信息

4. 部署 Discuz!

  • 先安装并配置好 Apache、MariaDB、PHP
  • 使用 yum​ 安装 unzip
  • 下载 Discuz 安装包并使用 unzip​ 解压
  • 将 upload 目录下的内容移到 httpd 的网站根目录(位置可以从 httpd 的配置文件 /etc/httpd/conf/httpd.conf​ 中找到)
  • 修改网站根目录及其子目录的权限为777(chmod -R​)
  • 使用浏览器访问安装页面(IP 地址/install)

5. 编译 nginx 源码并安装

  • 下载对应平台的 nginx 源代码(https://nginx.org/en/download.html
  • 配置编译环境(gcc、pcre、pcre-devel、zlib、zlib-devel、openssl、openssl-devel)
  • 配置编译参数(安装位置、配置文件位置、日志文件位置、临时文件位置、要安装的模块等):./configure --prefix=安装位置 --error-log-path=错误日志文件位置 --with-http_ssl_module(SSL 模块,用于支持 https) --with-http_stub_status_module(监控模块)
  • 编译并安装:make && make install
  • 安装完成后,进入「安装位置/sbin」目录下启动 nginx
  • nginx -s stop​ 关闭nginx
  • nginx -s reload​ 重新加载
  • nginx -t​ 检查配置文件

6. nginx 部署静态站点

  • 修改位于 nginx 安装目录下 conf 目录的 nginx.conf​ 文件
  • nginx.conf​ 文件内,一个 server 文本块代表一个网站,location 文本块内的是网页文件的目录,root 表示文件所在目录,index 表示首页文件名称

    image-20230212001749-qfo7774.png

7. nginx 基于端口的虚拟主机

  • nginx 可以基于不同端口在同一个 nginx 实例中部署不同的网站
  • 修改 nginx.conf​,在其中新增一个 server 文本块,并修改监听端口、服务器名称、文件路径
  • 在防火墙中开放对应的端口并重新加载防火墙规则
  • 重新加载 nginx,并用浏览器访问网站

8. nginx 日志

  • nginx 能够记录用户的每一次访问请求
  • 目的:掌握服务器动态信息,可以用来排障、提高安全性;也可以用于监测并分析用户行为
  • 修改 nginx.conf​,在 server 文本块中添加「access_log 日志文件路径 main;​」,同时要把「log_format」对应的注释符号删除,之后保存文件并重载 nginx
  • 之后可以用 tail -f 日志文件路径​ ​来实时查看日志

    image-20230212002520-n8km5ya.png

9. nginx 反向代理

  • nginx 可以将用户的访问请求转发给目的地服务器,再将目的地服务器返回的数据转发给用户
  • 目的:原始资源服务器不需要暴露在互联网上,起到保护服务器的作用
  • 修改 nginx.conf​,将「server」文本块内的「location」文本块内容改为:proxy_pass 目的服务器地址;

    image-20230212002612-ld7kq30.png

  • 之后保存文件并重载 nginx 即可

五、MariaDB 数据库

MariaDB 是一个 MySQL 的开源分支,在 MySQL 成为了甲骨文旗下的产品后,开源社区对于甲骨文是否会继续对 MySQL 的免费版本提供支持有所担忧,所以 MariaDB 应运而生。

MariaDB 和 MySQL 都属于关系型数据库,由于性能高、成本低、可靠性好而一度成为最流行的数据库。

本文将会介绍 MariaDB 数据库的一些基本操作。

1. 安装和基本配置

  • 安装 MariaDB 非常简单,在 Linux 下只需要通过包管理器即可安装:yum install mariadb-server -y​​​
  • (可选)安装完成后,建议执行一次 mysql_secure_installation​​ 命令,设置 root 用户密码(此 root 非彼 root,这个章节里提及的 root 是数据库的管理员用户)、是否保留测试数据库、是否允许 root 用户远程登录等。
  • 安装完成后需要启动 MariaDB 服务,如果要顺便设置开机自启的话:systemctl enable --now mariadb
  • 启动服务后,运行 mysql -uroot -p​ 之后输入密码即可登录,如果没有设置 root 用户的密码,那在输入密码的步骤直接按回车即可。

    image-20230212202927-4ymq18c.png

    登录成功后,可以随时执行 exit​​​ 命令退出,如上图所示。

2. 创建一个数据库和表

在关系型数据库中,数据会按照数据库→表→记录的递进关系来存储。所以我们需要先拥有数据库和表,才能进行记录的编辑,也就是常说的「增删改查」。

默认情况下,MariaDB 会提供一个用于测试的数据库,但是我们通常不会用到它,而是另行创建自己所需的数据库。

  • 创建一个数据库:create database <数据库名称> character set utf8 collate utf8_bin;

    其中「character set」设定的是数据库内容的编码方式;而「collate」是设定校对方式,「utf8_bin」会以二进制方式来校对,也就是数据库的内容会区分大小写,与之相对的还有不区分大小写的「uft-8_general_ci」可供选择。

    注意:MariaDB 使用的是一种叫 SQL 的编程语言,每条语句结尾都要有一个英文的分号。而且所有的名称(例如,数据库,表,字段)都是区分大小写的。

    创建成功后,MariaDB 会返回「Query OK」的消息,如下图所示:

    image-20230213005455-zv0ur27.png

  • 之后就可以执行 show databases;​ 命令来查看当前有哪些数据库:

    image-20230213005749-ycg9rv4.png

  • 我们需要进入数据库才能创建表格,所以此处需要执行 use <数据库名称>;​ 命令。如果我们需要在多个数据库间切换,不需要退出当前的数据库,使用 use <数据库名称>;​ 命令即可切换数据库。
  • 进入数据库后,可以先看看这个数据库里有什么:show tables;

    image-20230213011503-3fqonbr.png

    可以看到是一个空的集合,现在就可以创建一个表了。

    表中的数据结构类似于 Excel 表格,分为列和行,列(field)是字段,行(row)是记录。

    -- 创建一个名为 student 的表
    create table student(
    -- 表的第一个字段叫 sno,它的格式是字符型,可以容纳 10 个字符,并且它是这个表的主键
    sno char(10) primary key,
    -- 表的第二个字段叫 sname,也是字符型,能容纳 8 个字符,not null 表示这个字段的值不允许为空
    sname char(8) not null,
    classnumber char(8) not null,
    -- 第四个字段有一个 default 属性,说明这个字段有默认值,如果在插入记录的时候没有设定这个字段的值,那就会用默认值代替
    ssex char(2) default '女' not null
    );

    一个字段的默认值有三种:DEFAULT NULL(允许空值)、NOT NULL(不允许空值)和 DEFAULT '默认值'。

    如果输出结果为 Query OK, 0 rows affected​ ,说明表就创建成功了。

  • 创建一个表后,可以使用 desc <表名>;​ 命令查询表的结构:

    image-20230213013950-241orbu.png

    也可以用 show create table <表名>;​ 语句来查询创建这个表的 SQL 语句:

    image-20230214165035-1cq8kb3.png

3. 修改字段的属性

在创建一个表后,我们可能需要对这个表的某些字段进行修改,或者增加新的字段。

  • 修改字段的数据类型:如果创建表的时候选择的数据类型满足不了需求,就可以对数据类型进行修改。

    SQL 语句:alter table <表名> modify <字段名称> <新的数据类型(数据长度)> <新的默认值> <新的注释>;

    摘自:https://www.w3cschool.cn/mariadb/mariadb_data_types.html

    MariaDB数据类型可以分为数字,日期和时间以及字符串值。

    数字数据类型

    MariaDB支持的数字数据类型如下 -

    • TINYINT - 此数据类型表示落入-128到127的有符号范围内的小整数,以及0到255的无符号范围。
    • BOOLEAN - 此数据类型将值0与“false”相关联,值1与“true”相关联。
    • SMALLINT - 此数据类型表示-32768到32768的有符号范围内的整数,以及0到65535的无符号范围。
    • MEDIUMINT - 此数据类型表示有符号范围-8388608到8388607中的整数,无符号范围0到16777215。
    • INT(也为INTEGER) - 此数据类型表示正常大小的整数。当标记为unsigned时,范围跨越0到4294967295.当有符号(默认设置)时,范围跨越-2147483648到2147483647.当列设置为ZEROFILL(无符号状态)时,其所有值都由零添加INT值中的M个数字。
    • BIGINT - 此数据类型表示有符号范围9223372036854775808到9223372036854775807内的整数,无符号范围0到18446744073709551615。
    • DECIMALDEC,NUMERIC,FIXED) - 该数据类型表示精确的定点数,M指定其数字,D指定小数后的数字。 M值不添加“ - ”或小数点。如果D设置为0,则不会出现小数或小数部分,并且该值将舍入为最接近的DECIMAL INSERT。最大允许位数为65,小数位数的最大值为30.默认值M的默认值为10,省略时D为0。
    • FLOAT - 此数据类型表示值0的小的浮点数或以下范围内的数字 -

      • -3.402823466E + 38至-1.175494351E-38
      • 1.175494351E-38至3.402823466E + 38
    • DOUBLE(也是REAL和DOUBLE PRECISION) - 此数据类型表示值0的正常大小的浮点数,或以下范围内的值 -

      • -1.7976931348623157E + 308至-2.2250738585072014E-308
      • 2.2250738585072014E-308至1.7976931348623157E + 308
    • BIT - 此数据类型表示位字段,M指定每个值的位数。省略M时,默认值为1.位值可以通过“b'[value]'”应用,其中值表示0和1中的位值。零填充从左边自动发生全长;例如,“10”变为“0010”。

    日期和时间数据类型

    MariaDB支持的日期和时间数据类型如下 -

    • DATE - 此数据类型表示日期范围“1000-01-01”到“9999-12-31”,并使用“YYYY-MM-DD”日期格式。
    • TIME - 此数据类型表示“-838:59:59.999999”到“838:59:59.999999”的时间范围。
    • DATETIME - 此数据类型表示范围“1000-01-01 00:00:00.000000”至“9999-12-31 23:59:59.999999”。它使用“YYYY-MM-DD HH:MM:SS”格式 。
    • TIMESTAMP - 此数据类型表示“YYYY-MM-DD HH:MM:DD”格式的时间戳。 它主要用于详细描述数据库修改的时间,例如插入或更新。
    • YEAR - 此数据类型表示4位数格式的年份。 四位数格式允许在1901到2155和0000范围内的值。

    字符串数据类型

    MariaDB支持的字符串类型值如下 -

    • String literals - 此数据类型表示用引号括起来的字符序列。
    • CHAR - 此数据类型表示包含指定长度的空格的右侧带有固定长度的字符串。 M表示字符的列长度,取值范围为0〜255,缺省值为1。
    • VARCHAR - 此数据类型表示一个可变长度字符串,M范围(最大列长度)为0到65535。
    • BINARY - 此数据类型表示二进制字节字符串,M为列长度(以字节为单位)。
    • VARBINARY - 此数据类型表示可变长度的二进制字节字符串,M为列长度。
    • TINYBLOB - 此数据类型表示最大长度为255(28 - 1)个字节的blob列。在存储中,每个都使用一个字节长度的前缀,表示值中的字节数量。
    • BLOB - 此数据类型表示最大长度为65,535(216 - 1)个字节的blob列。在存储中,每个都使用两字节长度的前缀,表示值中的字节数量。
    • MEDIUMBLOB - 此数据类型表示最大长度为16,777,215(224 - 1)个字节的blob列。在存储中,每个都使用一个三字节长度前缀,表示值中的字节数量。
    • LONGBLOB - 此数据类型表示最大长度为4,294,967,295(232 - 1)个字节的blob列。在存储中,每个使用四字节长度的前缀,表示值中的字节数量。
    • TINYTEXT - 此数据类型表示最大长度为255(28 - 1)个字符的文本列。在存储中,每个都使用一个字节长度的前缀,表示值中的字节数量。
    • TEXT - 此数据类型表示最大长度为65,535(216 - 1)个字符的文本列。在存储中,每个都使用两字节长度的前缀,表示值中的字节数量。
    • MEDIUMTEXT - 此数据类型表示最大长度为16,777,215(224 - 1)个字符的文本列。在存储中,每个都使用三字节长度前缀,表示值中的字节数量。
    • LONGTEXT - 此数据类型表示最大长度为4,294,967,295或4GB(232 - 1)个字符的文本列。在存储中,每个使用四字节长度的前缀,表示值中的字节数量。
    • ENUM - 此数据类型表示一个列表中只有一个值的字符串对象。
    • SET - 此数据类型表示一个列表中具有零个或多个值的字符串对象,最多包含64个成员。 SET值在内部作为整数值存在。
  • 修改字段名称:ALTER TABLE <表名> CHANGE <旧字段名> <新字段名> <新数据类型>;

    是的,change​ 关键字也是能修改字段的数据类型、数据长度和注释的。

  • 新增一个字段:alter table <表名> add <新字段名> <新字段的数据类型> after <已有字段名>;

    使用 after 关键字,说明是在已有字段名的后面新增字段。

    注意:没有 before 的用法,如果新字段要位于表中的第一位,那就把「after 已有字段名」改为「first」即可。

  • 删除一个字段:ALTER TABLE <表名> DROP <字段名>;

4. 插入和修改记录

  • 插入一条记录:insert into <表名>(<要插入的字段>) values(<每个字段的值>);

    可以同时插入多条记录:INSERT INTO products VALUES (1, “first row”), (2, “second row”);

    甚至将 A 表的查询结果插入到 B 表中:INSERT INTO B SELECT * FROM A WHERE status = 'available';

  • 修改一条记录:update <表名> set <字段>=<值> where <匹配字段>=<匹配的值>;

    这里出现了一个新的关键字where​,它是用来定位一条或一些记录的。它可以使用 AND 和 OR 运算符来指定多个条件,也可以使用 =、!=、>、<、>=、<= 这些运算符来确定查询的范围。

5. 选择和删除记录

  • 选择记录的语法:SELECT field, field2,... FROM table_name, table_name2,... WHERE...

    SELECT语句提供了多个选项来指定使用的表:

    • database_name.table_name
    • table_name.column_name
    • database_name.table_name.column_name

    所有select语句必须包含一个或多个select表达式。 选择表达式由以下选项之一组成:

    • 字段名。
    • 使用运算符和函数的表达式。
    • 规范"table_name.*"以选择给定表中的所有列。
    • 字符"*"用于从FROM子句中指定的所有表中选择所有列。
  • 如果只有模糊的条件,可以在 where 子句后面接上 like 子句进行模糊匹配:SELECT field, field2,... FROM table_name, table_name2,... WHERE field LIKE condition​​

    例如:SELECT * from products_tbl WHERE product_manufacturer LIKE 'XYZ%';​​

    注意:在 like 子句中,是用%来表示通配符的

  • 如果要对查询的结果进行筛选,可以在语句结尾加上 HAVING 子句:SELECT COUNT(field) as alias, field2 ... FROM table_name HAVING alias > 1 AND/OR ...;
  • 如果要对查询的结果进行排序,可以在语句的结尾加上 ORDER BY 子句:SELECT field, field2,... [or column] FROM table_name, table_name2,... ORDER BY field, field2,... ASC[or DESC]​​

    其中,ASC 表示升序,DESC 表示降序。如果没有指定排序方式,默认按升序排列。

  • 如果要对查询的结果按字段进行分组(例如查询一个名字出现了几次),可以使用 GROUP BY 子句:SELECT column_name FROM table_name WHERE ... GROUP BY column_name;​​

    如果要对结果集进一步统计,可以使用 WITH ROLLUP 关键字:SELECT column_name, MIN/MAX/SUM/AVG/COUNT(column_name) as alias FROM table_name GROUP BY column_name WITH ROLLUP;​​

    如果统计之后的结果集中有 NULL,可以用 coalesce 来替代掉 NULL。例如:SELECT coalesce(<出现 NULL 值的字段>, alias), MIN/MAX/SUM/AVG/COUNT(column_name) as alias FROM table_name GROUP BY column_name WITH ROLLUP;​​

  • 多表连接查询:如果需要从多个表中选择记录,可以使用 JOIN 子句,它可以将多个表的查询结果合并到单个对象中。JOIN 子句的语法如下:SELECT column FROM table_1 ​INNER JOIN​ table_2 ON table_1.column = table_2.column;​​

    连接有三种形式:

    • 左连接(LEFT JOIN):获取左表的所有记录,即使右表中没有匹配的记录
    • 右连接(RIGHT JOIN):获取右表的所有记录,即使左表中没有匹配的记录
    • 内连接(INNER JOIN):只获取两个表中字段匹配关系表达式的记录,相当于从两个表的交集中查询所需的记录
  • 了解了前面的语法之后,删除记录就很简单了:DELETE FROM table_name [WHERE …]​​

6. 约束

约束(CONSTRAINT)的主要作用是防止重复数据的存在,避免数据混乱和数据冗余的问题。

给已创建的表添加约束:ALTER TABLE table_name ADD CONSTRAINT constraint_name constraint_type(column_name or expression) expression;

约束主要有:主键、外键、唯一约束和检查约束。

  • 主键:保证数据的唯一性,一个表只能有一个主键,主键可以是一个字段或多个字段的集合,而且主键的值不能为空。在创建表或修改表时用 PRIMARY KEY​ 关键字来设定主键。如果在插入记录时试图插入与主键的值重复的数据,系统会抛出错误。可以在插入时使用 INSERT IGNORE INTO​ 让系统跳过重复的记录,达到在间隙中插入数据的目的;或者使用 REPLACE INTO​ 来删除表中已有的重复记录,并插入新的记录。
  • 外键:与主键约束一起使用,用于建立主表和从表的关联,约束两个表中数据的一致性和完整性。当主表删除某条记录时,从表中对应的记录也必须有相应的改变。
  • 唯一约束:所有记录中不能出现重复的值。唯一约束与主键约束相似的是它们都可以确保列的唯一性。不同的是,唯一约束在一个表中可有多个,并且设置唯一约束的列允许有空值,但是只能有一个空值。而主键约束在一个表中只能有一个,且不允许有空值。比如,在用户信息表中,为了避免表中用户名重名,可以把用户名设置为唯一约束。
  • 检查约束:用于对字段的值设置限制,避免无效数据输入。

7. 函数、存储过程和触发器

  • 创建一个函数:

    DELIMITER //
    CREATE FUNCTION func_student(id INT(11))
    RETURNS VARCHAR(20)
    COMMENT '查询某个学生的姓名'
    BEGIN
    RETURN(SELECT name FROM tb_student WHERE tb_student.id = id);
    END //
  • 创建一个存储过程:

    -- 设置语句结束符为 //
    DELIMITER //
    CREATE PROCEDURE ShowStuScore()
    BEGIN
    SELECT * FROM tb_students_score;
    END //

调用函数或存储过程:CALL function_name/procedure_name(parameter);

  • 创建一个触发器:

    CREATE TRIGGER double_salary
    AFTER INSERT ON tb_emp6
    FOR EACH ROW
    INSERT INTO tb_emp7
    VALUES (NEW.id,NEW.name,deptId,2*NEW.salary);

    注意:每个表都支持 INSERT、UPDATE 和 DELETE 的 BEFORE 与 AFTER,因此每个表最多支持 6 个触发器。

8. 视图和索引

  • 创建一个视图:

    CREATE VIEW v_students_info
    (s_id,s_name,d_id,s_age,s_sex,s_height,s_date)
    AS SELECT id,name,dept_id,age,sex,height,login_date
    FROM tb_students_info;

    这样可以限制用户能访问的内容,保证数据库的安全。

  • 索引可以在创建表或修改表属性时添加,也可以用 CREATE 语句创建索引:CREATE <索引名> ON <表名> (<列名> [<长度>] [ ASC | DESC]);

9. 用户管理

  • 创建用户:CREATE USER <用户> [ IDENTIFIED BY [ PASSWORD ] 'password' ] [ ,用户 [ IDENTIFIED BY [ PASSWORD ] 'password' ]]
  • 给用户授权:

    GRANT priv_type [(column_list)] ON database.table
    TO user [IDENTIFIED BY [PASSWORD] 'password']
    [, user[IDENTIFIED BY [PASSWORD] 'password']] ...
    [WITH with_option [with_option]...]

10. 备份和恢复

数据作为业务和操作的基础,会面临各种可能的威胁(例如,网络攻击,系统故障,系统崩溃和维护错误),所以备份至关重要。备份的主要选项包括逻辑备份和物理备份。 逻辑备份保存用于恢复数据的SQL语句。 物理备份包含数据副本。

  • 物理备份相比,逻辑备份提供了在具有不同配置的另一台机器上恢复数据的灵活性,物理备份通常限于相同的机器和数据库类型。 逻辑备份发生在数据库和表级,物理发生在目录和文件级。
  • 物理备份的大小小于逻辑备份,并且执行和恢复所需的时间也更少。 物理备份还包括日志和配置文件,但逻辑备份不包括。

物理备份通常需要第三方软件来实现,MariaDB 自身提供的备份方式是逻辑备份工具 mysqldump​。它会将数据转储为SQL,CSV,XML和许多其他格式。 但要注意,它的备份数据中不包含存储过程,视图和事件。

mysqldump​ 主要有三种使用方式:

  • 原始数据 - 通过--tab选项将表转储为原始数据文件,该选项还指定备份文件的存储位置

    mysqldump -u root -p --no-create-info --tab=<备份文件存储位置> <数据库名> <表名>

  • 直接输出内容 - 此选项允许将单个或多个表的 SQL 语句输出,并支持备份主机上的所有现有数据库。

    mysqldump -u root -p <数据库名> <表名> > export_file.txt

  • 远程传输 - 您还可以将数据库和表导出到另一个主机

    mysqldump -u root -p <数据库名> | mysql -h <主机地址> <数据库名>

注意:InnoDB使用缓冲池来提高性能。所以建议在 MariaDB 的配置文件 my.cnf​ 或 my.ini​中将innodb_change_buffering​的值设为 none​。


六、自动化运维软件 Ansible

1. 业务部署流程和引发的问题

  • 开发环境→测试环境→预生产环境→生产环境
  • 为了解决手工维护大量机器带来的成本问题,人们提出了自动化运维
  • 将需要重复执行的操作写入到脚本中,再让每台机器执行脚本,就可以自动完成运维任务
  • 但是脚本的编写及维护较为复杂,且需要管理员掌握脚本语言
  • 后来诞生了自动化运维软件,可以使用命令或者图形界面批量配置和管理系统
  • 随着 DevOps 的概念诞生,运维需要掌握一定的开发技能

2. Ansible 的架构

  • playbook:需要执行的剧本
  • inventory file:记录主机信息和分组
  • managed node:被管理的节点
  • control machine:管理节点
  • Ansible 默认使用 SSH 登录被管理的节点,所以需要提前配置好 SSH 免密登录(密码或者密钥)
  • Ansible 无需安装客户端,也不需要以 root 用户登录,使用 yaml 语法编写配置文件
  • Ansible 使用 Python 开发,由 paramiko 模块发起和管理 SSH 连接;PyYAML 模块维护配置文件

3. Ansible 部署

  • 准备三台虚拟机,第一台 master,剩余两台 slave1 和 slave2,都安装 CentOS,并配置 epel 软件源
  • ansible_master: 192.168.50.12
  • ansible_slave1: 192.168.50.15
  • ansible_slave2: 192.168.50.14
  • 管理节点(服务端)安装 ansible:yum install ansible libselinux-python
  • 查询 ansible 版本信息:ansible --version

    image-20230212010640-q5wsu1r.png

  • 被管理节点(客户端)安装 libselinux-python
  • 服务端修改 ansible 目录下的 hosts​ 文件,添加客户端节点的地址
  • 执行命令之前需要先用 SSH 连接一次客户端节点,添加客户端节点的指纹信息
  • 服务端远程执行hostname命令输出客户端主机名称:ansible 客户端节点地址 -m command -a 'hostname' -k -u 执行命令的用户身份

    参数说明:

    -m:模块

    -a:模块的参数

    -k:连接前询问密码

    -u:指定身份

4. SSH 免密登录

由于每次需要输入密码过于繁琐,我们可以考虑通过密钥对的方式设置免密登录,这样每次执行命令时就不需要输入客户端的密码了。

  • 方式 1:在 ansible 目录下的 hosts 文件中填写密码,之后就不再需要指定验证方式和身份

    image-20230212011031-hvqbikw.png

  • 方式 2:SSH 密钥验证,在管理节点创建 SSH 密钥(ssh-keygen -t rsa),之后通过公钥分发脚本将公钥分发给被管理节点:

    #!/bin/bash
    ssh_pass=12345678
    key_path=~/.ssh/id_rsa.pub
    for ip in 14 15
        do
            sshpass -p$ssh_pass ssh-copy-id -i $key_path "-o StrictHostKeyChecking=no" 192.168.50.$ip
            done

    之后给脚本添加可执行权限,再使用 sh + 脚本路径​ 命令执行脚本即可

5. Ansible 的管理模式

  • Ad-HOC:通过命令行实现管理,适用于少量的、简单的命令。可以通过 ansible-doc​ 命令查询支持的模块

    命令用途
    ​`ansible-doc -lgrep ^command`​输出模块列表并从中过滤出 command 模块
    ansible-doc -s command查看 command 模块的具体说明
  • Playbook:通过剧本批量执行复杂任务,类比脚本

6. 基本命令模块:command

语法:ansible 节点 -m command -a 命令和参数

常用参数用途
chdir执行命令之前切换到指定目录
creates执行命令之前判断文件/目录是否存在,如果存在就跳过该条命令
free_form参数中可以输入任何系统命令
removes执行命令之前判断文件/目录是否存在,如果不存在就跳过该条命令
warn是否提供警告信息,默认开启

注意事项:

  • 不能使用 shell 变量,也不能使用特殊符号

案例:

  • 获取所有被管理节点的负载信息:ansible all -m command -a "uptime"
  • 切换到 /tmp 目录并打印当前工作目录:ansible all -m command -a "pwd chdir=/tmp"
  • 列出当前目录信息,但是因为存在 /root 目录,该命令会被跳过:ansible slave1 -m command -a "uname -a creates=/root"

7. 支持 shell 语法的模块:shell

常用参数用途
chdir执行命令之前切换到指定目录
creates执行命令之前判断文件/目录是否存在,如果存在就跳过该条命令
free_form参数中可以输入任何系统命令
removes执行命令之前判断文件/目录是否存在,如果不存在就跳过该条命令

案例:

  • 获取被管理节点的某个进程信息:ansible all -m shell -a "ps -ef | grep bash | grep -v grep"

8. 脚本模块:script

只需管理节点有一份脚本,就可以在所有被管理节点上执行

常用参数用途
chdir执行命令之前切换到指定目录
creates执行命令之前判断文件/目录是否存在,如果存在就跳过该条命令
removes执行命令之前判断文件/目录是否存在,如果不存在就跳过该条命令

9. 软件包管理模块:yum

常用参数用途
name指定需要管理的软件包名称
state状态,包含三种:present/installed=安装;latest=安装最新版本;absent/removed=卸载
disable_gpg_check禁用 rpm 包的公钥验证,如果 yum 源没有开启验证,则需要将其禁用
enablerepo临时指定 yum 源,注意需要是源的名称,而不是 repo 文件的名称
disablerepo临时禁用 yum 源

案例:

  • 安装 nginx 软件包

    ansible all -m yum -a "name=nginx"

  • 安装 nginx 并临时关闭公钥验证

    ansible all -m yum -a 'name=nginx state=installed disable_gpg_check=yes'

  • 卸载 nginx 软件包

    ansible all -m yum -a 'name=nginx state=removed'

  • 批量安装软件包

    ansible all -m yum -a "name=nginx,telnet state=installed"

10. 自动化执行命令:playbook

运维人员可以通过提前编写好的 yml 文件来远程执行命令,并充分利用 Ansible 提供的众多模块。通过设置触发器或定时任务,即可实现自动化执行命令。

基本命令:ansible-playbook yml配置文件路径

  • yml 配置文件基本语法如下,yml 通过缩进来表示层级关系,所以必须要有缩进,至于用几个空格就看个人喜好了。

    - hosts: 需要执行命令的客户端或组
      tasks:
      - name: 任务描述
        模块名称:
          参数1: 值
          参数2: 值
        vars:
          变量组名称:
          - 变量值1
          - 变量值2
          - 变量值3
  • 案例1:批量安装软件包

    - hosts: all
      tasks:
      - name: install some packages
        yum:
          name: "{{packages}}"
        vars:
          packages:
          - nginx
          - telnet
          - httpd
  • 案例2:更新所有软件包,并排除名称包含「kernel」和「foo」的软件包

    - hosts: all
      tasks:
      - name: upgrade all packages, excluding kernel & foo related packages
        yum:
          name: '*'
          state: latest
          exclude: kernel*, foo*

七、监控平台 Zabbix

1. 监控体系:

  • 硬件监控:通过专用的监控硬件来完成物理设备的监控工作,例如硬件温度、风扇转速、处理器频率、硬件故障、网络接口状态等
  • 系统监控:CPU 频率和占用率、内存占用、硬盘 IO 和占用、系统负载、进程和线程数量、TCP连接数
  • 服务监控:httpd、nginx、php-fpm、mysql、memcache、redis、tomcat 等
  • 性能监控:网站性能、服务器性能、数据库性能、存储性能
  • 日志监控:监控运行的软件是否产生了错误日志
  • 安全监控:统计不同的攻击来源和攻击类型,监控用户登录、密码修改、文件改动
  • 网络监控:端口、访问 URL、速率、网卡累计流量、各种协议的数据包(ICMP、SMTP 等)

2. 监控方式

  • 基础设施监控(硬件监控):硬件温度、风扇转速、存储占用(df​、fdisk​、iotop​)、CPU 占用(lscpu​、uptime​、top​、htop​、glances​)、内存占用(free​)、网络吞吐(iftop​)
  • 应用监控:mysql、redis、nginx、php-fpm、python 等

3. Zabbix 的特点

  • 能自定义监控内容,通过脚本采集所需的数据
  • 数据可以写入到数据库,便于日后分析
  • 可以通过模板快速部署一组监控项
  • 每个监控项都可以看到历史记录,且 Web UI 友好
  • 有触发器机制,可以定义复杂的告警逻辑
  • 提供了告警确认机制,告知运维人员故障是否有人处理
  • 支持邮件、短信、微信等告警方式
  • 触发告警后,可以远程执行系统命令,诸如自我修复、重启、采集数据等
  • 有原生的绘图模块,便于二次开发

4. Zabbix 安装

客户端可以不安装软件,Zabbix 可以通过 TCP、ICMP 等协议与客户端建立连接并进行监控。

4.1 服务端部署

  • 如果是 CentOS,需要先禁用 SELinux:修改 /etc/selinux/config 文件,将SELINUX=disabled,之后重启系统
  • 安装 Zabbix 软件源:rpm -Uvh https://mirrors.aliyun.com/zabbix/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm
  • 替换软件源中的地址为阿里云镜像:sed -i 's#http://repo.zabbix.com#https://mirrors.aliyun.com/zabbix#' /etc/yum.repos.d/zabbix.repo
  • 启用 Zabbix 前端源:修改 zabbix.repo​ 文件,将 zabbix-frontend 的值设为 enabled
  • 清理并重建 yum 缓存:yum clean all​ 和 yum makecache
  • 安装 SCL,便于在同一个系统中安装不同版本的软件,SCL 版本的软件会安装在 /etc/opt/rh​ 目录下:yum install centos-release-scl -y
  • 安装 Zabbix 组件:yum install zabbix-server-mysql zabbix-agent zabbix-web-mysql-scl zabbix-apache-conf-scl -y
  • 安装 MariaDB:yum install mariadb-server -y
  • 令 MariaDB 开机自启并立即启动:systemctl enable --now mariadb
  • 设置 MariaDB 的 root 密码:mysql_secure_installation
  • 登录 MariaDB,创建 Zabbix 所需的用户和数据库:

    create database zabbix character set utf8 collate utf8_bin;

    create user zabbix@localhost identified by 'password';

  • 将 zabbix 数据库授权给 zabbix 用户:

    grant all privileges on zabbix.* to zabbix@localhost; ​

    flush privileges;

  • 导入 Zabbix 数据库信息:zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix
  • 修改 Zabbix 配置文件 /etc/zabbix/zabbix_server.conf​,找到「DBPassword」字段,修改数据库的密码
  • 修改 PHP 配置文件 /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf​,将「date.timezone」的值改为「Asia/Shanghai」并将行首的分号去掉
  • 启动 Zabbix:systemctl enable --now zabbix-server zabbix-agent httpd rh-php72-php-fpm
  • 防火墙开放对应的服务端口:

    firewall-cmd --permanent --add-service="zabbix-agent"

    firewall-cmd --permanent --add-service="zabbix-server"

    firewall-cmd --permanent --add-service="http"

    firewall-cmd --permanent --add-service="https"

    firewall-cmd --reload

  • 访问 Zabbix:服务器地址/zabbix,默认用户名 Admin​,密码 zabbix
  • 校准时间:

    yum install ntpdate -y​​

    ntpdate -u ntp.aliyun.com​​

  • 修改时区:

    mv /etc/localtime{,.bak}

    ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

4.2 客户端安装

  • zabbix-agent2 基于 golang 开发,并发性能好,默认使用 10050 端口
  • 禁用 SELinux:sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

    可以结合前面学习的 Ansible 远程执行指令:ansible all -m command -a "sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config"

  • 开放防火墙端口:

    firewall-cmd --permanent --add-service="zabbix-agent"​​

    firewall-cmd --reload

  • 校准时间:

    yum install ntpdate -y

    ntpdate -u ntp.aliyun.com

  • 修改时区:

    mv /etc/localtime{,.bak}

    ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

  • 安装 Zabbix 软件源:rpm -Uvh https://mirrors.aliyun.com/zabbix/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm
  • 替换软件源中的地址为阿里云镜像:sed -i 's#http://repo.zabbix.com#https://mirrors.aliyun.com/zabbix#' /etc/yum.repos.d/zabbix.repo
  • 安装 zabbix-agent2 软件包:yum install zabbix-agent2 -y
  • 修改 zabbix-agent2 配置文件 /etc/zabbix/zabbix_agent2.conf​,将「Server」字段和「ServerActive」的值改为服务器的地址。之后将「Hostname」的值改为空,并将下面「HostnameItem=system.hostname」这个字段的注释符去掉,让 Zabbix 直接读取本机名称。
  • 启动 zabbix-agent2:systemctl enable --now zabbix-agent2​​
  • 服务端验证连通性:

    yum install zabbix-get -y

    zabbix_get -s '客户端 IP 地址' -p 10050 -k 'agent.ping'

4.3 服务端添加客户端

  • 在服务端的「配置」---「主机」中点击「创建主机」
  • 输入主机名称、群组、IP 地址、端口,然后点击「模板」,搜索「Linux」,选中「Template OS Linux by Zabbix agent」或其它可用的模板
  • 添加模板后添加主机即可

5. 修复 Web UI 图表乱码问题

  • 安装一个中文字体:yum install wqy-microhei-fonts -y
  • 覆盖原有的字体文件:cp /usr/share/fonts/wqy-microhei/wqy-microhei.ttc /usr/share/fonts/dejavu/DejaVuSans.ttf

八、Docker 容器引擎

在生产环境中,部署应用,尤其是 Web 应用,一直是一件让运维头疼的事情,其中运行环境的差异是最大的难点。以 Docker 为代表的容器技术应运而生,容器中只包含了最小根文件系统、运行环境和应用,消除了不同物理机上运行环境的差异,运维人员不需要关心宿主机运行的是 Debian 还是 CentOS,只要能安装 Docker 这个容器引擎就行。而且 Docker 可以轻松构建属于自己的镜像,还可以利用 Docker Compose 实现一个脚本部署多个容器,大大提高了部署效率。

本章目标:搭建一个 Django + uWSGI + MySQL + Nginx 的 Web 应用。

目标拆解:使用三个 Docker 容器分别安装这些应用,其中:

  • db 容器:安装 MySQL 8,用于存储网站数据
  • web 容器:安装 Django + uWSGI,Django 是基于 Python 的 Web 应用框架,uWSGI 用于处理动态请求,并将其转发给 Django 应用处理
  • nginx 容器:安装 Nginx,搭建反向代理并处理静态请求

写好每个容器的 Dockerfile 之后,编写 Docker Compose 配置文件,让 Docker Compose 一次性部署这三个容器并启动。

整个项目的框架图:

Django容器框架.png

先从最简单的开始——安装 Docker 容器引擎,创建一个容器,部署一个最小的 Django 项目。

一、安装 Docker

不同的操作系统,安装 Docker 的方式也会有差异,所以具体的安装步骤建议直接参考 Docker 官网的文档,安装 Docker 引擎和 Docker Compose。

安装文档地址: https://docs.docker.com/engine/install/

二、部署一个 Django 项目

  1. 安装好 Python,用pip install django命令安装 Django 模块。
  2. 切换到一个新的目录,运行django-admin startproject 项目名命令,就会在当前目录创建一个 以项目名为名称的目录,这个就是 Django 项目所在的目录,里面有一个 manage.py 文件和一个与项目名一致的文件夹。后续的步骤需要切换到 manage.py 所在的目录下。
  3. 修改<项目名>/settings.py,把ALLOWED_HOST的值修改为宿主机的 IP 地址。为什么是宿主机的地址?当用户访问 Web 应用时,用户输入的是宿主机的地址,之后通过 Docker 提供的端口映射功能,把请求转发到容器中的特定端口,经过转发之后,来源 IP 地址就变为了宿主机的地址,所以只需要允许宿主机地址访问 Web 应用即可。

    ALLOWED_HOSTS = ["192.168.216.132"]
  4. 新建一个文件名为requirements.txt,用来告知 pip 需要安装什么模块。该文件的内容只有一行:

    Django==4.2
  5. 新建一个文件名为pip.conf,用于设置 pip 的镜像源,提高模块下载速度。

    [global]
    index-url = https://mirrors.aliyun.com/pypi/simple/
    [install]
    trusted-host=mirrors.aliyun.com
  6. 新建 dockerfile,向 Docker 引擎描述如何创建镜像。这里的基础镜像选择基于 alpine 构建的 Python 环境,占用体积会大幅减少。内容如下:

    # 基础镜像
    FROM python:3.9-alpine3.16
    # 镜像作者
    MAINTAINER Yulin Wei
    # 设置环境变量,防止 Python 缓冲输出
    ENV PYTHONUNBUFFERED 1
    # 设置 pip 镜像源
    COPY pip.conf /root/.pip/pip.conf
    # 在容器内部创建目录
    RUN mkdir -p /var/www/html/mysite1
    # 切换工作目录
    WORKDIR /var/www/html/mysite1
    # 将当前目录文件复制到容器的工作目录
    ADD . /var/www/html/mysite1
    # 使用 pip 安装依赖模块
    RUN pip install -r requirements.txt
    # 运行迁移命令
    RUN python3 manage.py migrate
    # 设置容器启动时执行的命令
    ENTRYPOINT ["python3", "manage.py"]
    CMD ["runserver", "0.0.0.0:8000"]
  7. 根据描述文件生成镜像

    docker build -t django_image:v1 .

    生成镜像之后,可以查看镜像列表:

    docker images

    显示如下结果就说明镜像创建成功了。如果对镜像不满意,可以使用docker rmi <IMAGE ID>命令来删除镜像。

    REPOSITORY     TAG       IMAGE ID       CREATED         SIZE                                                    
    django_image   v1        08dee7494f35   5 seconds ago   88.7MB
  8. 使用镜像生成容器并运行,其中会设定一些参数:

    • -it表示可交互
    • -d表示后台运行
    • --name指定容器名称
    • -p设定端口映射,把容器的 8000 端口映射到宿主机的 80 端口
    # 生成容器并运行
    docker run -it -d --name testsite -p 80:8000 django_image:v1
    9bd79deff7d63258cbee28a0f58c62331b6600fb4fc97e03a76bbffe7c0ad734
    
    # 查看运行中的容器
    docker ps
    CONTAINER ID   IMAGE             COMMAND     CREATED         STATUS         PORTS                                   NAMES
    9bd79deff7d6   django_image:v1   "python3"   4 seconds ago   Up 2 seconds   0.0.0.0:80->8000/tcp, :::80->8000/tcp   testsite
  9. 可以使用命令进入到容器中,执行其他的命令:

    # 进入容器
    docker exec -it testsite /bin/sh
    /var/www/html/mysite1 # 
  10. 打开浏览器,输入宿主机的 IP 地址,就可以看到 Django 的欢迎页面了。

三、向镜像中添加 uWSGI

在上一个镜像中,为了方便测试,使用了 Django 自带的脚本来启动 Web 应用,但是在生产环境中这是不可取的。我们需要使用安全性和并发性能都更好的其它服务器软件来启动 Web 应用。

uWSGI 是一个 Web 服务器和应用服务器,可以将 Python 应用程序部署到生产环境中。它可以作为独立服务器或与 Nginx、Apache 等 Web 服务器配合使用。

uWSGI 可以与各种 Web 框架和应用程序一起使用,例如 Django、Flask、Pyramid 等。它支持多种协议和接口,例如 WSGI、HTTP、FastCGI 等,并提供高性能和可扩展性。

uWSGI 还具有许多有用的功能,例如自动进程管理、负载均衡、缓存、限流、监控等。它可以通过配置文件进行配置,并提供了丰富的命令行选项和 API 接口。

这一步我们需要构建一个新的镜像,准备一个新的 Django 项目,进入 manage.py 文件所在的目录。记得修改<项目名>/settings.py,把ALLOWED_HOST的值修改为宿主机的 IP 地址。

把上一个镜像中的 Dockerfile、pip.conf、requirements.txt 复制过来,稍作修改就可以使用了。

  1. 修改 requirements.txt 文件

    • 新增一行:uwsgi >= 2.0.18
  2. 新建一个脚本文件,名为start.sh并赋予可执行权限,文件内容如下:

    #!/bin/sh
    # 迁移模型,将模型写入数据库
    python3 manage.py makemigrations
    python3 manage.py migrate
    # 启动服务
    uwsgi --ini uwsgi.ini
    # 阻塞进程,防止容器自动退出
    tail -f /dev/null
  3. 新建一个文本文件,名为uwsgi.ini,存放 uWSGI 的配置信息:

    [uwsgi]
    project=mysite2
    base=/var/www/html
    
    chdir=%(base)/%(project)
    module=%(project).wsgi:application
    master=True
    processes=2
    
    #这里直接使用uwsgi做web服务器,使用http协议。如果使用nginx,需要借助socket做 uwsgi 与 nginx 之间的通信。
    http=0.0.0.0:8000 
    buffer-size=65536
    
    pidfile=/tmp/%(project)-master.pid
    vacuum=True
    max-requests=5000
    daemonize=/tmp/%(project)-uwsgi.log
    
    #设置一个请求的超时时间(秒),如果一个请求超过了这个时间,则请求被丢弃
    harakiri=60
    #当一个请求被harakiri丢弃时,会输出一条日志
    harakiri-verbose=true
  4. 修改 Dockerfile,仅在有变动的地方做了注释。

    FROM python:3.9-alpine3.16
    MAINTAINER Yulin Wei
    ENV PYTHONUNBUFFERED 1
    COPY pip.conf /root/.pip/pip.conf
    RUN mkdir -p /var/www/html/mysite2
    WORKDIR /var/www/html/mysite2
    COPY . /var/www/html/mysite2
    # 设置 apk 镜像源
    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
    # 安装编译 uwsgi 所需的软件包
    RUN apk add --no-cache build-base musl-dev linux-headers
    RUN pip install -r requirements.txt
    # 给脚本文件设置可执行权限
    RUN chmod +x ./start.sh
    # 设置启动命令
    ENTRYPOINT ["sh", "start.sh"]
  5. 构建镜像,--no-cache参数可以避免使用之前缓存的镜像层,导致镜像中没有集成最新的文件:docker build --no-cache -t django_image:v2 .
  6. 启动容器,记得让之前创建的容器停止运行。

    • 停止容器:docker stop <容器 ID>
    • 启动容器:docker run -it -d --name testsite2 -p 80:8000 django_image:v2
  7. 浏览器访问宿主机地址,就可以看到 Django 的欢迎页面了。

四、双容器部署

在生产环境中,往往不会让一个容器“单打独斗”,而是利用 SaaS 的思想,把一个应用部署在一个容器中,容器之间相互通信,打通前后端,实现 Web 应用的各项功能。

整个项目的文件布局如图所示:

mysite3    # web 容器
├── db.sqlite3
├── dockerfile
├── manage.py    # django 项目管理脚本
├── media    # 存放媒体文件的目录
├── mysite3    # 存放 django 项目文件的目录
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── pip.conf    # pip 镜像源配置文件
├── requirements.txt    # pip 模块描述文件
├── start.sh    # 启动项目的脚本
├── static    # 存放静态文件(css 和 js)的目录
└── uwsgi.ini    # 配置 web 服务器和 web 应用的接口
nginx    # nginx 容器
├── dockerfile
├── log
└── nginx.conf    # nginx 配置文件

该项目的难点:

  • 创建两个容器,分别部署不同的服务,容器间通过 Docker 的虚拟局域网通信
  • 容器启动的先后顺序,先启动 Django 容器,再启动 Nginx 容器,否则 Nginx 收到动态请求之后不知道转发给谁
  • 由于需要让 Nginx 处理静态请求,需要预先设置好静态文件的路径

接下来开始部署。

  1. 创建 Django 项目,进入 manage.py 所在的目录下,创建staticmedia两个目录,用于存放静态文件。
  2. 把上一个项目的 pip.conf、Dockerfile、requirements.txt、start.sh 和 uwsgi.ini 拷贝到当前目录。
  3. 修改<项目名/settings.py>,设置ALLOWED_HOSTS为宿主机 IP 地址,并添加静态文件路径:

    import os
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
    STATIC_URL = "/static/"
    
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    MEDIA_URL = "/media/"
  4. 修改 Dockerfile,这次不需要复制整个项目到镜像中了,因为运行的时候会直接把宿主机中的项目目录挂载到容器中。

    FROM python:3.9-alpine3.16
    MAINTAINER Yulin Wei
    ENV PYTHONUNBUFFERED 1
    COPY pip.conf /root/.pip/pip.conf
    RUN mkdir -p /var/www/html/mysite3
    WORKDIR /var/www/html/mysite3
    # 只复制安装模块所需的描述文件
    COPY requirements.txt /var/www/html/mysite3
    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
    RUN apk add --no-cache build-base musl-dev linux-headers
    RUN pip install -r requirements.txt
    ENTRYPOINT ["sh", "start.sh"]
  5. 修改 start.sh ,在开头添加一行:

    python manage.py collectstatic --noinput
  6. 修改 uwsgi.ini,更新项目路径,设置 socket 套接字通信。将 http 开头的一整行替换为:

    socket=0.0.0.0:8000
  7. 构建 Django 镜像并启动,容器名为“testsite3”,这里要注意使用-v参数挂载网站目录到 manage.py 所在的目录下,这样就可以直接用宿主机本地的目录存放网站数据,不用担心容器被删除后网站数据会丢失。

    docker build --no-cache -t django_image:v3 .
    docker run -it -d --name testsite3 -p 8000:8000 -v /root/Downloads/mysite3:/var/www/html/mysite3 django_image:v3
    • 可以使用docker exec -it testsite3 cat /tmp/mysite3-uwsgi.log查看日志,检查 uWSGI 是否正常运行。
    • 之后执行docker inspect testsite3 | grep "IPAddress"命令查看容器的 IP 地址,这里查看到 IP 地址是 172.17.0.2。
  8. 在另一个目录创建 Nginx 的镜像,Dockerfile 内容如下:

    # nginx镜像
    FROM nginx:latest
    
    # 删除原有配置文件,创建静态资源文件夹和ssl证书保存文件夹
    RUN rm /etc/nginx/conf.d/default.conf \
    && mkdir -p /usr/share/nginx/html/static \
    && mkdir -p /usr/share/nginx/html/media \
    && mkdir -p /usr/share/nginx/ssl
    
    # 添加配置文件
    ADD ./nginx.conf /etc/nginx/conf.d/
    
    # 关闭守护模式
    CMD ["nginx", "-g", "daemon off;"]
  9. 添加 nginx.conf 文件,内容如下:

    # nginx配置文件
    upstream django {
    ip_hash;
    server 172.17.0.2:8000; # Django容器所在IP地址及开放端口,非宿主机外网IP
    }
    
    server {
    listen 80; # 监听80端口
    server_name localhost; # 可以是nginx容器所在ip地址或127.0.0.1,不能写宿主机外网ip地址
    location /static {
        alias /usr/share/nginx/html/static; # 静态资源路径
    }
    
    location /media {
        alias /usr/share/nginx/html/media; # 媒体资源,用户上传文件路径
    }
    
    location / {
        include /etc/nginx/uwsgi_params;
        uwsgi_pass django;
        uwsgi_read_timeout 600;
        uwsgi_connect_timeout 600;
        uwsgi_send_timeout 600;
        # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # proxy_set_header Host $http_host;
        # proxy_redirect off;
        # proxy_set_header X-Real-IP $remote_addr;
        # proxy_pass http://django;
    }
    }
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
  10. 构建 Nginx 镜像并启动容器。

    docker build --no-cache -t mynginx:v1
    docker run -it -d -p 80:80 --name testsite3-nginx \
    -v /root/Downloads/mysite3/static:/usr/share/nginx/html/static \
    -v /root/Downloads/mysite3/media:/usr/share/nginx/html/media \
    -v /root/Downloads/nginx/var/log/nginx \
    mynginx:v1
  11. 打开浏览器,输入宿主机 IP 地址,可以访问欢迎页面。

在这一部分,我们手动构建了两个容器,一个容器安装了 Django+uWSGI,另一个容器安装了 Nginx,并且成功部署了一个基本的 Django 项目。然而在实际的生产环境中,往往需要定义数量庞大的容器,且容器之间存在依赖关系,一个个手动创建容器不仅效率低还容易出错,所以需要一种可以定义和部署容器集群的工具,而 Docker 提供了 Docker Compose 来实现这个功能。接下来就会利用这个工具来构建三个容器组成的集群,并部署一个 Django+uWSGI+Nginx+MySQL 的Web 应用项目。

五、初探容器集群

Docker Compose 是一个用来定义和运行复杂容器的工具,它可以创建、启动多个容器,并实现容器的管理功能。

在这一部分,我们将使用 Docker Compose 编排并启动 3 个容器,这三个容器分别是:

  • web 容器,部署 Django+uWSGI,开放 8000 端口
  • db 容器,部署 MySQL,开放 3306 端口
  • nginx 容器,部署 Nginx,开放 80 和 443 端口

在前面的例子中,可以通过给容器起名的方式来标识容器,这样就不需要根据容器 ID 或者 IP 地址来区分容器。

这三个容器存在的依赖关系:web 容器依赖于 db 容器,nginx 容器依赖于 web 容器。

所有文件的布局如下:

mysite4_root/
├── compose
│   ├── mysql
│   │   ├── conf
│   │   │   └── my.cnf
│   │   ├── init
│   │   │   └── init.sql
│   │   └── my.cnf
│   ├── nginx
│   │   ├── dockerfile
│   │   ├── log
│   │   │   ├── access.log
│   │   │   └── error.log
│   │   ├── nginx.conf
│   │   └── ssl
│   └── uwsgi
│       └── mysite4-uwsgi.log
├── docker-compose.yml
└── mysite4
    ├── dockerfile
    ├── manage.py
    ├── media
    ├── mysite4
    │   ├── asgi.py
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── pip.conf
    ├── requirements.txt
    ├── start.sh
    ├── static
    └── uwsgi.ini

下面开始部署。

  1. 创建 Django 项目。进入一个空目录,本例中该目录名为mysite4_root,执行django-admin startproject <name>命令创建一个新的 Django 项目,在本例中,项目名称为mysite4
  2. 创建docker-compose.yml文件,用于描述各个容器的配置信息,例如挂载的目录、环境变量、开放的端口、是否自动重启等等,这样就不需要用冗长的命令去创建容器了。注意,yaml 格式的文件对缩进是敏感的,一定要注意各个层级的缩进,每级缩进的空格的数量也要统一。

    version: "3"
     
    volumes: # 自定义数据卷,位于宿主机/var/lib/docker/volumes
      mysite4_db_vol: # 定义数据卷同步容器内mysql数据
      mysite4_media_vol: # 定义数据卷同步media文件夹数据
     
    services:
      db:    # 服务名称
        image: mysql:8.0    # 容器使用的镜像
        environment:    # 环境变量
           - MYSQL_ROOT_PASSWORD=123456 # 数据库密码
           - MYSQL_DATABASE=mysite4 # 数据库名称
           - MYSQL_USER=dbuser # 数据库用户名
           - MYSQL_PASSWORD=password # 用户密码
     
        volumes:
           - mysite4_db_vol:/var/lib/mysql:rw # 挂载数据库目录, 可读可写
           - ./compose/mysql/my.cnf:/etc/mysql/my.cnf # 挂载配置文件
           - ./compose/mysql/init:/docker-entrypoint-initdb.d/ # 挂载初始化sql脚本
        ports:
           - "3306:3306" # 开放端口
        restart: always    # 故障时自动重启容器
     
      web:
        build: ./mysite4 # 使用mysite4目录下的Dockerfile构建容器
        expose:
           - "8000"
        volumes:
           - ./mysite4:/var/www/html/mysite4 # 挂载项目目录
           - mysite4_media_vol:/var/www/html/myproject/media # 以数据卷挂载媒体文件
           - ./compose/uwsgi:/tmp # 挂载uwsgi日志
        links:    # 与容器建立关联
           - db
        depends_on: # 建立依赖关系,指定容器启动后才能启动该容器
           - db
        restart: always
        tty: true
        stdin_open: true
     
      nginx:
        build: ./compose/nginx
        ports:
           - "80:80"
           - "443:443"
        expose:
           - "80"
        volumes:
           - ./mysite4/static:/usr/share/nginx/html/static # 挂载静态文件
           - ./compose/nginx/ssl:/usr/share/nginx/ssl # 挂载ssl证书目录
           - ./compose/nginx/log:/var/log/nginx # 挂载日志
           - mysite4_media_vol:/usr/share/nginx/html/media # 挂载媒体文件
        links:
           - web
        depends_on:
           - web
        restart: always
  3. 准备 web 容器所需的文件。进入mysite4目录,创建 static 和 media 目录,编写 dockerfilepip.confrequirements.txtstart.shuwsgi.ini,可以沿用之前编写的文件,进行一些修改即可。

    • dockerfile:
    FROM python:3.9-alpine3.16
    MAINTAINER Yulin Wei
    ENV PYTHONUNBUFFERED 1
    COPY pip.conf /root/.pip/pip.conf
    RUN mkdir -p /var/www/html/mysite4
    WORKDIR /var/www/html/mysite4
    COPY requirements.txt /var/www/html/mysite4
    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
    RUN apk add --no-cache build-base musl-dev linux-headers mariadb-dev
    RUN pip install -r requirements.txt
    CMD ["sh", "start.sh"]
    • requirements.txt
    Django == 4.2
    uwsgi >= 2.0.18
    mysqlclient >= 1.4.6
    • uwsgi.ini
    [uwsgi]
    project=mysite4
    base=/var/www/html
    
    chdir=%(base)/%(project)
    module=%(project).wsgi:application
    master=True
    processes=2
    
    socket=0.0.0.0:8000
    buffer-size=65536
    
    pidfile=/tmp/%(project)-master.pid
    vacuum=True
    max-requests=5000
    daemonize=/tmp/%(project)-uwsgi.log
    
    #设置一个请求的超时时间(秒),如果一个请求超过了这个时间,则请求被丢弃
    harakiri=60
    #当一个请求被harakiri杀掉会,会输出一条日志
    harakiri-verbose=true
    
    memory-report = true
    # 当占用了一定内存后,自动回收内存
    reload-on-as = 1024
    python-autoreload = 1
  4. 回到mysite_root目录,创建一个新的目录名为compose,这里面放置的是 nginx 和 db 容器所需的文件。在 compose 目录下创建 nginx 和 mysql 文件夹。之后进入 nginx 文件夹,准备 nginx 容器所需的文件。

    • dockerfile:
    # nginx镜像
    FROM nginx:latest
    
    # 删除原有配置文件,创建静态资源文件夹和ssl证书保存文件夹
    RUN rm /etc/nginx/conf.d/default.conf \
    && mkdir -p /usr/share/nginx/html/static \
    && mkdir -p /usr/share/nginx/html/media \
    && mkdir -p /usr/share/nginx/ssl
    
    # 添加配置文件
    ADD ./nginx.conf /etc/nginx/conf.d/
    
    # 设置media文件夹所有权和读写权限
    RUN chown -R www-data:www-data /usr/share/nginx/html/media
    RUN chmod -R 775 /usr/share/nginx/html/media
    
    # 启动nginx
    CMD ["nginx", "-g", "daemon off;"]
    • nginx.conf
    # nginx配置文件
    upstream django {
      ip_hash;
      server web:8000; # Django容器主机名及开放端口,非宿主机外网IP
    }
    
    server {
      listen 80; # 监听80端口
      server_name localhost; # 可以是nginx容器所在ip地址或127.0.0.1,不能写宿主机外网ip地址
      location /static {
        alias /usr/share/nginx/html/static; # 静态资源路径
      }
    
      location /media {
        alias /usr/share/nginx/html/media; # 媒体资源,用户上传文件路径
      }
    
      location / {
        include /etc/nginx/uwsgi_params;
        uwsgi_pass django;
        uwsgi_read_timeout 600;
        uwsgi_connect_timeout 600;
        uwsgi_send_timeout 600;
        # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # proxy_set_header Host $http_host;
        # proxy_redirect off;
        # proxy_set_header X-Real-IP $remote_addr;
        # proxy_pass http://django;
      }
    }
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
  5. 进入 mysql 文件夹,准备 db 容器所需的文件。在里面创建一个 init 文件夹,用于放置初始化脚本。

    • my.cnf:
    [mysqld]
    user=mysql
    default-storage-engine=INNODB
    character-set-server=utf8
    
    port            = 3306 # 端口与docker-compose里映射端口保持一致
    #bind-address= localhost #一定要注释掉,mysql所在容器和django所在容器不是同一个 IP
    
    basedir         = /usr
    datadir         = /var/lib/mysql
    tmpdir          = /tmp
    pid-file        = /var/run/mysqld/mysqld.pid
    socket          = /var/run/mysqld/mysqld.sock
    skip-name-resolve  # 这个参数是禁止域名解析的,远程访问推荐开启skip_name_resolve。
    
    [client]
    port = 3306
    default-character-set=utf8
    
    [mysql]
    no-auto-rehash
    default-character-set=utf8
    • init 文件夹里的 init.sql:
    GRANT ALL PRIVILEGES ON myproject.* TO dbuser@"%" IDENTIFIED BY "password";
    FLUSH PRIVILEGES;
  6. 修改 Django 项目的配置文件settings.py,这里仅列出需要修改的部分:

    # 允许访问服务器的名单
    ALLOWED_HOSTS = ["宿主机 IP 地址"]
    
    # 数据库配置
    DATABASES = {
         'default': {
             'ENGINE': 'django.db.backends.mysql',
             'NAME': 'mysite4', # 数据库名
             'USER':'dbuser', # 你设置的用户名 - 非root用户
             'PASSWORD':'password', # # 换成你自己密码
             'HOST': 'db', # 注意:这里使用 db 容器的名称,docker会自动解析成ip
             'PORT':'3306', # 端口
        }
    }
    
    # 静态文件路径
    import os
    STATIC_ROOT = os.path.join(BASE_DIR, 'static')
    STATIC_URL = "/static/"
    
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    MEDIA_URL = "/media/"
  7. 使用 Docker Compose 构建镜像和启动容器。

    • 构建镜像:docker compose build
    • 查看镜像:docker images
    • 创建容器并在后台运行:docker compose up -d
    • 查看运行中的容器列表:docker ps
    • 停止容器:docker compose stop,可在后面添加docker-compose.yml中定义的服务名称来指定要停止的容器
    • 启动已停止的容器:docker compose start
    • 停止并删除所有容器:docker compose down
    • 删除所有容器和关联镜像:docker-compose down --rmi all
    • 容器启动后,可以用浏览器访问宿主机的 IP 地址,检查是否能打开页面。
  8. 如果服务出现异常,可以在以下位置查看日志文件:

    • uWSGI 的日志可以在宿主机mysite4_root/compose/uwsgi中查看到;
    • Nginx 的日志可以在宿主机mysite4_root/compose/nginx/log中查看到;
    • MySQL 的数据存放在宿主机的/var/lib/docker/volumes/mysite4_root_mysite4_db_vol目录,即使容器被删除,数据也会一直保留,维护人员也可以对数据进行备份。

六、改进容器集群

在前面创建了一个由三个容器组成的容器集群,但是其中还存在一些问题:

  • MySQL 的配置信息写在 Docker Compose 的配置文件中
  • Nginx 的配置文件没有挂载到宿主机
  • 容器间通信使用的是旧的 links 方式
  • 启动 Django 应用时没有异常处理

下面着重描写需要修改的地方,项目的布局与上一个项目是一致的。

  1. 容器间通信
    在上一个项目中,容器间通信使用的是 links 的方式实现,这种方式 docker 官方已经不推荐使用,所以要用桥接网络的形式来实现容器间的网络连接。

    • 修改docker-compose.yml
    version: "3"
     
    volumes: # 自定义数据卷,位于宿主机/var/lib/docker/volumes
      mysite4_db_vol: # 定义数据卷同步容器内mysql数据
      mysite4_media_vol: # 定义数据卷同步media文件夹数据
    
    # 设置虚拟网络,分配给各个容器,分配了同一个虚拟网络的容器就可以互通了
    networks:
      nginx_network:
        driver: bridge
      db_network:
        driver: bridge
     
    services:
      db:
        # 前面的部分省略
        # 去掉 links 部分,绑定虚拟网络
        networks:
          - db_network
        restart: always     # 故障时自动重启容器
     
      web:
        # 前面的部分省略
        # 去掉 links 部分,绑定虚拟网络
        networks:
          - db_network
          - nginx_network
        depends_on: # 建立依赖关系,指定容器启动后才能启动该容器
          - db
        restart: always
        tty: true
        stdin_open: true
     
      nginx:
        # 前面的部分省略
        # 去掉 links 部分,绑定虚拟网络
        networks:
          - nginx_network
        depends_on:
          - web
        restart: always
  2. 修改配置文件
    有些配置信息,例如用户名、密码等,可以用单独的文件来存储,构建镜像的时候再从文件中读取,这样在部署不同的项目时,只需要修改这些配置信息就可以了。下文以 db 容器的配置信息来举例。

    • 修改docker-compose.yml
    services:
      db:
        image: mysql:8.0
        # 从文件中读取环境变量
        env_file:
          - ./compose/mysql/.env
    • mysite4_root/compose/mysql目录下创建.env文件,写入数据库的配置信息:
    MYSQL_ROOT_PASSWORD=123456
    MYSQL_USER=dbuser
    MYSQL_DATABASE=mysite4
    MYSQL_PASSWORD=password
    • 修改 web 容器的 dockerfile:
    FROM python:3.9-alpine3.16
    MAINTAINER Yulin Wei
    ENV PYTHONUNBUFFERED 1
    COPY pip.conf /root/.pip/pip.conf
    # 为应用根目录设置环境变量
    ENV APP_HOME=/var/www/html/mysite4
    RUN mkdir -p $APP_HOME
    WORKDIR $APP_HOME
    COPY requirements.txt $APP_HOME
    RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
    RUN apk add --no-cache build-base musl-dev linux-headers mariadb-dev netcat-openbsd    # 安装 netcat 软件包
    RUN pip install -r requirements.txt
    ENTRYPOINT /bin/sh ./start.sh
  3. 修改启动脚本
    web 容器内安装的应用是通过start.sh这个脚本文件来启动的,如果在这个脚本执行的时候,db 容器内的数据库尚未完成启动,就会抛出连接数据库失败的错误信息,所以在脚本文件中添加一个循环,当连接不上数据库时,等待三秒钟再尝试连接。

    • 修改start.sh
    # 尝试连接数据库,连接不上时等待 3 秒再执行
    while ! nc -z db 3306 ; do
        echo "Waiting for the MySQL Server"
        sleep 3
    done
    
    python3 manage.py collectstatic --noinput
    python3 manage.py makemigrations
    python3 manage.py migrate
    uwsgi --ini uwsgi.ini
    
    # 无限循环,防止脚本执行完成后 uWSGI 随之退出
    tail -f /dev/null
  4. 挂载宿主机上的 Nginx 配置文件到容器中,允许在容器运行时动态修改配置文件。

    • 修改docker-compose.yml
    # 前面的内容省略
    nginx:
        build: ./compose/nginx
        ports:
           - "80:80"
           - "443:443"
        expose:
           - "80"
        volumes:
           - ./mysite4/static:/usr/share/nginx/html/static # 挂载静态文件
           - ./compose/nginx/ssl:/usr/share/nginx/ssl # 挂载ssl证书目录
           - ./compose/nginx/log:/var/log/nginx # 挂载日志
           - mysite4_media_vol:/usr/share/nginx/html/media # 挂载媒体文件
           - ./compose/nginx/nginx.conf:/etc/nginx/conf.d/nginx.conf # 挂载配置文件
    # 后面的内容省略
    • 修改compose/nginx/dockerfile,将复制配置文件的部分删除。
  5. 构建镜像和启动容器,使用浏览器查看是否能访问页面

    • 构建镜像:docker compose build
    • 查看镜像:docker images
    • 创建容器并在后台运行:docker compose up -d
    • 查看运行中的容器列表:docker ps
  6. 进入容器,创建管理员用户,检查数据库相关功能是否正常

    • 执行docker compose exec web /bin/sh命令进入 web 容器
    • 进入容器后,执行python3 manage.py createsuperuser,根据提示输入用户名、邮箱和密码
    • 创建用户后,使用浏览器输入宿主机 IP 地址/admin进入管理页面,查看是否能用刚才创建的用户登录

参考资料来源:
运维工程师视频教程
Docker 部署 Django 教程

docker 云计算 Linux 数据库 运维
Theme Jasmine by Kent Liao 粤ICP备2021153836号