情况 | 内核行为 | 当无可用内存时 |
---|---|---|
硬内存保护 | OOM killer | |
尽最大努力保护内存 | ||
常规状态 | ||
尽可能维持更小内存 | ||
尽可能回收自身 | OOM killer |
内存回收压力(memory reclaim pressure) 指操作系统在内存不足时,通过回收内存来释放更多的空闲内存页。这个过程中,数据会从 RAM 中删除(如果可以重新获取)或复制到交换文件中(以便数据可以重新获取)。
pids.xxx
线程数限制io.xxx
硬盘读写限制cpuset.xxx
NUMA 节点限制rdma.xxx
RDMA 资源限制infalloc.cpp
#include <bits/stdc++.h>
using namespace std;
int total;
int main(){
while (char* f=(char*)malloc(1024*1024)){
for (int i=0;i<1024*1024;i++)
f[i]=rand();
total++;
cout<<total<<"mb in total"<<endl;
}
cout<<strerror(errno)<<endl;
}
loop.cpp
#include <bits/stdc++.h>
using namespace std;
void loop(){
while (1);
}
int main(){
vector<jthread> threads;
for (int i=0;i<32;i++)
threads.emplace_back(loop);
}
g++ infalloc.cpp -o infalloc
g++ loop.cpp -o loop
echo $$ #查看当前终端进程PID
cd /sys/fs/cgroup
mkdir test #创建cgroups
cd test
sudo bash -c "echo $PID >> cgroup.procs" #分配进程至/test
vim cpu.max
vim memory.max
使用 top 等工具查看资源占用吧
cgroup namespace隔离cgroup挂载和设施,在创建cgroup ns时会将当前进程的cgroup控制器设为新cgroup ns的父亲。/proc/self/cgroup
变成0::/
以隔离cgroup设施。但是原先的cgroup控制器也对新的cgroup ns的所有进程生效。
namespace 解决了资源隔离问题;
cgroups 解决了资源配额问题;
但是这仅仅解决了容器如何运行起来的问题,既然容器还是一个“软件”,我们还需要为容器添加版本控制。
对文件系统进行版本控制,怎么实现?
一图胜千言
mount -t overlay overlay -o lowerdir=/lower1:/lower2,upperdir=/upper,workdir=/work /merged
/merge=
/upper
/lower2
/lower1
lower层只读,对/merge的修改集中在upper上,work为临时目录。
秒级启动
运行一个软件=目录结构+共享库文件+可执行文件+配置文件
传统 Linux 发行版
改配置文件麻烦,软件包版本管理更麻烦...
万一哪一天出错了还不能回滚配置文件...
How to improve?
用户级包管理器,内部使用namespace+cgroup维护软件沙箱环境,达成跨不同发行版运行软件。
将这一切文本化,并辅以可选的版本控制。configuration.nix
:
{ config, ...}: {
services.nginx = {
enable = true;
virtualHosts."blog.example.com" = {
enableACME = true;
forceSSL = true;
root = "/var/www/blog";
locations."~ \.php$".extraConfig = ''
fastcgi_pass unix:${config.services.phpfpm.pools.mypool.socket};
fastcgi_index index.php;
'';
};
};
services.mysql = {
enable = true;
package = pkgs.mariadb;
};
services.phpfpm.pools.mypool = {
user = "nobody";
settings = {
pm = "dynamic";
"listen.owner" = config.services.nginx.user;
"pm.max_children" = 5;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 1;
"pm.max_spare_servers" = 3;
"pm.max_requests" = 500;
};
};
cgroups+namespace+overlayfs
docker pull <Image> #下载一个镜像
docker run --it --rm <Image> #运行一个容器并分配终端tty设备,结束后就销毁
docker run -d <Image> #后台运行一个容器
docker ps -a #查看所有容器
docker start/stop <Container> #启动一个容器
docker exec <Conatiner> #在容器中运行命令(Ctrl+C退出;Ctrl+P Ctrl+Q分离)
其中Image格式为NAME[:TAG|@DIGEST]
,tag为版本号。常见如debian:latest
表示最新版本的debian镜像。
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
ENV http_proxy=http://127.0.0.1:7890
ENV https_proxy=http://127.0.0.1:7890
RUN pip install --no-cache-dir \
-r requirements.txt \
-i https://pypi.tuna.tsinghua.edu.cn/simple
#shell-like命令行
COPY . .
CMD [ "uvicorn" , "webui:app" , "--reload" , "--host" , "0.0.0.0" ]
docker build 'https://github.com/docker/rootfs.git#branch:docker'
SQL-like语法,带缓存
version: '3'
services:
rsshub: #实际容器名为动态分配,用户无需管理
image: diygod/rsshub
restart: always
ports: #端口映射,主机:容器
- '1200:1200'
environment: #环境变量
NODE_ENV: production
CACHE_TYPE: redis
REDIS_URL: 'redis://redis:6379/'
PUPPETEER_WS_ENDPOINT: 'ws://browserless:3000'
depends_on: #依赖其他容器
- redis
- browserless
browserless:
image: browserless/chrome
restart: always
ulimits:
core:
hard: 0
soft: 0
redis:
image: redis:alpine
restart: always
volumes: #映射存储
- redis-data:/data
volumes:
redis-data:
docker volume create redis-data #创建redis-data介质
docker-compose up -d #使用当前目录的docker-compose.yml创建镜像
docker-compose down -d
搭建容器集群以实现分布式系统。
namespace/cgroup/overlayfs is everywhere!
namespace
cgroups
overlayfs