Cで作るコンテナもどき #2
はじめに
この記事は、FUNアドベントカレンダー2020の22日目の記事になります。
昨日はともかさんの学内限定初心者に優しいハッカソンFunLocksを運営した話でした。
今年はpart1とpart2それぞれ記事を書いているのですが、2つともともかさんの次になります。なんででしょうね。不思議。
環境
- debian buster
- kernel 4.19.0
さくらのクラウド上で実行しています。
今回で扱う範囲
namespace,chroot,cgroupを実装します。
今回、cgroupではcpuの制御しか行いません。
namespaceの切り分け
コンテナを作成するために、namespaceの切り分けを行う必要があります。これに対応したシステムコールはunshareとなります。
似たような機能を持つシステムコールにcloneがあります。
せっかくですので、ここでunshareとcloneの違いを簡単にまとめてからどちらを使うか考えましょう。
unshare
現在のプロセスに対して名前空間共有の制御を行う。
引数
-
int flags
与えられたflagに対応した資源を共有しません。 つまり、何も指定しないとすべて共有されるってことかな。
返り値 : int
成功時0, 失敗時-1
clone
子プロセスの作成と同時に名前空間共有制御を行う。
fork + unshareが混ざったイメージ
引数
-
unsigned long flags
与えられたflagに対応した資源を共有します。 -
void *child_stack
割愛 -
void *ptid
割愛 -
void *ctid
割愛 -
struct pt_regs *regs
割愛
なんかいっぱいありますね。
unshareとは違い子プロセスも作成するので、子プロセスの使用するメモリやその他諸々が必要になります。(めんどくさくて調べていないので詳しくは各自で)
返り値 : long
子プロセスのスレッドidが返されます。失敗時-1、子プロセスは作成されません。
結局どっち使うの?
すこし長くなってしまいましたが、今回はunshareを使用したいと思います。
理由としては以下となります。
- 引数と返り値がわかりやすい。
- cloneの説明にスレッドに使うことが多いみたいなこと書いてある。
実装してみる
#define _GNU_SOURCE
#include<sched.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
int main(){
const unsigned int UNSHARE_FLAGS = ( CLONE_FILES | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_NEWPID);
if (unshare(UNSHARE_FLAGS) < 0){
perror("unshare");
}
printf("ok\n");
return 0;
}
で名前空間を分離することができるとおもいます。
unshareの実行しかしておらず、エラーが出なかったことだけしか確認していないので、この段階ではちゃんと動いたかわかりません。
flagの説明は割愛します。UNSHAREに書いてあるので各自見てみてください。
rootの変更
namespaceを切り分けたあとはrootを変更してあげます。
システムコールのchrootを使います。
chroot
ルートディレクトリの変更。
引数で指定したpathが以降で'/'として扱われます。
引数
- const char* path
変更先のパス
返り値
成功時0, エラーの場合-1
実装してみる
#include<unistd.h>
#include<errno.h>
#include<stdio.h>
int main(){
char *argv[3];
argv[0] = "/";
argv[1] = NULL;
if( chroot("./test") < 0 ){
perror("chroot");
return 1;
}
if ( chdir("./test") < 0 ){
perror("chdir");
return 1;
}
if ( execve("/bin/ls", argv, NULL) < 0){
perror("execve");
return 1;
}
printf("ok\n");
return 0;
}
testというディレクトリを作成し、そこにchrootを行います。
その後/bin/ls
を実行するコードを書きました。
testは空のディレクトリなので、もちろん、/bin/ls
なんてないのでうまくいきません。
これでchrootがうまく機能しているというのが確認できます。(賢い確認方法ではない)
ちなみに、ホストマシンのものではないdebianのrootディレクトリを用意して、chrootした場合は正常に動きました。
リソースの制限
コンテナとして成り立たせるためにはリソースの制限を行う必要があります。例えば、メモリの使用量だとか、CPU利用の制限とか、いろいろです。
リソースの制限に関しては、システムコールがあるわけではなく、cgroupというLinuxの機能があります。
cgroupはv1とv2がありますが、今回はv2を使用します。
cgroup v2の細かい説明や仕様については長くなるのでここでは説明しません。
興味あるかたはぜひこちらをご覧になってください。
cgroupを用いたリソースの制限
cgroupを使えるようにするには以下の手順を踏む必要があります。
1. cgroupfsのマウント
2. cgroupの作成(ディレクトリの作成)
3. cgroup.procsへ制御するプロセスIDの書き込み
4. サブシステムの登
5. サブシステムへの制限
実装してみる
今回僕の環境では/sys/fs/cgroup
にすでにマウントされていたので行いません。
#include<sys/mount.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<stdio.h>
int main(){
//make cgroup
if( access("/sys/fs/cgroup/container", F_OK) < 0){
if( mkdir("/sys/fs/cgroup/container", 0644) < 0){
perror("mkdir");
return -1;
}
}
//set pid
int fd;
fd = open("/sys/fs/cgroup/container/cgroup.procs", O_WRONLY);
if( fd < 0 ){
perror("cgroup open");
return -1;
}
int _pid = getpid();
char buff[6];
snprintf(buff, 6 , "%d", _pid);
write(fd, buff, 6);
close(fd);
//set subsystem
fd = open("/sys/fs/cgroup/container/cgroup.subtree_control", O_WRONLY);
if( fd < 0 ){
perror("subsystem open");
return -1;
}
write(fd, "+cpu", 5);
close(fd);
//set cpu max
fd = open("/sys/fs/cgroup/container/cpu.max", O_WRONLY);
if( fd < 0 ){
perror("cpu open");
return -1;
}
write(fd, "10000", 6);
close(fd);
while(1){
fd = open("/dev/null", O_WRONLY);
write(fd,"hello world\n", 12);
close(fd);
}
}
現在のプロセスに対して、CPU最大10%までの制限をかけて、"hello world"を/dev/nullに無限に出力するものです。
(yesコマンドみたいな?)
これを実行した状態でプロセスのCPU使用率を確認してみましょう。
おおよそ10%に固定されていることがわかります。
多分上手く動いてますね。
補足
//set cpu max
fd = open("/sys/fs/cgroup/container/cpu.max", O_WRONLY);
if( fd < 0 ){
perror("cpu open");
return -1;
}
write(fd, "10000", 6);
close(fd);c
writeで"10000"とありますが、これはCPU時間のことです。今回、CPU時間の最大が100000なので1/10の"10000"となっています。
これでCPU使用率の設定ができます。
おわり
今回はnamespace,chroot,cgroupを実装しました。
コードの質問や、挙動が違う部分、間違ってる部分あればTwitterまでお願いします。
次回はいつになるかわかりませんが、capabilityを実装しようと思います。
参考にしたところ
Cで作るコンテナもどき #1
LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術
Man page of UNSHARE
Man page of CLONE
Man page of CHROOT
Author And Source
この問題について(Cで作るコンテナもどき #2), 我々は、より多くの情報をここで見つけました https://qiita.com/materialofmouse/items/7451f295fd789ab0f770著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .