<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: nasa9084</title>
    <description>The latest articles on Forem by nasa9084 (@nasa9084).</description>
    <link>https://forem.com/nasa9084</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F43064%2Fca24f42f-064b-4ea1-814c-0419ecf8328a.png</url>
      <title>Forem: nasa9084</title>
      <link>https://forem.com/nasa9084</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nasa9084"/>
    <language>en</language>
    <item>
      <title>TOTPを実装する</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Fri, 30 Mar 2018 17:03:45 +0000</pubDate>
      <link>https://forem.com/nasa9084/totp-5doe</link>
      <guid>https://forem.com/nasa9084/totp-5doe</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.web-apps.tech%2Fcontent%2Fimages%2F2018%2F03%2Fgopher-5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.web-apps.tech%2Fcontent%2Fimages%2F2018%2F03%2Fgopher-5.png" alt="TOTPを実装する"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ここ数年で多くのサービスで採用されてきている二要素認証ですが、皆さん使っているでしょうか。&lt;br&gt;&lt;br&gt;
私は実は最近までは面倒であまり使っていなかったのですが、ようやく重い腰を上げてあちこち設定しました。&lt;br&gt;&lt;br&gt;
そのうち、近年特によく使われているのがTOTP(Time-Based One-Time Password)と呼ばれるアルゴリズムです。&lt;br&gt;&lt;br&gt;
TOTPアルゴリズムは&lt;a href="https://tools.ietf.org/html/rfc6238" rel="noopener noreferrer"&gt;RFC6238&lt;/a&gt;で定義されたアルゴリズムで、サーバとクライアントが共有する秘密鍵および現在時刻から確認用のコードを生成するものです。&lt;br&gt;&lt;br&gt;
RFCや&lt;a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;を見てわかるよう、かなり簡素なアルゴリズムで、一つ一つ理解していけば比較的簡単に実装することができます。&lt;br&gt;&lt;br&gt;
Go言語のコードを実例に、サンプルコードを実装してみます。&lt;/p&gt;
&lt;h2&gt;
  
  
  HOTPとTOTP
&lt;/h2&gt;

&lt;p&gt;TOTPアルゴリズムとよく似たものに、HOTP(HMAC-Based One-Time Password)と呼ばれるアルゴリズムがあります。&lt;br&gt;&lt;br&gt;
これは、サーバとクライアントが共有する秘密鍵と、「何回目の認証か」から確認用のコードを生成するアルゴリズムです。&lt;br&gt;&lt;br&gt;
HOTPアルゴリズムは(勿論)アルゴリズムですから、ある計算手順であり、秘密鍵と認証回数を引数にとって認証用コードを返す関数として表すことができます。&lt;br&gt;&lt;br&gt;
この、認証回数という引数に対して、現在時刻を入力したものがTOTPです。&lt;br&gt;&lt;br&gt;
認証「回数」というくらいですから、値は正の整数値です。時刻を整数として入力するため、UnixTimeを使用します。&lt;/p&gt;

&lt;p&gt;実際にはUnixTimeそのままで入力すると1秒ごとに認証用コードが変わってしまい実用できではありませんから、ある秒数を一周期として、現在が何周期目なのか、という値を入力します。&lt;/p&gt;
&lt;h2&gt;
  
  
  TOTPを実装する
&lt;/h2&gt;

&lt;p&gt;扨、前置きはこれくらいにしてTOTPアルゴリズムを実装します。&lt;br&gt;&lt;br&gt;
次の式で表されます。&lt;/p&gt;

&lt;p&gt;$$TOTP(K, T_0, X) = HOTP(K, T(T_0, X))$$&lt;br&gt;&lt;br&gt;
$$T(T_0, X) = \frac{(CurrentUnixTime - T_0)}{X}$$&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$K$は共有秘密鍵です。&lt;/li&gt;
&lt;li&gt;$T_0$は数えはじめの時間で、通常はUnix epoch、すなわち0を使用します。&lt;/li&gt;
&lt;li&gt;$X$は一周期の秒数で、規定値は30秒です。(実際、多くのサービスが30秒ベースです)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;プログラム実装は以下の様に書いてみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TOTP(k string, t0, x int) int {
    return HOTP(k, T(t0, x))
}

func T(t0, x int) int {
    return (time.Now().Unix - t0)/x
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;簡単ですね。上記の内、定義されていないのは&lt;code&gt;HOTP(K, T)&lt;/code&gt;だけとなりました。&lt;/p&gt;

&lt;h2&gt;
  
  
  HOTPを実装する
&lt;/h2&gt;

&lt;p&gt;TOTPのコードではHOTPアルゴリズム部分が実装されていませんので、ここを実装すれば実際に使用できるはずです。&lt;br&gt;&lt;br&gt;
HOTPアルゴリズムは&lt;a href="https://tools.ietf.org/html/rfc4226" rel="noopener noreferrer"&gt;RFC4226&lt;/a&gt;で定義されているので、これを読みながら実装します。&lt;/p&gt;

&lt;p&gt;RFCを読むと、HOTPは大きく次の3ステップで求められることがわかります。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;共有秘密鍵と認証回数からHMAC-SHA1の値を求める&lt;/li&gt;
&lt;li&gt;4byteの文字列を生成する&lt;/li&gt;
&lt;li&gt;HOTPの値を計算する&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;何が何やらですね。もう少し詳しく見ていきましょう。&lt;/p&gt;
&lt;h3&gt;
  
  
  1. HMAC-SHA1の値を求める
&lt;/h3&gt;

&lt;p&gt;HMACはメッセージ認証コードの一種で、ハッシュ関数を使用し、秘密鍵とメッセージから認証コードを生成します。&lt;br&gt;&lt;br&gt;
ここでは、その名の通り、ハッシュ関数としてSHA1を使用します。&lt;br&gt;&lt;br&gt;
また、メッセージとして認証回数を使用します。&lt;/p&gt;

&lt;p&gt;HMAC-SHA1の値を$HS$として、式で表すと次のような形です。&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;&lt;br&gt;
HS = HMAC-SHA1(K, C)&lt;br&gt;&lt;br&gt;
]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$K$は秘密鍵&lt;/li&gt;
&lt;li&gt;$C$はメッセージ(ここでは認証回数)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go言語では、HMACもSHA1も標準パッケージに入ってますので、こちらを使用します。&lt;br&gt;&lt;br&gt;
HMACは&lt;a href="https://golang.org/pkg/crypto/hmac" rel="noopener noreferrer"&gt;&lt;code&gt;crypto/hmac&lt;/code&gt;&lt;/a&gt;パッケージ、SHA1は&lt;a href="https://golang.org/pkg/crypto/sha1" rel="noopener noreferrer"&gt;&lt;code&gt;crypto/sha1&lt;/code&gt;&lt;/a&gt;パッケージです。&lt;/p&gt;

&lt;p&gt;実装してみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func HMACSHA1(k, c []byte) []byte {
    mac := hmac.New(sha1.New, k)
    mac.Write(c)
    return mac.Sum(nil)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go言語で、HMACは&lt;code&gt;hash.Hash&lt;/code&gt;として実装されており、&lt;code&gt;hmac.New(func() hash.Hash, []byte)&lt;/code&gt;でハッシュ関数オブジェクトを得て使用します。&lt;br&gt;&lt;br&gt;
SHA1のブロック長&lt;sup&gt;[1]&lt;/sup&gt;は160bitですから、文字列でいうと20文字の文字列を得ることになります。&lt;/p&gt;
&lt;h3&gt;
  
  
  2. 4byteの文字列を生成する
&lt;/h3&gt;

&lt;p&gt;次に、先に計算した$HS$から4byteの文字列を作ります。&lt;br&gt;&lt;br&gt;
RFCの6ページ目に、計算方法が書かれていますので、その通り計算します。&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;&lt;br&gt;
Sbits = DT(HS)&lt;br&gt;&lt;br&gt;
]]&lt;/p&gt;

&lt;p&gt;まず、&lt;code&gt;offsetbits&lt;/code&gt;を求めます。&lt;br&gt;&lt;br&gt;
&lt;code&gt;offsetbits&lt;/code&gt;は$HS$の20文字目(つまり最後の文字)の下位4bitです。&lt;br&gt;&lt;br&gt;
Go言語で扱うのはbyte列ですから、20文字目を8bitまるごと取り出して、上位4bitを0で埋める($00001111_{(2)}$でマスクをかける)ことで下位4bitを取り出したこととします。&lt;br&gt;&lt;br&gt;
なお、Python等では2進数のリテラルもありますが、Goでは2進数のリテラルはありませんので、16進数で表記します($00001111_{(2)}$は$F_{(16)}$です)。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;offsetbits := hs[19] &amp;amp; 0xF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;次に、&lt;code&gt;offset&lt;/code&gt;を求めます。&lt;br&gt;&lt;br&gt;
&lt;code&gt;offset&lt;/code&gt;は、&lt;code&gt;offsetbits&lt;/code&gt;を数値として取り出します。&lt;br&gt;&lt;br&gt;
これは、特別変換を行うわけでは無く、byte列を直接intとして読みます(4bitなので、0〜15の値)。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;offset := int(offsetbits)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;続いて、$HS$の&lt;code&gt;offset&lt;/code&gt;番目の文字から4文字を抜き出し、これを&lt;code&gt;p&lt;/code&gt;とします。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p := hs[offset:offset+4]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;p&lt;/code&gt;の終端31bitを抜き出します。&lt;br&gt;&lt;br&gt;
これも、&lt;code&gt;offsetbits&lt;/code&gt;の時の計算同様、マスクをかけることで同様の処理とします($01111111111111111111111111111111_{(2)}$は$7FFFFFFF_{(16)}$)。&lt;br&gt;&lt;br&gt;
尚、byte列に直接マスクをかけるのは少々面倒ですし、次のステップで数値への変換を行いますので、一旦数値にしてからマスクをかけます。&lt;br&gt;&lt;br&gt;
&lt;code&gt;[]byte&lt;/code&gt;から&lt;code&gt;int&lt;/code&gt;へは直接キャストできないため、&lt;a href="https://golang.org/pkg/encoding/binary" rel="noopener noreferrer"&gt;&lt;code&gt;encoding/binary&lt;/code&gt;&lt;/a&gt;パッケージを使用します。&lt;/p&gt;

&lt;p&gt;ここまでをまとめると以下の様になります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func DT(hs []byte) int {
    offsetbits := hs[19] &amp;amp; 0xF
    offset := int(offsetbits)
    p := hs[offset:offset+4]
    return int(binary.BigEndian.Uint32(p)) &amp;amp; 0x7FFFFFFF
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. HOTPの値を計算する
&lt;/h3&gt;

&lt;p&gt;前のステップで出た値の数値表現と、$10^{Digit}$の剰余をとり、所定の桁数に納めます。&lt;br&gt;&lt;br&gt;
幸い、前のステップの出力値は&lt;code&gt;int&lt;/code&gt;となっていますから、ほとんどそのまま使えます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func ReductionModulo(snum int) int {
    return int(int64(snum) % int64(math.Pow10(g.Digit)))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HOTPアルゴリズムまとめ
&lt;/h3&gt;

&lt;p&gt;ここまでに見てきたステップを合わせて、HOTPを得る関数を作成しましょう。&lt;br&gt;&lt;br&gt;
改めてRFCを眺めると、HOTPは次のように定義されています。&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;&lt;br&gt;
HOTP(K, C) = Truncate(HMAC-SHA-1(K, C))&lt;br&gt;&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;$Truncate$は&lt;code&gt;DT&lt;/code&gt;のことです。&lt;/p&gt;

&lt;p&gt;これをGo言語で書き直すと次のようになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func HOTP(k, c []byte) int {
    return DT(HMACSHA1(k, c))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TOTPに併せてHOTPを修正する
&lt;/h2&gt;

&lt;p&gt;扨、本記事の目標はTOTPアルゴリズムの実装でした。&lt;br&gt;&lt;br&gt;
本記事の冒頭で作成したTOTP関数は、次のようなものでした。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func TOTP(k string, t0, x int) int {
    return HOTP(k, T(t0, x))
}

func T(t0, x int) int {
    return (time.Now().Unix - t0)/x
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;T(t0, x)&lt;/code&gt;の返り値は&lt;code&gt;int&lt;/code&gt;ですから、&lt;code&gt;func HOTP(k, c []byte) int&lt;/code&gt;に渡すことができません。&lt;br&gt;&lt;br&gt;
ここはHOTP関数をちょっと修正してみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func HOTP(k []byte, c int) int {
    cb := make([]byte, 8)
    binary.BigEndian.PutUint64(cb, c)
    return DT(HMACSHA1(k, cb))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;引数を&lt;code&gt;TOTP&lt;/code&gt;関数で与えたい形に合わせ、関数内で型変換ロジックを入れました。&lt;/p&gt;

&lt;p&gt;これで、無事TOTPアルゴリズムを実装することができました。&lt;br&gt;&lt;br&gt;
(本文中のソースコードはテストされていません。 &lt;strong&gt;本番では使用しないでください&lt;/strong&gt; )&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;出力の長さのこと ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>go</category>
      <category>cryptosha1</category>
      <category>encodingbinary</category>
      <category>cryptohmac</category>
    </item>
    <item>
      <title>Let's Encryptでワイルドカード証明書を取得する</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Wed, 14 Mar 2018 02:31:55 +0000</pubDate>
      <link>https://forem.com/nasa9084/lets-encrypt--4jg5</link>
      <guid>https://forem.com/nasa9084/lets-encrypt--4jg5</guid>
      <description>

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VGhlmFxY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/03/letsencrypt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VGhlmFxY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/03/letsencrypt.png" alt="Let's Encryptでワイルドカード証明書を取得する"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;先日twitterでサポートされたと発表されたLet's Encryptのワイルドカード証明書ですが、本日未明、正式にcertbotがワイルドカード証明書に対応したと&lt;a href="https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579"&gt;発表されました&lt;/a&gt;!&lt;sup&gt;[1]&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;早速ですが、実際にワイルドカード証明書を取得してみます。&lt;/p&gt;

&lt;p&gt;尚、今回対象とした環境は以下のとおりです。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CentOS 7&lt;/li&gt;
&lt;li&gt;リバースプロキシとしてnginx&lt;/li&gt;
&lt;li&gt;DNSはGehirn DNS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;では、作業していきます。&lt;/p&gt;

&lt;p&gt;CentOS 7でcertbotを使用する場合、大抵はyumでcertbotをインストールしていると思いますので、まずはアップデートします。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo yum update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;出力は省略しますが、certbotが0.22.0にアップデートされます。&lt;br&gt;&lt;br&gt;
0.22.0はワイルドカード対応バージョンですので、問題ないですね！&lt;/p&gt;

&lt;p&gt;公式のマニュアルでは、&lt;a href="https://certbot.eff.org/lets-encrypt/centosrhel7-nginx"&gt;dns-pluginを使うよう書いてあり&lt;/a&gt;ますが、Gehirn DNSのプラグインは無いため、今回は手動で行きます。&lt;br&gt;&lt;br&gt;
以下のコマンドを実行します。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# certbot certonly --manual --preferred-challenges dns -d *.web-apps.tech --server https://acme-v02.api.letsencrypt.org/directory
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): nasa.9084.bassclarinet@gmail.com
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: a

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: y
Starting new HTTPS connection (1): supporters.eff.org
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for web-apps.tech

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o: y

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.web-apps.tech with the following value:

qiGA8Vep17l0nYJ1O1AdF68D9iT7bL5Mpoe3j7-Caag

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;上記のようにTXTレコードを追加するようにメッセージが出たら、指定された値をDNSに追加します。&lt;br&gt;&lt;br&gt;
Gehirnでは以下のような形になります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cudgwvxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/03/Gehirn_txt_record.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cudgwvxD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/03/Gehirn_txt_record.png" alt="Let's Encryptでワイルドカード証明書を取得する"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;追加したら、digコマンドを使用してDNSレコードが浸透()したことを確認できるまで待ちます。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dig -t txt _acme-challenge.web-apps.tech

(前略)

;; ANSWER SECTION:
_acme-challenge.web-apps.tech. 30 IN    TXT "qiGA8Vep17l0nYJ1O1AdF68D9iT7bL5Mpoe3j7-Caag"

(以下略)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;certbotを実行している端末に戻り、Enterで進みます。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/web-apps.tech/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/web-apps.tech/privkey.pem
   Your cert will expire on 2018-06-12. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
   Donating to EFF: https://eff.org/donate-le
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;上手くいくと、上記のように &lt;code&gt;Congratulations!&lt;/code&gt;が含まれたメッセージが出力されます。&lt;br&gt;&lt;br&gt;
fullchainが&lt;code&gt;/etc/letsencrypt/live/&amp;lt;ドメイン名&amp;gt;/fullchain.pem&lt;/code&gt;に、キーファイルが&lt;code&gt;/etc/letsencrypt/live/&amp;lt;ドメイン名&amp;gt;/privkey.pem&lt;/code&gt;に保存されます。&lt;/p&gt;

&lt;p&gt;自動更新の設定はまた別の記事で。&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;実際は一週間ほど前にリリースはされていたようです ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;


</description>
      <category>letsencrypt</category>
      <category>tls</category>
      <category>certificate</category>
      <category>reverseproxy</category>
    </item>
    <item>
      <title>graceful shutdown server with syg</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Sat, 10 Mar 2018 15:42:54 +0000</pubDate>
      <link>https://forem.com/nasa9084/graceful-shutdown-server-with-syg--334h</link>
      <guid>https://forem.com/nasa9084/graceful-shutdown-server-with-syg--334h</guid>
      <description>&lt;p&gt;&lt;em&gt;originally posted to &lt;a href="https://blog.web-apps.tech/graceful-shutdown-with-syg"&gt;blob.web-apps.tech&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://github.com/nasa9084/syg"&gt;&lt;code&gt;github.com/nasa9084/syg&lt;/code&gt;&lt;/a&gt;, you can map signal to callback function easily.(c.f. &lt;a href="https://dev.to/nasa9084/syg-simply-signal-to-callback-mapping-in-go-24kf"&gt;syg: simply signal to callback mapping in Go&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Now, let's implement a HTTP server which shuts down gracefully when catch SIGINT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package app

import (
    "context"
    "net/http"
    "os"
    "time"

    "github.com/nasa9084/syg"
)

type Server struct {
    server *http.Server
    closed chan struct{}
}

func NewServer() *Server {
    http.HandleFunc("/", longlongHandler)
    return &amp;amp;Server{
        server: &amp;amp;http.Server{
            Addr: ":8080",
        },
        closed: make(chan struct{}),
    }
}

func (s *Server) Run() error {
    // os.Interrupt　= syscall.SIGINT
    cancel := syg.Listen(s.shutdown, os.Interrupt)
    defer cancel()

    err := s.server.ListenAndServe()
    &amp;lt;-s.closed
    return err
}

func (s *Server) shutdown(os.Signal) {
    s.Shutdown(context.Background())
    close(s.closed)
}

func longlongHandler(w http.ResponseWriter, r *http.Request) {
    // a very long process!
    time.Sleep(10 * time.Second)
    w.Write([]byte("hello"))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;call from main:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "log"

    "foo/bar/app" // assume the app package above is in $GOPATH/foo/bar/app
)

func main() {
    s := app.NewServer()
    if err := s.Run(); err != http.ErrServerClosed {
        log.Print(err)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In main, you don't need to think goroutine, graceful shutdown, or signal listening.&lt;/p&gt;

&lt;p&gt;Check the server is running well.&lt;/p&gt;

&lt;p&gt;At first, build and run the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go build main.go
&lt;span class="nv"&gt;$ &lt;/span&gt;./main

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then launch one more Terminal, and send a request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl localhost:8080
hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after 10 seconds, message "hello" is returned.&lt;/p&gt;

&lt;p&gt;Now, send request once again, type &lt;code&gt;Ctrl-C&lt;/code&gt;(this means SIGINT) in the first terminal before returned the response.&lt;br&gt;
The server will shut down after return the response, not suddenly.&lt;/p&gt;

&lt;p&gt;We did it!&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>syg: simply signal to callback mapping in Go</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Wed, 07 Mar 2018 04:24:11 +0000</pubDate>
      <link>https://forem.com/nasa9084/syg-simply-signal-to-callback-mapping-in-go-24kf</link>
      <guid>https://forem.com/nasa9084/syg-simply-signal-to-callback-mapping-in-go-24kf</guid>
      <description>&lt;p&gt;When you want to catch system signals in Go, you can use &lt;code&gt;os&lt;/code&gt; package, &lt;code&gt;os/signal&lt;/code&gt; package, and &lt;code&gt;syscall&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Here's an example of catching a system signal then shutdown the HTTP server gracefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sigCh&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sigCh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// configure server properties&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;sigCh&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrServerClosed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// error handling&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, this is a very simple pattern.&lt;br&gt;
If server downed with some error before signal is sent, the goroutine which listening the signal will leak.&lt;br&gt;
So, we use &lt;code&gt;context&lt;/code&gt; and for-select loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sigCh&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sigCh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// configure server properties&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;sigCh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrServerClosed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// error handling&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmmm.... This is also a very simple pattern, there's no error handling code, no server configure, or no flag parsing.&lt;br&gt;
However, the code is so complex. I cannot remember this signal catch idiom.&lt;/p&gt;

&lt;p&gt;So I created &lt;a href="https://github.com/nasa9084/syg"&gt;&lt;code&gt;github.com/nasa9084/syg&lt;/code&gt;&lt;/a&gt; package for listen signals and call a callback function.&lt;/p&gt;
&lt;h2&gt;
  
  
  github.com/nasa9084/syg
&lt;/h2&gt;

&lt;p&gt;Rethink the example code.&lt;br&gt;
I rewrite the example code with &lt;code&gt;syg&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// configure server properties&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;syg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrServerClosed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// error handling&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wow, very shortened.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;syg&lt;/code&gt; package has only two function, &lt;code&gt;Listen()&lt;/code&gt; and &lt;code&gt;ListenContext()&lt;/code&gt;.&lt;br&gt;
As you thought, these are essentially same function.&lt;br&gt;
&lt;code&gt;ListenContext()&lt;/code&gt; takes &lt;code&gt;context.Context&lt;/code&gt;, but &lt;code&gt;Listen()&lt;/code&gt; is not.&lt;br&gt;
&lt;code&gt;Listen()&lt;/code&gt; calls &lt;code&gt;context.Background()&lt;/code&gt; inside the function, and calls &lt;code&gt;ListenContext()&lt;/code&gt; with that context.&lt;/p&gt;

&lt;p&gt;These functions calls &lt;code&gt;signal.Notify()&lt;/code&gt; and generates goroutine which waits the signal, and returns the cancel function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;syg&lt;/code&gt; package is very small package, but it is very useful.&lt;br&gt;
Please use this package and make issues or PR if you find problems or enhancements!&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Goでコマンドラインオプションを処理する</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Sat, 24 Feb 2018 03:22:28 +0000</pubDate>
      <link>https://forem.com/nasa9084/go-3fma</link>
      <guid>https://forem.com/nasa9084/go-3fma</guid>
      <description>

&lt;p&gt;&lt;em&gt;this post originally appears on &lt;a href="https://blog.web-apps.tech/"&gt;blog.web-apps.tech&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;github.com/jessevdk/go-flags&lt;/code&gt;が便利&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  flagパッケージ
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U6N-EOGd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/02/gopher.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U6N-EOGd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/02/gopher.png" alt="Goでコマンドラインオプションを処理する"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;コマンドライン・ツールを作ろうと考えたとき、避けては通れないのがコマンドラインオプションを如何に処理するか、ということです。&lt;/p&gt;

&lt;p&gt;Go言語では、標準パッケージに&lt;code&gt;flag&lt;/code&gt;というパッケージが存在し、これを用いることでコマンドラインオプションをパースすることが出来ます。&lt;br&gt;&lt;br&gt;
しかし、&lt;code&gt;flag&lt;/code&gt;パッケージでは、ロングオプションとショートオプションを一度に定義することが出来ず、また、ロングオプションであろうとショートオプションであろうと&lt;code&gt;-XXX&lt;/code&gt;という、ハイフンが一つつく形式のオプションとなります。&lt;br&gt;&lt;br&gt;
これはあまり一般的ではなく&lt;sup&gt;[1]&lt;/sup&gt;、便利とも言いにくいでしょう。&lt;/p&gt;

&lt;p&gt;そこで便利なのが、&lt;a href="https://github.com/jessevdk/go-flags"&gt;&lt;code&gt;jessevdk/go-flags&lt;/code&gt;&lt;/a&gt;パッケージです。&lt;/p&gt;

&lt;h2&gt;
  
  
  go-flagsパッケージ
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/jessevdk/go-flags"&gt;&lt;code&gt;jessevdk/go-flags&lt;/code&gt;&lt;/a&gt;パッケージは、その名の通り、コマンドラインオプションを取り扱うパッケージです。&lt;br&gt;&lt;br&gt;
ショートオプション、ロングオプションはもちろんのこと、ショートオプションをまとめて指定する、同じオプションを違う引数で複数回指定する、環境変数からの読み込み、デフォルト値の指定などに対応していて、一般的なオプションの処理に幅広く対応出来ます。&lt;br&gt;&lt;br&gt;
オプションは構造体として定義出来るため、パースした後の処理で取り回すのも簡単です。&lt;/p&gt;

&lt;p&gt;簡単な例を見てみましょう。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type options struct {
    Name string `short:"n" long:"name" description: "listen address"`
}

func main() {
    var opts options
    if _, err := flags.Parse(&amp;amp;opts); err != nil {
        // some error handling
        return
    }
    fmt.Printf("Hello, %s\n", opts.name)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;パッケージ宣言やインポートの節は省略していますが、上記をmain.goとして実行すると、以下の様に出力されます。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go run main.go -h
Usage:
  main [OPTIONS]

Application Options:
  -n, --name= listen address

Help Options:
  -h, --help Show this help message

$ go run main.go -n Foo
Hello, Foo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  フィールドの設定
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;jessevdk/go-flags&lt;/code&gt;パッケージでは、オプションを定義するために構造体を作ります。&lt;br&gt;&lt;br&gt;
構造体の各フィールドがオプション一つ一つに当たります。&lt;br&gt;&lt;br&gt;
そのため、細かい設定はタグで行うことになります。&lt;br&gt;&lt;br&gt;
以下で、このパッケージで使用できる、主なタグを紹介します。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;short&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;ショートオプションの名前を決定します。ショートオプションですので、一文字をしていします。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;long&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;ロングオプションを指定します。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;ヘルプで表示される、オプションの説明文を指定します。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;no-flag&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;フィールドをオプションとして扱わないときに指定します。値は空値以外とします。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;読み込む環境変数を指定します。環境変数から読み込まれた値は、オプションの規定値となります。(オプションで指定されたものが優先されます)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env-delim&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;sliceやmapのオプションに環境変数から値を読み込む場合、このタグで指定した値で分割されます。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;規定値を設定します。mapやsliceの値の場合は複数回指定することもできます。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default-mask&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;ヘルプで規定値の欄に表示される値を指定します。指定しなかった場合は&lt;code&gt;default&lt;/code&gt;の値が表示されますが、このタグを指定すると表示を上書きできます。&lt;code&gt;-&lt;/code&gt;を指定すると、規定値を表示しない設定となります。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;choice&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;オプションに指定可能な値を定義できます。複数の選択肢を定義する場合は複数回指定します。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;required&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;オプションが必須な場合に指定します。値は空値以外とします。このタグが指定されたオプションが実行時に与えられなかった場合、ErrRequiredを返します。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;positional-args&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;構造体に対してこのタグを指定することで、位置引数を定義できます。&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;このほか、オプショングループ、オプションの名前空間、サブコマンドの定義などに対応しています。&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;javaのオプションなど、全く見ないわけではありませんが ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;


</description>
      <category>go</category>
      <category>commandline</category>
    </item>
    <item>
      <title>kubesprayを使用してkubernetes clusterを構築する(2)</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Fri, 23 Feb 2018 14:45:49 +0000</pubDate>
      <link>https://forem.com/nasa9084/kubespraykubernetes-cluster2-3dd3</link>
      <guid>https://forem.com/nasa9084/kubespraykubernetes-cluster2-3dd3</guid>
      <description>&lt;p&gt;&lt;em&gt;this post originally appears on &lt;a href="https://blog.web-apps.tech/"&gt;blog.web-apps.tech&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kjb1o-Xk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2018/02/kubernetes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kjb1o-Xk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2018/02/kubernetes.png" alt="kubesprayを使用してkubernetes clusterを構築する(2)" width="437" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3ヶ月ほど前に、&lt;a href="https://dev.to/nasa9084/kubespraykubernetes-cluster-bel-temp-slug-6093684"&gt;kubesprayを使用してkubernetes clusterを構築する&lt;/a&gt;という、&lt;a href="https://kubespray.io/"&gt;kubespray&lt;/a&gt;とkubespray-cliを使用してKubernetesクラスタを構築する記事を書きました。&lt;br&gt;&lt;br&gt;
しかし、kubespray-cliはすでに&lt;a href="https://github.com/kubernetes-incubator/kubespray/commit/1869aa39859bff4d27bf1337c1352fd383e980a5"&gt;deprecatedだということなので&lt;/a&gt;、kubespray-cliを使用せずにkubesprayでクラスタを構築する手順をまとめておきます。&lt;/p&gt;
&lt;h2&gt;
  
  
  要件
&lt;/h2&gt;

&lt;p&gt;kubesprayを使用してkubernetesクラスタを構築するための要件は以下のようになっています。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ansible 2.4以降とpython-netaddr (python-netaddrを忘れがちなので注意)

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pip install ansible netaddr&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Jinja 2.9以降(ansibleの依存でインストールされると思います)&lt;/li&gt;
&lt;li&gt;構築先サーバがインターネットに接続できること&lt;/li&gt;
&lt;li&gt;構築先サーバでIPv4 forwardingが有効になっていること

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sysctl -w net.ipv4.ip_forward=1&lt;/code&gt;する(再起動するまで)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/sysctl.conf&lt;/code&gt;に&lt;code&gt;net.ipv4.ip_forward = 1&lt;/code&gt;と記入する(再起動後)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ansibleを実行するマシンから構築先サーバにSSH鍵が渡されていること&lt;/li&gt;
&lt;li&gt;ファイアウォールが無効化されていること

&lt;ul&gt;
&lt;li&gt;ファイアウォールの設定をしっかりできる人は有効でも&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;また、kubesprayには(kubespray-cliのような)inventory生成ツールが付属されており、これを利用する場合はpython3系である必要が有ります。&lt;/p&gt;
&lt;h2&gt;
  
  
  構成
&lt;/h2&gt;

&lt;p&gt;前回の記事同様、以下のIPを持った三台のサーバを対象として構築してみます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;192.168.1.11&lt;/li&gt;
&lt;li&gt;192.168.1.12&lt;/li&gt;
&lt;li&gt;192.168.1.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;それぞれ、IPv4 forwardingが有効化され、firewalldを無効化し、Python 3をインストール済みのCentOS 7のサーバとします。また、kubesprayを実行するローカルマシンから、各サーバのrootユーザにSSH鍵を配置&lt;sup&gt;[1]&lt;/sup&gt;済みとします。&lt;/p&gt;
&lt;h2&gt;
  
  
  手順
&lt;/h2&gt;
&lt;h3&gt;
  
  
  準備
&lt;/h3&gt;

&lt;p&gt;まず、kubesprayをダウンロードします。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clone https://github.com/kubernetes-incubator/kubespray
$ cd kubespray
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;リポジトリのクローンが完了したら、ansibleなどの依存モジュールを導入します&lt;sup&gt;[2]&lt;/sup&gt;。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubespray$ pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;次に、ansible用のインベントリを作成します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubespray$ cp -rfp inventory/sample inventory/mycluster
kubespray$ declare -a IPS=(192.168.1.11 192.168.1.12 192.168.1.13)
CONFIG_FILE=inventory/mycluster/hosts.ini python3 contrib/inventory\builder/inventory.py ${IPS[@]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IPSは対象サーバのIPに合わせて定義をします。&lt;br&gt;&lt;br&gt;
また、環境によっては、&lt;code&gt;python3&lt;/code&gt;コマンドではなく、&lt;code&gt;python&lt;/code&gt;コマンドでPython 3が実行される場合も有ります。適宜読み替えてください。&lt;/p&gt;

&lt;p&gt;最後に、構成するkubernetesクラスタの設定をします。&lt;code&gt;inventory/mycluster/group_vars&lt;/code&gt;ディレクトリにある、&lt;code&gt;all.yml&lt;/code&gt;と&lt;code&gt;k8s-cluster.yml&lt;/code&gt;を適宜変更します。&lt;br&gt;&lt;br&gt;
特に、&lt;code&gt;k8s-cluster.yml&lt;/code&gt;に含まれる、以下の項目は確認しておく必要があるでしょう。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;kube_version

&lt;ul&gt;
&lt;li&gt;kubernetesのバージョンを指定します。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kube_network_plugin

&lt;ul&gt;
&lt;li&gt;kubernetesのnetwork pluginを指定します。初期値はcalicoですが、flannelが一般的です。&lt;sup&gt;[3]&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;kube_service_addresses, kube_pods_subnet

&lt;ul&gt;
&lt;li&gt;kubernetes内部で使用するIPの範囲をCIDR形式で指定します。LAN内のネットワークとかぶらないよう注意しましょう。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dashboard_enabled

&lt;ul&gt;
&lt;li&gt;kubernetes dashboardを用意するかどうかの真偽値です。初期値はtrueです。trueの場合、RBACが有効になっている必要があります。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;helm_enabled, istio_enabled, registry_enabled

&lt;ul&gt;
&lt;li&gt;それぞれ、kubernetes Helm、Istio、Docker registryをデプロイするかどうかの真偽値です。インストールする予定ならここでtrueにしておくと楽です。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;設定が完了したら、準備は終了です。&lt;/p&gt;
&lt;h3&gt;
  
  
  デプロイする
&lt;/h3&gt;

&lt;p&gt;準備ができたら、デプロイしましょう！&lt;br&gt;&lt;br&gt;
とはいっても、ココまで来たら後は普通にansible playbookを流し込むだけです。&lt;br&gt;&lt;br&gt;
次のコマンドを実行します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubespray$ ansible-playbook -i inventory/mycluster/hosts.ini cluster.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;エラーを出さずに終了したら、kubernetesのデプロイは完了です！&lt;/p&gt;

&lt;h2&gt;
  
  
  kubernetesを操作する
&lt;/h2&gt;

&lt;p&gt;デプロイできたら、kubernetesを操作してみましょう。&lt;br&gt;&lt;br&gt;
一番前に指定したサーバにSSHで接続し、&lt;code&gt;kubectl&lt;/code&gt;を使って操作します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready master,node 10d v1.9.2+coreos.0
node2 Ready master,node 10d v1.9.2+coreos.0
node3 Ready node 10d v1.9.2+coreos.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;また、LAN内の他のマシンから操作できるように、管理者アカウントを追加してみましょう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl create serviceaccount nasa
serviceaccount "nasa" created
$ kubectl create clusterrolebinding nasa --clusterrole cluster-admin --serviceaccount=default:nasa
clusterrolebinding "nasa" created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;追加できたら、トークンを確認します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl describe serviceaccount nasa
Name: nasa
Namespace: default
Labels: &amp;lt;none&amp;gt;
Annotations: &amp;lt;none&amp;gt;
Image pull secrets: &amp;lt;none&amp;gt;
Mountable secrets: nasa-token-pcn8j
Tokens: nasa-token-pcn8j
Events: &amp;lt;none&amp;gt;
$ kubectl describe secret nasa-token-pcn8j
Name: nasa-token-pcn8j
Namespace: default
Labels: &amp;lt;none&amp;gt;
Annotations: kubernetes.io/service-account.name=nasa
              kubernetes.io/service-account.uid=8916621a-1010-11e8-8bf3-0200c0a80130

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1090 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwaa3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im5yc2EtdG9rZW4tcGNuOGoiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibmFzYSIsImt1YmVybmV0ZXMuaW8dc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6Ijg5MTY2MjFhLTEwMTAtMTFlOC04YmYzLTAyMDBjMGE4MDEfMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDgkZWZhdWx0Om5hc2FifQ.D1o3Jvko91dX6pk2qG505dd2zaXW468GGc9RT6eSzJlrjEG7UEtjF9vlhy7c3BegjPddpPpHsc_ouMx5BAmFdWh74v-PvxnX0IKsCVt_9dlSAcxbbk2PSOloqiwMxTs5q-j6y0Tx64zKzq5e520cNBQrkjJV96-f_riRHHXCrLXQKh2vroh_kpVDViQPqM-e4UKLU4zINGHnraouc7T95ib5wIMcVHEejgsZvF-hLgItxiMAhu4NQXzJ2gM4tMhXupgQZLL1-N_oqoTCNFssPQcoE9Ziyj9_RBkUoodhizpxGOKMFogUgG07DRae4OkEjywoR5xDAuQSJMPihTPqzw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt;コマンドをインストールした、別のマシンにトークンを設定します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl config set-cluster mycluster --server=https://192.168.1.11:6443 --insecure-skip-tls-verify=true
$ kubectl config set-credentials mycluster --token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwaa3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Im5yc2EtdG9rZW4tcGNuOGoiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibmFzYSIsImt1YmVybmV0ZXMuaW8dc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6Ijg5MTY2MjFhLTEwMTAtMTFlOC04YmYzLTAyMDBjMGE4MDEfMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDgkZWZhdWx0Om5hc2FifQ.D1o3Jvko91dX6pk2qG505dd2zaXW468GGc9RT6eSzJlrjEG7UEtjF9vlhy7c3BegjPddpPpHsc_ouMx5BAmFdWh74v-PvxnX0IKsCVt_9dlSAcxbbk2PSOloqiwMxTs5q-j6y0Tx64zKzq5e520cNBQrkjJV96-f_riRHHXCrLXQKh2vroh_kpVDViQPqM-e4UKLU4zINGHnraouc7T95ib5wIMcVHEejgsZvF-hLgItxiMAhu4NQXzJ2gM4tMhXupgQZLL1-N_oqoTCNFssPQcoE9Ziyj9_RBkUoodhizpxGOKMFogUgG07DRae4OkEjywoR5xDAuQSJMPihTPqzw
$ kubectl config set-context mycluster --cluster=mycluster --user=mycluster
$ kubectl config use-context mycluster
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready master,node 10d v1.9.2+coreos.0
node2 Ready master,node 10d v1.9.2+coreos.0
node3 Ready node 10d v1.9.2+coreos.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;無事、アクセスすることができました！&lt;br&gt;&lt;br&gt;
今回はclusterの設定で&lt;code&gt;--insecure-skip-tls-verify=true&lt;/code&gt;とし、TLSの確認を省略しましたが、マスターノードの&lt;code&gt;/etc/kubernetes/ssl/apiserver.pem&lt;/code&gt;をコピーしてきて、&lt;code&gt;kubectl config set-cluster&lt;/code&gt;の&lt;code&gt;--certificate-authority=&lt;/code&gt;に指定することで、TLSを確認した上で使用することができます。&lt;/p&gt;

&lt;p&gt;楽しいkubernetesライフを送りましょう！&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;&lt;code&gt;ssh-copy-id&lt;/code&gt;コマンドを利用すると便利です。 ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn2"&gt;
&lt;p&gt;環境によってはsudoが必要かもしれません。 ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn3"&gt;
&lt;p&gt;個人的にはcontivで構成しています。深い意味はありませんが、Web UIがついているのが気に入っています。 ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>kubernetes</category>
      <category>kubespray</category>
      <category>docker</category>
      <category>ansible</category>
    </item>
    <item>
      <title>Generates LICENSE file: git-license</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Wed, 21 Feb 2018 04:56:49 +0000</pubDate>
      <link>https://forem.com/nasa9084/generates-license-file-git-license-bc9</link>
      <guid>https://forem.com/nasa9084/generates-license-file-git-license-bc9</guid>
      <description>

&lt;p&gt;&lt;em&gt;this post originally appears on &lt;a href="https://blog.web-apps.tech/"&gt;blog.web-apps.tech&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vBW-S9Yl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/02/git-logo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vBW-S9Yl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/02/git-logo.png" alt="Generates LICENSE file: git-license"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we create a new repository on &lt;a href="https://github.com"&gt;GitHub&lt;/a&gt;, we can choose an open source license.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4jLG9zn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/02/create_new_repo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4jLG9zn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2018/02/create_new_repo.png" alt="Generates LICENSE file: git-license"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We choose an OSS license, then, &lt;code&gt;LICENSE&lt;/code&gt; file is put into the new repository.&lt;/p&gt;

&lt;p&gt;Now, I'm usually using &lt;a href="https://hub.github.com/"&gt;&lt;code&gt;hub&lt;/code&gt;&lt;/a&gt; command to create a new repository.&lt;br&gt;&lt;br&gt;
I'll do below to create:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir my_new_repository
$ cd my_new_repository
$ git init
#
# ... some code writing and commit ...
#
$ git create # git command is aliased to hub
$ git push -u origin master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this flow, I can write description for repository&lt;sup&gt;[1]&lt;/sup&gt;, set homepage&lt;sup&gt;[2]&lt;/sup&gt;, make the repository private&lt;sup&gt;[3]&lt;/sup&gt;, but I &lt;strong&gt;CANNOT&lt;/strong&gt; choose LICENSE.&lt;br&gt;&lt;br&gt;
I can choose and create LICENSE file on the GitHub web page, or I can copy from my other repositories because its content is fixed.&lt;br&gt;&lt;br&gt;
However, I don't do that.&lt;/p&gt;

&lt;p&gt;Thus I created &lt;a href="https://github.com/nasa9084/git-license"&gt;&lt;code&gt;git-license&lt;/code&gt;&lt;/a&gt;, which is subcommand for git.&lt;br&gt;&lt;br&gt;
As you know, when you put &lt;code&gt;git-XXX&lt;/code&gt; executable into your PATH, you can use &lt;code&gt;git-XXX&lt;/code&gt; command as subcommand of git like &lt;code&gt;git XXX&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git-license&lt;/code&gt; is written in Go.&lt;br&gt;&lt;br&gt;
So its executable is only one binary, and it means very easy to start using.&lt;br&gt;&lt;br&gt;
If you have already had Go environment, you can install with:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/nasa9084/go-license
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If not, you can download binary for your os from &lt;a href="https://github.com/nasa9084/git-license/releases"&gt;Releases page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The command creates LICENSE file you choose using &lt;a href="https://developer.github.com/v3/licenses/"&gt;GitHub Licenses API&lt;/a&gt;, so network connection is required.&lt;/p&gt;

&lt;p&gt;If there's some bugs, comment, or enhancement, please feel free to make issues or make Pull Request.&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;with &lt;code&gt;-d&lt;/code&gt; option ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn2"&gt;
&lt;p&gt;with &lt;code&gt;-h&lt;/code&gt; option ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn3"&gt;
&lt;p&gt;with &lt;code&gt;-p&lt;/code&gt; option ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;


</description>
      <category>git</category>
      <category>go</category>
      <category>github</category>
      <category>commandline</category>
    </item>
    <item>
      <title>container-upというツールを書いた</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Fri, 22 Dec 2017 04:23:39 +0000</pubDate>
      <link>https://forem.com/nasa9084/container-up-5823</link>
      <guid>https://forem.com/nasa9084/container-up-5823</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.web-apps.tech%2Fcontent%2Fimages%2F2017%2F12%2Fcontainer-up.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.web-apps.tech%2Fcontent%2Fimages%2F2017%2F12%2Fcontainer-up.png" alt="container-upというツールを書いた"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nasa9084/container-up" rel="noopener noreferrer"&gt;container-up&lt;/a&gt;というツールを書いたのでご紹介。&lt;/p&gt;

&lt;h2&gt;
  
  
  背景
&lt;/h2&gt;

&lt;p&gt;このブログは&lt;a href="https://ghost.org/" rel="noopener noreferrer"&gt;ghost&lt;/a&gt;というブログエンジンで動いています。動作環境として&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;を使用していて、Ghostの&lt;a href="https://hub.docker.com/_/ghost/" rel="noopener noreferrer"&gt;公式イメージ&lt;/a&gt;を使用しています。&lt;br&gt;&lt;br&gt;
過去の経緯から、単体のDockerコンテナで動作させており、永続データはDockerボリュームとしてマウントしている形です。&lt;/p&gt;

&lt;p&gt;扨、Docker ComposeやKubernetesなどのオーケストレーションツールを使っている場合、コンテナのバージョンアップは比較的簡単に行うことができます。&lt;br&gt;&lt;br&gt;
たとえば、Docker Composeを使用している場合、&lt;code&gt;docker-compose up&lt;/code&gt;で、新しいイメージで作成したコンテナに差し替えることができます。&lt;/p&gt;

&lt;p&gt;しかし、Dockerを単体で使っている場合、基本的には手作業で差し替えを行う必要があります。&lt;br&gt;&lt;br&gt;
Ghostコンテナの更新時は手作業でBlue-Greenアップグレードを行ってきたのですが、Ghostはかなりアップデートのペースが速く、毎度コンテナを差し替えるのが面倒になってきました。&lt;br&gt;&lt;br&gt;
それを楽にするため、 &lt;strong&gt;container-up&lt;/strong&gt; を作りました。&lt;/p&gt;
&lt;h2&gt;
  
  
  インストール
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Go環境がある人
&lt;/h3&gt;

&lt;p&gt;Go言語の環境がすでにある人は、以下のコマンドで使用できるようになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/nasa9084/container-up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  それ以外の人
&lt;/h3&gt;

&lt;p&gt;Go言語の環境がない人は、&lt;a href="https://github.com/nasa9084/container-up/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;ページから自分のOSに併せてバイナリをダウンロード、パスを通してください。&lt;br&gt;&lt;br&gt;
windows, linux, macos向け、それぞれamd64版のバイナリを用意してあります。&lt;br&gt;&lt;br&gt;
動作確認はmacos、linux(CentOS 7)のみ行っています。&lt;/p&gt;

&lt;p&gt;これら以外の環境の人は、予め&lt;a href="https://github.com/golang/dep" rel="noopener noreferrer"&gt;dep&lt;/a&gt;をインストールした上で以下のコマンドでコンパイルしてください。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clonse https://github.com/nasa9084/container-up.git
$ cd container-up
$ dep ensure
$ go build -o container-up main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;コンパイルしたら、任意の場所にバイナリを移動し、パスを通してください。&lt;/p&gt;

&lt;h2&gt;
  
  
  使い方
&lt;/h2&gt;

&lt;p&gt;基本的な使い方は、引数にコンテナ名またはコンテナIDを渡すだけです。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ container-up CONTAINER_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;与えられたコンテナと同じ名称のイメージを使用して、ボリュームやネットワークなどの設定はそのままに新しいコンテナを作成し、差し替えます。&lt;br&gt;&lt;br&gt;
&lt;code&gt;:latest&lt;/code&gt;なイメージを使用している場合、&lt;code&gt;docker pull&lt;/code&gt;した後にこのコマンドを実行することで、最新のイメージから作られたコンテナに差し替わるということです。&lt;/p&gt;

&lt;p&gt;もとのコンテナは&lt;code&gt;--rm&lt;/code&gt;オプションをつけて起動していた場合を除いて、&lt;code&gt;_oldContainer&lt;/code&gt;というサフィックスが付いた状態でstopします。&lt;br&gt;&lt;br&gt;
なにか問題があった場合は、このコンテナに戻すと良いでしょう。&lt;/p&gt;

&lt;p&gt;もし、元のコンテナが必要ない場合は、&lt;code&gt;--rm&lt;/code&gt;オプションをつけると、差し替え時に削除します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ container-up --rm CONTAINER_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;:latest&lt;/code&gt;ではないような、バージョンタグが付いたイメージを使用していて、新しいバージョンのイメージを使いたい場合などのため、新しいイメージ名を指定して実行することもできます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ container-up -i IMAGE_NAME CONTAINER_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;この場合、差し替えるコンテナは指定されたイメージで作成されます。&lt;/p&gt;

&lt;p&gt;これらのコマンドでは、ボリュームのマウント設定は引き継ぎますが、それ以外のファイルはすべて新しいイメージに差し替わります。&lt;br&gt;&lt;br&gt;
もし、ボリュームマウントしているところ以外に引き継ぎたいファイルなどがある場合&lt;sup&gt;[1]&lt;/sup&gt;、&lt;code&gt;-f&lt;/code&gt;オプションにファイルパスを指定することで新しいコンテナにコピーすることができます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ container-up -f /path/to/file.ex CONTAINER_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-f&lt;/code&gt;オプションは複数指定することができますので、複数のファイルをコピーしたい場合には複数回指定してください。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ container-up -f /path/to/file1 -f /path/to/file2 CONTAINER_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;現状実装されている機能は以上です。&lt;br&gt;&lt;br&gt;
ヘルプは&lt;code&gt;-h&lt;/code&gt;オプションをつけることで見られます。&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;たとえば設定ファイルなど ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>docker</category>
      <category>go</category>
    </item>
    <item>
      <title>Kubernetes-powered Docker for mac is released!</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Thu, 14 Dec 2017 05:03:01 +0000</pubDate>
      <link>https://forem.com/nasa9084/kubernetes-powered-docker-for-mac-is-released-c6m</link>
      <guid>https://forem.com/nasa9084/kubernetes-powered-docker-for-mac-is-released-c6m</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s2HM5DcH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/kubernetes_logo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s2HM5DcH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/kubernetes_logo.png" alt="Kubernetes-powered Docker for mac is released!" width="437" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DockerCon EU 2017で、DockerがKubernetesを統合・サポートすると&lt;a href="http://www.publickey1.jp/blog/17/dockerkubernetesdockercon_eu_2017.html"&gt;発表されました&lt;/a&gt;が、本日ついにKubernetesサポート版Docker for macが(Edgeリリースですが)リリースされました！&lt;/p&gt;

&lt;p&gt;これにより、macを使用している場合は(おそらく過去最も簡単に)開発用Kubernetesクラスタを起動することができるようになりました！&lt;/p&gt;

&lt;p&gt;この記事では、Docker for macでKubernetesを立ちあげる手順をまとめておきます。&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetesの起動手順
&lt;/h2&gt;

&lt;p&gt;Docker for macのStable版を利用している場合、Edge版をインストールする必要があります。&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.docker.com/docker-for-mac/install/"&gt;Install Docker for mac&lt;/a&gt;のページから、Edge Channelのインストーラをダウンロードし、インストールします。&lt;br&gt;&lt;br&gt;
Dockerを終了させておけば、Stable版を自分でアンインストールする必要はありません&lt;sup&gt;[1]&lt;/sup&gt;。&lt;br&gt;&lt;br&gt;
Edge版をインストールし、Dockerの設定画面を開くと、以下の様にKubernetesタブが追加されています！！&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j_Uqdnis--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-general.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j_Uqdnis--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-general.png" alt="Kubernetes-powered Docker for mac is released!" width="612" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kubernetesタブで、Kubernetesの有効化・無効化を簡単に切り替えることができます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l4Up0rdi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-kubernetes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l4Up0rdi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-kubernetes.png" alt="Kubernetes-powered Docker for mac is released!" width="612" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Enable Kubernetes&lt;/code&gt;にチェックを入れ(&lt;code&gt;Show system containers&lt;/code&gt;はお好みで)、&lt;code&gt;Apply &amp;amp; Restart&lt;/code&gt;ボタンを押すと、「Kubernetesの初回インストールは少し時間かかるけど、インストールする？」という確認画面が出るので、&lt;code&gt;Install&lt;/code&gt;を押します。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4q0BFO5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-progress-kubernetes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4q0BFO5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-progress-kubernetes.png" alt="Kubernetes-powered Docker for mac is released!" width="612" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;一応プログレスバーが出るのですが、余り意味はありませんでした・・・&lt;br&gt;&lt;br&gt;
数分でインストールが終わります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NZbtDBA6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-done-kubernetes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NZbtDBA6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-preference-done-kubernetes.png" alt="Kubernetes-powered Docker for mac is released!" width="584" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;無事インストールが終了したら、&lt;code&gt;Close&lt;/code&gt;を押したあと、設定画面を閉じます。&lt;/p&gt;

&lt;p&gt;この時点で、DockerメニューにもKubernetesのステータスが表示されています。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dIg-YKKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-menu-kubernetes-is-running.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dIg-YKKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-menu-kubernetes-is-running.png" alt="Kubernetes-powered Docker for mac is released!" width="257" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;今回私は&lt;code&gt;Show system containers&lt;/code&gt;をオンにしたので、&lt;code&gt;docker ps&lt;/code&gt;&lt;sup&gt;[2]&lt;/sup&gt;するとKubernetesの動作に必要なコンテナが起動しています。&lt;br&gt;&lt;br&gt;
DNS、API Server、Etcd、Scheduler、Proxyと最小限の構成ですね。&lt;/p&gt;

&lt;p&gt;kubectlが自動でインストールされるのかどうかは・・・・わかりませんでした。(すでにインストール済みだったため)&lt;br&gt;&lt;br&gt;
kubectlがインストール済みの場合、自動でコンフィグが追加され、以下のコマンドでcontextを選択することで操作できるようになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl config use-context docker-for-desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R-01xxSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-kubectl-get-nodes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R-01xxSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/12/docker-kubectl-get-nodes.png" alt="Kubernetes-powered Docker for mac is released!" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl get nodes&lt;/code&gt;すると、確かに1ノード構成でクラスタが立ち上がっているのがわかります。&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn1"&gt;
&lt;p&gt;Edge版の初回起動時にアンインストールされます ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn2"&gt;
&lt;p&gt;&lt;code&gt;docker container ls&lt;/code&gt;と打つのは面倒ですね ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>docker</category>
      <category>macos</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Parcel + Riot.js</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Tue, 12 Dec 2017 15:00:00 +0000</pubDate>
      <link>https://forem.com/nasa9084/parcel--riotjs-cgn</link>
      <guid>https://forem.com/nasa9084/parcel--riotjs-cgn</guid>
      <description>

&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://parceljs.org/"&gt;Parcel&lt;/a&gt;というJavaScriptのモジュールバンドラを触ってみた&lt;/li&gt;
&lt;li&gt;webpackなどと比べて設定ファイルなどもいらずとても簡単&lt;/li&gt;
&lt;li&gt;ホットリロードな開発サーバを簡単に実行できる&lt;/li&gt;
&lt;li&gt;Riotと組み合わせるのもそれほど難しくない&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parcel + Riot.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lKqpRp5h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2017/12/parcel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lKqpRp5h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.web-apps.tech/content/images/2017/12/parcel.png" alt="Parcel + Riot.js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://parceljs.org/"&gt;Parcel&lt;/a&gt;というJavaScriptのモジュールバンドラが話題なのでさわってみました。&lt;br&gt;&lt;br&gt;
国内で話題になっている元の記事は「&lt;a href="https://qiita.com/bitrinjani/items/b08876e0a2618745f54a"&gt;webpack時代の終わりとparcel時代のはじまり&lt;/a&gt;」。&lt;br&gt;&lt;br&gt;
&lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;との組み合わせで記事を書かれています。&lt;/p&gt;

&lt;p&gt;個人的には&lt;a href="http://riotjs.com/ja/"&gt;Riot.js&lt;/a&gt;が好みなので、Riot.jsとの組み合わせで触ってみました。&lt;br&gt;&lt;br&gt;
尚、webpackは挫折したため比較できません。&lt;/p&gt;

&lt;h2&gt;
  
  
  Parcel/Riot.jsのインストール
&lt;/h2&gt;

&lt;p&gt;npmを使ってインストールします。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install -g parcel-bundler riot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  source code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ディレクトリ構造
&lt;/h3&gt;

&lt;p&gt;以下の様なディレクトリ構造だとします。&lt;br&gt;&lt;br&gt;
なお、練習用のため、動作確認に関係ない部分は適当に削っています。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
|- index.html
|- index.js
|- package.json
|- app/
| |- App.tag
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  index.html
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ja"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;App&amp;gt;&amp;lt;/App&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  index.js
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import riot from 'riot'
import './app/tags'

riot.mount('App')
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  App.tag
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;App&amp;gt;
  &amp;lt;h1&amp;gt;Hello, parcel world!&amp;lt;/h1&amp;gt;

  &amp;lt;script&amp;gt;
  import riot from 'riot' 
  &amp;lt;/script&amp;gt;
&amp;lt;/App&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  package.json
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt;は&lt;code&gt;npm init -y&lt;/code&gt;で作成しました。&lt;/p&gt;

&lt;h2&gt;
  
  
  compile/bundle &amp;amp; run
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ riot app/ app/tags.js
$ parcel index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;上記のコマンドで &lt;code&gt;http://localhost:1234&lt;/code&gt; でホットリロードのサーバが動作します。&lt;br&gt;&lt;br&gt;
最終的な成果物を作る場合は &lt;code&gt;parcel build index.html&lt;/code&gt;とすれば良いようです。&lt;/p&gt;

&lt;p&gt;しかし、riot.jsのコンパイルはparcelの前段で別途行っているため、tagファイルの変更はwatchされません。riotコマンドに&lt;code&gt;-w&lt;/code&gt;オプションをつけることでウォッチできますが、そのままだと二つのコマンドを別々の端末で開くなどする必要があり、若干面倒です。&lt;br&gt;&lt;br&gt;
&lt;code&gt;package.json&lt;/code&gt;の&lt;code&gt;scripts&lt;/code&gt;に以下の三つのコマンドを追加します。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"watch": "npm run watch:riot &amp;amp; npm run watch:parcel",
"watch:riot": "riot -w app/ app/tags.js",
"watch:parcel": "parcel index.html"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;追加したら、以下のコマンドを実行します。&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm run watch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;これでコマンド一つでホットリロードの開発サーバを動作させることができます。&lt;/p&gt;


</description>
      <category>javascript</category>
      <category>packagebundler</category>
    </item>
    <item>
      <title>Application Specific Context</title>
      <dc:creator>nasa9084</dc:creator>
      <pubDate>Tue, 21 Nov 2017 04:21:58 +0000</pubDate>
      <link>https://forem.com/nasa9084/application-specific-context-810</link>
      <guid>https://forem.com/nasa9084/application-specific-context-810</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KwO-jZ45--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/11/gopher-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KwO-jZ45--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.web-apps.tech/content/images/2017/11/gopher-1.png" alt="Application Specific Context" width="245" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;元ネタは&lt;a href="https://twitter.com/lestrrat"&gt;@lestrrat&lt;/a&gt;さんの「&lt;a href="https://medium.com/@lestrrat/abusing-type-aliases-to-augment-context-context-a08a85692fa8"&gt;Abusing type aliases to augment context.Context&lt;/a&gt;」。&lt;/p&gt;

&lt;p&gt;golangを用いてHTTPサーバを作る場合、ルーティングを定義するのに以下の様な関数を用います。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http.HandleFunc(path string, handler func(w http.ResponseWriter, r *http.Request)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;もちろん、&lt;code&gt;http.Handle&lt;/code&gt;を用いる場合もありますし、&lt;a href="https://github.com/gorilla/mux"&gt;gorilla/mux&lt;/a&gt;などのライブラリを用いることもあると思います。&lt;br&gt;&lt;br&gt;
ここで重要なのは、&lt;code&gt;func(w http.ResponseWriter, r *http.Request)&lt;/code&gt;という引数の方です。&lt;/p&gt;

&lt;p&gt;多くの場合、アプリケーションのハンドラ内ではデータベースなどの外部アプリケーション・ミドルウェアを用いることになります。&lt;br&gt;&lt;br&gt;
しかし、golangのHTTPアプリケーションでは、ハンドラ関数の形式が&lt;code&gt;func (w http.ResponseWriter, r *http.Request)&lt;/code&gt;と決まっています。引数の追加はできないため、引数以外の方法でDB接続情報などを渡す必要があります。&lt;/p&gt;

&lt;p&gt;これまで、golangでWebアプリケーション開発を行う場合によく用いられていたデータベースコネクションの保持方法は、&lt;code&gt;db&lt;/code&gt;パッケージを作成し、そこにパッケージ変数として持つ方法かと思います。が、&lt;a href="https://dave.cheney.net/2017/06/11/go-without-package-scoped-variables"&gt;グローバルな変数はできるだけ持ちたくない&lt;/a&gt;ですよね。&lt;/p&gt;

&lt;p&gt;そこで、Go 1.8から追加された&lt;a href="https://golang.org/pkg/context"&gt;&lt;code&gt;context&lt;/code&gt;&lt;/a&gt;を使うことができます。&lt;a href="https://golang.org/pkg/net/http#Reqeuest"&gt;&lt;code&gt;http.Request&lt;/code&gt;&lt;/a&gt;には&lt;code&gt;context.Context&lt;/code&gt;が入っていて、&lt;a href="https://golang.org/pkg/net/http#Request.Context"&gt;&lt;code&gt;Request.Context()&lt;/code&gt;&lt;/a&gt;でget、&lt;a href="https://golang.org/pkg/net/http#Request.WithContext"&gt;&lt;code&gt;Request.WithContext()&lt;/code&gt;&lt;/a&gt;でsetできます。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;context.Context&lt;/code&gt;に値を持たせる方法で最初に思いつくのは&lt;a href="https://golang.org/pkg/context#Context.WithValue"&gt;&lt;code&gt;Context.WithValue()&lt;/code&gt;&lt;/a&gt;を用いる方法ですが、これは値を取得する度にtype assertionをする必要があり、あまり&lt;a href="https://medium.com/@lestrrat/alternative-to-using-context-value-f2efe6bd2788"&gt;よくありません&lt;/a&gt;。&lt;br&gt;&lt;br&gt;
これを解消するため、自分で型を定義するのがよいでしょう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package context // internal context subpackage

import (
    "context"
    "errors"
)

type withSomethingContext struct {
    context.Context
    something *Something
}

func WithSomething(ctx context.Context, something *Something) context.Context {
    return &amp;amp;withSomethingContext{
        Context: ctx,
        something: something,
    }
}

func Something(ctx context.Context) (*Something, error) {
    if sctx, ok := ctx.(*withSomethingContext); ok {
        if sctx.something != nil {
            return sctx.something, nil
        }
    }
    return nil, errors.New(`no asscosiated something`)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;このように定義をすることで、毎回type assertionをする必要もなくなり、すっきりします。&lt;/p&gt;

&lt;p&gt;扨、このパッケージと&lt;code&gt;context&lt;/code&gt;パッケージを両方読み込むためには、どちらかの読み込み名称を変更する必要があります。&lt;br&gt;&lt;br&gt;
たとえば、以下の様な具合です。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import (
    "context"
    mycontext "github.com/hoge/fuga/context"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;また、ソースコード中でも&lt;code&gt;context&lt;/code&gt;と&lt;code&gt;mycontext&lt;/code&gt;を使い分ける必要があり、煩雑です。&lt;br&gt;&lt;br&gt;
この問題は、Go 1.9で導入された&lt;a href="https://golang.org/doc/go1.9#language"&gt;Type Alias&lt;/a&gt;を使うときれいに書くことができます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "context"

type Context = context.Context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;このように書くと、標準パッケージの&lt;code&gt;context.Context&lt;/code&gt;と、このアプリケーションにおける&lt;code&gt;context.Context&lt;/code&gt;が同一のものとして扱われます。&lt;br&gt;&lt;br&gt;
そのため、一つのパッケージのインポートだけで良くなります。&lt;/p&gt;

&lt;p&gt;最終的な&lt;code&gt;context&lt;/code&gt;サブパッケージのコードは以下の様になるでしょう。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package context // internal context subpackage

import (
    "context"
    "errors"
)

type Context = context.Context
/*
** some more definition
*/

type withSomethingContext struct {
    Context
    something *Something
}

func WithSomething(ctx Context, something *Something) Context {
    return &amp;amp;withSomethingContext{
        Context: ctx,
        something: something,
    }
}

func Something(ctx Context) (*Something, error) {
    if sctx, ok := ctx.(*withSomethingContext); ok {
        if sctx.something != nil {
            return sctx.something, nil
        }
    }
    return nil, errors.New(`no asscosiated something`)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;実際には、標準パッケージの&lt;code&gt;context&lt;/code&gt;と同等に使用するにはその他の定義の再定義や、複数の&lt;code&gt;withXXXContext&lt;/code&gt;を定義した場合には再帰的に値を読み出す処理が必要になりますが、基本的にはこの形を使用すると便利です。&lt;br&gt;&lt;br&gt;
このように&lt;code&gt;context&lt;/code&gt;を定義しておき、以下の様に使用します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func withSomethingMiddleware(h http.Handler) http.Handler {
    return http.Handler(func(w http.ResponseWriter, r *http.Request) {
        something := &amp;amp;Something{}
        r = r.WithContext(context.WithSomething(r.Context(), something))
        h.ServeHTTP(w, r)
    })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;http.Handler&lt;/code&gt;のmiddlewareについてはまたの機会に。&lt;/p&gt;

</description>
      <category>go</category>
    </item>
  </channel>
</rss>
