Snippets

sironekotoro Docker実践活用ガイド Chapter 10 DockerとJavaScriptでウェブサービスを作る(簡易オンラインジャッジシステム)

Created by sironekotoro last modified
# Docker実戦活用ガイド 初版第1刷 172p
# Perlを実行できるよう、Perlを追加
# イメージ作成時に ubuntu-devの名前をつける
# docker build -t ubuntu-dev .

FROM ubuntu
RUN apt-get update
RUN apt-get install -y perl
RUN apt-get install -y ruby
RUN apt-get install -y python
RUN apt-get install -y clang
RUN apt-get install -y time
RUN apt-get install -y binutils
// Docker実戦活用ガイド 初版第1刷 174p
// Perlを実行できるよう、Perlを追加

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var child_process = require('child_process');
var fs = require('fs');

app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: false }));

app.post('/api/run', function (req, res) {
    var language = req.body.language;
    var source_code = req.body.source_code;
    var input = req.body.input;

    var filename, execCmd;
    if (language === 'perl') {
        filename = 'Main.pl';
        execCmd = 'perl Main.pl';
    } else if (language === 'ruby') {
        filename = 'Main.rb';
        execCmd = 'ruby Main.rb';
    } else if (language === 'python') {
        filename = 'Main.py';
        execCmd = 'python Main.py';
    } else if (language === 'c') {
        filename = 'Main.c';
        execCmd = 'cc -Wall -o Main Main.c && ./Main';
    }

    // Create a container
    var dockerCmd =
        'docker create -i ' +
        '--net none ' +
        '--cpuset-cpus 0 ' +
        '--memory 512m --memory-swap 512m ' +
        '--ulimit nproc=10:10 ' +
        '--ulimit fsize=1000000 ' +
        '-w /workspace ' +
        'ubuntu-dev ' +
        '/usr/bin/time -q -f "%e" -o /time.txt ' +
        'timeout 3 ' +
        'su nobody -s /bin/bash -c "' +
        execCmd +
        '"';
    console.log("Running: " + dockerCmd);
    var containerId = child_process.execSync(dockerCmd).toString().substr(0, 12);
    console.log("ContainerID: " + containerId);

    // Copy the source code to container
    child_process.execSync('rm -rf /tmp/workspace && mkdir /tmp/workspace && chmod 777 /tmp/workspace');

    fs.writeFileSync('/tmp/workspace/' + filename, source_code);
    dockerCmd = "docker cp /tmp/workspace " + containerId + ":/";
    console.log("Running: " + dockerCmd);
    child_process.execSync(dockerCmd);

    // Start the container
    dockerCmd = "docker start -i " + containerId;
    console.log("Running: " + dockerCmd);
    var child = child_process.exec(dockerCmd, {}, function (error, stdout, stderr) {

        // Copy time command result
        dockerCmd = "docker cp " + containerId + ":/time.txt /tmp/time.txt";
        console.log("Running: " + dockerCmd);
        child_process.execSync(dockerCmd);
        var time = fs.readFileSync("/tmp/time.txt").toString();

        // Remove the container
        dockerCmd = "docker rm " + containerId;
        console.log("Running: " + dockerCmd);
        child_process.execSync(dockerCmd);

        console.log("Result: ", error, stdout, stderr);
        res.send({
            stdout: stdout,
            stderr: stderr,
            exit_code: error && error.code || 0,
            time: time,
        });
    });
    child.stdin.write(input);
    child.stdin.end();
});

app.listen(3000, function () {
    console.log('Listening on port 3000');
});

1
2
3
4
5
6
7
8
9
{
    "_comment": "Docker実戦活用ガイド 初版第1刷 174p",
    "name": "coderunner",
    "version": "1.0.0",
    "dependencies": {
        "body-parser": "^1.15.0",
        "express": "^4.13.4"
    }
}
Docker実戦活用ガイド 初版第1刷 189p
1行目の $HOME/coderunner はコード置いてある状況に応じて変更する

docker run -v $HOME/coderunner:$HOME/coderunner \
-v /usr/local/bin/docker:/usr/local/bin/docker \
-v /var/run/docker.sock:/var/run/docker.sock \
-w $HOME/coderunner \
-p 3000:3000 \
--rm -i -t node /bin/bash

-i -t があるので、そのままコンテナの中のbashに入る。
ここで
npm install
node app.js
を実行する
<html>

<head>
    <!-- Docker実戦活用ガイド 初版第1刷 182p -->
    <!-- Perlを実行できるよう、Perlを追加 -->
    
    <meta charset="UTF-8">
    <title>簡易オンラインジャッジ</title>
</head>

<body>
    <h1>コードをDockerコンテナで実行!</h1>
    言語:
    <select id="language">
        <option value="perl">Perl</option>
        <option value="ruby">Ruby</option>
        <option value="python">Python</option>
        <option value="c">C</option>
    </select>
    <br />

    ソースコード: <br />
    <div id="source_code" style="width: 100%; height: 200px; border: 1px solid"></div>

    標準入力: <br />
    <textarea id="input" style="width: 100%; height: 50px; resize: vertical;"></textarea>

    <button id="run_button" class="btn btn-primary">実行(Ctrl-Enter)</button>
    <br />
    <hr />

    標準出力:
    <pre><div id="stdout" style="background: lightgray"></div></pre>

    標準エラー出力:
    <pre><div id="stderr" style="background: lightgray"></div></pre>

    実行時間:
    <div id="time" style="background: lightgray"></div>

    終了コード:
    <div id="exit_code" style="background: lightgray"></div>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.3/ace.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.3/ext-language_tools.js"></script>
    <script src="run.js"></script>
    <script src="ui.js"></script>

</body>

</html>
// Docker実戦活用ガイド 初版第1刷 187p

/*
    Seubmit and run the code.
*/

function runCode() {
    $("#run_button").text("実行中...").prop("disabled", true);

    var language = $("#language").val();
    var source_code = aceEditor.getValue();
    var input = $("#input").val();
    $.ajax({
        url: "/api/run",
        method: "POST",
        data: {
            language: language,
            source_code: source_code,
            input: input,
        },
    }).done(function (result) {
        $("#stdout").text(result.stdout);
        $("#stderr").text(result.stderr);
        $("#time").text(result.time);
        $("#exit_code").text(result.exit_code);
        $("#run_button").text("実行(Ctrl-Enter)").prop("disabled", false);
    }).fail(function (error) {
        alert("Request Failed:" + error);
        $("#run_button").text("実行(Ctrl-Enter)").prop("disabled", false);
    });
}
// Docker実戦活用ガイド 初版第1刷 184p
// Perlを実行できるよう、Perlを追加

var aceEditor = ace.edit("source_code");
/*
    Set ACE editor options
*/
aceEditor.setOptions({
    enableBasicAutocompletion: true,
    enableLiveAutocompletion: true,
    enableSnippets: true,
});

/*
    run the code when "run_button" is clicked, or Ctrl-Enter is pressed.
*/
$("#run_button").on("click", function (event) {
    runCode();
});
aceEditor.commands.addCommand({
    bindKey: { win: "Ctrl-Enter", mac: "Ctrl-Enter" },
    exec: runCode,
});

/*
    Language settings
*/
function setEditorLanguage(language) {
    var languageToMode = {
        perl: "perl",
        ruby: "ruby",
        python: "python",
        c: "c_cpp",
    }
    var mode = languageToMode[language];
    aceEditor.getSession().setMode("ace/mode/" + mode);
}
$("#language").val("perl");
setEditorLanguage("perl");
$('#language').on("change", function (event) {
    setEditorLanguage(this.value);
})


Comments (0)