Linux Namespace浅析
https://zhuanlan.zhihu.com/p/102364063
Namespace是将内核的全局资源做封装,使得每个Namespace都有一份独立的资源,因此不同的进程在各自的Namespace内对同一种资源的使用不会互相干扰。
Linux对Namespace的操作,主要是通过clone、setns和unshare这3个系统调用来完成的,clone创建新进程时,接收一个叫flags的参数,这些flag包括CLONE_NEWNS、CLONE_NEWIPC、CLONE_NEWUTS、CLONE_NEWNET(Mount namespace)、CLONE_NEWPID
和CLONE_NEWUSER
,用于创建新的namespace,这样clone创建出来新进程之后就属于新的namespace了,后续新进程创建的进程默认属于同一namespace。
如果想要给已存在进程设置新的namespace,可通过unshare函数(long unshare(unsigned long flags)
)完成设置,其入参flags表示新的namespace。当想要给已存在进程设置已存在的namespace,可通过setns函数(int setns(int fd, int nstype)
)来完成设置,每个进程在procfs目录下存储其相关的namespace信息,可找到已存在的namesapce,然后通过setns设置即可:
[root@centos ~]# ls -l /proc/10401/ns
总用量 0
lrwxrwxrwx 1 root root 0 1月 12 11:36 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 1月 12 11:36 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 1月 12 11:36 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 1月 12 11:36 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 1月 12 11:36 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 1月 12 11:36 uts -> uts:[4026531838]
上述每个虚拟文件对应该进程所处的namespace,如果其他进程想进入该namespace,open该虚拟文件获取到fd,然后传给setns函数的fd入参即可,注意虚拟文件type和nstype要对应上。
目前Linux内核总共支持以下6种Namespace,分别是IPC、Network、Mount、PID、UTS、User:
- IPC:隔离System V IPC和POSIX消息队列。
- Network:隔离网络资源。
- Mount:隔离文件系统挂载点。
- PID:隔离进程ID。
- UTS:隔离主机名和域名。
- User:隔离用户ID和组ID。
IPC
IPC也就是进程间通信,Linux下有多种进程间通信,比如socket、共享内存、Posix消息队列和SystemV IPC等,这里的IPC namespace针对的是SystemV IPC和Posix消息队列,其会用标识符表示不同的消息队列,进程间通过找到标识符对应的消息队列来完成通信,IPC namespace做的事情就是相同的标识符在不同namespace上对应不同的消息队列,这样不同namespace的进程无法完成进程间通信。
Network
Network Namespace隔离网络资源,每个Network Namespace都有自己的网络设备、IP地址、路由表、/proc/net目录、端口号等。每个Network Namespace会有一个loopback设备(除此之外不会有任何其他网络设备)。因此用户需要在这里面做自己的网络配置。IP工具已经支持Network Namespace,可以通过它来为新的Network Namespace配置网络功能。
Mount
Mount namesapce用户隔离文件系统挂载点,每个进程能看到的文件系统都记录在/proc/$$/mounts
里。在创建了一个新的Mount Namespace后,进程系统对文件系统挂载/卸载的动作就不会影响到其他Namespace。
PID
PID Namespace用于隔离进程PID号,这样一来,不同的Namespace里的进程PID号就可以是一样的了。当创建一个PID Namespace时,第一个进程的PID号是1,也就是init进程。init进程有一些特殊之处,例如init进程需要负责回收所有孤儿进程的资源。另外,发送给init进程的任何信号都会被屏蔽,即使发送的是SIGKILL信号,也就是说,在容器内无法“杀死”init进程。
UTS
UTS为Unix Timesharing System的简称,包含内存名称、版本、底层体系结构等信息,UTS Namespace用于对主机名和域名进行隔离,也就是uname系统调用使用的结构体structutsname里的nodename和domainname这两个字段,UTS这个名字也是由此而来的。为什么需要uts namespace呢,因为为主机名可以用来代替IP地址,比如局域网通过主机名访问机器。
User
User Namespace用来隔离用户资源,比如一个进程在Namespace里的用户和组ID与它在host里的ID可以不一样,这样可以做到,一个host的普通用户可以在该容器(user namespace)下拥有root权限,但是它的特权被限定在容器内。(容器内的这类root用户,实际上还是有很多特权操作不能执行,基本上如果这个特权操作会影响到其他容器或者host,就不会被允许)
Linux Cgroup浅析
https://zhuanlan.zhihu.com/p/102372680
cgroups是Linux下控制一个(或一组)进程的资源限制机制,全称是control groups,可以对cpu、内存等资源做精细化控制,比如目前很多的Docker在Linux下就是基于cgroups提供的资源限制机制来实现资源控制的;除此之外,开发者也可以指直接基于cgroups来进行进程资源控制,比如8核的机器上部署了一个web服务和一个计算服务,可以让web服务仅可使用其中6个核,把剩下的两个核留给计算服务。cgroups cpu限制除了可以限制使用多少/哪几个核心之外,还可以设置cpu占用比(注意占用比是各自都跑满情况下的使用比例,如果一个cgroup空闲而另一个繁忙,那么繁忙的cgroup是有可能占满整个cpu核心的)。
cgroups概念
从实现角度来看,cgroups实现了一个通用的进程分组框架,不同资源的具体管理工作由各cgroup子系统来实现,当需要多个限制策略比如同时针对cpu和内存进行限制,则同时关联多个cgroup子系统即可。
cgroups子系统
cgroups为每种资源定义了一个子系统,典型的子系统如下:
- cpu 子系统,主要限制进程的 cpu 使用率。
- cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
- cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
- memory 子系统,可以限制进程的 memory 使用量。
- blkio 子系统,可以限制进程的块设备 io。
- devices 子系统,可以控制进程能够访问某些设备。
- net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
- freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
- ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。
每个子系统都是定义了一套限制策略,它们需要与内核的其他模块配合来完成资源限制功能,比如对 cpu 资源的限制是通过进程调度模块根据 cpu 子系统的配置来完成的;对内存资源的限制则是内存模块根据 memory 子系统的配置来完成的,而对网络数据包的控制则需要 Traffic Control 子系统来配合完成。
Docker安装
yum安装
https://developer.aliyun.com/mirror/docker-ce
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新并安装Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务
sudo service docker start
# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ce.repo
# 将[docker-ce-test]下方的enabled=0修改为enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
yum list docker-ce.x86_64 --showduplicates | sort -r
Loading mirror speeds from cached hostfile
Loaded plugins: branch, fastestmirror, langpacks
docker-ce.x86_64 3:20.10.9-3.el7 docker-ce-stable
docker-ce.x86_64 3:20.10.8-3.el7 docker-ce-stable
docker-ce.x86_64 3:20.10.7-3.el7 docker-ce-stable
docker-ce.x86_64 3:20.10.6-3.el7 docker-ce-stable
docker-ce.x86_64 3:20.10.5-3.el7 docker-ce-stable
docker-ce.x86_64 3:20.10.4-3.el7 docker-ce-stable
docker-ce.x86_64 3:20.10.3-3.el7 docker-ce-stable
Available Packages
# Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
sudo yum -y install docker-ce.x86_64 3:20.10.12-3.el7
cat /etc/docker/daemon.json
{
"insecure-registries": ["http://10.0.2.13"],
"live-restore": true
}
docker 常用命令
https://docs.docker.com/engine/reference/commandline/docker/
# 显示 Docker 系统信息
docker info
# 拉取 centos:7 镜像
docker pull centos:7
# 根据当前目录的Dockerfile构建app:v1镜像
docker build -t app:v1 .
# 在容器 57a77a350082 里执行 ls /etc/blackbox_exporter/config.yml 命令
docker exec -it 57a77a350082 ls /etc/blackbox_exporter/config.yml
# 用镜像nginx:latest 启动容器
docker run --name nginx nginx:latest
# 将容器 57a77a350082 中的 /etc/blackbox_exporter/config.yml 文件复制到本地 /tmp 目录下
docker cp 57a77a350082:/etc/blackbox_exporter/config.yml /tmp
# 显示目前正在运行的容器
docker ps
# 显示当前阶段所有的容器
docker ps -a
# 停止容器 807a9a776ac6
docker stop 807a9a776ac6
# 显示容器f49fc00fe206的后100行日志
docker logs -n 100 f49fc00fe206
Harbor
单机部署
# docker安装参考上文
# 安装docker-compose
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod u+x /usr/local/bin/docker-compose
# 下载Harbor离线安装包
curl -O -L https://github.com/goharbor/harbor/releases/download/v2.4.1/harbor-offline-installer-v2.4.1.tgz
tar -xzf harbor-offline-installer-v2.4.1.tgz -C /opt/
cd /opt/harbor
# 配置 harbor.yml
harbor]# vi harbor.yml
# 修改主机名
hostname: 10.0.2.11
# 禁用https访问
# https:
# # https port for harbor, default is 443
# port: 443
# # The path of cert and key files for nginx
# certificate: /your/certificate/path
# private_key: /your/private/key/path
# 创建数据目录
mkdir /data
# 安装选项
harbor]# ./install.sh --help
Note: Please set hostname and other necessary attributes in harbor.yml first. DO NOT use localhost or 127.0.0.1 for hostname, because Harbor needs to be accessed by external clients.
Please set --with-notary if needs enable Notary in Harbor, and set ui_url_protocol/ssl_cert/ssl_cert_key in harbor.yml bacause notary must run under https.
Please set --with-trivy if needs enable Trivy in Harbor
Please set --with-chartmuseum if needs enable Chartmuseum in Harbor
# 执行安装
harbor]# ./install.sh --with-trivy
[Step 0]: checking if docker is installed ...
Note: docker version: 20.10.12
[Step 1]: checking docker-compose is installed ...
Note: docker-compose version: 1.29.2
[Step 2]: loading Harbor images ...
......
[Step 3]: preparing environment ...
......
[Step 4]: preparing harbor configs ...
......
[Step 5]: starting Harbor ...
Creating network "harbor_harbor" with the default driver
......
✔ ----Harbor has been installed and started successfully.----
HA部署
主机规划
IP | 角色 |
---|---|
10.0.2.11 | harbor-db、redis 、haproxy |
10.0.2.12 | harbor |
10.0.2.13 | harbor |
所有机器关闭防火墙、SELinux
systemctl stop firewalld && systemctl disable firewalld
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
配置外部数据库
# 和单机部署一样,解压安装包
tar -xzf harbor-offline-installer-v2.4.1.tgz -C /opt/
cd /opt/harbor
# 配置 harbor.yml,修改hostname,关闭https
harbor]# vi harbor.yml
hostname: 10.0.2.11
# https related config
#https:
# # https port for harbor, default is 443
# port: 443
# # The path of cert and key files for nginx
# certificate: /your/certificate/path
# private_key: /your/private/key/path
# 执行 ./prepare 脚本,成功后会生成 docker-compose.yml 文件
harbor]# ./prepare
# 修改 docker-compose.yml 文件,只保留 log,postgresql,redis 这3个service,postgresql映射5432端口,redis映射6379端口
harbor]# cat docker-compose.yml
version: '2.3'
services:
log:
image: goharbor/harbor-log:v2.4.1
container_name: harbor-log
restart: always
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- SETGID
- SETUID
volumes:
- /var/log/harbor/:/var/log/docker/:z
- type: bind
source: ./common/config/log/logrotate.conf
target: /etc/logrotate.d/logrotate.conf
- type: bind
source: ./common/config/log/rsyslog_docker.conf
target: /etc/rsyslog.d/rsyslog_docker.conf
ports:
- 127.0.0.1:1514:10514
networks:
- harbor
postgresql:
image: goharbor/harbor-db:v2.4.1
container_name: harbor-db
restart: always
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- SETGID
- SETUID
volumes:
- /data/database:/var/lib/postgresql/data:z
ports:
- 5432:5432
networks:
harbor:
env_file:
- ./common/config/db/env
depends_on:
- log
logging:
driver: "syslog"
options:
syslog-address: "tcp://localhost:1514"
tag: "postgresql"
shm_size: '1gb'
redis:
image: goharbor/redis-photon:v2.4.1
container_name: redis
restart: always
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
volumes:
- /data/redis:/var/lib/redis
ports:
- 6379:6379
networks:
harbor:
depends_on:
- log
logging:
driver: "syslog"
options:
syslog-address: "tcp://localhost:1514"
tag: "redis"
networks:
harbor:
external: false
# 启动数据库,默认会初始化 notaryserver,notarysigner,registry 这3个数据库
harbor]# docker-compose up -d
harbor]# less /var/log/harbor/postgresql.log
...
harbor-db | /docker-entrypoint.sh: running /docker-entrypoint-initdb.d/initial-notaryserver.sql
harbor-db | CREATE DATABASE
harbor-db | /docker-entrypoint.sh: running /docker-entrypoint-initdb.d/initial-notarysigner.sql
harbor-db | CREATE DATABASE
harbor-db | /docker-entrypoint.sh: running /docker-entrypoint-initdb.d/initial-registry.sql
harbor-db | CREATE DATABASE
harbor-db | You are now connected to database "registry" as user "postgres".
...
harbor]# docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------------
harbor-db /docker-entrypoint.sh 96 13 Up (healthy) 0.0.0.0:5432->5432/tcp,:::5432->5432/tcp
harbor-log /bin/sh -c /usr/local/bin/ ... Up (healthy) 127.0.0.1:1514->10514/tcp
redis redis-server /etc/redis.conf Up (healthy) 0.0.0.0:6379->6379/tcp,:::6379->6379/tcp
配置 harbor 节点
harbor 节点 除了hostname配置为各自节点IP,其他配置保持一致
harbor]# vi harbor.yml
# 修改主机名
hostname: 10.0.2.12
# 关闭https访问
# https:
# # https port for harbor, default is 443
# port: 443
# # The path of cert and key files for nginx
# certificate: /your/certificate/path
# private_key: /your/private/key/path
# 配置外部数据库
# Uncomment external_database if using external database.
external_database:
harbor:
host: 10.0.2.11
port: 5432
db_name: registry
username: postgres
password: root123
ssl_mode: disable
notary_signer:
host: 10.0.2.11
port: 5432
db_name: notarysigner
username: postgres
password: root123
ssl_mode: disable
notary_server:
host: 10.0.2.11
port: 5432
db_name: notaryserver
username: postgres
password: root123
ssl_mode: disable
# Uncomment external_redis if using external Redis server
external_redis:
host: 10.0.2.11:6379
registry_db_index: 1
jobservice_db_index: 2
chartmuseum_db_index: 3
trivy_db_index: 5
idle_timeout_seconds: 30
# 执行安装
harbor]# ./install.sh --with-trivy
配置仓库复制
先配置 仓库管理
再配置 复制管理
图形界面操作,参考文档,不再一一演示
https://goharbor.io/docs/2.4.0/administration/configuring-replication/
配置Haproxy代理
~]# yum install haproxy -y
# 增加 harbor代理配置段
~]# vi /etc/haproxy/haproxy.cfg
# 启用统计页面
listen stats
bind *:9090
stats enable
stats uri /admin?stats
stats realm HAPorxy\ Stats\ Page
stats auth admin:admin
stats admin if TRUE
# harbor 代理
listen harbor
bind *:80
mode http
balance source
server node02 node02:80 check
server node03 node03:80 check
harbor-db 备份与恢复
# 进入harbor-db容器
docker exec -it harbor-db bash
postgres [ / ]$ id
uid=999(postgres) gid=999(postgres) groups=999(postgres)
postgres [ / ]$ ls -l /var/lib/postgresql/data
total 4
drwx------. 19 postgres postgres 4096 Dec 28 07:13 pg13
# 执行 psql
postgres [ /tmp ]$ psql -U postgres
psql (13.5)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
--------------+----------+----------+-------------+-------------+-----------------------
notaryserver | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =Tc/postgres +
| | | | | postgres=CTc/postgres+
| | | | | server=CTc/postgres
notarysigner | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =Tc/postgres +
| | | | | postgres=CTc/postgres+
| | | | | signer=CTc/postgres
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
registry | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
(6 rows)
postgres=# \c registry
You are now connected to database "registry" as user "postgres".
registry=# \d
List of relations
Schema | Name | Type | Owner
--------+------------------------------------+----------+----------
public | access | table | postgres
public | access_access_id_seq | sequence | postgres
public | alembic_version | table | postgres
public | artifact | table | postgres
public | artifact_blob | table | postgres
...
(90 rows)
registry=# select username from harbor_user;
username
-----------
anonymous
admin
yangxy
(3 rows)
# 备份数据库
docker exec harbor-db /bin/sh -c "pg_dumpall -U postgres -c" > /tmp/dumpall.sql
# 将备份的sql复制到容器内
docker cp /tmp/dumpall.sql harbor-db:/tmp
# 容器内执行(执行数据恢复时先停掉harbor)
postgres [ /tmp ]$ psql -f dumpall.sql -U postgres