【项目实战】高可用web集群的实现(nginx + keepalived)

【项目实战】高可用web集群的实现(nginx + keepalived)

项目概述

项目名称:使用nginx和keepalived搭建高可用web集群

项目环境:centOS 7.9,nginx,keepalived,ansible,prometheus,nfs,bind,ab压力测试工具等

项目简介

​ 本项目旨在通过使用nginx和keepalived搭建高可用的web集群,提供稳定、可靠的web服务。通过使用ansible进行自动化配置管理,实现快速部署和维护。

项目步骤

  1. 绘制集群网络拓扑结构图,实现集群网络规划。
  2. 为中控机安装ansible,并和其他相关机器配置免密通道,将服务器按职能在ansible中控机上进行分组。
  3. 编写ansible playbook,在web服务器上安装nginx,并通过ansible修改nginx的软件配置,包括长连接、并发数、worker进程数等。同时,完成prometheus服务的安装和部署。
  4. 使用ansible在负载均衡服务器上配置nginx的7层负载均衡,使用轮询调度算法,并在web集群上配置realip。
  5. 实现nfs服务器,在web服务器相关目录挂载nfs服务并设置开机自动挂载,实现数据一致性。
  6. 在被Prometheus监控的服务器上安装exporter程序,并使用Grafana显示Prometheus数据,完成Prometheus服务的告警功能。
  7. 在负载均衡服务器上安装keepalived,为两台负载均衡服务器实现双VIP配置(高可用实现)。
  8. 完成dns服务器配置,为整个集群提供dns解析,并对整个集群的配置结果进行验证。
  9. 客户端使用ab软件对web集群进行压力测试,评估web集群的性能。
  10. 对整个web集群进行优化,提升性能和可靠性。

项目步骤

集群网络规划实现

集群网络拓扑结构图如下所示:

【项目实战】高可用web集群的实现(nginx + keepalived)_第1张图片

为所有机器关闭防火墙和seLinux:

# 关闭防火墙
systemctl stop firewalld
# 临时关闭seLinux
setenforce 0
# 永久关闭
sed -i "/^SELINUX=/ s/enforcing/disabled/g" /etc/selinux/config

关闭防火墙之后,根据网络拓扑结构图为相关机器配置IP地址,以及按职责设置主机名称。

ansible中控机配置

1 在中控机安装ansible
# 通过epel源安装ansible
yum install epel-release -y
yum install ansible -y
2 为中控机和其他内网服务器配置ssh免密通道

由于ansible底层通过ssh协议进行集群管理,要求需要配置ssh免密

ssh教学博客:ssh详解–让你彻底学会ssh

# 生成密钥
ssh-keygen
# 将密钥传入相关服务器,完成免密通道
# -i指定密钥文件,可不接默认使用~/.ssh/id_rsa.pub
ssh-copy-id [-i ~/.ssh/id_rsa.pub] [email protected]
ssh-copy-id [email protected]
ssh-copy-id [email protected]
ssh-copy-id [email protected]
ssh-copy-id [email protected]
ssh-copy-id [email protected]
ssh-copy-id [email protected]
ssh-copy-id [email protected]

完成免密通道配置之后,使用ssh命令测试免密通道配置是否成功(部分验证结果如下):

web1服务器连接测试结果:

请添加图片描述

web2服务器连接测试结果:

请添加图片描述

web3服务器连接测试结果:

在这里插入图片描述

prometheus服务器连接测试结果:

请添加图片描述

3 修改ansible配置,按服务器职能进行分组

使用yum安装方式安装ansible,在/etc/ansible有一个hosts文件,其中包含ansible管理和控制的远程主机清单。

# 进入hosts文件
vim /etc/ansible/hosts

# 写入下列内容
[webservers]
192.168.94.100
192.168.94.101
192.168.94.102

[monitor]
192.168.94.105

[load_balance]
192.168.94.107
192.168.94.108

[nfs]
192.168.94.103

[dnsserver]
192.168.94.106

ansible并非守护进程,修改相关配置文件之后,并不需要重启服务

在完成配置之后,使用ansible的ping模块,通过控制机器ping 验证配置是否成功。

通过执行如下命令并查看结果:

ansible all -m ping

成功的预期结果如下所示:

your_host | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

验证结果如下所示:

【项目实战】高可用web集群的实现(nginx + keepalived)_第2张图片

使用ansible实现nginx和Prometheus的安装部署

1 为web机器安装nginx

1. 撰写nginx一键安装脚本

此处使用shell脚本以二进制编译的方式安装nginx

nginx二进制安装脚本如下所示:

#!/bin/bash

# 创建工作目录
mkdir -p /nginx
cd /nginx

# 创建nginx运行用户
useradd nginx -s /sbin/nologin

# 安装软件使用的依赖或辅助工具
yum install epel-release -y
yum install -y zlib zlib-devel openssl openssl-devel pcre pcre-devel gcc gcc-c++ autoconf automake make psmisc net-tools lsof vim geoip geoip-devel wget -y

# 下载nginx源码包
wget https://nginx.org/download/nginx-1.25.1.tar.gz

# 解压文件
tar xf nginx-1.25.1.tar.gz

cd nginx-1.25.1

# 指定部分nginx运行模块
./configure --prefix=/usr/local/bin/nginx --user=nginx --with-http_ssl_module --with-http_v2_module --with-threads --with-http_stub_status_module --with-stream

# 执行configure之后,会生成Makefile的编译蓝图文件
# 直接使用make即可进行安装
# 指定多线程安装
make -j 2
make install

# 将nginx的安装目录加入环境变量
echo "PATH=/usr/local/bin/nginx/sbin:$PATH" >> /etc/rc.local
echo "PATH=/usr/local/bin/nginx/sbin:$PATH" >> ~/.bashrc
chmod +x /etc/rc.local
export PATH=/usr/local/bin/nginx/sbin:$PATH

# 启动nginx
nginx

2. 使用ansible编写playbook将nginx安装脚本传送到web机器,并完成安装

在ansible编写上述one_key_install.sh脚本,然后编写playbook文件nginx_playbook.yaml

nginx_playbook.yaml文件内容如下所示:

- hosts: load_balance
  remote_user: root
  tasks:
    - name: transfer script
      copy:
        src: ./one_key_install.sh
        dest: ~/
      notify:
        - install nginx

  handlers:
    - name: install nginx
      shell: bash ~/one_key_install.sh

该playbook有两个任务,第一个将nginx安装脚本传到web机器上,其次运行nginx安装脚本。

# 检查playbook文件语法
ansible-playbook --syntax-check nginx_playbook.yaml
# 确认无误执行playbook
ansible-playbook nginx_playbook.yaml

任务执行完成之后确认web集群nginx软件安装完毕,再次编写playbook文件check_nginx.yaml

内容如下所示:

- hosts: webservers
  remote_user: root
  tasks:
  - name: check nginx
    shell: pgrep nginx
    register: nginx_process
    ignore_errors: true
    
  - name: Display nginx process status
    debug:
      msg: "Nginx process is {{ 'running' if nginx_process.rc == 0 else 'not running' }}"

执行结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第3张图片

3. 修改nginx相关参数配置

在ansible中控机上编写一份nginx.conf文件,在该文件中修改worker进程数,长连接数,日志文件格式等配置,然后通过ansible将该文件替换各web机器上的nginx配置文件

# 获取nginx.conf文件
ansible webservers -m fetch -a 'src=/usr/local/bin/nginx/conf/nginx.conf dest=~/'

nginx.conf主要文件修改内容如下所示:

...
# 修改worker进程数
worker_processes  2;
# 修改并发数
events {
    worker_connections 2048;
}
...
http{
...
    # 打开日志文件格式配置,并添加$http_x_real_ip
    log_format  main  '$remote_addr - $http_x_real_ip - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
	# 打开access_log logs/access.log main;
	access_log  logs/access.log  main;
...
	# 设置最大请求数
	keepalive_requests 10000;
...
}
...

使用ansible替换各机器nginx.conf文件

文件名:change_nginx_config.yaml,内容如下所示:

- hosts: webservers
  remote_user: root
  tasks: 
  - name: Change Nginx Config
    copy: 
      src: ~/nginx.conf
      dest: /usr/local/bin/nginx/conf/
      backup: yes
    notify: 
    - Reload Nginx service
      
  handlers:
  - name: Reload Nginx service
    command: nginx -s reload

此处为web集群安装nginx之后,不对其展示内容进行修改

2 搭建Prometheus监控服务器

编写一键安装脚本

脚本名称:one_key_install_prome.sh

编写完成之后与nginx安装一致,使用playbook文件通过ansible进行安装和运行

  • 此处已提前准备prometheus安装包
#!/bin/bash

# 关闭防火墙和selinux
systemctl stop firewalld
systemctl disable firewalld
sed -i "/^SELINUX/ s/enforcing/disabled/" /etc/selinux/config

# 创建工作目录
mkdir /prom

# 解压安装文件并将安装文件传入工作目录
tar xf prometheus-2.45.0-rc.0.linux-amd64.tar.gz
mv prometheus-2.45.0-rc.0.linux-amd64 /prom/prometheus

# 将Prometheus制作为主机服务
cat  << EOF > /usr/lib/systemd/system/prometheus.service
[Unit]
Description=prometheus
[Service]
ExecStart=/prom/prometheus/prometheus --config.file=/prom/prometheus/prometheus.yml
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
# 刷新服务
systemctl daemon-reload

# 使用systemctl命令重新启动prometheus
systemctl start prometheus
systemctl enable prometheus

# 查看运行结果
systemctl status prometheus

prometheus的playbook安装文件:

- hosts: monitor
  remote_user: root
  tasks:
  - name: transfer script
    copy:
      src: ./one_key_install_prom.sh
      dest: ~/
    notify:
    - prometheus install

  handlers:
  - name: prometheus install
    shell: bash ~/one_key_insatll_prom.sh

确保playbook完美执行成功:

【项目实战】高可用web集群的实现(nginx + keepalived)_第4张图片

使用浏览器访问prometheus网页(192.168.94.105:9090),查看prometheus安装情况,以下结果即安装成功:

【项目实战】高可用web集群的实现(nginx + keepalived)_第5张图片

使用ansible在负载均衡服务器进行nginx 7层负载均衡配置

1 为负载均衡服务器安装nginx

在上一步为web集群安装nginx时,已经准备好playbook文件,此处直接运行命令进行安装即可:(需将组名改为对应主机组load_balance)

# 为负载均衡服务器安装nginx
ansible-playbook nginx_playbook.yaml

验证安装结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第6张图片

2 实现nginx 7层负载均衡

此处使用同样的方法,通过在ansible中控机上修改预设config文件再通过替换load balance机器的config文件来实现配置修改。

负载均衡机器nginx.config文件主要修改如下:

http{
......
    upstream webservers{        
        server 192.168.94.100;
        server 192.168.94.101;
        server 192.168.94.102;
    }
......
    server{
       ...
       location / {
           proxy_pass http://webservers;
           # 获取真实IP地址,以便做日志分析
           proxy_set_header X-Real-IP $remote_addr;
       }
       ...
    }
}

通过修改之前编写playbook文件(主要修改组名为load_balance)替换负载均衡机器nginx配置文件:

ansible-playbook change_nginx_config.yaml
3 验证负载均衡实现效果

此处使用的nginx为7层负载均衡,然后使用IP-hash的负载均衡方式,通过使用tail -f命令查看web服务器的nginx日志(access.log),命令如下:

tail -f /usr/local/bin/nginx/logs/access.log

通过该命令紧盯文件末尾,一直查看被访问日志,并且使用IP-hash的方式,同一IP地址访问会转发至同一台web服务器上,而且该日志中显示IP地址应该为负载均衡器IP地址。

使用浏览器访问其中一台负载均衡器,可得到如下结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第7张图片

至此,集群基本的web服务功能已经实现。

搭建nfs文件共享服务器

在完成基本的web功能之后,通过搭建一个nfs服务器,实现web服务器之间共享同一个页面显示文件(文件共享)

部署nfs服务:

所有需要使用nfs服务的机器均需要安装nfs服务,这也包括挂载nfs文件的web服务器

# 安装nfs服务
yum install nfs-utils -y

#为所有的web服务器安装nfs服务
ansible webservers -m yum -a "name=nfs-utils state=installed"

# 启动nfs服务
systemctl start nfs
systemctl enable nfs

安装完成nfs服务之后,设置共享目录,共享内容为nginx服务器显示页面

此处操作可在ansible中控机上执行。

# 在nfs服务器上设置挂载文件
# 通过修改nfs服务器上/etc/exports文件挂载提前准备好的web页面文件
# 创建挂载文件夹
mkdir /html
cat << EOF > /html/index.html

hello, world! This is my web

EOF

设置共享文件之后,通过编写yaml文件使用ansible为web服务器进行文件挂载

playbook文件内容如下所示:

- hosts: webservers
  remote_user: root
  tasks:
  - name: Monut NFS
    mount:
      path: /usr/local/bin/nginx/html
      src: 192.168.94.103:/html
      fstype: nfs
      opts: "defaults"
      state: mounted

上述挂载只是临时挂载,还需要实现开机自动挂载。

相关ansible playbook文件如下所示:

实现开机自动挂载

- hosts: webservers
  remote_user: root
  tasks:
  - name: Auto Monut
    lineinfile:
      dest: /etc/fstab
      line: "192.168.94.103:/html /usr/local/bin/nginx/html nfs defaults 0 0"
      state: present

挂载文件完成之后,可以通过访问对应网页进行验证,也可以使用df -Th查看web服务器的磁盘分区来验证配置结果。

df -Th命令查看结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第8张图片

浏览器通过访问负载均衡服务器查看结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第9张图片

完善监控服务器配置

在前面的步骤中,已经为监控服务器安装好了prometheus监控软件,而现在需要为每台服务器安装后门软件,为监控服务器提供监控数据来源,并确保各服务器正常运行。

1 在被监控服务器安装exporter软件

监控服务器的职责是监控整个集群的性能,所以需要为所有的服务器安装ndoe_exporter数据获取软件。

提前准备node_exporter安装包及其一键安装脚本,通过ansible传递至各个服务器上,并进行安装。

一键安装脚本one_key_install_node_exporter.sh如下所示:

#!/bin/bash

# 解压文件夹
tar -xf ~/node_exporter-1.6.0.linux-amd64.tar.gz

# 移动文件至工作目录
mv ~/node_exporter-1.6.0.linux-amd64 /node_exporter

# 将node_expoerter制作为主机服务
cat << EOF > /usr/lib/systemd/system/node_exporter.service
[Unit]
Description=prometheus

[Service]
ExecStart=/node_exporter/node_exporter --web.listen-address 0.0.0.0:9090
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

# 刷新服务
systemctl daemon-reload

# 使用systemctl命令启动node_exporter
systemctl start node_exporter
systemctl enable node_exporter

# 查看安装结果
systemctl status node_exporter

playbook文件如下所示:

- hosts: webservers:load_balance:nfs:dnsserver
  remote_user: root
  tasks:
  - name: Copy Node_Exports
    copy:
      src: ./node_exporter-1.6.0.linux-amd64.tar.gz
      dest: ~/
  - name: Copy One key Install Shell
    copy:
      src: ./one_key_install_node_exporter.sh
      dest: ~/
    notify:
    - install node exporter
    
  handlers:
  - name: install node exporter
    shell: bash one_key_install_node_exporter.sh

在为集群服务安装node_exporter之后,可通过访问IP地址:8090/metrics来确定配置结果

【项目实战】高可用web集群的实现(nginx + keepalived)_第10张图片

在确定安装node_exporter之后,还需对prometheus服务器进行配对服务器数据进行获取。

通过ansible替换prometheus配置的方法来修改prometheus配置文件。

首先获取prometheus配置文件

ansible monitor -m fetch -a "src=/prom/prometheus/prometheus.yml dest=~/"

prometheus配置文件添加如下配置:

scrape_configs:
  # The job name is added as a label `job=` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]

# 主要修改位置
  - job_name: "allservers"
    static_configs:
      - targets: ["192.168.94.100:9090","192.168.94.101:9090","192.168.94.102:9090","192.168.94.103:9090","192.168.94.104:9090","192.168.94.106:9090","192.168.94.107:9090","192.168.94.108:9090"]

使用ansible替换配置文件并重启prometheus服务:

- hosts: monitor
  remote_user: root
  tasks:
  - name: copy config
    copy:
      src: ~/prometheus.yml
      dest: /prom/prometheus
    notify:
    - restart service
    
  handlers:
  - name: restart service
    shell: systemctl restart prometheus

使用浏览器进入192.168.94.105:9090/targets网页验证服务器数据抓取结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第11张图片

2 安装Grafana

由于prometheus自带的web UI图片显示效果并不好,所以使用Grafana用于参数显示

在prometheus服务器上安装Grafana软件,playbook如下所示:

- hosts: monitor
  remote_user: root
  tasks:
  - name: Copy Grafana Rpm
    copy:
      src: ~/grafana-enterprise-10.1.1-1.x86_64.rpm
      dest: ~/
    notify:
    - install Grafana
    
  handlers:
  - name: install Grafana
    yum:
      name: grafana-enterprise-10.1.1-1.x86_64.rpm
      state: installed

后续还需在prometheus启动grafana服务,此处不再赘述

访问192.168.94.105:3000查看效果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第12张图片

然后在grafana网页配置prometheus源并且设置恰当数据可视化模板

【项目实战】高可用web集群的实现(nginx + keepalived)_第13张图片

Keepalived安装部署

在前面的步骤已经开启高可用服务器的nginx反向代理功能,但是并未配置keepalived软件,不好做消息转发,以及高可用未实现。

安装keepalived软件

使用ansible中控机控制高可用服务器安装keepalived

ansible load_balance -m yum -a "name=keepalived state=installed"
修改keepalived配置,实现双VIP架构

keepalived配置文件如下所示:

LB1(192.168.94.107):

! Configuration File for keepalived

global_defs {
   notification_email {
     [email protected]
     [email protected]
     [email protected]
   }
   notification_email_from [email protected]
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
  # vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_script chk_nginx{
    script "/etc/keepalived/chck_nginx.sh"
    interval 2
    weight -20
}

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 60
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.94.188
    }
    track_script {
        chk_nginx
    }
}

vrrp_instance VI_2 {
    state BACKUP
    interface ens33
    virtual_router_id 61
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.94.199
    }
    track_script {
        chk_nginx
    }
}

LB2(192.168.94.108):

! Configuration File for keepalived

global_defs {
   notification_email {
     [email protected]
     [email protected]
     [email protected]
   }
   notification_email_from [email protected]
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
  # vrrp_strict
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_script chk_nginx {
   script "/etc/keepalived/chck_nginx.sh"
   interval 2
   weight -20
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 60
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.94.188
    }
    track_script {
        chk_nginx
    }
}

vrrp_instance VI_2 {
    state MASTER
    interface ens33
    virtual_router_id 61
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.94.199
    }
     }
    track_script {
        chk_nginx
    }

}

当一台高可用机器的nginx挂掉之后,这台高可用机器不应该再拥有VIP,即优先级应该降低。

检测脚本如下所示:

#!/bin/bash

# 判断nginx是否运行
if pidof nginx >/dev/null 2>&1;then
exit 0
else
exit 1
fi

至此,除去dns服务外,所有服务部署完毕。

DNS服务安装部署

部署dns服务器的目的是为了将双vip绑定在同一个域名上,并提供相关的域名解析功能,再二外提供。

在一台服务器上安装dns服务,再将其服务器做成dns主域名服务器,对集群域名做解析:

# 安装dns服务
yum install bind* -y

# 运行dns服务
systemctl start named
systemctl enable named

对dns服务相关配置文件进行修改,对其他主机提供域名解析服务,并将该服务器搭建为主域名服务器。

修改named配置文件配置文件,文件路径:/etc/named.conf,部分配置文件如下所示(修改部分):

options {
        listen-on port 53 { any; };
        listen-on-v6 port 53 { any; };
        directory       "/var/named";
        dump-file       "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        memstatistics-file "/var/named/data/named_mem_stats.txt";
        recursing-file  "/var/named/data/named.recursing";
        secroots-file   "/var/named/data/named.secroots";
        allow-query     { any; };

通过修改/etc/named.rfc1912.zones文件添加主域名解析,为集群web网页设置自定义的域名(该项目域名设置为:webceshi.cn)。

/etc/named.rfc1912.zones配置文件修改如下(仅展示修改添加部分):

zone "webceshi.cn" IN {
	type master;
	file "/var/named/webceshi.cn.zone";
	allow-update { none; };
};

修改格式如下所示:

zone "需要解析的域名" IN {
        type master;
        file "解析文件";
        allow-update { none; };
};

添加解析文件,此处文件名称为:/var/named/webceshi.cn.zone

文件内容如下所示:

$TTL 1D
@	IN SOA	@ rname.invalid. (
					0	; serial
					1D	; refresh
					1H	; retry
					1W	; expire
					3H )	; minimum
	NS	@
	A	127.0.0.1
	AAAA	::1

webceshi.cn. IN A 192.168.94.188
webceshi.cn. IN A 192.168.94.199

一定要修改/var/named/webceshi.cn.zone的文件属组:

chown root:named /var/named/webceshi.cn.zone

配置文件修改完成重启named服务

systemctl restart named

将Linux客户机域名服务器IP地址修改为该域名服务器,并测试解析服务是否成功。

Linux机器nslookup命令查询结果:

【项目实战】高可用web集群的实现(nginx + keepalived)_第14张图片

Linux机器curl命令获取结果:

请添加图片描述

使用ab软件进行压力测试

在客户机上安装ab软件,然后通过ab对集群进行压力测试

安装ab软件

yum install httpd-tools -y

在客户机上执行压力测试命令

ab -n 10000 -c 10 http://192.168.94.107/

然后可以得到相关性能参数。

优化web集群

通过测试结果,对Linux机器的内核信息进行优化,可以通过使用ulimit命令查看内核参数。修改内核参数使得web服务器性能释放。

项目心得

  1. 对集群有了更深一步的了解,并且通过多次使用ansible在集群中分配任务,对自动化运维有了更深的理解。
  2. 对高可用和高性能有了更深的理解。通过进行压力测试,对机器的性能参数有了更深的理解。通过使用keepalived实现高可用,对keepalived的机制和相关设置有了很深的理解。
  3. 通过部署集群项目,提高了我的改错能力,而且通过实践,找到了平时的知识盲区,比如nginx的日志格式设置等。

你可能感兴趣的:(nginx,运维,linux,ansible,网络,负载均衡,服务器)