Linux 4.11 での cgroup 関連の話題
Linux 4.11 で cgroup に動きがありました。と言ってもしばらく新機能追えてないので、これまでも色々変更されているかも?
追加された事自体を忘れてしまいそうなのでメモしておくだけで、詳しく調べるわけではありません。
rdma controller
rdma コントローラというコントローラが新たに追加されています。RDMA は “Remote Direct Memory Access” ですか。知りませんでした。
Added rdma cgroup controller that does accounting, limit enforcement on rdma/IB resources.
とのことですから、Infiniband 関係でリソース制限を行うために追加されたのでしょうか。
Drop the matching uid requirement on migration for cgroup v2
cgroup v2 での、root 以外のユーザによる cgroup 操作の制限を一部外しましょう、というもののようですね。cgroup v2 では別に制限かかっているから、これがなくても OK みたいな。
slub: make sysfs directories for memcg sub-caches optional
cgroup 初期化時の v1 の処理だけ抜き出して追ってみる
cgroup 初期化のメモ。間違っている可能性大なので信用しないでください。何度も同じところを繰り返し見てるので忘れないようにメモです。
サブシステム (cgroup_subsys
) と各グループのサブシステムの状態 (cgroup_subsys_state
) とそれらのセット (css_set
) の関係とかわかってないと以下を見てもわけわからんかも。自分用のメモで、間違いあるかもしれません。
start_kernel
からまず cgroup_init_early
が呼び出された後、cgroup_init
が呼び出される。
492 asmlinkage __visible void __init start_kernel(void) :(snip) 511 cgroup_init_early(); :(snip) 661 cgroup_init();
cgroup_init_early
4960 RCU_INIT_POINTER(init_task.cgroups, &init_css_set);
init_task
の css_set
型のメンバ cgroups
を init_css_set
で初期化します。init_css_set
は cgroup.c で定義されています。
4962 for_each_subsys(ss, i) {
これは静的に定義されているサブシステム分要素を持つ cgroup_subsys
型の cgroup_subsys[]
変数 (配列) 分ループをまわすものでした (cgroup の SUBSYS マクロ 参照)。ss
には i
番目のサブシステムが入ります。
4963 WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id, 4964 "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p name:id=%d:%s\n", 4965 i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free, 4966 ss->id, ss->name);
まずは
- サブシステムに必須のメソッドである
css_alloc/free
が存在しているかのチェック - サブシステム名とIDがまだ構造体のメンバに設定されていないチェック
を行います。
4967 WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN, 4968 "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]);
次にサブシステム名 (cgroup_subsys_name[i]
に入ってます) の名前が長すぎないかチェックしています。
4969 4970 ss->id = i; 4971 ss->name = cgroup_subsys_name[i];
サブシステム (変数の各メンバ) に、ID と名前を設定します。(各サブシステムで cgroup_subsys
を定義する際には定義されていないメンバ)
4973 if (ss->early_init) 4974 cgroup_init_subsys(ss, true);
そしてサブシステムの early_init
を行うように 1
が設定されている時は cgroup_init_subsys
を呼び出します。
cgroup_init_subsys
を見てみると、
4895 static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) 4896 { :(snip) 4906 /* Create the root cgroup state for this subsystem */ 4907 ss->root = &cgrp_dfl_root; 4908 css = ss->css_alloc(cgroup_css(&cgrp_dfl_root.cgrp, ss));
と ss->root
には v2 の root が放り込まれます。つまり cgroup_init_subsys
は v2 の処理を行うだけです。
cgroup_init
cgroup_init
はちょっと長いのですが、これも v2 の処理を無視するとやっていることはシンプルです。
4992 BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files));
まずはこれ。cgroup を操作するために各グループに現れるファイルがあります。このファイルを cftype
という構造体で定義します。
このファイルのうち、サブシステムの処理とは直接関係ない、cgroup 自体の操作に関連する cgroup コアで扱うファイル群があり、その定義が cftype
の配列として静的に定義されています。それが cgroup_legacy_base_files
変数です。これを cgroup_init_cftypes
関数に渡して、ファイル操作の定義を行います (多分)。
4996 /* Add init_css_set to the hash table */ 4997 key = css_set_hash(init_css_set.subsys); 4998 hash_add(css_set_table, &init_css_set.hlist, key);
システム上の css_set
はすべて css_set_table
というハッシュテーブルで管理されますので、それに init_css_set
を登録します。css_set
内のサブシステムから計算される値を使ってハッシュのキー (key
) を生成し、使用します。とは言っても init_css_set
って v2 用じゃ…
そしてまたサブシステム分ループ。ループの最初に以下のような処理がありますが、
5005 if (ss->early_init) { 5006 struct cgroup_subsys_state *css = 5007 init_css_set.subsys[ss->id]; 5008 5009 css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, 5010 GFP_KERNEL); 5011 BUG_ON(css->id < 0); 5012 } else { 5013 cgroup_init_subsys(ss, false); 5014 }
ここですが、cgroup_init_early
で初期化したサブシステムは ID を確保、されなかったサブシステムは cgroup_init_subsys
で初期化します。とは言っても、いずれも v2 処理。
その後の条件分岐、
5035 if (ss->dfl_cftypes == ss->legacy_cftypes) { 5036 WARN_ON(cgroup_add_cftypes(ss, ss->dfl_cftypes)); 5037 } else { 5038 WARN_ON(cgroup_add_dfl_cftypes(ss, ss->dfl_cftypes)); 5039 WARN_ON(cgroup_add_legacy_cftypes(ss, ss->legacy_cftypes)); 5040 }
if 文 true の条件は特別な起動オプションを付けないと満たさない開発目的の条件なので無視。通常は false になるので、v2 の設定を行った (cgroup_add_dfl_cftypes
呼び出し) 後の 5039 行目だけが v1 の処理でしょう、と思えますが、実は通常はここは何もしません。この理由は後で。
5042 if (ss->bind) 5043 ss->bind(init_css_set.subsys[ssid]); 5044 }
その後、サブシステムに bind
関数が設定されている場合はそれを実行。
5046 err = sysfs_create_mount_point(fs_kobj, "cgroup");
sysfs にマウントポイントとなるディレクトリを作成 (/sys/fs/cgroup
)。
5050 err = register_filesystem(&cgroup_fs_type);
cgroup というファイルシステムを登録。
5056 proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations);
proc に “cgroups” というファイルを作成。で終わりです。
cgroup_add_legacy_cftypes で何もしない理由
cgroup_add_legacy_cftypes
で v1 っぽい処理を呼び出しているにも関わらず「通常は何もしません」と書いた理由を追ってみます。
cgroup_add_legacy_cftypes
cgroup_add_legacy_cftypes
にサブシステムと cgroup_subsys
構造体型の各サブシステムの変数で定義されている legacy_cftypes
を渡します。legacy_cftypes
は各サブシステムで v1 のグループに出現させるファイルが各サブシステムごとに定義されています。
cgroup_add_legacy_cftypes
関数を見てみます。
3313 int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) 3314 { :(snip) 3322 if (!cgroup_legacy_files_on_dfl || 3323 ss->dfl_cftypes != ss->legacy_cftypes) { 3324 for (cft = cfts; cft && cft->name[0] != '\0'; cft++) 3325 cft->flags |= __CFTYPE_NOT_ON_DFL; 3326 } 3327 3328 return cgroup_add_cftypes(ss, cfts); 3329 }
通常は (開発目的のフラグがオン、またはデフォルトの cftypes
が v1 の場合 (これも通常はない) でなければ)、各 cftype
型の変数のメンバ flags
に “v2 階層には表示させない” というフラグを設定したのち、cgroup_add_cftypes
を呼び出します。
cgroup_add_cftypes
cgroup_add_cftypes
関数
3263 static int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) 3264 { :(snip) 3273 ret = cgroup_init_cftypes(ss, cfts); :(snip) 3280 ret = cgroup_apply_cftypes(cfts, true);
cgroup_init_cftypes
で cft->ss = ss
という cftype
構造体の ss
にサブシステムを入れている部分があります。そして cgroup_apply_cftypes
関数を呼びます。
cgroup_apply_cftypes
3138 static int cgroup_apply_cftypes(struct cftype *cfts, bool is_add) 3139 { 3141 struct cgroup_subsys *ss = cfts[0].ss; 3142 struct cgroup *root = &ss->root->cgrp;
ss
には cfts[0].ss
を入れてますが、これは cgroup_init_cftypes
でサブシステムそのものを入れました。root
変数にはサブシステムの root
が入りますが、これは cgroup_init_subsys
で v2 の root が入っていたはず。
3155 ret = cgroup_addrm_files(cgrp, cfts, is_add);
cgroup_addrm_files
そして cgroup_addrm_files
呼び出し。
3105 static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], 3106 bool is_add) 3107 { :(snip) 3113 for (cft = cfts; cft->name[0] != '\0'; cft++) { 3114 /* does cft->flags tell us to skip this file on @cgrp? */ 3115 if ((cft->flags & __CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp)) 3116 continue; 3117 if ((cft->flags & __CFTYPE_NOT_ON_DFL) && cgroup_on_dfl(cgrp)) 3118 continue; :(snip)
というループがあり、
- 現在の
cgrp
は v2 の root の cgroup が入っているはず (cgroup_apply_cftypes
の処理参照) cft->flags
には__CFTYPE_NOT_ON_DFL
が放り込まれていたはず (cgroup_add_legacy_cftypes
の処理参照)
というわけで、ここはずっと continue
で何もしないままループを抜けます。ループを抜けなければ cgroup_add_file
で cgroup 用のファイルを追加するのですが、何もしないということです。
関連
- cgroupのv1とv2の切り分け的なところ (φ(・・*)ゞ ウーン カーネルとか弄ったりのメモ)
cgroup の SUBSYS マクロ
cgroup_subsys.h というヘッダがあって、cpuset の部分だけ抜き出すと
#if IS_ENABLED(CONFIG_CPUSETS) SUBSYS(cpuset) #endif
という風に SUBSYS
マクロの中に cpuset
のようなサブシステム名を与えているだけのヘッダファイルがあります。
この SUBSYS
マクロは cgroup.h と cgroup.c で計 4 回ほど定義されています。
まず最初に、cgroup.h で
#define SUBSYS(_x) _x ## _cgrp_id,
と定義されています。サブシステム名と _cgrp_id
という文字列を連結します。
直後に enum の定義があり、
enum cgroup_subsys_id { #include <linux/cgroup_subsys.h> CGROUP_SUBSYS_COUNT, };
上記のように、そこで cgroup_subsys.h が include されているので、さきほどの SUBSYS(cpuset)
が cpuset_cgrp_id
という風に展開されます。
つまり、
enum cgroup_subsys_id {
cpuset_cgrp_id,
となります。cgroup_subsys.h には、他に存在するサブシステムが (kernel の config で有効になっている場合に) SUBSYS(サブシステム名)
という風に定義されているので、(サブシステム名)_cgrp_id
という値をサブシステム分持つ enum が定義されます。そして、最後が CGROUP_SUBSYS_COUNT
となるわけですね。つまり、
enum cgroup_subsys_id {
cpuset_cgrp_id,
cpu_cgrp_id,
cpuacct_cgrp_id,
blkio_cgrp_id,
memory_cgrp_id,
devices_cgrp_id,
freezer_cgrp_id,
net_cls_cgrp_id,
perf_event_cgrp_id,
net_prio_cgrp_id,
hugetlb_cgrp_id,
debug_cgrp_id,
CGROUP_SUBSYS_COUNT,
};
という enum が完成します。直後に
#undef SUBSYS
と SUBSYS は undef されているので、ここで上記の enum を作って役割は終わりです。この CGROUP_SUBSYS_COUNT
はサブシステム分ループする場合に使えますね。例えばこんなふうに
#define for_each_subsys(ss, ssid) \ for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT && \ (((ss) = cgroup_subsys[ssid]) || true); (ssid)++)
そして、サブシステムの配列などで、それぞれのサブシステムの ID を表す (サブシステム名)_cgrp_id
という値が利用できます。あとは配列の領域確保のときに使われています。
もう 1 か所、cgroup.h に SUBSYS
の定義があります。
#define SUBSYS(_x) extern struct cgroup_subsys _x ## _cgrp_subsys; #include <linux/cgroup_subsys.h> #undef SUBSYS
サブシステム名を _cgrp_subsys
と連結して、extern struct cgroup_subsys
の後に書くので、例えば cpuset なら
extern struct cgroup_subsys cpuset_cgrp_subsys;
となります。cpuset_cgrp_subsys
変数は kernel/cpuset.c に定義がありますので、それの extern 宣言になりますね。同様に、有効になっている各サブシステムの cgroup_subsys
構造体ごとに宣言されます。
kernel/cgroup.c にも 2 ヶ所ほどあります。
/* generate an array of cgroup subsystem pointers */ #define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys, static struct cgroup_subsys *cgroup_subsys[] = { #include <linux/cgroup_subsys.h> }; #undef SUBSYS
最初に紹介した、サブシステム名と _cgrp_id
を連結した enum の id が使われていますね。これも同様に
[cpuset_cgrp_id] = &cpuset_cgrp_subsys,
という行が生成されますね。つまり cgroup_subsys
という配列の (サブシステム名)_cgrp_id
番目は (サブシステム名)_cgrp_subsys
へのポインタということです。
これは先に紹介した for_each_subsys
マクロで使われていましたね。
さいごに kernel/cgroup.c にあるのが、サブシステム名を命名するマクロです。
/* array of cgroup subsystem names */ #define SUBSYS(_x) [_x ## _cgrp_id] = #_x, static const char *cgroup_subsys_name[] = { #include <linux/cgroup_subsys.h> }; #undef SUBSYS
これもさきほどと同様の処理でサブシステム名の配列を作っています。配列の (サブシステム名)_cgrp_id
番目は (サブシステム名)
、つまり SUBSYS
マクロに指定した文字列となります。
[cpuset_cgrp_id] = cpuset,
つまり、カーネルが使うサブシステムに関わる構造体の配列とか ID とか名前の配列なんかは、カーネルの config 時 (make config
とか make menuconfig
とか) の時点で定義され、コンパイル時点で作成されるってことですね。
いやー、さすがカーネル開発するような人は賢いなあ (←言ってる人アホっぽい)
cgroup のデフォルトルート cgrp_dfl_root
このエントリはほぼ個人的なメモで、色々唐突です。
cgroupのコアは kernel/cgroup.c に色々処理があります。
その中に「デフォルトヒエラルキ(のルート)」という変数があります。
4.1 kernel のコードです。
/* * The default hierarchy, reserved for the subsystems that are otherwise * unattached - it never has more than a single cgroup, and all tasks are * part of that cgroup. */ struct cgroup_root cgrp_dfl_root;
cgroup の初期化処理は start_kernel から呼び出される cgroup_init_early と cgroup_init で行われますが、cgrp_dfl_root は cgroup_init で初期化されているようです。
int __init cgroup_init(void) { struct cgroup_subsys *ss; unsigned long key; int ssid, err; BUG_ON(cgroup_init_cftypes(NULL, cgroup_dfl_base_files)); BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files)); mutex_lock(&cgroup_mutex); /* Add init_css_set to the hash table */ key = css_set_hash(init_css_set.subsys); hash_add(css_set_table, &init_css_set.hlist, key); BUG_ON(cgroup_setup_root(&cgrp_dfl_root, 0));
ここで cgroup_setup_root に cgrp_dfl_root が渡されます。この中で渡された root (つまり cgrp_dfl_root) の初期化処理がなされていきます。
ここで root = cgrp_dfl_root の場合は cgroup_dfl_base_files
static int cgroup_setup_root(struct cgroup_root *root, unsigned int ss_mask) { : (snip) if (root == &cgrp_dfl_root) base_files = cgroup_dfl_base_files; else base_files = cgroup_legacy_base_files; : (snip) ret = cgroup_addrm_files(root_cgrp, base_files, true);
ここで設定した base_files を cgroup_addrm_files に渡して、cgroup コアが各 cgroup (つまり cgroup を表す各ディレクトリ) に作成するファイルを設定します。
cgroup_dfl_base_files ってのは
/* cgroup core interface files for the default hierarchy */ static struct cftype cgroup_dfl_base_files[] = { { .name = "cgroup.procs", .seq_start = cgroup_pidlist_start, .seq_next = cgroup_pidlist_next, .seq_stop = cgroup_pidlist_stop, .seq_show = cgroup_pidlist_show, .private = CGROUP_FILE_PROCS, .write = cgroup_procs_write, .mode = S_IRUGO | S_IWUSR, }, { .name = "cgroup.controllers", .flags = CFTYPE_ONLY_ON_ROOT, .seq_show = cgroup_root_controllers_show, }, { .name = "cgroup.controllers", .flags = CFTYPE_NOT_ON_ROOT, .seq_show = cgroup_controllers_show, }, { .name = "cgroup.subtree_control", .seq_show = cgroup_subtree_control_show, .write = cgroup_subtree_control_write, }, { .name = "cgroup.populated", .flags = CFTYPE_NOT_ON_ROOT, .seq_show = cgroup_populated_show, }, { } /* terminate */ };
という内容ですが、ここで .name で指定されているファイルって cgroup-v2 の各 cgroup (ディレクトリ) に作成されるファイルです。
つまり start_kernel -> cgroup_init で起動時に設定される「デフォルトのヒエラルキ(のルート)」は v2 ?
確かに v2 は単一階層構造なのでここで初期化しておけば、あとはそれを利用すれば良いのに対して、v1 はいくつでもマウントできるので後で設定してもいいのかな? と思えますが、もしかして今後は v2 をデフォルトにしたいという考えの現れ?
3.15 kernel では
ちなみに v2 のコードがまだそれほど入っていないころの 3.15 だと、cgroup_init 内で cgroup_setup_root が呼ばれているのは同じですが、cgroup_setup_root は
static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask) { :(snip) ret = cgroup_addrm_files(root_cgrp, cgroup_base_files, true);
と条件文はなくて直接 cgroup_base_files が渡されており、cgroup_base_files は
static struct cftype cgroup_base_files[] = { { .name = "cgroup.procs", : (snip) }, { .name = "cgroup.clone_children", .flags = CFTYPE_INSANE, : (snip0) }, { .name = "cgroup.sane_behavior", .flags = CFTYPE_ONLY_ON_ROOT, : (snip) }, /* * Historical crazy stuff. These don't have "cgroup." prefix and * don't exist if sane_behavior. If you're depending on these, be * prepared to be burned. */ { .name = "tasks", .flags = CFTYPE_INSANE, /* use "procs" instead */ : (snip) }, { .name = "notify_on_release", .flags = CFTYPE_INSANE, : (snip) }, { .name = "release_agent", .flags = CFTYPE_INSANE | CFTYPE_ONLY_ON_ROOT, : (snip) }, { } /* terminate */ };
というふうに v1 のファイルは flags に CFTYPE_INSANE ってのが指定されています。追加の際に v1, v2 を判定して追加するかどうかを決めています。
static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], bool is_add) { : (snip) for (cft = cfts; cft->name[0] != '\0'; cft++) { /* does cft->flags tell us to skip this file on @cgrp? */ if ((cft->flags & CFTYPE_ONLY_ON_DFL) && !cgroup_on_dfl(cgrp)) continue; if ((cft->flags & CFTYPE_INSANE) && cgroup_sane_behavior(cgrp)) continue; if ((cft->flags & CFTYPE_NOT_ON_ROOT) && !cgrp->parent) continue; if ((cft->flags & CFTYPE_ONLY_ON_ROOT) && cgrp->parent) continue; if (is_add) { ret = cgroup_add_file(cgrp, cft); : (snip) }
という風に v1, v2 混在したようなコードになってます。
Plamo Linux で Let's Encrypt
はじめに (読まなくても問題ありません)
自前で Plamo Linux で運用しているサーバはずっと StartSSL を使って証明書を取得していました。
Let's Encrypt が出てきた時は、いろいろ調べてみるとコマンド著名ディストリビューション前提で、Plamo には対応していないようなので、更新時期がきた証明書も引き続き StartSSL で取得しました。
その後、Zenlogicで運用している趣味のブログをZenlogicの機能で Let's Encrypt を使った SSL/TLS 化をしたのをきっかけに少し調べてみると、Plamo Linux でも Let's Encrypt の証明書が取得できそうなので、試してみました。
最近は certbot というコマンドを使うようですね。
まず最初に考えたのが、Plamo 用のパッケージを作ってしまうことです。SlackwareでもSlackbuildsにパッケージがありますし、ArchLinuxのスクリプトを見てもそれほど難しくはなさそうです。
しかし、依存パッケージが多い! いくら python setup.py を実行するだけとはいえ、これだけのためにいくつものパッケージを自作するのは面倒だし、それだけのためにたくさんパッケージを入れるのもなんか無駄な気がします。
というわけで、certbot-auto コマンドを実行すると、virtualenv を使った環境を作って、そこで必要なことはやってくれるようなので、お気楽にやってみようということで試しました。
準備とインストール
まずは必要最低限準備するパッケージをインストールしました。(私の作ったオレオレパッケージを公開しているのでリンクしておきます)
- Plamo 公式の python2_setuptools パッケージ
- pip (オレオレパッケージ(python2_pip))
- virtualenv (オレオレパッケージ (python2_virtualenv))
pip, virtualenv は Plamo 標準ではパッケージが準備されていませんので、適当にインストールするなり、私の作ったオレオレビルドスクリプト(python2_pip)、(python2_virtualenv)でパッケージを作成するなりしてインストールします (pip パッケージの作成には他にもパッケージインストールが必要かもしれません)。
以下では standalone プラグインしか使ってないので、他のプラグインを使う場合は他にも準備が必要かもしれません (試してません)。
さて、ここまできたらおもむろに certbot を取得します。
$ git clone https://github.com/certbot/certbot.git
準備は OK です。clone した certbot ディレクトリに移動して、virtualenv 環境をします。
$ cd certbot $ sh tools/venv.sh
これでいろいろと準備が行われて環境が構築されます。
動作テスト
きちんと動作するか確認してみましょう。activate を実行して virtualenv 環境に入り (?)、
# source certbot/venv/bin/activate (venv) # certbot --help all usage: certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ... Certbot can obtain and install HTTPS/TLS/SSL certificates. By default, it will attempt to use a webserver both for obtaining and installing the cert. The most common SUBCOMMANDS and flags are: :(略)
とりあえず help を表示させてみました。うまくセットアップされていないと、ここでエラーになります。
証明書発行のテスト
これであとは証明書を発行すれば良いのですが、スクリプト化をしたり、テスト実行したりしたいでしょうから、何回も証明書の発行を行うこともあるかもしれません。
Let's Encrypt は一定期間中に発行できる証明書数の上限がありますので、とりあえずテストの証明書を発行してみましょう。そのためにステージング環境が準備されています。これで発行した証明書はブラウザでは警告が出てページがそのままでは表示されませんが、証明書の発行テストにはなります。
ステージング環境で実行するには、本番と同じコマンドに "--staging" とつけるだけなので簡単です。さらにまずは "--dry-run" でオプションに間違いがないかを確認してみましょう。
"--standalone" をつけて standalone プラグインを使う場合は、ローカルで Web サーバが起動しますので、Apache や nginx など、他の Web サーバが起動している場合は停止しましょう。
# apachectl stop # certbot certonly --dry-run --standalone --domain www.example.com --email myaddress@example.com --non-interactive --staging Saving debug log to /var/log/letsencrypt/letsencrypt.log Obtaining a new certificate Performing the following challenges: tls-sni-01 challenge for www.example.com Waiting for verification... Cleaning up challenges Generating key (2048 bits): /etc/letsencrypt/keys/0001_key-certbot.pem Creating CSR: /etc/letsencrypt/csr/0001_csr-certbot.pem IMPORTANT NOTES: - The dry run was successful.
うまくいきそうなら、"--dry-run" を外して実行しましょう。
発行のテストが済んだら証明書更新のテストもやってみましょう。有効な証明書があると言われるので、強制的に新しい証明書を発行するために "--force-renewal" をつけます。
certbot renew --force-renewal --staging
発行
"--staging" で発行できたら、いよいよ本番です。
手元でステージングで発行した証明書がある場合、本番でも有効な証明書があるから発行できないと怒られるかもしれません (いろいろ試してたのでどうだったか忘れたw)。その場合は先と同様に "--force-renewal" をつけます。
# certbot certonly --standalone --domain www.example.com --email myaddress@example.com --non-interactive --agree-tos Saving debug log to /var/log/letsencrypt/letsencrypt.log Obtaining a new certificate Performing the following challenges: :(snip)
更新は "renew" コマンドを実行するだけですが、cron なんかで自動実行する場合は、起動中の Web サーバの停止・起動が必要ですので、
certbot renew --force-renewal --pre-hook "apachectl stop" --post-hook "apachectl start"
みたいにしておくと、確認の前後で hook を実行してくれます。
certbot 実行の詳しくは以下が参考になります (以上で書いたのは適当です)。