源碼見(jiàn):https://github.com/lovercode/GO_OJ.git灵妨,demo見(jiàn):https://codelover.me/run.html
Docker相關(guān)
compiler
和runner
都是運(yùn)行在docker容器中湾宙,可以進(jìn)行資源限制和隔離,主要的實(shí)現(xiàn)如下。
初始化compiler容器
主要是根據(jù)配置文件來(lái)啟動(dòng)多個(gè)compiler
的容器
func init() {
for num := uint64(0); num < conf.GlobalConfig.CompilerNum; num++ {
go initCompilerDocker(conf.GlobalConfig.CompilerDocker)
}
}
func initCompilerDocker(Conf conf.DockerConfig) {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.WithVersion("1.18"))
if err != nil {
panic(err)
}
if err != nil {
panic(err)
}
Config := container.Config{
Image: Conf.DockerName,
Cmd: Conf.Cmd,
WorkingDir: Conf.WorkDir,
}
var mounts []mount.Mount
for k, v := range Conf.DockerMount {
mounts = append(mounts, mount.Mount{
Type: mount.TypeBind,
Source: k,
Target: v,
})
}
HostConfig := container.HostConfig{
Mounts: mounts,
// Resources: Resources,
ReadonlyRootfs: Conf.ReadonlyRootfs,
// Runtime: "runsc",
}
if Conf.Memory >= 4194304 { //<4m設(shè)置無(wú)效
Resources := container.Resources{
Memory: Conf.Memory, //4m
CPUQuota: Conf.CpuQuota, //CPU資源的絕對(duì)限制,一般和CpuPeriod結(jié)合在一起,CpuQuota/CpuPeriod壶栋,就是能夠使用的核數(shù)
CPUPeriod: Conf.CpuPeriod,
}
HostConfig.Resources = Resources
}
resp, err := cli.ContainerCreate(ctx, &Config, &HostConfig, nil, "")
defer cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
if err != nil {
panic(err)
}
tag:
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
panic(err)
}
var res container.ContainerWaitOKBody
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
log.Println("編譯機(jī)出錯(cuò)退出")
goto end
}
case res = <-statusCh:
if res.StatusCode != 0 {
}
log.Println("編譯機(jī)退出,準(zhǔn)備重啟...")
goto tag
}
end:
log.Println("編譯機(jī)退出...")
}
- 配置文件
CompilerNum=1
CompileDataPath="/home/codelover/go/src/go_oj/bin/run"
[CompilerDocker]
Memory=-1
CpuQuota=100000
CpuPeriod=100000
DockerName = "compiler:v2"
Cmd = ["/bin/sh", "-c", "./compiler"]
WorkDir = "/compiler"
[CompilerDocker.DockerMount]
"/home/codelover/go/src/go_oj/bin" = "/compiler"
初始化所有runner容器
與初始化compiler
一樣普监,讀取配置直接初始化
func init() {
for _, v := range conf.GlobalConfig.RunnerDocker {
for num := 0; num < v.RunNum; num++ {
go initRunnerDocker(v)
}
}
}
func initRunnerDocker(Conf conf.DockerConfig) {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.WithVersion("1.37"))
if err != nil {
panic(err)
}
if err != nil {
panic(err)
}
Config := container.Config{
Image: Conf.DockerName,
Cmd: Conf.Cmd,
WorkingDir: Conf.WorkDir,
}
var mounts []mount.Mount
for k, v := range Conf.DockerMount {
mounts = append(mounts, mount.Mount{
Type: mount.TypeBind,
Source: k,
Target: v,
// ReadOnly: true,
})
}
Init := true
HostConfig := container.HostConfig{
Mounts: mounts,
ReadonlyRootfs: Conf.ReadonlyRootfs,
Init: &Init,
// Runtime: "runsc",
}
if Conf.Memory >= 4194304 { //<4m設(shè)置無(wú)效
Resources := container.Resources{
Memory: Conf.Memory, //4m
CPUQuota: Conf.CpuQuota, //CPU資源的絕對(duì)限制贵试,一般和CpuPeriod結(jié)合在一起,CpuQuota/CpuPeriod凯正,就是能夠使用的核數(shù)
CPUPeriod: Conf.CpuPeriod,
}
HostConfig.Resources = Resources
}
resp, err := cli.ContainerCreate(ctx, &Config, &HostConfig, nil, "")
defer cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
if err != nil {
panic(err)
}
tag:
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
panic(err)
}
var res container.ContainerWaitOKBody
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
log.Println("運(yùn)行機(jī)出錯(cuò)退出", err)
goto end
}
case res = <-statusCh:
if res.StatusCode != 0 {
}
log.Println("運(yùn)行機(jī)退出锡移,準(zhǔn)備重啟...", res)
goto tag
}
end:
log.Println("運(yùn)行機(jī)退出...")
}
- 配置如下
#c語(yǔ)言
[RunnerDocker.1]
RunNum=1
Memory=419430400
CpuQuota=50000
CpuPeriod=100000
ReadonlyRootfs=false
DockerName="alpine_c:latest"
Cmd = ["./runner","conf/runner_config_1.toml"]
WorkDir = "/runner"
[RunnerDocker.1.DockerMount]
"/home/codelover/go/src/go_oj/bin" = "/runner"
#php
[RunnerDocker.5]
RunNum=1
Memory=-1
CpuQuota=100000
CpuPeriod=100000
ReadonlyRootfs=false
DockerName="alpine_php:latest"
Cmd = ["./runner","conf/runner_config_5.toml"]
WorkDir = "/runner"
[RunnerDocker.5.DockerMount]
"/home/codelover/go/src/go_oj/bin" = "/runner"
dockerfile
- 編譯機(jī)需要有所有編譯環(huán)境
FROM frolvlad/alpine-glibc:latest
RUN apk add --no-cache gcc &&\
apk add --no-cache g++ &&\
apk add --no-cache php &&\
apk add --no-cache openjdk8 &&\
apk add --no-cache pyflakes &&\
apk add --no-cache go &&\
apk add --no-cache libstdc++
ENV PATH "$PATH:/usr/lib/jvm/default-jvm/bin"
- 運(yùn)行機(jī)
dockerfile
示例(php)
user
用于運(yùn)行用戶(hù)程序
FROM frolvlad/alpine-glibc:latest
RUN adduser -D user0 -G nogroup &&\
adduser -D user1 -G nogroup &&\
adduser -D user2 -G nogroup &&\
adduser -D user3 -G nogroup &&\
adduser -D user4 -G nogroup &&\
adduser -D user5 -G nogroup &&\
adduser -D user6 -G nogroup &&\
adduser -D user7 -G nogroup &&\
adduser -D user8 -G nogroup &&\
adduser -D user9 -G nogroup &&\
adduser -D user10 -G nogroup &&\
apk add --no-cache php7 &&\
apk add --no-cache libseccomp-dev