Skip to content

Instantly share code, notes, and snippets.

@whitekid
Last active August 29, 2015 14:22
Show Gist options
  • Save whitekid/0b6e9781455e550f0042 to your computer and use it in GitHub Desktop.
Save whitekid/0b6e9781455e550f0042 to your computer and use it in GitHub Desktop.
sync docker container network namespace
package main
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"time"
"github.com/fsouza/go-dockerclient"
)
const connect_retry_interval = 3 * time.Second
func get_docker_client() (*docker.Client, error) {
endpoint := os.Getenv("DOCKER_HOST")
if endpoint == "" {
endpoint = "unix:///var/run/docker.sock"
}
cert_path := os.Getenv("DOCKER_CERT_PATH")
var client *docker.Client
var err error
log.Printf("Connecting %v", endpoint)
if cert_path != "" {
ca := fmt.Sprintf("%s/ca.pem", cert_path)
cert := fmt.Sprintf("%s/cert.pem", cert_path)
key := fmt.Sprintf("%s/key.pem", cert_path)
client, err = docker.NewTLSClient(endpoint, cert, key, ca)
} else {
client, err = docker.NewClient(endpoint)
}
if err != nil {
return nil, err
}
if _, err = client.ListImages(docker.ListImagesOptions{}); err != nil {
return nil, err
}
log.Printf("Connected at %v", endpoint)
return client, nil
}
func get_container_pid(client *docker.Client, id string) int {
container, _ := client.InspectContainer(id)
return container.State.Pid
}
func file_exists(name string) bool {
if _, err := os.Stat(name); os.IsNotExist(err) {
return false
}
return true
}
func sync_netns(client *docker.Client) {
const netns_dir = "/var/run/netns"
log.Printf("sync docker namespaces...")
// delete invalid netns
files, _ := ioutil.ReadDir(netns_dir)
for _, f := range files {
if f.IsDir() {
continue
}
if f.Mode()&os.ModeSymlink != 0 {
netns_file := fmt.Sprintf("/var/run/netns/%s", f.Name())
fname, _ := os.Readlink(netns_file)
if !file_exists(fname) {
log.Printf("Remove staled netns file: %s", netns_file)
err := os.Remove(netns_file)
if err != nil {
log.Printf("%s", err)
}
}
}
}
// create missed netns
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
if err != nil {
log.Printf("%v", err)
}
for _, c := range containers {
pid := get_container_pid(client, c.ID)
// skip stopped container
if pid == 0 {
continue
}
if !file_exists(fmt.Sprintf("/var/run/netns/%v", c.ID)) {
create_netns(c.ID, pid)
}
}
}
func create_netns(id string, pid int) {
log.Printf("Create namespace %v", id)
err := os.Symlink(fmt.Sprintf("/proc/%v/ns/net", pid), fmt.Sprintf("/var/run/netns/%v", id))
if err != nil {
log.Printf(err.Error())
}
}
func delete_netns(id string) {
log.Printf("Delete namespace %v", id)
err := os.Remove(fmt.Sprintf("/var/run/netns/%s", id))
if err != nil {
log.Printf(err.Error())
}
}
func handle_event() error {
client, err := get_docker_client()
if err != nil {
return err
}
sync_netns(client)
listener := make(chan *docker.APIEvents, 10)
err = client.AddEventListener(listener)
if err != nil {
log.Fatalf("AddEventListener failed: %v", err)
}
defer client.RemoveEventListener(listener)
for {
select {
case event, ok := <-listener:
if !ok {
return errors.New("Docker connectin close.. retring.")
}
log.Printf("Event received: Status=%v, ID=%v", event.Status, event.ID)
switch event.Status {
case "create", "start":
pid := get_container_pid(client, event.ID)
create_netns(event.ID, pid)
case "stop", "destroy":
delete_netns(event.ID)
}
}
}
}
func main() {
for {
err := handle_event()
log.Printf("ERROR: %v", err.Error())
time.Sleep(connect_retry_interval)
}
}
#!/bin/env python
import os
import json
from requests.packages.urllib3.exceptions import ProtocolError
from docker import Client
def get_docker():
return Client(base_url='unix:///var/run/docker.sock')
def sync_netns(dk):
netns_dir = '/var/run/netns'
# delete invalid netns
for f in os.listdir(netns_dir):
fn = os.path.join(netns_dir, f)
if os.path.islink(fn) and not os.path.exists(os.readlink(fn)):
os.unlink(fn)
# create missing netns
for c in dk.containers():
cid = c['Id']
pid = dk.inspect_container(c['Id'])["State"]["Pid"]
if not os.path.islink("/var/run/netns/%s" % cid):
os.symlink("/proc/%s/ns/net" % pid, "/var/run/netns/%s" % cid)
def main():
while True:
try:
dk = get_docker()
sync_netns(dk)
for event_r in dk.events():
event = json.loads(event_r)
status, cid = event['status'], event['id']
# get pid of container
pid = dk.inspect_container(cid)["State"]["Pid"]
if status == 'start':
os.symlink("/proc/%s/ns/net" % pid, "/var/run/netns/%s" % cid)
elif status == 'kill':
os.unlink("/var/run/netns/%s" % cid)
except ProtocolError, e:
# restart docker
print e
if __name__ == '__main__':
main()
# vim: et ai ts=4 sw=4
@ziozzang
Copy link

개인적인 의견으로

  • 사실 Docker 관련 MGMT 쪽은 가능하면 파이썬을 안쓰는데 이유가.
  • CoreOS 같은놈에는 파이썬이 기본으로 안들어가 있고.
  • 이제 향후에 경량 컨테이너용 OS들이 나올텐데 이것도 파이썬이 있을거란 생각이 안들고.
  • 그래서 요즘 추세가 Go-lang 인듯요. 근데 위의 것만 따지면 생각보다 어렵지 않게 쉘로도 짤수 있을것 같은데요. 짜서 공개 해보도록 할게용 @_@...
  • 뭐 물론 이벤트 같은 그런건 안될거 같지만은...

@whitekid
Copy link
Author

@ziozzang 그냥 개념 증명이라 생각해주세요... docker-py를 설치하는 비용도 만만하지 않아서 실제로는 다르게 쓰겠죠..

@ziozzang
Copy link

https://gist.github.com/ziozzang/80b7a7bffc185b4d507b
간단하게 배쉬로 만들어 봤스므니다..
근데 이벤트 드리븐이 안되서 그냥 슬립으로 처리를 하긴 했는데 동작은 잘 됩니다. -,.-)b
근데 파이썬보다 코드가 짧네요.. ㅋㅋ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment