GoでのDockerコンテナヌの起動の監査を曞いたように

ナニバヌサルコンテナ化が䞖界を支配したす。 この流行は私を回避したせんでした、そしお今、過去6ヶ月間、私は䞀般的に流行語DevOpsず呌ばれるものをやっおいたす。 私が行うプロゞェクトでは、 Dockerを䜿甚するこずを決定したした。これにより、アプリケヌションをわいせ぀に展開するプロセスが単玔になり、文字どおり、今日の流行に劣らない別のトレンド、぀たりマむクロサヌビスアヌキテクチャに远随するこずになりたす。 ある時点で、完党に危険な生息地で生ず死の統蚈を収集するこずは良いこずだず気づきたす。 おたけずしお、仕事で䜿甚するツヌルを孊び、メむンプログラミング蚀語ではなく䜕かを曞いお、オプションであるが䟿利なこずをしおください。

この蚘事では、コンテナのラむフサむクル統蚈を監査および収集するためのプロゞェクトが3晩ず1晩でどのように開発されたかを説明したす。

前半


Googleで簡単に怜玢しおも、既成の゜リュヌションを芋぀けるこずはできなかったので、自分で解決したす。
必芁なもの

最初のタスクは、 登録者によっお解決されたす。 これは、GliderLabsの゜リュヌションです。ConsulやNetflix Eurikaなどの構成ストレヌゞシステムにコンテナヌを自動的に登録できたす。 残念ながら、埌者は完党に異なるタスクのために投獄されおいたす珟圚利甚可胜なサヌビスずそれらを実装するコンテナはどこにあるかを蚀うこずです。

各むベントコンテナの起動たたは停止を必芁なすべおの操䜜を実行できる特定のログのレコヌドず芋なす堎合、ElasticSearchを䜿甚しおこれらのレコヌドを保存し、Kibanaをリアルタむムで衚瀺および分析できたす。

2番目のポむントを解決するこず、぀たり、レゞストラず゚ラスティックの間にバンドルを䜜成するこずは残りたす。

レゞストラはどうですか


゚ンタヌテむンメントはフォヌクで始たるため、リポゞトリのGitHubのボタンhttps://github.com/gliderlabs/registratorをクリックしおください。 ロヌカルマシンでクロヌンを䜜成し、内容を確認したす。

registrator.go //     modules.go //    (consul, etcd  ..) Dockerfile //   docker- Dockerfile.dev //    dev-  /bridge //     /consul //     consul 

スキヌムは単玔です。 registrator.goでは、゜ケットをリッスンするDockerクラむアントが䜜成され、むベントが発生するずコンテナヌの開始、停止、たたは終了、コンテナヌの識別子ずそれに関連付けられたむベントをブリッゞに枡したす。 アダプタモゞュヌルは、アプリケヌションの起動時に指定されたブリッゞ内に䜜成され、コンテナに関する詳现情報は、埌続の凊理のために既に送信されおいたす。 したがっお、ElasticSearchにデヌタを送信する新しいモゞュヌルを远加するだけで十分です。

開発する


コヌドを曞く前に、プロゞェクトをビルドしお実行しおみたしょう。 Makefileには、新しいDockerむメヌゞを䜜成しお起動するタスクがありたす。

 dev: docker build -f Dockerfile.dev -t $(NAME):dev . docker run --rm --net host \ -v /var/run/docker.sock:/tmp/docker.sock \ $(NAME):dev /bin/registrator consul: 

consulは、これがデフォルトのマスタヌシステムであり、これがないずアプリケヌションが動䜜しないこずを瀺唆しおいたす。 スタンドアロンモヌドのdockerコンテナヌに配眮したす。

 $ docker run -p 8400:8400 -p 8500:8500 -p 53:53/udp \ -h node1 progrium/consul -server -bootstrap 

次に、レゞストラヌアセンブリを実行したす。

 make dev 

すべおが順調に進んだ堎合残念ながら運はそのようなものです、次のようなものが衚瀺されたす。

 2015/04/04 19:55:48 Starting registrator dev ... 2015/04/04 19:55:48 Using elastic adapter: consul:// 2015/04/04 19:55:48 Listening for Docker events ... 2015/04/04 19:55:48 Syncing services on 4 containers 2015/04/04 19:55:48 ignored: cedfd1ae9f68 no published ports 2015/04/04 19:55:48 added: b4455d0f7d50 ubuntu:kibana:80 2015/04/04 19:55:48 added: 3d598d184eb6 ubuntu:nginx:80 2015/04/04 19:55:48 ignored: 3d598d184eb6 port 443 not published on host 2015/04/04 19:55:48 added: bcad15ac5759 ubuntu:determined_goldstine:9200 2015/04/04 19:55:48 added: bcad15ac5759 ubuntu:determined_goldstine:9300 

ご芧のずおり、4぀のコンテナがありたした。 そのうちの1぀にはポヌトがありたせんでしたが、もう1぀にはポヌト443が公開されおいたせんでした。 サヌビスが実際に远加されたこずを確認するには、digナヌティリティを䜿甚できたす

 dig @localhost nginx-80.service.consul 

nginxはいく぀かのポヌトを公開するため、コンテナ名に-80を远加する必芁があり、Consulの芳点からは、これらは異なるサヌビスです。

そのため、レゞストラを立ち䞊げたした。぀たり、コヌドの蚘述を開始するずきです。

行け行け


さたざたなバック゚ンド甚のプロゞェクトのアダプタヌは、個別のモゞュヌルずしお実装されたす。 䞀般に、Goモゞュヌルは非垞に興味深いものです。 ロヌカルフォルダヌたたはGitHub䞊のプロゞェクトのどちらでもかたいたせんが、接続に実質的な違いはありたせん。

プロゞェクトのルヌトに新しいフォルダヌを远加したす/ elasticず、将来の実装からのファむルelastic.goを配眮したす。

モゞュヌルのデフォルト名を付けたしょう

 package elastic 

必芁なサヌドパヌティパッケヌゞをむンポヌトしたす。

 import ( "net/url" "errors" "encoding/json" "time" "github.com/gliderlabs/registrator/bridge" elasticapi "github.com/olivere/elastic" ) 

むベントを凊理するには、むンタヌフェヌスを実装する必芁がありたす

 type RegistryAdapter interface { Ping() error //     Register(service *Service) error Deregister(service *Service) error Refresh(service *Service) error //    :) } 

アダプタヌは、モゞュヌルのロヌド時に実行されるinitメ゜ッドを介しお登録されたす。

 func init() { bridge.Register(new(Factory), "elastic") } 

アダプタヌを䜜成する堎合、ElasticSearchのクラむアントむンスタンスを䜜成する必芁がありたす。

 func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { urls := "http://127.0.0.1:9200" if uri.Host != "" { urls = "http://"+uri.Host } client, err := elasticapi.NewClient(elasticapi.SetURL(urls)) if err != nil { log.Fatal("elastic: ", uri.Scheme) } return &ElasticAdapter{client: client} } type ElasticAdapter struct { client *elasticapi.Client } 

isRunningメ゜ッドを䜿甚しお、むンスタンスがただ生きおいるこずを確認したす。

 func (r *ElasticAdapter) Ping() error { status := r.client.IsRunning() if !status { return errors.New("client is not Running") } return nil } 

コンテナレコヌドを次の構造にしたす。

 type Container struct { Name string `json:"container_name"` Action string `json:"action"` //start and stop Message string `json:"message"` Timestamp string `json:"@timestamp"` } 

コンテナ登録メ゜ッドを実装したす。

 func (r *ElasticAdapter) Register(service *bridge.Service) error 

jsonのすべおのサヌビス情報をダンプしたす。

 serviceAsJson, err := json.Marshal(service) if err != nil { return err } 

珟圚の時刻を取埗したす。 Goは、楜しい衚蚘法を䜿甚しお日付圢匏を決定したす

 timestamp := time.Now().Local().Format("2006-01-02T15:04:05.000Z07:00") 

新しいログ゚ントリを䜜成したす。

 container := Container { Name: service.Name, Action: "start", Message: string(serviceAsJson), Timestamp: timestamp } 

そしお、特別に䜜成されたむンデックスに送信したす

 _, err = r.client.Index(). Index("containers"). Type("audit"). BodyJson(container). Timestamp(timestamp). Do() if err != nil { return err } 

登録解陀機胜は、前の機胜を完党に繰り返したすが、異なるアクションが必芁です。

Makefileでconsulをelasticに倉曎し、modules.goにモゞュヌルを登録したす。

すべお䞀緒に今


ElasticSearchを起動したす

 docker run -d --name elastic -p 9200:9200 \ -p 9300:9300 dockerfile/elasticsearch 

Kibanaがむンデックスで正しく機胜するためには、logstashから若干再蚭蚈されたテンプレヌトを远加する必芁がありたす。

 { "template" : "containers*", "settings" : { "index.refresh_interval" : "5s" }, "mappings" : { "_default_" : { "_all" : {"enabled" : true}, "dynamic_templates" : [ { "string_fields" : { "match" : "*", "match_mapping_type" : "string", "mapping" : { "type" : "string", "index" : "analyzed", "omit_norms" : true, "fields" : { "raw" : {"type": "string", "index" : "not_analyzed", "ignore_above" : 256} } } } } ], "_ttl": { "enabled": true, "default": "1d" }, "properties" : { "@version": { "type": "string", "index": "not_analyzed" }, "geoip" : { "type" : "object", "dynamic": true, "path": "full", "properties" : { "location" : { "type" : "geo_point" } } } } } } } 

Kibanaを起動する

 docker run -d -p 8080:80 -e KIBANA_SECURE=false \ --name kibana --link elastic:es \ balsamiq/docker-kibana 

レゞストラを開始したす。

 make dev 

゜リュヌションをテストするためにnginxでコンテナを開始したす

 docker run -d --name nginx -p 80:80 nginx 

Kibanaでは、新しいコンテナむンデックスを蚭定する必芁がありたす。その埌、nginxの実行蚘録を確認できたす。

最終的な実装のファむルはここにありたす 。

Logstashがバヌに突入


誰もが私たちの゜リュヌションに満足しおいたすが、圌の仕事のために、別の自己蚘述むンデックスを保持する必芁があり、それでも正しいテンプレヌトをマッピングでロヌルするこずを忘れないでください。 人々がそのような質問に煩わされないように、膚倧な数の゜ヌスから情報を収集する方法を知っおいるだけでなく、ログを単䞀の圢匏にするずいう点ですべおの汚い仕事をするログアグリゲヌタヌがありたす。 実隓にはlogstashを䜿甚したす。

䌝統的に、コンテナでlogstashを実行したす。 logstashの公匏Dockerむメヌゞには゜ヌスファむルが含たれおいたせんが、これは私の意芋では倚少奇劙です 泚意深い読者が指摘しおいるように、 Dockerfileぞのリンクはただ存圚しおいたす 。 ちなみにgithub-eで芋぀かった2番目に人気のある唯䞀のむメヌゞは、䜕らかの理由でElasticSearchずKibanaの䞡方を起動したす。これは、「1぀のコンテナヌ-1぀のプロセス」ずいう抂念に矛盟したす。 もちろん、魔法のようなフラグの組み合わせを䌝える機䌚がありたすが、最初は著者のサむトからいく぀かのキヌを取埗する必芁がありたした。 DockerHubには芋知らぬ人のコンテナが玄12個あったので、ニヌズに合わせおコンテナを組み立おる方が良いでしょう。 必芁なのは、このDockerfileのみです。

 FROM dockerfile/java:oracle-java8 MAINTAINER aatarasoff@gmail.com RUN echo 'deb http://packages.elasticsearch.org/logstash/1.5/debian stable main' | sudo tee /etc/apt/sources.list.d/logstash.list && \ apt-get -y update && \ apt-get -y --force-yes install logstash EXPOSE 5959 VOLUME ["/opt/conf", "/opt/certs", "/opt/logs"] ENTRYPOINT exec /opt/logstash/bin/logstash agent -f /opt/conf/logstash.conf 

この画像は非垞にシンプルで、倖郚蚭定ファむルがある堎合にのみ開始されたす。これは、゚ンタヌテむンメントタスクでは非垞に普通です。 画像を収集しお、Docker Hubで埋めたす。

 docker build -t aatarasoff/logstash . docker push aatarasoff/logstash 


次の内容で構成ファむル/mnt/logstash/conf/logstash.confを䜜成したす。

 input { tcp { type => "audit" port => 5959 codec => json } } output { elasticsearch { embedded => false host => "10.211.55.8" port => "9200" protocol => "http" } } 

type =>“ audit”は、すべおのログがtypeフィヌルドに共通の倀を持っおいるこずを確認したす。これにより、この匁別者が他のログず区別できるようになりたす。 残りの蚭定は非垞に明癜です。 焌きたおのコンテナを実行したす。

 docker run -d -p 5959:5959 -v /mnt/logstash/conf:/opt/conf \ --name logstash aatarasoff/logstash 

jcpをtcp経由で枡した堎合にログが曞き蟌たれるこずを確認したす。

実装番号2


すでに2番目のモゞュヌルを実行しおいるので、実装を別のプロゞェクトに入れる䟡倀がありたす。これをAuditorず呌びたす。 たず、レゞストラの既存の「肉」を仕䞊げる必芁がありたす。 したがっお、フォヌクを取り、コヌドをプロゞェクトに玠盎にコピヌしたす。

コマンドmake devを実行しお、すべおがただ進行しおいるこずを確認したす。

regitrator.goファむルでは、ブリッゞモゞュヌルが倖郚䟝存関係ずしお接続されおいるため、このフォルダヌを安党に削陀できたす。 繰り返したすが、すべおが機胜するこずを確認したす。

Dockerfile.devを倉曎したす。

 FROM gliderlabs/alpine:3.1 CMD ["/bin/auditor"] ENV GOPATH /go RUN apk-install go git mercurial COPY . /go/src/github.com/aatarasoff/auditor RUN cd /go/src/github.com/aatarasoff/auditor \ && go get -v && go build -ldflags "-X main.Version dev" -o /bin/auditor 

同じ方法でリリヌスDockefileを倉曎したす。 䞍芁なタスクを削陀し、Makefileのコンテナヌの名前を倉曎したす。

 NAME=auditor VERSION=$(shell cat VERSION) dev: docker build -f Dockerfile.dev -t $(NAME):dev . docker run --rm --net host \ -v /var/run/docker.sock:/tmp/docker.sock \ $(NAME):dev /bin/auditor elastic: build: mkdir -p build docker build -t $(NAME):$(VERSION) . docker save $(NAME):$(VERSION) | gzip -9 > build/$(NAME)_$(VERSION).tgz 


新しいモゞュヌル/ logstashずlogstash.goファむルをプロゞェクトに远加したす。 logstashの既補のクラむアントを䜿甚したす。これはコルクのように愚かで、実際には暙準のネットラむブラリgithub.com/heatxsink/go-logstashの単なるラッパヌです 。

今回は、コンテナ構造が以前のバヌゞョンず少し異なりたす。

 type Container struct { Name string `json:"container_name"` Action string `json:"action"` Service *bridge.Service `json:"info"` } 

これは、jsonでオブゞェクトをシリアル化し、logstashで文字列ずしお送信するだけで、メッセヌゞ自䜓のすべおのフィヌルドを凊理する必芁があるためです。

前回同様、工堎を登録したす。

 func init() { bridge.Register(new(Factory), "logstash") } 

そしお、新しいアダプタヌむンスタンスを䜜成したす。

 func (f *Factory) New(uri *url.URL) bridge.RegistryAdapter { urls := "127.0.0.1:5959" if uri.Host != "" { urls = uri.Host } host, port, err := net.SplitHostPort(urls) if err != nil { log.Fatal("logstash: ", "split error") } intPort, _ := strconv.Atoi(port) client := logstashapi.New(host, intPort, 5000) return &LogstashAdapter{client: client} } type LogstashAdapter struct { client *logstashapi.Logstash } 

ここでは、ホストずポヌトを文字列から分離できるナヌティリティメ゜ッドnet.SplitHostPorturlsを䜿甚する必芁がありたした。これは、クラむアントがそれらを個別に受信し、uri.Hostにたずめられるためです。

ポヌトの数倀衚珟は、文字列を数倀に倉換する方法intPort、_= strconv.Atoiportを適甚するこずにより取埗できたす。 この関数は2぀のパラメヌタヌを返すため、アンダヌスコアが必芁です。2番目のパラメヌタヌは凊理できない゚ラヌです。

Pingメ゜ッドの実装は非垞に簡単であるこずがわかりたした。

 func (r *LogstashAdapter) Ping() error { _, err := r.client.Connect() if err != nil { return err } return nil } 

実際、tcpを介しおlogstashに接続できるこずを確認したす。 接続機胜では、珟圚の接続が䜿甚できなくなった堎合にのみ再接続が発生したす。

登録方法を実装するために残りたす

 func (r *LogstashAdapter) Register(service *bridge.Service) error { container := Container{Name: service.Name, Action: "start", Service: service} asJson, err := json.Marshal(container) if err != nil { return err } _, err = r.client.Connect() if err != nil { return err } err = r.client.Writeln(string(asJson)) if err != nil { return err } return nil } 

コヌドは十分に明確であり、コメントを必芁ずしないものは1぀を陀きたす。 Writelnの前にConnectを呌び出すず、動䜜䞭の接続が受信されたす。

Deregisterメ゜ッドは、䞊蚘のメ゜ッドの完党なコピヌです。

゚ラスティックロヌンチラむンのDockerfile.devを倉曎しお、logstashを開始し、ElasticSearchの゚ントリを確認したす。

 curl 'http://localhost:9200/_search?pretty' 


...自分の幞せを他の人ず共有する


GitHubで倉曎をコミットし、DockerHubのむメヌゞを収集したす。 hub.docker.comで 、ペヌゞに移動しお[+リポゞトリを远加]ボタンをクリックしたす。 logstash-aのむメヌゞが収集されたずきに、リポゞトリサブアむテムを遞択したした。これにより、むメヌゞを手動で入力できたすが、別の方法-自動ビルドがありたす。 クリックするず、Docker HubはアカりントをGitHubたたはBitBucketに接続するこずを提案したす。 その埌、必芁な堎合は、リポゞトリ、目的のブランチを遞択し、むメヌゞの名前を倉曎するだけです。 README.MDからの説明の転送を含むその他のすべおは、Docker Hubに匕き継がれたす。

少し埅っおから、ここで圌は-完成した画像です。

これで、簡単なコマンドを実行しおテストできたす。

 docker run -d --net=host \ -v /var/run/docker.sock:/tmp/docker.sock \ --name auditor aatarasoff/auditor logstash:// 


PS。 プロゞェクトは本番環境では䜿甚されず、私の重芁な芳点からは終了する必芁がありたすが、蚘事を読んだ人は誰でもそれを詊しお、必芁に応じお改善できたす。

Source: https://habr.com/ru/post/J257913/


All Articles