yannstatic/static/2023/05/20/systemd-nspawn.html

2123 lines
214 KiB
HTML
Raw Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>Archlinux conteneur systemd-nspawn - YannStatic</title>
<meta name="description" content="systemd-nspawn peut être utilisé pour exécuter une commande ou un système dexploitation dans un espace de noms léger. Il est plus puissant que chroot car il...">
<link rel="canonical" href="https://static.rnmkcy.eu/2023/05/20/systemd-nspawn.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root"><!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main">
<div class="header__title">
<div class="header__brand"><svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewBox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.5
" href="/">YannStatic</a></div><!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">Archlinux conteneur systemd-nspawn</h1></header></div><meta itemprop="headline" content="Archlinux conteneur systemd-nspawn"><div class="article__info clearfix"><ul class="left-col menu"><li>
<a class="button button--secondary button--pill button--sm"
href="/archive.html?tag=systemd">systemd</a>
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">20&nbsp;mai&nbsp;&nbsp;&nbsp;2023</span>
<span title="Modification" style="color:#00FF7F">23&nbsp;mai&nbsp;&nbsp;&nbsp;2023</span></li></ul></div><meta itemprop="datePublished" content="2023-05-23T00:00:00+02:00">
<meta itemprop="keywords" content="systemd"><div class="js-article-content">
<div class="layout--article"><!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page">&#8679;</button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody"><details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><p><em>systemd-nspawn peut être utilisé pour exécuter une commande ou un système dexploitation dans un espace de noms léger. Il est plus puissant que chroot car il virtualise entièrement la hiérarchie du système de fichiers, ainsi que larborescence des processus, les différents sous-systèmes IPC et le nom de lhôte et du domaine.</em></p>
<h2 id="service-systemd-nspawn">Service systemd-nspawn</h2>
<p><a href="https://wiki.archlinux.org/title/Systemd-nspawn">systemd-nspawn</a> ressemble à la commande chroot, mais cest un chroot sur les stéroïdes.</p>
<p>systemd-nspawn limite laccès à diverses interfaces du noyau dans le conteneur à la lecture seule, comme /sys, /proc/sys ou /sys/fs/selinux. Les interfaces réseau et lhorloge système ne peuvent pas être modifiées depuis le conteneur. Les nœuds de périphériques ne peuvent pas être créés. Le système hôte ne peut pas être redémarré et les modules du noyau ne peuvent pas être chargés à partir du conteneur.</p>
<p><em>systemd-nspawn est un outil plus simple à configurer que LXC ou Libvirt.</em></p>
<ul>
<li><a href="https://wiki.archlinux.org/title/Systemd-nspawn">systemd-nspawn (wiki archlinux)</a></li>
<li><a href="https://blog.karmacomputing.co.uk/using-systemd-nspawn-containers-with-publicly-routable-ips-ipv6-and-ipv4-via-bridged-mode-for-high-density-testing-whilst-balancing-tenant-isolation/">Using systemd-nspawn containers with publicly routable ips (IPv6 and IPv4) via bridged mode for high density testing whilst balancing tenant isolation</a></li>
</ul>
<h3 id="exemple-conteneur-archlinux">Exemple conteneur ArchLinux</h3>
<p><em>Créer et démarrer un conteneur Arch Linux minimal</em></p>
<p>Installez dabord <strong>arch-install-scripts</strong></p>
<p>Ensuite, créez un répertoire pour contenir le conteneur. Dans cet exemple, nous utiliserons <code class="language-plaintext highlighter-rouge">~/MonConteneur</code></p>
<p>Ensuite, nous utilisons <code class="language-plaintext highlighter-rouge">pacstrap</code> pour installer un système Arch de base dans le conteneur.<br />
Au minimum, nous devons installer le paquet de base.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># pacstrap -K -c ~/MonConteneur base [paquets/groupes supplémentaires]</span>
</code></pre></div></div>
<p>Astuce : Le paquet base ne dépend pas du paquet linux kernel et est prêt pour le conteneur.</p>
<p>Une fois linstallation terminée, chrootez dans le conteneur et définissez un mot de passe root :</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># systemd-nspawn -D ~/MonConteneur</span>
<span class="c"># passwd</span>
<span class="c"># logout</span>
</code></pre></div></div>
<p>Enfin, démarrez dans le conteneur :</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># systemd-nspawn -b -D ~/MonConteneur</span>
</code></pre></div></div>
<p>Loption <code class="language-plaintext highlighter-rouge">-b</code> permet de démarrer le conteneur (cest-à-dire dexécuter systemd en tant que PID=1), au lieu dexécuter simplement un shell, et <code class="language-plaintext highlighter-rouge">-D</code> spécifie le répertoire qui devient le répertoire racine du conteneur.</p>
<p>Après le démarrage du conteneur, connectez-vous en tant que “root” avec votre mot de passe.</p>
<p class="info">Note : Si la connexion échoue avec “Login incorrect”, le problème vient probablement de la liste blanche du périphérique TTY securetty. Voir #Root login fails.</p>
<p>Le conteneur peut être mis hors tension en exécutant <code class="language-plaintext highlighter-rouge">poweroff</code> depuis le conteneur. Depuis lhôte, les conteneurs peuvent être contrôlés par loutil <code class="language-plaintext highlighter-rouge">machinectl</code>.</p>
<p class="info">Remarque : pour mettre fin à la session à partir du conteneur, maintenez les touches <code class="language-plaintext highlighter-rouge">Ctrl ALTGR</code> enfoncées et appuyez rapidement sur <code class="language-plaintext highlighter-rouge">]</code> à trois reprises.</p>
<h3 id="exemple-conteneur-debian">Exemple conteneur Debian</h3>
<p><em>Créer un environnement Debian ou Ubuntu</em></p>
<p>Installez <strong>debootstrap</strong>, ainsi que <strong>debian-archive-keyring</strong> ou <strong>ubuntu-keyring</strong>, ou les deux, en fonction de la distribution choisie.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yay -S debootstrap debian-archive-keyring
</code></pre></div></div>
<p class="info">Note : <em>systemd-nspawn</em> nécessite que le système dexploitation dans le conteneur utilise <em>systemd init</em> (quil tourne en tant que PID 1) et que <em>systemd-nspawn</em> soit installé dans le conteneur. Ces exigences peuvent être satisfaites en sassurant que le paquetage <em>systemd-container</em> est installé sur le système du conteneur. Le paquetage <em>systemd-container</em> dépend de dbus, qui nest pas installé par défaut.<br />
<strong>Assurez-vous que le paquetage <em>dbus</em> ou <em>dbus-broker</em> est installé sur le système de conteneurs.</strong></p>
<p>À partir de là, il est assez facile de configurer les environnements Debian ou Ubuntu :</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cd /var/lib/machines</span>
<span class="c"># debootstrap --include=dbus-broker,systemd-container --components=main,universe codename container-name repository-url</span>
</code></pre></div></div>
<p>Pour <strong>Debian</strong>, les noms de code valides sont soit les noms de roulement comme “stable” et “testing”, soit les noms de version comme “bullseye” et “sid”.</p>
<p>Pour <strong>Ubuntu</strong>, le nom de code “xenial” ou “zesty” doit être utilisé. Une liste complète des noms de code se trouve dans /usr/share/debootstrap/scripts et la table officielle des noms de code et des numéros de version se trouve dans <a href="https://wiki.ubuntu.com/DevelopmentCodeNames#Release_Naming_Scheme">DevelopmentCodeNames</a>.</p>
<p>Dans le cas dune image Debian, le “repository-url” peut être https://deb.debian.org/debian/.<br />
Pour une image Ubuntu, le “repository-url” peut être http://archive.ubuntu.com/ubuntu/. Le “repository-url” ne doit pas contenir de barre oblique à la fin.</p>
<p>Le container bullseyes est créé</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I: Base system installed successfully.
</code></pre></div></div>
<p>Tout comme Arch, Debian et Ubuntu ne vous laisseront pas vous connecter sans mot de passe. Pour définir le mot de passe root, exécutez systemd-nspawn sans loption <code class="language-plaintext highlighter-rouge">-b</code> :</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> /var/lib/machines
systemd-nspawn <span class="nt">-D</span> ./nspbullseye
passwd
<span class="nb">logout</span>
</code></pre></div></div>
<h3 id="options-par-défaut">Options par défaut</h3>
<p>Les conteneurs situés dans <code class="language-plaintext highlighter-rouge">/var/lib/machines/</code> peuvent être contrôlés par la commande <code class="language-plaintext highlighter-rouge">machinectl</code>, qui contrôle en interne les instances de lunité de <code class="language-plaintext highlighter-rouge">service systemd-nspawn@</code><br />
Les sous-répertoires de <code class="language-plaintext highlighter-rouge">/var/lib/machines/</code> correspondent aux noms des conteneurs, cest-à-dire <code class="language-plaintext highlighter-rouge">/var/lib/machines/nom du conteneur/</code></p>
<p class="info">Note : Si le conteneur ne peut pas être déplacé dans /var/lib/machines/ pour une raison quelconque, il peut être lié par un lien symbolique. Voir <a href="https://man.archlinux.org/man/machinectl.1#FILES_AND_DIRECTORIES">machinectl § FILES AND DIRECTORIES</a> pour plus de détails.</p>
<p>Il est important de comprendre que les conteneurs démarrés via <code class="language-plaintext highlighter-rouge">machinectl</code> ou <code class="language-plaintext highlighter-rouge">systemd-nspawn@.service</code> utilisent des options par défaut différentes de celles des conteneurs démarrés manuellement par la commande <code class="language-plaintext highlighter-rouge">systemd-nspawn</code><br />
Les options supplémentaires utilisées par le service sont les suivantes :</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">-b/--boot</code> - Les conteneurs gérés recherchent automatiquement un programme dinitialisation et linvoquent en tant que PID 1.</li>
<li><code class="language-plaintext highlighter-rouge">--network-veth</code> qui implique <code class="language-plaintext highlighter-rouge">--private-network</code> - Les conteneurs gérés obtiennent une interface réseau virtuelle et sont déconnectés du réseau hôte.</li>
<li><code class="language-plaintext highlighter-rouge">-U</code> - Les conteneurs gérés utilisent la fonctionnalité user_namespaces(7) par défaut si elle est supportée par le noyau.</li>
<li><code class="language-plaintext highlighter-rouge">--link-journal=try-guest</code></li>
</ul>
<p>Le comportement peut être modifié dans les fichiers de configuration par conteneur, voir le paragraohe <strong>Configuration</strong> pour plus de détails.</p>
<h3 id="machinectl">machinectl</h3>
<p class="info">Note : Loutil <code class="language-plaintext highlighter-rouge">machinectl</code> nécessite que <em>systemd</em> et <em>dbus</em> soient installés dans le conteneur.</p>
<p>Les conteneurs peuvent être gérés par la sous-commande <code class="language-plaintext highlighter-rouge">machinectl nom-du-conteneur</code><br />
Par exemple, pour démarrer un conteneur</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>machinectl start nom-du-conteneur
</code></pre></div></div>
<p class="warning">Remarque : machinectl exige que le nom du conteneur soit composé uniquement de lettres ASCII, de chiffres et de traits dunion afin quil sagisse dun nom dhôte valide. Par exemple, si le nom du conteneur contient un trait de soulignement, il ne sera tout simplement pas reconnu et lexécution de machinectl start nom_du_conteneur entraînera lerreur Nom_du_conteneur invalide.</p>
<p>De même, il existe des sous-commandes telles que <code class="language-plaintext highlighter-rouge">poweroff</code>, <code class="language-plaintext highlighter-rouge">reboot</code>, <code class="language-plaintext highlighter-rouge">status</code> et <code class="language-plaintext highlighter-rouge">show</code>. Voir <a href="https://man.archlinux.org/man/machinectl.1#Machine_Commands">machinectl § Commandes machine</a> pour des explications détaillées.</p>
<p><strong>Astuce</strong> : Les opérations de mise hors tension et de redémarrage peuvent être effectuées depuis le conteneur à laide des commandes <code class="language-plaintext highlighter-rouge">poweroff</code> et <code class="language-plaintext highlighter-rouge">reboot</code>.</p>
<p>Dautres commandes courantes sont :</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">machinectl lis</code>t - affiche la liste des conteneurs en cours dexécution</li>
<li><code class="language-plaintext highlighter-rouge">machinectl login nom-du-conteneur</code> - ouvre une session de connexion interactive dans un conteneur</li>
<li><code class="language-plaintext highlighter-rouge">machinectl shell [username@]container-name</code> - ouvre une session interactive dans un conteneur (cela invoque immédiatement un processus utilisateur sans passer par le processus de connexion dans le conteneur)</li>
<li><code class="language-plaintext highlighter-rouge">machinectl enable container-name</code> et <code class="language-plaintext highlighter-rouge">machinectl disable container-name</code> - active ou désactive le lancement dun conteneur au démarrage, voir <a href="https://wiki.archlinux.org/title/Systemd-nspawn#Enable_container_to_start_at_boot">#Enable container to start at boot</a> pour plus de détails</li>
</ul>
<p><strong>machinectl</strong> possède également des sous-commandes pour gérer les images des conteneurs (ou des machines virtuelles) et les transferts dimages. Voir <a href="https://man.archlinux.org/man/machinectl.1#Image_Commands">machinectl §Image Commands</a> et <a href="https://man.archlinux.org/man/machinectl.1#Image_Transfer_Commands">machinectl §Image Transfer Commands</a> pour plus de détails. A partir du 2023Q1, les 3 premiers exemples de machinectl(1) § EXEMPLES démontrent les commandes de transfert dimages. <a href="https://man.archlinux.org/man/machinectl.1#FILES_AND_DIRECTORIES">machinectl(1) § FILES AND DIRECTORIES</a> discute de lendroit où trouver des images appropriées.</p>
<h3 id="chaîne-doutils-systemd">Chaîne doutils systemd</h3>
<p>Une grande partie de la chaîne doutils de base de systemd a été mise à jour pour fonctionner avec les conteneurs. Les outils qui le font fournissent généralement une option <code class="language-plaintext highlighter-rouge">-M</code>, <code class="language-plaintext highlighter-rouge">--machine=</code> qui prend un nom de conteneur comme argument.</p>
<p>Exemples :</p>
<p>Voir les journaux dune machine particulière :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>journalctl -M nom-du-conteneur
</code></pre></div></div>
<p>Afficher le contenu du groupe de contrôle :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-cgls -M nom-du-conteneur
</code></pre></div></div>
<p>Voir lheure de démarrage du conteneur :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-analyze -M nom-du-conteneur
</code></pre></div></div>
<p>Pour une vue densemble de lutilisation des ressources :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemd-cgtop
</code></pre></div></div>
<h3 id="fichiers-nspawn">Fichiers .nspawn</h3>
<p>Les fichiers <code class="language-plaintext highlighter-rouge">.nspawn</code> peuvent être utilisés pour spécifier des paramètres par conteneur et non des paramètres globaux. Voir <a href="https://man.archlinux.org/man/systemd.nspawn.5">systemd.nspawn(5)</a> pour plus de détails.</p>
<p>Remarque : les fichiers <code class="language-plaintext highlighter-rouge">.nspawn</code> peuvent être retirés de la base de données :</p>
<ul>
<li>Les fichiers .nspawn peuvent être supprimés de manière inattendue de /etc/systemd/nspawn/ lorsque vous exécutez <code class="language-plaintext highlighter-rouge">machinectl remove</code> <a href="https://github.com/systemd/systemd/issues/15900">[5]</a></li>
<li>Linteraction des options réseau spécifiées dans le fichier <code class="language-plaintext highlighter-rouge">.nspawn</code> et sur la ligne de commande ne fonctionne pas correctement lorsquil y a <code class="language-plaintext highlighter-rouge">--settings=override</code> (qui est spécifié dans le fichier <code class="language-plaintext highlighter-rouge">systemd-nspawn@.service</code>). Comme solution de contournement, vous devez inclure loption <code class="language-plaintext highlighter-rouge">VirtualEthernet=on</code>, même si le service spécifie <code class="language-plaintext highlighter-rouge">--network-veth</code>.</li>
</ul>
<h3 id="lancement-au-démarrage">Lancement au démarrage</h3>
<p>Lorsque vous utilisez fréquemment un conteneur, il se peut que vous souhaitiez le lancer au démarrage.</p>
<p>Assurez-vous dabord que loption <code class="language-plaintext highlighter-rouge">machines.target</code> est activée.</p>
<p>Les conteneurs détectables par <code class="language-plaintext highlighter-rouge">machinectl</code> peuvent être activés ou désactivés :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>machinectl enable nom-du-conteneur
</code></pre></div></div>
<p>Remarque :</p>
<ul>
<li>Cela a pour effet dactiver lunité systemd <code class="language-plaintext highlighter-rouge">systemd-nspawn@container-name.service</code>.</li>
<li>Comme mentionné dans <a href="https://wiki.archlinux.org/title/Systemd-nspawn#Default_systemd-nspawn_options">#Options systemd-nspawn par défaut</a>, les conteneurs démarrés par machinectl obtiennent une interface Ethernet virtuelle. Pour désactiver le réseau privé, voir <a href="https://wiki.archlinux.org/title/Systemd-nspawn#Use_host_networking">#Utiliser le réseau de lhôte</a>.</li>
</ul>
<h3 id="contrôle-des-ressources">Contrôle des ressources</h3>
<p>Vous pouvez profiter des groupes de contrôle pour implémenter des limites et une gestion des ressources de vos conteneurs avec <code class="language-plaintext highlighter-rouge">systemctl set-property</code>, voir <a href="https://man.archlinux.org/man/systemd.resource-control.5">systemd.resource-control(5)</a>.<br />
Par exemple, vous pouvez vouloir limiter la quantité de mémoire ou lutilisation du processeur. Pour limiter la consommation de mémoire de votre conteneur à 2 GiB :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl set-property systemd-nspawn@container-name.service MemoryMax=2G
</code></pre></div></div>
<p>Ou pour limiter lutilisation du temps CPU à léquivalent de 2 cœurs :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl set-property systemd-nspawn@container-name.service CPUQuota=200%
</code></pre></div></div>
<p>Cela créera des fichiers permanents dans <code class="language-plaintext highlighter-rouge">/etc/systemd/system.control/systemd-nspawn@container-name.service.d/</code></p>
<p>Daprès la documentation, <code class="language-plaintext highlighter-rouge">MemoryHigh</code> est la méthode préférée pour contrôler la consommation de mémoire, mais elle ne sera pas limitée de manière stricte comme cest le cas avec <code class="language-plaintext highlighter-rouge">MemoryMax</code>. Vous pouvez utiliser les deux options en laissant <code class="language-plaintext highlighter-rouge">MemoryMax</code> comme dernière ligne de défense. Prenez également en considération le fait que vous ne limiterez pas le nombre de CPU que le conteneur peut voir, mais vous obtiendrez des résultats similaires en limitant le temps que le conteneur obtiendra au maximum, par rapport au temps total du CPU.</p>
<blockquote>
<p>Astuce : Si vous voulez que ces changements ne soient que temporaires, vous pouvez passer loption <code class="language-plaintext highlighter-rouge">--runtime</code><br />
Vous pouvez vérifier les résultats avec <code class="language-plaintext highlighter-rouge">systemd-cgtop</code></p>
</blockquote>
<h3 id="mise-en-réseau">Mise en réseau</h3>
<p>Les conteneurs <em>systemd-nspawn</em> peuvent utiliser le réseau hôte ou le réseau privé :</p>
<ul>
<li>Dans le mode réseau hôte, le conteneur a un accès complet au réseau hôte. Cela signifie que le conteneur pourra accéder à tous les services réseau de lhôte et que les paquets provenant du conteneur apparaîtront au réseau extérieur comme provenant de lhôte (cest-à-dire quils partageront la même adresse IP).</li>
<li>En mode réseau privé, le conteneur est déconnecté du réseau de lhôte. Toutes les interfaces réseau sont alors indisponibles pour le conteneur, à lexception du périphérique de bouclage et des interfaces explicitement attribuées au conteneur. <br />
Il existe plusieurs façons de configurer des interfaces réseau pour le conteneur :
* Une interface existante peut être affectée au conteneur (par exemple, si vous avez plusieurs périphériques Ethernet).
* Une interface réseau virtuelle associée à une interface existante (cest-à-dire une interface VLAN) peut être créée et attribuée au conteneur.
* Un lien Ethernet virtuel entre lhôte et le conteneur peut être créé.<br />
Dans ce dernier cas, le réseau du conteneur est totalement isolé (du réseau extérieur et des autres conteneurs) et il appartient à ladministrateur de configurer le réseau entre lhôte et les conteneurs. Cela implique généralement la création dun pont réseau pour connecter plusieurs interfaces (physiques ou virtuelles) ou la mise en place dune traduction dadresses réseau entre plusieurs interfaces.</li>
</ul>
<p>Le mode de<u> mise en réseau de l'hôte</u> convient aux conteneurs dapplication qui nexécutent aucun logiciel de mise en réseau susceptible de configurer linterface attribuée au conteneur. Le mode réseau hôte est le mode par défaut lorsque vous exécutez systemd-nspawn à partir de linterpréteur de commandes.</p>
<p>En revanche, le <u>mode réseau privé</u> convient aux conteneurs système qui doivent être isolés du système hôte. La création de liens Ethernet virtuels est un outil très flexible qui permet de créer des réseaux virtuels complexes. Cest le mode par défaut pour les conteneurs démarrés par machinectl ou systemd-nspawn@.service.</p>
<p>Les sous-sections suivantes décrivent des scénarios courants. Voir <a href="https://man.archlinux.org/man/systemd-nspawn.1#Networking_Options">systemd-nspawn(1) § Options de mise en réseau</a> pour plus de détails sur les options disponibles de <em>systemd-nspawn</em>.</p>
<h4 id="utiliser-la-mise-en-réseau-de-lhôte">Utiliser la mise en réseau de lhôte</h4>
<p>Pour désactiver la mise en réseau privée et la création dun lien Ethernet virtuel utilisé par les conteneurs démarrés avec machinectl, ajoutez un fichier .nspawn avec loption suivante :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/systemd/nspawn/nom-du-conteneur.nspawn
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Network]
VirtualEthernet=no
</code></pre></div></div>
<p>Cette option remplacera loption <code class="language-plaintext highlighter-rouge">-n/--network-veth</code> utilisée dans <code class="language-plaintext highlighter-rouge">systemd-nspawn@.service</code> et les conteneurs nouvellement démarrés utiliseront le mode réseau de lhôte.</p>
<h4 id="utiliser-un-lien-ethernet-virtuel">Utiliser un lien Ethernet virtuel</h4>
<p>Si un conteneur est démarré avec loption <code class="language-plaintext highlighter-rouge">-n/--network-veth</code>, <em>systemd-nspawn</em> créera un lien Ethernet virtuel entre lhôte et le conteneur.<br />
Le côté hôte du lien sera disponible en tant quinterface réseau nommée <code class="language-plaintext highlighter-rouge">ve-container-name</code> <br />
Le côté conteneur du lien sera nommé <code class="language-plaintext highlighter-rouge">host0</code>. Notez que cette option implique <code class="language-plaintext highlighter-rouge">--private-network</code></p>
<p>Remarque :</p>
<ul>
<li>Si le nom du conteneur est trop long, le nom de linterface sera raccourci (par exemple, <code class="language-plaintext highlighter-rouge">ve-long-conKQGh</code> au lieu de <code class="language-plaintext highlighter-rouge">ve-long-nom-du-conteneur</code>) afin de respecter la <a href="https://stackoverflow.com/a/29398765">limite de 15 caractères</a>. Le nom complet sera défini comme la propriété <code class="language-plaintext highlighter-rouge">altname</code> de linterface (voir <a href="https://man.archlinux.org/man/ip-link.8">ip-link(8)</a>) et pourra toujours être utilisé pour référencer linterface.</li>
<li>Lors de lexamen des interfaces avec <code class="language-plaintext highlighter-rouge">ip link</code>, les noms dinterface seront affichés avec un suffixe, comme <code class="language-plaintext highlighter-rouge">ve-container-name@if2</code> et <code class="language-plaintext highlighter-rouge">host0@if9</code>. Le <code class="language-plaintext highlighter-rouge">@ifN</code> ne fait pas partie du nom de linterface, mais <code class="language-plaintext highlighter-rouge">ip link</code> ajoute cette information pour indiquer à quel “emplacement” le câble Ethernet virtuel se connecte à lautre extrémité.<br />
Par exemple, une interface Ethernet virtuelle hôte présentée comme <code class="language-plaintext highlighter-rouge">ve-foo@if2</code> est connectée au conteneur foo, et à lintérieur du conteneur à la deuxième interface réseau - celle présentée avec lindex 2 lors de lexécution dip link à lintérieur du conteneur. De même, linterface nommée <code class="language-plaintext highlighter-rouge">host0@if9</code> dans le conteneur est connectée à la 9ème interface réseau de lhôte.</li>
</ul>
<p>Lorsque vous démarrez le conteneur, une adresse IP doit être attribuée aux deux interfaces (sur lhôte et dans le conteneur). Si vous utilisez <a href="https://wiki.archlinux.org/title/Systemd-networkd">systemd-networkd</a> sur lhôte ainsi que dans le conteneur, ceci est fait out-of-the-box :</p>
<ul>
<li>le fichier <code class="language-plaintext highlighter-rouge">/usr/lib/systemd/network/80-container-ve.network</code> sur lhôte correspond à linterface <code class="language-plaintext highlighter-rouge">ve-container-name</code> et démarre un serveur DHCP, qui attribue des adresses IP à linterface de lhôte ainsi quau conteneur,</li>
<li>le fichier <code class="language-plaintext highlighter-rouge">/usr/lib/systemd/network/80-container-host0.network</code> du conteneur correspond à linterface <code class="language-plaintext highlighter-rouge">host0</code> et démarre un client DHCP qui reçoit une adresse IP de lhôte.</li>
</ul>
<p>Si vous nutilisez pas <em>systemd-networkd</em>, vous pouvez configurer des adresses IP statiques ou démarrer un serveur DHCP sur linterface hôte et un client DHCP dans le conteneur. Voir <a href="https://wiki.archlinux.org/title/Network_configuration">Configuration du réseau</a> pour plus de détails.</p>
<p>Pour permettre au conteneur daccéder au réseau extérieur, vous pouvez configurer NAT comme décrit dans <a href="https://wiki.archlinux.org/title/Internet_sharing#Enable_NAT">Partage Internet#Activer NAT</a>. Si vous utilisez <em>systemd-networkd</em>, cela est fait (partiellement) automatiquement via loption <code class="language-plaintext highlighter-rouge">IPMasquerade=both</code> dans /usr/lib/systemd/network/80-container-ve.network. Cependant, cette option német quune règle <a href="https://wiki.archlinux.org/title/Iptables">iptables</a> (ou <a href="https://wiki.archlinux.org/title/Nftables">nftables</a>) telle que</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-t nat -A POSTROUTING -s 192.168.163.192/28 -j MASQUERADE
</code></pre></div></div>
<p>La table de filtrage doit être configurée manuellement comme indiqué dans <a href="https://wiki.archlinux.org/title/Internet_sharing#Enable_NAT">Partage Internet#Activer NAT</a>.<br />
Vous pouvez utiliser un joker pour faire correspondre toutes les interfaces commençant par ve- :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A FORWARD -i ve-+ -o internet0 -j ACCEPT
</code></pre></div></div>
<p class="info">Note : <em>systemd-networkd</em> et <em>systemd-nspawn</em> peuvent sinterfacer avec <a href="https://wiki.archlinux.org/title/Iptables">iptables</a> (en utilisant la librairie <a href="https://tldp.org/HOWTO/Querying-libiptc-HOWTO/whatis.html">libiptc</a>) ainsi quavec <a href="https://wiki.archlinux.org/title/Nftables">nftables</a> <a href="https://github.com/systemd/systemd/issues/13307">[7]</a><a href="https://github.com/systemd/systemd/blob/9ca34cf5a4a20d48f829b2a36824255aac29078c/NEWS#L295-L304">[8]</a>. Dans les deux cas, la NAT IPv4 et IPv6 est prise en charge.</p>
<p>De plus, vous devez ouvrir le port UDP 67 sur les interfaces <code class="language-plaintext highlighter-rouge">ve-+</code> pour les connexions entrantes vers le serveur DHCP (géré par <em>systemd-networkd</em>) :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A INPUT -i ve-+ -p udp -m udp --dport 67 -j ACCEPT
</code></pre></div></div>
<h4 id="utiliser-un-pont-réseau">Utiliser un pont réseau</h4>
<p>Si vous avez configuré un pont réseau sur le système hôte, vous pouvez créer un lien Ethernet virtuel pour le conteneur et ajouter son côté hôte au pont réseau.<br />
Cette opération seffectue à laide de loption <code class="language-plaintext highlighter-rouge">--network-bridge=nom du pont</code><br />
Notez que loption <code class="language-plaintext highlighter-rouge">--network-bridge</code> implique <code class="language-plaintext highlighter-rouge">--network-veth</code>, cest-à-dire que le lien Ethernet virtuel est créé automatiquement.<br />
Cependant, le côté hôte du lien utilisera le préfixe <code class="language-plaintext highlighter-rouge">vb-</code> au lieu de <code class="language-plaintext highlighter-rouge">ve-</code>, de sorte que les options <em>systemd-networkd</em> de démarrage du serveur DHCP et de masquage dIP ne seront pas appliquées.</p>
<p>La gestion du pont est laissée à ladministrateur. Par exemple, le pont peut connecter des interfaces virtuelles avec une interface physique, ou il peut connecter seulement des interfaces virtuelles de plusieurs conteneurs. Voir <a href="https://wiki.archlinux.org/title/Systemd-networkd#Network_bridge_with_DHCP">systemd-networkd#Network bridge with DHCP</a> et <a href="https://wiki.archlinux.org/title/Systemd-networkd#Network_bridge_with_static_IP_addresses">systemd-networkd#Network bridge with static IP addresses</a> pour des exemples de configurations utilisant <em>systemd-networkd</em>.</p>
<p>Il existe également une option <code class="language-plaintext highlighter-rouge">--network-zone=zone-name</code> qui est similaire à <code class="language-plaintext highlighter-rouge">--network-bridge</code> mais le pont réseau est géré automatiquement par <em>systemd-nspawn</em> et <em>systemd-networkd</em><br />
Linterface de pont nommée <code class="language-plaintext highlighter-rouge">vz-zone-name</code> est automatiquement créée lorsque le premier conteneur configuré avec loption <code class="language-plaintext highlighter-rouge">--network-zone=zone-name</code> est démarré, et est automatiquement supprimée lorsque le dernier conteneur configuré avec loption <code class="language-plaintext highlighter-rouge">--network-zone=zone-name</code> se termine.<br />
Cette option permet donc de placer facilement plusieurs conteneurs apparentés sur un réseau virtuel commun.<br />
Notez que les interfaces <code class="language-plaintext highlighter-rouge">vz-*</code> sont gérées par <em>systemd-networkd</em> de la même manière que les interfaces <code class="language-plaintext highlighter-rouge">ve-*</code> en utilisant les options du fichier <code class="language-plaintext highlighter-rouge">/usr/lib/systemd/network/80-container-vz.network</code></p>
<h4 id="utiliser-une-interface-macvlan-ou-ipvlan">Utiliser une interface “macvlan” ou “ipvlan</h4>
<p>Au lieu de créer un lien Ethernet virtuel (dont le côté hôte peut ou non être ajouté à un pont), vous pouvez créer une interface virtuelle sur une interface physique existante (cest-à-dire une interface <a href="https://wiki.archlinux.org/title/VLAN">VLAN</a>) et lajouter au conteneur.<br />
Linterface virtuelle sera pontée avec linterface hôte sous-jacente et le conteneur sera donc exposé au réseau extérieur, ce qui lui permet dobtenir une adresse IP distincte via DHCP à partir du même réseau local que celui auquel lhôte est connecté.</p>
<p><em>systemd-nspawn</em> propose 2 options :</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">--network-macvlan=interface</code> - linterface virtuelle aura une adresse MAC différente de linterface physique sous-jacente et sera nommée <code class="language-plaintext highlighter-rouge">mv-interface</code></li>
<li><code class="language-plaintext highlighter-rouge">--network-ipvlan=interface</code> - linterface virtuelle aura la même adresse MAC que linterface physique sous-jacente et sera nommée <code class="language-plaintext highlighter-rouge">iv-interface</code></li>
</ul>
<p>Les deux options impliquent <code class="language-plaintext highlighter-rouge">--private-network</code></p>
<h4 id="utiliser-une-interface-existante">Utiliser une interface existante</h4>
<p>Si le système hôte possède plusieurs interfaces réseau physiques, vous pouvez utiliser loption <code class="language-plaintext highlighter-rouge">--network-interface=interface</code> pour affecter une interface au conteneur (et la rendre indisponible pour lhôte pendant le démarrage du conteneur).<br />
Notez que <code class="language-plaintext highlighter-rouge">--network-interface</code> implique <code class="language-plaintext highlighter-rouge">--private-network</code></p>
<p class="warning">Note : Le passage dinterfaces réseau sans fil aux conteneurs systemd-nspawn nest actuellement pas supporté <a href="https://github.com/systemd/systemd/issues/7873">[9]</a></p>
<h3 id="mappage-des-ports">Mappage des ports</h3>
<p>Lorsque le réseau privé est activé, les ports individuels de lhôte peuvent être mappés aux ports du conteneur en utilisant loption <code class="language-plaintext highlighter-rouge">-p/--port</code> ou en utilisant le paramètre <a href="URL">Port</a> dans un fichier <em>.nspawn</em>. Cela se fait en émettant des règles <a href="https://wiki.archlinux.org/title/Iptables">iptables</a> dans la table nat, mais la chaîne <code class="language-plaintext highlighter-rouge">FORWARD</code> dans la table de filtrage doit être configurée manuellement comme indiqué dans <a href="https://wiki.archlinux.org/title/Systemd-nspawn#Use_a_virtual_Ethernet_link">#Use a virtual Ethernet link (Utiliser un lien Ethernet virtuel)</a>.</p>
<p>Par exemple, pour mapper un port TCP 8000 sur lhôte au port TCP 80 dans le conteneur :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/systemd/nspawn/nom-du-conteneur.nspawn
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Network]
Port=tcp:8000:80
</code></pre></div></div>
<p class="warning">Remarque : systemd-nspawn exclut explicitement linterface <code class="language-plaintext highlighter-rouge">loopback</code> lors du mappage des ports. Ainsi, dans lexemple ci-dessus, localhost:8000 se connecte à lhôte et non au conteneur. Seules les connexions vers dautres interfaces sont soumises au mappage des ports. Voir <a href="https://github.com/systemd/systemd/issues/6106">[10]</a> pour plus de détails.</p>
<h3 id="résolution-nom-domaine">Résolution nom domaine</h3>
<p>La résolution des noms de domaine dans le conteneur peut être configurée de la même manière que sur le système hôte. En outre, systemd-nspawn fournit des options pour gérer le fichier /etc/resolv.conf à lintérieur du conteneur :</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">--resolv-conf</code> peut être utilisé en ligne de commande</li>
<li><code class="language-plaintext highlighter-rouge">ResolvConf=</code> peut être utilisé dans les fichiers <em>.nspawn</em></li>
</ul>
<p>Ces options correspondantes ont de nombreuses valeurs possibles qui sont décrites dans <a href="https://man.archlinux.org/man/systemd-nspawn.1#Integration_Options">systemd-nspawn(1) § Options dintégration</a>.<br />
La valeur par défaut est <code class="language-plaintext highlighter-rouge">auto</code>, ce qui signifie que :</p>
<ul>
<li>Si <code class="language-plaintext highlighter-rouge">--private-network</code> est activé, le fichier <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> est laissé tel quel dans le conteneur.</li>
<li>Sinon, si <a href="https://wiki.archlinux.org/title/Systemd-resolved">systemd-resolved</a> est en cours dexécution sur lhôte, son fichier stub <code class="language-plaintext highlighter-rouge">resolv.conf</code> est copié ou monté dans le conteneur.</li>
<li>Sinon, le fichier<code class="language-plaintext highlighter-rouge"> /etc/resolv.conf</code> est copié ou monté de lhôte vers le conteneur.</li>
</ul>
<p>Dans les deux derniers cas, le fichier est copié si la racine du conteneur est accessible en écriture, et monté par liaison sil est en lecture seule.</p>
<p>Dans le second cas où <a href="https://wiki.archlinux.org/title/Systemd-resolved">systemd-resolved</a> sexécute sur lhôte, <em>systemd-nspawn</em> sattend à ce quil sexécute également dans le conteneur, afin que ce dernier puisse utiliser le fichier de liens symboliques <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> de lhôte. Si ce nest pas le cas, la valeur par défaut <code class="language-plaintext highlighter-rouge">auto</code> ne fonctionne plus et vous devez remplacer le lien symbolique en utilisant lune des options <code class="language-plaintext highlighter-rouge">replace-*</code></p>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2023-05-20T00:00:00+02:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2023/05/04/VPS-Contabo-Debian-11-Yunohost-eolam.html">Contabo Debian 11 Bullseye - Yunohost eolam.fr</a></div><div class="next"><span>SUIVANT</span><a href="/2023/05/22/Endeavour_Dell_Latitude_e6230_conteneur_nspawn_debian_bullseye_nspyan.html">EndeavourOS Dell Latitude e6230 --> conteneur nspawn debian bullseye nspyan</a></div></div></div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div></div>
</div><script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div></div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>