先日、きりきりやままさんがこのような記事を公開していました
UTF-8 の文字列をできる限り Shift_JIS に変換したい - きりきりやま
それでは実際にそのような文字列変換を行うにはどうすればよいのか、またコメントでiconvについて触れられていたので、この記事ではUnicodeにおけるNFKC正規化をどうやって行うのか試してみることにしました。
僕にとって文字列処理といえばRubyなので、まずは以下のようなscriptを書いてみました。
puts "\u304c"
puts "String#encode('Shift_JIS') => #{"\u304c".encode('Shift_JIS').inspect}"
puts "codepoints => #{"\u304c".codepoints}"
puts "NFKC normalized codepoints => #{"\u304c".unicode_normalize(:nfkc).codepoints}"
puts "Encode to SJIS with NFKC => #{"\u304c".unicode_normalize(:nfkc).encode('Shift_JIS').inspect}"
puts "=" * 20
puts "\u304b\u3099"
puts "String#encode('Shift_JIS', undef: :replace) => #{"\u304b\u3099".encode('Shift_JIS', undef: :replace).inspect}"
puts "codepoints => #{"\u304b\u3099".codepoints.inspect}"
puts "NFKC normalized codepoints => #{"\u304b\u3099".unicode_normalize(:nfkc).codepoints}"
puts "Encode to SJIS with NFKC => #{"\u304b\u3099".unicode_normalize(:nfkc).encode('Shift_JIS').inspect}"
puts "=" * 20
puts "\u0063\u006d"
puts "String#encode('Shift_JIS') => #{"\u0063\u006d".encode('Shift_JIS').inspect}"
puts "codepoints => #{"\u0063\u006d".codepoints}"
puts "NFKC normalized codepoints => #{"\u0063\u006d".unicode_normalize(:nfkc).codepoints}"
puts "=" * 20
puts "\u339d"
puts "String#encode('Shift_JIS', undef: :replace) => #{"\u339d".encode('Shift_JIS', undef: :replace).inspect}"
puts "codepoints => #{"\u339d".codepoints}"
puts "NFKC normalized codepoints => #{"\u339d".unicode_normalize(:nfkc).codepoints}"
$ ruby script.rb
が
String#encode('Shift_JIS') => "\x{82AA}"
codepoints => [12364]
NFKC normalized codepoints => [12364]
Encode to SJIS with NFKC => "\x{82AA}"
====================
が
String#encode('Shift_JIS', undef: :replace) => "\x{82A9}?"
codepoints => [12363, 12441]
NFKC normalized codepoints => [12364]
Encode to SJIS with NFKC => "\x{82AA}"
====================
cm
String#encode('Shift_JIS') => "cm"
codepoints => [99, 109]
NFKC normalized codepoints => [99, 109]
====================
㎝
String#encode('Shift_JIS', undef: :replace) => "?"
codepoints => [13213]
NFKC normalized codepoints => [99, 109]
https://wandbox.org/permlink/CQaSM6ffOHc0zLu6
Rubyにおいては、Unicode正規化を行うには String#unicode_normalize
によって行うことができます。その際にoptionとして正規化の形式を指定することができます。とても簡単ですね。
https://docs.ruby-lang.org/ja/latest/class/String.html#I_UNICODE_NORMALIZE
import unicodedata
print('\u304c (U+304c)')
print('codepoints => ', end='')
for char in '\u304c'.strip():
print(hex(ord(char)) + ' ' , end='')
print()
print('NFKC normalized codepoints => ', end='')
for char in unicodedata.normalize('NFKC', '\u304c').strip():
print(hex(ord(char)) + ' ' , end='')
print()
print('=' * 20)
print('\u304b\u3099 (U+304b U+3099)')
print('codepoints => ', end='')
for char in '\u304b\u3099'.strip():
print(hex(ord(char)) + ' ' , end='')
print()
print('NFKC normalized codepoints => ', end='')
for char in unicodedata.normalize('NFKC', '\u304b\u3099').strip():
print(hex(ord(char)), end='')
print()
print('=' * 20)
print('\u0063\u006d (U+0063 U+006d)')
print('codepoints => ', end='')
for char in '\u0063\u006d'.strip():
print(hex(ord(char)) + ' ' , end='')
print()
print('NFKC normalized codepoints => ', end='')
for char in unicodedata.normalize('NFKC', '\u0063\u006d').strip():
print(hex(ord(char)) + ' ' , end='')
print()
print('=' * 20)
print('\u339d (U+339d)')
print('codepoints => ', end='')
for char in '\u339d'.strip():
print(hex(ord(char)) + ' ' , end='')
print()
print('NFKC normalized codepoints => ', end='')
for char in unicodedata.normalize('NFKC', '\u339d').strip():
print(hex(ord(char)) + ' ' , end='')
print()
https://wandbox.org/permlink/cMc7S5blWLZLLObD
Pythonにおいては、unicodedata
モジュールをインポートすることによって使用できる unicodedata.normalize
により、形式を指定して正規化を行うことができます。
unicodedata — Unicode データベース — Python 3.8.5 ドキュメント
package main
import (
"fmt"
"strings"
"unicode/utf8"
"golang.org/x/text/unicode/norm"
)
func printCodepoints(str string) {
fmt.Print("codepoints => ")
for i, w := 0, 0; i < len(str); i += w {
runeValue, width := utf8.DecodeRuneInString(str[i:])
fmt.Printf("%U ", runeValue)
w = width
}
fmt.Print("\n")
}
func main() {
fmt.Println("\u304c (U+304c)")
printCodepoints("\u304c")
fmt.Print("NFKC normalized ")
printCodepoints(norm.NFKC.String("\u304c"))
fmt.Println(strings.Repeat("=", 20))
fmt.Println("\u304b\u3099 (U+204c u+3099)")
printCodepoints("\u304b\u3099")
fmt.Print("NFKC normalized ")
printCodepoints(norm.NFKC.String("\u304b\u3099"))
fmt.Println(strings.Repeat("=", 20))
fmt.Println("\u0063\u006d (U+0063 U+006d)")
printCodepoints("\u0063\u006d")
fmt.Print("NFKC normalized ")
printCodepoints(norm.NFKC.String("\u0063\u006d"))
fmt.Println(strings.Repeat("=", 20))
fmt.Println("\u339d (U+339d)")
printCodepoints("\u339d")
fmt.Print("NFKC normalized ")
printCodepoints(norm.NFKC.String("\u339d"))
}
https://play.golang.org/p/xG255G32mlJ
Goでは、norm
packageを使用することで正規化を行うことができます。
// function from https://jsprimer.net/basic/string-unicode/#code-point-is-not-code-unit
function convertCodeUnits(str) {
const codeUnits = [];
for (let i = 0; i < str.length; i++) {
codeUnits.push(str.charCodeAt(i).toString(16));
}
return codeUnits;
}
console.log('\u304c (U+304c)')
console.log('codepoints => ' + convertCodeUnits('\u304c'))
console.log('NFKC normalized codepoints => ' + convertCodeUnits('\u304c'.normalize('NFKC')))
console.log('=' .repeat(20))
console.log('\u304b\u3099 (U+304b U+3099)')
console.log('codepoints => ' + convertCodeUnits('\u304b\u3099'))
console.log('NFKC normalized codepoints => ' + convertCodeUnits('\u304b\u3099'.normalize('NFKC')))
console.log('='.repeat(20))
console.log('\u0063\u006d (U+0063 U+006d)')
console.log('codepoints => ' + convertCodeUnits('\u0063\u006d'))
console.log('NFKC normalized codepoints => ' + convertCodeUnits('\u0063\u006d'.normalize('NFKC')))
console.log('='.repeat(20))
console.log('\u339d (U+339d)')
console.log('codepoints => ' + convertCodeUnits('\u339d'))
console.log('NFKC normalized codepoints => ' + convertCodeUnits('\u339d'.normalize('NFKC')))
console.log('='.repeat(20))
https://wandbox.org/permlink/JLQH8LasdQo9ewgS
JavaScriptでは、 String.prototype.normalize()
によって正規化を行うことができます。
それでは他のツールはどうでしょうか。
nkfはNetwork Kanji Filterの略で、古くからある文字コード変換ツールです。
https://ja.osdn.net/projects/nkf/
Rubyはnkfを同梱しているので、手軽に試すことができます。今回は一度Shift_JISに変換してからUTF-8に戻すことで、正しく変換できているかを確認してみます。
require 'kconv' # kconvはnkfのラッパーです
puts "\u304c (U+304c)"
puts "Endoce to SJIS by nkf => #{"\u304c".tosjis.inspect}"
puts "=" * 20
puts "\u304b\u3099 (U+304b U+3099)"
puts "Endoce to SJIS to UTF-8 by nkf => #{"\u304b\u3099".tosjis.toutf8}"
puts "=" * 20
puts "\u0063\u006d (U+0063 U+006d)"
puts "Endoce to SJIS by nkf => #{"\u0063\u006d".tosjis.inspect}"
puts "=" * 20
puts "\u339d (U+339d)"
puts "Endoce to SJIS to UTF-8 by nkf => #{"\u339d".tosjis.toutf8}"
$ ruby script.rb
が (U+304c)
Endoce to SJIS by nkf => "\x{82AA}"
====================
が (U+304b U+3099)
Endoce to SJIS to UTF-8 by nkf => 縺九y
====================
cm (U+0063 U+006d)
Endoce to SJIS by nkf => "cm"
====================
㎝ (U+339d)
Endoce to SJIS to UTF-8 by nkf => ㎝
このように、「が」(U+304B U+3099
) の変換に失敗していることがわかります。そもそもnkfはUnicodeにおける正規化形式を指定できるのでしょうか。
nkfは2006-03-27にリリースされた 2.0.6 以降 (正確には2.0.6-beta2以降) においてUnicodeの正規化に対応するようになりましたが、「UTF8-MACの範囲のみ」と明言されています。
ここでの UTF-8-MAC
は、macOSがAPFS以前1に採用していた HFS+ というファイルシステムにおいて使用されている正規化形式の通称2で、一見NFD形式のようで互換性のない正規化3を行っています。
nkfは入力においてのみUTF-8-MACを受け付けるようになっているようで、他の正規化形式に対応していません。
nkfにオプションから文字コードを指定した変換をして確かめてみましょう。
# nkf.rbとして保存
require 'nkf'
puts "\u304b\u3099 : U+304b U+3099"
puts "nkf --ic=UTF-8 --oc=Shift_JIS"
ga_to_sjis_from_utf8 = NKF.nkf('--ic=UTF-8 --oc=Shift_JIS', "\u304b\u3099")
puts ga_to_sjis_from_utf8.inspect
puts ga_to_sjis_from_utf8.encode('UTF-8')
puts "=" * 20
puts "nkf --ic=UTF-8-MAC --oc=Shift_JIS"
ga_to_sjis_from_utf8mac = NKF.nkf('--ic=UTF-8-MAC --oc=Shift_JIS', "\u304b\u3099")
puts ga_to_sjis_from_utf8mac.inspect
puts ga_to_sjis_from_utf8mac.encode('UTF-8')
puts "=" * 20
puts "\ufa19 (U+fa19)"
puts "NFD normalized => #{"\ufa19".unicode_normalize(:nfd).inspect}"
puts "NFKC normalized => #{"\ufa19".unicode_normalize(:nfkc).inspect}"
puts "nkf convert => #{NKF.nkf('--ic=UTF-8-MAC --oc=UTF-8', "\ufa19").inspect}"
$ ruby nkf.rb
が : U+304b U+3099
nkf --ic=UTF-8 --oc=Shift_JIS
"\x{82A9}"
か
====================
nkf --ic=UTF-8-MAC --oc=Shift_JIS
"\x{82AA}"
が
====================
神 (U+fa19)
NFD normalized => "\u795E"
NFKC normalized => "\u795E"
nkf convert => "\uFA19"
ところで、㎝ (U+339D
)の変換にも失敗しそうな気がしますが、成功しています。これはどういうことなのでしょうか。
Shift_JISに含まれる文字列の集合はJIS X 0201とJIS X 0208です。このどちらにも1文字で"cm"となる字体は定義されていません。4ではこの「㎝」はどこからやってきたのでしょうか。
「㎝」はNEC特殊文字に含まれており、NECやIBMによるShift_JIS拡張が統合された文字コードであるWindows-31Jに含まれています。これをCP932と呼ぶこともあり5、CP932からUnicodeへの文字変換表には CP932における 0x8770
をUnicodeでの 0x339D
に変換すると定義されています。
https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT
Shift_JISの規定において、0x8770
は「保留域」となっています。6 このことからも、JISに規定されているShift_JISにはなく、それの拡張であるWindows-31Jに含まれている文字であることがわかります。7
また余談として、JIS X 0213にて規定されたShift_JISX0213における 0x8770
に「㎝」の字形が含まれています。8
これもnkfで確認することができ、CP932において拡張された文字を扱わないオプション --no-cp932ext
を指定することで文字が消えていることが確かめられます。
require 'nkf'
puts "\u339d : U+339d"
cm_to_sjis_from_utf8 = NKF.nkf('--ic=UTF-8 --oc=Shift_JIS --no-cp932ext', "\u339d")
puts cm_to_sjis_from_utf8.inspect
puts cm_to_sjis_from_utf8.encode('UTF-8').inspect
puts "=" * 20
$ ruby nkf.rb
㎝ : U+339d
""
""
====================
nkfでは、事前にNFKC正規化を行ってからでないと正しくShift_JISに変換できないことがわかりました。
iconvは、以前はRubyの標準添付ライブラリでしたが、2.0で削除されました。
https://www.ruby-lang.org/ja/news/2013/02/24/ruby-2-0-0-p0-is-released/
現在でもgemとしてインストールできるようにはなっていますが、String#encode
を使用することが推奨されているので、今回はコマンドラインの結果をみることにします。
# iconv.rbとして保存
require 'open3'
puts "\u304c (U+304c)"
puts "String#encode('Shift_JIS') => #{"\u304c".encode('Shift_JIS').inspect}"
Open3.popen2e('iconv --from-code=UTF-8 --to-code=SHIFT-JIS') do |stdin, stdout_e, _|
stdin.print "\u304c"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by iconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
puts "=" * 20
puts "\u304b\u3099 (U+304b U+3099)"
puts "String#encode('Shift_JIS', undef: :replace) => #{"\u304b\u3099".encode('Shift_JIS', undef: :replace).inspect}"
Open3.popen2e('iconv --from-code=UTF-8 --to-code=SHIFT-JIS') do |stdin, stdout_e, _|
stdin.print "\u304b\u3099"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by iconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
puts "=" * 20
puts "\u0063\u006d (U+0063 U+006d)"
puts "String#encode('Shift_JIS') => #{"\u0063\u006d".encode('Shift_JIS').inspect}"
Open3.popen2e('iconv --from-code=UTF-8 --to-code=SHIFT-JIS') do |stdin, stdout_e, _|
stdin.print "\u0063\u006d"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by iconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
puts "=" * 20
puts "\u339d (U+339d)"
puts "String#encode('Shift_JIS', undef: :replace) => #{"\u339d".encode('Shift_JIS', undef: :replace).inspect}"
Open3.popen2e('iconv --from-code=UTF-8 --to-code=SHIFT-JIS') do |stdin, stdout_e, _|
stdin.print "\u339d"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by iconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
Open3.popen2e('iconv --from-code=UTF-8 --to-code=SHIFTJISX0213') do |stdin, stdout_e, _|
stdin.print "\u339d"
stdin.close
result = stdout_e.read
puts "Convert to ShiftJISX0213 by iconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('CP932').encode('UTF-8')}"
end
$ bundle exec ruby iconv.rb
が (U+304c)
String#encode('Shift_JIS') => "\x{82AA}"
Convert to Shift_JIS by iconv => "\x82\xAA"
Re-convert to UTF-8 => が
====================
が (U+304b U+3099)
String#encode('Shift_JIS', undef: :replace) => "\x{82A9}?"
Convert to Shift_JIS by iconv => "\x82\xA9iconv: illegal input sequence at position 3\n"
Re-convert to UTF-8 => かiconv: illegal input sequence at position 3
====================
cm (U+0063 U+006d)
String#encode('Shift_JIS') => "cm"
Convert to Shift_JIS by iconv => "cm"
Re-convert to UTF-8 => cm
====================
㎝ (U+339d)
String#encode('Shift_JIS', undef: :replace) => "?"
Convert to Shift_JIS by iconv => "iconv: illegal input sequence at position 0\n"
Re-convert to UTF-8 => iconv: illegal input sequence at position 0
Convert to ShiftJISX0213 by iconv => "\x87p"
Re-convert to UTF-8 => ㎝
nkfと同様に「が」(U+304B U+3099
) の変換に失敗している様子がわかります。「か」までの出力には成功していることから、濁点 U+3099
の変換に失敗していそうですね。
またnkfについての説明で触れた「㎝」 (U+339D
) については、Shift_JISへの変換は失敗していますが、Shift_JISX0213への変換は成功していますね。
他に指定できそうなoptionもないので、iconvでも事前にNFKC正規化しておく必要がありそうです。
それでは、Rubyを使用せずコマンドラインから使用できる、Unicodeの正規化形式も扱うことのできるツールはないのでしょうか?
これを行うことのできる uconv というものがあります。これはUnicode Consortiumが保守しているInternational Components for Unicodeというコンポーネント(?)に含まれており、Debianにおいては icu-devtools
というパッケージ名で入手できます。
https://packages.debian.org/buster/icu-devtools
uconvに対して -x nfkc
というふうに正規化形式を指定する(正確には、適用したいTransliterationを指定する)ことによって、NFKC正規化がされた上で文字コードの変換ができます。
require 'open3'
puts "\u304c (U+304c)"
puts "String#encode('Shift_JIS') => #{"\u304c".encode('Shift_JIS').inspect}"
Open3.popen2e('uconv --from-code UTF-8 --to-code Shift_JIS -x nfkc') do |stdin, stdout_e, _|
stdin.print "\u304c"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by uconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
puts "=" * 20
puts "\u304b\u3099 (U+304b U+3099)"
puts "String#encode('Shift_JIS', undef: :replace) => #{"\u304b\u3099".encode('Shift_JIS', undef: :replace).inspect}"
Open3.popen2e('uconv --from-code UTF-8 --to-code Shift_JIS -x nfkc') do |stdin, stdout_e, _|
stdin.print "\u304b\u3099"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by uconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
puts "=" * 20
puts "\u0063\u006d (U+0063 U+006d)"
puts "String#encode('Shift_JIS') => #{"\u0063\u006d".encode('Shift_JIS').inspect}"
Open3.popen2e('uconv --from-code UTF-8 --to-code Shift_JIS -x nfkc') do |stdin, stdout_e, _|
stdin.print "\u0063\u006d"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by uconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8')}"
end
puts "=" * 20
puts "\u339d (U+339d)"
puts "String#encode('Shift_JIS', undef: :replace) => #{"\u339d".encode('Shift_JIS', undef: :replace).inspect}"
Open3.popen2e('uconv --from-code UTF-8 --to-code Shift_JIS -x nfkc') do |stdin, stdout_e, _|
stdin.print "\u339d"
stdin.close
result = stdout_e.read
puts "Convert to Shift_JIS by uconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('Shift_JIS').encode('UTF-8').inspect}"
puts "codepoints => #{result.codepoints}"
end
Open3.popen2e('uconv --from-code UTF-8 --to-code cp932') do |stdin, stdout_e, _|
stdin.print "\u339d"
stdin.close
result = stdout_e.read
puts "Convert to CP932 by uconv => #{result.inspect}"
puts "Re-convert to UTF-8 => #{result.force_encoding('CP932').encode('UTF-8')}"
puts "codepoints => #{result.codepoints}"
end
$ bundle exec ruby uconv.rb
が (U+304c)
String#encode('Shift_JIS') => "\x{82AA}"
Convert to Shift_JIS by uconv => "\x82\xAA"
Re-convert to UTF-8 => が
====================
が (U+304b U+3099)
String#encode('Shift_JIS', undef: :replace) => "\x{82A9}?"
Convert to Shift_JIS by uconv => "\x82\xAA"
Re-convert to UTF-8 => が
====================
cm (U+0063 U+006d)
String#encode('Shift_JIS') => "cm"
Convert to Shift_JIS by uconv => "cm"
Re-convert to UTF-8 => cm
====================
㎝ (U+339d)
String#encode('Shift_JIS', undef: :replace) => "?"
Convert to Shift_JIS by uconv => "cm"
Re-convert to UTF-8 => "cm"
codepoints => [99, 109]
Convert to CP932 by uconv => "\x87p"
Re-convert to UTF-8 => ㎝
codepoints => [34672]
このように、NFKC正規化を行ったうえで、正しくShift_JISに変換できていることが、またCP932を指定したときには正規化を行わなくても「㎝」 (U+339D
)を1文字のまま相互に変換できていることがわかります。
ちなみにTransliterationを活用するとこのようにひらがなをローマ字に変換するという面白いこともできます。
$ echo おはようございます | uconv -x '::hiragana-latin;'
ohayougozaimasu
元記事や現在において指定できる正規化形式、NFC、NFD、NFKC、NFKDの4つは、それぞれの名前が規格に登場するのはUnicode 3.0.1からであり、そのリリースは 2000-08-31 です。
https://web.archive.org/web/20050211134342/http://www.unicode.org/unicode/reports/tr15/tr15-19.html
それ以前のリリースにおいては、正規化形式について触れられている記述がなく、前述のUnicode Standard Annex #15から参照できる"Previous Version" においても、 “It is a stable document and may be used as reference material” などの記述が存在しないことから、正規化形式というものが存在するのはUnicode 3.0.1 以降ということになります。
https://web.archive.org/web/20050207015030/http://www.unicode.org/unicode/reports/tr15/tr15-18.html
このあたりの話は、技術評論社から出版されている[改訂新版]プログラマのための文字コード技術入門 の Appendix 4「Unicodeの諸問題」にて詳細に説明されています。この本はとても面白いのでぜひ読んでみてください。
あるUnicode文字列に対してNFKCなどの正規化を適用したい場合、Rubyでは String#unicode_normalize
にオプションとして、コマンドラインでは uconv を使用することで目的を達成できます。文字コード変換で良く知られるnkfやiconvでは適切に正規化が行われていない文字列を変換することができません。
ではAPFSではどうなのかというと、規格書には j_drec_hashed_jey_t
構造体に格納される name_len_and_hash
を計算するときにNFD正規化を行うとこが記載されていますが、ファイル名そのものの正規化についての記述は見付かりませんでした。 https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf 正規化が行われず、与えられたバイト列をそのまま保持するようになっているのか、同じ名前に見えるファイルを複数作成することができるという記事もあります。 https://eclecticlight.co/2017/04/06/apfs-is-currently-unusable-with-most-non-english-languages/ ↩
軽く目を通しましたが、Apple側でこの正規化形式に名前をつけたりはしていないようです。 https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFileSystem/BPFileSystem.html#//apple_ref/doc/uid/10000185-SW1 ただし、Appleの配布しているiconvのencodingには UTF-8-MAC
を指定できるので、ほぼ公式だとしていいでしょう。 ↩
https://developer.apple.com/library/archive/qa/qa1173/_index.html より ↩
JIS X 0208-1997 附属書3 表1より https://www.jisc.go.jp/pdfb6/PDFView/ShowPDF/5gMAAIAz9AGm_33fhhRp ↩
「呼ぶこともあり」というのは、当初定められたCP932をいくつかのベンダが独自拡張したあと、それをMicrosoftが統合したWindows-31JのこともCP932と呼ぶからです。現代においてはCP932 = Windows31J としていいと思いますが。 ↩
JIS X 0208-1997 附属書1より https://www.jisc.go.jp/pdfa8/PDFView/ShowPDF/7gIAAKR6fwk8ZKtaZttC ↩
MSDNに記載されていたCP932の文字一覧より https://web.archive.org/web/20180405180457/https://msdn.microsoft.com/en-us/library/cc194892.aspx ↩
JIS X 0213-2000 附属書4 表23より https://www.jisc.go.jp/pdfa5/PDFView/ShowPDF/5AIAAHLePslwm6mc6z4g ↩
https://unasuke.fm を公開しました。ロゴは衰咲ふち(@otoroesaki)さんに作成していただきました。この場を借りてお礼申しあげます。ありがとうございます。
一旦は今までのエピソードを聞けるようにしただけになります。今後、エピソードごとのサマリやshownoteの記載、RSS feedの公開などの改善を進めていきます。
加えて、次の収録についても日程含め検討中になります。
Podcastは、なぜだかまたブームが来ているようで、AnchorやTransistor、stand.fmなどのプラットフォームが登場しています。 プラットフォームに依存することで、労力の削減や便利な機能を使うことができますが、自分はいちから構築することにしました。
自分が発信する場を他人に委ねる怖さ、というものも多分どこかにあって、一度Twitterのアカウントが凍結されたときにはそれはもう気が気ではありませんでした。ブログをMiddlemanでbuildしてS3にホストしているのも、大して有効活用できていないMastodonを維持し続けているのも、そのためかもしれません。
今回は今までの自分の選択であるMiddlemanによる構築ではなく、Next.jsによる構築を選択しました。
理由は、Webサイトをつくるのであれば、Webの一級市民言語であるJavaScriptでつくるほうがいいのではないかという判断、自分の技術の幅を広げたくて、まだ仕事でも触ったことのないReactに慣れておきたかったという学習目的の2つが主です。また、Next.js を選択するのであれば、素JavaScriptよりは、TypeScriptで書くほうがいいと思い、そのようにしました。
そうなると、TypeScriptとReactとNext.jsの3つを初めて触ることになるので、2つのことを同時に学ばない (by ところてん)ようにするどころか3つのことを同時に学ぼうとしてしまっています。 実際、書いては思うように動かず、何度も友人に助けを求めたり、ひたすらWeb上の記事を読んだりして、それでも全然わからず、多忙を言い訳にしてしばらく放置してしまったりしました。
半分エタりかけていましたが、公式ドキュメントを何度も何度も読んでいくうちになんだかある程度「わかる」ようになりました。結局自分は、悩んで泥臭く試行錯誤を繰り返して手を動かさないと技術を身に付けることはできないんじゃないかと思います。
正直まだまだ機能面や見た目の面でも足りていない部分が多く構築中ですが、オリジナルのファイルがSoundCloud上にもう存在しないので、とりあえずWeb上に参照可能な形で復活させることを優先しました。RSSもないのでPodcastとしては片手落ちですが、"Done is better than perfect" と言いますし、まずは公開することにしました。
まず、Next.jsを静的サイトジェネレーターとして採用しました。そうなると必然、Viewを記述するためにReactを採用することになります。また前述のように、主にTypeScriptで記述しています。ESLintとPrettierを用いてコードフォーマットをしています。
音声ファイルのホスティングについては、転送量による課金のないWasabiを、WebサイトそのもののホスティングについてはFirebase Hostingを選択し、DeployはGitHub Actions経由で行うようにしました。確認用にmaster branchをNetlifyにてHostingしています。
また Content Security Policy を有効化にし、Report Onlyの状態にして report-uri.io に収集させるようにしました。
余談ですが、開発はほぼ全てをWindows 10(not WSL)上で行いました。開発環境において特にハマることはありませんでしたが、ESLintのruleに改行コードをUnix styleかWindows Styleかに設定するものがあり、これの扱いが悩ましいところでした。
— うなすけ (@yu_suke1994) June 28, 2020
まずはこの動画の状況になるまでの出来事を書いていきます。
一度セーフモードに入ると抜けられなくなるというのは結構な絶望感がありました。
という流れだったのでした。イチから環境構築をやりなおしになってしまいましたが、ひとまず直ってよかったです。
※ この記事は復旧させたWindowsのWSL上で書かれました。
久々に自分がReleaseしたので、なにが入ったのかを軽くまとめます。
debug logにどのactionによる実行なのかを表示するものです。descriptionに貼ってあるlogを見るとわかりやすいですね。
check_package_is_installed
calling when package version is not specified by pocke · #314package
resourceにおいて、そのresourceのversionが指定されていない場合には不要となる処理を実行せずfast returnすることによって処理の高速化を図るものです。具体的には。installするactionにおいては、versionが指定されていない場合にはどのversionが存在するかの確認はしなくていいですし、removeするactionにおいてはinstallされているかどうかのみ確認すればよいだけです。
patch authorによるbenchmarkでは1.3倍高速になったとか。
git resourceにおいて、git rev-list
の最初行を取得するのではなく、git rev-parse
の結果を使用するように変更するものです。無駄な出力をさせなくてよくなり、実行速度も少し向上するようです。
SSH時の設定について、 StrictHostKeyChecking
が no
に設定されている場合にはその設定値を削除し、代わりにverify_host_key
を :never
に設定するものです。
net-ssh gemの v6.0.2 において ArgumentError: invalid option(s): strict_host_key_checking
が出てしまったことからいただいたpull requestではあるのですが、itamaeが依存しているSpecinfra側で対応がされたため、こちらでは特に対処しないこととしました。
基本的に僕のレスが遅れがちでちょっと申し訳なさがあります。そして6月もリリースする予定です。
こんにちは、趣味でこのようなものを作っている者です。
https://github.com/unasuke/distroless-ruby
作るだけ作って、実用はしていませんけど。
Distrolessとは何か、ということについては以下の記事をご参照ください。
distrolessイメージを使って、ランタイムDockerイメージを作ってみる - Qiita
さて私がこれまでこのimageをどのように作成していたのかと言いますと、この記事に書いたように人力で依存関係を調べて頑張っていました。 Rubyが動くdistroless image は作ることができるのか - Qiita
Ruby 2.6までは順調だったのですが、2.7ではリンクされるライブラリが増えたのかなんなのか、これまでのように単純なバージョンの変更では上手くいかずに手が止まってしまっていました。
そこに登場したのがこのツール、magicpakです。
これを使えば、Rubyが実行するために必要なファイルを列挙でき、distrolessなimageの作成が楽になるのではないかと考え、試してみました。
では早速次のようなDockerfileを作成し、buildの様子を眺めてみます。
FROM ruby:2.7.0-buster
ADD https://github.com/coord-e/magicpak/releases/latest/download/magicpak-x86_64-unknown-linux-musl /usr/bin/magicpak
RUN chmod +x /usr/bin/magicpak
RUN /usr/bin/magicpak -v $(which ruby) /bundle
docker buildが……
Sending build context to Docker daemon 2.09MB
Step 1/4 : FROM ruby:2.7.0-buster as ruby
---> ea1d77821a3c
Step 2/4 : ADD https://github.com/coord-e/magicpak/releases/latest/download/magicpak-x86_64-unknown-linux-musl /usr/bin/magicpak
Downloading [==================================================>] 4.222MB/4.222MB
---> bd6ae35e14fe
Step 3/4 : RUN chmod +x /usr/bin/magicpak
---> Running in e75094fba6fd
Removing intermediate container e75094fba6fd
---> 8280a91988f3
Step 4/4 : RUN /usr/bin/magicpak -v $(which ruby) /bundle
---> Running in 818a39c7466f
[INFO] exe: loading /usr/local/bin/ruby
[INFO] action: bundle shared object dependencies of /usr/local/bin/ruby
[INFO] exe: loading /usr/local/lib/libruby.so.2.7
################ snip #############################
[INFO] exe: using default interpreter /lib64/ld-linux-x86-64.so.2
[INFO] action: bundle executable /usr/local/bin/ruby as None
[INFO] action: emit /bundle
[INFO] action: emit: creating /bundle as it does not exist
[INFO] emit: link /lib/x86_64-linux-gnu/libpthread-2.28.so => /bundle/lib/x86_64-linux-gnu/libpthread.so.0
[INFO] emit: copy /lib/x86_64-linux-gnu/libpthread-2.28.so => /bundle/lib/x86_64-linux-gnu/libpthread-2.28.so
[INFO] emit: link /usr/local/lib/libruby.so.2.7.0 => /bundle/usr/local/lib/libruby.so.2.7
[INFO] emit: copy /usr/local/lib/libruby.so.2.7.0 => /bundle/usr/local/lib/libruby.so.2.7.0
[INFO] emit: link /lib/x86_64-linux-gnu/libdl-2.28.so => /bundle/lib/x86_64-linux-gnu/libdl.so.2
[INFO] emit: copy /lib/x86_64-linux-gnu/libdl-2.28.so => /bundle/lib/x86_64-linux-gnu/libdl-2.28.so
[INFO] emit: link /lib/x86_64-linux-gnu/libm-2.28.so => /bundle/lib/x86_64-linux-gnu/libm.so.6
[INFO] emit: copy /lib/x86_64-linux-gnu/libm-2.28.so => /bundle/lib/x86_64-linux-gnu/libm-2.28.so
[INFO] emit: link /lib/x86_64-linux-gnu/ld-2.28.so => /bundle/lib64/ld-linux-x86-64.so.2
[INFO] emit: copy /lib/x86_64-linux-gnu/ld-2.28.so => /bundle/lib/x86_64-linux-gnu/ld-2.28.so
[INFO] emit: link /lib/x86_64-linux-gnu/libz.so.1.2.11 => /bundle/lib/x86_64-linux-gnu/libz.so.1
[INFO] emit: copy /lib/x86_64-linux-gnu/libz.so.1.2.11 => /bundle/lib/x86_64-linux-gnu/libz.so.1.2.11
[INFO] emit: link /lib/x86_64-linux-gnu/libcrypt-2.28.so => /bundle/lib/x86_64-linux-gnu/libcrypt.so.1
[INFO] emit: copy /lib/x86_64-linux-gnu/libcrypt-2.28.so => /bundle/lib/x86_64-linux-gnu/libcrypt-2.28.so
[INFO] emit: link /lib/x86_64-linux-gnu/librt-2.28.so => /bundle/lib/x86_64-linux-gnu/librt.so.1
[INFO] emit: copy /lib/x86_64-linux-gnu/librt-2.28.so => /bundle/lib/x86_64-linux-gnu/librt-2.28.so
[INFO] emit: link /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2 => /bundle/usr/lib/x86_64-linux-gnu/libgmp.so.10
[INFO] emit: copy /usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2 => /bundle/usr/lib/x86_64-linux-gnu/libgmp.so.10.3.2
[INFO] emit: copy /usr/local/bin/ruby => /bundle/usr/local/bin/ruby
[INFO] emit: link /lib/x86_64-linux-gnu/libc-2.28.so => /bundle/lib/x86_64-linux-gnu/libc.so.6
[INFO] emit: copy /lib/x86_64-linux-gnu/libc-2.28.so => /bundle/lib/x86_64-linux-gnu/libc-2.28.so
Removing intermediate container 818a39c7466f
---> 077d681487ae
Successfully built 077d681487ae
多いですね。これを /bundle
にまとめて展開すれば動くのでしょうか?次のようなDockerfileを使って試してみましょう。
FROM ruby:2.7.0-buster as ruby
ADD https://github.com/coord-e/magicpak/releases/latest/download/magicpak-x86_64-unknown-linux-musl /usr/bin/magicpak
RUN chmod +x /usr/bin/magicpak
RUN /usr/bin/magicpak -v $(which ruby) /bundle
FROM gcr.io/distroless/base-debian10
COPY --from=ruby /bundle /.
RUN ["/usr/local/bin/ruby", "-v"]
RUN ["/usr/local/bin/gem", "install", "sinatra"]
buildしてみると……
# ...snip...
Step 5/8 : FROM gcr.io/distroless/base-debian10
---> 5bb0e81ff6e4
Step 6/8 : COPY --from=ruby /bundle /.
---> f2a8875bdfdc
Step 7/8 : RUN ["/usr/local/bin/ruby", "-v"]
---> Running in 2cce67b240b1
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
Removing intermediate container 2cce67b240b1
---> 145cba6957e9
Step 8/8 : RUN ["/usr/local/bin/gem", "install", "sinatra"]
---> Running in e445421dbc07
OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"/usr/local/bin/gem\": stat /usr/local/bin/gem: no such file or directory": unknown
/usr/local/bin/gem
がないようです。確かにmagicpakの対象にしたのはrubyコマンドのみで、gemやbundlerに対しては何もしていません。これらも含めてあげましょう。
FROM ruby:2.7.0-buster as ruby
ADD https://github.com/coord-e/magicpak/releases/latest/download/magicpak-x86_64-unknown-linux-musl /usr/bin/magicpak
RUN chmod +x /usr/bin/magicpak
RUN /usr/bin/magicpak -v $(which ruby) /bundle
FROM gcr.io/distroless/base-debian10
COPY --from=ruby /bundle /.
COPY --from=ruby /usr/local/bin/ /usr/local/bin # ここを追加
RUN ["/usr/local/bin/ruby", "-v"]
RUN ["/usr/local/bin/gem", "install", "sinatra"]
結果は……
Step 9/9 : RUN ["/usr/local/bin/gem", "install", "sinatra"]
---> Running in e638dbdb65fd
<internal:gem_prelude>:1:in `require': cannot load such file -- rubygems.rb (LoadError)
from <internal:gem_prelude>:1:in `<internal:gem_prelude>'
The command '/usr/local/bin/gem install sinatra' returned a non-zero code: 1
こんどは rubygems.rb
がみつかりません。これは /usr/local/lib/ruby/
以下にあります。これも含めます。
FROM ruby:2.7.0-buster as ruby
ADD https://github.com/coord-e/magicpak/releases/latest/download/magicpak-x86_64-unknown-linux-musl /usr/bin/magicpak
RUN chmod +x /usr/bin/magicpak
RUN /usr/bin/magicpak -v $(which ruby) /bundle
FROM gcr.io/distroless/base-debian10
COPY --from=ruby /bundle /.
COPY --from=ruby /usr/local/bin/ /usr/local/bin
COPY --from=ruby /usr/local/lib/ruby/ /usr/local/lib/ruby # ここを追加
RUN ["/usr/local/bin/ruby", "-v"]
RUN ["/usr/local/bin/gem", "install", "sinatra"]
さてどうか……
Step 10/10 : RUN ["/usr/local/bin/gem", "install", "sinatra"]
---> Running in 888f7427612a
/usr/local/lib/ruby/2.7.0/yaml.rb:3: warning: It seems your ruby installation is missing psych (for YAML output).
To eliminate this warning, please install libyaml and reinstall your ruby.
/usr/local/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': libyaml-0.so.2: cannot open shared object file: No such file or directory - /usr/local/lib/ruby/2.7.0/x86_64-linux/psych.so (LoadError)
libyamlが見付からないというエラーが出ました。gemコマンドに対してはmagicpakを通してないからでしょうか。ちなみにそれをやってもエラーが出ます。
root@4033859f93d6:/# ./magicpak -v /usr/local/bin/gem /bundle
[INFO] exe: loading /usr/local/bin/gem
[ERROR] error: The executable is malformed: unknown magic number: 7795575320214446371
とりあえず、libyamlを足してやると……
FROM ruby:2.7.0-buster as ruby
ADD https://github.com/coord-e/magicpak/releases/latest/download/magicpak-x86_64-unknown-linux-musl /usr/bin/magicpak
RUN chmod +x /usr/bin/magicpak
RUN /usr/bin/magicpak -v $(which ruby) /bundle
FROM gcr.io/distroless/base-debian10
COPY --from=ruby /bundle /.
COPY --from=ruby /usr/lib/x86_64-linux-gnu/libyaml* /usr/lib/x86_64-linux-gnu/ # ここを追加
COPY --from=ruby /usr/local/bin/ /usr/local/bin
COPY --from=ruby /usr/local/lib/ruby/ /usr/local/lib/ruby
RUN ["/usr/local/bin/ruby", "-v"]
RUN ["/usr/local/bin/gem", "install", "sinatra"]
今度は……
6 gems installed
Removing intermediate container 1a7823880c0d
---> 680de9256898
Successfully built 680de9256898
Successfully tagged unasuke/distroless-ruby:2.7.0-buster
sinatraがインストールできました!ここでは省きますが、ちゃんとリクエストも受けつけるようになっています。imageのサイズは次のようになりました。
image | size |
---|---|
ruby:2.7.0-buster |
842MB |
ruby:2.7.0-slim-buster |
149MB |
ruby:2.7.0-alpine |
51.4MB |
rubylang/ruby:2.7.0-bionic |
359MB |
unasuke/distroless-ruby:2.7.0-buster |
59.4MB |
alpineには敵わないものの、slim-busterのおよそ半分のサイズになりました。まあ、実用性については疑問点が残りますが。
magickpakを使うことで、比較的楽にdistroless-rubyのDocker imageを作成することができました(それまでのtry-and-errorはこの比ではなかったので……)。distroless自体は、Goなどのシングルバイナリが動けばいいimageを作るときには便利に使えるのではないかと思います。
GoogleContainerTools/distroless: 🥑 Language focused docker images, minus the operating system.
皆さん、自宅待機生活はいかがお過ごしでしょうか。昨今はZoomやDiscordなどでビデオ通話をしながら飲み会をするというのが流行っていますね。 そんな会を盛り上げるために、DJをして会話のBGMをいい感じにしたいということがありますね。 ただ、DJをしながら通話に参加するには、ちょっと色々と工夫しないといけなかったので、それをメモとしてまとめます。
まず前提として、DJはWindows上のrekordbox 5及びDDJ-RRで行いました。 これがmacOSの場合は多分こんな苦労は不要です。あくまでもWindows環境での話になります。 また、試せてはいませんがrekordbox 6でもおそらく同様の手順が使えると思います。
以上!
AG03などのミキサーや、物理マシンがもうひとつあればもうすこしうまいことできる気がします。皆さんそれぞれのやり方を公開していただけると有り難いです。
■ - masawadaの日記 を読んで触発され、記録の目的も兼ねて書きます。
一時期、半年ほど1人でリモートワークをしていた時期がありますが、精神に異常をきたしたりしておりません。
— うなすけ (@yu\_suke1994) April 6, 2020
僕自身、一時期半年近くリモートワークをしていたのですが、特に閉塞感がどうこう、というのはありませんでした。(他の要因もあります) ただ昨今の外出自粛な状況になって同様のメンタルを保っていられるかはまた別の話だと思います。
とりあえず最近の状況についてまとめていきます。
「リモートワーク力」というのがあると思います。これは筋力のような身体的なものというよりは、自宅の設備などの話であったり、仕事のスタイルであったりを指して言っています。
僕自身、新卒入社したところでリモートワークが盛んに行われていたこともあり、また将来の展望から、自宅での作業環境についてはそれなりに投資してきています。それはベッドに寝ながら作業をできるようにしたり、身体にあったオフィスチェアを買ったりなどです。これらは数年かけて整えてきたものなので、ここ数ヶ月でいきなりリモートに切り替えるとなると、どうしてもパフォーマンスが落ちたり身体を痛めたり、チームでの仕事がうまくいかなかったり、ということが起こるでしょう。
現時点での作業環境は冒頭画像のようになっています。このPCで通話をしたり、Slackやコラボレーションツールを表示したりして、実際の作業は支給されたマシンを机や膝に置いて行っています。 (手頃な箱を卓上に置いて、その上にマシンを置くことでスタンディングデスクもできます)
この環境のためにベッド上のディスプレイを流用したので、寝ながらの作業はもうできなくなっています。
現在、契約関係にあるのは3社です。このうち1社については感染症の流行以前から完全なリモートワークになっていました。 2社目は、あまり出社しなくてもよい状態だったのが、完全に出社しなくてもよくなりました。 そして3社目(以下A社)では、ここは基本的に出社をしていたのですが、東京都のロックダウン宣言の噂が出た時期あたりから原則としてリモートワークに切り替わりました。
これにより、契約関係のある全ての会社でリモートワークをすることになりました。
一番混乱が大きかったのはA社です。突然リモートワークに切り替わったので、masawadaさんのように家での作業環境が整っていないメンバーがそこそこ居たり、コミュニケーションの方法を検討し直さざるを得なかったりと、多分まだ落ち着いてはいないのかな、という様子を感じています。一番厄介なのがインターネット回線の問題だと思っています。
具体的に起こった問題を挙げます。
A社で、僕が発熱し(COVID-19ではありません)、解熱してから感染防止のため1週間ほどリモートワークをしたときは、ビデオ通話をつけっぱなしにして作業をしていました。これはオフィスのメンバーはオフィスの回線で繋いでいたので問題がなかったのですが、全員自宅勤務に変わってからは、回線の太さやそもそも容量制限のかかっている回線しかなかったりして、ビデオ通話を接続したままということが難しくなってしまいました。いわゆる「空気感」の共有が難しくなったような気がします。
僕も長時間のビデオ会議ということはこれまでやったことがなく、常用のヘッドホンは長時間つけていると耳が痛くなるという問題が発生しました。ひとまず社内でリモートワークにおすすめの機材を教えてもらい、良さそうなものを購入しました。
また、おやつどうする問題があると思います。出社すればおやつが用意してあった環境だと、家で作業するときに口さみしいとか糖分が欲しいのになにもないという状況になりがちです。あとは、用意してあっても飽きるとかですね。 なので、たまたまTwitterでみかけた、スナックミーというサービスを契約してみました。
おやつ体験BOX https://t.co/7oS7mgoJBY のプレミアムドライパイン #うなすけのおやつ #異常独身男性の菓子 #もちぷにゃGET #スナックミー pic.twitter.com/iQnID04uPR
— うなすけ (@yu\_suke1994) April 3, 2020
おやつの好みを選べるし、小分けになってて食べすぎず、美味しいのでオススメです。
全てがつらい。怒っているひとがあまりに多すぎる。特にTwitter。
同感です。これについては、身内しかいないSlackやDiscordでうまいこと発散することで心を穏やかに保つようにしています。公に怒りを増幅させたり、いわゆる冷笑とかをしたりせず、自分自身は普段通りの発言を日々続けていこうと考えています。これは東日本大震災のときの経験が大きいのかもしれません。
ここ最近、食事についてはほとんどUberEatsやChompyなどの出前に頼るか、最寄りのコンビニで済ますようになっています。コンビニ食ばかりだとさすがに飽きがくるので出前を頼りがちですが、それだとどうしても高額になってしまい、これがずっと続くのがよくないことは感じています。
ただ料理をしようにも、コンロが1口しかない狭いキッチンなので、あまり手の込んだものを作ることができません。ホットクックなどを導入するか、キッチンが整った物件に引っ越すこともうっすら考えています。
外に出られない鬱憤については、いわゆる「引きこもり耐性」のある側の人間だと思っているのでそこまで苦ではないですが、イベントが中止になったり中止したりすることがあり、昨今の情勢と重なって気分が沈みがちかもしれません。なので最近は配信をするというていでDJをして楽しんでいます。
圧倒的な実力、この仕事はこの人にお願いしたいという立場になり、リモートワークを認めてもらうことを当初は考えていたが、そういう能力によって待遇に勾配をつけるのもよろしくないように思えてきて、つまり普通にみんながリモートワークを認めてもらえる社会になればいいのに、もしくはそういう会社に勤めればいいという考えに変わってきた。
とある場所でこのような意見を書きました。この、リモートワークが実は可能だったということが判明していき、もっとリモートワークへの障壁が低くなっていくのは、僕にとってはありがたいことです。
また、これを見据えていることもあり、masawadaさんが感じている、終わりが見えないことへの閉塞感というものは、リモートワークに関してはずっと続くことを覚悟していたのであまり感じてはいません。外に出られないのはしんどいですが。
一度引っ越しをしてから地元に戻るのか、今の物件のままでUターンの時期を見計らうかが若干迷いどころですが、いずれは戻りたいです。(実家に、ではない)
完全リモートワークに慣れてから出社生活に切り替わると、通勤の過程がつらくなります。家から出る勇気が必要になります。満員電車は本当にしんどいです。
触発されたといいつつ、書き出してみると「自分は大丈夫ですけど?」みたいな感じになってしまいました。まあ個人差があるということで……
以前、udzuraさんがOCI Runtime Specを簡単にまとめた以下のようなブログを書かれていました。
OCI Runtime Specification を読む - ローファイ日記
この記事にてまとめられているのは 95a6ecf であり、このrevisionから一番近いreleaseはpre-releaseである v1.0.0-rc2 です。
さて、 v1.0.2 のリリースがそろそろということもあり、ここでは簡単のため、v1.0.0-rc2からv1.0.2までの間に入った変更について見てみようと思います。ただし全部は取り上げません。
https://github.com/opencontainers/runtime-spec/releases/tag/v1.0.0-rc3
rcの間は駆け足で行きます。 Windowsについての記述が追加されています。 #565, #573 それに関してか?consoleのサイズについての情報を格納するfiledが #563 で追加されています。これらはMicrosoftの人によるcommitですね。
https://github.com/opencontainers/runtime-spec/releases/tag/v1.0.0-rc4
いくつかのresouceに負の値を指定できるようになっています。でもmemory usage limitとかに負を指定できて嬉しいんだろうか? #648
https://github.com/opencontainers/runtime-spec/releases/tag/v1.0.0-rc5
platformについての記述がREQUIEDに指定されています。 #695 それに関連してWindowsやSolarisについての記述が追加されたり修正されたり。 #673
あ、rc4 で負の値を指定できるようになったいくつかのfieldで、またunsignedに戻されていますね。#704
libseccompのバージョンが v2.3.0からv2.3.2に上がっています。 #705
https://github.com/opencontainers/runtime-spec/releases/tag/v1.0.0-rc6
最後のrc release。 WindowsでのCPU使用率についての記述がperecnt指定からmaximum指定になっていたり。#777 指定したいパーセントに100を掛けた値を設定するように読めますけども。
やっぱりmemory usageに負の値を指定できるようになりました。-1
を指定するとunlimitedの意味になるようですね。 #876
platformについてのfiledが削除されました。runcでは使用されておらず、image-specのみが気にすることだろうということで? #850
https://github.com/opencontainers/runtime-spec/releases/tag/v1.0.0
first release!
disableOOMKiller
がmemory section配下に移動しました。#896
breaking chnageとされているのはこれだけ。
https://github.com/opencontainers/runtime-spec/releases/tag/v1.0.1
ほぼほぼwordingやtypo fixですね。当然ですけど。
https://github.com/opencontainers/runtime-spec/pull/1037
v1.0.1に比べるとたくさんあります。 差分は(多分)これ https://github.com/opencontainers/runtime-spec/compare/v1.0.1…c4ee7d1
hookが追加されました。 createContainer
, startContainer
, createRuntime
の3つです。代わりにprestart
がdeprecatedに指定されています。LXCと同じ名前だとか。 #1008
memoryに関してcgroupのuse_hierarchyが使用できるようになっています。 #985 第3回 Linuxカーネルのコンテナ機能[2] ─cgroupとは?(その1):LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術|gihyo.jp … 技術評論社
WindowsにおいてDeviceをschemaに含められるようになっています。 #976
libseccompが v2.4.0になり、SMCP_ACT_LOG
が使用できるようになっています。#1019
syscallのlogを残すようにする設定? http://man7.org/linux/man-pages/man3/seccomp_init.3.html
また、実装の一覧にgVisor、kara-container、crunが追加されています。crunはOCIのC実装で、runcより高速に動作するようです。元々はRedHatのgiuseppe氏による個人project?がContainers org配下に移動しています。 https://github.com/containers/crun
取り上げられていない変更がめちゃくちゃあるので気になった人は直接見てください。
適当なWindowsが動いているEC2インスタンスがあり、それにリモートデスクトップしてもらいたいということが皆さんにもあるかと思います。そういう時、冒頭の画像にあるように、AWSコンソールからEC2インスタンスを作成し、「接続」からの「リモートデスクトップファイルのダウンロード」からダウンロードしたファイルを開くことで、ユーザーのパスワードを入力すればもう接続できる、というお手軽便利機能があります。
しかし、ざっとaws-sdkを眺めてみましたが、このファイルをAPI経由で取得する方法が見当りませんでした。
では、リモートデスクトップで作業してもらう人にAWSのコンソールには入ってほしくない、というときにはどうすればよいでしょう。
ところで、ダウンロードしたRDPファイルを見てみると、中身はテキストになっていました。
auto connect:i:1
full address:s:ec2-192-0-2-1.ap-northeast-1.compute.amazonaws.com
username:s:Administrator
なんとなく自動で生成できそうに見えます。そして、それぞれがどのような値なのかがここに記載されていました。
サポートされるリモート デスクトップ RDP ファイルの設定 | Microsoft Docs
auto connect:i:1
についてはこのドキュメントに記載がないので触らないようにして、full address:s:
には いわゆるPublic DNS (IPv4)
を設定してやればよさそうです。
また接続するユーザーも、 username:s:
で自由に設定できそうですね。
パスワードだけは別で伝えないといけないのは仕方ありませんが、作業のおまかせが手軽にできそうでよかったです。
C96で友人のサークルをお手伝いしているとき、「サークル参加申込書買ってくるけど、どうする?」と聞かれ、勢いで「僕のぶんもお願い!」と言ったのが全ての始まりでした。 この時、そもそもサークルが存在しない状態であり、その上、何を同人誌として頒布するのかなどは何一つ決まっていませんでした。
その後、直近にあった出来事からサークル名を「キリンセル」とし、まずは日々更新しているpixivFANBOXのまとめを冊子にし、余力があればその他にも技術的なことをまとめて本にしようということになりました。
何なんだマジで pic.twitter.com/DeuNKCpVl3
— うなすけ(C97火曜日南リ17a) (@yu_suke1994) July 16, 2019
結果としては余力などなく。
正直なところ、楽観視していました。というのも、本にする内容は既に大量に存在しているからです。
実際にはログインしないと読めない秘密のコンテンツをどのようにローカルにdumpし、組版ツールに流し込むかなどの部分で大幅にてこずりました。
組版自体はRe:VIEWを使うことにし、TechBooster/ReVIEW-Template の構成をベースにしました。ここで大分楽ができました、ありがとうございます。
このとき、印刷会社の入稿締め切りスケジュールを見ながら作業していたのですが、できあがった原稿が200ページを超えることがまだわかっていませんでした。そしていざ入稿しようとすると、「そのページ数では入稿できない」という事態になり、あわてて別の印刷会社を探すことになったりもしました。
入稿締め切りのギリギリまで表紙のデザイン作業をすることになったりもして、余裕をもったスケジュールがどれだけ大切かを思い知らされました。 (ページ数的に背表紙が存在することが判明したのが結構遅かった)
ファンブックは、主体として動いていたのは蜘蛛糸まなさんなので僕はされるがまま、という感じではありましたが、まれに進捗をつついたり、インタビューの文字起こしをしたりなどのお手伝いをやっていました。これも結構ギリギリまで作業することになってしまい、朝5時くらいまで手を動かす日もありました。
上記の作業と平行して、当日必要なさまざまなものの準備をしていました。
当初、交通系ICカードが使えることからCoineyを申請したのですが、審査が通らずSquareの導入となりました。また、審査に必要であることからサークルWebサイトの作成も平行して行っていました。 (このカード決済用端末に使えるので、iPad Proを買ったり……)
他にも、会計用のコインケース、サークルクロス、お品書き用のポスタースタンドなどなどの購入、当日売り子として手伝ってくれる友人(コミケ初参加)のためにどのようなことをすべきか、どのようなスケジュールでの行動かなどをまとめたものの作成、両替など、様々なタスクにずっと追われていました。
あと、告知配信とか。
キャッシュレス決済は、事前告知配信に来ていただいた(であろう)方々や、同業の方々はすぐクレジットカードを出してくださいました。が、イベント全体としてはやはり現金決済が主流なので、はじめは現金で支払おうとした方も多かった印象です。
単純な決済にかかる時間としては、EMV Contactless = pixiv PAY < 現金決済 < Squareカード決済(要サイン) の順で所要時間が短い(早い)印象でした。
このあたりは、iDやQUICPayが使用できるようになるとまた変わるかもしれないですね。
売上げの管理は、クレジットカードと現金による決済はSquareにて一元管理するようにしました。これだと、レポート画面でどの時間帯にどれだけ売上げがあったのかなどの詳細な情報が入手できてとても便利でした。
事前決済はpixiv PAYによるものを、FANBOXまとめのほうでだけ受け付けていました。利用があってよかったです。
当日、ファンカードによるディスカウントはありませんでした。みんな電子書籍で欲しいのか、それとも来れなかったのか…… ただ、事前決済リンクからの購入であればファンカード提示はなくてもよいので、それも若干あるかもしれません。
ほぼ立ちっぱなしの6時間でした。自分のサークルに居たい気持ちとあちこちに挨拶&買い出しに行きたい気持ちとの戦いで、簡単な挨拶でそそくさ退散してしまったブースがいくつもあります。サークル出展って大変ですね。 当日、差し入れを下さった皆さんありがとうございました。全て美味しく頂きました。